[
  {
    "path": ".gitattributes",
    "content": "*.css linguist-language=java\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.yml",
    "content": "name: 💣 Bug\ntitle: 'Bug: '\ndescription: bug template\nlabels: bug\nbody:\n  - type: input\n    id: version\n    attributes:\n      label: Chat2DB Version\n    validations:\n      required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Describe the bug\n      description: |\n        If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem and error log.\n        清晰的描述遇到的问题，并建议附上错误截图及错误日志。\n    validations:\n      required: true"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/optimized.md",
    "content": "---\nname: \"💫 Optimized\"\nabout: optimized template\ntitle: '优化: '\nlabels: 'optimized'\n---"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/suggest.md",
    "content": "---\nname: \"💌 Suggest\"\nabout: suggest template\ntitle: 'suggest: '\nlabels: 'suggest'\n---\n"
  },
  {
    "path": ".github/workflows/pushdocker.yml",
    "content": "# When tagging a release, do two things\n# 1.\nname: Push To Docker\n\n# Workflow's trigger\non:\n  release:\n    types: [ published ]\n\n# Workflow's jobs\njobs:\n  docker:\n    strategy:\n      matrix:\n        include:\n          - arch: amd64\n          - arch: arm64\n            variant: v8\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out git repository\n        uses: actions/checkout@main\n\n      # Unable to obtain version number. Since the workflow doesn't support it, we'll use a plugin\n      - name: Create version\n        id: chat2db_version\n        uses: bhowell2/github-substring-action@1.0.1\n        with:\n          value: ${{ github.ref }}\n          index_of_str: \"refs/tags/v\"\n\n\n      # Outputting basic information\n      - name: Print basic information\n        run: |\n          echo \"current version: ${{ steps.chat2db_version.outputs.substring }}\"\n\n      # Install Node.js\n      - name: Install Node.js\n        uses: actions/setup-node@main\n        with:\n          node-version: 16\n          cache: \"yarn\"\n          cache-dependency-path: chat2db-client/yarn.lock\n\n      # Build static file information\n      - name: Yarn install & build & copy\n        run: |\n          cd chat2db-client\n          yarn install \n          yarn run build:web:prod --app_version=${{ steps.chat2db_version.outputs.substring }}\n          cp -r dist ../chat2db-server/chat2db-server-web-start/src/main/resources/static/front\n          cp -r dist/index.html ../chat2db-server/chat2db-server-web-start/src/main/resources/thymeleaf/\n\n      # Install java and maven\n      - name: Install Java and Maven\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"adopt\"\n          cache: \"maven\"\n\n      # Compile server-side Java version\n      - name: Build Java\n        run: mvn clean package -B '-Dmaven.test.skip=true' -f chat2db-server/pom.xml\n      \n\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v2\n        with:\n          platforms: amd64,arm64\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v2\n\n      # Log in docker hub\n      - name: Log in to Docker Hub\n        uses: docker/login-action@v2\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n\n      # Packaging and sending to Docker\n      - name: Build and push\n        uses: docker/build-push-action@v4\n        with:\n          context: .\n          push: true\n          platforms: linux/amd64,linux/arm64/v8\n          tags: chat2db/chat2db:${{ steps.chat2db_version.outputs.substring }},chat2db/chat2db:latest\n          file: docker/Dockerfile"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "# Workflow's name\nname: Build Client\n\n# Workflow's trigger\n# Pack when creating tags\non:\n  push:\n    tags:\n      - v*\n\n# Workflow's jobs\n# A total of 3 computers are required to run\n# windows\n# macos-latest x86_64\n# macos-latest arm64\njobs:\n  release:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: windows-latest\n          - os: macos-latest\n            arch: x86_64\n          - os: macos-latest\n            arch: arm64\n          - os: ubuntu-latest\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Check out git repository\n        uses: actions/checkout@main\n\n      # Obtaining the version number is not supported by workflow, so a plug-in is used.\n      - name: Create version\n        id: chat2db_version\n        uses: bhowell2/github-substring-action@1.0.1\n        with:\n          value: ${{ github.ref }}\n          index_of_str: \"refs/tags/v\"\n\n      # Output basic information\n      - name: Print basic information\n        run: |\n          echo \"current environment: ${{ env.CHAT2DB_ENVIRONMENT }}\"\n          echo \"current version: ${{ steps.chat2db_version.outputs.substring }}\"\n\n      # Install jre Windows\n      - name: Install Jre for Windows\n        if: ${{ runner.os == 'Windows' }}\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          java-package: \"jre\"\n\n      # Install jre MacOS X64\n      - name: Install Jre MacOS X64\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          java-package: \"jre\"\n          architecture: \"x64\"\n\n      # Install jre MacOS arm64\n      - name: Install Jre MacOS arm64\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          java-package: \"jre\"\n          architecture: \"aarch64\"\n\n          # Install jre Linux\n      - name: Install Jre for Linux\n        if: ${{ runner.os == 'Linux' }}\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          java-package: \"jre\"\n\n      # java.security open tls1 Windows\n      - name: Enable tls1\n        if: ${{ runner.os == 'Windows' }}\n        run: |\n          # sed -i '' \"s/\\(^jdk.tls.disabledAlgorithms=\\)\\(.*\\)\\( TLSv1, TLSv1.1,\\)\\(.*\\)/\\1\\2\\4/\" \"${{ env.JAVA_HOME }}\\conf\\security\\java.security\"\n          $filePath = \"${{ env.JAVA_HOME }}\\conf\\security\\java.security\"\n          $content = Get-Content $filePath -Raw\n          $updatedContent = $content -replace '^(jdk.tls.disabledAlgorithms=)(.*)( TLSv1, TLSv1.1,)(.*)', '$1$2$4'\n          $updatedContent | Set-Content $filePath\n        shell: pwsh\n\n      # java.security open tls1 macOS\n      - name: Enable tls1\n        if: ${{ runner.os == 'macOS' }}\n        run: |\n          sed -i '' \"s/\\(^jdk.tls.disabledAlgorithms=\\)\\(.*\\)\\( TLSv1, TLSv1.1,\\)\\(.*\\)/\\1\\2\\4/\" $JAVA_HOME/conf/security/java.security\n\n      # Copy jre Windows\n      - name: Copy Jre for Windows\n        if: ${{ runner.os == 'Windows' }}\n        run: |\n          mkdir chat2db-client/static\n          cp -r \"${{ env.JAVA_HOME }}\" chat2db-client/static/jre\n\n      # Copy jre macOS\n      - name: Copy Jre for macOS\n        if: ${{ runner.os == 'macOS' }}\n        run: |\n          mkdir chat2db-client/static\n          cp -r $JAVA_HOME chat2db-client/static/jre\n          chmod -R 777 chat2db-client/static/jre/\n\n      # Copy jre Linux\n      - name: Copy Jre for Linux\n        if: ${{ runner.os == 'Linux' }}\n        run: |\n          mkdir chat2db-client/static\n          cp -r $JAVA_HOME chat2db-client/static/jre\n          chmod -R 777 chat2db-client/static/jre/\n\n      # Install node\n      - name: Install Node.js\n        uses: actions/setup-node@main\n        with:\n          node-version: 16\n          cache: \"yarn\"\n          cache-dependency-path: chat2db-client/yarn.lock\n\n      # Install java\n      - name: Install Java and Maven\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          cache: \"maven\"\n\n      # Build static file information\n      - name: Yarn install & build & copy\n        run: |\n          cd chat2db-client\n          yarn \n          yarn run build:web:prod --app_version=${{ steps.chat2db_version.outputs.substring }}\n          cp -r dist ../chat2db-server/chat2db-server-start/src/main/resources/static/front\n          cp -r dist/index.html ../chat2db-server/chat2db-server-start/src/main/resources/thymeleaf/\n          cd src/main\n          yarn\n          yarn run build\n\n      # Compile server-side java version\n      - name: Build Java\n        run: mvn clean package -B '-Dmaven.test.skip=true' -f chat2db-server/pom.xml\n\n      # touch versions\n      - name: touch versions\n        run: |\n          cd chat2db-client\n          mkdir versions\n          mkdir versions/${{ steps.chat2db_version.outputs.substring }}\n          mkdir versions/${{ steps.chat2db_version.outputs.substring }}/static\n          touch version\n          echo -n ${{ steps.chat2db_version.outputs.substring }} > version\n          cp -r version ./versions/\n\n      # Copy server-side java to the specified location\n      - name: Copy App\n        run: |\n          cp chat2db-server/chat2db-server-start/target/chat2db-server-start.jar chat2db-client/versions/${{ steps.chat2db_version.outputs.substring }}/static/\n      #    cp -r chat2db-server/chat2db-server-start/target/lib chat2db-client/versions/${{ steps.chat2db_version.outputs.substring }}/static/lib\n\n      - name: Prepare Build Electron\n        run: |\n          cd chat2db-client\n          yarn run build:web:desktop --app_version=${{ steps.chat2db_version.outputs.substring }}\n          cp -r dist ./versions/${{ steps.chat2db_version.outputs.substring }}/\n          rm -r dist\n\n      # windows\n      - name: Build/release Electron app for Windows\n        if: ${{ runner.os == 'Windows' }}\n        uses: samuelmeuli/action-electron-builder@v1\n        with:\n          package_root: \"chat2db-client/\"\n          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}\n          mac_certs: ${{ secrets.mac_certs }}\n          mac_certs_password: ${{ secrets.mac_certs_password }}\n          skip_build: true\n          args: \"-c.extraMetadata.version=${{ steps.chat2db_version.outputs.substring }} --win --x64\"\n          release: true\n\n      # macos x86_64\n      - name: Build/release Electron app for MacOS X64\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        uses: samuelmeuli/action-electron-builder@v1\n        with:\n          package_root: \"chat2db-client/\"\n          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}\n          mac_certs: ${{ secrets.mac_certs }}\n          mac_certs_password: ${{ secrets.mac_certs_password }}\n          skip_build: true\n          args: \"-c.extraMetadata.version=${{ steps.chat2db_version.outputs.substring }} --mac --x64\"\n          release: true\n\n      # x86_64 notarization\n      - name: Notarization x86_64 App\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        run: |\n          xcrun notarytool store-credentials \"Chat2DB\" --apple-id \"${{secrets.MAC_APPLE_ID}}\" --password \"${{secrets.MAC_APPLE_PASSWORD}}\" --team-id \"${{secrets.MAC_TEAM_ID}}\"\n          xcrun notarytool submit chat2db-client/release/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.dmg --keychain-profile \"Chat2DB\"\n\n      # macos arm64\n      - name: Build/release Electron app for MacOS arm64\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}\n        uses: samuelmeuli/action-electron-builder@v1\n        with:\n          package_root: \"chat2db-client/\"\n          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}\n          mac_certs: ${{ secrets.mac_certs }}\n          mac_certs_password: ${{ secrets.mac_certs_password }}\n          skip_build: true\n          args: \"-c.extraMetadata.version=${{ steps.chat2db_version.outputs.substring }} --mac --arm64\"\n          release: true\n\n      # arm notarization\n      - name: Notarization arm64 App\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}\n        run: |\n          xcrun notarytool store-credentials \"Chat2DB\" --apple-id \"${{secrets.MAC_APPLE_ID}}\" --password \"${{secrets.MAC_APPLE_PASSWORD}}\" --team-id \"${{secrets.MAC_TEAM_ID}}\"\n          xcrun notarytool submit chat2db-client/release/Chat2DB-${{ steps.chat2db_version.outputs.substring }}-arm64.dmg --keychain-profile \"Chat2DB\"\n\n      # Linux\n      - name: Delete File\n        if: ${{ runner.os == 'Linux' }}\n        run: |\n          cd chat2db-client/static/jre/\n          ls -la\n          rm -rf legal\n          ls -la\n\n      - name: Build/release Electron app for Linux\n        if: ${{ runner.os == 'Linux' }}\n        uses: samuelmeuli/action-electron-builder@v1\n        with:\n          package_root: \"chat2db-client/\"\n          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}\n          skip_build: true\n          args: \"-c.extraMetadata.version=${{ steps.chat2db_version.outputs.substring }} --linux\"\n          release: true\n\n      # Prepare the required data Windows\n      - name: Prepare upload for Windows\n        if: runner.os == 'Windows'\n        run: |\n          mkdir oss_temp_file\n          cp -r chat2db-client/release/*Setup*.exe ./oss_temp_file\n\n      # Prepare the required data MacOS x86_64\n      - name: Prepare upload for MacOS x86_64\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        run: |\n          mkdir oss_temp_file\n          cp chat2db-client/versions/${{ steps.chat2db_version.outputs.substring }}/static/chat2db-server-start.jar   ./oss_temp_file\n          cp -r chat2db-client/release/*.dmg ./oss_temp_file\n          cp -r chat2db-client/versions/${{ steps.chat2db_version.outputs.substring }}/dist ./oss_temp_file/dist\n          cd chat2db-client/versions/${{ steps.chat2db_version.outputs.substring }}/ && zip -r ${{ steps.chat2db_version.outputs.substring }}.zip ./\n          cp -r ${{ steps.chat2db_version.outputs.substring }}.zip ../../../oss_temp_file\n          cd static/ && zip -r chat2db-server-start.zip ./ \n          cp -r chat2db-server-start.zip ../../../../oss_temp_file\n\n      # Prepare the required data MacOS arm64\n      - name: Prepare upload for MacOS arm64\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}\n        run: |\n          mkdir oss_temp_file\n          cp -r chat2db-client/release/*.dmg ./oss_temp_file\n\n      # Prepare the required data Linux\n      - name: Prepare upload for Linux\n        if: runner.os == 'Linux'\n        run: |\n          mkdir oss_temp_file\n          cp -r chat2db-client/release/*.AppImage ./oss_temp_file\n\n      # Upload files to OSS for easy downloading\n      - name: Set up oss utils\n        uses: yizhoumo/setup-ossutil@v1\n        with:\n          endpoint: \"oss-accelerate.aliyuncs.com\"\n          access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}\n          access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}\n          ossutil-version: \"1.7.16\"\n      - name: Upload to oss\n        run: |\n          ossutil cp -rf --acl=public-read ./oss_temp_file/ oss://chat2db-client/release/${{ steps.chat2db_version.outputs.substring }}/\n\n      # Build completion notification\n      - name: Send dingtalk message for Windows\n        if: ${{ runner.os == 'Windows' }}\n        uses: ghostoy/dingtalk-action@master\n        with:\n          webhook: ${{ secrets.DINGTALK_WEBHOOK }}\n          msgtype: markdown\n          content: |\n            {\n              \"title\": \"Windows-release-打包完成通知\",\n              \"text\": \"# Windows-release-打包完成通知   \\n ![bang](https://oss.sqlgpt.cn/static/happy100.jpg)   \\n ###  任务id：[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})   \\n ### Windows下载地址：[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB%20Setup%20${{ steps.chat2db_version.outputs.substring }}.exe](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB%20Setup%20${{ steps.chat2db_version.outputs.substring }}.exe) \"\n            }\n\n      # Build completion notification\n      - name: Send dingtalk message for MacOS x86_64\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        uses: ghostoy/dingtalk-action@master\n        with:\n          webhook: ${{ secrets.DINGTALK_WEBHOOK }}\n          msgtype: markdown\n          content: |\n            {\n              \"title\": \"MacOS-x86_64-release-构建完成通知\",\n              \"text\": \"# MacOS-x86_64-release-打包完成通知   \\n ![bang](https://oss.sqlgpt.cn/static/happy100.jpg)   \\n ###  任务id：[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})    \\n ### Intel芯片下载地址：[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.dmg](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.dmg)   \\n ### jar包下载地址：[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/chat2db-server-start.zip](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/chat2db-server-start.zip) \"\n            }\n\n      # Build completion notification\n      - name: Send dingtalk message for MacOS arm64\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}\n        uses: ghostoy/dingtalk-action@master\n        with:\n          webhook: ${{ secrets.DINGTALK_WEBHOOK }}\n          msgtype: markdown\n          content: |\n            {\n              \"title\": \"MacOS-arm64-release-构建完成通知\",\n              \"text\": \"# MacOS-arm64-release-打包完成通知   \\n ![bang](https://oss.sqlgpt.cn/static/happy100.jpg)   \\n ###  任务id：[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})   \\n ### Apple芯片下载地址：[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}-arm64.dmg](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}-arm64.dmg) \"\n            }\n\n      # Build completion notification\n      - name: Send dingtalk message for Linux\n        if: ${{ runner.os == 'Linux' }}\n        uses: ghostoy/dingtalk-action@master\n        with:\n          webhook: ${{ secrets.DINGTALK_WEBHOOK }}\n          msgtype: markdown\n          content: |\n            {\n              \"title\": \"Linux-test-打包完成通知\",\n              \"text\": \"# Linux-test-打包完成通知   \\n ![bang](https://oss.sqlgpt.cn/static/happy100.jpg)   \\n ###  任务id：[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})   \\n ### Linux下载地址：[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.AppImage](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.AppImage)\"\n            }"
  },
  {
    "path": ".github/workflows/release_test.yml",
    "content": "name: Build Test Client\n\non:\n  push:\n    branches:\n    - \"release_test\"\n\njobs:\n  release:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: windows-latest\n            file_extension: \".exe\"\n            file_name: \"https://oss.sqlgpt.cn/test/99.0.${{ github.run_id }}/Chat2DB-Test%20Setup%2099.0.${{ github.run_id }}-Test.exe\"\n            build_arg: \"--win --x64\"\n          - os: macos-latest\n            arch: x86_64\n            file_name: \"https://download.sqlgpt.cn/test/99.0.${{ github.run_id }}/Chat2DB-Test-99.0.${{ github.run_id }}-Test.dmg\"\n            file_extension: \".dmg\"\n            build_arg: \"--mac --x64\"\n          - os: macos-latest\n            arch: arm64\n            file_name: \"https://download.sqlgpt.cn/test/99.0.${{ github.run_id }}/Chat2DB-Test-99.0.${{ github.run_id }}-Test-arm64.dmg\"\n            file_extension: \".dmg\"\n            build_arg: \"--mac --arm64\"\n          - os: ubuntu-latest\n            file_name: \"https://download.sqlgpt.cn/test/99.0.${{ github.run_id }}/Chat2DB-Test-99.0.${{ github.run_id }}-Test.AppImage\"\n            file_extension: \".AppImage\"\n            build_arg: \"--linux\"\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Check out git repository\n        uses: actions/checkout@main\n\n      # Install JRE\n      - name: Install JRE\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          java-package: \"jre\"\n          # architecture: ${{ matrix.arch == 'arm64' && 'aarch64' || 'x64' }}\n\n      # OpenTLS\n      - name: Enable TLS 1.0 and 1.1 in java.security\n        run: |\n          if [ \"$RUNNER_OS\" = \"Windows\" ]; then\n            sed -i \"s/\\(^jdk.tls.disabledAlgorithms=\\)\\(.*\\)\\(TLSv1, TLSv1.1,\\)\\(.*\\)/\\1\\2\\4/\" \"$JAVA_HOME/conf/security/java.security\"\n          elif [ \"$RUNNER_OS\" = \"Linux\" ]; then\n            sed -i \"s/\\(^jdk.tls.disabledAlgorithms=\\)\\(.*\\)\\(TLSv1, TLSv1.1,\\)\\(.*\\)/\\1\\2\\4/\" \"$JAVA_HOME/conf/security/java.security\"\n          elif [ \"$RUNNER_OS\" = \"macOS\" ]; then\n            sed -i '' \"s/\\(^jdk.tls.disabledAlgorithms=\\)\\(.*\\)\\(TLSv1, TLSv1.1,\\)\\(.*\\)/\\1\\2\\4/\" \"$JAVA_HOME/conf/security/java.security\"\n          fi\n        shell: bash\n        env:\n          RUNNER_OS: ${{ runner.os }}\n          JAVA_HOME: ${{ env.JAVA_HOME }}\n\n      # Copy JRE to the front-end static directory\n      - name: Copy JRE to static directory\n        run: |\n          mkdir -p chat2db-client/static\n          cp -r \"$JAVA_HOME\"/ chat2db-client/static/jre\n          if [ \"${{ runner.os }}\" != \"Windows\" ]; then\n            chmod -R 777 chat2db-client/static/jre\n          fi\n        shell: bash\n        env:\n          JAVA_HOME: ${{ env.JAVA_HOME }}\n\n      # Delete related files in jre in Linux\n      - if: ${{ runner.os == 'Linux' }}\n        name: Delete File on Linux\n        run: |\n          cd chat2db-client/static/jre/\n          ls -la\n          rm -rf legal\n          ls -la\n\n      # Install Node.js\n      - name: Install Node.js\n        uses: actions/setup-node@main\n        with:\n          node-version: \"16\"\n          cache: \"yarn\"\n          cache-dependency-path: chat2db-client/yarn.lock\n\n      # Install Java\n      - name: Install Java and Maven\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          cache: \"maven\"\n\n      # Packaging web front-end resources\n      - name: Build FE Static\n        run: |\n          cd chat2db-client\n          yarn\n          yarn run build:web:prod --app_version=99.0.${{ github.run_id }} --app_port=10822\n          cp -r dist ../chat2db-server/chat2db-server-start/src/main/resources/static/front\n          cp -r dist/index.html ../chat2db-server/chat2db-server-start/src/main/resources/thymeleaf/\n          cd src/main\n          yarn\n          yarn run build\n\n\n      # Package backend project & send to frontend\n      - name: Build BE Static\n        run: |\n          mvn clean package -B '-Dmaven.test.skip=true' -f chat2db-server/pom.xml\n          mkdir -p chat2db-client/versions/99.0.${{ github.run_id }}/static\n          echo -n 99.0.${{ github.run_id }} > chat2db-client/version\n          cp -r chat2db-client/version chat2db-client/versions/\n          cp chat2db-server/chat2db-server-start/target/chat2db-server-start.jar chat2db-client/versions/99.0.${{ github.run_id }}/static/\n\n      # Packaging desktop front-end resources\n      - name: Prepare Build Electron\n        run: |\n          cd chat2db-client\n          yarn run build:web:desktop --app_version=99.0.${{ github.run_id }} --app_port=10822\n          cp -r dist ./versions/99.0.${{ github.run_id }}/\n          rm -r dist\n\n      # Packaging Electron\n      - name: Build/release Electron app\n        uses: samuelmeuli/action-electron-builder@v1\n        with:\n          package_root: \"chat2db-client/\"\n          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}\n          mac_certs: ${{ secrets.mac_certs_temp }}\n          mac_certs_password: ${{ secrets.mac_certs_password_temp }}\n          skip_build: true\n          args: >\n            -c.appId=com.chat2db.test\n            -c.productName=Chat2DB-Test\n            -c.win.publisherName=Chat2DB-Test\n            -c.nsis.shortcutName=Chat2DB-Test\n            -c.extraMetadata.version=99.0.${{ github.run_id }}-Test\n            ${{ matrix.build_arg}}\n\n      # Notarization & Signature Mac App\n      - name: Notarize MacOS x86_64 App\n        if: matrix.os == 'macos-latest' && matrix.arch == 'x86_64'\n        run: |\n          xcrun notarytool store-credentials \"Chat2DB\" --apple-id \"${{ secrets.MAC_APPLE_ID }}\" --password \"${{ secrets.MAC_APPLE_PASSWORD }}\" --team-id \"${{ secrets.MAC_TEAM_ID }}\"\n          xcrun notarytool submit chat2db-client/release/Chat2DB-Test-99.0.${{ github.run_id }}-Test.dmg --keychain-profile \"Chat2DB\"\n      \n      - name: Notarize MacOS ARM64 App\n        if: matrix.os == 'macos-latest' && matrix.arch == 'arm64'\n        run: |\n          xcrun notarytool store-credentials \"Chat2DB\" --apple-id \"${{ secrets.MAC_APPLE_ID }}\" --password \"${{ secrets.MAC_APPLE_PASSWORD }}\" --team-id \"${{ secrets.MAC_TEAM_ID }}\"\n          xcrun notarytool submit chat2db-client/release/Chat2DB-Test-99.0.${{ github.run_id }}-Test-arm64.dmg --keychain-profile \"Chat2DB\"\n    \n          \n      \n\n      # Build Jar\n      - name: Prepare upload for Jar\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        run: |\n          mkdir -p oss_temp_file\n          cp chat2db-client/versions/99.0.${{ github.run_id }}/static/chat2db-server-start.jar   ./oss_temp_file\n          cp -r chat2db-client/release/*.dmg ./oss_temp_file\n          cp -r chat2db-client/versions/99.0.${{ github.run_id }}/dist ./oss_temp_file/dist\n          cd chat2db-client/versions/99.0.${{ github.run_id }}/ && zip -r 99.0.${{ github.run_id }}.zip ./\n          cp -r 99.0.${{ github.run_id }}.zip ../../../oss_temp_file\n          cd static/ && zip -r chat2db-server-start.zip ./ \n          cp -r chat2db-server-start.zip ../../../../oss_temp_file\n\n      # Prepare files to be sent to OSS\n      - name: Prepare upload for OSS\n        run: |\n          mkdir -p oss_temp_file\n          cp -r chat2db-client/release/*${{ matrix.file_extension }} ./oss_temp_file\n\n      # Set up OSS\n      - name: Set up oss utils\n        uses: yizhoumo/setup-ossutil@v1\n        with:\n          endpoint: \"oss-accelerate.aliyuncs.com\"\n          access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}\n          access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}\n          ossutil-version: \"1.7.16\"\n\n      # Upload to OSS\n      - name: Upload to OSS\n        run: |\n          ossutil cp -rf --acl=public-read ./oss_temp_file/ oss://chat2db-client/test/99.0.${{ github.run_id }}/\n\n      # Configure SSH to be uploaded to the server\n      - name: Install ssh key\n        run: |\n          mkdir -p ~/.ssh\n          echo \"${{ secrets.SERVER_DOWNLOAD_SSH_PRIVATE_KEY }}\" > ~/.ssh/id_rsa\n          chmod 600 ~/.ssh/id_rsa\n          ssh-keyscan -t rsa ${{ secrets.SERVER_DOWNLOAD_HOST }} >> ~/.ssh/known_hosts\n          eval `ssh-agent -s`\n          ssh-add ~/.ssh/id_rsa     \n      # upload to server\n      - name: Upload package\n        run: |\n          ssh -t ${{ secrets.SERVER_DOWNLOAD_USERNAME }}@${{ secrets.SERVER_DOWNLOAD_HOST }} \"mkdir -p ${{ secrets.SERVER_DOWNLOAD_PATH }}/test//99.0.${{ github.run_id }}\" \n          scp ./oss_temp_file/* ${{ secrets.SERVER_DOWNLOAD_USERNAME }}@${{ secrets.SERVER_DOWNLOAD_HOST }}:${{ secrets.SERVER_DOWNLOAD_PATH }}/test//99.0.${{ github.run_id }}/ \n              \n\n      # Send to DingTalk\n      - name: Send dingtalk message\n        uses: ghostoy/dingtalk-action@master\n        with:\n          webhook: ${{ secrets.DINGTALK_WEBHOOK }}\n          msgtype: markdown\n          content: |\n            {\n              \"title\": \"${{ matrix.os }}-test-打包完成通知\",\n              \"text\": \"# ${{ matrix.os }}-test-打包完成通知\\n !\\n ###  任务id：[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})\\n ### 下载地址：[${{matrix.file_name}}](${{matrix.file_name}})\"\n            }\n\n      # Send Jar package address to DingTalk\n      - if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        name: Send dingtalk message\n        uses: ghostoy/dingtalk-action@master\n        with:\n          webhook: ${{ secrets.DINGTALK_WEBHOOK }}\n          msgtype: markdown\n          content: |\n            {\n              \"title\": \"Jar-test-构建完成通知\",\n              \"text\": \"### jar包下载地址：[https://download.sqlgpt.cn/test/99.0.${{ github.run_id }}/chat2db-server-start.zip](https://download.sqlgpt.cn/test/99.0.${{ github.run_id }}/chat2db-server-start.zip) \"\n            }\n"
  },
  {
    "path": ".github/workflows/release_test_2.yml",
    "content": "name: Build Test Client 2 \n\non:\n  push:\n    branches:\n    - \"release_test_2\"\n\njobs:\n  release:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: windows-latest\n            file_extension: \".exe\"\n            file_name: \"https://oss.sqlgpt.cn/test/99.0.${{ github.run_id }}/Chat2DB-Test%20Setup%2099.0.${{ github.run_id }}-Test.exe\"\n            build_arg: \"--win --x64\"\n          - os: macos-latest\n            arch: x86_64\n            file_name: \"https://oss.sqlgpt.cn/test/99.0.${{ github.run_id }}/Chat2DB-Test-99.0.${{ github.run_id }}-Test.dmg\"\n            file_extension: \".dmg\"\n            build_arg: \"--mac --x64\"\n          - os: macos-latest\n            arch: arm64\n            file_name: \"https://oss.sqlgpt.cn/test/99.0.${{ github.run_id }}/Chat2DB-Test-99.0.${{ github.run_id }}-Test-arm64.dmg\"\n            file_extension: \".dmg\"\n            build_arg: \"--mac --arm64\"\n          - os: ubuntu-latest\n            file_name: \"https://oss.sqlgpt.cn/test/99.0.${{ github.run_id }}/Chat2DB-Test-99.0.${{ github.run_id }}-Test.AppImage\"\n            file_extension: \".AppImage\"\n            build_arg: \"--linux\"\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Check out git repository\n        uses: actions/checkout@main\n\n      # Install JRE\n      - name: Install JRE\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          java-package: \"jre\"\n          # architecture: ${{ matrix.arch == 'arm64' && 'aarch64' || 'x64' }}\n\n      # Open TLS\n      - name: Enable TLS 1.0 and 1.1 in java.security\n        run: |\n          if [ \"$RUNNER_OS\" = \"Windows\" ]; then\n            sed -i \"s/\\(^jdk.tls.disabledAlgorithms=\\)\\(.*\\)\\(TLSv1, TLSv1.1,\\)\\(.*\\)/\\1\\2\\4/\" \"$JAVA_HOME/conf/security/java.security\"\n          elif [ \"$RUNNER_OS\" = \"Linux\" ]; then\n            sed -i \"s/\\(^jdk.tls.disabledAlgorithms=\\)\\(.*\\)\\(TLSv1, TLSv1.1,\\)\\(.*\\)/\\1\\2\\4/\" \"$JAVA_HOME/conf/security/java.security\"\n          elif [ \"$RUNNER_OS\" = \"macOS\" ]; then\n            sed -i '' \"s/\\(^jdk.tls.disabledAlgorithms=\\)\\(.*\\)\\(TLSv1, TLSv1.1,\\)\\(.*\\)/\\1\\2\\4/\" \"$JAVA_HOME/conf/security/java.security\"\n          fi\n        shell: bash\n        env:\n          RUNNER_OS: ${{ runner.os }}\n          JAVA_HOME: ${{ env.JAVA_HOME }}\n\n      # Copy JRE to the front-end static directory\n      - name: Copy JRE to static directory\n        run: |\n          mkdir -p chat2db-client/static\n          cp -r \"$JAVA_HOME\"/ chat2db-client/static/jre\n          if [ \"${{ runner.os }}\" != \"Windows\" ]; then\n            chmod -R 777 chat2db-client/static/jre\n          fi\n        shell: bash\n        env:\n          JAVA_HOME: ${{ env.JAVA_HOME }}\n\n      # Delete related files in jre in Linux\n      - if: ${{ runner.os == 'Linux' }}\n        name: Delete File on Linux\n        run: |\n          cd chat2db-client/static/jre/\n          ls -la\n          rm -rf legal\n          ls -la\n\n      # Install Node.js\n      - name: Install Node.js\n        uses: actions/setup-node@main\n        with:\n          node-version: \"16\"\n          cache: \"yarn\"\n          cache-dependency-path: chat2db-client/yarn.lock\n\n      # Install Java\n      - name: Install Java and Maven\n        uses: actions/setup-java@main\n        with:\n          java-version: \"17\"\n          distribution: \"temurin\"\n          cache: \"maven\"\n\n      # Packaging web front-end resources\n      - name: Build FE Static\n        run: |\n          cd chat2db-client\n          yarn\n          yarn run build:web:prod --app_version=99.0.${{ github.run_id }} --app_port=10822\n          cp -r dist ../chat2db-server/chat2db-server-start/src/main/resources/static/front\n          cp -r dist/index.html ../chat2db-server/chat2db-server-start/src/main/resources/thymeleaf/\n          cd src/main\n          yarn\n          yarn run build\n\n      # Package backend project & send to frontend\n      - name: Build BE Static\n        run: |\n          mvn clean package -B '-Dmaven.test.skip=true' -f chat2db-server/pom.xml\n          mkdir -p chat2db-client/versions/99.0.${{ github.run_id }}/static\n          echo -n 99.0.${{ github.run_id }} > chat2db-client/version\n          cp -r chat2db-client/version chat2db-client/versions/\n          cp chat2db-server/chat2db-server-start/target/chat2db-server-start.jar chat2db-client/versions/99.0.${{ github.run_id }}/static/\n\n      # Packaging desktop front-end resources\n      - name: Prepare Build Electron\n        run: |\n          cd chat2db-client\n          yarn run build:web:desktop --app_version=99.0.${{ github.run_id }} --app_port=10822\n          cp -r dist ./versions/99.0.${{ github.run_id }}/\n          rm -r dist\n\n      # Packing Electron\n      - name: Build/release Electron app\n        uses: samuelmeuli/action-electron-builder@v1\n        with:\n          package_root: \"chat2db-client/\"\n          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}\n          mac_certs: ${{ secrets.mac_certs }}\n          mac_certs_password: ${{ secrets.mac_certs_password }}\n          skip_build: true\n          args: >\n            -c.appId=com.chat2db.test\n            -c.productName=Chat2DB-Test\n            -c.win.publisherName=Chat2DB-Test\n            -c.nsis.shortcutName=Chat2DB-Test\n            -c.extraMetadata.version=99.0.${{ github.run_id }}-Test\n            ${{ matrix.build_arg}}\n\n      # Notarization & Signature Mac App\n      - name: Notarize MacOS x86_64 App\n        if: matrix.os == 'macos-latest' && matrix.arch == 'x86_64'\n        run: |\n          xcrun notarytool store-credentials \"Chat2DB\" --apple-id \"${{ secrets.MAC_APPLE_ID }}\" --password \"${{ secrets.MAC_APPLE_PASSWORD }}\" --team-id \"${{ secrets.MAC_TEAM_ID }}\"\n          xcrun notarytool submit chat2db-client/release/Chat2DB-Test-99.0.${{ github.run_id }}-Test.dmg --keychain-profile \"Chat2DB\"\n\n      # Build Jar\n      - name: Prepare upload for Jar\n        if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        run: |\n          mkdir -p oss_temp_file\n          cp chat2db-client/versions/99.0.${{ github.run_id }}/static/chat2db-server-start.jar   ./oss_temp_file\n          cp -r chat2db-client/release/*.dmg ./oss_temp_file\n          cp -r chat2db-client/versions/99.0.${{ github.run_id }}/dist ./oss_temp_file/dist\n          cd chat2db-client/versions/99.0.${{ github.run_id }}/ && zip -r 99.0.${{ github.run_id }}.zip ./\n          cp -r 99.0.${{ github.run_id }}.zip ../../../oss_temp_file\n          cd static/ && zip -r chat2db-server-start.zip ./ \n          cp -r chat2db-server-start.zip ../../../../oss_temp_file\n\n      # Prepare files to be sent to OSS\n      - name: Prepare upload for OSS\n        run: |\n          mkdir -p oss_temp_file\n          cp -r chat2db-client/release/*${{ matrix.file_extension }} ./oss_temp_file\n\n      # Set up OSS\n      - name: Set up oss utils\n        uses: yizhoumo/setup-ossutil@v1\n        with:\n          endpoint: \"oss-accelerate.aliyuncs.com\"\n          access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}\n          access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}\n          ossutil-version: \"1.7.16\"\n\n      # Upload to OSS\n      - name: Upload to OSS\n        run: |\n          ossutil cp -rf --acl=public-read ./oss_temp_file/ oss://chat2db-client/test/99.0.${{ github.run_id }}/\n\n      # Send to DingTalk\n      - name: Send dingtalk message\n        uses: ghostoy/dingtalk-action@master\n        with:\n          webhook: ${{ secrets.DINGTALK_WEBHOOK }}\n          msgtype: markdown\n          content: |\n            {\n              \"title\": \"${{ matrix.os }}-test-打包完成通知\",\n              \"text\": \"# ${{ matrix.os }}-test-打包完成通知\\n ![bang](https://oss.sqlgpt.cn/static/bang100.gif)\\n ###  任务id：[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})\\n ### 下载地址：[${{matrix.file_name}}](${{matrix.file_name}})\"\n            }\n\n      # Send Jar address to DingTalk\n      - if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}\n        name: Send dingtalk message\n        uses: ghostoy/dingtalk-action@master\n        with:\n          webhook: ${{ secrets.DINGTALK_WEBHOOK }}\n          msgtype: markdown\n          content: |\n            {\n              \"title\": \"Jar-test-构建完成通知\",\n              \"text\": \"### jar包下载地址：[https://oss.sqlgpt.cn/test/99.0.${{ github.run_id }}/chat2db-server-start.zip](https://oss.sqlgpt.cn/test/99.0.${{ github.run_id }}/chat2db-server-start.zip) \"\n            }\n"
  },
  {
    "path": ".gitignore",
    "content": "### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n.out\n\n### Visual Studio Code ###\npackage-lock.json\n\n### Mac\n.DS_Store\n/chat2db-server/ali-dbhub-server-start/src/main/resources/static/front/*\n/chat2db-server/ali-dbhub-server-start/src/main/resources/thymeleaf\n\n### docker 数据不用上传\n/docker/redis/data/*\n/docker/test/reds/*\n/docker/test/mongodb/*\n/chat2db-server/ali-dbhub-server-domain/ali-dbhub-server-domain-support/src/main/resources/lib/*\n/chat2db-server/ali-dbhub-server-domain/ali-dbhub-server-domain-support/lib/*\n/lib\n/out/*"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"cSpell.words\": [\n    \"aarch\",\n    \"ahooks\",\n    \"alicdn\",\n    \"aliyuncs\",\n    \"altool\",\n    \"andale\",\n    \"antd\",\n    \"asar\",\n    \"AZUREAI\",\n    \"bgcolor\",\n    \"blockmap\",\n    \"cascader\",\n    \"charsets\",\n    \"chmod\",\n    \"CLOB\",\n    \"Consolas\",\n    \"Datas\",\n    \"datasource\",\n    \"Datetime\",\n    \"DBAI\",\n    \"DBHUB\",\n    \"Dchat\",\n    \"dingtalk\",\n    \"Dloader\",\n    \"Dmaven\",\n    \"Dproject\",\n    \"Dserver\",\n    \"Dspring\",\n    \"echart\",\n    \"echarts\",\n    \"favicons\",\n    \"fulltext\",\n    \"ghostoy\",\n    \"hljs\",\n    \"iconfont\",\n    \"indexs\",\n    \"jdbc\",\n    \"kingbase\",\n    \"lucida\",\n    \"macos\",\n    \"Mddhhmmss\",\n    \"Menlo\",\n    \"mkdir\",\n    \"monaco\",\n    \"msgtype\",\n    \"Navciat\",\n    \"Navicat\",\n    \"nsis\",\n    \"OPENAI\",\n    \"ossutil\",\n    \"partialize\",\n    \"pgsql\",\n    \"plsql\",\n    \"pnpm\",\n    \"Popconfirm\",\n    \"Prec\",\n    \"quickinput\",\n    \"redownload\",\n    \"remaininguses\",\n    \"RESTAI\",\n    \"samuelmeuli\",\n    \"scroller\",\n    \"Sercurity\",\n    \"singlestoredb\",\n    \"sortablejs\",\n    \"SQLSERVER\",\n    \"temurin\",\n    \"thymeleaf\",\n    \"Tigger\",\n    \"togglefullscreen\",\n    \"transactsql\",\n    \"trino\",\n    \"umijs\",\n    \"umirc\",\n    \"USERANDPASSWORD\",\n    \"uuidv\",\n    \"VARCHAR\",\n    \"VIEWCOLUMN\",\n    \"VIEWCOLUMNS\",\n    \"wechat\",\n    \"wireframe\",\n    \"Wppk\",\n    \"xcrun\",\n    \"yapi\",\n    \"yizhoumo\",\n    \"zustand\"\n  ],\n  \"java.compile.nullAnalysis.mode\": \"automatic\"\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## 3.1.19\n\n`2024-1-3`\n\n**Changelog**\n\n- ⭐【New Features】Open table supports filtering sort\n- ⚡️【Optimize】Optimize startup speed\n- 🐞【Fixed】SQL error generated by modifying the table structure\n- 🐞【Fixed】The problem of direct query table data error due to characters\n- 🐞【Fixed】null point error\n\n\n## 3.1.18\n\n`2023-12-28`\n\n**Changelog**\n\n- 🐞【Fixed】 docker startup error\n\n## 3.1.17\n\n`2023-12-27`\n\n**Changelog**\n\n- ⭐【New Features】Connection replicable\n- ⚡️【Optimize】Select the table to view the DDL of the table in Information on the right expansion bar\n- 🐞【Fixed】 Modifying table sorting does not generate sorting SQL problems\n- 🐞【Fixed】Home connection cannot switch pages\n- 🐞【Fixed】Columns cannot be viewed or edited in some tables of the Oracle database\n\n## 3.1.16\n\n`2023-12-25`\n\n**Changelog**\n\n- ⭐【New Features】Execute the Record sql Add replication button\n- ⭐【New Features】Execution records can be opened directly in the console\n- ⭐【New Features】The SQL column on the right of execution record and save can be expanded and dragged\n- 🐞【Fixed】Some databases could not display the Database/Schema structure\n- 🐞【Fixed】During web deployment, a page error occurs after the page switching page is refreshed\n- ⚡️【Optimize】The query result cancels in-cell scrolling and instead uses hover to view cell contents\n\n## 3.1.15\n\n`2023-12-20`\n\n**Changelog**\n\n- 🐞【Fixed】Every time I open the application, I occasionally cannot select the database problem\n- 🐞【Fixed】Compatible with old data types can not show deletion problems\n- 🐞【Fixed】Some databases cannot display the database /Schema structure\n\n## 3.1.14\n\n`2023-12-17`\n\n**Changelog**\n\n- 🐞【Fixed】Tree structure search for bugs\n- 🐞【Fixed】Switching tab causes edit data reset problem\n- 🐞【Fixed】Rename is reset after switching tab\n\n\n## 3.1.12\n\n`2023-12-15`\n\n**Changelog**\n\n- ⚡️【Optimize】Optimized tree structure search\n- ⚡️【Optimize】Tree structure search box resident\n\n## 3.1.11\n\n`2023-12-13`\n\n**Changelog**\n\n- 🐞【Fixed】A chart with a Schema cannot be saved and executed\n- 🐞【Fixed】Failure to start after the upgrade\n\n## 3.1.1\n\n`2023-12-13`\n\n**Changelog**\n\n- 🐞【Fixed】Table blank problem when switching Tabs\n- 🐞【Fixed】DM or Oracle cannot display Schema\n- 🐞【Fixed】The import connection is lost. Procedure\n\n## 3.1.0\n\n`2023-12-12`\n\n**Changelog**\n\n- 🔥🔥【Optimize】The first startup time has been increased by 65%\n- 🔥🔥【Optimize】Changed the structure of the left tree \n- 🔥🔥【Optimize】Optimized the tab switchover problem \n- ⚡️ 【Optimize】All nodes are supported. The name of each node can be copied \n- ⚡️【Optimize】The sql console input box supports switching databases, and will not change when the left database is switched \n- ⭐ 【New Features】Save records moved to the right toolbar, you can directly modify the name in the list \n- ⭐【New Features】Support mongoDB \n- ⭐【New Features】Support for viewing all tables\n\n## 3.0.14\n\n`2023-11-20`\n\n**Changelog**\n\n- 🐞【Fixed】Team paging problem\n- 🐞【Fixed】Oracle service name bug\n- 🐞【Fixed】Oracle datatype error\n- 🐞【Fixed】Fixed an issue where MySQL changed table structure without displaying comments.\n- ⚡️【Optimize】Support database or schema\n- 【Developer】Friends don't worry, the company has some things recently, and is preparing 3.1.0, be patient\n\n## 3.0.13\n\n`2023-11-15`\n\n**Changelog**\n\n- 🐞【Fixed】oracle datatype error\n- 🐞【Fixed】DM index error\n\n\n## 3.0.12\n\n`2023-11-13`\n\n**Changelog**\n\n- 🐞【Fixed】Copy as insert first row lost problem\n- 🐞【Fixed】DM database index bug\n- 🐞【Fixed】Point Garbled code problem\n- 🐞【Fixed】MariaDB connec database bug\n- 🐞【Fixed】Issues 792 NullPointerException\n- 🐞【Fixed】Kingbase8r6 error\n\n\n## 3.0.11\n\n`2023-11-08`\n\n**Changelog**\n\n- ⭐【New Features】Oracle connections support the Service name mode\n- ⭐【New Features】【New function】 Edit table data to support batch copy, clone, delete (click 1X1 cell to select/cancel, hold down shift/ctrl/cmd to select multiple)\n- ⚡️【Optimize】After the update is completed, click restart to close the problem that cannot be automatically opened (hot update cannot fix this problem, you need to download a new version to cover the client)\n- 🐞【Fixed】database and schema searches support case ambiguity matching\n- 🐞【Fixed】Where database was not displayed after being added\n- 🐞【Fixed】sql formatting to ·now()· format error\n\n\n## 3.0.10\n\n`2023-11-06`\n\n**Changelog**\n- ⭐【New Features】Add multiple CN AI configurations Add multiple domestic AI configurations\n  - Supports single-row replication of Insert, Update, table header fields, and row data \n  - Clone the selected row \n  - Replication of cell data is supported \n  - You can set the cell to Null or Default \n  - Row deletion is supported\n  - Supports zooming in to view or modify data\n- ⭐【New Features】Supports the ctrl/cmd+c shortcut to copy row data or cell data\n- ⭐【New Features】Supports the shortcut key ctrl/cmd+v to paste and copy row data/cell data to row/cell\n- ⭐【New Features】Edit table structure supports setting primary keys in columns\n- ⭐【New Features】History is added to the foldable panel on the right\n- ⭐【New Features】Edit data to support cell-level undo changes\n- ⭐【New Features】The Table tree node operation menu on the left supports copying table, field, key, index, and function names\n- ⭐【New Features】The node in the left Table tree supports ctrl/cmd+c to copy the node text\n- ⭐【New Features】You can right-click to close tabs, close other tabs, or close all tabs\n- ⭐【New Features】Top database and schema support search\n- ⚡️【Optimize】Smart prompts for SQL editing\n- ⚡️【Optimize】Edit the table structure to add loading\n- ⚡️【Optimize】The tree node operation menu supports right-clicking\n- 🐞【Fixed】Fixed table structure editing floating-point decimal Settings display exception\n- 🐞【Fixed】Fixed switching the saved sql on the console will eliminate the problem\n- 🐞【Fixed】After multiple tables are paged, the context cannot select a table other than the current page\n- 🐞【Fixed】Console and resulting Tabs mouse wheel not scrolling\n\n## 3.0.9\n\n`2023-11-01`\n\n**Changelog**\n- ⭐【New Features】Query results can be refreshed\n- ⚡️【Optimize】Console Tabs adaptive width\n- 🐞【Fixed】console save bug\n- 🐞【Fixed】sqlite can only retrieve one piece of data\n\n## 3.0.5\n\n`2023-10-23`\n\n**Changelog**\n- ⭐【New Features】Supports visual database creation\n- ⭐【New Features】Support hot update\n- ⭐【New Features】Double-click the table to open it directly\n- ⚡️【Optimize】The search table supports size fuzzy matching\n- ⚡️【Optimize】Sort Database and Schema at the top\n- ⚡️【Optimize】The queried data supports editing and modification in the large popup window of the view\n- ⚡️【Optimize】Example Query the page loading effect of data\n- ⚡️【Optimize】Keep the top focused tab always in the viewable area\n- ⚡️【Optimize】Query data cell does not have scroll bar problem\n\n## 3.0.4\n\n`2023-10-20`\n\n**Changelog**\n- 🐞【Fixed】Bugs are displayed when more than 100 data items are queried\n\n## 3.0.1\n\n`2023-10-19`\n\n**Changelog**\n- ⚡️【Optimize】Search result scroll bar\n- ⚡️【Fixed】Oracle update result data bug\n\n## 3.0.0\n\n`2023-10-17`\n\n**Changelog**\n- 🔥【New Features】Support for team collaboration mode\n- 🔥【New Features】Support for visual table structure creation, editing, and deletion\n- 🔥【New Features】Support for editing, adding, and deleting query data results\n- ⭐【New Features】Support the feature of importing Navicat/DBever data source links\n- ⭐【New Features】Support for AI automatic sync table structure。\n- ⭐【New Features】Support export table structure\n- ⭐【New Features】Support importing SQL files\n- ⭐【New Features】Support the connection supports adding an environment,better distinguishing between online and daily\n- ⚡️【Optimize】Optimize Editor Intellisense\n- ⚡️【Optimize】Optimize AI Input\n- ⚡️【Optimize】Sql query support is stopped\n- ⚡️【Optimize】Sql execution supports viewing the number of affected rows\n- ⚡️【Optimize】Reclaiming non-administrator permissions to edit shared connections\n- ⚡️【Optimize】`Cmd/Ctrl + R` Run SQL， `Cmd/Ctrl + Shift + R` Refresh Page\n- 🐞【Fixed】Table operation columns are overridden by table comments\n- 🐞【Fixed】The last Tab in the query result cannot be closed\n\n## 2.1.0\n\n## ⭐ New Features\n\n- 🔥The team function is newly launched, supporting team collaboration. R&D does not require knowing the online database\n  password, solving the security issue of enterprise database accounts. It is recommended to directly deploy the team\n  function using 'docker'\n- Added support for environment selection, better distinguishing between online and daily\n\n## 2.0.14\n\n## 🐞 Bug Fixes\n\n- Fix the issue of 'Oracle' query 'Blob' reporting errors\n- Modify the paging logic and fix some SQL queries that cannot be queried\n\n## 2.0.13\n\n## ⭐ New Features\n\n## 🐞 Bug Fixes\n\n- Fixed a bug where sql formatting was not selected\n- Fixed open view lag issue\n- Solve the white screen problem of connected non-relational databases (non-relational databases are not supported)\n\n## 2.0.12\n\n## ⭐ New Features\n\n- 🔥Supports viewing views, functions, triggers, and procedures\n- Support selected sql formatting\n- Added new dark themes\n\n## 🐞 Bug Fixes\n\n- Fixed sql formatting failure issue\n- Fixed an issue where locally stored theme colors and background colors are incompatible with the new version, causing\n  page crashes\n- Logs desensitize sensitive data\n- Fix the issue of 'CLOB' not displaying specific content 【Issue #440】(https://github.com/chat2db/Chat2DB/issues/440)\n- Fix the problem that non-Select does not display query results\n- Fix the problem that Oracle cannot query without schema\n- Fix the problem of special type of SQL execution error reporting\n- Fix the problem that the test link is successful, but the error is reported when saving the link\n\n## 2.0.11\n\n## 🐞 Bug Fixes\n\n- Fix the issue where SSH does not support older versions of encryption algorithms\n- Fix the issue of SQL Server 2008 not being able to connect\n- Fix the issue of not being able to view table name notes and field notes\n\n## 2.0.10\n\n## 🐞 Bug Fixes\n\n- Activate the console for the latest operation when you create or start a console、Records the last console used\n- The replication function of the browser, such as edge, is unavailable\n- table Indicates an error when ddl is exported after the search\n- Adds table comments and column field types and comments\n\n## 2.0.9\n\n## 🐞 Bug Fixes\n\n-Fix the issue of Windows flash back\n\n## 2.0.8\n\n## 🐞 Bug Fixes\n\n- Repair the Scientific notation in some databases 【Issue #378】(https://github.com/chat2db/Chat2DB/issues/378)\n- Fix some cases where data is not displayed\n\n## 🐞 问题修复\n\n- 修复部分数据库出现科学计数法的情况 【Issue #378】(https://github.com/chat2db/Chat2DB/issues/378)\n- 修复部分情况数据不展示\n\n## 2.0.7\n\n## ⭐ New Features\n\n- Export query result as file is supported\n\n## 🐞 Bug Fixes\n\n- Fixed ai config issues 【Issue #346】(https://github.com/chat2db/Chat2DB/issues/346)\n\n## 2.0.6\n\n## 🐞 Bug Fixes\n\n- Fixed: When there are too many tables under the selected library, the \"New Console\" button at the bottom\n  disappears 【Issue #314】(https://github.com/chat2db/Chat2DB/issues/314)\n\n## 2.0.5\n\n## ⭐ New Features\n\n- Supports 25 free uses of AIGC every day.\n- Support for querying data pagination.\n- Support switching between multiple databases in PostgreSQL.\n- Support for hot updating of client-side code allows for rapid bug fixes.\n\n## 🐞 Bug Fixes\n\n- Default return alias for returned results 【Issue #270】(https://github.com/chat2db/Chat2DB/issues/270)\n- Fixed around 100 bugs, of course, many were repetitive bugs.\n\n## 2.0.4\n\n## ⭐ New Features\n\n- Support DB2 database\n- Support renaming after console saving\n- Support prompts during SQL execution\n\n## 🐞 Bug Fixes\n\n- Fix the bug that the database in sqlserver is all numbers\n- Fix ssh connection bug\n\n## 2.0.2\n\n## ⭐ New Features\n\n- Brand new AI binding process\n- Support for custom drivers\n\n## 🐞 Bug Fixes\n\n- Optimized dataSource link editing\n- Enhanced error messages\n- Improved table selection interaction\n- Enhanced table experience\n\n## 2.0.1\n\n## 🐞 Bug Fixes\n\n- Fix bug where executing multiple SQL statements at once will prompt for exceptions\n- Fix getJDBCDriver error: null 【Issue #123】(https://github.com/chat2db/Chat2DB/issues/123)\n- Fixing the Hive connection and then viewing columns results in an\n  error. 【Issue #136】(https://github.com/chat2db/Chat2DB/issues/136)\n\n\n## 2.0.0\n\n## What's Changed\n\n- 🔥An intelligent solution that perfectly integrates SQL queries, AI assistant, and data analysis.\n- 🔥New focused mode experience for advanced datasource management.\n- AI integration of more LLM.\n- Bilingual in Chinese and English support for client.\n\n## 1.0.11\n\n- fixed: SQL 有特殊字符时 AI 功能无法正常使用\n- 增减版本信息检测\n\n## 1.0.10\n\n- fixed: The formatted SQL is abnormal \n- Optimized AI network connection exception message \n- Custom AI Adds a local example \n- Support OceanBase Presto DB2 Redis MongoDB Hive KingBase\n\n## 1.0.9\n\n- Fixed an issue where Open Ai could not connect \n \n- Support domestic Dameng database \n- Supports custom OPEN AI API_HOST \n- 🔥 Supports custom AI interfaces \n- Support theme color following system\n\n## 1.0.6\n\n- Fixed Oracle database character set issues \n- Fix mac installation prompts for security issues\n\n## 1.0.5\n\n- 🔥 Optimizes the boot speed of Apple chips \n- Rectify database connection problems on Windows \n- The database modification does not take effect \n- NullPointerException\n\n## 1.0.4\n\n- Fix ClickHouse jdbc issues \n- Restore the NPE managed by the connection pool \n- Fixed front-end edit data source error \n- Added default database properties\n\n## 1.0.3\n\n- 🔥 Supports SSH connection to the database \n- 🎉 Allows a client to view logs \n- 🎉 Supports chat sessions on the Console \n- Supports setting OPENAI agents on clients \n- An application that has been started will not be started again\n\n## 1.0.1\n\n- Fixed oracle connection configuration editing and connection query issues \n- Fix possible risks of Apikey output to logs \n- Fixed the login bug of web version\n\n## 1.0.0\n\n- Fixed oracle connection configuration editing and connection query issues \n- Fix possible risks of Apikey output to logs \n- repair bugChat2DB login web version 1.0.0 release come 🎉 🎉 🎉 🎉 🎉 🎉 🎉 🎉 🎉 \n \n- 🌈 AI intelligent assistant, supports natural language to SQL, SQL to natural language, and SQL optimization suggestions \n- 👭 Support team collaboration, R & D does not need to know the online database password, to solve the security problem of enterprise database account \n- ⚙️ Provides powerful data management capabilities, including data tables, views, stored procedures, functions, triggers, indexes, sequences, users, roles, and authorization \n- 🔌 Powerful expansion ability, currently supports Mysql, PostgreSQL, Oracle, SQLServer, ClickHouse, Oceanbase, H2, SQLite and so on, the future will support more databases \n- 🛡 The front-end is developed using Electron, providing an integrated solution of Windows, Mac, Linux clients, and web versions \n- 🎁 Supports environment isolation and separation of online and daily data rights\n\n\n## 0.0.0\n\n`2023--`\n\n**Changelog**\n- ⭐【New Features】\n- ⚡️【Optimize】\n- 🐞【Fixed】\n\n\n\n\n"
  },
  {
    "path": "CHANGELOG_CN.md",
    "content": "## 3.1.19\n\n`2024-1-3`\n\n**更新日志**\n\n- ⭐【新功能】打开表支持筛选排序\n- ⚡️【优化】优化启动速度\n- 🐞【修复】修改表结构生成SQL错误\n- 🐞【修复】直接查询表因字符导致数据错误的问题\n- 🐞【修复】null point error\n\n## 3.1.18\n\n`2023-12-28`\n\n**更新日志**\n\n- 🐞【修复】docker启动报错问题\n\n## 3.1.17\n\n`2023-12-27`\n\n**更新日志**\n\n- ⭐【新功能】连接可复制\n- ⚡️【优化】选中表可直接在右侧拓展栏的“信息”中查看表DDL\n- 🐞【修复】修改表排序无法生成排序SQL问题\n- 🐞【修复】Oracle数据库部分表无法查看列、无法编辑问题\n- 🐞【修复】团队管理归属连接无法切换分页\n\n\n## 3.1.16\n\n`2023-12-25`\n\n**更新日志**\n\n- ⭐【新功能】执行记录添加复制sql按钮\n- ⭐【新功能】执行记录可直接在控制台打开\n- ⭐【新功能】右侧执行记录、保存SQL栏展开大小可拖动\n- 🐞【修复】部分数据库无法显示数据库/Schema结构的问题\n- 🐞【修复】网页部署时，切换页面刷新后页面报错问题\n- ⚡️【优化】查询结果取消单元格内滚动，改为hover查看单元格内容\n\n## 3.1.15\n\n`2023-12-20`\n\n**更新日志**\n\n- 🐞【修复】每次打开应用时，偶现无法选择数据库问题\n- 🐞【修复】兼容老数据类型无法显示删除问题\n\n## 3.1.14\n\n`2023-12-17`\n\n**更新日志**\n\n- 🐞【修复】树结构搜索bug\n- 🐞【修复】切换tab导致编辑数据重置问题\n- 🐞【修复】切换tab后重命名被重置\n\n\n## 3.1.12\n\n`2023-12-15`\n\n**更新日志**\n\n- ⚡️【优化】优化树结构搜索\n- ⚡️【优化】树结构搜索框常驻\n\n## 3.1.11\n\n`2023-12-13`\n\n**更新日志**\n\n- 🐞【修复】带Schema的图表无法保存和执行\n- 🐞【修复】升级后无法启动问题\n\n## 3.1.10\n\n`2023-12-13`\n\n**更新日志**\n\n- 🐞【修复】切换Tabs时，表空白问题\n- 🐞【修复】DM、Oracle无法显示Schema问题\n- 🐞【修复】导入连接丢失问题\n\n## 3.1.0\n\n`2023-12-12`\n\n**更新日志**\n\n- 🔥🔥【优化】首次启动时间时间提升了65%\n- 🔥🔥【优化】更改了左侧树的结构\n- 🔥🔥【优化】优化切换tab卡顿问题\n- ⚡️【优化】所有节点都支持选中，可以复制每个节点的名称\n- ⚡️【优化】sql的console输入框支持切换数据库，不会在跟着左侧数据库的切换而改变了\n- ⭐【新功能】保存记录移动到了右侧工具栏，可以直接在列表里修改名称\n- ⭐【新功能】支持mongoDB\n- ⭐【新功能】支持查看所有表\n\n## 3.0.14\n\n`2023-11-20`\n\n**更新日志**\n\n- 🐞【修复】团队分页问题\n- 🐞【修复】Oracle服务名称错误\n- 🐞【修复】Oracle数据类型错误\n- 🐞【修复】修复MySQL修改表结构，不回显注释的问题。\n- ⚡️【优化】支持数据库或模式\n- 【开发者】友友们不要着急呀，最近公司有些事情，并且在准备3.1.0，耐心等待哦\n\n## 3.0.13\n\n`2023-11-15`\n\n**更新日志**\n\n- 🐞【修复】oracle datatype 错误\n- 🐞【修复】DM index 错误\n\n## 3.0.12\n\n`2023-11-13`\n\n**更新日志**\n\n- 🐞【修复】复制为insert第一行丢失问题\n- 🐞【修复】达梦数据库index问题\n- 🐞【修复】Point 乱码问题\n- 🐞【修复】MariaDB连接数据库错误\n- 🐞【修复】#792 NullPointerException\n- 🐞【修复】Kingbase8r6 错误\n\n\n## 3.0.11\n\n`2023-11-10`\n\n**更新日志**\n\n- ⭐【新功能】Oracle 连接支持 Service name 方式\n- ⭐【新功能】编辑表数据支持批量复制、克隆、删除(点击1X1单元格全选/取消，按住shift/ctrl/cmd多选)\n- ⚡️【优化】更新完成后点击重启关闭后无法自动打开问题(热更新无法修复该问题，需要下载新版版本覆盖客户端)\n- 🐞【修复】database和schema搜索支持大小写模糊匹配\n- 🐞【修复】添加database后不显示问题\n- 🐞【修复】sql格式化对·now()·格式错误问题\n\n\n## 3.0.10\n\n`2023-11-06`\n\n**更新日志**\n\n- ⭐【新功能】增加多个国内 AI 配置\n- ⭐【新功能】编辑数据支持右键操作\n  - 支持单行复制 Insert、Update、表头字段、行数据\n  - 支持克隆选中行\n  - 支持复制单元格数据\n  - 支持设置单元格为Null和Default\n  - 支持删除行\n  - 支持放大查看或修改数据\n- ⭐【新功能】支持快捷键ctrl/cmd+c 复制行数据/单元格数据\n- ⭐【新功能】支持快捷键ctrl/cmd+v 粘贴复制行数据/单元格数据到行/单元格\n- ⭐【新功能】编辑表结构支持在列中设置主键\n- ⭐【新功能】编辑数据支持单元格级别撤销修改\n- ⭐【新功能】左侧Table树节点操作菜单支持复制表、字段、key、index、函数等名称\n- ⭐【新功能】左侧Table树节点支持ctrl/cmd+c 复制节点文本\n- ⭐【新功能】右侧可折叠面板中增加历史记录\n- ⭐【新功能】支持右键关闭tab/关闭其他tab/关闭所有tab\n- ⭐【新功能】顶部database和schema支持搜索\n- ⚡️【优化】SQL 编辑时的智能提示\n- ⚡️【优化】编辑表结构添加loading\n- ⚡️【优化】树节点操作菜单支持右键唤出\n- 🐞【修复】修复表结构编辑浮点数小数位设置显示异常\n- 🐞【修复】修复切换控制台保存的sql会消失问题\n- 🐞【修复】表多的分页后，上下文选不到当前分页以外的表\n- 🐞【修复】console和结果的Tabs鼠标滚轮无法滚动的问题\n\n\n\n## 3.0.9\n\n`2023-11-01`\n\n**更新日志**\n- ⭐【新功能】查询结果支持刷新\n- ⚡️【优化】控制台Tabs自适应宽度\n- 🐞【修复】console保存bug\n- 🐞【修复】sqlite只能查到一条数据问题\n\n## 3.0.5\n\n`2023-10-23`\n\n**更新日志**\n- ⭐【新功能】支持可视化创建数据库\n- ⭐【新功能】支持热更新\n- ⭐【新功能】双击表直接打开表\n- ⚡️【优化】搜索表支持大小模糊匹配\n- ⚡️【优化】Database 和 Schema 排序\n- ⚡️【优化】查询的数据支持在查看的大的弹窗中编辑修改\n- ⚡️【优化】查询数据翻页loading效果\n- ⚡️【优化】保持顶部聚焦的tab永远在可视区域内\n- ⚡️【优化】查询数据单元格没有滚动条问题\n\n\n## 3.0.4\n\n`2023-10-20`\n\n**更新日志**\n- 🐞【修复】查询数据超过100条时显示bug\n\n## 3.0.1\n\n`2023-10-19`\n\n**更新日志**\n- ⚡️【优化】查询结果滚动条\n- 🐞【修复】Oracle更新结果数据错误\n\n## 3.0.0\n\n`2023-10-17`\n\n**更新日志**\n- 🔥【新功能】支持团队协作模式\n- 🔥【新功能】支持可视化表结构新增、编辑、删除\n- 🔥【新功能】支持查询数据结果编辑、新增、删除\n- ⭐【新功能】支持导入Navicat/DBeaver数据源链接的功能\n- ⭐【新功能】支持AI自动同步表结构\n- ⭐【新功能】支持导出表结构\n- ⭐【新功能】支持导入sql文件\n- ⭐【新功能】连接支持添加环境标识，更好地区分在线和日常\n- ⚡️【优化】优化编辑器提示功能\n- ⚡️【优化】优化AI输入\n- ⚡️【优化】sql查询支持停止\n- ⚡️【优化】sql执行支持查看影响行数\n- ⚡️【优化】回收非管理员编辑共享连接权限\n- ⚡️【优化】`Cmd/Ctrl + R` 运行SQL， `Cmd/Ctrl + Shift + R` 刷新页面\n- 🐞【修复】表操作列被表注释覆盖问题\n- 🐞【修复】查询结果最后一个Tab无法关闭问题\n\n## 2.1.0\n\n## ⭐ 新特性\n\n-🔥 新推出团队功能，支持团队协作。研发不需要知道在线数据库\n密码，解决企业数据库帐号的安全问题。建议直接部署团队\n使用'docker'的函数 -增加了环境选择的支持，更好地区分在线和日常\n\n## 2.0.14\n\n## ⭐ 新特性\n\n- 🔥 团队功能全新上线，支持团队协作，研发无需知道线上数据库密码，解决企业数据库账号安全问题,团队功能建议直接使用 `docker` 部署\n- 新增支持环境选择，更好的区分线上、日常环境\n\n## 🐞 问题修复\n\n- 修复 `Oracle` 查询 `Blob` 报错的问题\n- 修改分页逻辑，修复部分 SQL 无法查询\n\n\n## 2.0.13\n\n## 🐞 问题修复\n\n- 修复不选中 sql 格式化的 bug\n- 修复打开视图卡顿问题\n- 解决已连接的非关系型数据库打开白屏问题（暂不支持非关系性数据库）\n\n## 2.0.12\n\n## ⭐ 新特性\n\n- 🔥 支持查看视图、函数、触发器、存储过程\n- 支持选中 sql 格式化\n- 增加新的暗色主题\n\n## 🐞 问题修复\n\n- 修复 sql 格式化会失败问题\n- 修复本地存储的主题色、背景色与新版本不兼容时会导致页面崩溃问题\n- 日志对敏感数据进行脱敏\n- 修复 `CLOB` 不展示具体内容的问题 [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)\n- 修复非 Select 不展示查询结果的问题\n- 修复 Oracle 不带 schema 无法查询的问题\n- 修复特殊类型的 SQL 执行报错的问题\n- 修复测试链接成功，但保存链接报错的问题\n\n## 2.0.11\n\n## 🐞 问题修复\n\n- 修复 SSH 不支持老版本加密算法的问题\n- 修复 SQLServer2008 无法连接的问题\n- 修复无法查看表名备注、字段备注的问题\n\n## 2.0.10\n\n## 🐞 问题修复\n\n- 新建、开打 console 时激活最新操作的 console、记录最后一次使用的 console\n- edge 等浏览器复制功能无法正常使用\n- table 搜索后导出 ddl 报错\n- 增加表注释以及列字段类型和注释\n- 当数据源添加了 database 默认选择第一个 database\n\n## 2.0.9\n\n## 🐞 问题修复\n\n- 修复 windows 闪退的问题\n\n## 2.0.8\n\n## 🐞 问题修复\n\n- 修复部分数据库出现科学计数法的情况 [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)\n- 修复部分情况数据不展示\n\n## 2.0.7\n\n## ⭐ 新特性\n\n- 支持导出查询结果\n\n## 🐞 问题修复\n\n- 修复 ai 配置 [Issue #346](https://github.com/chat2db/Chat2DB/issues/346)\n\n## 2.0.6\n\n## 🐞 问题修复\n\n- Fixed: 当选择的库下面表过多时最下面的“新建控制台”按钮消失 [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)\n\n## 2.0.5\n\n## ⭐ 新特性\n\n- 支持每天 25 次免费使用 AIGC\n- 支持查询数据分页\n- 支持 PostgreSQL 数据库多个 database 的切换\n- 支持客户端代码热更新可以快速修复 bug\n- 支持客户端字体放大缩小\n\n## 🐞 问题修复\n\n- 返回结果默认返回别名 [Issue #270](https://github.com/chat2db/Chat2DB/issues/270)\n- 修复了 100 个左右的 bug，当然很多是重复 bug\n\n## 2.0.4\n\n## ⭐ 新特性\n\n- 支持 DB2 数据库\n- 支持控制台保存后重命名\n- 支持 SQL 执行中提示\n\n## 🐞 问题修复\n\n- 修复 sqlserver 中 database 全是数字的 bug\n- 修复 ssh 连接 bug\n\n## 2.0.2\n\n## ⭐ 新特性\n\n- 全新的 AI 绑定流程\n- 支持自定义驱动\n\n## 🐞 问题修复\n\n- 优化 dataSource 链接编辑\n- 优化错误提示\n- 优化选表交互\n- 优化表格体验\n\n## 2.0.1\n\n## 🐞 问题修复\n\n- 修复一次性执行多条 SQL 会提示异常的 BUG\n- 修复 getJDBCDriver error: null [Issue #123](https://github.com/chat2db/Chat2DB/issues/123)\n- 修复 hive 方式连接，然后查看 columns 报错 [Issue #136](https://github.com/chat2db/Chat2DB/issues/136)\n\n## 2.0.0\n\n## 更新内容\n\n- 🔥SQL 查询、AI 查询和数据报表完美集成的一体化解决方案设计与实现\n- 🔥 数据源连接和管理进阶为专注模式的全新体验设计与实现\n- 🔥AI 对话 SQL 升级为极简模式的全新交互设计与实现\n- 客户端 AI 体验重大升级，响应更多用户的诉求\n- 集成更多 AI 模型\n- 客户端双语支持\n- SQL 查询基础功能优化\n- Issue 问题修复\n\n## 1.0.11\n\n- fixed: SQL 有特殊字符时 AI 功能无法正常使用\n- 增减版本信息检测\n\n## 1.0.10\n\n- fixed: 格式化 SQL 异常\n- 优化 AI 网络连接异常提示\n- 自定义 AI 添加本地样例\n- Support OceanBase Presto DB2 Redis MongoDB Hive KingBase\n\n## 1.0.9\n\n- 修复 Open Ai 无法连接的问题\n\n- 支持国产达梦数据库\n- 支持自定义 OPEN AI API_HOST\n- 🔥 支持自定义 AI 接口\n- 支持主题色跟随系统\n\n## 1.0.6\n\n- 修复 Oracle 数据库字符集问题\n- 修复 mac 安装提示的安全问题\n\n## 1.0.5\n\n- 🔥 优化 Apple 芯片的启动速度\n- 修复 Windows 端数据库连接问题\n- 修改 database 不生效\n- NullPointerException\n\n## 1.0.4\n\n- 修复 ClickHouse jdbc 问题\n- 修复连接池管理的 NPE\n- 修复前端编辑数据源报错问题\n- 增加数据库默认属性配置\n\n## 1.0.3\n\n- 🔥 支持 SSH 连接数据库\n- 🎉 支持客户端查看日志\n- 🎉 支持在 Console 中聊天对话\n- 支持在客户端内设置 OPENAI 代理\n- 已经启动过应用不会再重复启动\n\n## 1.0.1\n\n- 修复 oracle 连接配置编辑、以及连接查询问题\n- 修复 Apikey 输出到日志可能存在的风险\n- 修复 web 版本登录的 bug\n\n## 1.0.0\n\nChat2DB 的 1.0.0 正式版来啦 🎉🎉🎉🎉🎉🎉🎉🎉🎉\n\n- 🌈 AI 智能助手，支持自然语言转 SQL、SQL 转自然语言、SQL 优化建议\n- 👭 支持团队协作，研发无需知道线上数据库密码，解决企业数据库账号安全问题\n- ⚙️ 强大的数据管理能力，支持数据表、视图、存储过程、函数、触发器、索引、序列、用户、角色、授权等管理\n- 🔌 强大的扩展能力，目前已经支持 Mysql、PostgreSQL、Oracle、SQLServer、ClickHouse、Oceanbase、H2、SQLite 等等，未来会支持更多的数据库\n- 🛡 前端使用 Electron 开发，提供 Windows、Mac、Linux 客户端、网页版本一体化的解决方案\n- 🎁 支持环境隔离、线上、日常数据权限分离\n\n\n## 0.0.0\n\n`2023--`\n\n**更新日志**\n- ⭐【新功能】\n- ⚡️【优化】\n- 🐞【修复】\n\n\n\n"
  },
  {
    "path": "CHAT2DB_AI_SQL.md",
    "content": "# Chat2DB AI SQL功能使用说明\nChat2DB包含一系列基于ChatGPT的AI SQL使用功能，主要包括自然语言转SQL、SQL解释、SQL优化和SQL转换。 使用这些AI功能，可以将自然语言转换成本地查询SQL，而不仅仅是SQL查询伪代码；可以将SQL解释成自然语言，帮助用户理解复杂的SQL；可以针对慢SQL提供全方位的优化建议，提升查询效率；可以转换不同数据库类型的SQL语言，降低数据库迁移难度等等。\n## 使用配置\n### 点击设置【UI旧】\n<a><img src=\"https://img.alicdn.com/imgextra/i2/O1CN01hecdtO1acLegtiP9k_!!6000000003350-2-tps-2400-1600.png\" width=\"100%\"/></a>\n### 配置AI\n#### 配置OPENAI【UI旧】\n使用OPENAI的ChatSql功能需要满足两个条件\n- 配置OPENAI_API_KEY，如没有OPENAI_API_KEY可加入答疑群根据群公告指引获取chat2db自定义key\n- 客户端网络可以连接到OPENAI官网，如果本地VPN未能全局生效，可以通过在客户端中设置网络代理HOST和PORT来保证网络连通性\n<a><img src=\"https://img.alicdn.com/imgextra/i2/O1CN01anrJMI1FEtSBbmTau_!!6000000000456-0-tps-1594-964.jpg\" width=\"100%\"/></a>\n#### 配置自定义AI【UI旧】\n- 自定义AI可以是用户自己部署的任意AI模型，例如ChatGLM、ChatGPT、文心一言、通义千问等等，但是自定义的接口输入和输出需要符合自定义的协议规范才可快速使用，否则可能需要二次开发。代码中提供了两个DEMO，只需要配置自定义AI接口地址，以及接口是否流式输出即可查看。具体使用中可以参考DEMO接口来编写自定义接口，或者直接在DEMO接口中进行二次开发，封装自己的自定义接口\n- 自定义的流式输出接口配置DEMO\n<a><img src=\"https://img.alicdn.com/imgextra/i1/O1CN01xMqnRH1DlkdSekvSF_!!6000000000257-0-tps-591-508.jpg\" width=\"100%\"/></a>\n- 自定义的非流式输出接口配置DEMO\n<a><img src=\"https://img.alicdn.com/imgextra/i1/O1CN01JqmbGo1fW0GAQhRu4_!!6000000004013-0-tps-587-489.jpg\" width=\"100%\"/></a>\n## 自然语言转SQL【UI旧】\n### 功能描述\n输入自然语言，选中执行并传入查询相关的表结构信息，则会返回相应的SQL\n- 输入样例：查询学生小明的各科目成绩，并传入关联数据表course,score,student,student_course\n- 输出样例：\n```sql\nSELECT score.score\nFROM score\nINNER JOIN student_course ON score.course_id = student_course.course_id\nINNER JOIN student ON student_course.student_id = student.id\nWHERE student.name = '小明'\n```\n## SQL解释\n### 功能描述\n输入SQL，选中执行并传入附加信息，附加信息可以添加希望解释《输入SQL》哪方面的信息，如SQL执行目标，则会返回此条SQL附加信息相关的解释\n- 输入样例：SELECT department_name FROM departments WHERE department_id IN (SELECT department_id FROM employees GROUP BY department_id HAVING COUNT(*) > 10)\n- 输出样例：这个SQL查询语句的意思是：从departments表中选择department_name列，其中department_id列的值在employees表中department_id列的值分组后，如果该组中的记录数大于10，则返回该department_id。换句话说，这个查询语句返回的是在employees表中有超过10个员工的部门名称。\n## SQL优化\n### 功能描述\n输入SQL，选中执行并传入优化附加信息，附加信息可以添加希望具体优化方面和要求，则会返回此条SQL附加信息相关的优化建议\n- 输入样例：SELECT * FROM department ORDER BY gmt_create DESC\n- 输出样例：\n  1. 尽量避免使用SELECT *，应该明确指定需要查询的列，以减少不必要的数据传输和提高查询效率。\n  2. 对于ORDER BY语句，如果查询的结果集比较大，可以考虑添加LIMIT限制返回的行数，以减少排序的开销。\n  3. 对于gmt_create字段，可以考虑添加索引以提高排序的效率。\n  4. 如果查询的结果集经常被访问，可以考虑使用缓存技术，如Redis等，以减少数据库的访问次数。\n## SQL转换\n### 功能描述\n输入SQL，选中执行并传入目标SQL类型和附加信息，附加信息中可以添加转换的自定义要求，则会返回目标SQL类型且符合附加要求的SQL语言\n- 输入样例：SELECT IFNULL(NULL, \"W3Schools.com\")，目标SQL类型为Oracle SQL\n- 输出样例：SELECT NVL(NULL, 'W3Schools.com') FROM dual;\n## CHATBOT\n### 功能描述\n输入任意prompt，点击ChatRobot按钮即可根据prompt返回相应的结果，此处聊天对话默认支持上下文，默认上下文长度为4，可在application.yml文件中修改上下文长度\n- 输入样例：针对第二条优化建议给出具体实现方法\n- 输出样例：针对第二条优化建议，可以考虑在student表中添加一个索引，以加快查询速度。具体实现方法如下：\n\n1. 查看student表中是否已经存在索引。可以使用以下命令查看：\n\n   ```sql\n   SHOW INDEX FROM student;\n   ```\n\n   如果已经存在索引，则可以跳过下一步。\n\n2. 在student表中添加索引。可以使用以下命令添加：\n\n   ```sql\n   ALTER TABLE student ADD INDEX name_index (name);\n   ```\n\n   这里的name_index是索引的名称，name是需要建立索引的列名。\n\n   注意：如果student表中的数据量很大，添加索引可能需要一些时间。\n\n3. 重新执行原始SQL语句，查看查询速度是否有所提升。\n\n   ```sql\n   SELECT score.score FROM score INNER JOIN student ON score.student_id = student.id WHERE student.name = '小明';\n   ```\n\n   如果查询速度有所提升，则说明索引建立成功。\n"
  },
  {
    "path": "Chat2DB_LICENSE",
    "content": "The Chat2DB License\n\n一、定义\n\n“许可方”是指分发其软件的 Chat2DB 软件团队。\n\n“软件”是指根据本许可提供的 Chat2DB 软件。\n\n2. 许可授予\n\n根据本许可的条款和条件，许可方特此授予您非排他性、全球性、不可转让、不可再许可、可撤销、免版税的版权许可，仅用于您的非商业研究目的。\n\n上述版权声明和本许可声明应包含在本软件的所有副本或重要部分中。\n\n3.限制\n\n您不得出于任何商业、军事或非法目的使用、复制、修改、合并、发布、分发、复制或创建本软件的全部或部分衍生作品。\n\n4.免责声明\n\n本软件“按原样”提供，不提供任何明示或暗示的保证，包括但不限于对适销性、特定用途的适用性和非侵权性的保证。 在任何情况下，作者或版权持有人均不对任何索赔、损害或其他责任负责，无论是在合同诉讼、侵权行为还是其他方面，由软件或软件的使用或其他交易引起、由软件引起或与之相关 软件。\n\n5. 责任限制\n\n除适用法律禁止的范围外，在任何情况下且根据任何法律理论，无论是基于侵权行为、疏忽、合同、责任或其他原因，任何许可方均不对您承担任何直接、间接、特殊、偶然、示范性、 或间接损害，或任何其他商业损失，即使许可人已被告知此类损害的可能性。\n\n6.争议解决\n\n本许可受中华人民共和国法律管辖并按其解释。 因本许可引起的或与本许可有关的任何争议应提交北京市海淀区人民法院。\n\n请注意，许可证可能会更新到更全面的版本。 有关许可和版权的任何问题，请通过1558143046@qq.com 与我们联系。\n\n\n1. Definitions\n\n \"Licensor\" refers to the Chat2DB software team that distributes its software.\n\n \"Software\" refers to the Chat2DB software provided under this license.\n\n2. License Grant\n\nSubject to the terms and conditions of this License, the Licensor hereby grants to you a non-exclusive, worldwide, non-transferable, non-sublicensable, revocable, royalty-free copyright license to use the Software solely for your non-commercial research purposes.\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\n3. Restriction\n\nYou will not use, copy, modify, merge, publish, distribute, reproduce, or create derivative works of the Software, in whole or in part, for any commercial, military, or illegal purposes.\n\n4. Disclaimer\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n5. Limitation of Liability\n\nEXCEPT TO THE EXTENT PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER BASED IN TORT, NEGLIGENCE, CONTRACT, LIABILITY, OR OTHERWISE WILL ANY LICENSOR BE LIABLE TO YOU FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES, OR ANY OTHER COMMERCIAL LOSSES, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n6. Dispute Resolution\n\nThis license shall be governed and construed in accordance with the laws of People’s Republic of China. Any dispute arising from or in connection with this License shall be submitted to Haidian District People's Court in Beijing.\n\nNote that the license is subject to update to a more comprehensive version.  For any questions related to the license and copyright, please contact us at 1558143046@qq.com.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright Pengfei Ji\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <h2>🚀 Zoer is Launching</h2>\n  <p><strong>Powered by Chat2DB Team - AI-powered app builder that creates professional applications in minutes, no coding required</strong></p>\n  \n  <a href=\"https://zoer.ai/?utm_source=chat2db&utm_medium=banner&utm_campaign=github\" target=\"_blank\">\n    <img width=\"1000\" height=\"auto\" alt=\"Zoer - AI App Builder\" src=\"https://github.com/user-attachments/assets/2f2a682d-9cc0-4470-93d3-19b4f1f6589e\" style=\"border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);\" />\n  </a>\n  \n  <br/><br/>\n  \n  ---\n  \n  <br/>\n  \n  <a href=\"https://trendshift.io/repositories/11808\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/11808\" alt=\"CodePhiliaX%2FChat2DB | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/>\n  </a>\n</div>\n\n<div align=\"center\">\n  \n[![ReadmeX][readmex-image]][readmex-url]\n[![Discord][discord-image]][discord-url]\n[![Twitter][twitter-image]][twitter-url]\n[![Telegram][telegram-image]][telegram-url]\n[![Whatsapp][whatsapp-image]][whatsapp-url]\n[![Reddit][reddit-image]][reddit-url]\n[![Gmail][gmail-image]][gmail-url]\n\n[readmex-image]: https://raw.githubusercontent.com/CodePhiliaX/resource-trusteeship/main/readmex.svg\n[readmex-url]: https://readmex.com/CodePhiliaX/Chat2DB\n[discord-image]: https://img.shields.io/badge/-Join%20us%20on%20Discord-%237289DA.svg?style=flat&logo=discord&logoColor=white\n[discord-url]: https://discord.com/invite/uNjb3n5JVN\n[twitter-image]: https://img.shields.io/twitter/follow/_Chat2DB?label=Chat2DB\n[twitter-url]: https://twitter.com/intent/tweet?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.&url=https://github.com/chat2db/Chat2DB&hashtags=ChatGPT,AGI,SQL%20Client,Reporting%20tool\n[telegram-image]: https://img.shields.io/twitter/url?label=Telegram&logo=Telegram&style=social&url=https://github.com/chat2db/Chat2DB\n[telegram-url]: https://t.me/share/url?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.&url=https://github.com/chat2db/Chat2DB\n[whatsapp-image]: https://img.shields.io/twitter/url?label=whatsapp&logo=whatsapp&style=social&url=https://github.com/chat2db/Chat2DB\n[whatsapp-url]: https://api.whatsapp.com/send?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.%20https://github.com/chat2db/Chat2DB\n[reddit-image]: https://img.shields.io/twitter/url?label=Reddit&logo=Reddit&style=social&url=https://github.com/chat2db/Chat2DB\n[reddit-url]: https://www.reddit.com/submit?url=https://github.com/chat2db/Chat2DB&title=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.\n[gmail-image]: https://img.shields.io/twitter/url?label=Gmail&logo=Gmail&style=social&url=https://github.com/chat2db/Chat2DB\n[gmail-url]: mailto:?subject=Check%20this%20GitHub%20repository%20out.&body=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.%3A%0Ahttps://github.com/chat2db/Chat2DB\n\n</div>\n\n<div align=\"center\">\n  <a href=\"./README.md\"><img alt=\"README in English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"./README_CN.md\"><img alt=\"简体中文版自述文件\" src=\"https://img.shields.io/badge/简体中文-d9d9d9\"></a>\n  <a href=\"./README_JA.md\"><img alt=\"日本語のREADME\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n\n</div>\n\n**1. Intelligent SQL Generation**:  \nChat2DB Pro supports AI-driven intelligent SQL development to help you write SQL queries faster.\n\n**2. Database Management**:  \nSupports more than 10 databases, including MySQL, PostgreSQL, H2, Oracle, SQLServer, SQLite, MariaDB, ClickHouse, DM, Presto, DB2, OceanBase, Hive, KingBase, MongoDB, Redis, Snowflake, and more.\n\n**3. Intelligent Report Generation**:  \nChat2DB Pro supports AI-driven intelligent data reporting to help you generate dashboards faster.\n\n**4. Data Structure Synchronization**:  \nChat2DB Pro supports database table structure synchronization to help you sync database table structures faster.\n\n## Feature Comparison\n\n<table style=\"width: 100%;\">\n  <tr>\n    <th align=\"center\">Feature</th>\n    <th align=\"center\">Community Open Source</th>\n    <th align=\"center\">Local</th>\n    <th align=\"center\">Pro </th>\n  </tr>\n  <tr>\n    <td align=\"center\">Database Types</td>\n    <td align=\"center\">16+</td>\n    <td align=\"center\">Target 100+</td>\n    <td align=\"center\">Target 100+</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Supported AI</td>\n    <td align=\"center\">Requires AI Configuration</td>\n    <td align=\"center\">AI ready on installation</td>\n    <td align=\"center\">AI ready on installation</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AI Capabilities</td>\n    <td align=\"center\">Basic</td>\n    <td align=\"center\">Varied</td>\n    <td align=\"center\">Varied</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Visual Table Editor</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">SQL Console</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">SQL Formatting</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Save Query Records</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Theme Color Settings</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Data Structure Sync</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Database Grouping</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Database Structure Import/Export</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Data Import/Export</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Data Migration</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Copy/Clear Table</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Open and Run SQL Files</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">UML Diagram</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">In Development</td>\n    <td align=\"center\">In Development</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Generate Code</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Copy Results as Insert/Update</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Modify Query Results</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Intelligent SQL Editor</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AI Table Creation</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AI Data Sets</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Chat2Excel</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Intelligent Dashboard</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Editor Settings</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Custom Shortcuts</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Cross-device Usage</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n  </tr>\n</table>\n\n## Download and Installation\nChat2DB is a cross-platform application that supports Windows, MacOS, and Linux. You can download Chat2DB from the following links:\n- [Download Pro Version](https://chat2db.ai/download)\n- [Download Local Version](https://chat2db.ai/download)\n- [Download Open Source Version](https://github.com/CodePhiliaX/Chat2DB/releases/tag/v0.3.6)\n\n## Community Edition Docker Installation\n\n### System Requirements\n\nBefore installing Chat2DB, ensure your system meets the following requirements:\n- Docker 19.03.0 or later\n- Docker Compose 1.25.0 or later\n- CPU >= 2 Cores\n- RAM >= 4 GiB\n\n```bash\n  docker rm chat2db\n  \n  docker run --name=chat2db -ti -p 10824:10824 -v ~/.chat2db-docker:/root/.chat2db  chat2db/chat2db:latest\n\n  docker start chat2db\n  \n```\n## Code Debugging\n\n## Runtime Environment\n\nNote:\nIf local debugging is needed:\n\n- Java runtime: <a href=\"https://adoptopenjdk.net/\" target=\"_blank\">Open JDK 17</a>\n- Node.js runtime: Node 16 <a href=\"https://nodejs.org/\" target=\"_blank\">Node.js</a>.\n\n**Clone the repository locally**\n\n```bash\n$ git clone git@github.com:chat2db/Chat2DB.git\n```\n\n**Frontend Debugging**\n\n```bash\nNode version must be 16 or higher  \nUse yarn only, npm is not supported\n$ cd Chat2DB/chat2db-client\n$ yarn\n$ yarn run start:web\n```\n\n**Backend Debugging**\n\n```bash\n$ cd ../chat2db-server\n$ mvn clean install # Maven version 3.8 or higher is required\n$ cd chat2db-server/chat2db-server-start/target/\n$ java -jar -Dloader.path=./lib -Dchatgpt.apiKey=xxxxx chat2db-server-start.jar  # 需要安装java 17以上版本，启动应用 chatgpt.apiKey 需要输入ChatGPT的key,如果不输入无法使用AIGC功能\n```\n**Standalone Deployment**\n```bash\n# chat2db-client\n$ npm run build:web:prod \n$ cp -r dist ../chat2db-server/chat2db-server-start/src/main/resources/static/front \n$ cp -r dist/index.html ../chat2db-server/chat2db-server-start/src/main/resources/thymeleaf\n```\n\n##  Contact Us\n\n- Email: Chat2DB@ch2db.com\n- Discord: [Join our Discord server](https://discord.gg/JDkwB6JS8A)\n- Twitter: [@Chat2DB](https://x.com/Chat2DB_AI)\n- YouTube: [Chat2DB Channel](https://www.youtube.com/@chat2db.tutorial)\n- GitHub: [Chat2DB GitHub](https://github.com/codePhiliaX/chat2db)\n\n\n##  Acknowledgments\n\n\nThanks to everyone who has contributed to Chat2DB~~\n\n\n<a href=\"https://github.com/chat2db/Chat2DB/graphs/contributors\">\n  <img src=\"https://contrib.rocks/image?repo=chat2db/Chat2DB\" />\n</a>\n\n## Star History\n\n<a href=\"https://star-history.com/#CodePhiliaX/chat2db&Date\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date&theme=dark\" />\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date\" />\n    <img alt=\"Star History Chart\" src=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date\" />\n  </picture>\n</a>\n\n## License\nThe primary license used by this software is the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0), supplemented by the [Chat2DB License](./Chat2DB_LICENSE).\n\n"
  },
  {
    "path": "README_CN.md",
    "content": "<div align=\"center\">\n    <a href=\"https://trendshift.io/repositories/11808\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/11808\" alt=\"CodePhiliaX%2FChat2DB | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/>\n</div>\n<br/>\n        \n<div align=\"center\">\n  \n[![ReadmeX][readmex-image]][readmex-url]\n[![Discord][discord-image]][discord-url]\n[![Twitter][twitter-image]][twitter-url]\n[![Telegram][telegram-image]][telegram-url]\n[![Whatsapp][whatsapp-image]][whatsapp-url]\n[![Reddit][reddit-image]][reddit-url]\n[![Gmail][gmail-image]][gmail-url]\n\n[readmex-image]: https://raw.githubusercontent.com/CodePhiliaX/resource-trusteeship/main/readmex.svg\n[readmex-url]: https://readmex.com/CodePhiliaX/Chat2DB\n[discord-image]: https://img.shields.io/badge/-Join%20us%20on%20Discord-%237289DA.svg?style=flat&logo=discord&logoColor=white\n[discord-url]: https://discord.com/invite/uNjb3n5JVN\n[twitter-image]: https://img.shields.io/twitter/follow/_Chat2DB?label=Chat2DB\n[twitter-url]: https://twitter.com/intent/tweet?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.&url=https://github.com/chat2db/Chat2DB&hashtags=ChatGPT,AGI,SQL%20Client,Reporting%20tool\n[telegram-image]: https://img.shields.io/twitter/url?label=Telegram&logo=Telegram&style=social&url=https://github.com/chat2db/Chat2DB\n[telegram-url]: https://t.me/share/url?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.&url=https://github.com/chat2db/Chat2DB\n[whatsapp-image]: https://img.shields.io/twitter/url?label=whatsapp&logo=whatsapp&style=social&url=https://github.com/chat2db/Chat2DB\n[whatsapp-url]: https://api.whatsapp.com/send?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.%20https://github.com/chat2db/Chat2DB\n[reddit-image]: https://img.shields.io/twitter/url?label=Reddit&logo=Reddit&style=social&url=https://github.com/chat2db/Chat2DB\n[reddit-url]: https://www.reddit.com/submit?url=https://github.com/chat2db/Chat2DB&title=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.\n[gmail-image]: https://img.shields.io/twitter/url?label=Gmail&logo=Gmail&style=social&url=https://github.com/chat2db/Chat2DB\n[gmail-url]: mailto:?subject=Check%20this%20GitHub%20repository%20out.&body=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.%3A%0Ahttps://github.com/chat2db/Chat2DB\n\n</div>\n\n<div align=\"center\">\n  <a href=\"./README.md\"><img alt=\"README in English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"./README_CN.md\"><img alt=\"简体中文版自述文件\" src=\"https://img.shields.io/badge/简体中文-d9d9d9\"></a>\n  <a href=\"./README_JA.md\"><img alt=\"日本語のREADME\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n \n</div>\n\n\n\n\nhttps://github.com/user-attachments/assets/3c857883-8153-4bda-92b8-d25c6adb5b13\n\n\n\n# \nChat2DB 是一个智能的通用SQL客户端和数据报表工具，它集成了AI的能力。Chat2DB可以帮助您更快地编写SQL查询、管理数据库、生成报告、探索数据、并且可以与多种数据库进行交互。Chat2DB是一个开源项目，我们欢迎您的贡献。\n\n**1. 智能生成SQL**:\nChat2DB Pro支持AI驱动的智能SQL开发，可以帮助您更快地编写SQL查询。\n\n\n**2. 数据库管理**:\n 支持多种10+数据库，包括MySQL、PostgreSQL、H2、Oracle、SQLServer、SQLite、MariaDB、ClickHouse、DM、Presto、DB2、OceanBase、Hive、KingBase、MongoDB、Redis、Snowflake等。\n\n\n\n**3. 智能生成报表**:\n    Chat2DB Pro支持AI驱动的智能数据报表，可以帮助您更快地生成看板。\n\n**4. 数据结构同步**:\n    Chat2DB Pro支持数据库表结构同步，可以帮助您更快地同步数据库表结构。\n\n## 功能比较\n\n<table style=\"width: 100%;\">\n  <tr>\n    <th align=\"center\">功能</th>\n    <th align=\"center\">社区开源版</th>\n    <th align=\"center\">Local版(收费)</th>\n    <th align=\"center\">Pro版(收费)</th>\n  </tr>\n  <tr>\n    <td align=\"center\">数据库类型</td>\n    <td align=\"center\">16+</td>\n    <td align=\"center\">目标100+</td>\n    <td align=\"center\">目标100+</td>\n  </tr>\n  <tr>\n    <td align=\"center\">支持的 AI</td>\n    <td align=\"center\">需要配置AI</td>\n    <td align=\"center\">安装即可使用AI</td>\n    <td align=\"center\">安装即可使用AI</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AI 能力</td>\n    <td align=\"center\">简单</td>\n    <td align=\"center\">多样</td>\n    <td align=\"center\">多样</td>\n  </tr>\n  <tr>\n    <td align=\"center\">可视化编辑表</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">SQL控制台</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n <tr>\n    <td align=\"center\">SQL格式化</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n <tr>\n    <td align=\"center\">保存查询记录</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n <tr>\n    <td align=\"center\">主题颜色设置</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">数据结构同步</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">数据库分组</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">数据库结构导入导出</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">数据导入导出</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">数据迁移</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">复制/清空表</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">打开运行SQL文件</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">UML图</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">开发中</td>\n    <td align=\"center\">开发中</td>\n  </tr>\n  <tr>\n    <td align=\"center\">生成代码</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">复制结果为\nInsert/update\n    </td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">修改查询结果</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">智能SQL编辑器</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AI建表</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AI数据集</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Chat2Excel</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n<tr>\n    <td align=\"center\">智能看板</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n<tr>\n    <td align=\"center\">编辑器设置</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n<tr>\n    <td align=\"center\">自定义快捷键</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n<tr>\n    <td align=\"center\">跨多设备使用</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n  </tr>\n</table>\n\n## 下载安装\nChat2DB 是一个跨平台的应用程序，支持Windows、MacOS和Linux。您可以从以下链接下载Chat2DB。\n- [下载 Pro 版](https://chat2db.ai/download)\n- [下载 Local 版](https://chat2db.ai/download)\n- [下载开源版](https://github.com/CodePhiliaX/Chat2DB/releases/tag/v0.3.6)\n\n## 社区版 Docker 安装\n\n### 系统要求\n\n在安装 Chat2DB 之前，请确保您的系统满足以下要求：\n- Docker 19.03.0 或更高版本\n- Docker Compose 1.25.0 或更高版本\n- CPU >= 2 Core\n- RAM >= 4 GiB\n\n\n```bash\n  // 拉取最新客户端,然后运行docker,名字是 `chat2db` , 并且将 `/root/.chat2db` 挂载到 `~/.chat2db-docker`\n  docker run --name=chat2db -ti -p 10824:10824 -v ~/.chat2db-docker:/root/.chat2db  chat2db/chat2db:latest\n  // 这里正常会提示`Tomcat started on port(s): 10824 (http) with context path` 就可以结束了\n\n  // 如果这里提示  `The container name \"/chat2db\" is already in use by container`, 代表已经存在容器了 运行\n  docker start chat2db\n  // 如果想更新chat2db 则需要先rm\n  docker rm chat2db\n```\n## 代码调试\n\n\n## 运行环境\n\n注意：\n如果需要本地调试\n\n- java 运行 <a href=\"https://adoptopenjdk.net/\" target=\"_blank\">Open JDK 17</a>\n- Node 运行环境 Node16 <a href=\"https://nodejs.org/\" target=\"_blank\">Node.js</a>.\n\n**git clone 到本地**\n\n```bash\n$ git clone git@github.com:chat2db/Chat2DB.git\n```\n\n**前端调试**\n\n```bash\nnode版本必须为16及以上 \n一定要用yarn\n$ cd Chat2DB/chat2db-client\n$ yarn\n$ yarn run start:web\n```\n\n**后端调试**\n\n```bash\n$ cd ../chat2db-server\n$ mvn clean install # 需要安装maven 3.8以上版本\n$ cd chat2db-server/chat2db-server-start/target/\n$ java -jar -Dloader.path=./lib -Dchatgpt.apiKey=xxxxx chat2db-server-start.jar  # 需要安装java 17以上版本，启动应用 chatgpt.apiKey 需要输入ChatGPT的key,如果不输入无法使用AIGC功能\n```\n**独立部署**\n```bash\n# chat2db-client\n$ npm run build:web:prod \n$ cp -r dist ../chat2db-server/chat2db-server-start/src/main/resources/static/front \n$ cp -r dist/index.html ../chat2db-server/chat2db-server-start/src/main/resources/thymeleaf\n\n# 再打包后端服务\n```\n\n##  联系我们\n\n<a><img src=\"https://github.com/chat2db/Chat2DB/assets/22975773/81d13eff-c615-49f5-aee3-4107089593e0\" width=\"25%\"/></a>\n\n- Email: Chat2DB@ch2db.com\n- Discord: [Join our Discord server](https://discord.gg/JDkwB6JS8A)\n- Twitter: [@Chat2DB](https://x.com/Chat2DB_AI)\n- YouTube: [Chat2DB Channel](https://www.youtube.com/@chat2db.tutorial)\n- GitHub: [Chat2DB GitHub](https://github.com/codePhiliaX/chat2db)\n\n\n##  致谢\n\n感谢所有为 Chat2DB 贡献力量的同学们~~\n\n<a href=\"https://github.com/chat2db/Chat2DB/graphs/contributors\">\n  <img src=\"https://contrib.rocks/image?repo=chat2db/Chat2DB\" />\n</a>\n\n## Star History\n\n<a href=\"https://star-history.com/#CodePhiliaX/chat2db&Date\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date&theme=dark\" />\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date\" />\n    <img alt=\"Star History Chart\" src=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date\" />\n  </picture>\n</a>\n\n## License\nThe primary license used by this software is the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0), supplemented by the [Chat2DB License](./Chat2DB_LICENSE).\n\n\n\n"
  },
  {
    "path": "README_JA.md",
    "content": "\n\n\n<div align=\"center\">\n    <a href=\"https://trendshift.io/repositories/11808\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/11808\" alt=\"CodePhiliaX%2FChat2DB | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n</div>\n<br/>\n<p align=\"center\">\n <a href=\"https://discord.com/invite/uNjb3n5JVN\" target=\"_blank\">\n    <img src=\"https://img.shields.io/badge/-Join%20us%20on%20Discord-%237289DA.svg?style=flat&logo=discord&logoColor=white\"\n            alt=\"chat on Discord\"></a>\n<a href=\"https://twitter.com/intent/tweet?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.&url=https://github.com/chat2db/Chat2DB&hashtags=ChatGPT,AGI,SQL%20Client,Reporting%20tool\" target=\"blank\" > <img src=\"https://img.shields.io/twitter/follow/_Chat2DB?label=Twitter&style=social\" alt=\"\"/> </a> \n<a href=\"https://t.me/share/url?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.&url=https://github.com/chat2db/Chat2DB\" target=\"_blank\"><img src=\"https://img.shields.io/twitter/url?label=Telegram&logo=Telegram&style=social&url=https://github.com/chat2db/Chat2DB\" alt=\"Share on Telegram\"/></a>\n<a href=\"https://api.whatsapp.com/send?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.%20https://github.com/chat2db/Chat2DB\"><img src=\"https://img.shields.io/twitter/url?label=whatsapp&logo=whatsapp&style=social&url=https://github.com/chat2db/Chat2DB\" /></a>\n<a href=\"https://www.reddit.com/submit?url=https://github.com/chat2db/Chat2DB&title=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.\" target=\"blank\"><img src=\"https://img.shields.io/twitter/url?label=Reddit&logo=Reddit&style=social&url=https://github.com/chat2db/Chat2DB\" alt=\"Share on Reddit\"/></a>\n<a href=\"mailto:?subject=Check%20this%20GitHub%20repository%20out.&body=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.%3A%0Ahttps://github.com/chat2db/Chat2DB\" target=\"_blank\"><img src=\"https://img.shields.io/twitter/url?label=Gmail&logo=Gmail&style=social&url=https://github.com/chat2db/Chat2DB\"/></a>\n\n</p>\n<div align=\"center\">\n  <a href=\"./README.md\"><img alt=\"README in English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"./README_CN.md\"><img alt=\"简体中文版自述文件\" src=\"https://img.shields.io/badge/简体中文-d9d9d9\"></a>\n  <a href=\"./README_JA.md\"><img alt=\"日本語のREADME\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n</div>\n\n\n\nhttps://github.com/user-attachments/assets/bd5d5f64-540f-4793-a801-17fa96c4766e\n\n\n\n\nChat2DBはAI機能を統合したインテリジェントで汎用的なSQLクライアントおよびデータ報告ツールです。Chat2DBは、SQLクエリの作成を迅速化し、データベースの管理、レポートの生成、データの探索、および複数のデータベースとのインタラクションをサポートします。Chat2DBはオープンソースプロジェクトであり、皆様の貢献を歓迎します。\n\n**1. インテリジェントSQL生成**:  \nChat2DB Proは、AI駆動によるインテリジェントなSQL開発をサポートし、SQLクエリをより速く作成する手助けをします。\n\n**2. データベース管理**:  \nMySQL、PostgreSQL、H2、Oracle、SQLServer、SQLite、MariaDB、ClickHouse、DM、Presto、DB2、OceanBase、Hive、KingBase、MongoDB、Redis、Snowflakeなど、10種類以上のデータベースをサポートしています。\n\n**3. インテリジェントレポート生成**:  \nChat2DB Proは、AI駆動によるインテリジェントなデータ報告をサポートし、ダッシュボードの作成を迅速に行う手助けをします。\n\n**4. データ構造の同期**:  \nChat2DB Proは、データベーステーブル構造の同期をサポートし、データベーステーブルの構造を迅速に同期する手助けをします。\n\n## 機能比較\n\n<table style=\"width: 100%;\">\n  <tr>\n    <th align=\"center\">機能</th>\n    <th align=\"center\">コミュニティ オープンソース</th>\n    <th align=\"center\">ローカル</th>\n    <th align=\"center\">Pro</th>\n  </tr>\n  <tr>\n    <td align=\"center\">データベースの種類</td>\n    <td align=\"center\">16種類以上</td>\n    <td align=\"center\">100種類以上を対象</td>\n    <td align=\"center\">100種類以上を対象</td>\n  </tr>\n  <tr>\n    <td align=\"center\">サポートされているAI</td>\n    <td align=\"center\">AI設定が必要</td>\n    <td align=\"center\">インストール時にAIが利用可能</td>\n    <td align=\"center\">インストール時にAIが利用可能</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AI機能</td>\n    <td align=\"center\">基本的</td>\n    <td align=\"center\">多様</td>\n    <td align=\"center\">多様</td>\n  </tr>\n  <tr>\n    <td align=\"center\">視覚的テーブルエディタ</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">SQLコンソール</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">SQLフォーマット</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">クエリ記録の保存</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">テーマカラー設定</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">データ構造の同期</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">データベースのグループ化</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">データベース構造のインポート/エクスポート</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">データのインポート/エクスポート</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">データ移行</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">テーブルのコピー/削除</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">SQLファイルのオープンと実行</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">UMLダイアグラム</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">開発中</td>\n    <td align=\"center\">開発中</td>\n  </tr>\n  <tr>\n    <td align=\"center\">コード生成</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">インサート/アップデートとして結果をコピー</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">クエリ結果の修正</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">インテリジェントSQLエディタ</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AIによるテーブル作成</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">AIデータセット</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">Chat2Excel</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">インテリジェントダッシュボード</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">エディタ設定</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">カスタムショートカット</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n    <td align=\"center\">✅</td>\n  </tr>\n  <tr>\n    <td align=\"center\">クロスデバイス使用</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">❌</td>\n    <td align=\"center\">✅</td>\n  </tr>\n</table>\n\n\n## ダウンロードとインストール\nChat2DBは、Windows、MacOS、Linuxをサポートするクロスプラットフォームアプリケーションです。以下のリンクからChat2DBをダウンロードできます：\n- [Proバージョンのダウンロード](https://chat2db.ai/download)\n- [ローカルバージョンのダウンロード](https://chat2db.ai/download)\n- [オープンソースバージョンのダウンロード](https://github.com/CodePhiliaX/Chat2DB/releases/tag/v0.3.6)\n\n## コミュニティエディションのDockerインストール\n\n### システム要件\n\nChat2DBをインストールする前に、システムが以下の要件を満たしていることを確認してください：\n- Docker 19.03.0以上\n- Docker Compose 1.25.0以上\n- CPU >= 2コア\n- RAM >= 4 GiB\n\n```bash\n  docker rm chat2db\n  \n  docker run --name=chat2db -ti -p 10824:10824 -v ~/.chat2db-docker:/root/.chat2db  chat2db/chat2db:latest\n\n  docker start chat2db\n```\n## コードデバッグ\n\n## 実行環境\n\n注意： ローカルデバッグが必要な場合：\n\n- Java runtime: <a href=\"https://adoptopenjdk.net/\" target=\"_blank\">Open JDK 17</a>\n- Node.js runtime: Node 16 <a href=\"https://nodejs.org/\" target=\"_blank\">Node.js</a>.\n\n**リポジトリをローカルにクローン**\n\n```bash\n$ git clone git@github.com:chat2db/Chat2DB.git\n```\n\n**フロントエンドデバッグ**\n\n```bash\nNode version must be 16 or higher  \nUse yarn only, npm is not supported\n$ cd Chat2DB/chat2db-client\n$ yarn\n$ yarn run start:web\n```\n\n**バックエンドデバッグ**\n\n```bash\n$ cd ../chat2db-server\n$ mvn clean install # Maven version 3.8 or higher is required\n$ cd chat2db-server/chat2db-server-start/target/\n$ java -jar -Dloader.path=./lib -Dchatgpt.apiKey=xxxxx chat2db-server-start.jar  # 需要安装java 17以上版本，启动应用 chatgpt.apiKey 需要输入ChatGPT的key,如果不输入无法使用AIGC功能\n```\n**スタンドアロンデプロイ**\n```bash\n# chat2db-client\n$ npm run build:web:prod \n$ cp -r dist ../chat2db-server/chat2db-server-start/src/main/resources/static/front \n$ cp -r dist/index.html ../chat2db-server/chat2db-server-start/src/main/resources/thymeleaf\n```\n\n## お問い合わせ\n\n- メール: Chat2DB@ch2db.com\n- Discord: [Discordサーバーに参加](https://discord.gg/JDkwB6JS8A)\n- Twitter: [@Chat2DB](https://x.com/Chat2DB_AI)\n- YouTube: [Chat2DB チャンネル](https://www.youtube.com/@chat2db.tutorial)\n- GitHub: [Chat2DB GitHub](https://github.com/codePhiliaX/chat2db)\n\n## 謝辞\n\nChat2DBに貢献してくださったすべての方々に感謝します~~\n\n\n\n<a href=\"https://github.com/chat2db/Chat2DB/graphs/contributors\">\n  <img src=\"https://contrib.rocks/image?repo=chat2db/Chat2DB\" />\n</a>\n\n## Star History\n\n<a href=\"https://star-history.com/#CodePhiliaX/chat2db&Date\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date&theme=dark\" />\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date\" />\n    <img alt=\"Star History Chart\" src=\"https://api.star-history.com/svg?repos=CodePhiliaX/chat2db&type=Date\" />\n  </picture>\n</a>\n\n## License\nこのソフトウェアで使用されている主なライセンスは[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)であり、[Chat2DB License](./Chat2DB_LICENSE)が補完されています。\n\n\n"
  },
  {
    "path": "chat2db-client/.eslintrc.js",
    "content": "module.exports = {\n  parser: '@typescript-eslint/parser',\n  env: {\n    browser: true,\n    es2021: true,\n  },\n  plugins: ['@typescript-eslint', 'babel', 'react-hooks', 'react'],\n  extends: [\n    'eslint:recommended',\n    'plugin:@typescript-eslint/recommended',\n    'plugin:react/recommended',\n    // 'airbnb-base', // airbnb-base中已经包含了eslint-plugin-import\n    // 'prettier', // 使得eslint中的样式规范失效，遵循prettier中的样式规范\n    // 'prettier/@typescript-eslint', // 使得@typescript-eslint中的样式规范失效，遵循prettier中的样式规范\n  ],\n  overrides: [\n    {\n      env: {\n        node: true,\n      },\n      files: ['.eslintrc.{js,cjs}'], //\n      parserOptions: {\n        sourceType: 'script',\n      },\n    },\n  ],\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n  },\n  ignorePatterns: ['src/main'],\n  rules: {\n    'func-names': 0, // 函数表达式必须有名字\n    'one-var': [1, 'never'], // 连续声明\n    'prefer-const': 1, // 首选const\n    'no-unused-expressions': 0, // 禁止无用的表达式\n    'new-cap': 2, // 构造函数首字母大写\n    'prefer-arrow-callback': 2, // 首选箭头函数\n    'arrow-body-style': 0, // 箭头函数体使用大括号\n    'max-len': [\n      // 一行最大长度\n      1,\n      {\n        code: 120,\n        ignoreStrings: true,\n        ignoreUrls: true,\n        ignoreRegExpLiterals: true,\n      },\n    ],\n    'consistent-return': 'off', // return 后面是否允许省略\n    'default-case': 2, // switch 语句必须有 default\n    'prefer-rest-params': 2, // 必须使用解构 ...args 来代替 arguments\n    'no-script-url': 0, // 禁止使用 javascript:void(0)\n    // 'no-console': [ // 禁止使用 console\n    //   2,\n    //   {\n    //     allow: ['info', 'error', 'warn'],\n    //   },\n    // ],\n    'no-duplicate-imports': [2], // 禁止重复 import\n    'newline-per-chained-call': 2, // 链式调用必须换行\n    // 'no-underscore-dangle': 2, // 禁止标识符中有悬空下划线\n    'eol-last': 2, // 文件以单一的换行符结束\n    'no-useless-rename': 2, // 禁止无用的重命名\n    'no-undef': 0, // 禁止使用未定义的变量\n    'class-methods-use-this': 0, // class 的非静态方法必须包含 this\n    'prefer-destructuring': 0, // 优先使用数组和对象解构\n    'no-unused-vars': 0, // 禁止未使用过的变量\n    '@typescript-eslint/no-unused-vars': 1, // 禁止未使用过的变量\n    'react/self-closing-comp': 2, // 非单行 JSX 必须使用括号包裹\n    'react/jsx-indent-props': [2, 2], // jsx props 缩进\n    'no-plusplus': 0, // 禁止使用 ++，--\n    'react/jsx-uses-vars': 1, // jsx 文件中禁止使用变量\n    // 'react/no-multi-comp': [ // 禁止一个文件中定义多个组件\n    //   2,\n    //   {\n    //     ignoreStateless: true,\n    //   },\n    // ],\n    'react/jsx-uses-react': 2, // jsx 文件中禁止使用 React\n    'react/react-in-jsx-scope': 2, // jsx 文件中禁止使用 React\n    'react/sort-comp': 1, // 组件内方法顺序\n    'react/jsx-tag-spacing': 2, // jsx 中的属性禁止使用空格\n    'react/jsx-no-bind': 0, // jsx 中禁止使用 bind\n    'react/jsx-closing-bracket-location': 2, // jsx 中的右括号必须换行\n    'react/prefer-stateless-function': 0, // 优先使用无状态组件\n    'react/display-name': 0, // 组件必须写 displayName\n    'react/prop-types': 0, // 组件必须写 propTypes\n    'import/prefer-default-export': 0, // 优先使用 export default\n    '@typescript-eslint/no-var-requires': 2, // 禁止 require() 使用表达式\n    'no-use-before-define': 0, // 禁止定义前使用\n    '@typescript-eslint/no-use-before-define': [\n      // 禁止定义前使用\n      0,\n      // {\n      //   functions: false,\n      // },\n    ],\n    '@typescript-eslint/explicit-function-return-type': 0, // 函数必须有返回值\n    '@typescript-eslint/interface-name-prefix': 0, // 接口名称必须以 I 开头\n    '@typescript-eslint/explicit-module-boundary-types': 0, // 导出函数和类的公共方法必须声明返回类型\n    'no-shadow': 0, // 禁止变量名与上层作用域内的定义过的变量重复\n    '@typescript-eslint/no-shadow': 1, // 禁止变量名与上层作用域内的定义过的变量重复TODO: 为2是不是好点？\n    'no-invalid-this': 0, // 禁止 this 关键字出现在类和类对象之外\n    'no-await-in-loop': 'off', // 禁止在循环中出现 await\n    'array-callback-return': 'off', // 数组方法的回调函数中必须有 return 语句\n    'no-restricted-syntax': 'off', // 禁止使用特定的语法\n    '@typescript-eslint/no-explicit-any': 0, // 禁止使用 any\n    'import/no-extraneous-dependencies': 0, // 禁止使用无关的 package\n    'import/no-unresolved': 0, // 禁止使用无关的 package\n    '@typescript-eslint/explicit-member-accessibility': 0, // 类的成员之间是否需要空行\n    '@typescript-eslint/no-object-literal-type-assertion': 0, // 禁止使用 as Type\n    'react/no-find-dom-node': 0, // 禁止使用 findDOMNode\n    'no-param-reassign': [\n      // 禁止对函数参数再赋值\n      2,\n      {\n        props: false,\n      },\n    ],\n    'arrow-parens': 0, // 箭头函数参数括号\n    indent: 0, // 缩进\n    'operator-linebreak': [0], // 换行符位置\n    'max-classes-per-file': [2, 10], // 一个文件最多定义几个类\n    '@typescript-eslint/no-empty-function': [0], // 禁止空函数\n    'import/extensions': 0, // 禁止导入文件时带上文件后缀\n  },\n};\n"
  },
  {
    "path": "chat2db-client/.gitignore",
    "content": "/node_modules\n/.env.local\n/.umirc.local.ts\n/config/config.local.ts\n/src/.umi\n/src/.umi-production\n/src/.umi-test\n/src/main/node_modules\n/src/main/dist\n/dist\n.swc\n./yarn-error.log\n\n\n/release\n/static\n/versions"
  },
  {
    "path": "chat2db-client/.npmrc",
    "content": "registry=https://registry.npmmirror.com/\n\n"
  },
  {
    "path": "chat2db-client/.prettierignore",
    "content": "node_modules\n.umi\n.umi-production\n"
  },
  {
    "path": "chat2db-client/.prettierrc",
    "content": "{\n  \"printWidth\": 120,\n  \"singleQuote\": true,\n  \"trailingComma\": \"all\",\n  \"proseWrap\": \"never\",\n  \"overrides\": [{ \"files\": \".prettierrc\", \"options\": { \"parser\": \"json\" } }],\n  \"plugins\": [\"prettier-plugin-organize-imports\", \"prettier-plugin-packagejson\"]\n}"
  },
  {
    "path": "chat2db-client/.umirc.prod.desktop.ts",
    "content": "import { extractYarnConfig } from './src/utils/webpack';\nimport { defineConfig } from 'umi';\nconst MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');\n\nconst yarn_config = extractYarnConfig(process.argv);\n\nconst chainWebpack = (config: any, { webpack }: any) => {\n  config.plugin('monaco-editor').use(MonacoWebpackPlugin, [\n    {\n      languages: ['mysql', 'pgsql', 'sql'],\n    },\n  ]);\n};\n\nexport default defineConfig({\n  history: {\n    type: 'hash',\n  },\n  publicPath: './',\n  chainWebpack,\n  define: {\n    'process.env.UMI_ENV': process.env.UMI_ENV,\n  },\n  headScripts: [\n    `window.dataLayer = window.dataLayer || [];\n    function gtag() {\n      window.dataLayer.push(arguments);\n    }\n    gtag('js', new Date());\n    gtag('config', 'G-V8M4E5SF61', {\n      platform: 'DESKTOP',\n      version: '${yarn_config['app_version']}'\n    });`,\n  ],\n});\n"
  },
  {
    "path": "chat2db-client/.umirc.prod.ts",
    "content": "import { defineConfig } from 'umi';\nimport { extractYarnConfig } from './src/utils/webpack';\nconst MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');\nconst yarn_config = extractYarnConfig(process.argv);\nconst publicPath = yarn_config.public_path || './static/front/';\n\nconst chainWebpack = (config: any, { webpack }: any) => {\n  config.plugin('monaco-editor').use(MonacoWebpackPlugin, [\n    {\n      languages: ['mysql', 'pgsql', 'sql'],\n    },\n  ]);\n};\n\nexport default defineConfig({\n  publicPath: publicPath,\n  chainWebpack,\n  define: {\n    'process.env.UMI_ENV': process.env.UMI_ENV,\n  },\n  headScripts: [\n    `window.dataLayer = window.dataLayer || [];\n    function gtag() {\n      window.dataLayer.push(arguments);\n    }\n    gtag('js', new Date());\n    gtag('config', 'G-V8M4E5SF61', {\n      platform: 'WEB',\n      version: '${yarn_config['app_version']}'\n    });`,\n  ],\n});\n"
  },
  {
    "path": "chat2db-client/.umirc.ts",
    "content": "import { defineConfig } from 'umi';\nimport { extractYarnConfig, transitionTimezoneTimestamp } from './src/utils/webpack';\n\nconst MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');\n\n// yarn run build --app_port=xx 获取打包时命令行传入的参数\nconst yarn_config = extractYarnConfig(process.argv);\n\nconst chainWebpack = (config: any, { webpack }: any) => {\n  config.plugin('monaco-editor').use(MonacoWebpackPlugin, [\n    {\n      languages: ['mysql', 'pgsql', 'sql'],\n    },\n  ]);\n};\n\nexport default defineConfig({\n  title: 'Chat2DB',\n  base: '/',\n  publicPath: '/',\n  hash: true,\n  routes: [\n    {\n      path: '/',\n      component: '@/layouts/GlobalLayout',\n      routes: [\n        {\n          path: '/login',\n          component: '@/pages/login',\n        },\n        {\n          path: '/demo',\n          component: '@/pages/demo',\n        },\n        {\n          path: '/connections',\n          component: 'main',\n        },\n        {\n          path: '/dashboard',\n          component: 'main',\n        },\n        {\n          path: '/team',\n          component: 'main',\n        },\n        {\n          path: '/workspace',\n          component: 'main',\n        },\n        {\n          path: '/',\n          component: 'main',\n        },\n      ],\n    },\n  ],\n\n  npmClient: 'yarn',\n  dva: {},\n  plugins: ['@umijs/plugins/dist/dva'],\n  chainWebpack,\n  proxy: {\n    '/api': {\n      target: 'http://127.0.0.1:10821',\n      changeOrigin: true,\n    },\n    '/client/remaininguses/': {\n      target: 'http://127.0.0.1:1889',\n      changeOrigin: true,\n    },\n  },\n  targets: {\n    chrome: 80,\n  },\n  // links: [{\n  //   rel: 'manifest',\n  //   href: 'manifest.json',\n  // }],\n  links: [{ rel: 'icon', type: 'image/ico', sizes: '32x32', href: '/static/front/logo.ico' }],\n  headScripts: [\n    `if (localStorage.getItem('app-local-storage-versions') !== 'v4') {\n      localStorage.clear();\n      localStorage.setItem('app-local-storage-versions', 'v4');\n    }`,\n    // `if (window.electronApi) { window.electronApi.startServerForSpawn() }`,\n    // `if (\"serviceWorker\" in navigator) {\n    //   window.addEventListener(\"load\", function () {\n    //     navigator.serviceWorker\n    //       .register(\"sw.js\")\n    //       .then(res => console.log(\"service worker registered\"))\n    //       .catch(err => console.log(\"service worker not registered\", err));\n    //   })\n    // }`,\n    // `var deferredPrompt = null;\n    // window.addEventListener(\"beforeinstallprompt\", e => {\n    //   e.preventDefault();\n    //   deferredPrompt = e;\n    // });\n    // window.addEventListener(\"appinstalled\", () => {\n    //   deferredPrompt = null;\n    // })`,\n    {\n      src: 'https://www.googletagmanager.com/gtag/js?id=G-V8M4E5SF61',\n      async: true,\n    },\n    // `window.dataLayer = window.dataLayer || [];\n    // function gtag() {\n    //   window.dataLayer.push(arguments);\n    // }\n    // gtag('js', new Date());\n    // gtag('config', 'G-V8M4E5SF61', {\n    //   platform: 'WEB',\n    //   version: '1.0.0'\n    // });`,\n  ],\n  favicons: ['logo.ico'],\n  define: {\n    __ENV__: process.env.UMI_ENV,\n    __BUILD_TIME__: transitionTimezoneTimestamp(new Date().getTime()),\n    __APP_VERSION__: yarn_config.app_version || '0.0.0',\n    __APP_PORT__: yarn_config.app_port,\n  },\n  esbuildMinifyIIFE: true,\n});\n"
  },
  {
    "path": "chat2db-client/.vscode/settings.json",
    "content": "{\n  \"workbench.colorTheme\": \"One Dark Pro\",\n  \"workbench.iconTheme\": \"vscode-icons-mac\",\n  \"editor.fontSize\": 14,\n  \"editor.tabSize\": 2,\n  // 自动格式化\n  \"editor.formatOnType\": true,\n  \"editor.formatOnSave\": true,\n  \"prettier.bracketSpacing\": true, // 在对象，数组括号与文字之间加空格 \"{ foo: bar }\"\n  \"[typescriptreact]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[less]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"explorer.confirmDelete\": true,\n  \"emmet.includeLanguages\": {\n    \"javascript\": \"javascriptreact\"\n  },\n  \"git.mergeEditor\": false,\n  \"cSpell.words\": [\n    \"ahooks\",\n    \"antd\",\n    \"Appstore\",\n    \"asar\",\n    \"AZUREAI\",\n    \"bgcolor\",\n    \"Cascader\",\n    \"charsets\",\n    \"chatgpt\",\n    \"CLICKHOUSE\",\n    \"Consolas\",\n    \"datas\",\n    \"datasource\",\n    \"DATETIME\",\n    \"DBAI\",\n    \"dbhub\",\n    \"Dmaven\",\n    \"echart\",\n    \"echarts\",\n    \"favicons\",\n    \"findstr\",\n    \"fulltext\",\n    \"gtag\",\n    \"hexi\",\n    \"hljs\",\n    \"icns\",\n    \"Iconfont\",\n    \"IIFE\",\n    \"indexs\",\n    \"JDBC\",\n    \"KEYPAIR\",\n    \"KINGBASE\",\n    \"linebreak\",\n    \"lsof\",\n    \"MARIADB\",\n    \"Mddhhmmss\",\n    \"Menlo\",\n    \"netstat\",\n    \"NOCASE\",\n    \"nsis\",\n    \"OCEANBASE\",\n    \"OPENAI\",\n    \"packagejson\",\n    \"Parens\",\n    \"partialize\",\n    \"pgsql\",\n    \"plusplus\",\n    \"pnpm\",\n    \"POSTGRESQL\",\n    \"Prec\",\n    \"remaininguses\",\n    \"RESTAI\",\n    \"RTRIM\",\n    \"scrollbar\",\n    \"Sercurity\",\n    \"sortablejs\",\n    \"SQLSERVER\",\n    \"tailwindcss\",\n    \"Tigger\",\n    \"ueabe\",\n    \"ueabf\",\n    \"ueac\",\n    \"umijs\",\n    \"USERANDPASSWORD\",\n    \"uuidv\",\n    \"VARCHAR\",\n    \"VIEWCOLUMN\",\n    \"VIEWCOLUMNS\",\n    \"webp\",\n    \"wireframe\",\n    \"Wppk\",\n    \"yapi\",\n    \"zustand\"\n  ],\n  \"typescript.tsdk\": \"/Users/wangjiaqi/Desktop/Chat2DB/chat2db-client/node_modules/typescript/lib\"\n}\n"
  },
  {
    "path": "chat2db-client/mock/sqlResult.json",
    "content": "{\n  \"success\": true,\n  \"errorCode\": null,\n  \"errorMessage\": null,\n  \"data\": [\n    {\n      \"sql\": \"SELECT *\\nFROM students\\nLIMIT 500\",\n      \"description\": \"执行成功\",\n      \"message\": null,\n      \"success\": true,\n      \"headerList\": [\n        {\n          \"dataType\": \"NUMERIC\",\n          \"name\": \"id\"\n        },\n        {\n          \"dataType\": \"STRING\",\n          \"name\": \"name\"\n        },\n        {\n          \"dataType\": \"STRING\",\n          \"name\": \"gender\"\n        },\n        {\n          \"dataType\": \"DATETIME\",\n          \"name\": \"birthday\"\n        },\n        {\n          \"dataType\": \"STRING\",\n          \"name\": \"address\"\n        },\n        {\n          \"dataType\": \"STRING\",\n          \"name\": \"phone\"\n        },\n        {\n          \"dataType\": \"STRING\",\n          \"name\": \"email\"\n        },\n        {\n          \"dataType\": \"DATETIME\",\n          \"name\": \"create_time\"\n        },\n        {\n          \"dataType\": \"DATETIME\",\n          \"name\": \"update_time\"\n        }\n      ],\n      \"dataList\": [\n        [\n          \"1\",\n          \"张三\",\n          \"男\",\n          null,\n          \"北京市海淀区\",\n          \"12345678901\",\n          \"zhangsan@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"2\",\n          \"李四\",\n          \"男\",\n          null,\n          \"上海市浦东新区\",\n          \"12345678902\",\n          \"lisi@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"3\",\n          \"王五\",\n          \"女\",\n          null,\n          \"广州市天河区\",\n          \"12345678903\",\n          \"wangwu@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"4\",\n          \"赵六\",\n          \"男\",\n          null,\n          \"深圳市南山区\",\n          \"12345678904\",\n          \"zhaoliu@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"5\",\n          \"陈七\",\n          \"女\",\n          null,\n          \"武汉市江汉区\",\n          \"12345678905\",\n          \"chenqi@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"6\",\n          \"刘八\",\n          \"男\",\n          null,\n          \"成都市高新区\",\n          \"12345678906\",\n          \"liuba@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"7\",\n          \"魏九\",\n          \"女\",\n          null,\n          \"重庆市渝北区\",\n          \"12345678907\",\n          \"weijiu@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"8\",\n          \"孙十\",\n          \"男\",\n          null,\n          \"南京市鼓楼区\",\n          \"12345678908\",\n          \"sunshi@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"9\",\n          \"郑十一\",\n          \"男\",\n          null,\n          \"西安市雁塔区\",\n          \"12345678909\",\n          \"zhengshiyi@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ],\n        [\n          \"10\",\n          \"许十二\",\n          \"女\",\n          null,\n          \"苏州市姑苏区\",\n          \"12345678910\",\n          \"xushier@example.com\",\n          \"2023-05-31 10:41:56.000\",\n          \"2023-05-31 10:41:56.000\"\n        ]\n      ],\n      \"sqlType\": \"SELECT\",\n      \"hasNextPage\": false,\n      \"pageNo\": 1,\n      \"pageSize\": 500,\n      \"duration\": 6\n    }\n  ],\n  \"traceId\": null\n}\n"
  },
  {
    "path": "chat2db-client/package.json",
    "content": "{\n  \"name\": \"chat2db\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/chat2db/Chat2DB\"\n  },\n  \"author\": \"fjy, hexi\",\n  \"main\": \"src/main/main.js\",\n  \"scripts\": {\n    \"build\": \"npm run build:web && npm run build:main\",\n    \"build:desktop\": \"npm run build:web:desktop && npm run build:main:prod\",\n    \"build:main\": \"cross-env NODE_ENV=development electron-builder\",\n    \"build:main:prod\": \"cross-env NODE_ENV=production electron-builder\",\n    \"build:prod\": \"npm run build:web:prod && npm run build:main:prod\",\n    \"build:web\": \"umi build\",\n    \"build:web:desktop\": \"cross-env UMI_ENV=desktop cross-env APP_VERSION=${npm_config_app_version} cross-env APP_PORT=${npm_config_app_port} umi build\",\n    \"build:web:prod\": \"cross-env UMI_ENV=prod cross-env APP_VERSION=${npm_config_app_version} cross-env APP_PORT=${npm_config_app_port} cross-env UMI_PublicPath=${npm_config_public_path} umi build\",\n    \"postinstall\": \"umi setup\",\n    \"lint\": \"umi lint\",\n    \"start\": \"concurrently \\\"npm run start:web\\\" \\\"npm run start:main\\\"\",\n    \"start:main\": \"cross-env NODE_ENV=development electron .\",\n    \"start:main:prod\": \"cross-env NODE_ENV=production electron .\",\n    \"start:web\": \"cross-env UMI_ENV=local HMR=none cross-env APP_VERSION=${npm_config_app_version} umi dev\",\n    \"start:web:hot\": \"cross-env UMI_ENV=local cross-env APP_VERSION=${npm_config_app_version} umi dev\"\n  },\n  \"dependencies\": {\n    \"@dnd-kit/modifiers\": \"^6.0.1\",\n    \"ahooks\": \"^3.7.8\",\n    \"ali-react-table\": \"^2.6.1\",\n    \"antd\": \"^5.12.1\",\n    \"copy-to-clipboard\": \"^3.3.3\",\n    \"echarts\": \"^5.4.2\",\n    \"echarts-for-react\": \"^3.0.2\",\n    \"event-source-polyfill\": \"^1.0.31\",\n    \"highlight.js\": \"^11.9.0\",\n    \"lodash\": \"^4.17.21\",\n    \"lucide-react\": \"^0.365.0\",\n    \"markdown-it-link-attributes\": \"^4.0.1\",\n    \"monaco-editor\": \"^0.44.0\",\n    \"monaco-editor-esm-webpack-plugin\": \"^2.1.0\",\n    \"monaco-editor-webpack-plugin\": \"^7.0.1\",\n    \"react-monaco-editor\": \"^0.54.0\",\n    \"react-sortablejs\": \"^6.1.4\",\n    \"sql-formatter\": \"^13.0.4\",\n    \"styled-components\": \"^6.0.1\",\n    \"umi\": \"^4.0.87\",\n    \"umi-request\": \"^1.4.0\",\n    \"uuid\": \"^9.0.0\",\n    \"zustand\": \"^4.4.4\"\n  },\n  \"devDependencies\": {\n    \"@types/event-source-polyfill\": \"^1.0.1\",\n    \"@types/lodash\": \"^4.14.195\",\n    \"@types/react\": \"^18.0.33\",\n    \"@types/react-dom\": \"^18.0.11\",\n    \"@types/uuid\": \"^9.0.1\",\n    \"@typescript-eslint/eslint-plugin\": \"^6.7.2\",\n    \"@typescript-eslint/parser\": \"^6.7.2\",\n    \"@umijs/plugins\": \"^4.0.55\",\n    \"concurrently\": \"^8.1.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"electron\": \"^22.3.0\",\n    \"electron-builder\": \"^23.6.0\",\n    \"electron-debug\": \"^3.2.0\",\n    \"eslint\": \"^8.49.0\",\n    \"eslint-config-airbnb-base\": \"^15.0.0\",\n    \"eslint-config-prettier\": \"^9.0.0\",\n    \"eslint-import-resolver-webpack\": \"^0.13.7\",\n    \"eslint-plugin-babel\": \"^5.3.1\",\n    \"eslint-plugin-import\": \"^2.28.1\",\n    \"eslint-plugin-prettier\": \"^5.0.0\",\n    \"eslint-plugin-react\": \"^7.33.2\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"is-electron\": \"^2.2.2\",\n    \"prettier\": \"^2\",\n    \"prettier-plugin-organize-imports\": \"^2\",\n    \"prettier-plugin-packagejson\": \"^2\",\n    \"tailwindcss\": \"^3\",\n    \"typescript\": \"^5.0.3\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^16.8.0\",\n    \"react-dom\": \"^16.8.0\"\n  },\n  \"engines\": {\n    \"node\": \">=16\"\n  },\n  \"build\": {\n    \"appId\": \"com.chat2db\",\n    \"directories\": {\n      \"output\": \"release/\"\n    },\n    \"productName\": \"Chat2DB\",\n    \"asar\": false,\n    \"files\": [\n      \"dist/**/*\",\n      \"src/main\",\n      \"static/\",\n      \"versions/**/*\",\n      \"package.json\",\n      \"!node_modules/**/*\"\n    ],\n    \"nsis\": {\n      \"oneClick\": false,\n      \"perMachine\": false,\n      \"allowElevation\": true,\n      \"allowToChangeInstallationDirectory\": true,\n      \"createDesktopShortcut\": true,\n      \"deleteAppDataOnUninstall\": false,\n      \"shortcutName\": \"Chat2DB\"\n    },\n    \"mac\": {\n      \"icon\": \"src/assets/logo/logo.icns\",\n      \"target\": [\n        \"zip\",\n        \"dmg\"\n      ]\n    },\n    \"win\": {\n      \"target\": [\n        {\n          \"target\": \"nsis\"\n        }\n      ],\n      \"publisherName\": \"Chat2DB\",\n      \"icon\": \"src/assets/logo/logo.ico\"\n    },\n    \"linux\": {\n      \"maintainer\": \"Chat2DB, huanyueyaoqin@qq.com\",\n      \"category\": \"Network;\",\n      \"target\": [\n        \"AppImage\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/readme.md",
    "content": "## 技术选型\n\n1. 脚手架：umi v4\n2. 组件库：antd v5\n3. 状态管理库 dva\n4. 图表库\n5. 国际化\n\n目录结构 tree ./ -L 2 -I node_modules\n\n## 启动项目\n\n\n强制使用 yarn，因为环境变量、lock 文件只维护了 yarn，npm/pnpm 可能会产生意想不到的 bug node 版本要求 16 以上 `npm i -g yarn` `yarn` `yarn run build:web:prod` `cp -r dist ../chat2db-server/chat2db-server-start/src/main/resources/static/front` (复制打包结果到指定目录。windows 可能命令不一样，可以手动复制下) 之后就可以启动后端了 `mvn clean package -B '-Dmaven.test.skip=true' -f chat2db-server/pom.xml`\n\n启动前端项目调试 `yarn run start:web` 注意：因为 electron 包比较难下载，如果 yarn 时 electron 下载失败或超时，可以删除掉 chat2db-client/package.json 下的 electron，再次 yarn\n\n## TS书写规范\n\n  1. 所有的interface 与 type 必须已I开头\n    `interface IState { name: string }` // good\n    `interface State { name: string }` // bad\n\n## 如何在 js 与 css 中使用颜色\n\n具体转换在 /theme/index.ts 中的 injectThemeVar\n\n- js 在 window.\\_AppThemePack 中去取 eg：`window._AppThemePack.controlItemBgActive` // good\n- css eg: `background: var(--control-item-bg-active)` // good\n- css `color: #fff` // bad\n\n## 如何使用国际化\n\n所有 key 参考格式为 `模块名称.文案类型.文案描述`。若文案包含可变部分，可使用 `{1}`、`{2}`、`{3}` 代替。\n\n`src/i18n/index.ts` 中默认导出 `i18n` 转换方法，可以将 key 转换为对应的实际文案。文案中的 `{1}` 将被替换为第二个入参，以此类推。例如：\n\n```tsx\n// 'home.tip.welcome': '欢迎您，{1}！'\ni18n('home.tip.welcome', user.name); // => '欢迎您，张三！'\n```\n\n也可以使用 `src/i18n/index.ts` 中导出的 `i18nElement` 方法，可以将文案中的占位符替换为 JSX 元素。例如：\n\n```tsx\ni18nElement('home.tip.welcome', <b>{user.name}</b>); // => <>欢迎您，<b>张三</b>！</>'\n```\n\n```code\n├── dist\n│   ├── index.html\n│   ├── layouts__index.async.js\n│   ├── layouts__index.chunk.css\n│   ├── p__docs.async.js\n│   ├── p__index.async.js\n│   └── umi.js\n├── package.json\n├── readme.md\n├── release\n│   ├── Chat2DB-1.0.0-arm64-mac.zip\n│   ├── Chat2DB-1.0.0-arm64-mac.zip.blockmap\n│   ├── Chat2DB-1.0.0-arm64.dmg\n│   ├── Chat2DB-1.0.0-arm64.dmg.blockmap\n│   ├── builder-debug.yml\n│   ├── builder-effective-config.yaml\n│   └── mac-arm64\n├── src\n│   ├── assets\n│   ├── blocks\n│   ├── components\n│   ├── config\n│   ├── constant\n│   ├── layouts\n│   ├── locales\n│   ├── main\n│   ├── models\n│   ├── pages\n│   ├── typings\n│   └── utils\n├── tsconfig.json\n├── typings.d.ts\n└── yarn.lock\n```\n"
  },
  {
    "path": "chat2db-client/src/assets/font/demo.css",
    "content": "/* Logo 字体 */\n@font-face {\n  font-family: \"iconfont logo\";\n  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');\n  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),\n    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),\n    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),\n    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');\n}\n\n.logo {\n  font-family: \"iconfont logo\";\n  font-size: 160px;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n/* tabs */\n.nav-tabs {\n  position: relative;\n}\n\n.nav-tabs .nav-more {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  height: 42px;\n  line-height: 42px;\n  color: #666;\n}\n\n#tabs {\n  border-bottom: 1px solid #eee;\n}\n\n#tabs li {\n  cursor: pointer;\n  width: 100px;\n  height: 40px;\n  line-height: 40px;\n  text-align: center;\n  font-size: 16px;\n  border-bottom: 2px solid transparent;\n  position: relative;\n  z-index: 1;\n  margin-bottom: -1px;\n  color: #666;\n}\n\n\n#tabs .active {\n  border-bottom-color: #f00;\n  color: #222;\n}\n\n.tab-container .content {\n  display: none;\n}\n\n/* 页面布局 */\n.main {\n  padding: 30px 100px;\n  width: 960px;\n  margin: 0 auto;\n}\n\n.main .logo {\n  color: #333;\n  text-align: left;\n  margin-bottom: 30px;\n  line-height: 1;\n  height: 110px;\n  margin-top: -50px;\n  overflow: hidden;\n  *zoom: 1;\n}\n\n.main .logo a {\n  font-size: 160px;\n  color: #333;\n}\n\n.helps {\n  margin-top: 40px;\n}\n\n.helps pre {\n  padding: 20px;\n  margin: 10px 0;\n  border: solid 1px #e7e1cd;\n  background-color: #fffdef;\n  overflow: auto;\n}\n\n.icon_lists {\n  width: 100% !important;\n  overflow: hidden;\n  *zoom: 1;\n}\n\n.icon_lists li {\n  width: 100px;\n  margin-bottom: 10px;\n  margin-right: 20px;\n  text-align: center;\n  list-style: none !important;\n  cursor: default;\n}\n\n.icon_lists li .code-name {\n  line-height: 1.2;\n}\n\n.icon_lists .icon {\n  display: block;\n  height: 100px;\n  line-height: 100px;\n  font-size: 42px;\n  margin: 10px auto;\n  color: #333;\n  -webkit-transition: font-size 0.25s linear, width 0.25s linear;\n  -moz-transition: font-size 0.25s linear, width 0.25s linear;\n  transition: font-size 0.25s linear, width 0.25s linear;\n}\n\n.icon_lists .icon:hover {\n  font-size: 100px;\n}\n\n.icon_lists .svg-icon {\n  /* 通过设置 font-size 来改变图标大小 */\n  width: 1em;\n  /* 图标和文字相邻时，垂直对齐 */\n  vertical-align: -0.15em;\n  /* 通过设置 color 来改变 SVG 的颜色/fill */\n  fill: currentColor;\n  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示\n      normalize.css 中也包含这行 */\n  overflow: hidden;\n}\n\n.icon_lists li .name,\n.icon_lists li .code-name {\n  color: #666;\n}\n\n/* markdown 样式 */\n.markdown {\n  color: #666;\n  font-size: 14px;\n  line-height: 1.8;\n}\n\n.highlight {\n  line-height: 1.5;\n}\n\n.markdown img {\n  vertical-align: middle;\n  max-width: 100%;\n}\n\n.markdown h1 {\n  color: #404040;\n  font-weight: 500;\n  line-height: 40px;\n  margin-bottom: 24px;\n}\n\n.markdown h2,\n.markdown h3,\n.markdown h4,\n.markdown h5,\n.markdown h6 {\n  color: #404040;\n  margin: 1.6em 0 0.6em 0;\n  font-weight: 500;\n  clear: both;\n}\n\n.markdown h1 {\n  font-size: 28px;\n}\n\n.markdown h2 {\n  font-size: 22px;\n}\n\n.markdown h3 {\n  font-size: 16px;\n}\n\n.markdown h4 {\n  font-size: 14px;\n}\n\n.markdown h5 {\n  font-size: 12px;\n}\n\n.markdown h6 {\n  font-size: 12px;\n}\n\n.markdown hr {\n  height: 1px;\n  border: 0;\n  background: #e9e9e9;\n  margin: 16px 0;\n  clear: both;\n}\n\n.markdown p {\n  margin: 1em 0;\n}\n\n.markdown>p,\n.markdown>blockquote,\n.markdown>.highlight,\n.markdown>ol,\n.markdown>ul {\n  width: 80%;\n}\n\n.markdown ul>li {\n  list-style: circle;\n}\n\n.markdown>ul li,\n.markdown blockquote ul>li {\n  margin-left: 20px;\n  padding-left: 4px;\n}\n\n.markdown>ul li p,\n.markdown>ol li p {\n  margin: 0.6em 0;\n}\n\n.markdown ol>li {\n  list-style: decimal;\n}\n\n.markdown>ol li,\n.markdown blockquote ol>li {\n  margin-left: 20px;\n  padding-left: 4px;\n}\n\n.markdown code {\n  margin: 0 3px;\n  padding: 0 5px;\n  background: #eee;\n  border-radius: 3px;\n}\n\n.markdown strong,\n.markdown b {\n  font-weight: 600;\n}\n\n.markdown>table {\n  border-collapse: collapse;\n  border-spacing: 0px;\n  empty-cells: show;\n  border: 1px solid #e9e9e9;\n  width: 95%;\n  margin-bottom: 24px;\n}\n\n.markdown>table th {\n  white-space: nowrap;\n  color: #333;\n  font-weight: 600;\n}\n\n.markdown>table th,\n.markdown>table td {\n  border: 1px solid #e9e9e9;\n  padding: 8px 16px;\n  text-align: left;\n}\n\n.markdown>table th {\n  background: #F7F7F7;\n}\n\n.markdown blockquote {\n  font-size: 90%;\n  color: #999;\n  border-left: 4px solid #e9e9e9;\n  padding-left: 0.8em;\n  margin: 1em 0;\n}\n\n.markdown blockquote p {\n  margin: 0;\n}\n\n.markdown .anchor {\n  opacity: 0;\n  transition: opacity 0.3s ease;\n  margin-left: 8px;\n}\n\n.markdown .waiting {\n  color: #ccc;\n}\n\n.markdown h1:hover .anchor,\n.markdown h2:hover .anchor,\n.markdown h3:hover .anchor,\n.markdown h4:hover .anchor,\n.markdown h5:hover .anchor,\n.markdown h6:hover .anchor {\n  opacity: 1;\n  display: inline-block;\n}\n\n.markdown>br,\n.markdown>p>br {\n  clear: both;\n}\n\n\n.hljs {\n  display: block;\n  background: white;\n  padding: 0.5em;\n  color: #333333;\n  overflow-x: auto;\n}\n\n.hljs-comment,\n.hljs-meta {\n  color: #969896;\n}\n\n.hljs-string,\n.hljs-variable,\n.hljs-template-variable,\n.hljs-strong,\n.hljs-emphasis,\n.hljs-quote {\n  color: #df5000;\n}\n\n.hljs-keyword,\n.hljs-selector-tag,\n.hljs-type {\n  color: #a71d5d;\n}\n\n.hljs-literal,\n.hljs-symbol,\n.hljs-bullet,\n.hljs-attribute {\n  color: #0086b3;\n}\n\n.hljs-section,\n.hljs-name {\n  color: #63a35c;\n}\n\n.hljs-tag {\n  color: #333333;\n}\n\n.hljs-title,\n.hljs-attr,\n.hljs-selector-id,\n.hljs-selector-class,\n.hljs-selector-attr,\n.hljs-selector-pseudo {\n  color: #795da3;\n}\n\n.hljs-addition {\n  color: #55a532;\n  background-color: #eaffea;\n}\n\n.hljs-deletion {\n  color: #bd2c00;\n  background-color: #ffecec;\n}\n\n.hljs-link {\n  text-decoration: underline;\n}\n\n/* 代码高亮 */\n/* PrismJS 1.15.0\nhttps://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */\n/**\n * prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: black;\n  background: none;\n  text-shadow: 0 1px white;\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n\n  -moz-tab-size: 4;\n  -o-tab-size: 4;\n  tab-size: 4;\n\n  -webkit-hyphens: none;\n  -moz-hyphens: none;\n  -ms-hyphens: none;\n  hyphens: none;\n}\n\npre[class*=\"language-\"]::-moz-selection,\npre[class*=\"language-\"] ::-moz-selection,\ncode[class*=\"language-\"]::-moz-selection,\ncode[class*=\"language-\"] ::-moz-selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\n\npre[class*=\"language-\"]::selection,\npre[class*=\"language-\"] ::selection,\ncode[class*=\"language-\"]::selection,\ncode[class*=\"language-\"] ::selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\n\n@media print {\n\n  code[class*=\"language-\"],\n  pre[class*=\"language-\"] {\n    text-shadow: none;\n  }\n}\n\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: .5em 0;\n  overflow: auto;\n}\n\n:not(pre)>code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #f5f2f0;\n}\n\n/* Inline code */\n:not(pre)>code[class*=\"language-\"] {\n  padding: .1em;\n  border-radius: .3em;\n  white-space: normal;\n}\n\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: slategray;\n}\n\n.token.punctuation {\n  color: #999;\n}\n\n.namespace {\n  opacity: .7;\n}\n\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #905;\n}\n\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #690;\n}\n\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n  color: #9a6e3a;\n  background: hsla(0, 0%, 100%, .5);\n}\n\n.token.atrule,\n.token.attr-value,\n.token.keyword {\n  color: #07a;\n}\n\n.token.function,\n.token.class-name {\n  color: #DD4A68;\n}\n\n.token.regex,\n.token.important,\n.token.variable {\n  color: #e90;\n}\n\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n\n.token.italic {\n  font-style: italic;\n}\n\n.token.entity {\n  cursor: help;\n}\n"
  },
  {
    "path": "chat2db-client/src/assets/font/demo_index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\"/>\n  <title>iconfont Demo</title>\n  <link rel=\"shortcut icon\" href=\"//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg\" type=\"image/x-icon\"/>\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg\"/>\n  <link rel=\"stylesheet\" href=\"https://g.alicdn.com/thx/cube/1.3.2/cube.min.css\">\n  <link rel=\"stylesheet\" href=\"demo.css\">\n  <link rel=\"stylesheet\" href=\"iconfont.css\">\n  <script src=\"iconfont.js\"></script>\n  <!-- jQuery -->\n  <script src=\"https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js\"></script>\n  <!-- 代码高亮 -->\n  <script src=\"https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js\"></script>\n  <style>\n    .main .logo {\n      margin-top: 0;\n      height: auto;\n    }\n\n    .main .logo a {\n      display: flex;\n      align-items: center;\n    }\n\n    .main .logo .sub-title {\n      margin-left: 0.5em;\n      font-size: 22px;\n      color: #fff;\n      background: linear-gradient(-45deg, #3967FF, #B500FE);\n      -webkit-background-clip: text;\n      -webkit-text-fill-color: transparent;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"main\">\n    <h1 class=\"logo\"><a href=\"https://www.iconfont.cn/\" title=\"iconfont 首页\" target=\"_blank\">\n      <img width=\"200\" src=\"https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg\">\n      \n    </a></h1>\n    <div class=\"nav-tabs\">\n      <ul id=\"tabs\" class=\"dib-box\">\n        <li class=\"dib active\"><span>Unicode</span></li>\n        <li class=\"dib\"><span>Font class</span></li>\n        <li class=\"dib\"><span>Symbol</span></li>\n      </ul>\n      \n      <a href=\"https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=3633546\" target=\"_blank\" class=\"nav-more\">查看项目</a>\n      \n    </div>\n    <div class=\"tab-container\">\n      <div class=\"content unicode\" style=\"display: block;\">\n          <ul class=\"icon_lists dib-box\">\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe672;</span>\n                <div class=\"name\">right_on_5</div>\n                <div class=\"code-name\">&amp;#xe672;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe673;</span>\n                <div class=\"name\">right_off_5-01</div>\n                <div class=\"code-name\">&amp;#xe673;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe674;</span>\n                <div class=\"name\">left_on_2</div>\n                <div class=\"code-name\">&amp;#xe674;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe670;</span>\n                <div class=\"name\">left_off</div>\n                <div class=\"code-name\">&amp;#xe670;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe671;</span>\n                <div class=\"name\">minimize21</div>\n                <div class=\"code-name\">&amp;#xe671;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe66b;</span>\n                <div class=\"name\">restore</div>\n                <div class=\"code-name\">&amp;#xe66b;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe66e;</span>\n                <div class=\"name\">resize</div>\n                <div class=\"code-name\">&amp;#xe66e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe66f;</span>\n                <div class=\"name\">close</div>\n                <div class=\"code-name\">&amp;#xe66f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe66a;</span>\n                <div class=\"name\">筛选</div>\n                <div class=\"code-name\">&amp;#xe66a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe69a;</span>\n                <div class=\"name\">排序</div>\n                <div class=\"code-name\">&amp;#xe69a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe8e8;</span>\n                <div class=\"name\">305信息-线性圆框</div>\n                <div class=\"code-name\">&amp;#xe8e8;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe726;</span>\n                <div class=\"name\">加号</div>\n                <div class=\"code-name\">&amp;#xe726;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec6b;</span>\n                <div class=\"name\">列表</div>\n                <div class=\"code-name\">&amp;#xec6b;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe65d;</span>\n                <div class=\"name\">减去</div>\n                <div class=\"code-name\">&amp;#xe65d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe669;</span>\n                <div class=\"name\">database</div>\n                <div class=\"code-name\">&amp;#xe669;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe888;</span>\n                <div class=\"name\">筛选</div>\n                <div class=\"code-name\">&amp;#xe888;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe668;</span>\n                <div class=\"name\">刷新</div>\n                <div class=\"code-name\">&amp;#xe668;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeb78;</span>\n                <div class=\"name\">加号_o</div>\n                <div class=\"code-name\">&amp;#xeb78;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6a9;</span>\n                <div class=\"name\">数据库_jurassic</div>\n                <div class=\"code-name\">&amp;#xe6a9;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe667;</span>\n                <div class=\"name\">权限</div>\n                <div class=\"code-name\">&amp;#xe667;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe816;</span>\n                <div class=\"name\">sharpicons_add-database</div>\n                <div class=\"code-name\">&amp;#xe816;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe663;</span>\n                <div class=\"name\">组织管理</div>\n                <div class=\"code-name\">&amp;#xe663;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe691;</span>\n                <div class=\"name\">空间</div>\n                <div class=\"code-name\">&amp;#xe691;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#x100be;</span>\n                <div class=\"name\">下箭头-copy</div>\n                <div class=\"code-name\">&amp;#x100be;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe788;</span>\n                <div class=\"name\">查看</div>\n                <div class=\"code-name\">&amp;#xe788;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe8db;</span>\n                <div class=\"name\">clone</div>\n                <div class=\"code-name\">&amp;#xe8db;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe687;</span>\n                <div class=\"name\">提交</div>\n                <div class=\"code-name\">&amp;#xe687;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe665;</span>\n                <div class=\"name\">查看</div>\n                <div class=\"code-name\">&amp;#xe665;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec7a;</span>\n                <div class=\"name\">复制</div>\n                <div class=\"code-name\">&amp;#xec7a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe686;</span>\n                <div class=\"name\">icon_answer</div>\n                <div class=\"code-name\">&amp;#xe686;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6a8;</span>\n                <div class=\"name\">icon_question</div>\n                <div class=\"code-name\">&amp;#xe6a8;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#x100bd;</span>\n                <div class=\"name\">发送</div>\n                <div class=\"code-name\">&amp;#x100bd;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe662;</span>\n                <div class=\"name\">重启</div>\n                <div class=\"code-name\">&amp;#xe662;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6cc;</span>\n                <div class=\"name\">提醒</div>\n                <div class=\"code-name\">&amp;#xe6cc;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe661;</span>\n                <div class=\"name\">提醒</div>\n                <div class=\"code-name\">&amp;#xe661;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe716;</span>\n                <div class=\"name\">提醒</div>\n                <div class=\"code-name\">&amp;#xe716;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe69c;</span>\n                <div class=\"name\">升级</div>\n                <div class=\"code-name\">&amp;#xe69c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe659;</span>\n                <div class=\"name\">全局_升级</div>\n                <div class=\"code-name\">&amp;#xe659;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe65c;</span>\n                <div class=\"name\">关于我们</div>\n                <div class=\"code-name\">&amp;#xe65c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe67d;</span>\n                <div class=\"name\">ico版本更新</div>\n                <div class=\"code-name\">&amp;#xe67d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe657;</span>\n                <div class=\"name\">对话气泡</div>\n                <div class=\"code-name\">&amp;#xe657;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe658;</span>\n                <div class=\"name\">角色权限</div>\n                <div class=\"code-name\">&amp;#xe658;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe654;</span>\n                <div class=\"name\">preview</div>\n                <div class=\"code-name\">&amp;#xe654;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe653;</span>\n                <div class=\"name\">导入</div>\n                <div class=\"code-name\">&amp;#xe653;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe652;</span>\n                <div class=\"name\">终止</div>\n                <div class=\"code-name\">&amp;#xe652;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6b2;</span>\n                <div class=\"name\">退出</div>\n                <div class=\"code-name\">&amp;#xe6b2;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6bb;</span>\n                <div class=\"name\">控桩终端</div>\n                <div class=\"code-name\">&amp;#xe6bb;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6e2;</span>\n                <div class=\"name\">撤销</div>\n                <div class=\"code-name\">&amp;#xe6e2;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe650;</span>\n                <div class=\"name\">向上</div>\n                <div class=\"code-name\">&amp;#xe650;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe651;</span>\n                <div class=\"name\">查看</div>\n                <div class=\"code-name\">&amp;#xe651;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6f2;</span>\n                <div class=\"name\">编辑数据_编辑录入操作_jurassic</div>\n                <div class=\"code-name\">&amp;#xe6f2;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6f3;</span>\n                <div class=\"name\">编辑表格_编辑录入操作_jurassic</div>\n                <div class=\"code-name\">&amp;#xe6f3;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe7b5;</span>\n                <div class=\"name\">报表数据录入</div>\n                <div class=\"code-name\">&amp;#xe7b5;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe656;</span>\n                <div class=\"name\">播放5</div>\n                <div class=\"code-name\">&amp;#xe656;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe64f;</span>\n                <div class=\"name\">清空@3x</div>\n                <div class=\"code-name\">&amp;#xe64f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe64e;</span>\n                <div class=\"name\">删除</div>\n                <div class=\"code-name\">&amp;#xe64e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe792;</span>\n                <div class=\"name\">new-document-worksheet</div>\n                <div class=\"code-name\">&amp;#xe792;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe7b7;</span>\n                <div class=\"name\">file-excel</div>\n                <div class=\"code-name\">&amp;#xe7b7;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe7b8;</span>\n                <div class=\"name\">file-markdown</div>\n                <div class=\"code-name\">&amp;#xe7b8;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe7ba;</span>\n                <div class=\"name\">file-word</div>\n                <div class=\"code-name\">&amp;#xe7ba;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe87d;</span>\n                <div class=\"name\">HTML5</div>\n                <div class=\"code-name\">&amp;#xe87d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe64d;</span>\n                <div class=\"name\">HTML</div>\n                <div class=\"code-name\">&amp;#xe64d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe67a;</span>\n                <div class=\"name\">pdf</div>\n                <div class=\"code-name\">&amp;#xe67a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe64c;</span>\n                <div class=\"name\">个人用户</div>\n                <div class=\"code-name\">&amp;#xe64c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe64b;</span>\n                <div class=\"name\">后台管理</div>\n                <div class=\"code-name\">&amp;#xe64b;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec83;</span>\n                <div class=\"name\">字体代码</div>\n                <div class=\"code-name\">&amp;#xec83;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe70c;</span>\n                <div class=\"name\">版本</div>\n                <div class=\"code-name\">&amp;#xe70c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe73c;</span>\n                <div class=\"name\">车位管理</div>\n                <div class=\"code-name\">&amp;#xe73c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe64a;</span>\n                <div class=\"name\">dictate</div>\n                <div class=\"code-name\">&amp;#xe64a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe76a;</span>\n                <div class=\"name\">circle-f</div>\n                <div class=\"code-name\">&amp;#xe76a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6fd;</span>\n                <div class=\"name\">图表-函数</div>\n                <div class=\"code-name\">&amp;#xe6fd;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe647;</span>\n                <div class=\"name\">视图管理器</div>\n                <div class=\"code-name\">&amp;#xe647;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe643;</span>\n                <div class=\"name\">回车</div>\n                <div class=\"code-name\">&amp;#xe643;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe642;</span>\n                <div class=\"name\">缺省</div>\n                <div class=\"code-name\">&amp;#xe642;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe88e;</span>\n                <div class=\"name\">进入箭头</div>\n                <div class=\"code-name\">&amp;#xe88e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe641;</span>\n                <div class=\"name\">右箭头</div>\n                <div class=\"code-name\">&amp;#xe641;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe660;</span>\n                <div class=\"name\">向右箭头</div>\n                <div class=\"code-name\">&amp;#xe660;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe640;</span>\n                <div class=\"name\">数据源</div>\n                <div class=\"code-name\">&amp;#xe640;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe67c;</span>\n                <div class=\"name\">question</div>\n                <div class=\"code-name\">&amp;#xe67c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe63a;</span>\n                <div class=\"name\">星星-copy</div>\n                <div class=\"code-name\">&amp;#xe63a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe69f;</span>\n                <div class=\"name\">控制台</div>\n                <div class=\"code-name\">&amp;#xe69f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe639;</span>\n                <div class=\"name\">星系</div>\n                <div class=\"code-name\">&amp;#xe639;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe638;</span>\n                <div class=\"name\">暂无数据 (1)</div>\n                <div class=\"code-name\">&amp;#xe638;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe637;</span>\n                <div class=\"name\">开始</div>\n                <div class=\"code-name\">&amp;#xe637;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe634;</span>\n                <div class=\"name\">关闭</div>\n                <div class=\"code-name\">&amp;#xe634;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeb6d;</span>\n                <div class=\"name\">下箭头</div>\n                <div class=\"code-name\">&amp;#xeb6d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe633;</span>\n                <div class=\"name\">more</div>\n                <div class=\"code-name\">&amp;#xe633;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe630;</span>\n                <div class=\"name\">设置</div>\n                <div class=\"code-name\">&amp;#xe630;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe628;</span>\n                <div class=\"name\">对话-未选</div>\n                <div class=\"code-name\">&amp;#xe628;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe629;</span>\n                <div class=\"name\">图表-未选</div>\n                <div class=\"code-name\">&amp;#xe629;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe62b;</span>\n                <div class=\"name\">编组 13备份 3</div>\n                <div class=\"code-name\">&amp;#xe62b;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe616;</span>\n                <div class=\"name\">编组备份</div>\n                <div class=\"code-name\">&amp;#xe616;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe618;</span>\n                <div class=\"name\">表格</div>\n                <div class=\"code-name\">&amp;#xe618;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe61d;</span>\n                <div class=\"name\">收藏 (1)</div>\n                <div class=\"code-name\">&amp;#xe61d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe621;</span>\n                <div class=\"name\">guthub-未选</div>\n                <div class=\"code-name\">&amp;#xe621;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe622;</span>\n                <div class=\"name\">数据-未选</div>\n                <div class=\"code-name\">&amp;#xe622;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe624;</span>\n                <div class=\"name\">编组 4</div>\n                <div class=\"code-name\">&amp;#xe624;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe627;</span>\n                <div class=\"name\">编组 14备份</div>\n                <div class=\"code-name\">&amp;#xe627;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe615;</span>\n                <div class=\"name\">guthub-未选</div>\n                <div class=\"code-name\">&amp;#xe615;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeabe;</span>\n                <div class=\"name\">24gl-folderMinus</div>\n                <div class=\"code-name\">&amp;#xeabe;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeabf;</span>\n                <div class=\"name\">24gl-folderOpen</div>\n                <div class=\"code-name\">&amp;#xeabf;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeac7;</span>\n                <div class=\"name\">24gf-folderOpen</div>\n                <div class=\"code-name\">&amp;#xeac7;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe744;</span>\n                <div class=\"name\">云数据库</div>\n                <div class=\"code-name\">&amp;#xe744;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe612;</span>\n                <div class=\"name\">报表</div>\n                <div class=\"code-name\">&amp;#xe612;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe614;</span>\n                <div class=\"name\">工作台</div>\n                <div class=\"code-name\">&amp;#xe614;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec21;</span>\n                <div class=\"name\">mongodb</div>\n                <div class=\"code-name\">&amp;#xec21;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6a2;</span>\n                <div class=\"name\">Redis</div>\n                <div class=\"code-name\">&amp;#xe6a2;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe60e;</span>\n                <div class=\"name\">HIVE_2</div>\n                <div class=\"code-name\">&amp;#xe60e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6a0;</span>\n                <div class=\"name\">Kingbase</div>\n                <div class=\"code-name\">&amp;#xe6a0;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe60d;</span>\n                <div class=\"name\">仪表盘</div>\n                <div class=\"code-name\">&amp;#xe60d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe60b;</span>\n                <div class=\"name\">presto</div>\n                <div class=\"code-name\">&amp;#xe60b;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe60a;</span>\n                <div class=\"name\">DB2</div>\n                <div class=\"code-name\">&amp;#xe60a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe982;</span>\n                <div class=\"name\">oceanbase</div>\n                <div class=\"code-name\">&amp;#xe982;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe655;</span>\n                <div class=\"name\">达梦</div>\n                <div class=\"code-name\">&amp;#xe655;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe63f;</span>\n                <div class=\"name\">proxy</div>\n                <div class=\"code-name\">&amp;#xe63f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe646;</span>\n                <div class=\"name\">openai</div>\n                <div class=\"code-name\">&amp;#xe646;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe60c;</span>\n                <div class=\"name\">关于</div>\n                <div class=\"code-name\">&amp;#xe60c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe666;</span>\n                <div class=\"name\">衣服</div>\n                <div class=\"code-name\">&amp;#xe666;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe609;</span>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">&amp;#xe609;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe649;</span>\n                <div class=\"name\">数据源配置</div>\n                <div class=\"code-name\">&amp;#xe649;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6a6;</span>\n                <div class=\"name\">服务器_数据库_jurassic</div>\n                <div class=\"code-name\">&amp;#xe6a6;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe607;</span>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">&amp;#xe607;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe625;</span>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">&amp;#xe625;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe63c;</span>\n                <div class=\"name\">数据库数据</div>\n                <div class=\"code-name\">&amp;#xe63c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe636;</span>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">&amp;#xe636;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe62f;</span>\n                <div class=\"name\">配置数据源</div>\n                <div class=\"code-name\">&amp;#xe62f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe80a;</span>\n                <div class=\"name\">SQL历史查询</div>\n                <div class=\"code-name\">&amp;#xe80a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe623;</span>\n                <div class=\"name\">重命名</div>\n                <div class=\"code-name\">&amp;#xe623;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe8ff;</span>\n                <div class=\"name\">ico_数据查询与统计_预约情况查询</div>\n                <div class=\"code-name\">&amp;#xe8ff;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe8f4;</span>\n                <div class=\"name\">clickhouse-云数据库ClickHouse</div>\n                <div class=\"code-name\">&amp;#xe8f4;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6f5;</span>\n                <div class=\"name\">rds_mariadb</div>\n                <div class=\"code-name\">&amp;#xe6f5;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe62a;</span>\n                <div class=\"name\">减少减去减号</div>\n                <div class=\"code-name\">&amp;#xe62a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe664;</span>\n                <div class=\"name\">sqlserver</div>\n                <div class=\"code-name\">&amp;#xe664;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe65a;</span>\n                <div class=\"name\">sqlite</div>\n                <div class=\"code-name\">&amp;#xe65a;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe760;</span>\n                <div class=\"name\">缺省页_暂无数据</div>\n                <div class=\"code-name\">&amp;#xe760;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe755;</span>\n                <div class=\"name\">未完成</div>\n                <div class=\"code-name\">&amp;#xe755;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe62e;</span>\n                <div class=\"name\">完成-01</div>\n                <div class=\"code-name\">&amp;#xe62e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe620;</span>\n                <div class=\"name\">成功</div>\n                <div class=\"code-name\">&amp;#xe620;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe70e;</span>\n                <div class=\"name\">机器人</div>\n                <div class=\"code-name\">&amp;#xe70e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe635;</span>\n                <div class=\"name\">换一换</div>\n                <div class=\"code-name\">&amp;#xe635;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe65b;</span>\n                <div class=\"name\">icon_infomation</div>\n                <div class=\"code-name\">&amp;#xe65b;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe775;</span>\n                <div class=\"name\">key</div>\n                <div class=\"code-name\">&amp;#xe775;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec6d;</span>\n                <div class=\"name\">mysql</div>\n                <div class=\"code-name\">&amp;#xec6d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec48;</span>\n                <div class=\"name\">oracle</div>\n                <div class=\"code-name\">&amp;#xec48;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec5d;</span>\n                <div class=\"name\">postgresql</div>\n                <div class=\"code-name\">&amp;#xec5d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe61c;</span>\n                <div class=\"name\">h2</div>\n                <div class=\"code-name\">&amp;#xe61c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe696;</span>\n                <div class=\"name\">cc-schema</div>\n                <div class=\"code-name\">&amp;#xe696;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6b6;</span>\n                <div class=\"name\">新建表格</div>\n                <div class=\"code-name\">&amp;#xe6b6;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe613;</span>\n                <div class=\"name\">export</div>\n                <div class=\"code-name\">&amp;#xe613;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe66d;</span>\n                <div class=\"name\">角色管理</div>\n                <div class=\"code-name\">&amp;#xe66d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe619;</span>\n                <div class=\"name\">console</div>\n                <div class=\"code-name\">&amp;#xe619;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeac5;</span>\n                <div class=\"name\">24gf-folderMinus</div>\n                <div class=\"code-name\">&amp;#xeac5;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe606;</span>\n                <div class=\"name\">查看</div>\n                <div class=\"code-name\">&amp;#xe606;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeb4e;</span>\n                <div class=\"name\">复制_o</div>\n                <div class=\"code-name\">&amp;#xeb4e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe626;</span>\n                <div class=\"name\">执行</div>\n                <div class=\"code-name\">&amp;#xe626;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe7f8;</span>\n                <div class=\"name\">m-格式化文字</div>\n                <div class=\"code-name\">&amp;#xe7f8;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe885;</span>\n                <div class=\"name\">github-fill</div>\n                <div class=\"code-name\">&amp;#xe885;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe645;</span>\n                <div class=\"name\">保存</div>\n                <div class=\"code-name\">&amp;#xe645;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeb93;</span>\n                <div class=\"name\">箭头_向左两次_o</div>\n                <div class=\"code-name\">&amp;#xeb93;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe603;</span>\n                <div class=\"name\">新建窗口</div>\n                <div class=\"code-name\">&amp;#xe603;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6cd;</span>\n                <div class=\"name\">loading</div>\n                <div class=\"code-name\">&amp;#xe6cd;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6ca;</span>\n                <div class=\"name\">链接克隆</div>\n                <div class=\"code-name\">&amp;#xe6ca;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe63b;</span>\n                <div class=\"name\">SQL升级文件</div>\n                <div class=\"code-name\">&amp;#xe63b;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe610;</span>\n                <div class=\"name\">sql</div>\n                <div class=\"code-name\">&amp;#xe610;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec57;</span>\n                <div class=\"name\">连接流</div>\n                <div class=\"code-name\">&amp;#xec57;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe685;</span>\n                <div class=\"name\">跳转/退出</div>\n                <div class=\"code-name\">&amp;#xe685;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe648;</span>\n                <div class=\"name\">key</div>\n                <div class=\"code-name\">&amp;#xe648;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe8ad;</span>\n                <div class=\"name\">播放记录</div>\n                <div class=\"code-name\">&amp;#xe8ad;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe605;</span>\n                <div class=\"name\">成功</div>\n                <div class=\"code-name\">&amp;#xe605;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe87c;</span>\n                <div class=\"name\">失败</div>\n                <div class=\"code-name\">&amp;#xe87c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe790;</span>\n                <div class=\"name\">收回 上下</div>\n                <div class=\"code-name\">&amp;#xe790;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe7b1;</span>\n                <div class=\"name\">展开 上下</div>\n                <div class=\"code-name\">&amp;#xe7b1;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe62c;</span>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">&amp;#xe62c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe936;</span>\n                <div class=\"name\">保存</div>\n                <div class=\"code-name\">&amp;#xe936;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec4c;</span>\n                <div class=\"name\">查询</div>\n                <div class=\"code-name\">&amp;#xec4c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe61f;</span>\n                <div class=\"name\">对勾</div>\n                <div class=\"code-name\">&amp;#xe61f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe617;</span>\n                <div class=\"name\">check</div>\n                <div class=\"code-name\">&amp;#xe617;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe632;</span>\n                <div class=\"name\">概览</div>\n                <div class=\"code-name\">&amp;#xe632;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe63d;</span>\n                <div class=\"name\">概览</div>\n                <div class=\"code-name\">&amp;#xe63d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe602;</span>\n                <div class=\"name\">编辑</div>\n                <div class=\"code-name\">&amp;#xe602;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec08;</span>\n                <div class=\"name\">刷新</div>\n                <div class=\"code-name\">&amp;#xec08;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe611;</span>\n                <div class=\"name\">菜单/列表</div>\n                <div class=\"code-name\">&amp;#xe611;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe63e;</span>\n                <div class=\"name\">表格</div>\n                <div class=\"code-name\">&amp;#xe63e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe65f;</span>\n                <div class=\"name\">展开</div>\n                <div class=\"code-name\">&amp;#xe65f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe61e;</span>\n                <div class=\"name\">收起</div>\n                <div class=\"code-name\">&amp;#xe61e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xeb6f;</span>\n                <div class=\"name\">主题_o</div>\n                <div class=\"code-name\">&amp;#xeb6f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe65e;</span>\n                <div class=\"name\">断开连接</div>\n                <div class=\"code-name\">&amp;#xe65e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe60f;</span>\n                <div class=\"name\">修改</div>\n                <div class=\"code-name\">&amp;#xe60f;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe604;</span>\n                <div class=\"name\">删除</div>\n                <div class=\"code-name\">&amp;#xe604;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe601;</span>\n                <div class=\"name\">更多</div>\n                <div class=\"code-name\">&amp;#xe601;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe644;</span>\n                <div class=\"name\">减少</div>\n                <div class=\"code-name\">&amp;#xe644;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe61b;</span>\n                <div class=\"name\">加</div>\n                <div class=\"code-name\">&amp;#xe61b;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe631;</span>\n                <div class=\"name\">加号</div>\n                <div class=\"code-name\">&amp;#xe631;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe608;</span>\n                <div class=\"name\">arrow drop down</div>\n                <div class=\"code-name\">&amp;#xe608;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe600;</span>\n                <div class=\"name\">search</div>\n                <div class=\"code-name\">&amp;#xe600;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe66c;</span>\n                <div class=\"name\">download</div>\n                <div class=\"code-name\">&amp;#xe66c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe79c;</span>\n                <div class=\"name\">向右箭头</div>\n                <div class=\"code-name\">&amp;#xe79c;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe6a7;</span>\n                <div class=\"name\">删除线型</div>\n                <div class=\"code-name\">&amp;#xe6a7;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xec8e;</span>\n                <div class=\"name\">cross</div>\n                <div class=\"code-name\">&amp;#xec8e;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe62d;</span>\n                <div class=\"name\">刷新</div>\n                <div class=\"code-name\">&amp;#xe62d;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe913;</span>\n                <div class=\"name\">提醒</div>\n                <div class=\"code-name\">&amp;#xe913;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe795;</span>\n                <div class=\"name\">138设置、系统设置、功能设置、属性</div>\n                <div class=\"code-name\">&amp;#xe795;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe759;</span>\n                <div class=\"name\">执行sql脚本</div>\n                <div class=\"code-name\">&amp;#xe759;</div>\n              </li>\n          \n            <li class=\"dib\">\n              <span class=\"icon iconfont\">&#xe61a;</span>\n                <div class=\"name\">虚拟数据库管理</div>\n                <div class=\"code-name\">&amp;#xe61a;</div>\n              </li>\n          \n          </ul>\n          <div class=\"article markdown\">\n          <h2 id=\"unicode-\">Unicode 引用</h2>\n          <hr>\n\n          <p>Unicode 是字体在网页端最原始的应用方式，特点是：</p>\n          <ul>\n            <li>支持按字体的方式去动态调整图标大小，颜色等等。</li>\n            <li>默认情况下不支持多色，直接添加多色图标会自动去色。</li>\n          </ul>\n          <blockquote>\n            <p>注意：新版 iconfont 支持两种方式引用多色图标：SVG symbol 引用方式和彩色字体图标模式。（使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。）</p>\n          </blockquote>\n          <p>Unicode 使用步骤如下：</p>\n          <h3 id=\"-font-face\">第一步：拷贝项目下面生成的 <code>@font-face</code></h3>\n<pre><code class=\"language-css\"\n>@font-face {\n  font-family: 'iconfont';\n  src: url('iconfont.woff2?t=1704794525154') format('woff2'),\n       url('iconfont.woff?t=1704794525154') format('woff'),\n       url('iconfont.ttf?t=1704794525154') format('truetype');\n}\n</code></pre>\n          <h3 id=\"-iconfont-\">第二步：定义使用 iconfont 的样式</h3>\n<pre><code class=\"language-css\"\n>.iconfont {\n  font-family: \"iconfont\" !important;\n  font-size: 16px;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n</code></pre>\n          <h3 id=\"-\">第三步：挑选相应图标并获取字体编码，应用于页面</h3>\n<pre>\n<code class=\"language-html\"\n>&lt;span class=\"iconfont\"&gt;&amp;#x33;&lt;/span&gt;\n</code></pre>\n          <blockquote>\n            <p>\"iconfont\" 是你项目下的 font-family。可以通过编辑项目查看，默认是 \"iconfont\"。</p>\n          </blockquote>\n          </div>\n      </div>\n      <div class=\"content font-class\">\n        <ul class=\"icon_lists dib-box\">\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-right_on_5\"></span>\n            <div class=\"name\">\n              right_on_5\n            </div>\n            <div class=\"code-name\">.icon-right_on_5\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-right_off_5-01\"></span>\n            <div class=\"name\">\n              right_off_5-01\n            </div>\n            <div class=\"code-name\">.icon-right_off_5-01\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-left_on_huaban11\"></span>\n            <div class=\"name\">\n              left_on_2\n            </div>\n            <div class=\"code-name\">.icon-a-left_on_huaban11\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-left_off_huaban1\"></span>\n            <div class=\"name\">\n              left_off\n            </div>\n            <div class=\"code-name\">.icon-a-left_off_huaban1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-minimize21\"></span>\n            <div class=\"name\">\n              minimize21\n            </div>\n            <div class=\"code-name\">.icon-minimize21\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-restore_button2\"></span>\n            <div class=\"name\">\n              restore\n            </div>\n            <div class=\"code-name\">.icon-restore_button2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-resize_button2\"></span>\n            <div class=\"name\">\n              resize\n            </div>\n            <div class=\"code-name\">.icon-resize_button2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-close_button2\"></span>\n            <div class=\"name\">\n              close\n            </div>\n            <div class=\"code-name\">.icon-close_button2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shaixuan\"></span>\n            <div class=\"name\">\n              筛选\n            </div>\n            <div class=\"code-name\">.icon-shaixuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-44tubiao-122\"></span>\n            <div class=\"name\">\n              排序\n            </div>\n            <div class=\"code-name\">.icon-a-44tubiao-122\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xinxi-xianxingyuankuang\"></span>\n            <div class=\"name\">\n              305信息-线性圆框\n            </div>\n            <div class=\"code-name\">.icon-xinxi-xianxingyuankuang\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jiahao\"></span>\n            <div class=\"name\">\n              加号\n            </div>\n            <div class=\"code-name\">.icon-jiahao\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-liebiao\"></span>\n            <div class=\"name\">\n              列表\n            </div>\n            <div class=\"code-name\">.icon-liebiao\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jianqu\"></span>\n            <div class=\"name\">\n              减去\n            </div>\n            <div class=\"code-name\">.icon-jianqu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-database\"></span>\n            <div class=\"name\">\n              database\n            </div>\n            <div class=\"code-name\">.icon-database\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shaixuan1\"></span>\n            <div class=\"name\">\n              筛选\n            </div>\n            <div class=\"code-name\">.icon-shaixuan1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shuaxin2\"></span>\n            <div class=\"name\">\n              刷新\n            </div>\n            <div class=\"code-name\">.icon-shuaxin2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jiahao_o\"></span>\n            <div class=\"name\">\n              加号_o\n            </div>\n            <div class=\"code-name\">.icon-jiahao_o\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jurassic_data\"></span>\n            <div class=\"name\">\n              数据库_jurassic\n            </div>\n            <div class=\"code-name\">.icon-jurassic_data\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-quanxian\"></span>\n            <div class=\"name\">\n              权限\n            </div>\n            <div class=\"code-name\">.icon-quanxian\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-sharpicons_add-database\"></span>\n            <div class=\"name\">\n              sharpicons_add-database\n            </div>\n            <div class=\"code-name\">.icon-sharpicons_add-database\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zuzhiguanli-\"></span>\n            <div class=\"name\">\n              组织管理\n            </div>\n            <div class=\"code-name\">.icon-zuzhiguanli-\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-moxing-miaobian\"></span>\n            <div class=\"name\">\n              空间\n            </div>\n            <div class=\"code-name\">.icon-moxing-miaobian\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xiajiantou1-copy\"></span>\n            <div class=\"name\">\n              下箭头-copy\n            </div>\n            <div class=\"code-name\">.icon-xiajiantou1-copy\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-chakan2\"></span>\n            <div class=\"name\">\n              查看\n            </div>\n            <div class=\"code-name\">.icon-chakan2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-clone\"></span>\n            <div class=\"name\">\n              clone\n            </div>\n            <div class=\"code-name\">.icon-clone\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tijiao\"></span>\n            <div class=\"name\">\n              提交\n            </div>\n            <div class=\"code-name\">.icon-tijiao\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-chakan1\"></span>\n            <div class=\"name\">\n              查看\n            </div>\n            <div class=\"code-name\">.icon-chakan1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-fuzhi\"></span>\n            <div class=\"name\">\n              复制\n            </div>\n            <div class=\"code-name\">.icon-fuzhi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-icon_answer\"></span>\n            <div class=\"name\">\n              icon_answer\n            </div>\n            <div class=\"code-name\">.icon-icon_answer\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-icon_question\"></span>\n            <div class=\"name\">\n              icon_question\n            </div>\n            <div class=\"code-name\">.icon-icon_question\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-fasong\"></span>\n            <div class=\"name\">\n              发送\n            </div>\n            <div class=\"code-name\">.icon-fasong\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zhongqi\"></span>\n            <div class=\"name\">\n              重启\n            </div>\n            <div class=\"code-name\">.icon-zhongqi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tixing2\"></span>\n            <div class=\"name\">\n              提醒\n            </div>\n            <div class=\"code-name\">.icon-tixing2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tixing3\"></span>\n            <div class=\"name\">\n              提醒\n            </div>\n            <div class=\"code-name\">.icon-tixing3\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tixing1\"></span>\n            <div class=\"name\">\n              提醒\n            </div>\n            <div class=\"code-name\">.icon-tixing1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shengji\"></span>\n            <div class=\"name\">\n              升级\n            </div>\n            <div class=\"code-name\">.icon-shengji\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-quanju_shengji\"></span>\n            <div class=\"name\">\n              全局_升级\n            </div>\n            <div class=\"code-name\">.icon-quanju_shengji\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-guanyuwomen1\"></span>\n            <div class=\"name\">\n              关于我们\n            </div>\n            <div class=\"code-name\">.icon-guanyuwomen1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-icobanbengengxin\"></span>\n            <div class=\"name\">\n              ico版本更新\n            </div>\n            <div class=\"code-name\">.icon-icobanbengengxin\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-duihuaqipao\"></span>\n            <div class=\"name\">\n              对话气泡\n            </div>\n            <div class=\"code-name\">.icon-duihuaqipao\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jiaosequanxian\"></span>\n            <div class=\"name\">\n              角色权限\n            </div>\n            <div class=\"code-name\">.icon-jiaosequanxian\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-preview1\"></span>\n            <div class=\"name\">\n              preview\n            </div>\n            <div class=\"code-name\">.icon-preview1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-daoru\"></span>\n            <div class=\"name\">\n              导入\n            </div>\n            <div class=\"code-name\">.icon-daoru\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zhongzhi\"></span>\n            <div class=\"name\">\n              终止\n            </div>\n            <div class=\"code-name\">.icon-zhongzhi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tuichu\"></span>\n            <div class=\"name\">\n              退出\n            </div>\n            <div class=\"code-name\">.icon-tuichu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-kongzhuangzhongduan\"></span>\n            <div class=\"name\">\n              控桩终端\n            </div>\n            <div class=\"code-name\">.icon-kongzhuangzhongduan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-chexiao1\"></span>\n            <div class=\"name\">\n              撤销\n            </div>\n            <div class=\"code-name\">.icon-chexiao1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xiangshang\"></span>\n            <div class=\"name\">\n              向上\n            </div>\n            <div class=\"code-name\">.icon-xiangshang\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-chakan-copy\"></span>\n            <div class=\"name\">\n              查看\n            </div>\n            <div class=\"code-name\">.icon-chakan-copy\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jurassic_edit-data\"></span>\n            <div class=\"name\">\n              编辑数据_编辑录入操作_jurassic\n            </div>\n            <div class=\"code-name\">.icon-jurassic_edit-data\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jurassic_edit-table\"></span>\n            <div class=\"name\">\n              编辑表格_编辑录入操作_jurassic\n            </div>\n            <div class=\"code-name\">.icon-jurassic_edit-table\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-baobiaoshujuluru\"></span>\n            <div class=\"name\">\n              报表数据录入\n            </div>\n            <div class=\"code-name\">.icon-baobiaoshujuluru\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-bofang5\"></span>\n            <div class=\"name\">\n              播放5\n            </div>\n            <div class=\"code-name\">.icon-bofang5\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-qingkong3x\"></span>\n            <div class=\"name\">\n              清空@3x\n            </div>\n            <div class=\"code-name\">.icon-a-qingkong3x\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shanchu\"></span>\n            <div class=\"name\">\n              删除\n            </div>\n            <div class=\"code-name\">.icon-shanchu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-newdocumentworksheet\"></span>\n            <div class=\"name\">\n              new-document-worksheet\n            </div>\n            <div class=\"code-name\">.icon-newdocumentworksheet\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-file-excel\"></span>\n            <div class=\"name\">\n              file-excel\n            </div>\n            <div class=\"code-name\">.icon-file-excel\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-file-markdown\"></span>\n            <div class=\"name\">\n              file-markdown\n            </div>\n            <div class=\"code-name\">.icon-file-markdown\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-file-word\"></span>\n            <div class=\"name\">\n              file-word\n            </div>\n            <div class=\"code-name\">.icon-file-word\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-HTML\"></span>\n            <div class=\"name\">\n              HTML5\n            </div>\n            <div class=\"code-name\">.icon-HTML\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-HTML1\"></span>\n            <div class=\"name\">\n              HTML\n            </div>\n            <div class=\"code-name\">.icon-HTML1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-pdf\"></span>\n            <div class=\"name\">\n              pdf\n            </div>\n            <div class=\"code-name\">.icon-pdf\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-gerenyonghu\"></span>\n            <div class=\"name\">\n              个人用户\n            </div>\n            <div class=\"code-name\">.icon-gerenyonghu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-houtaiguanli\"></span>\n            <div class=\"name\">\n              后台管理\n            </div>\n            <div class=\"code-name\">.icon-houtaiguanli\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zitidaima\"></span>\n            <div class=\"name\">\n              字体代码\n            </div>\n            <div class=\"code-name\">.icon-zitidaima\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-banben\"></span>\n            <div class=\"name\">\n              版本\n            </div>\n            <div class=\"code-name\">.icon-banben\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-cheweiguanli\"></span>\n            <div class=\"name\">\n              车位管理\n            </div>\n            <div class=\"code-name\">.icon-cheweiguanli\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-dianzhelidaochu\"></span>\n            <div class=\"name\">\n              dictate\n            </div>\n            <div class=\"code-name\">.icon-dianzhelidaochu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-circle-f\"></span>\n            <div class=\"name\">\n              circle-f\n            </div>\n            <div class=\"code-name\">.icon-circle-f\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tubiao-hanshu\"></span>\n            <div class=\"name\">\n              图表-函数\n            </div>\n            <div class=\"code-name\">.icon-tubiao-hanshu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shituguanliqi\"></span>\n            <div class=\"name\">\n              视图管理器\n            </div>\n            <div class=\"code-name\">.icon-shituguanliqi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-huiche\"></span>\n            <div class=\"name\">\n              回车\n            </div>\n            <div class=\"code-name\">.icon-huiche\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-quesheng\"></span>\n            <div class=\"name\">\n              缺省\n            </div>\n            <div class=\"code-name\">.icon-quesheng\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jinrujiantou\"></span>\n            <div class=\"name\">\n              进入箭头\n            </div>\n            <div class=\"code-name\">.icon-jinrujiantou\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-youjiantou_huaban\"></span>\n            <div class=\"name\">\n              右箭头\n            </div>\n            <div class=\"code-name\">.icon-youjiantou_huaban\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xiangyoujiantou1\"></span>\n            <div class=\"name\">\n              向右箭头\n            </div>\n            <div class=\"code-name\">.icon-xiangyoujiantou1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujuyuan\"></span>\n            <div class=\"name\">\n              数据源\n            </div>\n            <div class=\"code-name\">.icon-shujuyuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-question\"></span>\n            <div class=\"name\">\n              question\n            </div>\n            <div class=\"code-name\">.icon-question\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xingxing\"></span>\n            <div class=\"name\">\n              星星-copy\n            </div>\n            <div class=\"code-name\">.icon-xingxing\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-kongzhitai\"></span>\n            <div class=\"name\">\n              控制台\n            </div>\n            <div class=\"code-name\">.icon-kongzhitai\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xingxi\"></span>\n            <div class=\"name\">\n              星系\n            </div>\n            <div class=\"code-name\">.icon-xingxi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-zanwushuju1\"></span>\n            <div class=\"name\">\n              暂无数据 (1)\n            </div>\n            <div class=\"code-name\">.icon-a-zanwushuju1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-kaishi\"></span>\n            <div class=\"name\">\n              开始\n            </div>\n            <div class=\"code-name\">.icon-kaishi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-guanbi\"></span>\n            <div class=\"name\">\n              关闭\n            </div>\n            <div class=\"code-name\">.icon-guanbi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xiajiantou\"></span>\n            <div class=\"name\">\n              下箭头\n            </div>\n            <div class=\"code-name\">.icon-xiajiantou\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-gengduo\"></span>\n            <div class=\"name\">\n              more\n            </div>\n            <div class=\"code-name\">.icon-gengduo\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shezhi\"></span>\n            <div class=\"name\">\n              设置\n            </div>\n            <div class=\"code-name\">.icon-shezhi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-duihua-weixuan\"></span>\n            <div class=\"name\">\n              对话-未选\n            </div>\n            <div class=\"code-name\">.icon-duihua-weixuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tubiao-weixuan\"></span>\n            <div class=\"name\">\n              图表-未选\n            </div>\n            <div class=\"code-name\">.icon-tubiao-weixuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-bianzu13beifen3\"></span>\n            <div class=\"name\">\n              编组 13备份 3\n            </div>\n            <div class=\"code-name\">.icon-a-bianzu13beifen3\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-bianzubeifen\"></span>\n            <div class=\"name\">\n              编组备份\n            </div>\n            <div class=\"code-name\">.icon-bianzubeifen\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-biaoge1\"></span>\n            <div class=\"name\">\n              表格\n            </div>\n            <div class=\"code-name\">.icon-biaoge1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-shoucang1\"></span>\n            <div class=\"name\">\n              收藏 (1)\n            </div>\n            <div class=\"code-name\">.icon-a-shoucang1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-guthub-weixuan1\"></span>\n            <div class=\"name\">\n              guthub-未选\n            </div>\n            <div class=\"code-name\">.icon-guthub-weixuan1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shuju-weixuan\"></span>\n            <div class=\"name\">\n              数据-未选\n            </div>\n            <div class=\"code-name\">.icon-shuju-weixuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-bianzu4\"></span>\n            <div class=\"name\">\n              编组 4\n            </div>\n            <div class=\"code-name\">.icon-a-bianzu4\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-a-bianzu14beifen\"></span>\n            <div class=\"name\">\n              编组 14备份\n            </div>\n            <div class=\"code-name\">.icon-a-bianzu14beifen\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-guthub-weixuan\"></span>\n            <div class=\"name\">\n              guthub-未选\n            </div>\n            <div class=\"code-name\">.icon-guthub-weixuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-24gl-folderMinus\"></span>\n            <div class=\"name\">\n              24gl-folderMinus\n            </div>\n            <div class=\"code-name\">.icon-24gl-folderMinus\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-24gl-folderOpen\"></span>\n            <div class=\"name\">\n              24gl-folderOpen\n            </div>\n            <div class=\"code-name\">.icon-24gl-folderOpen\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-24gf-folderOpen\"></span>\n            <div class=\"name\">\n              24gf-folderOpen\n            </div>\n            <div class=\"code-name\">.icon-24gf-folderOpen\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-yunshujuku\"></span>\n            <div class=\"name\">\n              云数据库\n            </div>\n            <div class=\"code-name\">.icon-yunshujuku\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-baobiao\"></span>\n            <div class=\"name\">\n              报表\n            </div>\n            <div class=\"code-name\">.icon-baobiao\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-gongzuotai\"></span>\n            <div class=\"name\">\n              工作台\n            </div>\n            <div class=\"code-name\">.icon-gongzuotai\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-mongodb\"></span>\n            <div class=\"name\">\n              mongodb\n            </div>\n            <div class=\"code-name\">.icon-mongodb\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-Redis\"></span>\n            <div class=\"name\">\n              Redis\n            </div>\n            <div class=\"code-name\">.icon-Redis\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-HIVE\"></span>\n            <div class=\"name\">\n              HIVE_2\n            </div>\n            <div class=\"code-name\">.icon-HIVE\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-Kingbase\"></span>\n            <div class=\"name\">\n              Kingbase\n            </div>\n            <div class=\"code-name\">.icon-Kingbase\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-yibiaopan\"></span>\n            <div class=\"name\">\n              仪表盘\n            </div>\n            <div class=\"code-name\">.icon-yibiaopan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-presto_sql\"></span>\n            <div class=\"name\">\n              presto\n            </div>\n            <div class=\"code-name\">.icon-presto_sql\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujukuleixingtubiao-kuozhan-\"></span>\n            <div class=\"name\">\n              DB2\n            </div>\n            <div class=\"code-name\">.icon-shujukuleixingtubiao-kuozhan-\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-oceanbase\"></span>\n            <div class=\"name\">\n              oceanbase\n            </div>\n            <div class=\"code-name\">.icon-oceanbase\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-dameng1\"></span>\n            <div class=\"name\">\n              达梦\n            </div>\n            <div class=\"code-name\">.icon-dameng1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-proxy\"></span>\n            <div class=\"name\">\n              proxy\n            </div>\n            <div class=\"code-name\">.icon-proxy\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-openai\"></span>\n            <div class=\"name\">\n              openai\n            </div>\n            <div class=\"code-name\">.icon-openai\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-guanyu\"></span>\n            <div class=\"name\">\n              关于\n            </div>\n            <div class=\"code-name\">.icon-guanyu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-yifu\"></span>\n            <div class=\"name\">\n              衣服\n            </div>\n            <div class=\"code-name\">.icon-yifu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujuku4\"></span>\n            <div class=\"name\">\n              数据库\n            </div>\n            <div class=\"code-name\">.icon-shujuku4\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujuyuanpeizhi\"></span>\n            <div class=\"name\">\n              数据源配置\n            </div>\n            <div class=\"code-name\">.icon-shujuyuanpeizhi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jurassic_server\"></span>\n            <div class=\"name\">\n              服务器_数据库_jurassic\n            </div>\n            <div class=\"code-name\">.icon-jurassic_server\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujuku2\"></span>\n            <div class=\"name\">\n              数据库\n            </div>\n            <div class=\"code-name\">.icon-shujuku2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujuku3\"></span>\n            <div class=\"name\">\n              数据库\n            </div>\n            <div class=\"code-name\">.icon-shujuku3\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujukushuju\"></span>\n            <div class=\"name\">\n              数据库数据\n            </div>\n            <div class=\"code-name\">.icon-shujukushuju\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujuku1\"></span>\n            <div class=\"name\">\n              数据库\n            </div>\n            <div class=\"code-name\">.icon-shujuku1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-peizhishujuyuan\"></span>\n            <div class=\"name\">\n              配置数据源\n            </div>\n            <div class=\"code-name\">.icon-peizhishujuyuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-SQLlishichaxun\"></span>\n            <div class=\"name\">\n              SQL历史查询\n            </div>\n            <div class=\"code-name\">.icon-SQLlishichaxun\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zhongmingming\"></span>\n            <div class=\"name\">\n              重命名\n            </div>\n            <div class=\"code-name\">.icon-zhongmingming\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun\"></span>\n            <div class=\"name\">\n              ico_数据查询与统计_预约情况查询\n            </div>\n            <div class=\"code-name\">.icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-clickhouse-yunshujukuClickHouse\"></span>\n            <div class=\"name\">\n              clickhouse-云数据库ClickHouse\n            </div>\n            <div class=\"code-name\">.icon-clickhouse-yunshujukuClickHouse\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-rds_mariadb\"></span>\n            <div class=\"name\">\n              rds_mariadb\n            </div>\n            <div class=\"code-name\">.icon-rds_mariadb\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jianshaojianqujianhao\"></span>\n            <div class=\"name\">\n              减少减去减号\n            </div>\n            <div class=\"code-name\">.icon-jianshaojianqujianhao\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-sqlserver\"></span>\n            <div class=\"name\">\n              sqlserver\n            </div>\n            <div class=\"code-name\">.icon-sqlserver\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-sqlite\"></span>\n            <div class=\"name\">\n              sqlite\n            </div>\n            <div class=\"code-name\">.icon-sqlite\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-queshengye_zanwushuju\"></span>\n            <div class=\"name\">\n              缺省页_暂无数据\n            </div>\n            <div class=\"code-name\">.icon-queshengye_zanwushuju\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-weiwancheng\"></span>\n            <div class=\"name\">\n              未完成\n            </div>\n            <div class=\"code-name\">.icon-weiwancheng\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-wancheng-\"></span>\n            <div class=\"name\">\n              完成-01\n            </div>\n            <div class=\"code-name\">.icon-wancheng-\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-chenggong1\"></span>\n            <div class=\"name\">\n              成功\n            </div>\n            <div class=\"code-name\">.icon-chenggong1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jiqiren\"></span>\n            <div class=\"name\">\n              机器人\n            </div>\n            <div class=\"code-name\">.icon-jiqiren\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-huanyihuan\"></span>\n            <div class=\"name\">\n              换一换\n            </div>\n            <div class=\"code-name\">.icon-huanyihuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-icon_infomation\"></span>\n            <div class=\"name\">\n              icon_infomation\n            </div>\n            <div class=\"code-name\">.icon-icon_infomation\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-key1\"></span>\n            <div class=\"name\">\n              key\n            </div>\n            <div class=\"code-name\">.icon-key1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-mysql\"></span>\n            <div class=\"name\">\n              mysql\n            </div>\n            <div class=\"code-name\">.icon-mysql\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-oracle\"></span>\n            <div class=\"name\">\n              oracle\n            </div>\n            <div class=\"code-name\">.icon-oracle\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-postgresql\"></span>\n            <div class=\"name\">\n              postgresql\n            </div>\n            <div class=\"code-name\">.icon-postgresql\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-h2\"></span>\n            <div class=\"name\">\n              h2\n            </div>\n            <div class=\"code-name\">.icon-h2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-cc-schema\"></span>\n            <div class=\"name\">\n              cc-schema\n            </div>\n            <div class=\"code-name\">.icon-cc-schema\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xinjianbiaoge\"></span>\n            <div class=\"name\">\n              新建表格\n            </div>\n            <div class=\"code-name\">.icon-xinjianbiaoge\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-export\"></span>\n            <div class=\"name\">\n              export\n            </div>\n            <div class=\"code-name\">.icon-export\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jiaoseguanli\"></span>\n            <div class=\"name\">\n              角色管理\n            </div>\n            <div class=\"code-name\">.icon-jiaoseguanli\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-console\"></span>\n            <div class=\"name\">\n              console\n            </div>\n            <div class=\"code-name\">.icon-console\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-24gf-folderMinus\"></span>\n            <div class=\"name\">\n              24gf-folderMinus\n            </div>\n            <div class=\"code-name\">.icon-24gf-folderMinus\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-chakan\"></span>\n            <div class=\"name\">\n              查看\n            </div>\n            <div class=\"code-name\">.icon-chakan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-fuzhi_o\"></span>\n            <div class=\"name\">\n              复制_o\n            </div>\n            <div class=\"code-name\">.icon-fuzhi_o\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zhihang\"></span>\n            <div class=\"name\">\n              执行\n            </div>\n            <div class=\"code-name\">.icon-zhihang\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-m-geshihuawenzi\"></span>\n            <div class=\"name\">\n              m-格式化文字\n            </div>\n            <div class=\"code-name\">.icon-m-geshihuawenzi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-github-fill\"></span>\n            <div class=\"name\">\n              github-fill\n            </div>\n            <div class=\"code-name\">.icon-github-fill\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-baocun2\"></span>\n            <div class=\"name\">\n              保存\n            </div>\n            <div class=\"code-name\">.icon-baocun2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jiantou_xiangzuoliangci_o\"></span>\n            <div class=\"name\">\n              箭头_向左两次_o\n            </div>\n            <div class=\"code-name\">.icon-jiantou_xiangzuoliangci_o\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xinjianchuangkou\"></span>\n            <div class=\"name\">\n              新建窗口\n            </div>\n            <div class=\"code-name\">.icon-xinjianchuangkou\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-loading2\"></span>\n            <div class=\"name\">\n              loading\n            </div>\n            <div class=\"code-name\">.icon-loading2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-lianjiekelong\"></span>\n            <div class=\"name\">\n              链接克隆\n            </div>\n            <div class=\"code-name\">.icon-lianjiekelong\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-SQLshengjiwenjian\"></span>\n            <div class=\"name\">\n              SQL升级文件\n            </div>\n            <div class=\"code-name\">.icon-SQLshengjiwenjian\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-sql\"></span>\n            <div class=\"name\">\n              sql\n            </div>\n            <div class=\"code-name\">.icon-sql\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-lianjieliu\"></span>\n            <div class=\"name\">\n              连接流\n            </div>\n            <div class=\"code-name\">.icon-lianjieliu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tiaozhuan\"></span>\n            <div class=\"name\">\n              跳转/退出\n            </div>\n            <div class=\"code-name\">.icon-tiaozhuan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-key\"></span>\n            <div class=\"name\">\n              key\n            </div>\n            <div class=\"code-name\">.icon-key\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-bofangjilu\"></span>\n            <div class=\"name\">\n              播放记录\n            </div>\n            <div class=\"code-name\">.icon-bofangjilu\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-chenggong\"></span>\n            <div class=\"name\">\n              成功\n            </div>\n            <div class=\"code-name\">.icon-chenggong\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shibai\"></span>\n            <div class=\"name\">\n              失败\n            </div>\n            <div class=\"code-name\">.icon-shibai\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shouhuishangxia\"></span>\n            <div class=\"name\">\n              收回 上下\n            </div>\n            <div class=\"code-name\">.icon-shouhuishangxia\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zhankaishangxia\"></span>\n            <div class=\"name\">\n              展开 上下\n            </div>\n            <div class=\"code-name\">.icon-zhankaishangxia\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shujuku\"></span>\n            <div class=\"name\">\n              数据库\n            </div>\n            <div class=\"code-name\">.icon-shujuku\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-baocun\"></span>\n            <div class=\"name\">\n              保存\n            </div>\n            <div class=\"code-name\">.icon-baocun\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-chaxun\"></span>\n            <div class=\"name\">\n              查询\n            </div>\n            <div class=\"code-name\">.icon-chaxun\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-duigou11\"></span>\n            <div class=\"name\">\n              对勾\n            </div>\n            <div class=\"code-name\">.icon-duigou11\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-check1\"></span>\n            <div class=\"name\">\n              check\n            </div>\n            <div class=\"code-name\">.icon-check1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-gailan\"></span>\n            <div class=\"name\">\n              概览\n            </div>\n            <div class=\"code-name\">.icon-gailan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-huaban2\"></span>\n            <div class=\"name\">\n              概览\n            </div>\n            <div class=\"code-name\">.icon-huaban2\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-bianji\"></span>\n            <div class=\"name\">\n              编辑\n            </div>\n            <div class=\"code-name\">.icon-bianji\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shuaxin1\"></span>\n            <div class=\"name\">\n              刷新\n            </div>\n            <div class=\"code-name\">.icon-shuaxin1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-caidan\"></span>\n            <div class=\"name\">\n              菜单/列表\n            </div>\n            <div class=\"code-name\">.icon-caidan\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-biaoge\"></span>\n            <div class=\"name\">\n              表格\n            </div>\n            <div class=\"code-name\">.icon-biaoge\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zhankai\"></span>\n            <div class=\"name\">\n              展开\n            </div>\n            <div class=\"code-name\">.icon-zhankai\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shouqi\"></span>\n            <div class=\"name\">\n              收起\n            </div>\n            <div class=\"code-name\">.icon-shouqi\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zhuti_o\"></span>\n            <div class=\"name\">\n              主题_o\n            </div>\n            <div class=\"code-name\">.icon-zhuti_o\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-duankailianjie\"></span>\n            <div class=\"name\">\n              断开连接\n            </div>\n            <div class=\"code-name\">.icon-duankailianjie\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xiugai\"></span>\n            <div class=\"name\">\n              修改\n            </div>\n            <div class=\"code-name\">.icon-xiugai\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-delete\"></span>\n            <div class=\"name\">\n              删除\n            </div>\n            <div class=\"code-name\">.icon-delete\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-gengduo1\"></span>\n            <div class=\"name\">\n              更多\n            </div>\n            <div class=\"code-name\">.icon-gengduo1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jianshao\"></span>\n            <div class=\"name\">\n              减少\n            </div>\n            <div class=\"code-name\">.icon-jianshao\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-jia\"></span>\n            <div class=\"name\">\n              加\n            </div>\n            <div class=\"code-name\">.icon-jia\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-hao\"></span>\n            <div class=\"name\">\n              加号\n            </div>\n            <div class=\"code-name\">.icon-hao\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-right\"></span>\n            <div class=\"name\">\n              arrow drop down\n            </div>\n            <div class=\"code-name\">.icon-right\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-search1\"></span>\n            <div class=\"name\">\n              search\n            </div>\n            <div class=\"code-name\">.icon-search1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-download1\"></span>\n            <div class=\"name\">\n              download\n            </div>\n            <div class=\"code-name\">.icon-download1\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xiangyoujiantou\"></span>\n            <div class=\"name\">\n              向右箭头\n            </div>\n            <div class=\"code-name\">.icon-xiangyoujiantou\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shanchuxianxing\"></span>\n            <div class=\"name\">\n              删除线型\n            </div>\n            <div class=\"code-name\">.icon-shanchuxianxing\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-cross-copy\"></span>\n            <div class=\"name\">\n              cross\n            </div>\n            <div class=\"code-name\">.icon-cross-copy\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shuaxin\"></span>\n            <div class=\"name\">\n              刷新\n            </div>\n            <div class=\"code-name\">.icon-shuaxin\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-tixing\"></span>\n            <div class=\"name\">\n              提醒\n            </div>\n            <div class=\"code-name\">.icon-tixing\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-shezhixitongshezhigongnengshezhishuxing\"></span>\n            <div class=\"name\">\n              138设置、系统设置、功能设置、属性\n            </div>\n            <div class=\"code-name\">.icon-shezhixitongshezhigongnengshezhishuxing\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-zhihangsqljiaoben\"></span>\n            <div class=\"name\">\n              执行sql脚本\n            </div>\n            <div class=\"code-name\">.icon-zhihangsqljiaoben\n            </div>\n          </li>\n          \n          <li class=\"dib\">\n            <span class=\"icon iconfont icon-xunishujukuguanli\"></span>\n            <div class=\"name\">\n              虚拟数据库管理\n            </div>\n            <div class=\"code-name\">.icon-xunishujukuguanli\n            </div>\n          </li>\n          \n        </ul>\n        <div class=\"article markdown\">\n        <h2 id=\"font-class-\">font-class 引用</h2>\n        <hr>\n\n        <p>font-class 是 Unicode 使用方式的一种变种，主要是解决 Unicode 书写不直观，语意不明确的问题。</p>\n        <p>与 Unicode 使用方式相比，具有如下特点：</p>\n        <ul>\n          <li>相比于 Unicode 语意明确，书写更直观。可以很容易分辨这个 icon 是什么。</li>\n          <li>因为使用 class 来定义图标，所以当要替换图标时，只需要修改 class 里面的 Unicode 引用。</li>\n        </ul>\n        <p>使用步骤如下：</p>\n        <h3 id=\"-fontclass-\">第一步：引入项目下面生成的 fontclass 代码：</h3>\n<pre><code class=\"language-html\">&lt;link rel=\"stylesheet\" href=\"./iconfont.css\"&gt;\n</code></pre>\n        <h3 id=\"-\">第二步：挑选相应图标并获取类名，应用于页面：</h3>\n<pre><code class=\"language-html\">&lt;span class=\"iconfont icon-xxx\"&gt;&lt;/span&gt;\n</code></pre>\n        <blockquote>\n          <p>\"\n            iconfont\" 是你项目下的 font-family。可以通过编辑项目查看，默认是 \"iconfont\"。</p>\n        </blockquote>\n      </div>\n      </div>\n      <div class=\"content symbol\">\n          <ul class=\"icon_lists dib-box\">\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-right_on_5\"></use>\n                </svg>\n                <div class=\"name\">right_on_5</div>\n                <div class=\"code-name\">#icon-right_on_5</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-right_off_5-01\"></use>\n                </svg>\n                <div class=\"name\">right_off_5-01</div>\n                <div class=\"code-name\">#icon-right_off_5-01</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-left_on_huaban11\"></use>\n                </svg>\n                <div class=\"name\">left_on_2</div>\n                <div class=\"code-name\">#icon-a-left_on_huaban11</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-left_off_huaban1\"></use>\n                </svg>\n                <div class=\"name\">left_off</div>\n                <div class=\"code-name\">#icon-a-left_off_huaban1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-minimize21\"></use>\n                </svg>\n                <div class=\"name\">minimize21</div>\n                <div class=\"code-name\">#icon-minimize21</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-restore_button2\"></use>\n                </svg>\n                <div class=\"name\">restore</div>\n                <div class=\"code-name\">#icon-restore_button2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-resize_button2\"></use>\n                </svg>\n                <div class=\"name\">resize</div>\n                <div class=\"code-name\">#icon-resize_button2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-close_button2\"></use>\n                </svg>\n                <div class=\"name\">close</div>\n                <div class=\"code-name\">#icon-close_button2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shaixuan\"></use>\n                </svg>\n                <div class=\"name\">筛选</div>\n                <div class=\"code-name\">#icon-shaixuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-44tubiao-122\"></use>\n                </svg>\n                <div class=\"name\">排序</div>\n                <div class=\"code-name\">#icon-a-44tubiao-122</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xinxi-xianxingyuankuang\"></use>\n                </svg>\n                <div class=\"name\">305信息-线性圆框</div>\n                <div class=\"code-name\">#icon-xinxi-xianxingyuankuang</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jiahao\"></use>\n                </svg>\n                <div class=\"name\">加号</div>\n                <div class=\"code-name\">#icon-jiahao</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-liebiao\"></use>\n                </svg>\n                <div class=\"name\">列表</div>\n                <div class=\"code-name\">#icon-liebiao</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jianqu\"></use>\n                </svg>\n                <div class=\"name\">减去</div>\n                <div class=\"code-name\">#icon-jianqu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-database\"></use>\n                </svg>\n                <div class=\"name\">database</div>\n                <div class=\"code-name\">#icon-database</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shaixuan1\"></use>\n                </svg>\n                <div class=\"name\">筛选</div>\n                <div class=\"code-name\">#icon-shaixuan1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shuaxin2\"></use>\n                </svg>\n                <div class=\"name\">刷新</div>\n                <div class=\"code-name\">#icon-shuaxin2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jiahao_o\"></use>\n                </svg>\n                <div class=\"name\">加号_o</div>\n                <div class=\"code-name\">#icon-jiahao_o</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jurassic_data\"></use>\n                </svg>\n                <div class=\"name\">数据库_jurassic</div>\n                <div class=\"code-name\">#icon-jurassic_data</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-quanxian\"></use>\n                </svg>\n                <div class=\"name\">权限</div>\n                <div class=\"code-name\">#icon-quanxian</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-sharpicons_add-database\"></use>\n                </svg>\n                <div class=\"name\">sharpicons_add-database</div>\n                <div class=\"code-name\">#icon-sharpicons_add-database</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zuzhiguanli-\"></use>\n                </svg>\n                <div class=\"name\">组织管理</div>\n                <div class=\"code-name\">#icon-zuzhiguanli-</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-moxing-miaobian\"></use>\n                </svg>\n                <div class=\"name\">空间</div>\n                <div class=\"code-name\">#icon-moxing-miaobian</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xiajiantou1-copy\"></use>\n                </svg>\n                <div class=\"name\">下箭头-copy</div>\n                <div class=\"code-name\">#icon-xiajiantou1-copy</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-chakan2\"></use>\n                </svg>\n                <div class=\"name\">查看</div>\n                <div class=\"code-name\">#icon-chakan2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-clone\"></use>\n                </svg>\n                <div class=\"name\">clone</div>\n                <div class=\"code-name\">#icon-clone</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tijiao\"></use>\n                </svg>\n                <div class=\"name\">提交</div>\n                <div class=\"code-name\">#icon-tijiao</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-chakan1\"></use>\n                </svg>\n                <div class=\"name\">查看</div>\n                <div class=\"code-name\">#icon-chakan1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-fuzhi\"></use>\n                </svg>\n                <div class=\"name\">复制</div>\n                <div class=\"code-name\">#icon-fuzhi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-icon_answer\"></use>\n                </svg>\n                <div class=\"name\">icon_answer</div>\n                <div class=\"code-name\">#icon-icon_answer</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-icon_question\"></use>\n                </svg>\n                <div class=\"name\">icon_question</div>\n                <div class=\"code-name\">#icon-icon_question</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-fasong\"></use>\n                </svg>\n                <div class=\"name\">发送</div>\n                <div class=\"code-name\">#icon-fasong</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zhongqi\"></use>\n                </svg>\n                <div class=\"name\">重启</div>\n                <div class=\"code-name\">#icon-zhongqi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tixing2\"></use>\n                </svg>\n                <div class=\"name\">提醒</div>\n                <div class=\"code-name\">#icon-tixing2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tixing3\"></use>\n                </svg>\n                <div class=\"name\">提醒</div>\n                <div class=\"code-name\">#icon-tixing3</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tixing1\"></use>\n                </svg>\n                <div class=\"name\">提醒</div>\n                <div class=\"code-name\">#icon-tixing1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shengji\"></use>\n                </svg>\n                <div class=\"name\">升级</div>\n                <div class=\"code-name\">#icon-shengji</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-quanju_shengji\"></use>\n                </svg>\n                <div class=\"name\">全局_升级</div>\n                <div class=\"code-name\">#icon-quanju_shengji</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-guanyuwomen1\"></use>\n                </svg>\n                <div class=\"name\">关于我们</div>\n                <div class=\"code-name\">#icon-guanyuwomen1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-icobanbengengxin\"></use>\n                </svg>\n                <div class=\"name\">ico版本更新</div>\n                <div class=\"code-name\">#icon-icobanbengengxin</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-duihuaqipao\"></use>\n                </svg>\n                <div class=\"name\">对话气泡</div>\n                <div class=\"code-name\">#icon-duihuaqipao</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jiaosequanxian\"></use>\n                </svg>\n                <div class=\"name\">角色权限</div>\n                <div class=\"code-name\">#icon-jiaosequanxian</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-preview1\"></use>\n                </svg>\n                <div class=\"name\">preview</div>\n                <div class=\"code-name\">#icon-preview1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-daoru\"></use>\n                </svg>\n                <div class=\"name\">导入</div>\n                <div class=\"code-name\">#icon-daoru</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zhongzhi\"></use>\n                </svg>\n                <div class=\"name\">终止</div>\n                <div class=\"code-name\">#icon-zhongzhi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tuichu\"></use>\n                </svg>\n                <div class=\"name\">退出</div>\n                <div class=\"code-name\">#icon-tuichu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-kongzhuangzhongduan\"></use>\n                </svg>\n                <div class=\"name\">控桩终端</div>\n                <div class=\"code-name\">#icon-kongzhuangzhongduan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-chexiao1\"></use>\n                </svg>\n                <div class=\"name\">撤销</div>\n                <div class=\"code-name\">#icon-chexiao1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xiangshang\"></use>\n                </svg>\n                <div class=\"name\">向上</div>\n                <div class=\"code-name\">#icon-xiangshang</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-chakan-copy\"></use>\n                </svg>\n                <div class=\"name\">查看</div>\n                <div class=\"code-name\">#icon-chakan-copy</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jurassic_edit-data\"></use>\n                </svg>\n                <div class=\"name\">编辑数据_编辑录入操作_jurassic</div>\n                <div class=\"code-name\">#icon-jurassic_edit-data</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jurassic_edit-table\"></use>\n                </svg>\n                <div class=\"name\">编辑表格_编辑录入操作_jurassic</div>\n                <div class=\"code-name\">#icon-jurassic_edit-table</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-baobiaoshujuluru\"></use>\n                </svg>\n                <div class=\"name\">报表数据录入</div>\n                <div class=\"code-name\">#icon-baobiaoshujuluru</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-bofang5\"></use>\n                </svg>\n                <div class=\"name\">播放5</div>\n                <div class=\"code-name\">#icon-bofang5</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-qingkong3x\"></use>\n                </svg>\n                <div class=\"name\">清空@3x</div>\n                <div class=\"code-name\">#icon-a-qingkong3x</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shanchu\"></use>\n                </svg>\n                <div class=\"name\">删除</div>\n                <div class=\"code-name\">#icon-shanchu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-newdocumentworksheet\"></use>\n                </svg>\n                <div class=\"name\">new-document-worksheet</div>\n                <div class=\"code-name\">#icon-newdocumentworksheet</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-file-excel\"></use>\n                </svg>\n                <div class=\"name\">file-excel</div>\n                <div class=\"code-name\">#icon-file-excel</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-file-markdown\"></use>\n                </svg>\n                <div class=\"name\">file-markdown</div>\n                <div class=\"code-name\">#icon-file-markdown</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-file-word\"></use>\n                </svg>\n                <div class=\"name\">file-word</div>\n                <div class=\"code-name\">#icon-file-word</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-HTML\"></use>\n                </svg>\n                <div class=\"name\">HTML5</div>\n                <div class=\"code-name\">#icon-HTML</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-HTML1\"></use>\n                </svg>\n                <div class=\"name\">HTML</div>\n                <div class=\"code-name\">#icon-HTML1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-pdf\"></use>\n                </svg>\n                <div class=\"name\">pdf</div>\n                <div class=\"code-name\">#icon-pdf</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-gerenyonghu\"></use>\n                </svg>\n                <div class=\"name\">个人用户</div>\n                <div class=\"code-name\">#icon-gerenyonghu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-houtaiguanli\"></use>\n                </svg>\n                <div class=\"name\">后台管理</div>\n                <div class=\"code-name\">#icon-houtaiguanli</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zitidaima\"></use>\n                </svg>\n                <div class=\"name\">字体代码</div>\n                <div class=\"code-name\">#icon-zitidaima</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-banben\"></use>\n                </svg>\n                <div class=\"name\">版本</div>\n                <div class=\"code-name\">#icon-banben</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-cheweiguanli\"></use>\n                </svg>\n                <div class=\"name\">车位管理</div>\n                <div class=\"code-name\">#icon-cheweiguanli</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-dianzhelidaochu\"></use>\n                </svg>\n                <div class=\"name\">dictate</div>\n                <div class=\"code-name\">#icon-dianzhelidaochu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-circle-f\"></use>\n                </svg>\n                <div class=\"name\">circle-f</div>\n                <div class=\"code-name\">#icon-circle-f</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tubiao-hanshu\"></use>\n                </svg>\n                <div class=\"name\">图表-函数</div>\n                <div class=\"code-name\">#icon-tubiao-hanshu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shituguanliqi\"></use>\n                </svg>\n                <div class=\"name\">视图管理器</div>\n                <div class=\"code-name\">#icon-shituguanliqi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-huiche\"></use>\n                </svg>\n                <div class=\"name\">回车</div>\n                <div class=\"code-name\">#icon-huiche</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-quesheng\"></use>\n                </svg>\n                <div class=\"name\">缺省</div>\n                <div class=\"code-name\">#icon-quesheng</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jinrujiantou\"></use>\n                </svg>\n                <div class=\"name\">进入箭头</div>\n                <div class=\"code-name\">#icon-jinrujiantou</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-youjiantou_huaban\"></use>\n                </svg>\n                <div class=\"name\">右箭头</div>\n                <div class=\"code-name\">#icon-youjiantou_huaban</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xiangyoujiantou1\"></use>\n                </svg>\n                <div class=\"name\">向右箭头</div>\n                <div class=\"code-name\">#icon-xiangyoujiantou1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujuyuan\"></use>\n                </svg>\n                <div class=\"name\">数据源</div>\n                <div class=\"code-name\">#icon-shujuyuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-question\"></use>\n                </svg>\n                <div class=\"name\">question</div>\n                <div class=\"code-name\">#icon-question</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xingxing\"></use>\n                </svg>\n                <div class=\"name\">星星-copy</div>\n                <div class=\"code-name\">#icon-xingxing</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-kongzhitai\"></use>\n                </svg>\n                <div class=\"name\">控制台</div>\n                <div class=\"code-name\">#icon-kongzhitai</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xingxi\"></use>\n                </svg>\n                <div class=\"name\">星系</div>\n                <div class=\"code-name\">#icon-xingxi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-zanwushuju1\"></use>\n                </svg>\n                <div class=\"name\">暂无数据 (1)</div>\n                <div class=\"code-name\">#icon-a-zanwushuju1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-kaishi\"></use>\n                </svg>\n                <div class=\"name\">开始</div>\n                <div class=\"code-name\">#icon-kaishi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-guanbi\"></use>\n                </svg>\n                <div class=\"name\">关闭</div>\n                <div class=\"code-name\">#icon-guanbi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xiajiantou\"></use>\n                </svg>\n                <div class=\"name\">下箭头</div>\n                <div class=\"code-name\">#icon-xiajiantou</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-gengduo\"></use>\n                </svg>\n                <div class=\"name\">more</div>\n                <div class=\"code-name\">#icon-gengduo</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shezhi\"></use>\n                </svg>\n                <div class=\"name\">设置</div>\n                <div class=\"code-name\">#icon-shezhi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-duihua-weixuan\"></use>\n                </svg>\n                <div class=\"name\">对话-未选</div>\n                <div class=\"code-name\">#icon-duihua-weixuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tubiao-weixuan\"></use>\n                </svg>\n                <div class=\"name\">图表-未选</div>\n                <div class=\"code-name\">#icon-tubiao-weixuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-bianzu13beifen3\"></use>\n                </svg>\n                <div class=\"name\">编组 13备份 3</div>\n                <div class=\"code-name\">#icon-a-bianzu13beifen3</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-bianzubeifen\"></use>\n                </svg>\n                <div class=\"name\">编组备份</div>\n                <div class=\"code-name\">#icon-bianzubeifen</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-biaoge1\"></use>\n                </svg>\n                <div class=\"name\">表格</div>\n                <div class=\"code-name\">#icon-biaoge1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-shoucang1\"></use>\n                </svg>\n                <div class=\"name\">收藏 (1)</div>\n                <div class=\"code-name\">#icon-a-shoucang1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-guthub-weixuan1\"></use>\n                </svg>\n                <div class=\"name\">guthub-未选</div>\n                <div class=\"code-name\">#icon-guthub-weixuan1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shuju-weixuan\"></use>\n                </svg>\n                <div class=\"name\">数据-未选</div>\n                <div class=\"code-name\">#icon-shuju-weixuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-bianzu4\"></use>\n                </svg>\n                <div class=\"name\">编组 4</div>\n                <div class=\"code-name\">#icon-a-bianzu4</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-a-bianzu14beifen\"></use>\n                </svg>\n                <div class=\"name\">编组 14备份</div>\n                <div class=\"code-name\">#icon-a-bianzu14beifen</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-guthub-weixuan\"></use>\n                </svg>\n                <div class=\"name\">guthub-未选</div>\n                <div class=\"code-name\">#icon-guthub-weixuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-24gl-folderMinus\"></use>\n                </svg>\n                <div class=\"name\">24gl-folderMinus</div>\n                <div class=\"code-name\">#icon-24gl-folderMinus</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-24gl-folderOpen\"></use>\n                </svg>\n                <div class=\"name\">24gl-folderOpen</div>\n                <div class=\"code-name\">#icon-24gl-folderOpen</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-24gf-folderOpen\"></use>\n                </svg>\n                <div class=\"name\">24gf-folderOpen</div>\n                <div class=\"code-name\">#icon-24gf-folderOpen</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-yunshujuku\"></use>\n                </svg>\n                <div class=\"name\">云数据库</div>\n                <div class=\"code-name\">#icon-yunshujuku</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-baobiao\"></use>\n                </svg>\n                <div class=\"name\">报表</div>\n                <div class=\"code-name\">#icon-baobiao</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-gongzuotai\"></use>\n                </svg>\n                <div class=\"name\">工作台</div>\n                <div class=\"code-name\">#icon-gongzuotai</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-mongodb\"></use>\n                </svg>\n                <div class=\"name\">mongodb</div>\n                <div class=\"code-name\">#icon-mongodb</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-Redis\"></use>\n                </svg>\n                <div class=\"name\">Redis</div>\n                <div class=\"code-name\">#icon-Redis</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-HIVE\"></use>\n                </svg>\n                <div class=\"name\">HIVE_2</div>\n                <div class=\"code-name\">#icon-HIVE</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-Kingbase\"></use>\n                </svg>\n                <div class=\"name\">Kingbase</div>\n                <div class=\"code-name\">#icon-Kingbase</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-yibiaopan\"></use>\n                </svg>\n                <div class=\"name\">仪表盘</div>\n                <div class=\"code-name\">#icon-yibiaopan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-presto_sql\"></use>\n                </svg>\n                <div class=\"name\">presto</div>\n                <div class=\"code-name\">#icon-presto_sql</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujukuleixingtubiao-kuozhan-\"></use>\n                </svg>\n                <div class=\"name\">DB2</div>\n                <div class=\"code-name\">#icon-shujukuleixingtubiao-kuozhan-</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-oceanbase\"></use>\n                </svg>\n                <div class=\"name\">oceanbase</div>\n                <div class=\"code-name\">#icon-oceanbase</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-dameng1\"></use>\n                </svg>\n                <div class=\"name\">达梦</div>\n                <div class=\"code-name\">#icon-dameng1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-proxy\"></use>\n                </svg>\n                <div class=\"name\">proxy</div>\n                <div class=\"code-name\">#icon-proxy</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-openai\"></use>\n                </svg>\n                <div class=\"name\">openai</div>\n                <div class=\"code-name\">#icon-openai</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-guanyu\"></use>\n                </svg>\n                <div class=\"name\">关于</div>\n                <div class=\"code-name\">#icon-guanyu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-yifu\"></use>\n                </svg>\n                <div class=\"name\">衣服</div>\n                <div class=\"code-name\">#icon-yifu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujuku4\"></use>\n                </svg>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">#icon-shujuku4</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujuyuanpeizhi\"></use>\n                </svg>\n                <div class=\"name\">数据源配置</div>\n                <div class=\"code-name\">#icon-shujuyuanpeizhi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jurassic_server\"></use>\n                </svg>\n                <div class=\"name\">服务器_数据库_jurassic</div>\n                <div class=\"code-name\">#icon-jurassic_server</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujuku2\"></use>\n                </svg>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">#icon-shujuku2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujuku3\"></use>\n                </svg>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">#icon-shujuku3</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujukushuju\"></use>\n                </svg>\n                <div class=\"name\">数据库数据</div>\n                <div class=\"code-name\">#icon-shujukushuju</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujuku1\"></use>\n                </svg>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">#icon-shujuku1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-peizhishujuyuan\"></use>\n                </svg>\n                <div class=\"name\">配置数据源</div>\n                <div class=\"code-name\">#icon-peizhishujuyuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-SQLlishichaxun\"></use>\n                </svg>\n                <div class=\"name\">SQL历史查询</div>\n                <div class=\"code-name\">#icon-SQLlishichaxun</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zhongmingming\"></use>\n                </svg>\n                <div class=\"name\">重命名</div>\n                <div class=\"code-name\">#icon-zhongmingming</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun\"></use>\n                </svg>\n                <div class=\"name\">ico_数据查询与统计_预约情况查询</div>\n                <div class=\"code-name\">#icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-clickhouse-yunshujukuClickHouse\"></use>\n                </svg>\n                <div class=\"name\">clickhouse-云数据库ClickHouse</div>\n                <div class=\"code-name\">#icon-clickhouse-yunshujukuClickHouse</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-rds_mariadb\"></use>\n                </svg>\n                <div class=\"name\">rds_mariadb</div>\n                <div class=\"code-name\">#icon-rds_mariadb</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jianshaojianqujianhao\"></use>\n                </svg>\n                <div class=\"name\">减少减去减号</div>\n                <div class=\"code-name\">#icon-jianshaojianqujianhao</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-sqlserver\"></use>\n                </svg>\n                <div class=\"name\">sqlserver</div>\n                <div class=\"code-name\">#icon-sqlserver</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-sqlite\"></use>\n                </svg>\n                <div class=\"name\">sqlite</div>\n                <div class=\"code-name\">#icon-sqlite</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-queshengye_zanwushuju\"></use>\n                </svg>\n                <div class=\"name\">缺省页_暂无数据</div>\n                <div class=\"code-name\">#icon-queshengye_zanwushuju</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-weiwancheng\"></use>\n                </svg>\n                <div class=\"name\">未完成</div>\n                <div class=\"code-name\">#icon-weiwancheng</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-wancheng-\"></use>\n                </svg>\n                <div class=\"name\">完成-01</div>\n                <div class=\"code-name\">#icon-wancheng-</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-chenggong1\"></use>\n                </svg>\n                <div class=\"name\">成功</div>\n                <div class=\"code-name\">#icon-chenggong1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jiqiren\"></use>\n                </svg>\n                <div class=\"name\">机器人</div>\n                <div class=\"code-name\">#icon-jiqiren</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-huanyihuan\"></use>\n                </svg>\n                <div class=\"name\">换一换</div>\n                <div class=\"code-name\">#icon-huanyihuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-icon_infomation\"></use>\n                </svg>\n                <div class=\"name\">icon_infomation</div>\n                <div class=\"code-name\">#icon-icon_infomation</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-key1\"></use>\n                </svg>\n                <div class=\"name\">key</div>\n                <div class=\"code-name\">#icon-key1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-mysql\"></use>\n                </svg>\n                <div class=\"name\">mysql</div>\n                <div class=\"code-name\">#icon-mysql</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-oracle\"></use>\n                </svg>\n                <div class=\"name\">oracle</div>\n                <div class=\"code-name\">#icon-oracle</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-postgresql\"></use>\n                </svg>\n                <div class=\"name\">postgresql</div>\n                <div class=\"code-name\">#icon-postgresql</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-h2\"></use>\n                </svg>\n                <div class=\"name\">h2</div>\n                <div class=\"code-name\">#icon-h2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-cc-schema\"></use>\n                </svg>\n                <div class=\"name\">cc-schema</div>\n                <div class=\"code-name\">#icon-cc-schema</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xinjianbiaoge\"></use>\n                </svg>\n                <div class=\"name\">新建表格</div>\n                <div class=\"code-name\">#icon-xinjianbiaoge</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-export\"></use>\n                </svg>\n                <div class=\"name\">export</div>\n                <div class=\"code-name\">#icon-export</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jiaoseguanli\"></use>\n                </svg>\n                <div class=\"name\">角色管理</div>\n                <div class=\"code-name\">#icon-jiaoseguanli</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-console\"></use>\n                </svg>\n                <div class=\"name\">console</div>\n                <div class=\"code-name\">#icon-console</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-24gf-folderMinus\"></use>\n                </svg>\n                <div class=\"name\">24gf-folderMinus</div>\n                <div class=\"code-name\">#icon-24gf-folderMinus</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-chakan\"></use>\n                </svg>\n                <div class=\"name\">查看</div>\n                <div class=\"code-name\">#icon-chakan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-fuzhi_o\"></use>\n                </svg>\n                <div class=\"name\">复制_o</div>\n                <div class=\"code-name\">#icon-fuzhi_o</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zhihang\"></use>\n                </svg>\n                <div class=\"name\">执行</div>\n                <div class=\"code-name\">#icon-zhihang</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-m-geshihuawenzi\"></use>\n                </svg>\n                <div class=\"name\">m-格式化文字</div>\n                <div class=\"code-name\">#icon-m-geshihuawenzi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-github-fill\"></use>\n                </svg>\n                <div class=\"name\">github-fill</div>\n                <div class=\"code-name\">#icon-github-fill</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-baocun2\"></use>\n                </svg>\n                <div class=\"name\">保存</div>\n                <div class=\"code-name\">#icon-baocun2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jiantou_xiangzuoliangci_o\"></use>\n                </svg>\n                <div class=\"name\">箭头_向左两次_o</div>\n                <div class=\"code-name\">#icon-jiantou_xiangzuoliangci_o</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xinjianchuangkou\"></use>\n                </svg>\n                <div class=\"name\">新建窗口</div>\n                <div class=\"code-name\">#icon-xinjianchuangkou</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-loading2\"></use>\n                </svg>\n                <div class=\"name\">loading</div>\n                <div class=\"code-name\">#icon-loading2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-lianjiekelong\"></use>\n                </svg>\n                <div class=\"name\">链接克隆</div>\n                <div class=\"code-name\">#icon-lianjiekelong</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-SQLshengjiwenjian\"></use>\n                </svg>\n                <div class=\"name\">SQL升级文件</div>\n                <div class=\"code-name\">#icon-SQLshengjiwenjian</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-sql\"></use>\n                </svg>\n                <div class=\"name\">sql</div>\n                <div class=\"code-name\">#icon-sql</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-lianjieliu\"></use>\n                </svg>\n                <div class=\"name\">连接流</div>\n                <div class=\"code-name\">#icon-lianjieliu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tiaozhuan\"></use>\n                </svg>\n                <div class=\"name\">跳转/退出</div>\n                <div class=\"code-name\">#icon-tiaozhuan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-key\"></use>\n                </svg>\n                <div class=\"name\">key</div>\n                <div class=\"code-name\">#icon-key</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-bofangjilu\"></use>\n                </svg>\n                <div class=\"name\">播放记录</div>\n                <div class=\"code-name\">#icon-bofangjilu</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-chenggong\"></use>\n                </svg>\n                <div class=\"name\">成功</div>\n                <div class=\"code-name\">#icon-chenggong</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shibai\"></use>\n                </svg>\n                <div class=\"name\">失败</div>\n                <div class=\"code-name\">#icon-shibai</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shouhuishangxia\"></use>\n                </svg>\n                <div class=\"name\">收回 上下</div>\n                <div class=\"code-name\">#icon-shouhuishangxia</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zhankaishangxia\"></use>\n                </svg>\n                <div class=\"name\">展开 上下</div>\n                <div class=\"code-name\">#icon-zhankaishangxia</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shujuku\"></use>\n                </svg>\n                <div class=\"name\">数据库</div>\n                <div class=\"code-name\">#icon-shujuku</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-baocun\"></use>\n                </svg>\n                <div class=\"name\">保存</div>\n                <div class=\"code-name\">#icon-baocun</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-chaxun\"></use>\n                </svg>\n                <div class=\"name\">查询</div>\n                <div class=\"code-name\">#icon-chaxun</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-duigou11\"></use>\n                </svg>\n                <div class=\"name\">对勾</div>\n                <div class=\"code-name\">#icon-duigou11</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-check1\"></use>\n                </svg>\n                <div class=\"name\">check</div>\n                <div class=\"code-name\">#icon-check1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-gailan\"></use>\n                </svg>\n                <div class=\"name\">概览</div>\n                <div class=\"code-name\">#icon-gailan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-huaban2\"></use>\n                </svg>\n                <div class=\"name\">概览</div>\n                <div class=\"code-name\">#icon-huaban2</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-bianji\"></use>\n                </svg>\n                <div class=\"name\">编辑</div>\n                <div class=\"code-name\">#icon-bianji</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shuaxin1\"></use>\n                </svg>\n                <div class=\"name\">刷新</div>\n                <div class=\"code-name\">#icon-shuaxin1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-caidan\"></use>\n                </svg>\n                <div class=\"name\">菜单/列表</div>\n                <div class=\"code-name\">#icon-caidan</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-biaoge\"></use>\n                </svg>\n                <div class=\"name\">表格</div>\n                <div class=\"code-name\">#icon-biaoge</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zhankai\"></use>\n                </svg>\n                <div class=\"name\">展开</div>\n                <div class=\"code-name\">#icon-zhankai</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shouqi\"></use>\n                </svg>\n                <div class=\"name\">收起</div>\n                <div class=\"code-name\">#icon-shouqi</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zhuti_o\"></use>\n                </svg>\n                <div class=\"name\">主题_o</div>\n                <div class=\"code-name\">#icon-zhuti_o</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-duankailianjie\"></use>\n                </svg>\n                <div class=\"name\">断开连接</div>\n                <div class=\"code-name\">#icon-duankailianjie</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xiugai\"></use>\n                </svg>\n                <div class=\"name\">修改</div>\n                <div class=\"code-name\">#icon-xiugai</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-delete\"></use>\n                </svg>\n                <div class=\"name\">删除</div>\n                <div class=\"code-name\">#icon-delete</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-gengduo1\"></use>\n                </svg>\n                <div class=\"name\">更多</div>\n                <div class=\"code-name\">#icon-gengduo1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jianshao\"></use>\n                </svg>\n                <div class=\"name\">减少</div>\n                <div class=\"code-name\">#icon-jianshao</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-jia\"></use>\n                </svg>\n                <div class=\"name\">加</div>\n                <div class=\"code-name\">#icon-jia</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-hao\"></use>\n                </svg>\n                <div class=\"name\">加号</div>\n                <div class=\"code-name\">#icon-hao</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-right\"></use>\n                </svg>\n                <div class=\"name\">arrow drop down</div>\n                <div class=\"code-name\">#icon-right</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-search1\"></use>\n                </svg>\n                <div class=\"name\">search</div>\n                <div class=\"code-name\">#icon-search1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-download1\"></use>\n                </svg>\n                <div class=\"name\">download</div>\n                <div class=\"code-name\">#icon-download1</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xiangyoujiantou\"></use>\n                </svg>\n                <div class=\"name\">向右箭头</div>\n                <div class=\"code-name\">#icon-xiangyoujiantou</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shanchuxianxing\"></use>\n                </svg>\n                <div class=\"name\">删除线型</div>\n                <div class=\"code-name\">#icon-shanchuxianxing</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-cross-copy\"></use>\n                </svg>\n                <div class=\"name\">cross</div>\n                <div class=\"code-name\">#icon-cross-copy</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shuaxin\"></use>\n                </svg>\n                <div class=\"name\">刷新</div>\n                <div class=\"code-name\">#icon-shuaxin</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-tixing\"></use>\n                </svg>\n                <div class=\"name\">提醒</div>\n                <div class=\"code-name\">#icon-tixing</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-shezhixitongshezhigongnengshezhishuxing\"></use>\n                </svg>\n                <div class=\"name\">138设置、系统设置、功能设置、属性</div>\n                <div class=\"code-name\">#icon-shezhixitongshezhigongnengshezhishuxing</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-zhihangsqljiaoben\"></use>\n                </svg>\n                <div class=\"name\">执行sql脚本</div>\n                <div class=\"code-name\">#icon-zhihangsqljiaoben</div>\n            </li>\n          \n            <li class=\"dib\">\n                <svg class=\"icon svg-icon\" aria-hidden=\"true\">\n                  <use xlink:href=\"#icon-xunishujukuguanli\"></use>\n                </svg>\n                <div class=\"name\">虚拟数据库管理</div>\n                <div class=\"code-name\">#icon-xunishujukuguanli</div>\n            </li>\n          \n          </ul>\n          <div class=\"article markdown\">\n          <h2 id=\"symbol-\">Symbol 引用</h2>\n          <hr>\n\n          <p>这是一种全新的使用方式，应该说这才是未来的主流，也是平台目前推荐的用法。相关介绍可以参考这篇<a href=\"\">文章</a>\n            这种用法其实是做了一个 SVG 的集合，与另外两种相比具有如下特点：</p>\n          <ul>\n            <li>支持多色图标了，不再受单色限制。</li>\n            <li>通过一些技巧，支持像字体那样，通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>\n            <li>兼容性较差，支持 IE9+，及现代浏览器。</li>\n            <li>浏览器渲染 SVG 的性能一般，还不如 png。</li>\n          </ul>\n          <p>使用步骤如下：</p>\n          <h3 id=\"-symbol-\">第一步：引入项目下面生成的 symbol 代码：</h3>\n<pre><code class=\"language-html\">&lt;script src=\"./iconfont.js\"&gt;&lt;/script&gt;\n</code></pre>\n          <h3 id=\"-css-\">第二步：加入通用 CSS 代码（引入一次就行）：</h3>\n<pre><code class=\"language-html\">&lt;style&gt;\n.icon {\n  width: 1em;\n  height: 1em;\n  vertical-align: -0.15em;\n  fill: currentColor;\n  overflow: hidden;\n}\n&lt;/style&gt;\n</code></pre>\n          <h3 id=\"-\">第三步：挑选相应图标并获取类名，应用于页面：</h3>\n<pre><code class=\"language-html\">&lt;svg class=\"icon\" aria-hidden=\"true\"&gt;\n  &lt;use xlink:href=\"#icon-xxx\"&gt;&lt;/use&gt;\n&lt;/svg&gt;\n</code></pre>\n          </div>\n      </div>\n\n    </div>\n  </div>\n  <script>\n  $(document).ready(function () {\n      $('.tab-container .content:first').show()\n\n      $('#tabs li').click(function (e) {\n        var tabContent = $('.tab-container .content')\n        var index = $(this).index()\n\n        if ($(this).hasClass('active')) {\n          return\n        } else {\n          $('#tabs li').removeClass('active')\n          $(this).addClass('active')\n\n          tabContent.hide().eq(index).fadeIn()\n        }\n      })\n    })\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "chat2db-client/src/assets/font/iconfont.css",
    "content": "@font-face {\n  font-family: \"iconfont\"; /* Project id 3633546 */\n  src: url('iconfont.woff2?t=1704794525154') format('woff2'),\n       url('iconfont.woff?t=1704794525154') format('woff'),\n       url('iconfont.ttf?t=1704794525154') format('truetype');\n}\n\n.iconfont {\n  font-family: \"iconfont\" !important;\n  font-size: 16px;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.icon-right_on_5:before {\n  content: \"\\e672\";\n}\n\n.icon-right_off_5-01:before {\n  content: \"\\e673\";\n}\n\n.icon-a-left_on_huaban11:before {\n  content: \"\\e674\";\n}\n\n.icon-a-left_off_huaban1:before {\n  content: \"\\e670\";\n}\n\n.icon-minimize21:before {\n  content: \"\\e671\";\n}\n\n.icon-restore_button2:before {\n  content: \"\\e66b\";\n}\n\n.icon-resize_button2:before {\n  content: \"\\e66e\";\n}\n\n.icon-close_button2:before {\n  content: \"\\e66f\";\n}\n\n.icon-shaixuan:before {\n  content: \"\\e66a\";\n}\n\n.icon-a-44tubiao-122:before {\n  content: \"\\e69a\";\n}\n\n.icon-xinxi-xianxingyuankuang:before {\n  content: \"\\e8e8\";\n}\n\n.icon-jiahao:before {\n  content: \"\\e726\";\n}\n\n.icon-liebiao:before {\n  content: \"\\ec6b\";\n}\n\n.icon-jianqu:before {\n  content: \"\\e65d\";\n}\n\n.icon-database:before {\n  content: \"\\e669\";\n}\n\n.icon-shaixuan1:before {\n  content: \"\\e888\";\n}\n\n.icon-shuaxin2:before {\n  content: \"\\e668\";\n}\n\n.icon-jiahao_o:before {\n  content: \"\\eb78\";\n}\n\n.icon-jurassic_data:before {\n  content: \"\\e6a9\";\n}\n\n.icon-quanxian:before {\n  content: \"\\e667\";\n}\n\n.icon-sharpicons_add-database:before {\n  content: \"\\e816\";\n}\n\n.icon-zuzhiguanli-:before {\n  content: \"\\e663\";\n}\n\n.icon-moxing-miaobian:before {\n  content: \"\\e691\";\n}\n\n.icon-xiajiantou1-copy:before {\n  content: \"\\100be\";\n}\n\n.icon-chakan2:before {\n  content: \"\\e788\";\n}\n\n.icon-clone:before {\n  content: \"\\e8db\";\n}\n\n.icon-tijiao:before {\n  content: \"\\e687\";\n}\n\n.icon-chakan1:before {\n  content: \"\\e665\";\n}\n\n.icon-fuzhi:before {\n  content: \"\\ec7a\";\n}\n\n.icon-icon_answer:before {\n  content: \"\\e686\";\n}\n\n.icon-icon_question:before {\n  content: \"\\e6a8\";\n}\n\n.icon-fasong:before {\n  content: \"\\100bd\";\n}\n\n.icon-zhongqi:before {\n  content: \"\\e662\";\n}\n\n.icon-tixing2:before {\n  content: \"\\e6cc\";\n}\n\n.icon-tixing3:before {\n  content: \"\\e661\";\n}\n\n.icon-tixing1:before {\n  content: \"\\e716\";\n}\n\n.icon-shengji:before {\n  content: \"\\e69c\";\n}\n\n.icon-quanju_shengji:before {\n  content: \"\\e659\";\n}\n\n.icon-guanyuwomen1:before {\n  content: \"\\e65c\";\n}\n\n.icon-icobanbengengxin:before {\n  content: \"\\e67d\";\n}\n\n.icon-duihuaqipao:before {\n  content: \"\\e657\";\n}\n\n.icon-jiaosequanxian:before {\n  content: \"\\e658\";\n}\n\n.icon-preview1:before {\n  content: \"\\e654\";\n}\n\n.icon-daoru:before {\n  content: \"\\e653\";\n}\n\n.icon-zhongzhi:before {\n  content: \"\\e652\";\n}\n\n.icon-tuichu:before {\n  content: \"\\e6b2\";\n}\n\n.icon-kongzhuangzhongduan:before {\n  content: \"\\e6bb\";\n}\n\n.icon-chexiao1:before {\n  content: \"\\e6e2\";\n}\n\n.icon-xiangshang:before {\n  content: \"\\e650\";\n}\n\n.icon-chakan-copy:before {\n  content: \"\\e651\";\n}\n\n.icon-jurassic_edit-data:before {\n  content: \"\\e6f2\";\n}\n\n.icon-jurassic_edit-table:before {\n  content: \"\\e6f3\";\n}\n\n.icon-baobiaoshujuluru:before {\n  content: \"\\e7b5\";\n}\n\n.icon-bofang5:before {\n  content: \"\\e656\";\n}\n\n.icon-a-qingkong3x:before {\n  content: \"\\e64f\";\n}\n\n.icon-shanchu:before {\n  content: \"\\e64e\";\n}\n\n.icon-newdocumentworksheet:before {\n  content: \"\\e792\";\n}\n\n.icon-file-excel:before {\n  content: \"\\e7b7\";\n}\n\n.icon-file-markdown:before {\n  content: \"\\e7b8\";\n}\n\n.icon-file-word:before {\n  content: \"\\e7ba\";\n}\n\n.icon-HTML:before {\n  content: \"\\e87d\";\n}\n\n.icon-HTML1:before {\n  content: \"\\e64d\";\n}\n\n.icon-pdf:before {\n  content: \"\\e67a\";\n}\n\n.icon-gerenyonghu:before {\n  content: \"\\e64c\";\n}\n\n.icon-houtaiguanli:before {\n  content: \"\\e64b\";\n}\n\n.icon-zitidaima:before {\n  content: \"\\ec83\";\n}\n\n.icon-banben:before {\n  content: \"\\e70c\";\n}\n\n.icon-cheweiguanli:before {\n  content: \"\\e73c\";\n}\n\n.icon-dianzhelidaochu:before {\n  content: \"\\e64a\";\n}\n\n.icon-circle-f:before {\n  content: \"\\e76a\";\n}\n\n.icon-tubiao-hanshu:before {\n  content: \"\\e6fd\";\n}\n\n.icon-shituguanliqi:before {\n  content: \"\\e647\";\n}\n\n.icon-huiche:before {\n  content: \"\\e643\";\n}\n\n.icon-quesheng:before {\n  content: \"\\e642\";\n}\n\n.icon-jinrujiantou:before {\n  content: \"\\e88e\";\n}\n\n.icon-youjiantou_huaban:before {\n  content: \"\\e641\";\n}\n\n.icon-xiangyoujiantou1:before {\n  content: \"\\e660\";\n}\n\n.icon-shujuyuan:before {\n  content: \"\\e640\";\n}\n\n.icon-question:before {\n  content: \"\\e67c\";\n}\n\n.icon-xingxing:before {\n  content: \"\\e63a\";\n}\n\n.icon-kongzhitai:before {\n  content: \"\\e69f\";\n}\n\n.icon-xingxi:before {\n  content: \"\\e639\";\n}\n\n.icon-a-zanwushuju1:before {\n  content: \"\\e638\";\n}\n\n.icon-kaishi:before {\n  content: \"\\e637\";\n}\n\n.icon-guanbi:before {\n  content: \"\\e634\";\n}\n\n.icon-xiajiantou:before {\n  content: \"\\eb6d\";\n}\n\n.icon-gengduo:before {\n  content: \"\\e633\";\n}\n\n.icon-shezhi:before {\n  content: \"\\e630\";\n}\n\n.icon-duihua-weixuan:before {\n  content: \"\\e628\";\n}\n\n.icon-tubiao-weixuan:before {\n  content: \"\\e629\";\n}\n\n.icon-a-bianzu13beifen3:before {\n  content: \"\\e62b\";\n}\n\n.icon-bianzubeifen:before {\n  content: \"\\e616\";\n}\n\n.icon-biaoge1:before {\n  content: \"\\e618\";\n}\n\n.icon-a-shoucang1:before {\n  content: \"\\e61d\";\n}\n\n.icon-guthub-weixuan1:before {\n  content: \"\\e621\";\n}\n\n.icon-shuju-weixuan:before {\n  content: \"\\e622\";\n}\n\n.icon-a-bianzu4:before {\n  content: \"\\e624\";\n}\n\n.icon-a-bianzu14beifen:before {\n  content: \"\\e627\";\n}\n\n.icon-guthub-weixuan:before {\n  content: \"\\e615\";\n}\n\n.icon-24gl-folderMinus:before {\n  content: \"\\eabe\";\n}\n\n.icon-24gl-folderOpen:before {\n  content: \"\\eabf\";\n}\n\n.icon-24gf-folderOpen:before {\n  content: \"\\eac7\";\n}\n\n.icon-yunshujuku:before {\n  content: \"\\e744\";\n}\n\n.icon-baobiao:before {\n  content: \"\\e612\";\n}\n\n.icon-gongzuotai:before {\n  content: \"\\e614\";\n}\n\n.icon-mongodb:before {\n  content: \"\\ec21\";\n}\n\n.icon-Redis:before {\n  content: \"\\e6a2\";\n}\n\n.icon-HIVE:before {\n  content: \"\\e60e\";\n}\n\n.icon-Kingbase:before {\n  content: \"\\e6a0\";\n}\n\n.icon-yibiaopan:before {\n  content: \"\\e60d\";\n}\n\n.icon-presto_sql:before {\n  content: \"\\e60b\";\n}\n\n.icon-shujukuleixingtubiao-kuozhan-:before {\n  content: \"\\e60a\";\n}\n\n.icon-oceanbase:before {\n  content: \"\\e982\";\n}\n\n.icon-dameng1:before {\n  content: \"\\e655\";\n}\n\n.icon-proxy:before {\n  content: \"\\e63f\";\n}\n\n.icon-openai:before {\n  content: \"\\e646\";\n}\n\n.icon-guanyu:before {\n  content: \"\\e60c\";\n}\n\n.icon-yifu:before {\n  content: \"\\e666\";\n}\n\n.icon-shujuku4:before {\n  content: \"\\e609\";\n}\n\n.icon-shujuyuanpeizhi:before {\n  content: \"\\e649\";\n}\n\n.icon-jurassic_server:before {\n  content: \"\\e6a6\";\n}\n\n.icon-shujuku2:before {\n  content: \"\\e607\";\n}\n\n.icon-shujuku3:before {\n  content: \"\\e625\";\n}\n\n.icon-shujukushuju:before {\n  content: \"\\e63c\";\n}\n\n.icon-shujuku1:before {\n  content: \"\\e636\";\n}\n\n.icon-peizhishujuyuan:before {\n  content: \"\\e62f\";\n}\n\n.icon-SQLlishichaxun:before {\n  content: \"\\e80a\";\n}\n\n.icon-zhongmingming:before {\n  content: \"\\e623\";\n}\n\n.icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun:before {\n  content: \"\\e8ff\";\n}\n\n.icon-clickhouse-yunshujukuClickHouse:before {\n  content: \"\\e8f4\";\n}\n\n.icon-rds_mariadb:before {\n  content: \"\\e6f5\";\n}\n\n.icon-jianshaojianqujianhao:before {\n  content: \"\\e62a\";\n}\n\n.icon-sqlserver:before {\n  content: \"\\e664\";\n}\n\n.icon-sqlite:before {\n  content: \"\\e65a\";\n}\n\n.icon-queshengye_zanwushuju:before {\n  content: \"\\e760\";\n}\n\n.icon-weiwancheng:before {\n  content: \"\\e755\";\n}\n\n.icon-wancheng-:before {\n  content: \"\\e62e\";\n}\n\n.icon-chenggong1:before {\n  content: \"\\e620\";\n}\n\n.icon-jiqiren:before {\n  content: \"\\e70e\";\n}\n\n.icon-huanyihuan:before {\n  content: \"\\e635\";\n}\n\n.icon-icon_infomation:before {\n  content: \"\\e65b\";\n}\n\n.icon-key1:before {\n  content: \"\\e775\";\n}\n\n.icon-mysql:before {\n  content: \"\\ec6d\";\n}\n\n.icon-oracle:before {\n  content: \"\\ec48\";\n}\n\n.icon-postgresql:before {\n  content: \"\\ec5d\";\n}\n\n.icon-h2:before {\n  content: \"\\e61c\";\n}\n\n.icon-cc-schema:before {\n  content: \"\\e696\";\n}\n\n.icon-xinjianbiaoge:before {\n  content: \"\\e6b6\";\n}\n\n.icon-export:before {\n  content: \"\\e613\";\n}\n\n.icon-jiaoseguanli:before {\n  content: \"\\e66d\";\n}\n\n.icon-console:before {\n  content: \"\\e619\";\n}\n\n.icon-24gf-folderMinus:before {\n  content: \"\\eac5\";\n}\n\n.icon-chakan:before {\n  content: \"\\e606\";\n}\n\n.icon-fuzhi_o:before {\n  content: \"\\eb4e\";\n}\n\n.icon-zhihang:before {\n  content: \"\\e626\";\n}\n\n.icon-m-geshihuawenzi:before {\n  content: \"\\e7f8\";\n}\n\n.icon-github-fill:before {\n  content: \"\\e885\";\n}\n\n.icon-baocun2:before {\n  content: \"\\e645\";\n}\n\n.icon-jiantou_xiangzuoliangci_o:before {\n  content: \"\\eb93\";\n}\n\n.icon-xinjianchuangkou:before {\n  content: \"\\e603\";\n}\n\n.icon-loading2:before {\n  content: \"\\e6cd\";\n}\n\n.icon-lianjiekelong:before {\n  content: \"\\e6ca\";\n}\n\n.icon-SQLshengjiwenjian:before {\n  content: \"\\e63b\";\n}\n\n.icon-sql:before {\n  content: \"\\e610\";\n}\n\n.icon-lianjieliu:before {\n  content: \"\\ec57\";\n}\n\n.icon-tiaozhuan:before {\n  content: \"\\e685\";\n}\n\n.icon-key:before {\n  content: \"\\e648\";\n}\n\n.icon-bofangjilu:before {\n  content: \"\\e8ad\";\n}\n\n.icon-chenggong:before {\n  content: \"\\e605\";\n}\n\n.icon-shibai:before {\n  content: \"\\e87c\";\n}\n\n.icon-shouhuishangxia:before {\n  content: \"\\e790\";\n}\n\n.icon-zhankaishangxia:before {\n  content: \"\\e7b1\";\n}\n\n.icon-shujuku:before {\n  content: \"\\e62c\";\n}\n\n.icon-baocun:before {\n  content: \"\\e936\";\n}\n\n.icon-chaxun:before {\n  content: \"\\ec4c\";\n}\n\n.icon-duigou11:before {\n  content: \"\\e61f\";\n}\n\n.icon-check1:before {\n  content: \"\\e617\";\n}\n\n.icon-gailan:before {\n  content: \"\\e632\";\n}\n\n.icon-huaban2:before {\n  content: \"\\e63d\";\n}\n\n.icon-bianji:before {\n  content: \"\\e602\";\n}\n\n.icon-shuaxin1:before {\n  content: \"\\ec08\";\n}\n\n.icon-caidan:before {\n  content: \"\\e611\";\n}\n\n.icon-biaoge:before {\n  content: \"\\e63e\";\n}\n\n.icon-zhankai:before {\n  content: \"\\e65f\";\n}\n\n.icon-shouqi:before {\n  content: \"\\e61e\";\n}\n\n.icon-zhuti_o:before {\n  content: \"\\eb6f\";\n}\n\n.icon-duankailianjie:before {\n  content: \"\\e65e\";\n}\n\n.icon-xiugai:before {\n  content: \"\\e60f\";\n}\n\n.icon-delete:before {\n  content: \"\\e604\";\n}\n\n.icon-gengduo1:before {\n  content: \"\\e601\";\n}\n\n.icon-jianshao:before {\n  content: \"\\e644\";\n}\n\n.icon-jia:before {\n  content: \"\\e61b\";\n}\n\n.icon-hao:before {\n  content: \"\\e631\";\n}\n\n.icon-right:before {\n  content: \"\\e608\";\n}\n\n.icon-search1:before {\n  content: \"\\e600\";\n}\n\n.icon-download1:before {\n  content: \"\\e66c\";\n}\n\n.icon-xiangyoujiantou:before {\n  content: \"\\e79c\";\n}\n\n.icon-shanchuxianxing:before {\n  content: \"\\e6a7\";\n}\n\n.icon-cross-copy:before {\n  content: \"\\ec8e\";\n}\n\n.icon-shuaxin:before {\n  content: \"\\e62d\";\n}\n\n.icon-tixing:before {\n  content: \"\\e913\";\n}\n\n.icon-shezhixitongshezhigongnengshezhishuxing:before {\n  content: \"\\e795\";\n}\n\n.icon-zhihangsqljiaoben:before {\n  content: \"\\e759\";\n}\n\n.icon-xunishujukuguanli:before {\n  content: \"\\e61a\";\n}\n\n"
  },
  {
    "path": "chat2db-client/src/assets/font/iconfont.js",
    "content": "window._iconfont_svg_string_3633546='<svg><symbol id=\"icon-right_on_5\" viewBox=\"0 0 1024 1024\"><path d=\"M153.6 62.577778h716.8c59.733333 0 108.088889 48.355556 108.088889 108.088889v682.666666c-2.844444 59.733333-51.2 108.088889-108.088889 108.088889H153.6c-59.733333 0-108.088889-48.355556-108.088889-108.088889V167.822222c0-56.888889 48.355556-105.244444 108.088889-105.244444z m716.8 856.177778c34.133333 0 62.577778-28.444444 62.577778-62.577778V170.666667c0-34.133333-28.444444-62.577778-62.577778-62.577778H153.6c-34.133333 0-62.577778 28.444444-62.577778 62.577778v682.666666c0 34.133333 28.444444 62.577778 62.577778 62.577778l716.8 2.844445z\"  ></path><path d=\"M688.355556 82.488889h182.044444c48.355556 0 85.333333 36.977778 85.333333 85.333333V853.333333c0 48.355556-36.977778 85.333333-85.333333 85.333334h-182.044444V82.488889z\"  ></path><path d=\"M674.133333 68.266667h196.266667c54.044444 0 99.555556 45.511111 99.555556 99.555555V853.333333c0 54.044444-45.511111 99.555556-99.555556 99.555556h-196.266667V68.266667z m196.266667 856.177777c39.822222 0 71.111111-31.288889 71.111111-71.111111V167.822222c0-39.822222-31.288889-71.111111-71.111111-71.111111h-167.822222v827.733333h167.822222z\"  ></path></symbol><symbol id=\"icon-right_off_5-01\" viewBox=\"0 0 1024 1024\"><path d=\"M153.6 62.577778h716.8c59.733333 0 108.088889 48.355556 108.088889 108.088889v682.666666c-2.844444 59.733333-51.2 108.088889-108.088889 108.088889H153.6c-59.733333 0-108.088889-48.355556-108.088889-108.088889V167.822222c0-56.888889 48.355556-105.244444 108.088889-105.244444z m716.8 856.177778c34.133333 0 62.577778-28.444444 62.577778-62.577778V170.666667c0-34.133333-28.444444-62.577778-62.577778-62.577778H153.6c-34.133333 0-62.577778 28.444444-62.577778 62.577778v682.666666c0 34.133333 28.444444 62.577778 62.577778 62.577778l716.8 2.844445z\"  ></path><path d=\"M665.6 62.577778h204.8c59.733333 0 108.088889 48.355556 108.088889 108.088889v682.666666c-2.844444 59.733333-51.2 108.088889-108.088889 108.088889h-201.955556V62.577778h-2.844444z m204.8 856.177778c34.133333 0 62.577778-28.444444 62.577778-62.577778V170.666667c0-34.133333-28.444444-62.577778-62.577778-62.577778H711.111111v810.666667h159.288889z\"  ></path></symbol><symbol id=\"icon-a-left_on_huaban11\" viewBox=\"0 0 1024 1024\"><path d=\"M859.022222 972.8H142.222222c-59.733333 0-108.088889-48.355556-108.088889-108.088889v-682.666667C36.977778 122.311111 85.333333 73.955556 142.222222 73.955556h716.8c59.733333 0 108.088889 48.355556 108.088889 108.088888v685.511112c0 56.888889-48.355556 105.244444-108.088889 105.244444zM142.222222 116.622222c-34.133333 0-62.577778 28.444444-62.577778 62.577778v685.511111c0 34.133333 28.444444 62.577778 62.577778 62.577778h716.8c34.133333 0 62.577778-28.444444 62.577778-62.577778v-682.666667c0-34.133333-28.444444-62.577778-62.577778-62.577777H142.222222z\"  ></path><path d=\"M324.266667 952.888889H142.222222c-48.355556 0-85.333333-36.977778-85.333333-85.333333V182.044444c0-48.355556 36.977778-85.333333 85.333333-85.333333h182.044445v856.177778z\"  ></path><path d=\"M338.488889 967.111111H142.222222c-54.044444 0-99.555556-45.511111-99.555555-99.555555V182.044444c0-54.044444 45.511111-99.555556 99.555555-99.555555h196.266667V967.111111zM142.222222 110.933333C102.4 110.933333 71.111111 142.222222 71.111111 182.044444v685.511112C71.111111 907.377778 102.4 938.666667 142.222222 938.666667h167.822222V110.933333H142.222222z\"  ></path></symbol><symbol id=\"icon-a-left_off_huaban1\" viewBox=\"0 0 1024 1024\"><path d=\"M859.022222 972.8H142.222222c-59.733333 0-108.088889-48.355556-108.088889-108.088889v-682.666667C36.977778 122.311111 85.333333 73.955556 142.222222 73.955556h716.8c59.733333 0 108.088889 48.355556 108.088889 108.088888v685.511112c0 56.888889-48.355556 105.244444-108.088889 105.244444zM142.222222 116.622222c-34.133333 0-62.577778 28.444444-62.577778 62.577778v685.511111c0 34.133333 28.444444 62.577778 62.577778 62.577778h716.8c34.133333 0 62.577778-28.444444 62.577778-62.577778v-682.666667c0-34.133333-28.444444-62.577778-62.577778-62.577777H142.222222z\"  ></path><path d=\"M347.022222 972.8H142.222222c-59.733333 0-108.088889-48.355556-108.088889-108.088889v-682.666667C36.977778 122.311111 85.333333 73.955556 142.222222 73.955556h201.955556v898.844444zM142.222222 116.622222c-34.133333 0-62.577778 28.444444-62.577778 62.577778v685.511111c0 34.133333 28.444444 62.577778 62.577778 62.577778h159.288889V116.622222H142.222222z\"  ></path></symbol><symbol id=\"icon-minimize21\" viewBox=\"0 0 1024 1024\"><path d=\"M948.90666667 534.7555552H75.09333333c-13.65333333 0-22.7555552-9.10222187-22.7555552-22.7555552s9.10222187-22.7555552 22.7555552-22.7555552h876.08888854c13.65333333 0 22.7555552 9.10222187 22.75555626 22.7555552s-11.37777813 22.7555552-25.03111146 22.7555552z\"  ></path></symbol><symbol id=\"icon-restore_button2\" viewBox=\"0 0 1025 1024\"><path d=\"M843.40960156 962H181.96935664A119.96935664 119.96935664 0 0 1 62 843.40960156V181.96935664A119.96935664 119.96935664 0 0 1 181.96935664 62H843.40960156a119.96935664 119.96935664 0 0 1 119.96935664 119.96935664V843.40960156A119.96935664 119.96935664 0 0 1 843.40960156 962zM181.96935664 117.15832461A64.81103203 64.81103203 0 0 0 117.15832461 181.96935664V843.40960156a64.81103203 64.81103203 0 0 0 64.81103203 64.81103203H843.40960156A64.81103203 64.81103203 0 0 0 906.84167539 843.40960156V181.96935664A64.81103203 64.81103203 0 0 0 843.40960156 117.15832461z\"  ></path></symbol><symbol id=\"icon-resize_button2\" viewBox=\"0 0 1024 1024\"><path d=\"M875.33922237 766.41696143h-143.50706719V340.26855137a39.75265019 39.75265019 0 0 0-39.7526502-39.7526502h-437.27915127V143.49293281A81.49293281 81.49293281 0 0 1 333.90812744 62h541.43109493a81.49293281 81.49293281 0 0 1 81.49293281 81.49293281v541.43109581a81.49293281 81.49293281 0 0 1-81.49293281 81.49293281z m-103.754417-39.7526502h103.754417a39.75265019 39.75265019 0 0 0 41.74028261-39.75265019V143.49293281a39.75265019 39.75265019 0 0 0-41.74028261-39.75265019H333.90812744a39.75265019 39.75265019 0 0 0-39.7526502 39.75265019v114.09010576h397.52650196a79.50530039 79.50530039 0 0 1 79.50530039 81.49293281z\"  ></path><path d=\"M690.09187256 962H148.66077763a81.49293281 81.49293281 0 0 1-79.50530039-81.49293281V340.26855137A79.50530039 79.50530039 0 0 1 150.64841006 260.76325098h539.4434625a79.50530039 79.50530039 0 0 1 79.50530039 81.49293281v538.2508834a81.49293281 81.49293281 0 0 1-79.50530039 81.49293281zM148.66077763 297.33568877A39.75265019 39.75265019 0 0 0 106.92049502 340.26855137v540.23851582a39.75265019 39.75265019 0 0 0 39.75265019 41.74028262h543.41872735a39.75265019 39.75265019 0 0 0 39.7526502-41.74028262V340.26855137a39.75265019 39.75265019 0 0 0-39.7526502-39.7526502z\"  ></path></symbol><symbol id=\"icon-close_button2\" viewBox=\"0 0 1024 1024\"><path d=\"M883.62300147 907.5078125a23.88481258 23.88481258 0 0 1-17.11744913-7.16544362L123.6878867 156.33046199a23.08865227 23.08865227 0 0 1 0-33.43873715 23.08865227 23.08865227 0 0 1 32.64257684 0.79616031l744.01190611 742.81766641a24.28289275 24.28289275 0 0 1-16.71936818 39.80802047z\"  ></path><path d=\"M140.40725489 907.5078125a23.08865227 23.08865227 0 0 1-16.7193682-7.16544362 23.48673243 23.48673243 0 0 1 0-33.83681732L866.50555234 123.68788515a23.48673243 23.48673243 0 0 1 33.83681731 0 23.08865227 23.08865227 0 0 1 0 33.43873715L156.33046354 900.34236888a23.08865227 23.08865227 0 0 1-15.92320866 7.16544362z\"  ></path></symbol><symbol id=\"icon-shaixuan\" viewBox=\"0 0 1024 1024\"><path d=\"M925.918257 211.615069c-0.818627 1.125612-2.762866 2.251224-3.786149 3.17218L674.906765 459.966024v402.662136c0 46.354752-35.507944 84.011592-81.965024 84.011592-21.488958 0-39.703408-8.18627-55.359648-22.921555-1.125612-0.716299-2.251224-1.637254-3.17218-2.558209L465.645248 853.21395c-8.493255-8.390926-8.595583-22.0006-0.204656-30.596182l29.265913-29.470571c8.288598-8.083941 21.591286-8.18627 29.982213-0.102328l62.829619 65.080843c0.920955 0.818627 1.330269 1.637254 2.455881 2.660538-0.61397-172.832617-0.818627-378.61497-0.818627-419.750975v-7.265314c0-5.730389 5.116419-11.563106 9.209553-15.553912l264.211852-261.960628c0.716299-0.920955 1.534926-3.479165 2.353553-3.479164h-705.042471c0.818627 0 0.716299 2.455881 1.330269 3.376836L421.644049 418.625362c4.093135 4.093135 4.502448 9.516538 4.502448 15.246927V606.090936c0 0.204657 0.61397 0.511642 0.613971 0.716299-0.511642 23.330868-18.726092 41.852303-41.647647 41.852303-23.330868 0-44.819826-19.033077-44.819826-42.363945v-146.329569l-242.415909-245.588089c-0.920955-0.920955 0.61397-2.455881-0.102328-3.479164-14.837614-15.860897-23.126212-36.735885-23.023884-58.429499 0-46.354752 37.65684-85.648846 83.909264-85.648846h707.600679c22.409913 0 43.489557 10.335165 59.350455 26.196062 15.860897 15.860897 24.047167 37.759169 24.047167 60.169082-0.204657 21.591286-8.902568 42.773259-23.740182 58.429499zM388.182672 665.850705c25.786749 0.920955 45.945438 22.512241 45.024483 48.29899-0.818627 24.558809-20.568002 44.205856-45.024483 45.024483-25.786749-0.920955-45.945438-22.512241-45.024483-48.298991 0.818627-24.45648 20.465674-44.205856 45.024483-45.024482z\"  ></path></symbol><symbol id=\"icon-a-44tubiao-122\" viewBox=\"0 0 1024 1024\"><path d=\"M642.80720839 935.66707029a39.98357959 39.98357959 0 0 1-28.33273514-11.91563605l-132.39596032-132.39595918A39.71878798 39.71878798 0 0 1 538.47919161 735.48437959l63.55006122 64.34443606V329.82315918a39.71878798 39.71878798 0 0 1 79.43757597 0v566.91949909a39.71878798 39.71878798 0 0 1-24.89044082 36.80607688 41.04274717 41.04274717 0 0 1-13.76917959 2.11833514zM837.95885283 935.66707029a39.71878798 39.71878798 0 0 1-39.71878798-39.71878798V329.82315918a39.71878798 39.71878798 0 0 1 68.05152313-28.06794353l132.39595919 132.39596032a39.71878798 39.71878798 0 0 1-56.6654703 55.87109432l-63.55006122-64.60922766v471.32961564A39.71878798 39.71878798 0 0 1 837.95885283 935.66707029zM45.17184626 170.41842403a38.92441202 38.92441202 0 0 1-38.39482767-34.15815736 39.98357959 39.98357959 0 0 1 34.15815738-45.01462585c26.47919161-3.97187869 658.0079195-1.58875193 928.89005283 0a39.71878798 39.71878798 0 1 1 0 79.43757483C617.91676757 167.77050453 81.97792313 167.77050453 50.99726848 169.88884082zM331.67670272 538.47919161H45.17184626a39.71878798 39.71878798 0 0 1 0-79.43757596h286.50485646a39.71878798 39.71878798 0 1 1 0 79.43757596zM331.67670272 860.99574898H45.17184626a39.71878798 39.71878798 0 1 1 0-79.43757483h286.50485646a39.71878798 39.71878798 0 1 1 0 79.43757483z\"  ></path></symbol><symbol id=\"icon-xinxi-xianxingyuankuang\" viewBox=\"0 0 1024 1024\"><path d=\"M512 128c212 0 384 172 384 384s-172 384-384 384-384-172-384-384 172-384 384-384m0-64C264.8 64 64 264.8 64 512s200.8 448 448 448 448-200.8 448-448S759.2 64 512 64z m32 192h-64v64h64v-64z m0 448V384h-32.8L384 418.4l16.8 61.6 79.2-21.6V704H384v64h256v-64H544z\"  ></path></symbol><symbol id=\"icon-jiahao\" viewBox=\"0 0 1024 1024\"><path d=\"M512 958.016611c-119.648434 0-232.1288-46.367961-316.736783-130.559656-84.640667-84.255342-131.263217-196.255772-131.263217-315.455235 0-119.168499 46.624271-231.199892 131.232254-315.424271 84.607983-84.191695 197.088348-130.559656 316.736783-130.559656s232.1288 46.367961 316.704099 130.559656c84.67163 84.224378 131.263217 196.255772 131.263217 315.391587 0.032684 119.199462-46.591587 231.232576-131.263217 315.455235C744.1288 911.615966 631.648434 958.016611 512 958.016611zM512 129.983389c-102.623626 0-199.071738 39.743475-271.583282 111.936783-72.480581 72.12794-112.416718 168.063432-112.416718 270.079828s39.903454 197.951888 112.384034 270.047144c72.511544 72.191587 168.959656 111.936783 271.583282 111.936783 102.592662 0 199.071738-39.743475 271.583282-111.936783 72.480581-72.160624 112.416718-168.063432 112.384034-270.079828 0-102.016396-39.903454-197.919204-112.384034-270.016181C711.071738 169.759548 614.592662 129.983389 512 129.983389z\" fill=\"#575B66\" ></path><path d=\"M736.00086 480.00086 544.00086 480.00086 544.00086 288.00086c0-17.664722-14.336138-32.00086-32.00086-32.00086s-32.00086 14.336138-32.00086 32.00086l0 192L288.00086 480.00086c-17.664722 0-32.00086 14.336138-32.00086 32.00086s14.336138 32.00086 32.00086 32.00086l192 0 0 192c0 17.695686 14.336138 32.00086 32.00086 32.00086s32.00086-14.303454 32.00086-32.00086L544.00258 544.00086l192 0c17.695686 0 32.00086-14.336138 32.00086-32.00086S753.696546 480.00086 736.00086 480.00086z\" fill=\"#575B66\" ></path></symbol><symbol id=\"icon-liebiao\" viewBox=\"0 0 1024 1024\"><path d=\"M64 480h352V128H64v352z m64-288h224v224H128V192zM64 928h352V576H64v352z m64-288h224v224H128v-224zM526.848 224H928a32 32 0 1 0 0-64H526.848a32 32 0 0 0 0 64zM928 608H526.848a32 32 0 1 0 0 64H928a32 32 0 1 0 0-64zM928 384H526.848a32 32 0 0 0 0 64H928a32 32 0 1 0 0-64zM928 832H526.848a32 32 0 1 0 0 64H928a32 32 0 1 0 0-64z\"  ></path></symbol><symbol id=\"icon-jianqu\" viewBox=\"0 0 1024 1024\"><path d=\"M512.1 127.8c51.9 0 102.2 10.1 149.5 30.2 45.7 19.3 86.8 47 122.1 82.3s63 76.4 82.3 122.1c20 47.3 30.2 97.6 30.2 149.5S886 614 865.9 661.3c-19.3 45.7-47 86.8-82.3 122.1s-76.4 63-122.1 82.3c-47.3 20-97.6 30.2-149.5 30.2s-102.2-10.1-149.5-30.2c-45.7-19.3-86.8-47-122.1-82.3s-63-76.4-82.3-122.1c-20-47.3-30.2-97.6-30.2-149.5s10.1-102.2 30.2-149.5c19.3-45.7 47-86.8 82.3-122.1s76.4-63 122.1-82.3c47.4-19.9 97.7-30.1 149.6-30.1m0-64c-247.4 0-448 200.6-448 448s200.6 448 448 448 448-200.6 448-448-200.6-448-448-448z\"  ></path><path d=\"M256 511.8h512.1\" fill=\"#FFFFFF\" ></path><path d=\"M256 479.8h512.1v64H256z\"  ></path></symbol><symbol id=\"icon-database\" viewBox=\"0 0 1024 1024\"><path d=\"M510.664 0C271.44 0 84 84.352 84 192v640c0 107.648 187.44 192 426.664 192 239.232 0 426.672-84.352 426.672-192V192c0-107.648-187.44-192-426.672-192z m384 832c0 70.616-157.696 149.336-384 149.336s-384-78.72-384-149.336V703.232c68.4 64.08 212.696 107.44 384 107.44s315.608-43.36 384-107.44V832z m0-213.336c0 70.616-157.696 149.336-384 149.336s-384-78.72-384-149.336V489.896c68.4 64.088 212.696 107.44 384 107.44s315.608-43.36 384-107.44v128.768z m0-213.328c0 70.608-157.696 149.336-384 149.336s-384-78.72-384-149.336V276.56C195.064 340.64 339.36 384 510.664 384s315.608-43.352 384-107.44v128.776z m-384-64c-226.304 0-384-78.72-384-149.336 0-70.616 157.696-149.336 384-149.336s384 78.72 384 149.336c0 70.616-157.696 149.336-384 149.336z\" fill=\"#4F5F78\" ></path></symbol><symbol id=\"icon-shaixuan1\" viewBox=\"0 0 1024 1024\"><path d=\"M415.10380999 515.44670777c0-32.96218112-17.13644999-76.45866667-39.63714332-100.68271332L133.90582557 154.99870777C112.06049223 130.92029667 115.26447445 123.6385189 147.78974777 123.6385189h728.42050446c32.45245667 0 35.68071111 7.28177778 13.76256 31.53009777l-240.63848334 256.56130333c-22.47642112 23.95704889-39.61287111 67.38071666-39.61287111 100.24580779v339.96193109a24.27259221 24.27259221 0 1 0 48.54518556 0V512c0-20.53461333 12.45184-52.06471111 26.45712555-67.01662777l240.95402666-256.90112C976.06769778 132.35237888 950.84847445 75.09333333 876.21025223 75.09333333H147.78974777c-74.78385778 0-99.83317333 57.2833189-49.66172444 112.72192l241.77929557 260.00801224c14.17519445 15.24318777 26.65130667 46.94319445 26.65130667 67.6234422v408.89609557a24.27259221 24.27259221 0 1 0 48.54518442 0V515.44670777z\" fill=\"#3D3D3D\" ></path></symbol><symbol id=\"icon-shuaxin2\" viewBox=\"0 0 1024 1024\"><path d=\"M137.77487645 562.46044444a378.76895289 378.76895289 0 0 0 700.56527644 133.98471111 29.12711111 29.12711111 0 1 1 50.09863111 29.59314489A436.96492089 436.96492089 0 0 1 78.938112 562.46044444H15.90704355a11.65084445 11.65084445 0 0 1-8.27209955-19.86468978L96.06485333 454.10759111a11.65084445 11.65084445 0 0 1 16.42769067 0L200.92245333 542.65400888a11.65084445 11.65084445 0 0 1-8.27209955 19.86468978h-54.75896889zM889.54561422 475.07911111A378.65244445 378.65244445 0 0 0 175.98964622 329.44355555a29.12711111 29.12711111 0 1 1-51.67149511-26.91345067A436.90666667 436.90666667 0 0 1 947.91634489 475.07911111h60.23486578a11.65084445 11.65084445 0 0 1 8.27209955 19.86468977L927.93514667 583.43196444a11.65084445 11.65084445 0 0 1-16.42769067 0L823.07754667 494.88554666A11.65084445 11.65084445 0 0 1 831.40790045 475.07911111h58.13771377z\" fill=\"#9DA7B2\" ></path></symbol><symbol id=\"icon-jiahao_o\" viewBox=\"0 0 1024 1024\"><path d=\"M490.66666667 493.33333334V104.97185223h48.54518557v388.36148111h388.3614811v48.54518556h-388.3614811v388.36148111h-48.54518557v-388.36148111H102.30518557v-48.54518556h388.3614811z\" fill=\"#444444\" ></path></symbol><symbol id=\"icon-jurassic_data\" viewBox=\"0 0 1024 1024\"><path d=\"M927.8 193.7c0.1-1.6 0.2-3.2 0.2-4.8 0-15.7-6.3-30-18.7-42.9-6.9-7.2-15.8-13.9-26.5-20.1-21.9-12.7-52.3-23.9-90.5-33.3C717.1 74.2 617.5 64 512 64S306.9 74.2 231.7 92.6c-38.2 9.4-68.6 20.6-90.5 33.3-2.4 1.4-4.8 2.9-7 4.3-18.5 12.1-30.4 25.9-35.4 41.2-1.6 4.8-2.5 9.8-2.7 15H96v649.2c0 24.3 15.2 45.4 45.2 62.8 21.9 12.6 52.3 23.8 90.5 33.1C306.9 949.9 406.5 960 512 960s205.1-10.1 280.3-28.5c38.2-9.3 68.6-20.5 90.5-33.1 30-17.4 45.2-38.5 45.2-62.8V394h-0.2V193.7z m-58.3 419.6c-0.3 0.4-0.7 1-1.5 1.8l-0.6 0.6c-1.2 1.2-3 2.8-5.5 4.7-0.3 0.2-0.5 0.4-0.8 0.6-1.4 1-3 2.1-4.8 3.2-0.3 0.2-0.6 0.4-0.9 0.5-2.2 1.3-4.7 2.7-7.6 4.2-0.2 0.1-0.5 0.3-0.7 0.4-0.2 0.1-0.5 0.3-0.7 0.4-0.5 0.3-1 0.5-1.5 0.7-2.3 1.1-4.9 2.3-7.7 3.5-0.6 0.3-1.2 0.5-1.8 0.8 0 0-0.1 0-0.1 0.1-0.5 0.2-1.1 0.5-1.7 0.7-4.1 1.7-8.6 3.4-13.6 5.2-0.8 0.3-1.6 0.6-2.4 0.8-4.2 1.4-8.7 2.9-13.6 4.3-0.3 0.1-0.7 0.2-1 0.3-0.8 0.2-1.5 0.5-2.3 0.7-0.5 0.2-1.1 0.3-1.7 0.5-0.3 0.1-0.7 0.2-1 0.3-0.5 0.1-1 0.3-1.5 0.4-2 0.6-4.1 1.1-6.2 1.7l-3.3 0.9c-6.9 1.8-14.4 3.6-22.5 5.4-69.1 15.2-158.8 23.6-252.6 23.6s-183.5-8.4-252.6-23.6c-8.1-1.8-15.6-3.6-22.5-5.4l-3.3-0.9c-2.1-0.6-4.2-1.1-6.2-1.7-0.5-0.1-1-0.3-1.5-0.4-0.3-0.1-0.7-0.2-1-0.3-0.6-0.2-1.1-0.3-1.7-0.5-0.8-0.2-1.6-0.5-2.3-0.7-0.3-0.1-0.7-0.2-1-0.3-4.9-1.5-9.4-2.9-13.6-4.3-0.8-0.3-1.6-0.6-2.4-0.8-5-1.8-9.5-3.5-13.6-5.2-0.6-0.2-1.1-0.5-1.7-0.7 0 0-0.1 0-0.1-0.1-0.6-0.3-1.2-0.5-1.8-0.8-2.8-1.2-5.4-2.4-7.7-3.5-0.5-0.3-1-0.5-1.5-0.7-0.3-0.1-0.5-0.3-0.7-0.4-0.2-0.1-0.5-0.2-0.7-0.4-2.9-1.5-5.4-2.9-7.6-4.2-0.3-0.2-0.6-0.4-0.9-0.5-1.8-1.1-3.4-2.2-4.8-3.2-0.3-0.2-0.5-0.4-0.8-0.6-2.5-1.9-4.3-3.4-5.5-4.7l-0.6-0.6c-0.7-0.8-1.2-1.4-1.5-1.8V463.8c2 1 4.1 1.9 6.2 2.9 0.6 0.3 1.1 0.5 1.7 0.7l2.1 0.9c0.8 0.3 1.5 0.6 2.3 1 0.7 0.3 1.3 0.6 2 0.8 0.7 0.3 1.4 0.6 2.1 0.8 3.6 1.4 7.3 2.9 11.2 4.3 0.7 0.3 1.5 0.5 2.2 0.8 4.7 1.7 9.7 3.3 14.8 4.9 1.7 0.5 3.4 1.1 5.1 1.6 1.2 0.4 2.4 0.7 3.6 1.1l3.3 0.9c1.4 0.4 2.8 0.8 4.3 1.2 1.7 0.5 3.3 0.9 5 1.4 1.5 0.4 3.1 0.8 4.7 1.2 1 0.3 2.1 0.5 3.1 0.8l3.6 0.9c75.2 18.4 174.8 28.5 280.3 28.5h10.5c3.1 0 6.1 0 9.2-0.1h1.9c2.3 0 4.7-0.1 7-0.1 5.2-0.1 10.4-0.2 15.5-0.4 2.9-0.1 5.8-0.2 8.6-0.3 3.2-0.1 6.3-0.2 9.5-0.3 1.6-0.1 3.1-0.1 4.7-0.2 6.3-0.3 12.5-0.6 18.7-0.9 1.2-0.1 2.4-0.1 3.6-0.2l10.2-0.6c1.3-0.1 2.6-0.2 3.8-0.2 7.9-0.5 15.7-1.1 23.4-1.8 1.5-0.1 3-0.3 4.5-0.4 3.8-0.3 7.6-0.7 11.4-1 1.5-0.1 3-0.3 4.4-0.4 12.5-1.2 24.8-2.6 36.8-4.2 1.2-0.2 2.4-0.3 3.6-0.5 2.9-0.4 5.7-0.8 8.6-1.2 1.3-0.2 2.6-0.4 3.8-0.5 5.5-0.8 11-1.6 16.3-2.5 2.5-0.4 4.9-0.8 7.4-1.2 12.1-2 23.8-4.2 35-6.6 1.2-0.3 2.4-0.5 3.6-0.8 2.3-0.5 4.7-1 6.9-1.5 1.2-0.3 2.4-0.5 3.6-0.8 1.2-0.3 2.4-0.5 3.5-0.8 5.3-1.3 10.4-2.5 15.4-3.9 0.9-0.2 1.9-0.5 2.8-0.7 0.9-0.3 1.8-0.5 2.7-0.8l5.4-1.5c20.3-5.9 38-12.3 52.9-19.3 0.7-0.3 1.3-0.6 2-1v149.5zM154.3 258.9c1 0.5 2 1 3 1.4 19.9 9.3 44.8 17.6 74.4 24.9 75.2 18.5 174.8 28.6 280.3 28.6s205.1-10.2 280.3-28.6c31-7.6 56.8-16.4 77.1-26.2v133.5c-0.8 1.2-3.3 4.1-9 8.1l-0.9 0.6c-0.9 0.6-1.9 1.3-3 1.9-1.1 0.7-2.2 1.4-3.5 2.1-0.4 0.2-0.8 0.5-1.3 0.7-0.4 0.2-0.9 0.5-1.3 0.7-0.5 0.3-0.9 0.5-1.4 0.7-0.2 0.1-0.5 0.3-0.7 0.4-0.2 0.1-0.5 0.3-0.7 0.4-0.3 0.1-0.5 0.3-0.8 0.4-0.3 0.1-0.5 0.3-0.8 0.4-0.4 0.2-0.8 0.4-1.1 0.6-0.4 0.2-0.8 0.4-1.3 0.6-0.5 0.2-0.9 0.4-1.4 0.7-0.7 0.3-1.3 0.6-2.1 0.9-0.3 0.1-0.6 0.3-0.9 0.4-1.7 0.7-3.4 1.5-5.3 2.3l-1.5 0.6c-1.7 0.7-3.5 1.4-5.4 2.1-0.4 0.2-0.8 0.3-1.1 0.4-1.2 0.5-2.5 0.9-3.8 1.4l-0.9 0.3c-2.1 0.8-4.4 1.5-6.7 2.3-0.9 0.3-1.9 0.6-2.9 0.9-2.2 0.7-4.4 1.4-6.7 2.1-0.8 0.3-1.7 0.5-2.5 0.8-7.3 2.1-15.3 4.3-24.1 6.5-0.6 0.2-1.2 0.3-1.9 0.5-0.6 0.2-1.3 0.3-1.9 0.5-3.2 0.8-6.5 1.5-9.9 2.3-69.1 15.2-158.8 23.6-252.6 23.6s-183.5-8.4-252.6-23.6c-1.2-0.3-2.4-0.5-3.6-0.8-0.6-0.1-1.2-0.3-1.8-0.4-0.3-0.1-0.6-0.2-1-0.2-0.7-0.2-1.4-0.3-2.1-0.5-0.7-0.2-1.5-0.3-2.2-0.5-1.1-0.3-2.3-0.5-3.4-0.8-6.4-1.5-12.3-3.1-17.8-4.7-0.7-0.2-1.5-0.4-2.2-0.6-1.4-0.4-2.9-0.8-4.3-1.3-0.9-0.3-1.8-0.5-2.7-0.8l-3.9-1.2c-1.2-0.4-2.5-0.8-3.6-1.2-0.4-0.2-0.9-0.3-1.3-0.4-0.8-0.3-1.6-0.5-2.4-0.8-0.8-0.3-1.7-0.6-2.5-0.9-1.1-0.4-2.1-0.7-3.2-1.1-2.6-1-5.1-1.9-7.5-2.8-0.8-0.3-1.5-0.6-2.3-0.9-0.1-0.1-0.2-0.1-0.3-0.1-0.6-0.3-1.2-0.5-1.8-0.8-0.3-0.1-0.6-0.3-0.9-0.4-0.3-0.1-0.6-0.2-0.9-0.4-0.3-0.1-0.6-0.2-0.9-0.4-1.4-0.6-2.8-1.2-4.1-1.8-0.5-0.2-1-0.5-1.5-0.7-0.5-0.2-1-0.5-1.5-0.7-3.9-1.9-7.2-3.7-10.1-5.3-2.1-1.2-3.9-2.4-5.5-3.5-0.1-0.1-0.2-0.1-0.2-0.2-1.8-1.3-3.3-2.4-4.5-3.4-0.2-0.2-0.4-0.3-0.5-0.4-0.3-0.2-0.5-0.4-0.7-0.7l-0.6-0.6c-0.3-0.3-0.5-0.5-0.8-0.7-0.2-0.2-0.5-0.5-0.6-0.7-0.3-0.3-0.5-0.6-0.8-0.9-0.6-0.7-0.9-1.1-1-1.4V258.9z m11-77.8c12.8-8.5 39.5-21.3 94.1-33.4C328.5 132.4 418.2 124 512 124s183.5 8.4 252.6 23.7c54.5 12.1 81.3 24.9 94.1 33.4 5.2 3.4 8.1 6.2 9.6 7.9-5.3 6-27.1 24.3-103.7 41.3C695.5 245.6 605.8 254 512 254s-183.5-8.4-252.6-23.7C182.8 213.4 161 195 155.7 189c1.6-1.8 4.4-4.5 9.6-7.9z m-11 211.3c0.1 0.4 0.2 0.9 0.2 1.6h-0.2v-1.6zM869.5 834c-2.4 3.5-20 23.9-104.8 42.6-69.1 15.2-158.8 23.6-252.6 23.6s-183.5-8.4-252.6-23.6C174.6 858 157 837.5 154.5 834V684.5c2 1 4.1 1.9 6.2 2.9 0.6 0.3 1.1 0.5 1.7 0.7l2.1 0.9c0.8 0.3 1.5 0.6 2.3 1 0.7 0.3 1.3 0.6 2 0.8 0.7 0.3 1.4 0.6 2.1 0.8 3.6 1.4 7.3 2.9 11.2 4.3 0.7 0.3 1.5 0.5 2.2 0.8 3.5 1.2 7.1 2.5 10.9 3.7 1 0.3 2.1 0.7 3.1 1 1 0.3 2.1 0.7 3.1 1 4.4 1.3 8.9 2.6 13.5 3.9 1.8 0.5 3.7 1 5.5 1.5 0.9 0.3 1.9 0.5 2.8 0.7 0.9 0.2 1.9 0.5 2.8 0.7 1.9 0.5 3.8 1 5.7 1.4 75.2 18.4 174.8 28.5 280.3 28.5s205.1-10.1 280.3-28.5c1.9-0.5 3.9-1 5.7-1.4 0.9-0.2 1.9-0.5 2.8-0.7 0.9-0.2 1.9-0.5 2.8-0.7 1.9-0.5 3.7-1 5.5-1.5 4.6-1.3 9.1-2.6 13.5-3.9 1.1-0.3 2.1-0.6 3.1-1 1-0.3 2.1-0.7 3.1-1 3.7-1.2 7.4-2.4 10.9-3.7 0.7-0.3 1.5-0.5 2.2-0.8 3.9-1.4 7.6-2.8 11.2-4.3 0.7-0.3 1.4-0.6 2.1-0.8 0.7-0.3 1.3-0.5 2-0.8 0.8-0.3 1.5-0.6 2.3-1l2.1-0.9c0.6-0.2 1.1-0.5 1.7-0.7 2.1-0.9 4.2-1.9 6.2-2.9V834z\" fill=\"#727272\" ></path></symbol><symbol id=\"icon-quanxian\" viewBox=\"0 0 1024 1024\"><path d=\"M896 0a128 128 0 0 1 128 128v365.205333a42.666667 42.666667 0 1 1-85.333333 0V341.333333H85.333333v554.666667a42.666667 42.666667 0 0 0 39.466667 42.56L128 938.666667h334.976a42.666667 42.666667 0 1 1 0 85.333333H128a128 128 0 0 1-128-128V128a128 128 0 0 1 128-128z m-117.269333 490.666667c89.834667 0 136.576 59.968 138.645333 151.125333A128 128 0 0 1 1024 768v128a128 128 0 0 1-128 128H682.666667a128 128 0 0 1-128-128v-128a128.042667 128.042667 0 0 1 85.333333-120.725333c1.002667-95.786667 46.805333-156.608 138.730667-156.608zM896 725.333333H682.666667a42.666667 42.666667 0 0 0-42.666667 42.666667v128a42.666667 42.666667 0 0 0 42.666667 42.666667h213.333333a42.666667 42.666667 0 0 0 42.666667-42.666667v-128a42.666667 42.666667 0 0 0-42.666667-42.666667z m-542.485333-42.666666a42.666667 42.666667 0 1 1 0 85.333333H213.333333a42.666667 42.666667 0 1 1 0-85.333333z m425.216-106.666667c-36.650667 0-51.349333 16.810667-53.205334 64H832c-1.493333-46.016-16.96-64-53.248-64zM533.333333 469.333333a42.666667 42.666667 0 1 1 0 85.333334H213.333333a42.666667 42.666667 0 1 1 0-85.333334zM896 85.333333H128a42.666667 42.666667 0 0 0-42.666667 42.666667v128h853.333334V128a42.666667 42.666667 0 0 0-39.466667-42.56L896 85.333333z\" fill=\"#000000\" ></path></symbol><symbol id=\"icon-sharpicons_add-database\" viewBox=\"0 0 1024 1024\"><path d=\"M893.76 534.4A255.136 255.136 0 0 1 992 736c0 141.44-114.56 256-256 256-29.44 0-57.92-5.12-84.16-14.4a255.744 255.744 0 0 1-88-431.04 255.872 255.872 0 0 1 329.92-12.16z\" fill=\"#E0EBFE\" ></path><path d=\"M128 192a384 128 0 1 0 768 0 384 128 0 1 0-768 0Z\" fill=\"#E0EBFE\" ></path><path d=\"M928 521.92V192c0-105.024-209.28-160-416-160S96 86.976 96 192v672c0 105.024 209.28 160 416 160 50.592 0 98.432-3.008 142.176-8.896 1.888-0.256 3.392-1.248 5.12-1.792A283.52 283.52 0 0 0 736 1024c158.816 0 288-129.184 288-288a285.44 285.44 0 0 0-96-214.08zM512 96c227.744 0 352 63.392 352 96s-124.256 96-352 96S160 224.608 160 192s124.256-96 352-96zM160 281.056C237.632 327.776 375.2 352 512 352s274.368-24.224 352-70.944v197.184A285.184 285.184 0 0 0 736 448c-65.6 0-128.544 22.432-179.52 63.136-14.56 0.544-29.376 0.864-44.48 0.864-227.744 0-352-63.392-352-96V281.056z m0 224c74.816 45.024 205.28 69.024 337.12 70.72a288.16 288.16 0 0 0-49.024 158.432C258.848 723.552 160 668.8 160 640v-134.944zM512 960c-227.744 0-352-63.392-352-96v-135.008c68.544 41.12 182.624 64.096 295.52 69.632a289.248 289.248 0 0 0 99.328 160.064c-14.176 0.544-28.096 1.312-42.848 1.312z m224 0a224.928 224.928 0 0 1-222.336-196.288A224 224 0 0 1 736 512c50.368 0 98.112 16.48 138.048 47.616A222.304 222.304 0 0 1 960 736c0 123.52-100.48 224-224 224z\" fill=\"#5465CF\" ></path><path d=\"M864 704h-96v-96a32 32 0 1 0-64 0v96h-96a32 32 0 1 0 0 64h96v96a32 32 0 1 0 64 0v-96h96a32 32 0 1 0 0-64z\" fill=\"#5465CF\" ></path></symbol><symbol id=\"icon-zuzhiguanli-\" viewBox=\"0 0 1024 1024\"><path d=\"M896.1 636.65H801v-95a64.07 64.07 0 0 0-64-64H544v-96.53h95.9a64.07 64.07 0 0 0 64-64V160.79a64.07 64.07 0 0 0-64-64H384.37a64.07 64.07 0 0 0-64 64v156.33a64.07 64.07 0 0 0 64 64H480v96.57H288a64.07 64.07 0 0 0-64 64v95h-96a64.07 64.07 0 0 0-64 64v160.52a64.07 64.07 0 0 0 64 64h255.4a64.07 64.07 0 0 0 64-64V700.65a64.07 64.07 0 0 0-64-64h-95.48v-94.9l0.06-0.06h448.93l0.06 0.06v94.9H640.6a64.07 64.07 0 0 0-64 64v160.56a64.07 64.07 0 0 0 64 64h255.5a64.07 64.07 0 0 0 64-64V700.65a64.07 64.07 0 0 0-64-64zM384.37 317.07V160.85l0.06-0.06h255.38l0.06 0.06v156.22l-0.06 0.06H384.42z m-1 383.64v160.44l-0.06 0.06H128l-0.06-0.06V700.71l0.06-0.06h255.34zM896.1 861.15l-0.06 0.06H640.66l-0.06-0.06V700.71l0.06-0.06H896l0.06 0.06z\" fill=\"#666666\" ></path></symbol><symbol id=\"icon-moxing-miaobian\" viewBox=\"0 0 1024 1024\"><path d=\"M511.54 477.06l-1.11-0.04c-12.14-0.43-23.87-3.38-34.86-8.79L195.22 340.26c-18.8-8.13-33.57-23.46-41.03-42.92-7.33-19.13-6.66-39.92 1.91-58.56 7.99-17.36 22.12-31.09 39.79-38.67L462.34 77.78a118.24 118.24 0 0 1 98.1-0.04l267.35 122.69c18.75 8.1 33.52 23.43 40.98 42.9 7.33 19.12 6.66 39.91-1.91 58.54-7.99 17.37-22.12 31.11-39.8 38.68L546.6 468.53c-9.54 4.91-21.71 8.06-33.96 8.49l-1.1 0.04z m-0.08-347.53a55.96 55.96 0 0 0-23.16 5.03L221.21 257.17c-4.17 1.79-6.89 4.41-8.39 7.69-1.9 4.14-1.23 7.79-0.34 10.13 1.4 3.65 4.27 6.56 8.09 8.21l281.72 128.63c3.41 1.66 6.32 2.49 9.23 2.73 2.8-0.23 5.52-1 7.93-2.23l282.38-128.88c4.09-1.76 6.81-4.38 8.32-7.66 1.9-4.14 1.23-7.79 0.34-10.13-1.39-3.65-4.27-6.56-8.09-8.21L534.5 134.51a55.856 55.856 0 0 0-23.04-4.98zM426.88 956.94h-0.56c-12.63 0-25.19-3.16-36.32-9.14L127.1 824.59c-28.5-14.88-45.26-42.86-44.94-73.61V435.19c0.16-28.59 15.5-55.35 40.02-69.98 23.74-13.45 52.4-13.7 75.75-1.02l262.99 123.43c27.08 14.57 43.6 41.18 44.72 70.24l0.02 317.68c-0.32 28.97-15.12 54.95-39.6 69.84-11.94 7.11-25.21 11.05-38.63 11.53l-0.55 0.03z m-265.93-539.7c-2.56 0-5.12 0.64-7.41 1.94-5.34 3.19-8.92 9.38-8.96 16.19v315.91c-0.07 7.51 3.85 14.04 10.23 17.39l263.22 123.37c3.33 1.74 5.58 2.37 7.75 2.46 2.76-0.19 5.55-1.09 8.08-2.6 5.7-3.46 9.3-9.75 9.37-16.69V559.06c-0.24-5.9-4.25-12.39-10.4-15.71L168.34 419.17a14.99 14.99 0 0 0-7.39-1.93zM598.43 957.11h-0.77c-13.44-0.03-26.79-3.57-38.63-10.23-25.37-15.39-40.18-41.37-40.51-70.02V559.13c0.64-29.5 16.64-56.2 41.77-70.36l2.07-1.07 265.72-124.65c22.21-11.77 51.02-11.08 73.85 2.49 24.52 14.39 39.93 41.18 40.09 69.84v316.08c0.64 29.74-15.46 57.72-42.04 72.38l-267.26 125c-9.71 5.19-21.99 8.27-34.29 8.27z m-8.25-413.51c-5.58 3.5-9.09 9.62-9.25 16.22l0.01 316.68c0.08 6.59 3.67 12.89 9.6 16.5 1.67 0.91 4.51 1.68 7.32 1.68h0.43c2.14 0 4.43-0.56 6.56-1.69L871.7 768.22c4.32-2.46 8.06-8.99 7.91-16.1l-0.01-316.56c-0.04-6.58-3.64-12.87-9.39-16.26-4.38-2.58-9.83-2.73-14.31-0.39L590.18 543.6z\" fill=\"#666666\" ></path></symbol><symbol id=\"icon-xiajiantou1-copy\" viewBox=\"0 0 1024 1024\"><path d=\"M507.8 706.39466667a30.016 30.016 0 0 1-21.288-8.824L231.104 442.16266667a30.088 30.088 0 0 1 0-42.568 30.088 30.088 0 0 1 42.568 0l234.128 234.128 234.16-234.128a30.088 30.088 0 0 1 42.568 0 30.088 30.088 0 0 1 0 42.568L529.08 697.57066667a30 30 0 0 1-21.28 8.824z\" fill=\"#888888\" ></path></symbol><symbol id=\"icon-chakan2\" viewBox=\"0 0 1024 1024\"><path d=\"M793.56207445 694.04444445l-97.09037112-80.09955556c21.84533333-36.40888889 33.98163001-77.67229667 33.98163-123.79022222 0-133.49925888-109.22666667-242.72592555-242.72592554-242.72592555s-242.72592555 109.22666667-242.72592669 242.72592555 109.22666667 242.72592555 242.72592669 242.72592554c53.39970333 0 104.37214777-16.99081443 143.20829554-48.54518442l99.51763 82.52681443c9.70903666 9.70903666 26.69985223 7.28177778 33.98163001-2.42725888l31.55436999-36.40888889c9.70903666-9.70903666 7.28177778-24.27259221-2.42725888-33.98163zM487.72740779 638.2174811c-80.09955555 0-145.63555555-65.536-145.63555556-145.63555555s65.536-145.63555555 145.63555556-145.63555556 145.63555555 65.536 145.63555555 145.63555556-65.536 145.63555555-145.63555555 145.63555555zM172.18370333 342.09185223v-145.63555556c0-12.13629667 9.70903666-24.27259221 24.27259334-24.27259334h145.63555556c12.13629667 0 24.27259221-9.70903666 24.27259222-24.27259222V99.36592555c0-12.13629667-9.70903666-24.27259221-24.27259222-24.27259222h-169.9081489c-53.39970333 0-97.09036999 43.69066667-97.09037 97.09037v169.9081489c0 12.13629667 9.70903666 24.27259221 24.27259222 24.27259222h48.54518556c14.56355555 0 24.27259221-9.70903666 24.27259222-24.27259222zM681.90814777 172.18370333h145.63555556c12.13629667 0 24.27259221 9.70903666 24.27259334 24.27259334v145.63555556c0 12.13629667 9.70903666 24.27259221 24.27259222 24.27259222h48.54518556c12.13629667 0 24.27259221-9.70903666 24.27259222-24.27259222v-169.9081489c0-53.39970333-43.69066667-97.09036999-97.09037-97.09037h-169.9081489c-12.13629667 0-24.27259221 9.70903666-24.27259222 24.27259222v48.54518556c0 14.56355555 9.70903666 24.27259221 24.27259222 24.27259222z\" fill=\"#297AFF\" ></path><path d=\"M851.81629667 681.90814777v145.63555556c0 12.13629667-9.70903666 24.27259221-24.27259334 24.27259334h-145.63555556c-12.13629667 0-24.27259221 9.70903666-24.27259222 24.27259222v48.54518556c0 12.13629667 9.70903666 24.27259221 24.27259222 24.27259222h169.9081489c53.39970333 0 97.09036999-43.69066667 97.09037-97.09037v-169.9081489c0-12.13629667-9.70903666-24.27259221-24.27259222-24.27259222h-48.54518556c-14.56355555 0-24.27259221 9.70903666-24.27259222 24.27259222zM342.09185223 851.81629667h-145.63555556c-12.13629667 0-24.27259221-9.70903666-24.27259334-24.27259334v-145.63555556c0-12.13629667-9.70903666-24.27259221-24.27259222-24.27259222H99.36592555c-12.13629667 0-24.27259221 9.70903666-24.27259222 24.27259222v169.9081489c0 53.39970333 43.69066667 97.09036999 97.09037 97.09037h169.9081489c12.13629667 0 24.27259221-9.70903666 24.27259222-24.27259222v-48.54518556c0-14.56355555-9.70903666-24.27259221-24.27259222-24.27259222z\" fill=\"#297AFF\" ></path></symbol><symbol id=\"icon-clone\" viewBox=\"0 0 1024 1024\"><path d=\"M897.71428584 881.64285752V335.21428584q0-6.52901748-4.77120586-11.30022334T881.64285752 319.14285752H335.21428584q-6.52901748 0-11.30022334 4.77120498T319.14285752 335.21428584v546.42857168q0 6.52901748 4.77120498 11.30022246t11.30022334 4.77120586h546.42857168q6.52901748 0 11.30022246-4.77120586t4.77120586-11.30022246z m64.28571416-546.42857168v546.42857168q0 33.14732168-23.60491084 56.75223164t-56.75223164 23.60491084H335.21428584q-33.14732168 0-56.75223252-23.60491084t-23.60491084-56.75223164V335.21428584q0-33.14732168 23.60491084-56.75223252t56.75223252-23.60491084h546.42857168q33.14732168 0 56.75223164 23.60491084t23.60491084 56.75223252z m-192.85714248-192.85714336v80.35714336h-64.28571504V142.35714248q0-6.52901748-4.77120498-11.30022246T688.78571416 126.28571416H142.35714248q-6.52901748 0-11.30022246 4.77120586T126.28571416 142.35714248v546.42857168q0 6.52901748 4.77120586 11.30022334t11.30022246 4.77120498h80.35714336v64.28571504H142.35714248q-33.14732168 0-56.75223164-23.60491085T62 688.78571416V142.35714248Q62 109.20982168 85.60491084 85.60491084T142.35714248 62h546.42857168q33.14732168 0 56.75223251 23.60491084t23.60491085 56.75223164z\"  ></path></symbol><symbol id=\"icon-tijiao\" viewBox=\"0 0 1024 1024\"><path d=\"M443.76171875 872.3515625c0 16.56738281-12.22558594 30.41015625-28.09863281 32.96777344v0.43066406h-238.09570313C144.50292969 905.75 117.37109375 877.71289063 117.37109375 842.97851563V125.22851562C117.37109375 90.6875 144.3359375 62.45703125 177.55859375 62.45703125h667.99511719c33.08203125 0 60.1875 28.03710938 60.1875 62.77148438V455.22265625c0 17.63964844-14.43164063 32.08007813-32.08007813 32.08007813-17.63964844 0-32.08007813-14.43164063-32.08007812-32.08007813V297.6875c0-0.64160156 0.02636719-1.27441406 0.06152344-1.90722656l-1.39746094-167.44042969-655.62011719 1.74902344-2.0390625 709.91894531 65.84765625-0.29882813c2.26757813-0.4921875 4.62304688-0.75585938 7.03125-0.75585937h154.8984375c0.26367188 0 0.53613281 0.01757813 0.79980469 0.01757813l4.5-0.01757813v0.43066406c15.87304687 2.55761719 28.09863281 16.40039063 28.09863281 32.96777344zM766.00390625 262.390625c0-19.3359375-15.8203125-35.15625-35.15625-35.15625H294.91015625c-19.3359375 0-35.15625 15.8203125-35.15625 35.15625s15.8203125 35.15625 35.15625 35.15625h435.9375c19.3359375 0 35.15625-15.8203125 35.15625-35.15625zM294.2421875 411.75195312c-19.3359375 0-35.15625 15.8203125-35.15625 35.15625001s15.8203125 35.15625 35.15625 35.15625H508.484375c19.3359375 0 35.15625-15.8203125 35.15625-35.15625s-15.8203125-35.15625-35.15625-35.15625H294.2421875zM480.359375 631.53125c0-19.3359375-15.8203125-35.15625-35.15625-35.15625H293.8203125c-19.3359375 0-35.15625 15.8203125-35.15625 35.15625s15.8203125 35.15625 35.15625 35.15625H445.203125c19.3359375 0 35.15625-15.8203125 35.15625-35.15625z m427.09570313 64.04589844c-1.98632813 6.54785156-6.72363281 10.77539063-11.95312501 10.66992187h-85.69335937v216.50976563c0.140625 10.34472656-2.80371094 20.32910156-8.15625 27.71191406-5.35253906 7.3828125-12.67382813 11.53125-20.3203125 11.53125h-138.07617188c-11.55761719 0-20.96191406-12.5859375-21.10253906-28.23046875V706.93261719H525.78125c-5.35253906 0-10.13378906-4.54394531-11.953125-11.35546875-2.04785156-6.609375-0.71191406-14.26464844 3.3046875-18.93164063l194.01855469-238.20117187c4.91308594-6.33691406 12.63867188-6.33691406 17.54296875 0l175.95703125 237.84960937c3.62988281 5.14160156 4.71972656 12.66503906 2.80371093 19.28320313zM837.1953125 667.56640625L717.43554688 506.84082031 577.91796875 671.08203125h68.5546875c2.62792969-0.07910156 10.32714844 3.33105469 12.19921875 6.06445313 1.88085938 2.73339844 2.93554688 6.48632813 2.93554688 10.39746093V918.93359375H771.27734375v-36.9140625l-1.7578125-193.359375c0-2.63671875-2.21484375-10.96875 3.515625-16.69921875 3.515625-3.515625 11.43457031-3.59472656 14.0625-3.515625l50.09765625-0.87890625z\"  ></path></symbol><symbol id=\"icon-chakan1\" viewBox=\"0 0 1024 1024\"><path d=\"M968 926L872 830c18-30 30-66 30-102 0-102-84-186-186-186s-186 84-186 186c0 102 84 186 186 186 42 0 78-12 108-36l96 96c12 12 36 12 48 0 12-12 12-36 0-48z m-258-84c-66 0-114-54-114-114 0-66 54-114 114-114 66 0 114 54 114 114 6 66-48 114-114 114z\"  ></path><path d=\"M230 992c-48 0-150-30-150-132V218c0-120 78-180 150-180H752c90 0 150 60 150 156v216c0 18-12 42-36 42-30-6-42-18-42-42V206c0-66-60-90-84-90H230C200 116 152 152 152 230v600c0 90 60 90 84 90h180c24-6 36 18 36 36s-12 36-42 36h-180z\"  ></path><path d=\"M284 200H692c18 0 30 12 30 30s-12 30-30 30H284c-18 0-30-12-30-30s6-30 30-30zM284 374H692c18 0 30 12 30 30s-12 30-30 30H284c-18 0-30-12-30-30 0-12 6-30 30-30zM284 554h174c18 0 30 12 30 30s-18 30-30 30H284c-18 0-30-12-30-30s6-30 30-30z\"  ></path></symbol><symbol id=\"icon-fuzhi\" viewBox=\"0 0 1024 1024\"><path d=\"M720 192h-544A80.096 80.096 0 0 0 96 272v608C96 924.128 131.904 960 176 960h544c44.128 0 80-35.872 80-80v-608C800 227.904 764.128 192 720 192z m16 688c0 8.8-7.2 16-16 16h-544a16 16 0 0 1-16-16v-608a16 16 0 0 1 16-16h544a16 16 0 0 1 16 16v608z\"  ></path><path d=\"M848 64h-544a32 32 0 0 0 0 64h544a16 16 0 0 1 16 16v608a32 32 0 1 0 64 0v-608C928 99.904 892.128 64 848 64z\"  ></path><path d=\"M608 360H288a32 32 0 0 0 0 64h320a32 32 0 1 0 0-64zM608 520H288a32 32 0 1 0 0 64h320a32 32 0 1 0 0-64zM480 678.656H288a32 32 0 1 0 0 64h192a32 32 0 1 0 0-64z\"  ></path></symbol><symbol id=\"icon-icon_answer\" viewBox=\"0 0 1024 1024\"><path d=\"M512.001023 881.247762c-203.605188 0-369.248785-165.643598-369.248785-369.248785 0-203.602118 165.643598-369.245715 369.248785-369.245716 203.602118 0 369.245715 165.643598 369.245716 369.245716 0.001023 203.604164-165.643598 369.248785-369.245716 369.248785z m0-682.736593c-172.85893 0-313.489854 140.627854-313.489854 313.486784 0 172.860976 140.630924 313.489854 313.489854 313.489854s313.486784-140.627854 313.486784-313.489854c0-172.857906-140.627854-313.486784-313.486784-313.486784z\" fill=\"#272636\" ></path><path d=\"M673.77857 688.595021c-10.645454 0-20.812001-6.131651-25.450648-16.47216L512.899486 370.270045 377.471051 672.123884c-6.305613 14.048971-22.796192 20.348444-36.85028 14.021342-14.048971-6.300496-20.326954-22.799262-14.024411-36.848233l160.867827-358.549096a27.871791 27.871791 0 0 1 25.434276-16.46602c10.983145 0 20.941961 6.446829 25.434275 16.46602l160.862711 358.549096c6.305613 14.048971 0.027629 30.547737-14.021341 36.848233a27.734668 27.734668 0 0 1-11.395538 2.449795z\" fill=\"#272636\" ></path><path d=\"M622.413716 574.139246H403.382187c-15.396665 0-27.879977-12.483312-27.879977-27.879978s12.483312-27.879977 27.879977-27.879977h219.031529c15.398712 0 27.879977 12.483312 27.879977 27.879977s-12.480242 27.879977-27.879977 27.879978z\" fill=\"#272636\" ></path></symbol><symbol id=\"icon-icon_question\" viewBox=\"0 0 1024 1024\"><path d=\"M512 142.748076c-203.633457 0-369.200759 165.669631-369.200759 369.20076 0 203.633457 165.669631 369.200759 369.200759 369.200759 203.633457 0 369.200759-165.669631 369.200759-369.200759 0.102328-203.531128-165.567303-369.200759-369.200759-369.20076z m0 682.734886C339.167383 825.482962 198.568202 684.883781 198.568202 511.948836 198.568202 339.116219 339.167383 198.517038 512 198.517038s313.431798 140.599181 313.431798 313.431798c0.102328 172.934946-140.599181 313.534126-313.431798 313.534126z\" fill=\"#272636\" ></path><path d=\"M512 287.338063c-111.333267 0-186.237634 85.853503-186.237634 213.661637 0 125.761567 76.541621 213.661637 186.237634 213.661637 17.395823 0 33.973019-2.251224 49.62926-6.446687 2.353553 1.534926 4.911762 2.762866 7.674627 3.683821l67.229739 21.386629c15.963226 5.116419 33.052064-3.274508 38.782453-19.033076l2.660537-7.265315c6.549016-17.907465-4.40012-37.554512-23.126211-41.340661l-15.451584-3.17218c37.04287-37.963825 58.838813-94.244429 58.838813-161.474168 0-129.854702-73.062456-213.661637-186.237634-213.661637zM381.531328 500.897372c0-72.8578 34.177676-157.892675 130.468672-157.892675 121.054462 0 130.468672 120.849805 130.468672 157.892675 0 78.588188-40.317378 157.892675-130.468672 157.892675-90.048966 0-130.468672-79.304487-130.468672-157.892675z\" fill=\"#272636\" ></path></symbol><symbol id=\"icon-fasong\" viewBox=\"0 0 1024 1024\"><path d=\"M510.3616 90.4704c-232.0896 0-420.864 188.8256-420.864 420.864 0 232.0896 188.8256 420.864 420.864 420.864s420.864-188.8256 420.864-420.864-188.7744-420.864-420.864-420.864z m0 800.8192c-209.5104 0-379.904-170.4448-379.904-379.904 0-209.5104 170.4448-379.904 379.904-379.904 209.5104 0 379.904 170.4448 379.904 379.904 0 209.4592-170.4448 379.904-379.904 379.904z\" fill=\"#00A0E9\" ></path><path d=\"M259.4816 439.8592l422.5024-103.5776c20.4288-5.0176 37.5296 15.9744 28.4672 34.9696L522.24 766.4128c-9.728 20.3776-39.3728 18.432-46.2848-3.072l-48.0256-149.4016a25.06752 25.06752 0 0 1 5.2224-24.3712l82.1248-91.0336c5.0176-5.5808-1.6896-13.9264-8.192-10.0864l-108.9536 63.488a24.9856 24.9856 0 0 1-24.6272 0.3072L253.3888 485.9392c-19.9168-11.008-15.9744-40.704 6.0928-46.08z\" fill=\"#00A0E9\" ></path></symbol><symbol id=\"icon-zhongqi\" viewBox=\"0 0 1024 1024\"><path d=\"M512 505.6c19.2 0 38.4-19.2 38.4-38.4v-428.8c0-19.2-19.2-38.4-38.4-38.4s-38.4 19.2-38.4 38.4v428.8c0 19.2 12.8 38.4 38.4 38.4zM710.4 128c6.4 0 12.8 6.4 19.2 6.4 19.2 12.8 44.8 6.4 57.6-12.8 12.8-19.2 6.4-44.8-12.8-57.6-6.4-6.4-12.8-6.4-19.2-12.8-19.2-12.8-44.8 0-51.2 19.2-19.2 25.6-12.8 44.8 6.4 57.6z\" fill=\"#666666\" ></path><path d=\"M934.4 224c-12.8-19.2-38.4-25.6-57.6-12.8-6.4 6.4-12.8 12.8-19.2 25.6 0 12.8 0 19.2 6.4 32 44.8 70.4 70.4 153.6 70.4 236.8a428.8 428.8 0 0 1-857.6 0c0-160 89.6-307.2 230.4-384 19.2-12.8 25.6-32 19.2-51.2-12.8-19.2-32-25.6-51.2-19.2-172.8 89.6-275.2 262.4-275.2 454.4 0 281.6 230.4 512 512 512s512-230.4 512-512c0-96-32-198.4-89.6-281.6z\" fill=\"#666666\" ></path></symbol><symbol id=\"icon-tixing2\" viewBox=\"0 0 1024 1024\"><path d=\"M925.007405 706.493669c-9.007141-8.121981-23.219841-18.370393-42.243103-30.456662-5.865591-3.725858-9.318226-10.111289-9.238408-17.07898l0.031722-2.539846c0.60989-51.78134 1.529843-130.034589-12.19167-209.543434-16.686031-96.679923-51.42216-172.286901-103.242386-224.720088-40.829917-41.314964-92.434226-68.674079-154.064888-81.818447-3.056616-19.094894-12.19781-36.341696-26.516934-49.569976-17.135262-15.8285-40.165792-24.546046-64.846914-24.546046-24.679076 0-47.709605 8.718569-64.844867 24.546046-14.31503 13.222139-23.455201 30.463826-26.514887 49.552579-61.343113 13.07069-112.581078 40.223097-152.97302 81.204463-117.15014 118.866225-114.199948 322.050834-112.614847 431.207929l0.085958 5.973038c0.099261 6.963599-3.508917 13.56597-9.417487 17.231453-19.403932 12.038174-33.983999 22.151509-43.334971 30.05655-24.833595 20.99415-39.076994 51.64831-39.076994 84.102467 0 28.354791 23.067368 51.42216 51.42216 51.42216l277.239232 0c0.540306 65.73412 54.174853 119.045303 120.032794 119.045303s119.492488-53.312206 120.032794-119.045303l275.389094 0c29.375028 0 53.272297-23.898293 53.272297-53.272297C961.390034 757.14221 948.130032 727.34558 925.007405 706.493669zM512.693802 106.235556c14.58723 0 27.973098 4.945639 37.694507 13.925151 4.253884 3.929496 7.624654 8.439206 10.070356 13.418614-15.392572-1.550309-31.31317-2.334161-47.76384-2.334161-16.395412 0-32.256658 0.775666-47.586808 2.308579C473.319026 117.106138 491.085668 106.235556 512.693802 106.235556zM512.695848 920.54579c-43.792389 0-79.464843-35.363416-80.005149-79.029938l160.010297 0C592.160691 885.183398 556.488237 920.54579 512.695848 920.54579zM908.117736 801.500487 115.422799 801.500487c-6.28924 0-11.406794-5.117554-11.406794-11.406794 0-20.652366 9.073656-40.169885 24.896017-53.543474 7.752568-6.553253 21.099551-15.755846 38.59911-26.614148 17.772782-11.026124 28.628014-30.875195 28.331255-51.798736l-0.085958-5.986341c-1.500167-103.306854-4.29277-295.599392 101.104699-402.537959 51.240011-51.990095 123.857912-78.352509 215.832673-78.352509 92.148723 0 165.147294 26.586518 216.96752 79.022775 45.958728 46.502104 77.003791 114.936729 92.27152 203.39848 13.098319 75.896574 12.20395 151.944598 11.611455 202.266703l-0.029676 2.541893c-0.246617 20.922519 10.143011 40.107463 27.78788 51.321876 16.656355 10.584056 29.764907 19.958564 36.906561 26.39823 14.722306 13.276375 23.165606 32.242332 23.165606 52.033074C921.375691 795.553031 915.427212 801.500487 908.117736 801.500487z\" fill=\"#272636\" ></path><path d=\"M450.672236 213.276455c-24.200168 0-58.362222 22.532179-82.263585 43.620473-19.808138 17.477047-54.381561 53.168943-63.362096 95.835695-2.275833 10.812253 4.644787 21.423939 15.45704 23.698748 1.38965 0.292666 2.774183 0.433882 4.140297 0.433882 9.260921 0 17.575284-6.467295 19.558451-15.890922 4.823865-22.910802 23.295566-49.909713 50.681286-74.070996 26.590612-23.462365 49.811476-33.610492 55.788607-33.610492 11.048637 0 20.007683-8.958022 20.007683-20.007683C470.679919 222.234477 461.720873 213.276455 450.672236 213.276455z\" fill=\"#272636\" ></path></symbol><symbol id=\"icon-tixing3\" viewBox=\"0 0 1024 1024\"><path d=\"M616.2 887.4H385.5c-20.5 0-37.3 16.8-37.3 37.3S365 962 385.5 962h230.7c20.5 0 37.3-16.8 37.3-37.3s-16.7-37.3-37.3-37.3zM895 794.1h-23.9V511c0-198.8-141.6-361.5-315.3-363.5v-45.8c0-21.4-17.5-39-39-39-21.4 0-39 17.5-39 39v45.6h-1.7C300.9 147.3 157.5 311 157.5 511v283.2h-23.9c-12.9 0-23.4 10.5-23.4 23.4 0 12.9 10.5 23.4 23.4 23.4H895c12.9 0 23.4-10.5 23.4-23.4 0-13-10.5-23.5-23.4-23.5zM730.8 441c0-58.5-16.3-101.7-48.4-128.6-54.7-45.7-138-31.2-138.9-31.1l-7.1-39.2c4.1-0.8 102.7-17.8 171.5 39.7 41.6 34.8 62.7 88.3 62.7 159.2h-39.8z\" fill=\"#ED7C6C\" ></path></symbol><symbol id=\"icon-tixing1\" viewBox=\"0 0 1024 1024\"><path d=\"M808 712l-48-108.8V448c0-102.4-65.6-195.2-160-232-3.2-44.8-41.6-80-88-80s-83.2 35.2-88 80c-96 36.8-160 129.6-160 232v155.2l-48 108.8c-6.4 14.4-4.8 30.4 3.2 43.2 8 12.8 22.4 20.8 38.4 20.8h508.8c16 0 30.4-8 38.4-20.8s9.6-30.4 3.2-43.2z m-547.2 16l51.2-115.2V448c0-88 59.2-166.4 144-192l19.2-6.4-3.2-20.8V224c0-22.4 17.6-40 40-40s40 17.6 40 40v6.4l-3.2 20.8 19.2 6.4c84.8 25.6 144 104 144 192v164.8l51.2 115.2H260.8zM512 880c44.8 0 80-35.2 80-80H432c0 44.8 35.2 80 80 80z\" fill=\"#272636\" ></path></symbol><symbol id=\"icon-shengji\" viewBox=\"0 0 1024 1024\"><path d=\"M475.73333333 798.93333333c0.42666667 0.64 0.96 1.28 1.49333334 1.92 3.2 3.84 20.16 13.22666667 49.92 15.89333334 19.30666667 1.70666667 68.37333333 2.13333333 106.24-30.29333334 45.65333333-39.14666667 62.18666667-89.28 50.56-153.17333333l-2.13333334-11.73333333 8.53333334-8.32C841.06666667 466.56 816.85333333 281.06666667 801.38666667 213.01333333c-21.54666667-5.33333333-71.46666667-15.25333333-133.54666667-11.09333333-104 6.82666667-193.6 48-266.13333333 122.45333333l-8.32 8.53333334-11.73333334-2.13333334c-63.89333333-11.62666667-114.02666667 4.90666667-153.17333333 50.56-32.53333333 37.86666667-32 86.93333333-30.29333333 106.24 2.66666667 29.86666667 12.05333333 46.82666667 15.89333333 49.92 0.74666667 0.64 1.38666667 1.06666667 1.92 1.49333334l0.53333333-1.38666667 89.28-57.81333333-9.70666666 51.30666666c-5.54666667 29.44-10.34666667 66.02666667-9.38666667 78.93333334l118.82666667 118.82666666c11.41333333 1.38666667 45.86666667-3.09333333 77.44-10.13333333l53.33333333-11.84-59.09333333 91.30666667-1.49333334 0.74666666z\" fill=\"#D6292C\" ></path><path d=\"M542.18666667 862.18666667c-6.29333333 0-12.58666667-0.32-18.98666667-0.85333334-36.69333333-3.2-66.88-15.25333333-80.64-32.10666666-14.82666667-18.13333333-15.04-33.38666667-12.58666667-42.98666667 1.49333333-5.76 4.05333333-10.45333333 7.04-14.18666667-33.49333333 3.94666667-50.24 1.49333333-60.26666666-8.64L251.30666667 638.08c-8.42666667-8.42666667-12.05333333-20.58666667-7.36-61.33333333-3.94666667 3.41333333-8.96 6.4-15.36 8-9.6 2.45333333-24.74666667 2.24-42.98666667-12.58666667-16.85333333-13.76-28.90666667-43.94666667-32.10666667-80.64-4.69333333-52.8 10.24-103.57333333 40.85333334-139.30666667 47.25333333-55.14666667 108.90666667-77.76 183.46666666-67.41333333C456 208.42666667 554.98666667 164.37333333 664.85333333 157.22666667c93.01333333-6.08 161.38666667 16.21333333 162.13333334 16.53333333l11.62666666 3.84 3.41333334 13.65333333c13.97333333 53.86666667 18.88 112.10666667 14.08 168.53333334-4.16 49.38666667-15.68 96.64-34.34666667 140.37333333-21.44 50.24-52.26666667 96.21333333-91.84 136.74666667 10.34666667 74.56-12.26666667 136.21333333-67.41333333 183.46666666-31.25333333 26.98666667-74.45333333 41.81333333-120.32 41.81333334zM475.73333333 798.93333333c0.42666667 0.64 0.96 1.28 1.49333334 1.92 3.2 3.84 20.16 13.22666667 49.92 15.89333334 19.30666667 1.70666667 68.37333333 2.13333333 106.24-30.29333334 45.65333333-39.14666667 62.18666667-89.28 50.56-153.17333333l-2.13333334-11.73333333 8.53333334-8.32C841.06666667 466.56 816.85333333 281.06666667 801.38666667 213.01333333c-21.54666667-5.33333333-71.46666667-15.25333333-133.54666667-11.09333333-104 6.82666667-193.6 48-266.13333333 122.45333333l-8.32 8.53333334-11.73333334-2.13333334c-63.89333333-11.62666667-114.02666667 4.90666667-153.17333333 50.56-32.53333333 37.86666667-32 86.93333333-30.29333333 106.24 2.66666667 29.86666667 12.05333333 46.82666667 15.89333333 49.92 0.74666667 0.64 1.38666667 1.06666667 1.92 1.49333334l0.53333333-1.38666667 89.28-57.81333333-9.70666666 51.30666666c-5.54666667 29.44-10.34666667 66.02666667-9.38666667 78.93333334l118.82666667 118.82666666c11.41333333 1.38666667 45.86666667-3.09333333 77.44-10.13333333l53.33333333-11.84-59.09333333 91.30666667-1.49333334 0.74666666z\" fill=\"#E67F80\" ></path><path d=\"M98.88 794.13333333c-5.76 0-11.41333333-2.13333333-15.78666667-6.50666666-8.74666667-8.74666667-8.74666667-22.93333333 0-31.68l95.89333334-95.89333334c8.74666667-8.74666667 22.93333333-8.74666667 31.68 0s8.74666667 22.93333333 0 31.68l-95.89333334 95.89333334c-4.48 4.37333333-10.24 6.50666667-15.89333333 6.50666666zM147.41333333 888.64c-5.76 0-11.41333333-2.13333333-15.78666666-6.50666667-8.74666667-8.74666667-8.74666667-22.93333333 0-31.68l118.82666666-118.82666666c8.74666667-8.74666667 22.93333333-8.74666667 31.68 0s8.74666667 22.93333333 0 31.68L163.30666667 882.02666667c-4.37333333 4.37333333-10.13333333 6.61333333-15.89333334 6.61333333zM271.68 907.52c-5.76 0-11.41333333-2.13333333-15.78666667-6.50666667-8.74666667-8.74666667-8.74666667-22.93333333 0-31.68l66.13333334-66.13333333c8.74666667-8.74666667 22.93333333-8.74666667 31.68 0s8.74666667 22.93333333 0 31.68l-66.13333334 66.13333333c-4.48 4.37333333-10.24 6.50666667-15.89333333 6.50666667z\" fill=\"#D6292C\" ></path><path d=\"M617.38666667 517.86666667c-32.10666667 0-62.4-12.48-85.12-35.30666667-22.72-22.72-35.30666667-53.01333333-35.30666667-85.12s12.48-62.4 35.30666667-85.12c22.72-22.72 53.01333333-35.30666667 85.12-35.30666667s62.4 12.48 85.12 35.30666667c22.72 22.72 35.30666667 53.01333333 35.30666666 85.12s-12.48 62.4-35.30666666 85.12c-22.72 22.82666667-52.90666667 35.30666667-85.12 35.30666667z m0-196.05333334c-20.16 0-39.14666667 7.89333333-53.44 22.18666667-14.29333333 14.29333333-22.18666667 33.28-22.18666667 53.44s7.89333333 39.14666667 22.18666667 53.44c14.29333333 14.29333333 33.28 22.18666667 53.44 22.18666667s39.14666667-7.89333333 53.44-22.18666667c14.29333333-14.29333333 22.18666667-33.28 22.18666666-53.44s-7.89333333-39.14666667-22.18666666-53.44-33.17333333-22.18666667-53.44-22.18666667z\" fill=\"#FFFFFF\" ></path><path d=\"M229.33333333 227.30666667c-5.76 0-11.41333333-2.13333333-15.78666666-6.50666667-8.74666667-8.74666667-8.74666667-22.93333333 0-31.68l95.89333333-95.89333333c8.74666667-8.74666667 22.93333333-8.74666667 31.68 0s8.74666667 22.93333333 0 31.68L245.22666667 220.8c-4.37333333 4.26666667-10.13333333 6.50666667-15.89333334 6.50666667z\" fill=\"#D6292C\" ></path><path d=\"M835.52 646.82666667c-5.76 0-11.41333333-2.13333333-15.78666667-6.50666667-8.74666667-8.74666667-8.74666667-22.93333333 0-31.68l95.89333334-95.89333333c8.74666667-8.74666667 22.93333333-8.74666667 31.68 0s8.74666667 22.93333333 0 31.68l-95.89333334 95.89333333c-4.48 4.37333333-10.34666667 6.50666667-15.89333333 6.50666667z\" fill=\"#D6292C\" ></path><path d=\"M803.73333333 904.10666667c-5.76 0-11.41333333-2.13333333-15.78666666-6.50666667-8.74666667-8.74666667-8.74666667-22.93333333 0-31.68l95.89333333-95.89333333c8.74666667-8.74666667 22.93333333-8.74666667 31.68 0s8.74666667 22.93333333 0 31.68l-95.89333333 95.89333333c-4.37333333 4.37333333-10.24 6.50666667-15.89333334 6.50666667z\" fill=\"#D6292C\" ></path></symbol><symbol id=\"icon-quanju_shengji\" viewBox=\"0 0 1024 1024\"><path d=\"M670.464 618.88a32.256 32.256 0 0 1-22.72-9.408L511.488 472 374.08 608.256a32 32 0 0 1-45.056-45.504l160.128-158.72a31.936 31.936 0 0 1 45.248 0.192l158.784 160.128a32.128 32.128 0 0 1-0.192 45.312 32.128 32.128 0 0 1-22.528 9.216z\" fill=\"#231815\" ></path><path d=\"M510.976 968.256a32 32 0 0 1-32-32V481.408a32 32 0 0 1 64 0v454.848a32 32 0 0 1-32 32z\" fill=\"#231815\" ></path><path d=\"M807.872 759.488h-115.904a32 32 0 0 1 0-64h115.904A152.192 152.192 0 0 0 960 543.488a152.32 152.32 0 0 0-152.128-152.192H721.28l4.8-44.416c0.576-4.224 1.28-8.448 1.28-12.864a214.528 214.528 0 0 0-214.208-214.272A214.528 214.528 0 0 0 298.88 334.016c0 4.416 0.704 8.64 1.28 12.864l4.8 44.416H216.064A152.32 152.32 0 0 0 64 543.488a152.192 152.192 0 0 0 152.064 152h115.904a32 32 0 0 1 0 64H216.064A216.256 216.256 0 0 1 0 543.488a216.32 216.32 0 0 1 216.064-216.192h18.88A278.592 278.592 0 0 1 513.088 55.744a278.528 278.528 0 0 1 278.144 271.552h16.576A216.384 216.384 0 0 1 1024 543.488a216.32 216.32 0 0 1-216.128 216z\" fill=\"#231815\" ></path></symbol><symbol id=\"icon-guanyuwomen1\" viewBox=\"0 0 1024 1024\"><path d=\"M511.43111111 830.80533333c-23.66577778 0-42.89422222-19.22844445-42.89422222-42.89422222V409.6c0-23.66577778 19.22844445-42.89422222 42.89422222-42.89422222s42.89422222 19.22844445 42.89422222 42.89422222v378.31111111c0 23.66577778-19.22844445 42.89422222-42.89422222 42.89422222z\" fill=\"#223D60\" ></path><path d=\"M511.43111111 269.42577778m-53.248 0a53.248 53.248 0 1 0 106.496 0 53.248 53.248 0 1 0-106.496 0Z\" fill=\"#223D60\" ></path><path d=\"M511.43111111 1024C230.62755555 1024 2.27555555 795.648 2.27555555 514.84444445S230.62755555 5.68888889 511.43111111 5.68888889 1020.58666667 234.04088889 1020.58666667 514.84444445 792.23466667 1024 511.43111111 1024z m0-956.98488889c-246.89777778 0-447.82933333 200.93155555-447.82933333 447.82933334s200.93155555 447.82933333 447.82933333 447.82933333 447.82933333-200.93155555 447.82933334-447.82933333-200.93155555-447.82933333-447.82933334-447.82933334z\" fill=\"#223D60\" ></path></symbol><symbol id=\"icon-icobanbengengxin\" viewBox=\"0 0 1024 1024\"><path d=\"M512 55.25732C260.169881 55.25732 55.308484 260.118717 55.308484 511.948836s204.861397 456.691516 456.691516 456.691516 456.691516-204.861397 456.691516-456.691516S763.830119 55.25732 512 55.25732z m0 820.264215C311.538723 875.521535 148.427301 712.410113 148.427301 511.948836S311.538723 148.376137 512 148.376137s363.572699 163.111422 363.572699 363.572699-163.111422 363.572699-363.572699 363.572699z\" fill=\"\" ></path><path d=\"M548.838213 302.687319c-8.493255-8.493255-19.647047-12.688718-31.107824-13.098032-1.22794-0.204657-2.455881-0.102328-3.683822-0.204656-1.22794 0.102328-2.455881 0-3.683821 0.204656-11.460777 0.409313-22.61457 4.604777-31.107824 13.098032L320.134306 461.705606c-18.21445 18.21445-18.21445 47.68502 0 65.797142 18.21445 18.21445 47.582692 18.21445 65.797142 0l79.509144-79.509144v240.062357c0 25.684421 20.874988 46.559408 46.559408 46.559408 0.716299 0 1.330269-0.204657 1.944239-0.204656 0.61397 0 1.22794 0.204657 1.944239 0.204656 25.684421 0 46.559408-20.874988 46.559408-46.559408V447.891276l79.509144 79.509144c18.21445 18.21445 47.582692 18.21445 65.797142 0 18.21445-18.112122 18.21445-47.582692 0-65.797142L548.838213 302.687319z\" fill=\"\" ></path></symbol><symbol id=\"icon-duihuaqipao\" viewBox=\"0 0 1024 1024\"><path d=\"M781.69183326 208.58123803H242.36749291c-9.3092649 0-17.22930884 3.29754663-23.84417772 9.88769507-6.57531762 6.59509253-9.87780761 14.52502465-9.87780761 23.79473901v370.83801222c0 9.37847924 3.30743408 17.30841065 9.87780761 23.90350389 6.61486816 6.59014916 14.53491211 9.88769508 23.84417772 9.88769508h101.13629151v105.07159448l146.9311521-105.07159448h291.27667236c9.31420898 0 17.23425293-3.29754663 23.85900855-9.88769508 6.57037354-6.59509253 9.87780761-14.52502465 9.87780763-23.8985598V242.26367211c0-9.27465844-3.31237817-17.20458984-9.87780763-23.79968309-6.62475562-6.59014916-14.54479957-9.88769508-23.85900855-9.88769508h-0.01977562zM242.34771729 141.21142578h539.32434106c27.89318824 0 51.74725342 9.78881836 71.50286865 29.56420922 19.74572778 19.77539086 29.61364722 43.57507324 29.61364722 71.48803711v370.83801222c0 27.91790796-9.86792016 51.81646704-29.61364722 71.49298119-19.75561523 19.77539086-43.60968041 29.66308594-71.50286865 29.66308594h-269.68688989L276.049927 882.78857422v-168.52587867h-33.70220971c-27.91296386 0-51.74725342-9.88769508-71.50286866-29.66308594C151.08923339 664.9181521 141.21142578 641.0146482 141.21142578 613.10168433V242.26367211C141.21142578 214.34576416 151.08923339 190.55102516 170.84484863 170.775635 190.60046386 151.00024414 214.43475342 141.21142578 242.34771729 141.21142578z\"  ></path></symbol><symbol id=\"icon-jiaosequanxian\" viewBox=\"0 0 1024 1024\"><path d=\"M512 1024a512 512 0 1 1 512-512 512 512 0 0 1-512 512zM512 128a384 384 0 1 0 384 384 384 384 0 0 0-384-384z m210.56 429.248L640 474.496l-19.2 19.2A187.328 187.328 0 0 1 640 576a193.472 193.472 0 1 1-109.44-172.8l128-128.448 90.88 90.496-19.2 18.752 83.2 82.752zM448 512a64 64 0 1 0 64 64 64 64 0 0 0-64-64z\"  ></path></symbol><symbol id=\"icon-preview1\" viewBox=\"0 0 1024 1024\"><path d=\"M512 856A455.04 455.04 0 0 1 78.72 542.24a97.76 97.76 0 0 1 0-60.48 456 456 0 0 1 866.48 0 97.76 97.76 0 0 1 0 60.48A455.04 455.04 0 0 1 512 856z m0-624a391.12 391.12 0 0 0-372.4 269.68 33.28 33.28 0 0 0 0 20.64 392 392 0 0 0 744.88 0 33.28 33.28 0 0 0 0-20.64A391.12 391.12 0 0 0 512 232z\" fill=\"#3E3A39\" ></path><path d=\"M512 680a168 168 0 0 1 0-336 32 32 0 0 1 0 64 104 104 0 1 0 104 104 102.64 102.64 0 0 0-12.24-48.96 32 32 0 0 1 56.48-30.16A168 168 0 0 1 512 680z\" fill=\"#3E3A39\" ></path><path d=\"M512 512m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z\" fill=\"#3E3A39\" ></path></symbol><symbol id=\"icon-daoru\" viewBox=\"0 0 1024 1024\"><path d=\"M494.933333 128m42.666667 0l0 0q42.666667 0 42.666667 42.666667l0 426.666666q0 42.666667-42.666667 42.666667l0 0q-42.666667 0-42.666667-42.666667l0-426.666666q0-42.666667 42.666667-42.666667Z\" fill=\"#666666\" ></path><path d=\"M836.266667 341.333333h-85.333334v85.333334h85.333334v341.333333h-597.333334v-341.333333h85.333334V341.333333h-85.333334a85.333333 85.333333 0 0 0-85.333333 85.333334v341.333333a85.333333 85.333333 0 0 0 85.333333 85.333333h597.333334a85.333333 85.333333 0 0 0 85.333333-85.333333v-341.333333a85.333333 85.333333 0 0 0-85.333333-85.333334z\" fill=\"#666666\" ></path><path d=\"M561.493333 701.44l131.413334-131.413333a34.133333 34.133333 0 0 0-24.32-58.026667H406.613333A34.133333 34.133333 0 0 0 384 570.026667l128 131.413333a33.706667 33.706667 0 0 0 49.493333 0z\" fill=\"#666666\" ></path></symbol><symbol id=\"icon-zhongzhi\" viewBox=\"0 0 1024 1024\"><path d=\"M512 928A416 416 0 1 1 928 512 416 416 0 0 1 512 928z m0-768A352 352 0 1 0 864 512 352 352 0 0 0 512 160z m96 512h-192a64 64 0 0 1-64-64v-192a64 64 0 0 1 64-64h192a64 64 0 0 1 64 64v192a64 64 0 0 1-64 64z\" fill=\"#333333\" ></path></symbol><symbol id=\"icon-tuichu\" viewBox=\"0 0 1024 1024\"><path d=\"M512 938.666667C276.352 938.666667 85.333333 747.648 85.333333 512S276.352 85.333333 512 85.333333a426.026667 426.026667 0 0 1 284.032 108.202667 41.813333 41.813333 0 0 1-38.314667 71.68A56.746667 56.746667 0 0 1 737.706667 256c-32.341333-24.192-47.530667-36.608-73.514667-49.578667a341.333333 341.333333 0 1 0 6.613333 607.829334c12.330667-6.528 51.626667-33.962667 87.552-54.101334a39.168 39.168 0 0 1 46.293334 62.421334A425.770667 425.770667 0 0 1 512 938.666667z m298.666667-282.624V554.666667h-298.666667a42.666667 42.666667 0 0 1 0-85.333334h298.666667V367.957333a12.8 12.8 0 0 1 20.778666-9.984l171.733334 137.386667a21.333333 21.333333 0 0 1 0 33.28l-171.733334 137.386667a12.8 12.8 0 0 1-20.778666-9.984z\" fill=\"#5D6E7F\" ></path></symbol><symbol id=\"icon-kongzhuangzhongduan\" viewBox=\"0 0 1024 1024\"><path d=\"M19.504762 78.019048v682.666666h984.990476v-682.666666H19.504762z m887.466667 585.142857H117.028571v-487.619048h789.942858v487.619048zM204.8 848.457143h619.27619v97.523809H204.8z\"  ></path><path d=\"M287.695238 609.52381l190.171429-190.171429-190.171429-190.171429-68.266667 68.266667L341.333333 419.352381l-121.904762 121.904762zM536.380952 463.238095h268.190477v97.52381H536.380952z\"  ></path></symbol><symbol id=\"icon-chexiao1\" viewBox=\"0 0 1024 1024\"><path d=\"M289.6384 256H614.4a307.2 307.2 0 1 1 0 614.4H204.8a51.2 51.2 0 0 1 0-102.4h409.6a204.8 204.8 0 1 0 0-409.6H286.0032l59.2384 59.2384A51.2 51.2 0 1 1 272.7936 489.984L128 345.2416a51.2 51.2 0 0 1 0-72.448L272.7936 128a51.2 51.2 0 0 1 72.448 72.3968L289.6384 256z\" fill=\"#666666\" ></path></symbol><symbol id=\"icon-xiangshang\" viewBox=\"0 0 1024 1024\"><path d=\"M470.016 976.896q-44.032 0-59.392-20.48t-15.36-65.536q0-20.48-0.512-64.512t-1.024-93.696-1.536-96.768-1.024-74.752q0-39.936-7.68-62.464t-35.328-21.504q-20.48 0-48.64-1.024t-49.664 0q-35.84 0-45.568-19.456t13.824-50.176q24.576-30.72 57.344-72.704t67.584-86.016 68.096-87.04 58.88-75.776q23.552-29.696 45.568-30.72t46.592 26.624q24.576 29.696 56.832 69.632t67.072 82.432 68.608 83.968 60.416 73.216q29.696 35.84 23.04 58.88t-43.52 23.04q-11.264 0-25.088 0.512t-29.184 1.024-30.208 1.024-27.136 0.512q-25.6 1.024-32.256 16.384t-5.632 41.984q0 29.696 0.512 77.824t1.024 100.352 1.536 101.376 1.024 79.872q0 13.312-2.048 27.648t-9.728 26.112-21.504 19.968-36.352 8.192q-27.648 0-52.736 0.512t-56.832 1.536z\"  ></path></symbol><symbol id=\"icon-chakan-copy\" viewBox=\"0 0 1024 1024\"><path d=\"M576.5 930.2H163.1c-52.9 0-96-43.1-96-96v-672c0-52.9 43.1-96 96-96h672c52.9 0 96 43.1 96 96V577c0 17.7-14.3 32-32 32s-32-14.3-32-32V162.2c0-17.6-14.4-32-32-32h-672c-17.6 0-32 14.4-32 32v672c0 17.6 14.4 32 32 32h413.4c17.7 0 32 14.3 32 32s-14.3 32-32 32z\" fill=\"\" ></path><path d=\"M692.4 322.3H245.7c-17.7 0-32-14.3-32-32s14.3-32 32-32h446.7c17.7 0 32 14.3 32 32s-14.3 32-32 32zM388.5 530.2H245.7c-17.7 0-32-14.3-32-32s14.3-32 32-32h142.7c17.7 0 32 14.3 32 32 0.1 17.6-14.3 32-31.9 32zM388.5 738H245.7c-17.7 0-32-14.3-32-32s14.3-32 32-32h142.7c17.7 0 32 14.3 32 32 0.1 17.7-14.3 32-31.9 32z\" fill=\"\" ></path><path d=\"M624.1 792.5c-94.8 0-172-77.2-172-172s77.2-172 172-172 172 77.2 172 172-77.1 172-172 172z m0-280c-59.6 0-108 48.4-108 108s48.4 108 108 108 108-48.4 108-108-48.4-108-108-108z\" fill=\"\" ></path><path d=\"M820.8 864.2L710.5 753.9c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L866 818.9c12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.2 0z\" fill=\"\" ></path></symbol><symbol id=\"icon-jurassic_edit-data\" viewBox=\"0 0 1024 1024\"><path d=\"M230.7 223.2h214.8V287H230.7z\" fill=\"#E6C27C\" ></path><path d=\"M909.9 426.9c-22.7-22.6-59.5-22.6-82.2 0l-41.1 41 40.4 40.4c0.2 0.2 0.4 0.5 0.7 0.7 0.2 0.2 0.5 0.4 0.7 0.7l40.4 40.3 41.1-41c22.8-22.7 22.8-59.4 0-82.1zM745.6 509L498.9 755l82.2 82 246.7-246zM375.6 960L540 877.9 457.8 796z\" fill=\"#5280C1\" ></path><path d=\"M654.8 64H97v892.9h208.7l32-63.8H160.9V127.8h415.7v165.4h-0.3l-0.1 63.8 224.2 0.2v9.8c18.8-12.7 40.8-19.9 63.9-20.8v-42.6L654.8 64z m-14.2 229.2V144.8l130 148.6-130-0.2z m159.5 599.9H645.6l2.3 2.3-123.2 61.5H864l0.2-312.1-64 63.8-0.1 184.5z\" fill=\"#727272\" ></path><path d=\"M736.2 827.8v-55.3l-55.5 55.3h55.5zM224.7 382.4v445.4h145.6l69.9-139.5 13.5 13.4h8.1l116.5-116.2v-10.4h10.4l63.9-63.8h-74.3V446h93.9v45.8l24-24 39.9-39.8v-45.7H224.7zM384.6 764h-95.9v-62.3h95.9V764z m0-126h-95.9v-62.8h95.9V638z m0-126.6h-95.9v-65.3h95.9v65.3zM514.4 638h-65.9v-62.8h65.9V638z m0-126.6h-65.9v-65.3h65.9v65.3z\" fill=\"#B2B2B2\" ></path></symbol><symbol id=\"icon-jurassic_edit-table\" viewBox=\"0 0 1024 1024\"><path d=\"M895.9 288.5v62.4h2.8c21.9 0 42.9 5.8 61.3 16.7v-79.1h-64.1z m-211.2 164v-164h-64.1v164H404.4v-164h-64.1v164H129.1v-164H65v388h-1v64h1v219h279.1l60.2-119.3v-99.7h49.9l30.4-60.8 17.6 17.5 20.7-20.7H404.4v-160h216.2V579l108.3-108.1 18.4-18.4h-62.6z m-344.4 443H129.1v-155h211.2v155z m0-219H129.1v-160h211.2v160zM898.7 640.7v-0.1l-2.8 2.8v33.1h-33.1l-64.1 64h97.2v155H684.7v-8.6l-145.3 72.6H960V579.4z\" fill=\"#B2B2B2\" ></path><path d=\"M684.7 879.3v-24.9l-12.5 12.5z\" fill=\"#B2B2B2\" ></path><path d=\"M896 288.5v62.4h1.8c22.3 0 43.6 6 62.2 17.3v-79.7h-64z m-767 0H65v608h-1v63h280.3l31.5-63H129v-608z m768.8 352.1l-1.8 1.8v254.1H664.9l-126 63H960V578.4l-62.2 62.2z\" fill=\"#727272\" ></path><path d=\"M937.4 431.3c-21.9-21.9-57.3-21.9-79.2 0l-39.6 39.6 39.1 39.1c0.2 0.2 0.4 0.4 0.5 0.6 0.2 0.2 0.4 0.4 0.6 0.5l39 39.1 39.6-39.6c21.9-22 21.9-57.4 0-79.3zM541.4 748.105l237.586-237.586 79.195 79.196L620.595 827.3zM422.6 946.1L581 866.9l-79.2-79.2z\" fill=\"#5280C1\" ></path><path d=\"M65 64.5h895v224H65z\" fill=\"#5280C1\" ></path></symbol><symbol id=\"icon-baobiaoshujuluru\" viewBox=\"0 0 1024 1024\"><path d=\"M778.9985189 948.90666667H111.50222222a36.40888889 36.40888889 0 0 1-36.40888889-36.40888889v-756.49175666a36.40888889 36.40888889 0 0 1 36.40888889-36.40888889h500.14890667a36.40888889 36.40888889 0 1 1 0 72.81777778H147.91111111V876.08888889h594.6785189V467.4962011a36.40888889 36.40888889 0 1 1 72.81777778 0V912.49777778a36.40888889 36.40888889 0 0 1-36.40888889 36.40888889z\"  ></path><path d=\"M463.45481443 487.72740779H293.54666667a36.40888889 36.40888889 0 1 1 0-72.81777778h169.90814776a36.40888889 36.40888889 0 1 1 0 72.81777778zM596.95407445 706.18074112H293.54666667a36.40888889 36.40888889 0 1 1 0-72.81777778h303.40740778a36.40888889 36.40888889 0 1 1 0 72.81777778zM645.49925888 414.90963001a36.38461667 36.38461667 0 0 1-25.74108445-62.14997334l266.9985189-266.99851889a36.38461667 36.38461667 0 0 1 51.48216889 0 36.38461667 36.38461667 0 0 1 0 51.48216889l-266.99851889 266.9985189A36.28752555 36.28752555 0 0 1 645.49925888 414.90963001z\"  ></path></symbol><symbol id=\"icon-bofang5\" viewBox=\"0 0 1024 1024\"><path d=\"M324.085 95.787l500.422 300.664c82.373 50.453 79.284 136.946-1.030 186.37v0l-506.6 304.784c-41.187 23.683-87.522 37.068-131.798 9.267-36.037-22.653-46.335-58.691-46.335-97.819v-616.774c0-39.127 13.386-75.166 48.395-97.819 45.305-27.801 94.731-14.416 136.946 11.327v0z\"  ></path></symbol><symbol id=\"icon-a-qingkong3x\" viewBox=\"0 0 1024 1024\"><path d=\"M293.014588 805.436235l20.058353-25.901176-34.876235 70.716235a18.703059 18.703059 0 0 0 11.926588 26.352941l61.650824 16.534589a18.703059 18.703059 0 0 0 17.829647-4.638118c21.172706-20.449882 36.743529-37.135059 46.742588-49.995294l20.058353-25.961412-34.876235 70.776471a18.703059 18.703059 0 0 0 11.926588 26.322823l87.16047 23.371294a18.703059 18.703059 0 0 0 17.829647-4.638117c21.172706-20.48 36.743529-37.135059 46.712471-49.995295l20.088471-25.931294-34.906353 70.746353a18.703059 18.703059 0 0 0 11.956706 26.383059l74.390588 19.937882a18.703059 18.703059 0 0 0 17.859765-4.638117c21.172706-20.48 36.743529-37.135059 46.71247-50.025412l20.088471-25.931294-34.906353 70.776471a18.703059 18.703059 0 0 0 11.956706 26.322823l89.178353 24.094118 3.975529-0.240941c13.673412-0.722824 29.967059-5.180235 45.236706-13.19153 30.72-16.203294 49.694118-44.574118 53.820235-80.745412 2.228706-19.124706 4.939294-39.785412 8.673883-65.566117 1.716706-11.565176 3.041882-20.660706 6.445176-43.158589 11.384471-75.685647 15.088941-104.267294 17.679059-137.938823l0.963765-12.769882 2.921411-3.011765 12.739765-1.385412c9.577412-0.903529 18.582588-4.969412 25.6-11.595294 11.053176-10.541176 15.269647-26.202353 11.444706-43.550118l-38.761412-177.061647c-3.102118-14.125176-11.625412-28.822588-23.883294-41.08047-12.016941-12.016941-26.744471-20.781176-41.170824-24.666353l-116.85647-31.322353-2.349177-4.035765 54.573177-203.565176c8.613647-32.045176-12.107294-65.776941-46.381177-74.932706l-82.070588-21.985883c-34.243765-9.185882-69.029647 9.637647-77.643294 41.682824l-54.512941 203.595294-4.065882 2.319059-121.886118-32.64753c-14.456471-3.885176-31.623529-3.614118-47.977412 0.752942-16.775529 4.517647-31.472941 13.010824-41.261176 23.67247l-122.066824 133.933177c-11.986824 13.131294-16.143059 28.732235-11.89647 43.429647 4.306824 14.697412 16.353882 26.021647 33.972705 31.503059l14.034824 4.397176 2.198588 4.005647-3.734588 14.245647c-21.805176 82.401882-54.573176 141.312-88.455529 155.708235-49.061647 20.841412-75.294118 51.019294-80.926118 87.762824-5.210353 33.731765 9.577412 69.270588 38.791529 94.448941l22.738824 8.613647 77.040941 20.660706a44.032 44.032 0 0 0 42.526118-11.414588c15.781647-15.751529 27.738353-28.912941 35.96047-39.514353zM861.967059 396.227765c14.607059 3.915294 30.930824 19.817412 33.822117 33.008941l35.659295 162.816a10.992941 10.992941 0 0 1-13.583059 13.010823l-659.877647-176.790588a10.992941 10.992941 0 0 1-5.270589-18.070588l112.278589-123.151059c9.125647-9.999059 31.201882-15.631059 45.778823-11.715765l145.859765 39.092706a21.985882 21.985882 0 0 0 26.955294-15.570823l60.175059-224.587294a21.985882 21.985882 0 0 1 26.955294-15.570824l79.510588 21.323294a21.985882 21.985882 0 0 1 15.570824 26.955294l-60.175059 224.587294a21.985882 21.985882 0 0 0 15.540706 26.955295l140.8 37.737411v-0.030117zM335.209412 704.933647c-34.725647 46.561882-66.650353 81.588706-95.774118 105.020235l-6.083765 4.939294-2.921411 0.602353-90.925177-24.877176-3.011765-3.312941c-13.462588-14.607059-19.998118-32.015059-17.52847-48.067765 3.403294-21.985882 22.768941-40.598588 54.90447-54.241882 32.737882-13.914353 61.741176-48.609882 85.564236-103.092706 10.360471-23.702588 19.486118-50.296471 27.226353-79.028706l4.005647-14.878118 4.035764-2.319059 584.222118 156.521412 2.409412 3.433412-0.783059 12.589176c-2.048 31.593412-31.322353 236.724706-33.159529 252.747295-2.409412 21.082353-11.685647 36.080941-28.491294 45.778823a75.595294 75.595294 0 0 1-22.076236 8.372706l-5.180235 0.752941-62.283294-16.685176 11.595294-20.931765c8.523294-15.450353 23.371294-45.236706 44.453647-89.359059a21.805176 21.805176 0 0 0-10.270118-29.063529l-3.222588-1.536a21.985882 21.985882 0 0 0-27.105882 6.686117c-34.725647 46.471529-66.590118 81.408-95.62353 104.779295l-6.113882 4.909176-2.921412 0.602353-54.211764-14.516706 11.444705-20.901647c3.704471-6.716235 7.288471-13.462588 10.812236-20.269176 7.228235-13.914353 18.462118-36.924235 33.611294-68.969412a21.985882 21.985882 0 0 0-10.300235-29.214118l-3.102118-1.475765a21.985882 21.985882 0 0 0-27.226353 6.656c-34.665412 46.441412-66.529882 81.317647-95.563294 104.719059l-6.083765 4.909177-2.921412 0.602353-53.820235-14.396236 11.444706-20.931764c3.704471-6.686118 7.318588-13.432471 10.842353-20.239059 7.198118-13.884235 18.522353-37.104941 33.91247-69.571765a21.624471 21.624471 0 0 0-10.420705-28.912941l-3.433412-1.596235a21.985882 21.985882 0 0 0-26.925177 6.836706c-34.695529 46.471529-66.590118 81.498353-95.713882 104.929882l-6.113882 4.909176-2.921412 0.602353-33.67153-9.035294 11.444706-20.871529c3.644235-6.716235 7.258353-13.432471 10.782118-20.239059 7.198118-13.914353 18.522353-37.135059 33.912471-69.632a21.714824 21.714824 0 0 0-10.390589-28.943059l-3.312941-1.566118a21.985882 21.985882 0 0 0-27.015529 6.746353z\"  ></path></symbol><symbol id=\"icon-shanchu\" viewBox=\"0 0 1024 1024\"><path d=\"M512 883.2A371.2 371.2 0 1 0 140.8 512 371.2 371.2 0 0 0 512 883.2z m0 64a435.2 435.2 0 1 1 435.2-435.2 435.2 435.2 0 0 1-435.2 435.2z\" fill=\"#5A5A68\" ></path><path d=\"M557.056 512l122.368 122.368a31.744 31.744 0 1 1-45.056 45.056L512 557.056l-122.368 122.368a31.744 31.744 0 1 1-45.056-45.056L466.944 512 344.576 389.632a31.744 31.744 0 1 1 45.056-45.056L512 466.944l122.368-122.368a31.744 31.744 0 1 1 45.056 45.056z\" fill=\"#5A5A68\" ></path></symbol><symbol id=\"icon-newdocumentworksheet\" viewBox=\"0 0 1024 1024\"><path d=\"M960 224C960 241.664 945.6 256 928 256L832 256l0 96C832 369.664 817.6 384 800 384S768 369.664 768 352L768 256l-96 0C654.4 256 640 241.664 640 224S654.4 192 672 192L768 192 768 96C768 78.336 782.4 64 800 64S832 78.336 832 96L832 192l96 0C945.6 192 960 206.336 960 224zM832 480 832 896c0 35.392-28.608 64-64 64L128 960c-35.392 0-64-28.608-64-64L64 192c0-35.392 28.608-64 64-64l416 0C561.6 128 576 142.336 576 160S561.6 192 544 192l-64 0 0 320L768 512 768 480C768 462.336 782.4 448 800 448S832 462.336 832 480zM128 512l288 0L416 192 128 192 128 512zM128 896l288 0L416 576 128 576 128 896zM768 576 480 576l0 320L768 896 768 576z\" fill=\"#010101\" ></path></symbol><symbol id=\"icon-file-excel\" viewBox=\"0 0 1024 1024\"><path d=\"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326z m1.8 562H232V136h302v216c0 23.2 18.8 42 42 42h216v494z\"  ></path><path d=\"M514.1 580.1l-61.8-102.4c-2.2-3.6-6.1-5.8-10.3-5.8h-38.4c-2.3 0-4.5 0.6-6.4 1.9-5.6 3.5-7.3 10.9-3.7 16.6l82.3 130.4-83.4 132.8c-1.2 1.9-1.8 4.1-1.8 6.4 0 6.6 5.4 12 12 12h34.5c4.2 0 8-2.2 10.2-5.7L510 664.8l62.3 101.4c2.2 3.6 6.1 5.7 10.2 5.7H620c2.3 0 4.5-0.7 6.5-1.9 5.6-3.6 7.2-11 3.6-16.6l-84-130.4 85.3-132.5c1.2-1.9 1.9-4.2 1.9-6.5 0-6.6-5.4-12-12-12h-35.7c-4.2 0-8.1 2.2-10.3 5.8l-61.2 102.3z\"  ></path></symbol><symbol id=\"icon-file-markdown\" viewBox=\"0 0 1024 1024\"><path d=\"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326z m1.8 562H232V136h302v216c0 23.2 18.8 42 42 42h216v494z\"  ></path><path d=\"M429 481.2c-1.9-4.4-6.2-7.2-11-7.2h-35c-6.6 0-12 5.4-12 12v272c0 6.6 5.4 12 12 12h27.1c6.6 0 12-5.4 12-12V582.1l66.8 150.2c1.9 4.3 6.2 7.1 11 7.1H524c4.7 0 9-2.8 11-7.1l66.8-150.6V758c0 6.6 5.4 12 12 12H641c6.6 0 12-5.4 12-12V486c0-6.6-5.4-12-12-12h-34.7c-4.8 0-9.1 2.8-11 7.2l-83.1 191-83.2-191z\"  ></path></symbol><symbol id=\"icon-file-word\" viewBox=\"0 0 1024 1024\"><path d=\"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326z m1.8 562H232V136h302v216c0 23.2 18.8 42 42 42h216v494z\"  ></path><path d=\"M528.1 472h-32.2c-5.5 0-10.3 3.7-11.6 9.1L434.6 680l-46.1-198.7c-1.3-5.4-6.1-9.3-11.7-9.3h-35.4c-1.1 0-2.1 0.1-3.1 0.4-6.4 1.7-10.2 8.3-8.5 14.7l74.2 276c1.4 5.2 6.2 8.9 11.6 8.9h32c5.4 0 10.2-3.6 11.6-8.9l52.8-197 52.8 197c1.4 5.2 6.2 8.9 11.6 8.9h31.8c5.4 0 10.2-3.6 11.6-8.9l74.4-276c0.3-1 0.4-2.1 0.4-3.1 0-6.6-5.4-12-12-12H647c-5.6 0-10.4 3.9-11.7 9.3l-45.8 199.1-49.8-199.3c-1.3-5.4-6.1-9.1-11.6-9.1z\"  ></path></symbol><symbol id=\"icon-HTML\" viewBox=\"0 0 1024 1024\"><path d=\"M145 96l66 746.6L511.8 928l299.6-85.4L878.7 96H145z m610.9 700.6l-244.1 69.6-245.2-69.6-56.7-641.2h603.8l-57.8 641.2z\"  ></path><path d=\"M281 249l1.7 24.3 22.7 253.5h206.5v-0.1h112.9l-11.4 118.5L511 672.9v0.2h-0.8l-102.4-27.7-6.5-73.2h-91l11.3 144.7 188.6 52h1.7v-0.4l187.7-51.7 1.7-16.3 21.2-242.2 3.2-24.3H511v0.2H389.9l-8.2-94.2h352.1l1.7-19.5 4.8-47.2L742 249H511z\"  ></path></symbol><symbol id=\"icon-HTML1\" viewBox=\"0 0 1024 1024\"><path d=\"M847.872 240.128v688c0 26.56-21.408 48-48 48h-576c-26.56 0-48-21.44-48-48v-832c0-26.592 21.44-48 48-48h432z\" fill=\"#E9EDED\" ></path><path d=\"M160 768.128v160c0 35.456 28.544 64 64 64h576c35.456 0 64-28.544 64-64v-160H160z\" fill=\"#F05542\" ></path><path d=\"M847.872 240.128h-144c-26.56 0-48-21.44-48-48v-144\" fill=\"#F05542\" ></path><path d=\"M432.736 384.064a16 16 0 0 0-10.976 4.8l-96.96 95.776a16 16 0 0 0-0.416 0.416 16 16 0 0 0 1.664 23.808l95.68 94.56a16 16 0 1 0 22.528-22.72l-85.568-84.576 85.568-84.48a16 16 0 0 0-11.52-27.584z m157.824 0a16 16 0 0 0-11.008 27.552l85.504 84.48-85.504 84.576a16 16 0 1 0 22.496 22.752l95.648-94.56a16 16 0 0 0 5.44-17.504 16 16 0 0 0-1.28-2.912 16 16 0 0 0-0.608-1.12 16 16 0 0 0-0.192-0.256 16 16 0 0 0-0.192-0.256 16 16 0 0 0-0.8-0.992 16 16 0 0 0-0.192-0.256 16 16 0 0 0-0.864-0.96 16 16 0 0 0-0.064 0l-0.64-0.544a16 16 0 0 0-0.48-0.512l-95.776-94.688a16 16 0 0 0-11.488-4.8z\" fill=\"#F05542\" ></path><path d=\"M654.88 799.744a16 16 0 0 0-11.84 6.624L608 853.312l-35.136-46.944-0.128 0.064a16 16 0 0 0-13.056-6.368 16 16 0 0 0-15.744 16.192v127.68a16 16 0 1 0 32 0v-80.128l15.936 21.248a16 16 0 0 0 28.864 4.448l19.2-25.6v80.032a16 16 0 1 0 32 0v-126.816a16 16 0 0 0-15.424-17.376 16 16 0 0 0-1.632 0z m-383.136 0.32a16 16 0 0 0-15.744 16.192V943.936a16 16 0 1 0 32 0v-47.808h64v47.808a16 16 0 1 0 32 0v-61.312a16 16 0 0 0 0-5.12v-61.248a16 16 0 0 0-16.256-16.192 16 16 0 0 0-15.744 16.192v47.872H288v-47.872a16 16 0 0 0-16.256-16.192z m192 0a16 16 0 0 0-1.6 0.064H432a16 16 0 1 0 0 32h16v111.808a16 16 0 1 0 32 0v-111.808h16a16 16 0 1 0 0-32h-30.304a16 16 0 0 0-1.92-0.064z m255.936 0a16 16 0 0 0-15.744 16.192v126.496a16 16 0 0 0 0.64 5.824 16 16 0 0 0 0 0.064 16 16 0 0 0 0.096 0.416 16 16 0 0 0 0.448 1.12 16 16 0 0 0 0.864 1.824 16 16 0 0 0 0.64 0.992 16 16 0 0 0 0.192 0.32 16 16 0 0 0 0.736 0.96 16 16 0 0 0 0.832 0.864 16 16 0 0 0 0.352 0.416 16 16 0 0 0 0.832 0.704 16 16 0 0 0 0.448 0.384 16 16 0 0 0 0.352 0.32 16 16 0 0 0 1.12 0.736 16 16 0 0 0 2.464 1.248 16 16 0 0 0 0.864 0.32 16 16 0 0 0 5.376 0.864h63.552a16 16 0 1 0 0-32h-47.808v-111.872a16 16 0 0 0-16.256-16.192z\" fill=\"#E9EDED\" ></path></symbol><symbol id=\"icon-pdf\" viewBox=\"0 0 1024 1024\"><path d=\"M582.4 864H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V170.666667c0-6.4 4.266667-10.666667 10.666667-10.666667h309.333333V320c0 40.533333 34.133333 74.666667 74.666667 74.666667h160v38.4c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V298.666667c0-8.533333-4.266667-17.066667-8.533334-23.466667l-170.666666-170.666667c-6.4-6.4-14.933333-8.533333-23.466667-8.533333H170.666667C130.133333 96 96 130.133333 96 170.666667v682.666666c0 40.533333 34.133333 74.666667 74.666667 74.666667h411.733333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z m132.266667-550.4v17.066667H554.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V160h19.2l151.466667 153.6z\" fill=\"#666666\" ></path><path d=\"M332.8 533.333333c-12.8 0-19.2 2.133333-25.6 6.4-6.4 4.266667-8.533333 12.8-8.533333 23.466667v206.933333c0 6.4 2.133333 12.8 6.4 19.2 4.266667 4.266667 10.666667 8.533333 21.333333 8.533334s17.066667-4.266667 21.333333-8.533334c4.266667-4.266667 6.4-10.666667 6.4-19.2v-64h32c57.6 0 89.6-29.866667 89.6-87.466666 0-27.733333-8.533333-51.2-23.466666-64-14.933333-14.933333-36.266667-21.333333-66.133334-21.333334h-53.333333z m87.466667 85.333334c0 12.8-2.133333 23.466667-8.533334 27.733333-4.266667 4.266667-14.933333 8.533333-27.733333 8.533333h-32v-70.4H384c12.8 0 21.333333 2.133333 27.733333 8.533334 6.4 4.266667 8.533333 12.8 8.533334 25.6zM667.733333 571.733333c-8.533333-12.8-21.333333-21.333333-34.133333-29.866666-14.933333-4.266667-32-8.533333-51.2-8.533334h-61.866667c-8.533333 0-17.066667 0-23.466666 8.533334-2.133333 4.266667-4.266667 10.666667-4.266667 19.2V768c0 8.533333 2.133333 14.933333 4.266667 19.2 6.4 8.533333 14.933333 8.533333 23.466666 8.533333h64c19.2 0 34.133333-4.266667 49.066667-10.666666 12.8-6.4 25.6-17.066667 34.133333-29.866667 8.533333-12.8 14.933333-25.6 19.2-42.666667 4.266667-14.933333 6.4-32 6.4-49.066666 0-17.066667-2.133333-34.133333-6.4-49.066667-4.266667-14.933333-10.666667-29.866667-19.2-42.666667z m-42.666666 153.6c-8.533333 12.8-21.333333 19.2-38.4 19.2h-38.4v-160H576c21.333333 0 38.4 6.4 46.933333 19.2 10.666667 12.8 14.933333 34.133333 14.933334 59.733334 2.133333 27.733333-4.266667 46.933333-12.8 61.866666zM851.2 533.333333h-106.666667c-8.533333 0-17.066667 2.133333-21.333333 6.4-6.4 4.266667-8.533333 12.8-8.533333 21.333334v209.066666c0 6.4 2.133333 12.8 6.4 17.066667 4.266667 6.4 10.666667 8.533333 21.333333 8.533333 8.533333 0 17.066667-2.133333 21.333333-8.533333 2.133333-4.266667 6.4-8.533333 6.4-19.2v-85.333333h72.533334c12.8 0 23.466667-6.4 25.6-17.066667 2.133333-8.533333 2.133333-14.933333 0-17.066667-2.133333-4.266667-6.4-17.066667-25.6-17.066666H768v-49.066667h81.066667c8.533333 0 14.933333-2.133333 19.2-4.266667 4.266667-2.133333 8.533333-8.533333 8.533333-21.333333 2.133333-12.8-8.533333-23.466667-25.6-23.466667z\" fill=\"#666666\" ></path></symbol><symbol id=\"icon-gerenyonghu\" viewBox=\"0 0 1024 1024\"><path d=\"M877.248 795.2a420.992 420.992 0 0 0-90.688-134.528 422.848 422.848 0 0 0-134.528-90.688l-1.344-0.576A279.104 279.104 0 1 0 208 343.168c0 93.056 45.632 175.616 115.712 226.368-0.448 0.192-0.896 0.32-1.28 0.576a419.2 419.2 0 0 0-134.528 90.688A422.848 422.848 0 0 0 64 950.72c0 2.56 13.888 34.944 44.096 34.944s41.408-32 41.472-34.432A335.616 335.616 0 0 1 248.32 721.28a335.488 335.488 0 0 1 238.848-98.944c90.24 0 175.04 35.072 238.848 98.944a335.616 335.616 0 0 1 98.88 229.952c0 2.496 6.912 39.744 43.584 39.744 36.672 0 41.984-37.696 41.92-40.192a419.52 419.52 0 0 0-33.216-155.52zM487.232 537.6a197.12 197.12 0 0 1-140.224-58.112A197.12 197.12 0 0 1 288.832 339.2c0-52.928 20.672-102.784 58.176-140.288A197.12 197.12 0 0 1 487.232 140.8a197.12 197.12 0 0 1 140.288 58.112 197.12 197.12 0 0 1 58.112 140.288 197.12 197.12 0 0 1-58.112 140.288 197.12 197.12 0 0 1-140.288 58.112z\"  ></path></symbol><symbol id=\"icon-houtaiguanli\" viewBox=\"0 0 1024 1024\"><path d=\"M510.836364 1017.437091c-26.065455 0-52.224-6.749091-75.450182-20.107636l-304.686546-175.988364a151.365818 151.365818 0 0 1-75.496727-130.606546V338.757818c0-53.666909 28.858182-103.796364 75.496727-130.606545L435.386182 32.209455a151.319273 151.319273 0 0 1 150.807273 0l304.826181 175.941818a151.365818 151.365818 0 0 1 75.403637 130.606545v351.976727c0 53.666909-28.858182 103.796364-75.403637 130.606546l-304.686545 175.941818c-23.272727 13.405091-49.431273 20.154182-75.496727 20.154182zM512 102.818909c-8.843636 0-17.687273 2.327273-25.6 6.888727L175.010909 289.466182c-15.825455 9.076364-25.553455 26.065455-25.553454 44.311273v359.563636c0 18.245818 9.774545 35.234909 25.6 44.264727l311.38909 179.805091c15.825455 9.029818 35.328 9.029818 51.153455 0l311.389091-179.805091c15.825455-9.029818 25.553455-26.065455 25.553454-44.264727V333.730909c0-18.245818-9.774545-35.234909-25.6-44.311273l-311.38909-179.758545A50.967273 50.967273 0 0 0 512 102.818909z\" fill=\"#383D4B\" ></path><path d=\"M488.587636 559.662545L235.380364 413.323636a51.432727 51.432727 0 0 1-18.804364-70.283636c14.242909-24.669091 45.754182-25.041455 70.423273-10.845091L514.327273 463.592727l217.646545-127.255272c24.576-14.336 55.994182-14.103273 70.330182 10.426181 14.336 24.576 6.050909 56.133818-18.478545 70.469819l-243.432728 142.242909a51.712 51.712 0 0 1-51.758545 0.232727z\" fill=\"#383D4B\" ></path><path d=\"M512 849.687273c-28.439273 0-46.266182-23.04-46.266182-51.479273v-283.182545c0-28.392727 17.826909-51.479273 46.266182-51.479273s47.010909 23.04 47.010909 51.479273v283.275636c0 28.346182-18.571636 51.386182-47.010909 51.386182z\" fill=\"#383D4B\" ></path></symbol><symbol id=\"icon-zitidaima\" viewBox=\"0 0 1024 1024\"><path d=\"M300.224 224L32 525.76l268.224 301.76 71.776-63.776-211.552-237.984 211.552-237.984zM711.744 224L640 287.776l211.552 237.984L640 763.744l71.744 63.776 268.256-301.76z\"  ></path></symbol><symbol id=\"icon-banben\" viewBox=\"0 0 1024 1024\"><path d=\"M512 0c285.257143 0 512 226.742857 512 512s-226.742857 512-512 512-512-226.742857-512-512 226.742857-512 512-512z m7.460571 73.142857C273.993143 73.142857 73.142857 272.310857 73.142857 515.657143 73.142857 759.076571 273.993143 950.857143 512 950.857143s438.857143-199.168 438.857143-435.2C950.857143 279.698286 764.928 73.142857 519.460571 73.142857z\" fill=\"#6E7781\" ></path><path d=\"M542.866286 734.354286l139.776-403.382857H601.673143l-66.194286 220.086857a719.140571 719.140571 0 0 0-36.790857 117.321143c-14.701714-36.644571-29.476571-80.676571-44.178286-117.321143l-66.267428-220.086857H299.958857l147.163429 403.382857h95.744z\" fill=\"#6E7781\" ></path></symbol><symbol id=\"icon-cheweiguanli\" viewBox=\"0 0 1024 1024\"><path d=\"M932.306 334.43c-22.978-54.324-55.864-103.106-97.747-144.989-41.882-41.882-90.664-74.769-144.988-97.746C633.311 67.898 573.566 55.833 512 55.833c-61.567 0-121.311 12.065-177.57 35.862-54.324 22.977-103.105 55.864-144.988 97.746-41.882 41.883-74.77 90.665-97.747 144.988C67.898 390.689 55.833 450.432 55.833 512c0 61.567 12.065 121.311 35.862 177.571 22.977 54.323 55.864 103.105 97.747 144.987 41.883 41.883 90.664 74.77 144.988 97.747 56.26 23.796 116.003 35.861 177.57 35.861 61.566 0 121.311-12.065 177.57-35.861 54.324-22.978 103.106-55.864 144.988-97.747 41.883-41.882 74.77-90.664 97.747-144.988 23.796-56.26 35.861-116.003 35.861-177.57 0.001-61.568-12.064-121.311-35.86-177.57zM512 901.263c-214.64 0-389.263-174.623-389.263-389.263S297.36 122.737 512 122.737 901.263 297.36 901.263 512 726.64 901.263 512 901.263z\"  ></path><path d=\"M560.625 243.315h-137.02c-2.064 0-4.082 0.197-6.042 0.554-17.062 1.525-30.437 15.852-30.437 33.311v470.053c0 18.476 14.977 33.453 33.452 33.453 18.475 0 33.452-14.978 33.452-33.453V575.549h106.595c86.761 0 157.346-70.585 157.346-157.346V400.66c0-86.76-70.585-157.345-157.346-157.345z m90.441 174.888c0 49.87-40.572 90.441-90.441 90.441H454.03V310.219h106.595c49.869 0 90.441 40.571 90.441 90.44v17.544z\"  ></path></symbol><symbol id=\"icon-dianzhelidaochu\" viewBox=\"0 0 1024 1024\"><path d=\"M512 123.737496c-214.898472 0-388.578862 174.121626-388.578862 388.262504s173.68039 388.262504 388.578862 388.262504 388.578862-174.121626 388.578862-388.262504S726.898472 123.729171 512 123.729171zM47.245528 512c0-257.140553 208.371512-465.062504 464.754472-465.062504 256.382959 0 464.754472 207.921951 464.754472 465.062504S768.382959 977.062504 512 977.062504c-256.382959 0-464.754472-207.921951-464.754472-465.062504z\" fill=\"#333333\" ></path><path d=\"M303.245528 341.333333c0-21.204293 17.050016-38.395837 38.087805-38.395837h341.333334c21.037789 0 38.087805 17.191545 38.087805 38.395837S703.704455 379.729171 682.666667 379.729171H341.333333c-21.037789 0-38.087805-17.191545-38.087805-38.395838z\" fill=\"#333333\" ></path><path d=\"M512 302.937496c21.037789 0 38.087805 17.191545 38.087805 38.395837v384c0 21.204293-17.050016 38.395837-38.087805 38.395838s-38.087805-17.191545-38.087805-38.395838V341.333333c0-21.204293 17.050016-38.395837 38.087805-38.395837z\" fill=\"#333333\" ></path></symbol><symbol id=\"icon-circle-f\" viewBox=\"0 0 1024 1024\"><path d=\"M437.077333 309.290667c-11.306667 0-21.248 4.266667-29.354666 12.416l-0.170667 0.170666a42.410667 42.410667 0 0 0-12.245333 29.781334v356.992c0 11.434667 3.882667 21.461333 11.818666 29.354666 8.106667 8.149333 18.048 12.416 29.354667 12.416 11.605333 0 21.845333-4.181333 30.378667-12.245333l0.170666-0.128a40.448 40.448 0 0 0 12.373334-29.397333v-140.202667h164.181333c10.88 0 20.48-4.053333 28.586667-11.648l0.170666-0.213333a38.826667 38.826667 0 0 0 11.861334-28.16 39.509333 39.509333 0 0 0-11.861334-28.8 39.466667 39.466667 0 0 0-28.757333-11.818667h-164.138667V389.930667h184.533334c10.88 0 20.522667-4.010667 28.586666-11.648l0.213334-0.170667a38.826667 38.826667 0 0 0 11.818666-28.202667 39.466667 39.466667 0 0 0-11.818666-28.8 39.466667 39.466667 0 0 0-28.8-11.818666h-226.901334z\"  ></path><path d=\"M512 42.666667C252.8 42.666667 42.666667 252.8 42.666667 512s210.133333 469.333333 469.333333 469.333333 469.333333-210.133333 469.333333-469.333333S771.2 42.666667 512 42.666667zM128 512a384 384 0 1 1 768 0 384 384 0 0 1-768 0z\"  ></path></symbol><symbol id=\"icon-tubiao-hanshu\" viewBox=\"0 0 1024 1024\"><path d=\"M64 127.712C64 92.512 92.8 64 127.712 64H896.32C931.488 64 960 92.8 960 127.712V896.32c0 35.2-28.8 63.712-63.712 63.712H127.68C92.512 960 64 931.2 64 896.288V127.68z m64 32.32v703.936A32 32 0 0 0 160 896h704a32 32 0 0 0 32-32V160a32 32 0 0 0-32-32H160a32 32 0 0 0-32 32z m226.88 640c-57.728 0-88.512-53.12-97.6-84.48A25.952 25.952 0 0 1 307.2 701.12c0.096 0.32 14.496 47.04 47.744 47.04 21.76 0 32.416-28.928 33.824-33.024 3.808-15.68 38.24-158.272 64.896-266.368h-77.984a25.952 25.952 0 1 1 0-51.904h90.816c8.544-34.208 15.104-59.744 17.6-68.032l2.08-7.072c12.256-41.92 37.792-129.12 105.824-129.12 60.096 0 80.576 59.84 83.008 91.456a25.952 25.952 0 0 1-51.776 4.096c-0.16-2.016-4.192-43.616-31.232-43.616-29.12 0-48 64.32-56 91.776l-2.208 7.36c-1.952 6.624-6.944 25.984-13.76 53.12h63.328a25.952 25.952 0 1 1 0 51.936h-76.224a60869.312 60869.312 0 0 0-68.48 280.768c-7.744 26.176-34.08 70.496-83.712 70.496z m384.96-66.912c-12.064 0-20.512-5.632-28.576-15.68l-43.456-57.152-43.424 57.12c-7.232 8.864-15.68 15.68-26.56 15.68-16.064 0-28.544-11.232-28.544-26.56 0-8.416 3.2-14.848 8.448-20.896l55.104-65.152-50.272-57.92c-6.848-8.064-10.048-15.296-10.048-23.36 0-17.28 12.864-28.16 28.16-28.16 12.064 0 20.48 5.664 28.544 15.68l39.808 53.12 40.64-53.088c7.264-8.864 15.68-15.68 26.56-15.68 16.096 0 28.544 11.232 28.544 26.56 0 8.416-3.616 14.88-8.416 20.896L704 619.648l53.888 61.984c6.848 8.032 10.08 15.296 10.08 23.328 0 17.28-12.864 28.16-28.16 28.16z\" fill=\"#000000\" ></path></symbol><symbol id=\"icon-shituguanliqi\" viewBox=\"0 0 1024 1024\"><path d=\"M915.2 810.88H108.8a96 96 0 0 1-96-96V192a96 96 0 0 1 96-96h806.4A96 96 0 0 1 1011.2 192v524.8a96 96 0 0 1-96 94.08zM108.8 158.72a31.36 31.36 0 0 0-31.36 33.28v524.8a31.36 31.36 0 0 0 31.36 31.36h806.4a31.36 31.36 0 0 0 31.36-31.36V192a31.36 31.36 0 0 0-31.36-31.36z\" fill=\"#323333\" ></path><path d=\"M687.36 794.24v72.32a64 64 0 0 1-64 64H401.28a64 64 0 0 1-64-64v-72.32\" fill=\"#323333\" ></path><path d=\"M239.36 369.92m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z\" fill=\"#323333\" ></path><path d=\"M816.64 401.92H347.52a32 32 0 0 1-32-32 32 32 0 0 1 32-32.64h469.12a32 32 0 0 1 32 32.64 32 32 0 0 1-32 32z\" fill=\"#323333\" ></path><path d=\"M239.36 561.28m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z\" fill=\"#323333\" ></path><path d=\"M816.64 593.28H347.52a32 32 0 0 1-32-32 32 32 0 0 1 32-32.64h469.12a32 32 0 0 1 32 32.64 32 32 0 0 1-32 32z\" fill=\"#323333\" ></path></symbol><symbol id=\"icon-huiche\" viewBox=\"0 0 1024 1024\"><path d=\"M969.386667 119.466667c-29.013333-1.706667-52.906667 20.48-54.613334 49.493333v399.36H186.026667l129.706666-145.066667c22.186667-22.186667 20.48-58.026667-1.706666-80.213333-22.186667-22.186667-58.026667-22.186667-78.506667 1.706667L17.066667 585.386667c-22.186667 22.186667-22.186667 58.026667 0 78.506666l240.64 225.28c22.186667 20.48 58.026667 18.773333 78.506666-3.413333 20.48-22.186667 18.773333-58.026667-3.413333-80.213333l-134.826667-126.293334h771.413334c29.013333 1.706667 52.906667-20.48 54.613333-49.493333V174.08c1.706667-29.013333-18.773333-54.613333-47.786667-56.32-1.706667 1.706667-5.12 1.706667-6.826666 1.706667z\"  ></path></symbol><symbol id=\"icon-quesheng\" viewBox=\"0 0 1024 1024\"><path d=\"M391.2192 493.6704l-100.608-55.0656c-3.8528-2.688-8.8192-3.264-13.184-1.4976-3.9168 1.6-7.3984 4.096-10.1504 7.296a15.1808 15.1808 0 0 0 5.8368 21.7344l100.608 55.0656c3.8528 2.688 8.8064 3.264 13.1712 1.5104 3.9424-1.5488 7.4368-4.0704 10.1504-7.3088a11.008 11.008 0 0 0 1.5104-11.6096 12.9152 12.9152 0 0 0-7.3344-10.112z m431.616-70.9888a15.616 15.616 0 0 0-16.1792 0c-5.0944 2.9184-7.936 8.6144-7.1424 14.4256v203.2256L522.4832 797.12V508.288c0-2.9952-1.4976-4.4928-1.4976-7.3088l287.1808-159.5904c5.4656-2.816 8.8832-8.4608 8.832-14.6048a17.3824 17.3824 0 0 0-10.3424-14.4256L512.1408 168.512a13.4912 13.4912 0 0 0-14.4768 0L201.6384 312.3584a18.048 18.048 0 0 0-10.1504 14.4256v2.9952a2.8672 2.8672 0 0 1-1.5104 2.816V646.144c2.2016 5.952 6.2976 11.0208 11.6608 14.4256L494.656 837.76c3.1744 1.728 6.72 2.688 10.3424 2.816 3.0976-0.1024 6.0928-1.0752 8.6528-2.816l309.184-174.208a18.176 18.176 0 0 0 7.1424-14.592V437.0944c0.1024-5.6704-2.56-11.0464-7.1424-14.4256zM487.5008 508.288v285.824L226.4704 637.3376V354.3168l262.5408 146.6624a12.544 12.544 0 0 0-1.5104 7.296z m16-37.8368l-258.048-142.1696 258.048-127.744 258.0224 126.2464-258.0352 143.6672z\" fill=\"#666666\" ></path><path d=\"M749.248 172.5056a16.064 16.064 0 0 1-14.5152-3.8272 13.7088 13.7088 0 0 1-4.5184-10.2144c0-3.84 1.6128-7.552 4.5184-10.2144l39.9744-39.7056c2.688-2.8672 6.3488-4.48 10.3168-4.48 3.9808 0 7.6416 1.6128 10.3296 4.48 2.8928 2.6624 4.5056 6.272 4.5056 10.2144 0 3.84-1.6128 7.552-4.5056 10.2272l-39.9872 39.6928c-2.4704 2.4448-3.648 3.6224-6.1184 3.6224M838.8736 265.1008a11.9936 11.9936 0 0 1-6.1312 1.28l-50.8288-5.952a14.5024 14.5024 0 0 1-13.3248-15.6544c0.64-7.9744 7.7312-13.824 15.7952-13.184l50.8416 4.7744c8.064 0.7552 13.9648 7.6672 13.312 15.6544 0 5.952-3.8656 11.2768-9.664 13.0816M663.168 153.344c-3.6608 1.28-7.744 0.8576-11.1872-0.9472a14.336 14.336 0 0 1-6.9888-8.6272l-16.9856-48.1024a14.3104 14.3104 0 0 1 8.4992-18.0992c3.648-1.28 7.7312-0.9472 11.1744 0.8576 3.4432 1.8048 6.016 4.8896 7.0912 8.6144l16.9856 48.1152a13.184 13.184 0 0 1-0.1152 10.8544c-1.6128 3.5072-4.736 6.0672-8.4864 7.3472\" fill=\"#DCDCDC\" ></path></symbol><symbol id=\"icon-jinrujiantou\" viewBox=\"0 0 1024 1024\"><path d=\"M368.51199972 250.346667a22.4 22.4 0 0 1 0.533334-30.848 20.48 20.48 0 0 1 29.717333 0.64l272.426667 292.693333-272.426667 292.650667a20.458667 20.458667 0 0 1-29.717333 0.64c-8.32-8.32-8.746667-21.973333-0.533334-30.848l242.346667-262.464-242.346667-262.464z\" fill=\"#3D3D3D\" ></path></symbol><symbol id=\"icon-youjiantou_huaban\" viewBox=\"0 0 1024 1024\"><path d=\"M384 883.08c-8.19 0-16.38-3.12-22.62-9.38-12.5-12.5-12.5-32.75 0-45.25L677.83 512 361.38 195.55c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L734.4 478.07c18.72 18.72 18.72 49.16 0 67.88L406.62 873.7c-6.24 6.25-14.43 9.38-22.62 9.38z m305.14-359.77h0.31-0.31z\" fill=\"#333333\" ></path></symbol><symbol id=\"icon-xiangyoujiantou1\" viewBox=\"0 0 1024 1024\"><path d=\"M761.6 489.6l-432-435.2c-9.6-9.6-25.6-9.6-35.2 0-9.6 9.6-9.6 25.6 0 35.2l416 416-416 425.6c-9.6 9.6-9.6 25.6 0 35.2s25.6 9.6 35.2 0l432-441.6C771.2 515.2 771.2 499.2 761.6 489.6z\"  ></path></symbol><symbol id=\"icon-shujuyuan\" viewBox=\"0 0 1024 1024\"><path d=\"M572.3 678.5c-60.8 0-117.9-23.5-160.6-66.2-25.6-25.6-44.9-57.4-55.7-91.9-4.6-14.8 3.6-30.4 18.3-35.1 14.8-4.6 30.5 3.6 35.1 18.3 8.3 26.3 22.4 49.5 41.9 69 66.7 66.7 175.2 66.7 241.9 0l160.9-160.8c66.7-66.7 66.7-175.2 0-241.9C822 137.8 779 120 733.2 120c-45.9 0-88.8 17.7-120.9 49.9L498.5 283.6c-10.9 10.9-28.7 10.9-39.6 0s-10.9-28.7 0-39.6l113.8-113.8C615.3 87.5 672.4 64 733.2 64s117.9 23.5 160.6 66.2C936.5 172.9 960 230 960 290.8s-23.5 117.9-66.2 160.6L732.9 612.2c-42.8 42.8-99.8 66.3-160.6 66.3z\"  ></path><path d=\"M290.8 960c-60.8 0-117.9-23.5-160.6-66.2C87.5 851 64 794 64 733.2s23.5-117.9 66.2-160.6L291 411.8c42.7-42.7 99.8-66.2 160.6-66.2s117.9 23.5 160.6 66.2c25.6 25.6 44.9 57.4 55.7 91.9 4.6 14.8-3.6 30.5-18.3 35.1-14.8 4.6-30.5-3.6-35.1-18.3-8.3-26.3-22.4-49.5-41.9-69-32.1-32.1-75.1-49.9-120.9-49.9-45.8 0-88.8 17.7-120.9 49.9L169.9 612.2c-32.1 32.1-49.9 75.1-49.9 120.9 0 45.9 17.7 88.8 49.9 120.9 66.7 66.7 175.2 66.7 241.9 0l113.8-113.8c10.9-10.9 28.7-10.9 39.6 0s10.9 28.7 0 39.6L451.4 893.7c-42.7 42.8-99.8 66.3-160.6 66.3z\"  ></path></symbol><symbol id=\"icon-question\" viewBox=\"0 0 1024 1024\"><path d=\"M462.08 768a42.666667 42.666667 0 1 0 42.666667-42.666667 42.666667 42.666667 0 0 0-42.666667 42.666667z m42.666667-105.386667h-3.413334a35.84 35.84 0 0 1-32.426666-39.253333A231.253333 231.253333 0 0 1 554.666667 493.653333c64.426667-64.426667 65.706667-85.333333 66.56-106.666666a92.586667 92.586667 0 0 0-26.453334-69.12A112.213333 112.213333 0 0 0 512 283.306667a107.52 107.52 0 0 0-107.52 107.52 35.84 35.84 0 1 1-72.106667 0A179.2 179.2 0 0 1 512 213.333333a184.746667 184.746667 0 0 1 133.973333 57.173334 164.266667 164.266667 0 0 1 45.226667 120.32c-2.56 46.506667-16.64 82.773333-87.893333 153.6-35.84 35.84-58.88 63.146667-61.013334 85.333333a35.84 35.84 0 0 1-35.413333 34.133333z m316.16 159.146667A439.893333 439.893333 0 1 1 341.333333 106.666667a439.893333 439.893333 0 0 1 481.706667 716.373333zM512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z\" fill=\"#5E5C5C\" ></path></symbol><symbol id=\"icon-xingxing\" viewBox=\"0 0 1221 1024\"><path d=\"M1204.814491 122.670376l-104.202498-9.819364a18.026592 18.026592 0 0 1-14.655767-11.138382L1048.290907 11.139994a17.880035 17.880035 0 0 0-33.122032 0l-37.079088 90.572636a17.586919 17.586919 0 0 1-14.655767 11.138382l-104.349055 9.819364a18.026592 18.026592 0 0 0-10.112479 31.363339l78.554907 69.028659a17.733477 17.733477 0 0 1 5.715749 17.58692L908.91457 337.084236a18.026592 18.026592 0 0 0 26.673495 19.638727l86.908693-51.002067a17.586919 17.586919 0 0 1 18.173151 0L1128.751064 357.162636A18.026592 18.026592 0 0 0 1154.69177 337.084236l-23.302668-96.874615a17.733477 17.733477 0 0 1 5.715749-17.58692l78.554907-69.028659a18.319708 18.319708 0 0 0-10.845267-31.363339z\" fill=\"#d81e06\" ></path><path d=\"M928.699854 224.674509l4.250173-17.147247a18.026592 18.026592 0 0 0-5.715749-17.000689l-74.451293-66.097505a18.026592 18.026592 0 0 0-4.103614 29.311532l78.554907 69.028659a14.655766 14.655766 0 0 1 1.465576 1.90525zM1211.263028 124.575625L1137.104851 190.526573a18.026592 18.026592 0 0 0-5.715749 17.733478l4.103614 17.293804 1.612135-1.758692 78.554907-69.028659a18.319708 18.319708 0 0 0-4.39673-30.190879zM1150.588155 320.376662a17.880035 17.880035 0 0 1-22.56988 3.663942L1040.816466 272.89198a18.319708 18.319708 0 0 0-18.17315 0l-86.908694 51.148624a17.733477 17.733477 0 0 1-22.423322-3.810499L908.91457 337.084236a18.026592 18.026592 0 0 0 26.673495 19.638727l86.908693-51.002067a17.586919 17.586919 0 0 1 18.173151 0L1128.751064 357.162636A18.026592 18.026592 0 0 0 1154.69177 337.084236z\" fill=\"#d81e06\" ></path><path d=\"M869.490559 544.463328a42.501722 42.501722 0 0 0-25.06136-73.278831L601.143479 448.321502a42.208607 42.208607 0 0 1-34.880724-25.794149l-86.762136-211.043034a41.622376 41.622376 0 0 0-77.235888 0l-86.469021 211.043034a41.182703 41.182703 0 0 1-34.734166 25.794149L37.922382 471.331055a41.915491 41.915491 0 0 0-23.595784 73.278831l183.050521 161.213429a42.062049 42.062049 0 0 1 13.336747 41.182703L155.608185 971.97203a42.062049 42.062049 0 0 0 61.993891 46.019106l190.524961-112.116612c71.227024-56.4247 69.468332-125.160244 56.717816-147.583566-14.655766-25.354476-34.294493-36.199743-52.760759-26.673495s-19.638727 18.319708-19.638727 18.319708a34.734166 34.734166 0 1 1 34.294493 60.381757s-40.303357 25.06136-67.416524-22.862995 18.612823-83.39131 28.432186-88.813944a83.537868 83.537868 0 0 1 112.995958 39.424011c27.845956 49.096817 16.414458 112.995958-28.432187 166.489505l193.163 113.435631a41.915491 41.915491 0 0 0 61.993891-46.019106l-54.372893-225.552243a41.915491 41.915491 0 0 1 13.19019-41.182703z\" fill=\"#d81e06\" ></path><path d=\"M205.43779 713.590871l5.276076-24.768245a41.768934 41.768934 0 0 0-13.336747-41.036146L14.912829 487.012725A8.295164 8.295164 0 0 1 11.542003 483.641898 42.355164 42.355164 0 0 0 14.912829 544.463328l183.05052 161.213429a43.967299 43.967299 0 0 1 7.474441 7.914114zM871.542366 484.81436l-2.051807 2.198365-183.197078 161.213428a41.768934 41.768934 0 0 0-13.19019 41.036146l6.008864 24.621687a42.64828 42.64828 0 0 1 7.181326-8.353787l183.197078-161.213428a42.355164 42.355164 0 0 0 2.051807-59.502411zM460.887796 840.802922a41.622376 41.622376 0 0 0-40.596473 0.732788l-202.689247 119.15138a41.622376 41.622376 0 0 1-55.985027-13.19019l-6.008864 24.47513a42.062049 42.062049 0 0 0 61.993891 46.019106l190.524961-112.116612a173.524272 173.524272 0 0 0 52.760759-65.071602zM727.476184 971.97203l-5.862307-24.328572a41.622376 41.622376 0 0 1-56.131584 13.043632l-166.049832-97.607403a203.275478 203.275478 0 0 1-27.113168 41.475818l193.163 113.435631a41.915491 41.915491 0 0 0 61.993891-46.019106z\" fill=\"#d81e06\" ></path></symbol><symbol id=\"icon-kongzhitai\" viewBox=\"0 0 1024 1024\"><path d=\"M92.16 390.018246v-359.298246A30.899649 30.899649 0 0 1 122.88 0a30.899649 30.899649 0 0 1 30.72 30.72v359.298246M153.6 530.50386v462.77614A30.899649 30.899649 0 0 1 122.88 1024a30.899649 30.899649 0 0 1-30.72-30.72V530.863158M481.100351 645.12V30.72A30.899649 30.899649 0 0 1 512 0a30.899649 30.899649 0 0 1 30.72 30.72v614.4M542.72 808.421053v184.858947A30.899649 30.899649 0 0 1 512 1024 30.899649 30.899649 0 0 1 481.100351 993.28V808.421053M870.4 212.524912V30.72A30.899649 30.899649 0 0 1 901.12 0a30.899649 30.899649 0 0 1 30.72 30.72v179.649123M931.84 377.263158v616.016842A30.899649 30.899649 0 0 1 901.12 1024a30.899649 30.899649 0 0 1-30.72-30.72v-614.4\" fill=\"\" ></path><path d=\"M122.88 569.667368a108.867368 108.867368 0 1 1 108.867368-108.867368 109.047018 109.047018 0 0 1-108.867368 108.867368z m0-163.84a54.972632 54.972632 0 1 0 54.972632 54.972632 54.972632 54.972632 0 0 0-54.972632-54.972632zM901.12 405.827368a108.867368 108.867368 0 1 1 108.867368-108.867368 109.047018 109.047018 0 0 1-108.867368 108.867368z m0-163.84a54.972632 54.972632 0 1 0 54.972632 54.972632 54.972632 54.972632 0 0 0-54.972632-54.972632zM512 835.907368a108.867368 108.867368 0 1 1 108.867368-108.867368 109.047018 109.047018 0 0 1-108.867368 108.867368z m0-163.84a54.972632 54.972632 0 1 0 54.972632 54.972632 54.972632 54.972632 0 0 0-54.972632-54.972632z\" fill=\"\" ></path></symbol><symbol id=\"icon-xingxi\" viewBox=\"0 0 1205 1024\"><path d=\"M605.573957 234.305865C463.046606 234.305865 347.954097 349.398374 347.954097 491.925725S463.046606 749.545585 605.573957 749.545585c86.988524 0 168.623908-44.163405 216.133026-117.099936 2.67657 0 4.683997 0.669142 7.360567 0.669142 36.802837 1.338285 67.583392-28.103985 68.921677-64.906822 0.669142-24.758272-12.713707-48.17826-34.79541-60.222824 0-5.35314 0.669142-10.037137 0.669143-15.390277C863.193817 349.398374 748.101308 234.305865 605.573957 234.305865z m0 475.091171C485.797451 709.397036 388.102646 611.702232 388.102646 491.925725S485.797451 274.454415 605.573957 274.454415 823.045267 372.149219 823.045267 491.925725v6.691425c-34.79541 2.67657-61.561109 32.11884-62.230251 66.91425 0 19.405132 8.698852 38.141122 23.419987 50.854829-40.817692 58.215397-107.731942 93.010807-178.661046 93.010807z\" fill=\"#495FA9\" ></path><path d=\"M483.790023 493.26401a127.137074 126.467931 0 1 0 254.274148 0 127.137074 126.467931 0 1 0-254.274148 0Z\" fill=\"#495FA9\" ></path><path d=\"M160.594199 65.681957c10.037137 3.345712 19.405132 8.02971 28.103984 14.721134 6.691425 8.02971 12.044565 18.066847 15.390278 28.103985 2.007427 5.35314 8.02971 7.360567 13.38285 5.35314 2.67657-1.338285 4.014855-2.67657 5.353139-5.35314 3.345712-9.367995 8.02971-18.066847 14.051993-26.096557 9.367995-7.360567 19.405132-12.713707 30.780555-16.05942 4.683997-1.338285 7.360567-6.691425 6.022282-12.044565-0.669142-2.67657-3.345712-4.683997-6.022282-6.022282-10.70628-3.345712-20.743417-8.02971-30.111413-14.721135-6.691425-8.02971-11.375422-16.728562-14.721135-26.7657-2.007427-5.35314-8.02971-7.360567-13.382849-5.35314-2.007427 1.338285-4.014855 3.345712-5.35314 5.35314-3.345712 10.037137-8.698852 18.73599-15.390278 26.7657C180.668473 40.254542 170.631336 44.938539 160.594199 48.284252c-4.683997 1.338285-7.360567 6.691425-6.022283 12.044565 1.338285 2.67657 3.345712 4.683997 6.022283 5.35314zM1196.426779 691.330188c-15.390277-5.35314-31.449697-12.713707-38.810265-19.405132-8.698852-10.037137-14.721135-22.081702-18.735989-34.79541-2.67657-6.691425-10.037137-9.367995-16.728563-6.691425-3.345712 1.338285-5.35314 4.014855-6.691425 6.691425-4.014855 12.713707-10.70628 24.758272-19.405132 35.464553-6.691425 6.691425-22.081702 13.38285-36.802837 18.735989-6.022282 2.007427-9.367995 9.367995-7.360568 15.390278 1.338285 3.345712 4.014855 6.022282 7.360568 7.360567 13.38285 4.014855 25.427415 10.037137 36.133695 18.73599 8.698852 10.70628 15.390277 22.750845 20.074274 36.133695 2.67657 6.691425 10.037137 9.367995 16.728563 6.691425 3.345712-1.338285 5.35314-4.014855 6.691425-6.691425 4.014855-12.044565 10.037137-23.419987 18.066847-33.457125 7.360567-7.360567 24.08913-14.721135 39.479407-20.074275 6.691425-1.338285 10.70628-8.02971 8.698853-14.721135-0.669142-4.683997-4.014855-8.02971-8.698853-9.367995zM969.587474 218.915588c9.367995 2.67657 17.397705 6.691425 25.427414 12.713707 6.022282 7.360567 10.70628 15.390277 14.051993 24.758272 2.007427 4.683997 7.360567 6.691425 12.044565 4.683998 2.007427-0.669142 3.345712-2.67657 4.683997-4.683998 2.67657-8.02971 6.691425-16.05942 12.713708-22.750844 8.698852-6.022282 17.397705-10.70628 27.434842-14.051993 4.014855-1.338285 6.691425-6.022282 5.35314-10.037137-0.669142-2.67657-2.67657-4.683997-5.35314-5.35314-9.367995-2.67657-18.73599-7.360567-26.7657-13.38285-6.022282-6.691425-10.70628-15.390277-13.38285-23.419987-2.007427-4.683997-7.360567-6.691425-12.044565-4.683998-2.007427 0.669142-3.345712 2.67657-4.683997 4.683998-2.67657 8.698852-7.360567 16.728562-13.38285 24.089129-8.02971 6.022282-16.728562 10.037137-26.096557 12.713708-4.014855 1.338285-6.691425 6.022282-4.683998 10.70628 0 1.338285 2.007427 3.345712 4.683998 4.014855zM255.612433 772.29643c-9.367995-2.67657-18.73599-7.360567-26.7657-13.38285-6.022282-6.691425-10.70628-15.390277-13.38285-24.08913-2.007427-4.683997-7.360567-6.691425-12.044565-4.683997-2.007427 0.669142-4.014855 2.67657-4.683997 4.683997-2.67657 8.698852-7.360567 16.728562-13.38285 24.08913-8.02971 6.022282-16.728562 10.037137-26.096557 12.713708-4.014855 1.338285-6.691425 6.022282-5.35314 10.037137 0.669142 2.67657 2.67657 4.683997 5.35314 5.35314 9.367995 2.67657 17.397705 6.691425 25.427414 12.713707 6.022282 7.360567 10.70628 15.390277 14.051993 24.758273 2.007427 4.683997 7.360567 6.691425 11.375422 4.683997 2.007427-0.669142 4.014855-2.67657 4.683998-4.683997 2.67657-8.698852 6.691425-16.05942 12.713707-22.750845 8.02971-6.022282 17.397705-10.70628 27.434842-14.051993 4.014855-1.338285 6.691425-6.022282 5.35314-10.037137-0.669142-2.67657-2.67657-4.683997-4.683997-5.35314zM153.902774 455.122888c0 7.360567 6.022282 13.38285 13.382849 13.38285s13.38285-6.022282 13.38285-13.38285-6.022282-13.38285-13.38285-13.38285-13.38285 6.022282-13.382849 13.38285zM86.988524 863.299809c-14.721135 0-26.7657 12.044565-26.7657 26.7657s12.044565 26.7657 26.7657 26.7657 26.7657-12.044565 26.7657-26.7657-12.044565-26.7657-26.7657-26.7657zM1144.233665 348.060089c11.375422 0 20.074275-8.698852 20.074274-20.074275s-8.698852-20.074275-20.074274-20.074275-20.074275 8.698852-20.074275 20.074275 8.698852 20.074275 20.074275 20.074275zM920.070929 869.991234c-9.367995 0-16.728562 7.360567-16.728562 16.728563s7.360567 16.728562 16.728562 16.728562 16.728562-7.360567 16.728562-16.728562-7.360567-16.728562-16.728562-16.728563zM862.524675 57.652247c9.367995 2.67657 17.397705 6.691425 25.427414 12.713707 6.022282 7.360567 10.70628 15.390277 14.051993 24.758272 2.007427 4.683997 7.360567 6.691425 12.044565 4.683998 2.007427-0.669142 3.345712-2.67657 4.683997-4.683998 2.67657-8.698852 6.691425-16.05942 12.713708-22.750844 8.02971-6.022282 17.397705-10.70628 27.434842-14.051993 4.014855-1.338285 6.691425-6.022282 5.35314-10.037137-0.669142-2.67657-2.67657-4.683997-5.35314-5.35314-9.367995-2.67657-18.73599-7.360567-26.7657-13.38285-6.022282-7.360567-10.70628-15.390277-13.38285-24.08913-2.007427-4.683997-7.360567-6.691425-12.044565-4.683997-2.007427 0.669142-3.345712 2.67657-4.683997 4.683997-2.67657 8.698852-7.360567 16.728562-13.38285 24.08913-8.02971 6.022282-16.728562 10.037137-26.096557 12.713707-4.014855 1.338285-6.691425 6.022282-5.35314 10.037138 0.669142 2.67657 2.67657 4.683997 5.35314 5.35314zM428.251196 903.448359c-11.375422 0-20.074275 8.698852-20.074275 20.074275s8.698852 20.074275 20.074275 20.074275 20.074275-8.698852 20.074275-20.074275-8.698852-20.074275-20.074275-20.074275zM194.051323 321.294389c-7.360567 0-13.38285 6.022282-13.38285 13.38285s6.022282 13.38285 13.38285 13.38285 13.38285-6.022282 13.38285-13.38285-6.022282-13.38285-13.38285-13.38285zM669.142494 46.945967c11.375422 0 20.074275-8.698852 20.074275-20.074275s-8.698852-20.074275-20.074275-20.074275-20.074275 8.698852-20.074275 20.074275 8.698852 20.074275 20.074275 20.074275zM1071.297133 966.347753c-9.367995-2.67657-18.73599-7.360567-26.7657-13.38285-6.022282-6.691425-10.70628-15.390277-13.38285-24.089129-2.007427-4.683997-7.360567-6.691425-12.044565-4.683998-2.007427 0.669142-3.345712 2.67657-4.683997 4.683998-2.67657 8.698852-7.360567 16.728562-13.38285 24.089129-8.02971 6.022282-16.728562 10.037137-26.096557 12.713708-4.014855 1.338285-6.691425 6.022282-5.35314 10.037137 0.669142 2.67657 2.67657 4.683997 5.35314 5.35314 9.367995 2.67657 17.397705 6.691425 25.427414 12.713708 6.022282 7.360567 10.70628 15.390277 14.051993 24.758272 2.007427 4.683997 7.360567 6.691425 12.044565 4.683997 2.007427-0.669142 3.345712-2.67657 4.683997-4.683997 2.67657-8.02971 7.360567-16.05942 12.713707-22.750845 8.698852-6.022282 17.397705-10.70628 27.434843-14.051992 4.014855-1.338285 6.691425-6.022282 5.35314-10.037138 0-2.67657-2.007427-4.683997-5.35314-5.35314zM113.754224 570.215397c-10.70628-3.345712-20.743417-8.02971-30.111412-14.721135-6.691425-8.02971-11.375422-16.728562-14.721135-26.7657-2.007427-5.35314-8.02971-7.360567-13.38285-5.35314-2.007427 1.338285-4.014855 3.345712-5.35314 5.35314-3.345712 10.037137-8.698852 18.73599-15.390277 26.7657C26.7657 562.185687 16.728562 566.869684 6.691425 570.215397c-4.683997 1.338285-7.360567 6.691425-6.022283 12.044565 0.669142 2.67657 3.345712 4.683997 6.022283 6.022282 10.037137 2.67657 19.405132 7.360567 28.103985 14.051993 6.691425 8.02971 12.044565 18.066847 15.390277 28.103984 2.007427 5.35314 8.02971 7.360567 13.38285 5.35314 2.67657-1.338285 4.014855-2.67657 5.35314-5.35314 3.345712-9.367995 8.02971-18.066847 14.051992-26.096557 9.367995-7.360567 19.405132-12.713707 30.780555-16.05942 4.683997-1.338285 7.360567-6.691425 6.022282-12.044565-1.338285-2.67657-3.345712-4.683997-6.022282-6.022282zM521.931145 361.442939c11.375422 0 20.074275-8.698852 20.074275-20.074275s-8.698852-20.074275-20.074275-20.074275-20.074275 8.698852-20.074275 20.074275 8.698852 20.074275 20.074275 20.074275z\" fill=\"#495FA9\" ></path><path d=\"M1017.096591 491.925725c0-114.423366-48.847402-223.493593-134.497642-300.44498-11.375422-10.037137-28.103985-8.698852-38.141122 2.007428-10.037137 11.375422-8.698852 28.103985 2.007428 38.141122 143.865636 129.144501 156.579344 349.961524 27.434842 494.496303-49.516545 55.538827-115.092509 93.679949-187.359898 109.070227-16.728562-44.163405-66.914249-66.914249-111.077654-50.185687-22.750845 8.698852-40.817692 26.096557-49.516545 48.847402C367.359229 796.38556 254.94329 655.196494 254.274148 491.925725c0-91.003379 35.464552-177.991903 99.033089-243.567868 42.155977 21.41256 94.349092 4.683997 115.761651-37.471979 6.022282-12.044565 9.367995-24.758272 9.367995-38.141122v-6.691425C519.923718 149.324768 564.087122 140.625916 608.919669 140.625916c14.721135 0 26.7657-12.044565 26.7657-26.7657s-12.044565-26.7657-26.7657-26.7657c-52.193115 0-103.717087 10.037137-151.895346 29.44227-31.449697-35.464552-85.650239-38.810265-121.783934-8.02971-18.066847 16.05942-28.773127 39.479407-29.442269 64.23768 0 13.38285 3.345712 26.096557 9.367995 38.141122C242.229583 285.829837 200.742748 386.870354 200.742748 491.925725c0 194.051323 138.512496 356.652949 322.526682 396.132357 12.713707 45.50169 60.891967 72.267389 106.393657 59.553682 28.103985-8.02971 50.85483-30.111412 58.884539-58.215397 187.359898-37.47198 328.548965-201.411891 328.548965-397.470642z\" fill=\"#495FA9\" ></path><path d=\"M661.112784 116.536786c-2.67657 14.721135 6.022282 28.773127 20.743417 31.449697 32.11884 6.691425 62.899394 18.066847 92.341664 32.787983 4.014855 2.007427 8.02971 3.345712 12.044565 3.345712 14.721135 0 26.7657-12.044565 26.096558-27.434842 0-9.367995-5.35314-18.73599-14.051993-23.419988-33.457125-17.397705-68.921677-30.111412-105.724514-37.471979-14.051992-2.67657-28.103985 6.691425-31.449697 20.743417z\" fill=\"#495FA9\" ></path></symbol><symbol id=\"icon-a-zanwushuju1\" viewBox=\"0 0 1024 1024\"><path d=\"M762.487623 112.597906a18.89876 18.89876 0 0 1-17.076648-4.502573 16.127945 16.127945 0 0 1-5.300688-12.0169c0-4.517632 1.897405-8.884676 5.300688-12.016901l47.043606-46.712312c3.162342-3.373165 7.454092-5.27057 12.137371-5.270571 4.66822 0 8.975028 1.897405 12.13737 5.270571 3.418341 3.132225 5.315747 7.378799 5.315747 12.0169 0 4.517632-1.897405 8.884676-5.315747 12.031959l-47.043605 46.697254c-2.906343 2.876226-4.29175 4.261633-7.198094 4.261633m105.441526 109.176101a14.11007 14.11007 0 0 1-7.198093 1.505877l-59.813445-7.002329a17.061589 17.061589 0 0 1-15.661123-18.416879c0.752939-9.381615 9.095499-16.263474 18.567466-15.510535l59.813445 5.616922c9.487027 0.888468 16.429121 9.020205 15.676182 18.416879 0 7.002329-4.547749 13.266779-11.384432 15.390065m-206.72683-131.463085c-4.29175 1.505877-9.095499 0.993879-13.146309-1.129408a16.865825 16.865825 0 0 1-8.222089-10.149612l-19.967933-56.590868A16.835708 16.835708 0 0 1 629.849954 0.90699c4.306809-1.505877 9.110557-1.114349 13.146309 1.008938 4.05081 2.123287 7.077623 5.752451 8.34256 10.134554L671.321814 68.671467a15.510536 15.510536 0 0 1-0.12047 12.769839c-1.897405 4.126104-5.571746 7.137858-9.999025 8.643735M437.53437 1015.386382H220.823573a14.290775 14.290775 0 0 1-14.290775-14.155246c0-7.755268 6.445155-14.140187 14.290775-14.140188h216.590326a13.311955 13.311955 0 0 1 10.360436 3.885164 13.206544 13.206544 0 0 1 3.915281 10.270083 14.155246 14.155246 0 0 1-14.155246 14.155246z m-216.710797-75.248687a14.290775 14.290775 0 0 1-14.290775-14.155246c0-7.755268 6.445155-14.155246 14.290775-14.155246l481.850606-1.35529c7.830562 0 14.305834 6.369861 14.305834 14.140188 0 7.755268-6.475272 14.155246-14.305834 14.155246l-481.850606 1.370348z\"  ></path><path d=\"M130.666702 357.272843c8.598559-3.011755 14.667245-10.767022 15.555712-19.787227-16.429121-50.958886-32.873301-102.053302-49.437951-152.997129-3.026813-9.532203-14.531716-14.411245-25.660148-10.902552-11.143492 3.508694-17.829587 14.019717-14.667245 23.536862 16.429121 51.455826 32.873301 102.926711 49.437951 154.503007a23.642273 23.642273 0 0 0 24.771681 5.647039z m131.372732 401.31629c8.598559-3.011755 14.667245-10.782081 15.555712-19.787227-16.44418-50.973945-32.873301-102.053302-49.437951-153.012189-3.041872-9.517144-14.546774-14.411245-25.675207-10.902551-11.113374 3.508694-17.814528 14.034776-14.652186 23.536862 16.429121 51.470885 32.873301 102.926711 49.422892 154.518065 6.324684 6.62586 16.06771 8.764206 24.78674 5.631981z m35.403174 98.665078c8.598559-3.011755 14.667245-10.782081 15.555712-19.787227-16.44418-50.958886-0.376469-0.752939-16.820649-51.711825-3.026813-9.517144-14.531716-14.411245-25.660148-10.902552-11.143492 3.508694-17.829587 14.034776-14.667245 23.536862 16.429121 51.470885 0.376469 1.761876 16.80559 53.217702 6.460213 6.640919 16.188181 8.764206 24.78674 5.64704z m-9.231028 2.740697c52.103353-15.902064 1.656465-0.752939 53.74476-16.639944 9.607497-2.891284 14.667245-14.411245 11.248903-25.419208-3.418341-11.023022-14.034776-17.66394-23.642273-14.787715-51.711825 15.661123-0.752939 0.135529-52.479822 15.902064a23.521803 23.521803 0 0 0-7.574563 23.92839 23.461568 23.461568 0 0 0 18.702995 17.016413zM195.780834 558.232164c8.598559-3.011755 14.667245-10.767022 15.555712-19.772169-16.429121-50.973945-32.873301-102.053302-49.43795-153.012188-3.026813-9.517144-14.531716-14.411245-25.660149-10.902551-11.143492 3.508694-17.829587 14.034776-14.667244 23.55192a106637.342844 106637.342844 0 0 0 49.43795 154.503007A23.898272 23.898272 0 0 0 195.765775 558.232164z m606.266185 124.716754c51.470885-17.904881 102.941769-35.689291 154.397595-53.594171 9.607497-3.252695 14.034776-14.908185 10.239966-25.795678-3.794811-10.887493-14.667245-17.151942-24.154271-13.899247-51.079357 17.54347-102.158713 35.192352-153.23807 52.720763a23.401333 23.401333 0 0 0-6.580684 24.16933c2.665403 8.764206 10.239965 15.149125 19.350523 16.414062zM397.854504 822.18233c51.455826-17.904881 102.911652-35.689291 154.382536-53.579113 9.607497-3.267754 14.034776-14.908185 10.239966-25.795678-3.794811-10.902551-14.667245-17.167001-24.154272-13.899247-51.079357 17.528411-102.158713 35.177293-153.238069 52.705704a23.401333 23.401333 0 0 0-6.580684 24.16933c2.665403 8.643735 10.239965 15.149125 19.350523 16.414062z m201.667082-67.990359c51.455826-17.904881 102.926711-35.689291 154.382537-53.594171 9.607497-3.252695 14.034776-14.893126 10.239965-25.780619-3.794811-10.902551-14.667245-17.167001-24.154271-13.899247-51.079357 17.528411-102.158713 35.177293-153.23807 52.705704a23.401333 23.401333 0 0 0-6.580683 24.16933 23.19051 23.19051 0 0 0 19.350522 16.414062zM381.274795 588.665943l54.121229-103.800119 121.885705 62.343318c5.044689 2.635285 10.872434 3.147283 16.308651 1.385407l0.496939-0.12047c5.571746-1.882347 10.119495-6.023509 12.528899-11.279021l129.716267-283.601864a21.037105 21.037105 0 0 0-10.616434-28.054493c-10.751964-4.743513-23.521803-0.12047-28.310493 10.526082L556.875142 499.26201l-120.861709-61.740967a21.503927 21.503927 0 0 0-28.837549 9.155733l-63.969666 122.578409a21.09734 21.09734 0 0 0 9.231028 28.551433c10.480906 5.27057 23.386274 1.249878 28.82249-9.140675z\"  ></path></symbol><symbol id=\"icon-kaishi\" viewBox=\"0 0 1024 1024\"><path d=\"M309.5 837.7c-9.4 0-18.8-2.5-27.3-7.4-17.1-9.9-27.3-27.6-27.3-47.3V241.1c0-19.8 10.2-37.4 27.3-47.3 17.1-9.9 37.5-9.9 54.7 0l469.3 270.9c17.1 9.9 27.3 27.6 27.3 47.3 0 19.8-10.2 37.4-27.3 47.3l-469.3 271c-8.6 4.9-18 7.4-27.4 7.4z m9.4-580.4v509.5L760.1 512 318.9 257.3z\"  ></path></symbol><symbol id=\"icon-guanbi\" viewBox=\"0 0 1024 1024\"><path d=\"M846.841 799.024L562.002 514.185l284.839-284.96c13.714-13.714 13.714-36.045 0-49.759s-36.045-13.714-49.759 0L512.243 464.305l-284.96-284.839c-13.714-13.714-36.045-13.714-49.759 0s-13.714 36.045 0 49.759l284.96 284.96-284.96 284.839c-13.714 13.714-13.714 36.045 0 49.759s36.045 13.714 49.759 0l284.96-284.96 284.96 284.96c13.714 13.714 36.045 13.714 49.759 0 13.593-13.714 13.593-36.045-0.122-49.759z\"  ></path></symbol><symbol id=\"icon-xiajiantou\" viewBox=\"0 0 1024 1024\"><path d=\"M573.056 752l308.8-404.608A76.8 76.8 0 0 0 820.736 224H203.232a76.8 76.8 0 0 0-61.056 123.392l308.8 404.608a76.8 76.8 0 0 0 122.08 0z\"  ></path></symbol><symbol id=\"icon-gengduo\" viewBox=\"0 0 1024 1024\"><path d=\"M465.9 263c0-12.6 4.2-23.1 12.6-31.5s19-12.7 31.9-12.7c12.5 0 22.8 4.2 31.1 12.7s12.4 19 12.4 31.5c0 12.2-4.1 22.7-12.4 31.5s-18.6 13.2-31.1 13.2c-12.8 0-23.4-4.4-31.9-13.2s-12.6-19.3-12.6-31.5z m0 250.5c0-12.2 4.2-22.6 12.6-31s19-12.7 31.9-12.7c12.5 0 22.8 4.2 31.1 12.7s12.4 18.8 12.4 31c0 12.5-4.1 23.2-12.4 32s-18.6 13.2-31.1 13.2c-12.8 0-23.4-4.4-31.9-13.2s-12.6-19.5-12.6-32z m0 253c0-12.5 4.2-23 12.6-31.3s19-12.5 31.9-12.5c12.5 0 22.8 4.1 31.1 12.5 8.3 8.3 12.4 18.7 12.4 31.3 0 12.5-4.1 23.2-12.4 32s-18.6 13.2-31.1 13.2c-12.8 0-23.4-4.4-31.9-13.2s-12.6-19.5-12.6-32z\"  ></path></symbol><symbol id=\"icon-shezhi\" viewBox=\"0 0 1024 1024\"><path d=\"M965.891707 605.946188c-47.321532-53.017642-47.321532-134.187214 0-185.397436 24.646631-26.454051 32.04062-66.162512 18.895751-100.284403-16.97879-43.542382-41.625421-83.141302-68.134243-123.014074-20.812711-28.371011-58.604212-41.625421-94.588293-34.121892a137.090039 137.090039 0 0 1-98.367443-15.1166 133.639511 133.639511 0 0 1-62.438132-77.499962A96.505253 96.505253 0 0 0 583.759385 4.294539a571.6923 571.6923 0 0 0-139.883324 0A96.395713 96.395713 0 0 0 366.321329 70.511821c-11.33745 32.150161-32.04062 60.575942-62.438132 77.499962a137.802053 137.802053 0 0 1-98.312673 15.1166 98.148362 98.148362 0 0 0-96.395713 34.121892 478.035102 478.035102 0 0 0-69.996432 121.042343 97.381578 97.381578 0 0 0 18.89575 100.284404c47.321532 53.017642 47.321532 134.187214 0 185.397436a97.326808 97.326808 0 0 0-18.89575 100.339173c16.97879 43.487611 41.625421 83.250843 69.996432 121.042344 22.674901 28.371011 58.604212 41.625421 94.588293 34.121891 69.941662-15.1166 139.883324 24.646631 160.750805 92.561792 11.446991 35.874541 41.625421 60.521172 77.499962 66.272053 22.674901 3.72438 47.321532 5.69611 69.996432 5.69611 22.62013 0 47.266761-1.97173 69.941662-3.83392a96.395713 96.395713 0 0 0 77.499963-66.162512c11.33745-32.150161 32.04062-60.575942 62.438132-77.499962a138.459296 138.459296 0 0 1 98.312672-15.116601 96.395713 96.395713 0 0 0 94.588293-34.121891 478.035102 478.035102 0 0 0 69.996433-121.042344 97.052956 97.052956 0 0 0-18.895751-100.284403z m-55.208453 51.045911c5.69611 5.750881 5.69611 15.1166 3.77915 20.812711a466.478571 466.478571 0 0 1-60.466402 102.201364 21.196103 21.196103 0 0 1-18.89575 5.69611c-52.908101-11.446991-107.733163-3.77915-153.192505 22.674901a204.183646 204.183646 0 0 0-96.395713 121.042343c-1.97173 7.5583-7.61307 11.33745-15.17137 13.14487-20.812711 1.97173-39.708461 3.83392-60.411632 3.83392-18.89575 0-39.763231-1.97173-58.604211-3.83392-7.61307-1.80742-13.19964-5.58657-15.171371-13.14487-33.957581-104.063553-141.855054-168.309105-249.588217-143.717244-5.750881 0-15.1166-1.91696-18.895751-7.5583a452.621687 452.621687 0 0 1-58.604211-102.146593 17.800345 17.800345 0 0 1 3.724379-18.840981 214.480461 214.480461 0 0 0 0-287.544029c-5.69611-5.750881-5.69611-13.14487-3.77915-20.81271a466.478571 466.478571 0 0 1 60.466402-102.201364 21.196103 21.196103 0 0 1 18.89575-5.69611c52.908101 11.33745 107.733163 3.77915 153.192505-22.674901a204.840889 204.840889 0 0 0 96.560024-121.042343c1.97173-7.5583 7.5583-11.33745 15.1166-13.14487a415.5422 415.5422 0 0 1 119.180154 0c7.5583 0 13.14487 5.58657 15.1166 13.14487a205.05997 205.05997 0 0 0 96.395713 121.042343c45.459342 26.454051 100.339174 34.121891 151.385085 22.674901 7.5583-1.97173 15.1166 0 18.89575 5.69611a429.946786 429.946786 0 0 1 60.466402 102.146593c1.97173 7.5583 1.97173 15.1166-4.162542 20.812711a214.480461 214.480461 0 0 0 0.164311 287.434488z\"  ></path><path d=\"M582.77352 639.465606a58.768523 58.768523 0 1 0 117.537045 0 58.768523 58.768523 0 0 0-117.537045 0z\"  ></path><path d=\"M512.010303 304.545275c-114.469909 0-207.469863 93.109495-207.469863 207.415093 0 114.305598 93.109495 207.469863 207.469863 207.469864a32.095391 32.095391 0 0 0 0-64.081241 143.498163 143.498163 0 0 1-143.388622-143.388623A143.388623 143.388623 0 0 1 512.010303 368.626516a143.498163 143.498163 0 0 1 143.333853 143.333852 32.095391 32.095391 0 0 0 64.08124 0c0-114.305598-93.109495-207.415093-207.415093-207.415093z\"  ></path></symbol><symbol id=\"icon-duihua-weixuan\" viewBox=\"0 0 1024 1024\"><path d=\"M512 46.545455c257.070545 0 465.454545 208.384 465.454545 465.454545s-208.384 465.454545-465.454545 465.454545S46.545455 769.070545 46.545455 512 254.929455 46.545455 512 46.545455z m0 93.090909a372.363636 372.363636 0 1 0 0 744.727272 372.363636 372.363636 0 0 0 0-744.727272z\"  ></path><path d=\"M418.909091 139.636364a372.363636 372.363636 0 1 1 0 744.727272A372.363636 372.363636 0 0 1 418.909091 139.636364z m0 93.090909a279.272727 279.272727 0 1 0 0 558.545454A279.272727 279.272727 0 0 0 418.909091 232.727273z\"  ></path><path d=\"M558.545455 279.272727a232.727273 232.727273 0 1 1 0 465.454546 232.727273 232.727273 0 0 1 0-465.454546z m0 93.090909a139.636364 139.636364 0 1 0 0 279.272728 139.636364 139.636364 0 0 0 0-279.272728z\"  ></path></symbol><symbol id=\"icon-tubiao-weixuan\" viewBox=\"0 0 1024 1024\"><path d=\"M791.272727 139.636364a186.181818 186.181818 0 0 1 186.181818 186.181818v372.363636a186.181818 186.181818 0 0 1-186.181818 186.181818H232.727273a186.181818 186.181818 0 0 1-186.181818-186.181818V325.818182a186.181818 186.181818 0 0 1 186.181818-186.181818h558.545454z m0 93.090909H232.727273a93.090909 93.090909 0 0 0-92.858182 86.109091L139.636364 325.818182v372.363636a93.090909 93.090909 0 0 0 86.109091 92.858182L232.727273 791.272727h558.545454a93.090909 93.090909 0 0 0 92.858182-86.109091L884.363636 698.181818V325.818182a93.090909 93.090909 0 0 0-86.109091-92.858182L791.272727 232.727273z\"  ></path><path d=\"M870.586182 278.993455a46.545455 46.545455 0 0 1 68.980363 62.277818l-3.909818 4.328727-246.225454 240.546909a177.198545 177.198545 0 0 1-249.576728-1.861818l-6.842181-7.354182-10.891637-12.241454a93.090909 93.090909 0 0 0-113.757091-20.247273l-6.981818 4.189091-171.752727 112.64a46.545455 46.545455 0 0 1-55.389091-74.658909l4.328727-3.258182 171.799273-112.64a186.181818 186.181818 0 0 1 234.682182 24.994909l6.702545 7.214545 10.891637 12.241455a84.107636 84.107636 0 0 0 118.737454 7.121454l249.204364-243.29309z\"  ></path></symbol><symbol id=\"icon-a-bianzu13beifen3\" viewBox=\"0 0 1024 1024\"><path d=\"M768 179.2c113.1136 0 204.8 83.7632 204.8 187.0848S881.1136 563.2 768 563.2l-33.3952-1.8304c5.12-22.4256 7.7952-45.76 7.7952-69.76 0-10.752-0.5376-21.4144-1.5872-31.936 65.92-2.752 99.904-49.7536 99.904-96 0-46.2208-39.7824-83.968-89.7664-86.208l-4.7232-0.1024h-84.7872a356.1728 356.1728 0 0 0-139.0976-98.176L768 179.2z\"  ></path><path d=\"M768 844.8c113.1136 0 204.8-85.9648 204.8-192 0-106.0352-91.6864-192-204.8-192h-34.3552c5.7344 23.7568 8.7552 48.512 8.7552 74.0096 0 9.9072-0.4608 19.712-1.3568 29.4144l-2.8928-0.0384c52.1728 0 102.5664 42.368 102.5664 91.2896 0 47.4368-39.7824 86.1696-89.7664 88.4736l-4.7232 0.1024h-81.3568c-37.056 43.8656-85.5808 78.8352-141.5936 100.7616L768 844.8z\"  ></path><path d=\"M137.9712 856.832a38.4 38.4 0 0 1-32-52.4032l162.432-409.216a112.832 112.832 0 0 1 72.832 149.4016l-76.16 193.9072c17.8048 4.224 36.3776 6.464 55.4752 6.464 132.096 0 239.1936-107.0976 239.1936-239.1936s-107.0848-239.1936-239.1936-239.1936c-132.096 0-239.1936 107.0848-239.1936 239.1936l0.0256 3.52c-34.88-34.3552-68.864-133.0304-2.8672-230.656l2.8416-4.1216c1.5744-2.2144 3.008-4.288 4.3264-6.208C142.976 198.9504 223.744 153.6 320 153.6c196.2624 0 355.3664 159.104 355.3664 355.3664 0 196.2624-159.104 355.3536-355.3664 355.3536l-4.544 0.0128c-5.9904 0.0256-9.6512 0.128-12.8 0.2304l-1.4464 0.0384-2.112 0.0768-1.4336 0.0512c-3.4176 0.1152-7.424 0.2176-14.3232 0.256h-10.6496c-11.904-0.0512-29.5424-0.2432-57.0368-0.6656l-77.696-7.5008z\"  ></path></symbol><symbol id=\"icon-bianzubeifen\" viewBox=\"0 0 1609 1024\"><path d=\"M658.238697 0v146.275266L511.963431 146.348404a365.688165 365.688165 0 0 0-15.870866 731.010642L511.963431 877.724734l146.275266-0.073138v146.275266L511.963431 1024A511.963431 511.963431 0 0 1 511.963431 0.073138l146.275266-0.073138zM1097.064495 0.073138a511.963431 511.963431 0 0 1 0 1023.926862l-146.275266-0.073138v-146.275266L1097.064495 877.724734a365.688165 365.688165 0 0 0 15.870867-731.010642L1097.064495 146.348404l-146.275266-0.073138v-146.275266L1097.064495 0.073138z\"  ></path><path d=\"M438.825798 438.898936m73.137633 0l585.101064 0q73.137633 0 73.137633 73.137633l0 0q0 73.137633-73.137633 73.137633l-585.101064 0q-73.137633 0-73.137633-73.137633l0 0q0-73.137633 73.137633-73.137633Z\"  ></path></symbol><symbol id=\"icon-biaoge1\" viewBox=\"0 0 1024 1024\"><path d=\"M1024 365.824V146.346667C1024 65.536 958.464 0 877.653333 0h-731.306666C65.536 0 0 65.536 0 146.346667v731.306666C0 958.464 65.536 1024 146.346667 1024h731.306666c80.810667 0 146.346667-65.536 146.346667-146.346667V365.824zM94.549333 94.549333a72.874667 72.874667 0 0 1 51.797334-21.504h731.562666a73.130667 73.130667 0 0 1 73.045334 73.386667V292.693333H73.045333V146.346667c0-19.456 7.68-38.058667 21.504-51.797334z m307.626667 527.36v-256h219.648v256H402.176z m219.648 73.045334v256H402.176v-256h219.648z m-292.693333-73.130667h-256v-256h256v256z m-234.581334 307.712a72.533333 72.533333 0 0 1-21.504-51.626667V694.954667h256v256h-182.613333a72.704 72.704 0 0 1-51.882667-21.333334z m856.405334-51.626667a72.874667 72.874667 0 0 1-73.045334 73.045334H694.954667v-256h256v182.954666z m0-256h-256v-256h256v256z\"  ></path></symbol><symbol id=\"icon-a-shoucang1\" viewBox=\"0 0 1049 1024\"><path d=\"M178.413544 978.289685a95.168889 95.168889 0 0 1-11.024292-70.645464l57.146331-243.88434a16.19896 16.19896 0 0 0-4.949682-15.823984L32.39792 484.746375a95.018899 95.018899 0 0 1-10.12435-132.516491c15.973974-19.048777 38.847506-30.898016 63.670912-32.922886l245.684224-20.023715a14.774051 14.774051 0 0 0 12.299211-9.299403L438.72183 58.39875a93.069024 93.069024 0 0 1 172.638915 0l94.718918 231.510135c2.17486 5.399653 6.974552 8.999422 12.299211 9.374398l245.75922 20.023715a94.043962 94.043962 0 0 1 85.569505 101.54348c-1.949875 24.748411-13.499133 47.621942-32.097939 63.970892l-187.187981 163.114527a16.123965 16.123965 0 0 0-4.949682 15.823984l57.146331 243.88434a94.343942 94.343942 0 0 1-68.99557 113.467714 92.619053 92.619053 0 0 1-70.795454-11.324272L532.315821 878.996061a13.874109 13.874109 0 0 0-14.774052 0l-210.361493 130.716606c-44.172164 27.448238-101.993451 13.199152-128.766732-31.497977z\"  ></path></symbol><symbol id=\"icon-guthub-weixuan1\" viewBox=\"0 0 1051 1024\"><path d=\"M525.983545 0A525.516056 525.516056 0 0 0 0 525.516056c0 229.653964 147.259031 424.830616 352.311387 496.473303 27.58185 6.953899 23.374449-12.680639 23.374449-26.062511v-91.101916c-159.47218 18.699559-165.95859-86.836079-176.593964-104.425352-21.621366-36.93163-72.752973-46.339846-57.501145-63.929119 36.347269-18.699559 73.337335 4.67489 116.229449 68.019647 30.971145 45.930793 91.569405 38.217225 122.189933 30.562093 6.720154-27.640286 21.037004-52.30033 40.788414-71.467379-165.19892-29.627114-234.09511-130.429427-234.095109-250.281916 0-58.202379 19.167048-111.612995 56.799911-154.79729-23.958811-71.058326 2.220573-131.948766 5.726741-141.006365 68.311828-6.077357 139.253282 48.911035 144.804713 53.235308 38.74315-10.460066 83.037731-15.953062 132.65-15.953062 49.846013 0 94.257467 5.72674 133.351233 16.362115 13.323436-10.167885 79.122511-57.384273 142.584141-51.657533 3.389295 9.057599 28.984317 68.545573 6.427973 138.66892 38.100352 43.242731 57.501145 97.179273 57.501145 155.440088 0 120.144669-69.363678 221.063854-235.030087 250.22348 27.58185 27.28967 44.76207 65.097841 44.76207 106.938105v132.182511c0.934978 10.576938 0 21.037004 17.647709 21.037004C902.019998 953.852839 1051.850218 757.332157 1051.850218 525.632928 1051.850218 235.14696 816.352641 0 525.983545 0z\"  ></path></symbol><symbol id=\"icon-shuju-weixuan\" viewBox=\"0 0 1033 1024\"><path d=\"M930.103169 350.663203l4.130309-3.923793c40.063994-39.083045 71.454339-78.424235 87.201141-114.048147 7.22804 5.679174 11.151833 13.991421 11.151833 25.453027l0.103258 61.903v117.868683L1032.586452 452.578569v55.13962c0 142.547277-231.194026 258.14429-516.288579 258.14429-285.197811 0-516.288579-115.597013-516.28858-258.14429 0-52.196775 1.80701-122.205507 0-251.277652-0.206515-12.803957 2.942845-22.406924 8.931793-29.273562 15.643544 38.41187 50.234879 82.141513 94.635696 125.613011v154.886574c0 85.239244 165.418861 154.938203 413.289008 154.938203 247.818518 0 412.669462-69.698958 412.669462-154.886574v-10.325772l0.516288-129.794948v-16.934266z\"  ></path><path d=\"M1032.586452 270.380329v22.200409l0.103258 40.631911v228.870727L1032.586452 664.515031v101.295819c0 142.598906-231.194026 258.14429-516.288579 258.14429-285.197811 0-516.340208-115.545384-516.340209-258.14429V258.14429l5.369402 1.755381c13.216988 40.68354 49.873477 88.336976 98.249716 135.62901v370.333798c0 85.187616 165.418861 154.886574 413.289008 154.886574 247.818518 0 412.669462-69.698958 412.669462-154.886574v-7.434556-26.692119c0.154887-50.751167 0.413031-146.884101 0.516288-245.237076v-32.784324l0.051629-16.366348V392.37932l0.103258 0.92932c50.18325-48.169724 87.459285-97.010624 98.766005-138.984886a33.662015 33.662015 0 0 1 3.61402 16.056575z\"  ></path><path d=\"M516.297873 0c285.146182 0 516.288579 115.597013 516.288579 258.14429s-231.142397 258.14429-516.288579 258.144289S0.009293 400.691567 0.009293 258.14429s231.142397-258.14429 516.28858-258.14429z m0 89.52444c-230.161449 0-412.617833 88.543491-412.617833 167.69053 0 79.09541 182.766157 156.074038 412.927606 156.074038s412.979235-76.978627 412.979235-156.074038c0-79.147039-183.127559-167.690531-413.289008-167.69053z\"  ></path></symbol><symbol id=\"icon-a-bianzu4\" viewBox=\"0 0 1065 1024\"><path d=\"M529.747202 181.110154c9.765744 33.558646 41.371241 55.220841 74.841107 55.220842h318.984699a79.013744 79.013744 0 0 0-78.747406-78.836185H521.845827l7.901375 23.704123zM498.230483 78.836185h346.595119a158.027488 158.027488 0 0 1 157.494811 157.58359v31.516718c37.464944 29.47479 63.033436 74.841108 63.033436 126.066872V866.487797a158.027488 158.027488 0 0 1-157.49481 157.494811H157.494811A158.027488 158.027488 0 0 1 0 866.487797V157.494811A158.027488 158.027488 0 0 1 157.494811 0H360.444719c57.08521 0 110.264123 31.516718 137.874544 78.747405z m-137.874543 0H157.494811a79.013744 79.013744 0 0 0-78.747406 78.747405V866.487797c0 43.32439 35.511795 78.747405 78.747406 78.747406h748.411079a79.013744 79.013744 0 0 0 78.747405-78.747406V393.825806a79.013744 79.013744 0 0 0-78.836185-78.747405H608.494607c-68.892882 0-129.97317-45.277539-149.682216-110.264123l-23.615344-70.934811a76.26158 76.26158 0 0 0-74.841107-55.043282zM236.330996 748.411079H551.320617c21.662195 0 39.329313 17.755897 39.329313 39.329313 0 21.662195-17.755897 39.418092-39.329313 39.418092H236.153437a39.506872 39.506872 0 0 1-39.329313-39.418092c0-21.662195 17.755897-39.329313 39.418092-39.329313z\"  ></path></symbol><symbol id=\"icon-a-bianzu14beifen\" viewBox=\"0 0 1050 1024\"><path d=\"M307.295051 1009.787663c-44.172164 27.448238-101.993451 13.199152-128.766732-31.497978a95.168889 95.168889 0 0 1-11.024292-70.645464l57.14633-243.88434a16.19896 16.19896 0 0 0-4.949682-15.823984L32.512694 484.746375a95.018899 95.018899 0 0 1-10.12435-132.516491c15.973974-19.048777 38.847506-30.898016 63.670912-32.922886l245.684225-20.023715a14.774051 14.774051 0 0 0 12.29921-9.299403L438.836605 58.39875a93.069024 93.069024 0 0 1 172.638915 0l94.718918 231.510135c2.17486 5.399653 6.974552 8.999422 12.29921 9.374398l245.75922 20.023715a94.043962 94.043962 0 0 1 85.569506 101.54348c-1.949875 24.748411-13.499133 47.621942-32.097939 63.970892l-187.187981 163.114527a16.123965 16.123965 0 0 0-4.949682 15.823984l57.146331 243.88434a94.343942 94.343942 0 0 1-68.99557 113.467714 92.619053 92.619053 0 0 1-70.795455-11.324272L532.430595 878.996061a13.874109 13.874109 0 0 0-14.774051 0l-210.361493 130.716606z m266.757872-197.612312l210.361493 130.716607a13.949104 13.949104 0 0 0 10.649316 1.724889 15.673994 15.673994 0 0 0 10.949297-18.973782L748.866698 681.758725a94.943904 94.943904 0 0 1 29.848083-93.219015l187.187981-163.114526a15.973974 15.973974 0 0 0 5.324658-10.799307 15.299018 15.299018 0 0 0-13.499133-16.798921l-245.684225-20.09871a93.518995 93.518995 0 0 1-78.819939-57.971277L538.505205 88.246834a14.32408 14.32408 0 0 0-26.923271 0L417.013006 319.756969a93.518995 93.518995 0 0 1-78.744944 57.971277l-245.75922 20.09871a14.399075 14.399075 0 0 0-9.824369 5.099672 16.273955 16.273955 0 0 0 1.649894 22.498556l187.187981 163.114526c26.4733 23.173512 37.947563 59.02121 29.923078 93.219015l-57.221325 243.88434a16.423945 16.423945 0 0 0 1.874879 12.14922 14.17409 14.17409 0 0 0 19.723734 5.099673l210.436488-130.716607c29.998074-18.673801 67.870642-18.673801 97.868716 0z\"  ></path></symbol><symbol id=\"icon-guthub-weixuan\" viewBox=\"0 0 1051 1024\"><path d=\"M525.983545 0A525.516056 525.516056 0 0 0 0 525.516056c0 229.653964 147.259031 424.830616 352.311387 496.473303 27.58185 6.953899 23.374449-12.680639 23.374449-26.062511v-91.101916c-159.47218 18.699559-165.95859-86.836079-176.593964-104.425352-21.621366-36.93163-72.752973-46.339846-57.501145-63.929119 36.347269-18.699559 73.337335 4.67489 116.229449 68.019647 30.971145 45.930793 91.569405 38.217225 122.189933 30.562093 6.720154-27.640286 21.037004-52.30033 40.788414-71.467379-165.19892-29.627114-234.09511-130.429427-234.095109-250.281916 0-58.202379 19.167048-111.612995 56.799911-154.79729-23.958811-71.058326 2.220573-131.948766 5.726741-141.006365 68.311828-6.077357 139.253282 48.911035 144.804713 53.235308 38.74315-10.460066 83.037731-15.953062 132.65-15.953062 49.846013 0 94.257467 5.72674 133.351233 16.362115 13.323436-10.167885 79.122511-57.384273 142.584141-51.657533 3.389295 9.057599 28.984317 68.545573 6.427973 138.66892 38.100352 43.242731 57.501145 97.179273 57.501145 155.440088 0 120.144669-69.363678 221.063854-235.030087 250.22348 27.58185 27.28967 44.76207 65.097841 44.76207 106.938105v132.182511c0.934978 10.576938 0 21.037004 17.647709 21.037004C902.019998 953.852839 1051.850218 757.332157 1051.850218 525.632928 1051.850218 235.14696 816.352641 0 525.983545 0z\"  ></path></symbol><symbol id=\"icon-24gl-folderMinus\" viewBox=\"0 0 1024 1024\"><path d=\"M970.666667 213.333333H546.586667a10.573333 10.573333 0 0 1-7.54-3.126666L429.793333 100.953333A52.986667 52.986667 0 0 0 392.08 85.333333H96a53.393333 53.393333 0 0 0-53.333333 53.333334v704a53.393333 53.393333 0 0 0 53.333333 53.333333h874.666667a53.393333 53.393333 0 0 0 53.333333-53.333333V266.666667a53.393333 53.393333 0 0 0-53.333333-53.333334z m10.666666 629.333334a10.666667 10.666667 0 0 1-10.666666 10.666666H96a10.666667 10.666667 0 0 1-10.666667-10.666666V138.666667a10.666667 10.666667 0 0 1 10.666667-10.666667h296.08a10.573333 10.573333 0 0 1 7.54 3.126667l109.253333 109.253333A52.986667 52.986667 0 0 0 546.586667 256H970.666667a10.666667 10.666667 0 0 1 10.666666 10.666667z m-298.666666-309.333334a21.333333 21.333333 0 0 1-21.333334 21.333334H405.333333a21.333333 21.333333 0 0 1 0-42.666667h256a21.333333 21.333333 0 0 1 21.333334 21.333333z\"  ></path></symbol><symbol id=\"icon-24gl-folderOpen\" viewBox=\"0 0 1024 1024\"><path d=\"M1003.153333 404.96a52.933333 52.933333 0 0 0-42.38-20.96H896V266.666667a53.393333 53.393333 0 0 0-53.333333-53.333334H461.253333a10.573333 10.573333 0 0 1-7.54-3.126666L344.46 100.953333A52.986667 52.986667 0 0 0 306.746667 85.333333H53.333333a53.393333 53.393333 0 0 0-53.333333 53.333334v704a53.393333 53.393333 0 0 0 53.333333 53.333333h796.893334a53.453333 53.453333 0 0 0 51.453333-39.333333l110.546667-405.333334a52.953333 52.953333 0 0 0-9.073334-46.373333zM53.333333 128h253.413334a10.573333 10.573333 0 0 1 7.54 3.126667l109.253333 109.253333A52.986667 52.986667 0 0 0 461.253333 256H842.666667a10.666667 10.666667 0 0 1 10.666666 10.666667v117.333333H173.773333a53.453333 53.453333 0 0 0-51.453333 39.333333L42.666667 715.366667V138.666667a10.666667 10.666667 0 0 1 10.666666-10.666667z m917.726667 312.14l-110.546667 405.333333a10.666667 10.666667 0 0 1-10.286666 7.86H63.226667a10.666667 10.666667 0 0 1-10.286667-13.473333l110.546667-405.333333A10.666667 10.666667 0 0 1 173.773333 426.666667h787a10.666667 10.666667 0 0 1 10.286667 13.473333z\"  ></path></symbol><symbol id=\"icon-24gf-folderOpen\" viewBox=\"0 0 1024 1024\"><path d=\"M81.16 412.073333L0 709.653333V138.666667a53.393333 53.393333 0 0 1 53.333333-53.333334h253.413334a52.986667 52.986667 0 0 1 37.713333 15.62l109.253333 109.253334a10.573333 10.573333 0 0 0 7.54 3.126666H842.666667a53.393333 53.393333 0 0 1 53.333333 53.333334v74.666666H173.773333a96.2 96.2 0 0 0-92.613333 70.74z m922-7.113333a52.933333 52.933333 0 0 0-42.386667-20.96H173.773333a53.453333 53.453333 0 0 0-51.453333 39.333333L11.773333 828.666667a53.333333 53.333333 0 0 0 51.453334 67.333333h787a53.453333 53.453333 0 0 0 51.453333-39.333333l110.546667-405.333334a52.953333 52.953333 0 0 0-9.073334-46.373333z\"  ></path></symbol><symbol id=\"icon-yunshujuku\" viewBox=\"0 0 1024 1024\"><path d=\"M890.3 755.5C870.7 834.2 704.8 895.6 503 895.6s-367.8-61.4-387.3-140.1h-2V272.8c0-86 174.3-155.7 389.3-155.7s389.3 69.7 389.3 155.7v482.7h-2zM503 148.2c-197.8 0-358.2 55.8-358.2 124.6S305.2 397.4 503 397.4s358.1-55.8 358.1-124.6S700.8 148.2 503 148.2z m358.1 185.6c-59.4 55.6-197.3 94.7-358.1 94.7s-298.8-39-358.2-94.7v94.7c0 68.8 160.4 124.6 358.2 124.6s358.1-55.8 358.1-124.6v-94.7z m0 155.8c-59.4 55.6-197.3 94.7-358.1 94.7s-298.8-39-358.2-94.7v94.7c0 68.8 160.4 124.6 358.2 124.6S861.1 653 861.1 584.2v-94.6z m0 155.7c-59.4 55.6-197.3 94.6-358.1 94.6s-298.8-39-358.2-94.6v94.6c0 68.8 160.4 124.6 358.2 124.6s358.1-55.8 358.1-124.6v-94.6z m-77.8 79.1l31.1-15.6v46.7l-31.1 15.6v-46.7zM175.9 285.9v-18.2c56 48.9 181.3 82.9 327 82.9s271.1-34 327-82.9v18.2c-63.8 47.9-186.3 80.3-327 80.3s-263.2-32.4-327-80.3z\"  ></path></symbol><symbol id=\"icon-baobiao\" viewBox=\"0 0 1024 1024\"><path d=\"M948.9 787.5h-47V234.4c0-10.7-8.7-19.4-19.4-19.4H142.1c-10.7 0-19.4 8.7-19.4 19.4v553.1H75.1c-5.9 0-10.8 4.8-10.8 10.8 0 5.9 4.8 10.8 10.8 10.8H948.9c5.9 0 10.8-4.8 10.8-10.8 0-6-4.9-10.8-10.8-10.8z m-68.6 0h-736v-551h736v551z\"  ></path><path d=\"M196.5 637.8H472v21.5H196.5zM196.5 706.7h344.3v21.5H196.5zM573.6 515.7l5.4 9.3 5.4 9.3 0.4-0.2c26.6 38.8 70.1 60.3 114.8 60.3 23.6 0 47.4-6 69.2-18.6 66.3-38.3 89.2-123.4 50.9-189.8-36.5-63.2-115.5-86.9-180.3-55.9l-0.2-0.4-9.3 5.4-9.3 5.4 0.2 0.4c-56.3 38.6-76.2 113-46.8 174.5l-0.4 0.3zM801 396.8c32.4 56.1 13.1 128-43 160.4-52.9 30.6-120 15.1-154.6-33.9L714 459.4l-63.8-110.6c54.4-25.1 120.2-4.9 150.8 48z m-169.5-37.2l53.1 91.9-91.9 53.1c-23.7-51.2-7.2-112.4 38.8-145z\"  ></path></symbol><symbol id=\"icon-gongzuotai\" viewBox=\"0 0 1024 1024\"><path d=\"M257 538.3l126.4-128.6 187.7 90.6c4.8 2.3 10.5 1.6 14.5-1.8l198.7-164.8c5.8-4.8 6.6-13.3 1.8-19.1-4.8-5.8-13.3-6.6-19.1-1.8L575 472.1 386.4 381c-5.2-2.5-11.5-1.4-15.6 2.7L237.7 519.3c-5.2 5.3-5.2 13.9 0.2 19.2 2.6 2.6 6.1 3.9 9.5 3.9 3.5-0.1 7-1.4 9.6-4.1zM804.1 809.5H219c-7.5 0-13.5 6.1-13.5 13.5s6.1 13.5 13.5 13.5h585.2c7.5 0 13.5-6.1 13.5-13.5s-6.1-13.5-13.6-13.5z\"  ></path><path d=\"M851.4 187H171.7c-20.2 0-36.6 16.4-36.6 36.6v243.9c0 7.5 6.1 13.5 13.5 13.5s13.5-6.1 13.5-13.5V223.6c0-5.2 4.2-9.5 9.5-9.5h679.7c5.2 0 9.5 4.3 9.5 9.5v445.3c0 5.2-4.2 9.5-9.5 9.5H171.7c-5.2 0-9.5-4.3-9.5-9.5v-93.1-0.7c0-7.5-6.1-13.5-13.5-13.5s-13.5 6.1-13.5 13.5v93.8c0 20.2 16.4 36.6 36.6 36.6h679.7c20.2 0 36.6-16.4 36.6-36.6V223.6c-0.2-20.2-16.5-36.6-36.7-36.6z\"  ></path></symbol><symbol id=\"icon-mongodb\" viewBox=\"0 0 1024 1024\"><path d=\"M733.013333 406.101333c-53.888-237.226667-180.992-315.178667-194.645333-345.002666C523.349333 40.064 507.093333 0 507.093333 0l-0.213333 2.090667v0.554666h-0.042667a24.490667 24.490667 0 0 0-0.170666 1.664v0.64h-0.085334l-0.085333 1.109334v1.109333h-0.128c-0.042667 0.341333-0.042667 0.768-0.128 1.066667v0.896h-0.085333c0 0.298667 0 0.64-0.085334 0.896v0.853333h-0.085333l-0.085333 1.365333v0.085334l-0.384 2.133333v0.341333h-0.085334l-0.128 0.512v0.725334h-0.128v0.938666h-0.213333v0.768h-0.213333v0.896h-0.170667v0.810667h-0.170667v0.725333h-0.256v0.597334h-0.170666v0.768h-0.170667v0.597333h-0.213333v0.554667H503.466667v0.64h-0.170667l-0.042667 0.170666v0.426667h-0.128l-0.042666 0.256v0.256h-0.085334a1.877333 1.877333 0 0 0-0.085333 0.426667l-0.426667 0.896v0.085333a1.28 1.28 0 0 0-0.213333 0.298667v0.341333h-0.170667v0.341333h-0.213333v0.341334h-0.128v0.426666h-0.256v0.597334h-0.170667v0.170666h-0.170666v0.341334h-0.170667v0.469333h-0.170667v0.341333h-0.256v0.469334h-0.170666v0.341333h-0.213334v0.341333h-0.128v0.426667h-0.213333v0.341333h-0.170667v0.256h-0.170666v0.341334h-0.256V32.426667h-0.170667v0.256h-0.213333v0.341333h-0.170667v0.469333h-0.213333v0.170667h-0.128v0.341333h-0.256v0.170667h-0.170667v0.426667h-0.170667v0.170666h-0.170666v0.341334h-0.213334v0.256h-0.128l-0.085333 0.170666v0.170667h-0.085333l-0.085334 0.170667v0.042666h-0.042666a0.682667 0.682667 0 0 1-0.170667 0.298667v0.128h-0.042667L496.213333 37.12v0.042667c-0.085333 0.085333-0.298667 0.256-0.384 0.426666v0.085334h-0.042666l-0.128 0.128v0.128h-0.085334l-0.128 0.128v0.042666h-0.042666l-0.128 0.170667v0.170667h-0.128l-0.085334 0.085333v0.085333h-0.085333c0 0.085333-0.085333 0.085333-0.085333 0.128v0.128h-0.170667l-0.085333 0.128V39.253333h-0.128v0.170667h-0.170667V39.68h-0.170667v0.341333h-0.213333V39.68h-0.213333v0.170667h-0.170667V40.106667h-0.213333v0.341333h-0.213334v0.170667h-0.170666v0.256h-0.170667v0.170666h-0.170667V41.386667h-0.256v0.170666h-0.170666V41.813333h-0.213334v0.170667h-0.170666v0.213333h-0.213334v0.426667h-0.085333v0.170667h-0.256v0.213333h-0.170667v0.085333h-0.170666v0.170667h-0.213334v0.426667h-0.170666v0.170666h-0.213334v0.170667h-0.170666v0.256h-0.213334v0.170667h-0.213333v0.170666h-0.170667v0.170667h-0.170666v0.426667h-0.170667v0.213333h-0.256v0.170667h-0.170667v0.170666h-0.213333v0.256h-0.170667v0.170667h-0.213333v0.298667h-0.170667v0.170666h-0.256V46.933333h-0.085333v0.170667h-0.170667v0.170667h-0.213333v0.170666h-0.170667v0.256h-0.213333v0.170667h-0.128l-0.042667 0.085333v0.085334h-0.085333l-0.170667 0.170666-0.170666 0.128v0.256h-0.170667v0.213334h-0.170667v0.170666h-0.170666v0.170667h-0.128l-0.128 0.128v0.128h-0.085334l-0.085333 0.085333v0.128h-0.085333c-0.213333 0.256-0.298667 0.426667-0.597334 0.682667a15.616 15.616 0 0 0-2.176 1.792l-0.938666 0.725333v0.042667h-0.042667a120.362667 120.362667 0 0 1-2.090667 1.664v0.042667l-3.157333 2.645333V57.173333h-0.085333c-2.432 2.005333-4.992 4.266667-7.936 6.784V64h-0.042667c-7.210667 6.314667-15.786667 14.421333-25.386667 24.234667l-0.64 0.64-0.170666 0.170666C384 149.077333 292.565333 274.176 282.922667 476.330667c-0.853333 16.725333-0.682667 32.981333 0.256 48.810666v0.384c4.650667 79.658667 29.653333 147.669333 60.928 202.922667v0.042667c12.458667 22.016 25.898667 42.026667 39.509333 59.946666v0.042667c47.018667 62.08 95.018667 98.858667 107.264 107.776 18.816 43.648 17.066667 118.570667 17.066667 118.570667l27.477333 9.173333s-5.589333-72.576 2.261333-107.605333c2.432-10.965333 8.192-20.309333 14.890667-28.245334a357.546667 357.546667 0 0 0 34.005333-27.52c0.768-0.810667 1.194667-1.536 1.877334-2.304 64.896-60.501333 186.112-209.493333 144.554666-452.224z\"  ></path></symbol><symbol id=\"icon-Redis\" viewBox=\"0 0 1024 1024\"><path d=\"M497.800533 540.458667l433.809067-210.722134v-47.982933L497.92 71.202133 65.348267 282.914133l-0.725334 47.1296 433.1776 210.414934zM0 369.800533L1.962667 242.688 497.800533 0l497.809067 241.681067v128.119466L497.800533 611.6096 0 369.800533z\"  ></path><path d=\"M99.797333 388.1728L1.962667 436.053333 0 563.182933l497.800533 241.800534 497.809067-241.800534V435.063467L890.436267 384l-71.611734 36.3776 112.785067 54.7584v47.982933L497.800533 733.832533 64.622933 523.4176l0.725334-47.1296L173.687467 423.253333z\"  ></path><path d=\"M99.797333 580.1728L1.962667 628.053333 0 755.182933l497.800533 241.800534 497.809067-241.800534V627.063467L890.436267 576l-71.611734 36.3776 112.785067 54.7584v47.982933l-433.809067 210.7136L64.622933 715.4176l0.725334-47.1296L173.687467 615.253333z\"  ></path></symbol><symbol id=\"icon-HIVE\" viewBox=\"0 0 1024 1024\"><path d=\"M855.673849 554.023043c-5.716593 2.797482-11.676446 5.230075-17.39304 8.027557-11.919705 5.716593-23.839411 8.392446-35.880746 0.851407l0.243259-0.243259c13.500891-3.770519 27.4883-8.149186 41.840599-11.433187 3.284-0.851408 7.297779 1.581185 11.189928 2.797482zM580.912475 834.37938c2.554223 6.81126 10.703409 17.757928 8.270817 25.177337-4.378667 12.892743-19.095855 10.46015-30.164153 8.635705-18.244447-2.797482-36.124005-7.297779-54.246823-11.311557-7.662668-4.500297-15.446965-9.243853-23.352892-13.98741 16.541632 0 32.475116 0.973037 48.28697-0.243259 15.933484-1.337926 31.745338-4.986816 51.206081-8.270816zM548.31573 787.308706c5.230075 5.959853 8.878964 13.257632 13.987409 21.406818-47.192303 6.081482-91.343865 2.797482-133.306093-17.14978-12.284594-5.838223-23.961041-13.257632-36.002375-19.947262-4.257038-9.973631-16.541632-33.448153-20.9203-43.665043 27.4883 12.162965 52.544008 25.907115 79.42416 34.29956 26.880152 8.392446 55.706378 11.311557 83.437938 17.14978 5.108445 1.216296 9.852001 4.013778 13.379261 7.905927zM537.97721 711.411807c-1.946074 10.46015-3.892149 20.312151-5.838223 30.407411-1.337926 0.364889-2.675852 1.581185-3.892149 1.337927-64.950232-12.284594-122.724314-39.043117-170.038247-87.330087-2.189334-2.189334-4.257038-5.838223-4.257037-8.757335-0.486519-21.041929-0.12163-42.205488-0.12163-63.490676 1.581185-0.243259 3.284-0.729778 4.865186-0.973037 36.245635 76.991567 103.020311 109.466683 179.2821 128.805797zM547.585952 686.720988c-2.189334 0.486519-7.176149 3.284-9.365483 1.824445-39.772895-25.420596-82.221642-47.070673-112.507423-85.627272-18.974225-24.08267-28.218078-52.422378-32.718375-82.70816-1.459556-9.365483 1.702815-15.203706 9.365482-18.730965 12.041335-5.594964 24.2043-10.703409 37.461932-16.420003 7.662668 20.433781 13.74415 39.529635 22.258225 57.774083 15.446965 33.204894 31.258819 66.409787 49.260007 98.398384 8.878964 16.055113 22.866374 29.069486 36.245635 45.489488z\"  ></path><path d=\"M856.403626 554.266302c-0.243259 14.595558 1.702815 29.677634-1.337926 43.665044-7.05452 33.204894-22.501485 63.855565-44.881339 89.276161-27.123411 30.89393-63.369046 31.745338-94.749496 14.473928-23.352892-12.892743-43.786673-31.502079-65.801639-47.800452-6.081482 9.852001-15.082076 22.501485-22.258225 36.245635-6.689631 13.014372 3.162371 19.217484 16.420002 25.907115-15.690224-0.364889-22.379855-8.392446-25.907115-18.001188-6.93289-18.974225-17.757928-36.367265-31.745338-50.962822-20.190521-21.406818-12.041335-44.638081 0.851408-67.504454-11.311557-4.865186-22.258225-9.243853-32.718375-14.352298-14.717187-7.05452-24.325929-17.757928-23.961041-36.002376 0.608148-26.028745 0.12163-52.057489 0.12163-80.275567-17.27141 4.013778-36.610524 7.541038-55.34149 13.379261-32.231857 9.973631-64.828602 19.582373-95.844162 32.840005-35.637487 15.203706-71.883122 10.46015-108.372015 8.514075-2.919112-0.12163-7.541038-4.500297-8.027557-7.662668-8.514075-38.191709-30.042523-67.382825-57.895712-93.04668-18.244447-16.906521-31.13719-37.218672-37.097042-62.517638-7.662668-32.596745 1.702815-59.355268 23.96104-82.343272 3.770519-3.892149 10.581779-6.324742 16.176743-6.446371 39.772895-0.973037 76.991567 9.852001 112.385794 28.096449 8.392446 4.378667 17.14978 7.784297 25.663856 11.676446 0.729778-1.459556 1.337926-2.797482 1.946074-4.257038-29.191115-14.838817-58.382231-29.799264-85.627272-43.786673 15.325336-19.217484 31.258819-39.894524 48.043711-59.963416 18.609336-22.136596 42.448747-18.001188 66.166528-12.041335 50.719563 12.527854 97.182088 35.637487 142.428317 61.422972 29.069486 16.541632 57.652453 33.813042 85.748901 51.81423 18.730966 12.041335 30.407412 30.285782 37.218672 51.814229 8.514075 27.123411 17.14978 54.125193 26.880152 85.019124v-18.730966c6.324742 1.581185 12.041335 3.040741 19.825633 4.865186-1.946074-3.162371-2.554223-5.594964-4.135408-6.446371-18.487706-9.730372-24.325929-26.758522-27.245041-46.219266-2.189334-14.352298-4.986816-28.704597-9.365483-42.448747-2.554223-8.270816-7.05452-17.39304-13.500891-22.866374-16.176743-13.987409-33.813042-26.272004-51.084452-39.408006 13.86578-25.298967 53.152156-45.976007 81.370234-32.231856 4.257038 2.067704 10.825039-1.337926 16.176744-0.608148 5.351704 0.729778 11.676446 2.310963 15.325335 5.838223 24.934078 24.812448 49.746526 50.111415 62.882528 83.681197 4.621927 11.676446 5.108445 26.880152 2.675852 39.408006-3.648889 18.730966-10.946668 37.097042-17.879558 55.09823-6.81126 17.14978 1.459556 32.353486 4.865186 48.16534 0.12163 0.973037 4.865186 0.973037 7.541038 1.459556 3.770519 11.798076 7.662668 23.961041 12.284594 38.434968 14.838817 10.095261 19.582373 28.704597 23.352892 48.651859-10.33852-0.729778-19.095855-1.094667-28.461337-1.824444-0.486519 4.500297-0.851408 8.392446-0.851408 8.635705l12.527854 8.878964c-4.865186 0.851408-9.608742 1.824445-15.568595 3.040741 7.297779 4.500297 14.838817 9.000594 22.501485 13.62252-29.556004-9.973631-40.016154-7.419408-49.746526 13.014373 7.176149-3.284 14.838817-6.93289 22.623115-10.58178 0.729778 0.973037 1.459556 2.067704 2.067704 2.919112-5.838223 9.365483-11.554816 18.730966-18.730966 30.407412 15.446965-5.473334 18.609336-27.73156 40.502673-20.67704-7.784297 14.838817-15.082076 28.461337-23.109633 43.908302 19.460744-13.74415 29.556004-31.258819 36.002375-51.814229 1.581185-5.108445 6.324742-9.122224 9.973631-13.500891 0.973037 10.21689 9.000594 12.527854 16.663262 12.162965 16.055113-0.243259 30.407412-5.108445 37.705191-21.528448v-0.12163l0.364888-0.364889zM560.478695 339.225086c-0.243259 0.973037-0.486519 1.946074-0.608148 3.040741-10.33852 4.621927-20.67704 9.122224-31.015561 13.622521-0.12163 1.459556-0.12163 3.040741-0.243259 4.500297 44.638081-10.095261 64.463713 19.947262 83.802827 51.6926-6.81126-21.528448-15.690224-41.597339-23.474522-62.15275-6.081482-16.055113-18.852595-24.569189-33.569782-25.663855-24.812448-2.067704-49.868155-0.608148-74.923863-0.608149-0.12163 0.486519-0.12163 1.094667-0.12163 1.581186 26.758522 4.743556 53.395415 9.365483 80.153938 13.987409z m163.835135 3.040741c-4.257038-35.272598-12.406224-70.666825-44.151562-95.114384 14.838817 32.231857 29.920893 64.463713 44.151562 95.114384z m-11.919706 207.378549c-2.797482-9.122224-5.838223-18.366077-9.243853-29.191116-16.663262-5.351704-34.299561-0.851408-50.233044 11.919706 11.798076 3.892149 13.86578 10.581779 9.365482 25.177337 18.244447-3.040741 36.245635-5.838223 50.111415-7.905927z m-28.704597-75.410381c-23.474522-4.013778-33.204894 2.189334-32.110226 19.339113 10.21689-6.203112 20.79867-12.527854 32.110226-19.339113z\"  ></path><path d=\"M856.160367 554.266302c-7.297779 16.420002-21.650077 21.285188-37.70519 21.528448-7.662668 0.243259-15.568595-1.946074-16.663262-12.162965l0.608148-0.729778c12.162965 7.541038 23.961041 4.865186 35.880746-0.851407 5.716593-2.797482 11.676446-5.230075 17.39304-8.027557l0.486518 0.243259zM680.162268 247.151443c31.623708 24.447559 39.894524 59.841786 44.151562 95.114384-14.230669-30.650671-29.312745-62.882528-44.151562-95.114384zM703.271901 520.45326c3.40563 10.825039 6.324742 20.068892 9.243853 29.191116-13.86578 2.067704-31.866968 4.986816-50.111415 7.905927 4.500297-14.595558 2.432593-21.285188-9.365483-25.177337 15.811854-12.771113 33.448153-17.27141 50.233045-11.919706zM700.352789 836.325454l-0.486519 0.364889c-0.12163 0-0.12163 0-0.243259-0.121629 0.364889-0.12163 0.608148-0.243259 0.729778-0.24326zM651.579301 493.573108c-1.094667-17.14978 8.635705-23.352892 32.110226-19.339113-11.311557 6.81126-21.893337 13.136002-32.110226 19.339113z\"  ></path><path d=\"M647.079004 716.763511c16.298373 0 25.177337-7.905927 33.691412-22.014966-12.284594 7.905927-22.136596 14.352298-33.691412 22.014966z m-3.284001-0.851407c-13.379261-6.689631-23.109633-12.892743-16.420002-25.907115 7.05452-13.74415 16.176743-26.393633 22.258225-36.245635 22.136596 16.298373 42.448747 35.029338 65.801639 47.800451 31.258819 17.27141 67.504454 16.420002 94.749496-14.473928 22.501485-25.420596 37.94845-56.192897 44.881339-89.276161 3.040741-13.987409 1.094667-29.069486 1.337926-43.665043l-0.243259-0.12163v0.12163l-0.486518-0.24326c-3.892149-1.216296-8.027557-3.648889-11.311558-2.797481-14.352298 3.284-28.339708 7.662668-41.840598 11.433186 0 0.12163-0.12163 0.12163-0.24326 0.24326l-0.608148 0.729778c-3.648889 4.378667-8.514075 8.392446-9.973631 13.50089-6.324742 20.67704-16.541632 38.191709-36.002375 51.81423 8.027557-15.446965 15.325336-29.069486 23.109633-43.908302-22.014966-7.05452-25.055707 15.203706-40.502673 20.67704 7.05452-11.554816 12.892743-20.920299 18.730966-30.407412-0.729778-0.851408-1.459556-1.824445-2.067704-2.919112-7.784297 3.648889-15.446965 7.176149-22.623115 10.58178 9.608742-20.433781 20.068892-22.988003 49.746526-13.014373-7.662668-4.621927-15.203706-9.122224-22.501485-13.62252 5.959853-1.216296 10.825039-2.189334 15.568595-3.040741l-12.527854-8.878965c0-0.243259 0.486519-4.135408 0.851408-8.635705 9.365483 0.729778 18.122817 1.216296 28.461337 1.824445-3.770519-19.947262-8.514075-38.556598-23.352892-48.651859-4.743556-14.473928-8.514075-26.636893-12.284594-38.434968-2.675852-0.486519-7.419408-0.486519-7.541038-1.459556-3.40563-15.811854-11.554816-30.89393-4.865186-48.16534 6.93289-17.879558 14.109039-36.245635 17.879558-55.098231 2.432593-12.527854 1.946074-27.73156-2.675852-39.408005-13.136002-33.569783-37.94845-58.868749-62.882528-83.681198-3.648889-3.52726-9.852001-5.108445-15.325335-5.838223-5.351704-0.729778-11.919705 2.554223-16.176744 0.608149-28.339708-13.74415-67.504454 6.93289-81.370234 32.231856 17.27141 13.136002 35.029338 25.420596 51.084452 39.408006 6.324742 5.473334 10.946668 14.595558 13.500891 22.866373 4.378667 13.74415 7.176149 28.218078 9.365483 42.448747 2.797482 19.460744 8.635705 36.610524 27.245041 46.219266 1.581185 0.851408 2.189334 3.284 4.135408 6.446372-7.784297-1.824445-13.500891-3.284-19.825633-4.865186V420.838579c-9.730372-30.772301-18.366077-57.774082-26.880152-85.019123-6.81126-21.528448-18.609336-39.894524-37.218672-51.81423-28.218078-18.122817-56.801045-35.394227-85.748901-51.814229-45.367858-25.663856-91.708754-48.895118-142.428317-61.422972-23.596152-6.081482-47.557192-10.095261-66.166528 12.041335-16.784891 20.068892-32.718375 40.745932-48.043711 59.963416 27.366671 13.987409 56.436156 28.947856 85.627272 43.786673-0.729778 1.459556-1.337926 2.797482-1.946074 4.257038-8.514075-3.892149-17.27141-7.419408-25.663856-11.676447-35.394227-18.244447-72.612899-29.069486-112.385794-28.096448-5.473334 0.12163-12.284594 2.554223-16.176743 6.446371-22.258225 22.988003-31.623708 49.746526-23.96104 82.343271 5.959853 25.298967 18.852595 45.611118 37.097042 62.517639 27.853189 25.663856 49.381637 54.854971 57.895712 93.04668 0.608148 3.162371 5.108445 7.541038 8.027557 7.662668 36.367265 1.824445 72.612899 6.689631 108.372015-8.514075 31.01556-13.136002 63.612305-22.866374 95.844162-32.840005 18.730966-5.959853 38.07008-9.365483 55.34149-13.379261 0 28.339708 0.486519 54.368452-0.12163 80.275567-0.364889 18.244447 9.243853 28.947856 23.961041 36.002375 10.46015 5.108445 21.406818 9.365483 32.718375 14.352299-12.892743 22.866374-21.163559 46.097636-0.851408 67.504454 13.987409 14.595558 24.812448 31.988597 31.745338 50.962822 3.52726 10.095261 10.095261 18.122817 25.907115 18.487707z m-269.531298 29.312745c-21.528448-53.517045-33.204894-108.250386-22.014966-166.267728 5.351704-28.218078 17.028151-53.273785 39.408005-77.599715-8.514075 2.554223-13.136002 3.770519-17.757928 5.351704-34.664449 11.676446-70.058677 13.257632-105.817793 7.541038-5.108445-0.851408-11.919705-6.203112-13.622521-11.068297-12.406224-34.664449-28.582967-66.166528-55.949637-91.708754C183.913307 397.850576 172.48012 380.579166 161.533452 363.672645c-13.379261-21.163559-16.055113-44.638081-6.446371-68.355862C161.411822 279.748189 168.101453 263.936334 176.372269 249.340777c7.05452-12.527854 17.636299-17.879558 33.691412-15.203706 18.366077 3.040741 36.853783 0.12163 50.962822-15.811854 16.663262-18.609336 35.029338-35.880746 51.327711-54.733341 10.21689-12.041335 21.528448-13.257632 35.637487-11.433187 54.246823 7.176149 103.628459 27.974819 151.428911 54.368452 19.582373 10.825039 39.043117 22.014966 58.017342 33.934672 8.514075 5.351704 14.838817 3.770519 21.528447-2.189334 2.554223-2.310963 5.230075-4.378667 7.662668-6.689631 46.462525-43.543414 107.398979-39.164746 144.982539 12.649484 17.39304 23.961041 30.529041 51.327711 44.638081 77.599715 2.554223 4.865186 1.459556 13.014372-0.12163 18.974225-6.689631 24.569189-14.352298 49.016748-21.041929 73.707566-2.432593 8.635705 1.702815 15.325336 6.203112 23.596151 16.420002 29.312745 30.407412 59.963416 44.273192 90.614087 2.675852 5.959853 0.486519 13.987409 0.486519 25.298967 10.46015-5.108445 18.730966-8.514075 26.880152-12.892742 9.365483-5.108445 18.730966-10.946668 28.826226-2.919112 10.33852 8.270816 14.230669 18.974225 10.946668 32.231856-6.203112 25.298967-11.433187 50.962822-18.244447 76.14016-7.176149 26.636893-23.839411 47.070673-44.75971 64.220453-32.596745 26.272004-66.531417 19.582373-100.952607 4.865186-6.081482-2.554223-11.676446-6.446371-19.217485-10.703409-3.648889 4.378667-6.93289 9.000594-10.946668 13.014373-19.582373 19.947262-60.936453 16.055113-72.24801-12.771113-7.176149-18.244447-28.218078-21.041929-44.273192-7.419409 12.041335 21.771707 26.636893 40.624302 52.787267 46.584155-18.122817 5.230075-37.94845-2.310963-61.179712-23.109633-8.514075 29.799264 10.21689 46.584155 56.192897 50.476304 0.12163 1.337926 0.243259 2.432593 0.364888 3.770519-11.798076 1.337926-23.596152 2.432593-35.394227 3.648889 3.284 22.623114 8.878964 28.947856 32.961635 32.961635-7.419408 2.797482-12.284594 4.500297-17.27141 6.324741-0.364889 1.094667-0.729778 2.310963-1.094667 3.52726 5.594964 2.310963 11.189928 6.324742 16.906521 6.689631 27.001782 1.459556 54.003563 1.581185 81.005345 2.919111 4.257038 0.12163 8.514075 3.162371 12.649483 4.986816-28.218078 9.000594-56.922675 16.420002-84.289345 27.60993-38.556598 15.568595-74.558974 9.852001-110.43972-6.93289-0.12163 0-0.12163-0.12163-0.243259-0.12163 18.122817 3.892149 36.002376 8.514075 54.246822 11.311557 11.068298 1.702815 25.785485 4.135408 30.164153-8.635705 2.554223-7.419408-5.594964-18.366077-8.270816-25.177337-19.582373 3.162371-35.272598 6.93289-51.206082 8.270816-15.811854 1.337926-31.745338 0.243259-48.28697 0.24326 7.905927 4.865186 15.690224 9.487112 23.352893 13.987409h-0.12163c0.12163 0 0.243259 0.12163 0.364889 0.12163 0.12163 0 0.12163 0.12163 0.243259 0.121629-44.516451-15.203706-80.153938-43.056895-110.19646-79.05927-1.216296-1.581185-1.216296-4.378667-1.946075-6.689631 12.041335 6.689631 23.596152 14.109039 36.002376 19.947262 42.083858 19.947262 86.23542 23.231263 133.306093 17.14978-5.108445-8.149186-8.757335-15.446965-13.987409-21.406818-3.648889-3.892149-8.270816-6.689631-13.500891-7.905927-27.73156-5.959853-56.436156-8.878964-83.437938-17.14978-26.880152-8.392446-51.935859-22.136596-79.424159-34.29956 4.378667 10.21689 16.784891 33.691412 20.920299 43.665043-3.892149-4.378667-16.906521-21.528448-18.974225-26.393633z m-20.312151-161.645802c0 21.285188-0.243259 42.327117 0.121629 63.490676 0 3.040741 2.067704 6.689631 4.257038 8.757335 47.313933 48.28697 105.088015 75.045492 170.038247 87.330087 1.094667 0.243259 2.554223-0.851408 3.892149-1.337927 1.946074-10.095261 3.770519-19.947262 5.838223-30.407411C461.715421 692.072693 394.940745 659.597577 358.69511 582.60601c-1.581185 0.243259-3.284 0.729778-4.743556 0.973037z m184.147286 104.966386c2.310963 1.459556 7.176149-1.459556 9.365483-1.824445-13.379261-16.420002-27.366671-29.434375-36.245635-45.367858-18.001188-31.988597-33.813042-65.193491-49.260007-98.398385-8.392446-18.244447-14.595558-37.461931-22.258226-57.774082-13.257632 5.838223-25.420596 10.825039-37.461931 16.420002-7.662668 3.648889-10.825039 9.365483-9.365483 18.730966 4.500297 30.164153 13.74415 58.50386 32.718375 82.70816 30.285782 38.434969 72.734529 60.206675 112.507424 85.505642z\"  ></path><path d=\"M559.870547 342.265827c0.12163-0.973037 0.364889-1.946074 0.608148-3.040741-26.758522-4.621927-53.395415-9.243853-80.153938-13.987409 0-0.486519 0-1.094667 0.12163-1.581186 25.055707 0 50.111415-1.459556 74.923863 0.608149 14.717187 1.216296 27.4883 9.730372 33.569782 25.663855 7.784297 20.55541 16.663262 40.624302 23.474522 62.15275-19.339114-31.745338-39.286376-61.787861-83.802827-51.6926 0.12163-1.459556 0.12163-3.040741 0.243259-4.500297 10.33852-4.500297 20.67704-9.000594 31.015561-13.622521zM505.015576 857.124124c-0.12163 0-0.243259-0.12163-0.364889-0.12163h0.121629s0.12163 0.12163 0.24326 0.12163z\"  ></path></symbol><symbol id=\"icon-Kingbase\" viewBox=\"0 0 1024 1024\"><path d=\"M112 498.272L305.088 416V128h151.744v219.424L912 128v164.576L470.624 512 912 717.728V896l-455.168-205.728V896H291.296v-301.728z\"  ></path></symbol><symbol id=\"icon-yibiaopan\" viewBox=\"0 0 1228 1024\"><path d=\"M613.717333 34.133333a597.333333 597.333333 0 0 0-493.568 932.795734c7.714133 11.0592 20.343467 17.749333 33.792 17.749333a39.799467 39.799467 0 0 0 23.688534-7.099733 40.618667 40.618667 0 0 0 10.922666-56.661334 513.979733 513.979733 0 0 1-88.064-267.605333h66.56a44.168533 44.168533 0 0 0 0-88.064h-63.488a512.887467 512.887467 0 0 1 101.376-246.647467l38.0928 38.229334a44.168533 44.168533 0 0 0 62.464-62.122667l-40.618666-41.233067a514.2528 514.2528 0 0 1 308.155733-136.260266 40.277333 40.277333 0 0 0-2.389333 13.9264V200.704a44.168533 44.168533 0 0 0 87.995733 0v-68.949333a47.991467 47.991467 0 0 0-2.4576-13.9264 512.887467 512.887467 0 0 1 306.312533 134.3488l-42.530133 42.530133a44.168533 44.168533 0 0 0 62.395733 62.122667l40.072534-39.799467A511.249067 511.249067 0 0 1 1125.717333 565.248h-65.399466a44.168533 44.168533 0 0 0 0 88.064h68.949333a512 512 0 0 1-85.060267 263.168 40.891733 40.891733 0 0 0 68.130134 45.2608A597.6064 597.6064 0 0 0 613.717333 34.133333z\"  ></path><path d=\"M727.04 460.5952L597.333333 591.189333a60.2112 60.2112 0 0 0-10.103466 0 73.045333 73.045333 0 1 0 73.045333 72.772267 54.4768 54.4768 0 0 0 0-9.284267l129.979733-129.979733a43.963733 43.963733 0 1 0-62.122666-62.122667l-1.092267-1.911466z\"  ></path></symbol><symbol id=\"icon-presto_sql\" viewBox=\"0 0 1024 1024\"><path d=\"M566 179m-45 0a45 45 0 1 0 90 0 45 45 0 1 0-90 0Z\"  ></path><path d=\"M561.5 399.5m-49.5 0a49.5 49.5 0 1 0 99 0 49.5 49.5 0 1 0-99 0Z\"  ></path><path d=\"M674 287m-45 0a45 45 0 1 0 90 0 45 45 0 1 0-90 0Z\"  ></path><path d=\"M786.5 399.5m-49.5 0a49.5 49.5 0 1 0 99 0 49.5 49.5 0 1 0-99 0Z\"  ></path><path d=\"M894.5 516.5m-58.5 0a58.5 58.5 0 1 0 117 0 58.5 58.5 0 1 0-117 0Z\"  ></path><path d=\"M674 512m-54 0a54 54 0 1 0 108 0 54 54 0 1 0-108 0Z\"  ></path><path d=\"M786.5 624.5m-58.5 0a58.5 58.5 0 1 0 117 0 58.5 58.5 0 1 0-117 0Z\"  ></path><path d=\"M449 287m-45 0a45 45 0 1 0 90 0 45 45 0 1 0-90 0Z\"  ></path><path d=\"M336.5 174.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M336.5 624.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M444.5 732.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M561.5 849.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M228.5 732.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M336.5 849.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M111.5 849.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M116 179m-36 0a36 36 0 1 0 72 0 36 36 0 1 0-72 0Z\"  ></path><path d=\"M228.5 291.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M336.5 399.5m-40.5 0a40.5 40.5 0 1 0 81 0 40.5 40.5 0 1 0-81 0Z\"  ></path><path d=\"M449 512m-54 0a54 54 0 1 0 108 0 54 54 0 1 0-108 0Z\"  ></path><path d=\"M557 620m-54 0a54 54 0 1 0 108 0 54 54 0 1 0-108 0Z\"  ></path><path d=\"M669.5 732.5m-58.5 0a58.5 58.5 0 1 0 117 0 58.5 58.5 0 1 0-117 0Z\"  ></path></symbol><symbol id=\"icon-shujukuleixingtubiao-kuozhan-\" viewBox=\"0 0 1024 1024\"><path d=\"M233.3 894.2V647.3h80.5c82.7 0 123.9 40.2 123.9 120.5 0 38.1-11.5 68.9-34.8 91.9-23.3 23-53.1 34.7-89.6 34.7h-80v-0.2z m49.7-204v160.9h27.1c23.6 0 42-7.5 55.5-22.3 13.5-15 20.2-35 20.2-60.4 0-24.5-7.1-43.7-21-57.5-14.1-13.8-32.2-20.6-54.7-20.6H283v-0.1zM477 894.2V647.3h82.7c25.3 0 44.9 5.1 58.7 15.3 13.8 10.2 20.5 24.3 20.5 42.4 0 13.4-4.3 25.2-12.8 35.2-8.5 10-19.2 17-32.2 20.9v0.7c16.2 2.2 29.3 8.5 39.3 19.2 9.9 10.7 14.9 24 14.9 40 0 22.5-7.5 40.3-22.6 53.6-15.1 13.3-35.6 19.9-61.6 19.9H477v-0.3z m49.6-207.6v61.1h23.6c11.4 0 20.2-2.9 26.8-8.7 6.6-5.8 9.8-13.8 9.8-24.2 0-18.9-13-28.4-39.1-28.4l-21.1 0.2z m0 100.5v67.7h29.5c12.5 0 22.3-3.1 29.3-9.2S596 830.9 596 820c0-10.4-3.5-18.5-10.6-24.3-7.1-5.8-17-8.7-30-8.7l-28.8 0.1zM732.6 851.5h98.8v42.9H676.5V876c0-12.9 2.2-24.7 6.7-35.2s10.6-20.6 18.4-29.8c7.9-9.2 19.9-19.9 36.4-32.3 15.1-11.9 25.5-22.5 31.6-31.6 6.1-9.2 9-18.9 9-29.3 0-22.3-11.4-33.3-34.3-33.3-20 0-39.1 8.5-57.4 25.5v-46c20.4-14 43.1-20.9 68.6-20.9 23.3 0 41.7 6.3 55.2 18.9 13.5 12.6 20.4 29.9 20.4 52.1 0 12.4-1.9 23.5-5.9 33.3-3.8 9.9-9.6 19.2-17 28.1-7.4 8.7-19.2 19.2-35.6 31.5-15.7 11.9-26.3 21.1-31.7 27.6-5.4 6.5-8.3 11.9-8.3 16.9z\"  ></path><path d=\"M82.1 171.6h173.3v26H82.1zM280 171.6v26h247.1s-25.8-26-58.2-26H280zM576.3 171.6v26.2h149.9l-8.8-26.2zM973.3 171.6H833.6l-8.8 26.2h148.5zM82.1 222.6h173.3v26H82.1zM280 248.7h276.8s-2.6-20.6-9-26H280v26zM576.3 248.7H743l-7.5-26.1H576.3zM973.3 248.7v-26.1H815.7l-7.7 26.1zM132.4 272h75v26h-75zM330.5 272h75v26h-75zM479.3 298h75s5.1-13.8 5.1-26h-80.2v26h0.1zM626.8 272v26h133.1l-8.8-26zM791.1 298h134.6v-26H800zM132.4 322.7h75v26h-75zM330.5 348.7h192.7s15.6-13.8 20.7-26H330.5v26zM775.6 344.8l-6.4-22.1H626.8v26h75v-15l5.2 15h138.5l5-15v15h75.2v-26H783.1zM132.4 373.4h75v26h-75zM330.5 399.6h213.4c-5.1-12.4-20.7-26-20.7-26H330.5v26zM626.8 373.4h75v26h-75zM837.6 373.4H715.9l9.2 26.2h103.6zM849.3 373.4h75v26h-75zM132.4 424.2h75v26h-75zM330.5 424.2h75v26h-75zM554.3 424.2h-75v26h80.2c-0.1-12.3-5.2-26-5.2-26zM626.8 424.2h75v26h-75zM820.8 424.2h-88l9.1 26.1h69.9zM849.3 424.2h75v26h-75zM83.4 473.6h173.3v26H83.4zM557 473.6H280v26h267.8c5.2-5.4 9.2-26 9.2-26zM577.7 473.6h124.1v26H577.7zM802.5 473.6h-52.9l9.2 26h34.7zM849.3 473.6h125.5v26H849.3zM83.4 524.6h173.3v26H83.4zM527.2 524.6H280v26h188.9c33.6-0.1 58.3-26 58.3-26zM577.7 524.6h124.1v26H577.7zM776.7 550.5l9.3-25.9h-19.7l9.3 25.9zM849.3 524.6h125.5v26H849.3z\"  ></path></symbol><symbol id=\"icon-oceanbase\" viewBox=\"0 0 1024 1024\"><path d=\"M196.266667 315.733333C64 413.866667 21.333333 580.266667 102.4 691.2c81.066667 110.933333 256 119.466667 384 21.333333l12.8-12.8 64-384c-81.066667-89.6-243.2-93.866667-366.933333 0z m226.133333 298.666667c-29.866667 25.6-64 38.4-102.4 34.133333s-72.533333-17.066667-98.133333-46.933333c-17.066667-29.866667-25.6-68.266667-12.8-106.666667 8.533333-34.133333 29.866667-68.266667 64-85.333333 25.6-21.333333 59.733333-34.133333 98.133333-34.133333 38.4 0 72.533333 17.066667 98.133333 46.933333 17.066667 29.866667 25.6 68.266667 17.066667 106.666667-12.8 34.133333-34.133333 64-64 85.333333zM878.933333 503.466667c21.333333-12.8 42.666667-34.133333 55.466667-59.733334 12.8-21.333333 21.333333-51.2 21.333333-76.8 0-85.333333-98.133333-98.133333-98.133333-98.133333h-238.933333l-89.6 503.466667h247.466666c46.933333 0 93.866667-17.066667 128-51.2 34.133333-34.133333 55.466667-76.8 55.466667-123.733334 0-21.333333-8.533333-42.666667-21.333333-59.733333-17.066667-21.333333-34.133333-29.866667-59.733334-34.133333z m-149.333333-128h64c8.533333 0 17.066667 8.533333 25.6 17.066666 4.266667 8.533333 8.533333 17.066667 8.533333 29.866667 0 12.8-4.266667 25.6-12.8 34.133333-8.533333 8.533333-17.066667 17.066667-29.866666 25.6h-72.533334l17.066667-106.666666z m29.866667 294.4h-89.6l17.066666-102.4H768s55.466667 0 55.466667 46.933333-55.466667 55.466667-64 55.466667z\"  ></path></symbol><symbol id=\"icon-dameng1\" viewBox=\"0 0 1024 1024\"><path d=\"M294.784 300.928c0 54.656-49.408 225.92 153.472 248.832 88.576 21.376 111.232 68.992 103.552 211.584-0.128 11.52-0.512 23.04-1.408 34.432-30.976 17.664-52.864 28.16-65.664 31.872 2.048-19.712 22.784-175.616-19.712-211.84-42.368-36.224-71.936-40.704-115.712-46.848-21.12-3.072-54.272-22.144-79.232-38.784-25.088-16.512-39.68-48.64-44.16-78.72-2.944-20.096-3.84-49.024-2.56-87.04 16-19.456 39.808-40.704 71.424-63.488z m84.736-52.608c-1.408 19.84-29.952 191.616 52.096 232.32 29.952 14.976 42.496 20.736 63.744 26.112 21.12 5.376 74.752 13.056 96.896 37.248 8.96 9.728 22.528 26.24 30.08 47.104 11.008 30.72 13.952 74.368 13.952 119.68v34.176c-23.936 19.328-44.16 32.768-60.8 40.576 0-23.168 7.68-131.84-12.544-176.64-20.224-44.672-41.856-65.664-77.824-70.784-35.968-5.12-93.952-21.248-117.12-42.24-23.168-20.992-53.504-52.608-53.504-105.6 0-35.456 0.768-69.504 2.304-102.272 23.552-18.304 44.288-31.488 62.72-39.68z m86.272-41.472c-2.304 39.168-7.68 132.864 9.6 181.376 17.408 48.512 54.144 75.648 94.464 83.2 40.32 7.424 112.128 33.152 130.048 79.616 16.256 42.24 17.92 67.456 18.176 88.192v37.12c-22.144 21.76-41.728 38.784-58.88 51.072 0-63.872-0.256-124.672-17.408-160.512-17.152-35.712-39.68-57.216-88.96-69.504-49.152-12.416-100.608-17.792-131.584-61.056-20.736-28.8-28.16-76.544-22.528-143.104l5.12-56.32c20.48-11.904 41.088-22.016 61.952-30.08z m0 0\"  ></path><path d=\"M190.08 384.128c2.432 41.856 5.632 69.888 9.6 84.352 11.008 40.576 42.368 70.912 56.448 80 22.784 14.848 67.328 34.944 101.376 39.808 22.656 3.2 47.232 11.008 73.6 23.424 24.704 17.408 37.504 34.432 38.4 50.944 5.248 106.496-9.984 142.592-16.64 179.968-75.648 49.536-388.608 54.656-395.008-136.96-4.352-127.616 39.68-234.88 132.224-321.536z m701.312-137.6c68.352 105.472 31.616 240-110.08 403.712-0.256-12.288-1.408-24.576-3.584-36.736-17.664-83.2-49.024-119.168-87.68-134.272-38.656-15.232-150.784-16.64-172.544-100.48-9.472-62.464-7.04-126.08 7.04-187.648 174.464-51.84 296.832-33.408 366.848 55.424z m0 0\"  ></path></symbol><symbol id=\"icon-proxy\" viewBox=\"0 0 1024 1024\"><path d=\"M846.250667 835.328a431.701333 431.701333 0 0 0-94.549334-61.44l1.877334-5.717333c11.093333-35.413333 20.138667-73.386667 26.453333-113.493334 0.853333-5.034667 1.450667-10.154667 2.133333-15.274666 2.133333-15.189333 3.925333-30.72 5.290667-46.506667l1.450667-17.578667c0.853333-13.141333 1.109333-26.624 1.536-40.106666h186.026666A464.128 464.128 0 0 1 846.250667 835.413333zM650.154667 956.586667c9.984-10.496 19.626667-22.186667 28.757333-34.816l2.56-3.584c18.517333-25.856 35.157333-55.722667 49.493333-89.173334 0.853333-1.706667 1.536-3.669333 2.304-5.461333 0.853333-1.962667 1.536-4.266667 2.304-6.229333 28.757333 13.994667 54.528 30.805333 77.056 49.493333A463.36 463.36 0 0 1 650.24 956.586667z m-114.858667 18.517333V769.024a522.24 522.24 0 0 1 159.317333 30.72c-37.973333 96.682667-94.72 162.56-159.317333 175.36z m0-439.808H744.106667a824.661333 824.661333 0 0 1-34.474667 220.757333 564.992 564.992 0 0 0-174.421333-33.536V535.296z m0-233.813333a564.992 564.992 0 0 0 174.421333-33.536c19.968 64.853333 32.426667 139.946667 34.474667 220.757333H535.296V301.482667z m0-252.586667c64.597333 12.8 121.344 78.677333 159.317333 175.36a521.472 521.472 0 0 1-159.317333 30.72V48.896z m277.333333 108.202667c-22.528 18.773333-48.298667 35.584-77.056 49.664-0.853333-2.048-1.450667-4.266667-2.304-6.229334l-2.218666-5.546666a484.778667 484.778667 0 0 0-49.493334-89.173334l-2.56-3.584A344.32 344.32 0 0 0 650.069333 67.413333c60.416 18.773333 115.456 49.749333 162.474667 89.685334z m163.84 331.605333h-186.026666c-0.426667-13.397333-0.597333-26.794667-1.536-39.936l-1.450667-17.664a886.698667 886.698667 0 0 0-5.290667-46.506667c-0.682667-5.12-1.28-10.24-2.133333-15.36a785.92 785.92 0 0 0-26.453333-113.408l-1.877334-5.717333c35.413333-16.981333 66.986667-37.802667 94.549334-61.44a463.957333 463.957333 0 0 1 130.218666 300.032zM488.704 254.976a521.557333 521.557333 0 0 1-159.402667-30.72C367.36 127.573333 424.106667 61.781333 488.704 48.896v206.08z m0 233.813333H279.893333c2.048-80.810667 14.506667-155.989333 34.474667-220.842666 52.906667 19.626667 111.957333 31.146667 174.421333 33.536v187.221333z m0 233.728a564.992 564.992 0 0 0-174.421333 33.536 823.722667 823.722667 0 0 1-34.474667-220.757333h208.896v187.221333z m0 252.586667c-64.597333-12.8-121.344-78.677333-159.402667-175.36a522.24 522.24 0 0 1 159.402667-30.72v206.08z m-277.333333-108.202667c22.528-18.773333 48.213333-35.584 76.970666-49.578666 0.853333 1.962667 1.536 4.266667 2.389334 6.229333l2.218666 5.461333c14.506667 33.450667 31.061333 63.317333 49.493334 89.173334l2.56 3.584c9.216 12.629333 18.773333 24.32 28.842666 34.816a463.189333 463.189333 0 0 1-162.474666-89.685334zM47.530667 535.296h186.026666c0.426667 13.397333 0.597333 26.88 1.536 40.021333 0.341333 5.973333 0.938667 11.776 1.450667 17.578667 1.365333 15.786667 3.157333 31.317333 5.290667 46.506667 0.682667 5.12 1.28 10.24 2.133333 15.36 6.314667 40.106667 15.274667 77.994667 26.453333 113.408l1.877334 5.717333a431.786667 431.786667 0 0 0-94.549334 61.44A464.128 464.128 0 0 1 47.530667 535.296z m130.218666-346.538667c27.477333 23.552 59.136 44.373333 94.549334 61.44l-1.877334 5.632a785.92 785.92 0 0 0-26.453333 113.493334c-0.853333 5.034667-1.450667 10.24-2.133333 15.36-2.133333 15.189333-3.925333 30.72-5.290667 46.421333-0.512 5.888-1.109333 11.690667-1.450667 17.664-0.853333 13.141333-1.109333 26.538667-1.536 39.936H47.530667a463.957333 463.957333 0 0 1 130.218666-299.946667zM373.76 67.413333c-9.984 10.496-19.626667 22.186667-28.757333 34.816l-2.56 3.584a484.693333 484.693333 0 0 0-49.493334 89.173334c-0.853333 1.706667-1.536 3.669333-2.304 5.546666-0.853333 1.962667-1.536 4.181333-2.389333 6.229334a381.013333 381.013333 0 0 1-76.970667-49.664A462.762667 462.762667 0 0 1 373.76 67.498667zM512 0a512 512 0 1 0 0 1024A512 512 0 0 0 512 0z\"  ></path></symbol><symbol id=\"icon-openai\" viewBox=\"0 0 1024 1024\"><path d=\"M565.265178 954.519092c-22.289934 0-48.400999-8.151747-67.952455-14.838727a103.425292 103.425292 0 0 1-26.875292-11.272338c-12.737105-7.769634-15.411897-8.342804-19.806198-24.773669 21.971506-5.158528 81.581157-41.905075 103.871091-55.342721 148.896757-89.159735 119.028246-10.444426 119.028246-364.981743 15.029784 3.566389 82.791182 32.415932 82.791182 57.316972 0 133.102747 20.570425 273.847757-52.604243 354.91943-22.799418 25.47421-91.834527 58.972796-138.388646 58.972796z m-433.061569-299.321966c258.626916 136.860193 184.369594 157.048504 357.721593 52.094759 44.579867-27.193719 90.433445-49.292596 132.593263-77.568969v101.896839c-95.528287 22.226248-272.319304 227.038896-419.050754 100.686815l-25.856323-25.155782c-37.319718-43.943012-45.344094-72.410442-45.344094-151.889977z m375.744596-19.105658c-19.933569-13.310275-79.479535-51.330533-101.896839-57.316972v-133.739602a1158.312326 1158.312326 0 0 0 101.896839-57.316973c43.943012 10.189684 70.690933 47.063603 114.633945 57.316973v127.371049c-17.831947 12.10025-95.846715 58.718054-114.633945 63.749211z m-426.693016-178.31947c0-63.685525-4.26693-90.306074 38.84817-145.776166 23.945757-30.88748 47.509402-39.612396 82.090641-57.953828v261.110652c44.134069 23.372588 83.873836 49.037854 129.345301 74.448379l131.765351 78.396881c-59.545966 15.921381-63.685525 61.32916-109.602788 33.753328-104.699003-62.730242-272.38299-129.345301-272.38299-243.91556z m866.123138 127.37105c0 79.543221-47.573087 161.188063-121.002497 178.31947v-165.582365c0-82.791182 9.743885-84.574377-48.910483-116.608196L565.265178 362.371082c15.79401-23.62733 22.608361-19.105657 48.146256-34.835982 41.714019-25.47421 39.039227-16.112438 117.053995 28.785857 94.190891 54.196382 216.849212 100.559444 216.849212 228.885777z m-541.326961-197.425127v-95.528287c43.751956-23.181531 90.688187-50.94842 133.357489-76.804743 82.154327-49.547338 95.528287-63.303412 185.006449-63.303412 48.464684 0 102.533695 36.746548 125.651541 65.405034 42.223503 52.22213 39.930824 92.662439 39.930824 151.125751-31.078536-16.494551-192.393971-121.002497-222.899337-121.002498s-229.650003 123.422547-261.110652 140.108155z m-50.94842 159.213812c-16.36718-10.95391-63.112355-39.99451-82.791183-44.579867 0-168.320842-33.880699-314.606493 67.952455-390.519639 56.043262-41.714019 113.169178-53.814269 181.376375-30.696423 25.47421 8.661231 35.536523 20.888852 56.36169 26.429493-11.781822 16.048752-80.68956 50.311565-102.278953 63.303412-154.564769 93.235608-120.620384 7.451206-120.620384 376.063024z m-292.953415 121.002498c0 169.148754 115.143429 280.853165 274.293556 273.847756 59.800708-2.674792 26.811606-7.705949 69.417222 25.47421 97.820966 76.741057 228.822091 73.747838 319.637649 1.018969a251.939936 251.939936 0 0 0 52.604244-55.661149c58.20857-85.274918-10.95391-45.598836 81.96327-83.619094 130.236898-53.241099 199.399378-217.358696 128.64476-355.428914-27.448461-53.559526-40.249252-28.849543-28.276373-104.699003 18.723544-118.582447-63.176041-230.032116-157.621674-269.771884-98.90362-41.586648-129.090559 12.737105-178.892639-37.574459A161.888604 161.888604 0 0 0 580.103905 28.913674C474.003821-27.766443 331.284559 1.528898 258.428319 93.681853c-81.326415 102.979494 9.489143 54.705866-92.407697 98.648878C15.149614 257.353652-33.251385 439.175825 41.579107 561.06992c56.807488 92.598753 20.570425 4.967471 20.570424 106.800625z\"  ></path></symbol><symbol id=\"icon-guanyu\" viewBox=\"0 0 1040 1024\"><path d=\"M522.672 464c25.6 0 48 19.84 48 42.56v226.88c0 22.72-22.4 42.56-48 42.56s-48-19.84-48-42.56V506.56c0-22.72 22.4-42.56 48-42.56z m0-176a64 64 0 1 1 0 128 64 64 0 0 1 0-128z\"  ></path><path d=\"M524.14236445 2.27555555c282.04145778 0 509.72444445 227.68298667 509.72444444 509.72444445s-227.68298667 509.72444445-509.72444444 509.72444445-509.72444445-227.68298667-509.72444445-509.72444445 227.68298667-509.72444445 509.72444445-509.72444445z m0 100.12444445a408.76259555 408.76259555 0 0 0-409.6 409.6 408.76259555 408.76259555 0 0 0 409.6 409.6 408.76259555 408.76259555 0 0 0 409.6-409.6 408.76259555 408.76259555 0 0 0-409.6-409.6z\"  ></path></symbol><symbol id=\"icon-yifu\" viewBox=\"0 0 1024 1024\"><path d=\"M998.9586944 284.88426382L810.96066845 96.89206329a67.65062827 67.65062827 0 0 0-53.23270827-19.64914916 65.21560178 65.21560178 0 0 0-5.46424605-0.24466773H652.10140445l-7.01963378 3.90885831c-37.10211413 20.63947093-84.45114595 32.00486969-133.31478756 32.00486969-48.8636416 0-96.206848-11.36539875-133.30896213-32.00486969l-7.0254592-3.90885831H271.27025209a68.25064675 68.25064675 0 0 0-60.36302507 36.05353813l-185.82514347 185.83096889C12.25450382 311.71615858 5.18826667 328.76716942 5.18826667 346.90753422s7.06623715 35.19720107 19.89381688 48.0247808l135.77311574 135.77311573a67.34770631 67.34770631 0 0 0 41.85565866 19.59089494v328.13438293c0 37.80699022 30.75822933 68.57104498 68.56521956 68.57104498h480.98763662c37.80699022 0 68.57104498-30.76405475 68.57104498-68.57104498V536.35026489a67.35935715 67.35935715 0 0 0 42.34499414-19.64914916l135.790592-135.77311573c26.45906773-26.476544 26.45906773-69.56719218-0.01165085-96.04373618z m-43.8304768 52.2190848L819.34927645 472.88228978a5.74969173 5.74969173 0 0 1-4.15352605 1.71849955c-1.07770311 0-2.66804338-0.27962027-4.0894464-1.61946738l-52.26568818-49.3355008v454.7907129c0 3.64088889-2.95348907 6.58855253-6.58855253 6.58855253H271.27025209a6.58272711 6.58272711 0 0 1-6.58272711-6.58855253V439.82301867l-51.85790863 47.22087253c-1.40392675 1.28159289-2.95931449 1.54956231-4.00789048 1.54956231a5.76134258 5.76134258 0 0 1-4.1418752-1.71267413L68.90673493 351.10183822c-1.43887929-1.43305387-1.73597582-3.06999751-1.73597582-4.194304s0.30292195-2.76125013 1.73597582-4.20012942L256.49698133 155.1171584l5.99435947-5.37686471 2.67969422-6.63515591a6.59437795 6.59437795 0 0 1 6.10504249-4.13022436h84.33463751c45.35673742 23.52305493 99.14868622 35.90790258 156.15626809 35.90790258 57.01340729 0 110.80535609-12.38484765 156.16791894-35.90790258h84.28803413c0.33787449 0.01165085 0.66992355 0.06407965 0.99032177 0.11068303l4.43314632 0.63497102 4.42149546-0.64079645c1.40975218-0.19806435 3.27971271-0.1572864 5.06811734 1.63111823l187.99802595 187.99220053a5.94193067 5.94193067 0 0 1-0.00582542 8.40025884z\"  ></path></symbol><symbol id=\"icon-shujuku4\" viewBox=\"0 0 1024 1024\"><path d=\"M864 384H160a96 96 0 0 1-96-96V96a96 96 0 0 1 96-96h704a96 96 0 0 1 96 96v192a96 96 0 0 1-96 96zM160 64a32 32 0 0 0-32 32v192a32 32 0 0 0 32 32h704a32 32 0 0 0 32-32V96a32 32 0 0 0-32-32z\"  ></path><path d=\"M864 704H160a96 96 0 0 1-96-96v-192a96 96 0 0 1 96-96h704a96 96 0 0 1 96 96v192a96 96 0 0 1-96 96zM160 384a32 32 0 0 0-32 32v192a32 32 0 0 0 32 32h704a32 32 0 0 0 32-32v-192a32 32 0 0 0-32-32z\"  ></path><path d=\"M864 1024H160a96 96 0 0 1-96-96v-192a96 96 0 0 1 96-96h704a96 96 0 0 1 96 96v192a96 96 0 0 1-96 96zM160 704a32 32 0 0 0-32 32v192a32 32 0 0 0 32 32h704a32 32 0 0 0 32-32v-192a32 32 0 0 0-32-32z\"  ></path><path d=\"M224 192m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z\"  ></path><path d=\"M224 512m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z\"  ></path><path d=\"M224 832m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z\"  ></path></symbol><symbol id=\"icon-shujuyuanpeizhi\" viewBox=\"0 0 1024 1024\"><path d=\"M511.99 562.2c-7.66 0-15.32-1.76-22.36-5.28L105.66 364.94a49.996 49.996 0 0 1 0-89.44L489.63 83.51a49.986 49.986 0 0 1 44.72 0l383.97 191.98a49.996 49.996 0 0 1 0 89.44L534.35 556.92a49.976 49.976 0 0 1-22.36 5.28zM239.83 320.21L511.99 456.3l272.16-136.08-272.16-136.09-272.16 136.08zM511.99 946.16c-7.66 0-15.32-1.76-22.36-5.28L105.66 748.9c-24.7-12.35-34.71-42.38-22.36-67.08 12.35-24.7 42.38-34.71 67.08-22.36l361.61 180.8 361.61-180.8c24.7-12.35 54.73-2.34 67.08 22.36 12.35 24.7 2.34 54.73-22.36 67.08L534.35 940.89a50.098 50.098 0 0 1-22.36 5.27z\"  ></path><path d=\"M511.99 754.18c-7.66 0-15.32-1.76-22.36-5.28L105.66 556.92c-24.7-12.35-34.71-42.38-22.36-67.08 12.35-24.7 42.38-34.71 67.08-22.36l361.61 180.8 361.61-180.8c24.7-12.35 54.73-2.34 67.08 22.36 12.35 24.7 2.34 54.73-22.36 67.08L534.35 748.9a49.976 49.976 0 0 1-22.36 5.28z\"  ></path></symbol><symbol id=\"icon-jurassic_server\" viewBox=\"0 0 1024 1024\"><path d=\"M96 64v256.7h832V64H96z m678.6 160.3H614.2v-63.9h160.5v63.9zM96 960h832V703.3H96V960z m518.2-160.3h160.5v63.9H614.2v-63.9zM96 636.4h832V379.6H96v256.8zM614.2 476h160.5v64H614.2v-64z\"  ></path></symbol><symbol id=\"icon-shujuku2\" viewBox=\"0 0 1024 1024\"><path d=\"M209.85714247 842.94285753a37.73571415 37.73571415 0 0 0 38.18571506-38.18571506 37.73571415 37.73571415 0 0 0-38.12142921-38.12142832 37.73571415 37.73571415 0 0 0-38.18571417 38.12142832c0 21.40714249 16.77857167 38.18571417 38.18571417 38.18571504z m-11.05714247-38.18571506a11.57142832 11.57142832 0 0 1 11.12142832-11.05714247 11.57142832 11.57142832 0 0 1 11.05714336 11.05714247 11.57142832 11.57142832 0 0 1-11.05714336 11.1214292 11.57142832 11.57142832 0 0 1-11.12142832-11.1214292z m11.12142832-611.61428496a37.73571415 37.73571415 0 0 0-38.18571415 38.12142832c0 21.34285751 16.77857167 38.12142832 38.18571415 38.12142832a37.73571415 37.73571415 0 0 0 38.12142921-38.12142832 37.73571415 37.73571415 0 0 0-38.12142921-38.12142832z m11.05714336 38.12142832a11.57142832 11.57142832 0 0 1-11.05714336 11.05714249 11.57142832 11.57142832 0 0 1-11.12142832-11.05714249 11.57142832 11.57142832 0 0 1 11.12142832-11.05714336 11.57142832 11.57142832 0 0 1 11.05714336 11.05714336zM209.92142832 475.35714249a37.73571415 37.73571415 0 0 0-38.18571417 38.12142919c0 21.34285751 16.77857167 38.12142832 38.18571418 38.12142832a37.73571415 37.73571415 0 0 0 38.12142918-38.12142832 37.73571415 37.73571415 0 0 0-38.12142918-38.12142919z m11.05714336 38.12142919a11.57142832 11.57142832 0 0 1-11.05714336 11.05714249 11.57142832 11.57142832 0 0 1-11.12142832-11.05714249 11.57142832 11.57142832 0 0 1 11.12142832-11.12142919 11.57142832 11.57142832 0 0 1 11.05714336 11.12142919z\"  ></path><path d=\"M891.15714247 63.02857168h-758.57142832c-38.57142832 0-70.07142832 31.43571417-70.07142832 70.07142832v758.44285751c0 38.63571415 31.5 70.07142832 70.07142832 70.07142832h758.44285753c38.63571415 0 70.07142832-31.43571417 70.07142832-70.07142832V133.1a70.07142832 70.07142832 0 0 0-69.94285753-70.07142832zM112.52857168 668.21428583H911.21428583v210.66428584a35.67857168 35.67857168 0 0 1-35.61428583 35.6785708H148.14285753a35.67857168 35.67857168 0 0 1-35.6785717-35.67857079V668.27857167z m0-52.97142832V392.17142832H911.21428583v223.26428585H112.52857168v-0.12857168zM911.21428583 339.2H112.52857168V142.1c0-17.61428585 14.27142832-31.88571415 31.88571417-31.88571415h731.18571415c19.67142832 0 35.61428585 16.00714247 35.61428585 35.67857168v193.30714247z\"  ></path></symbol><symbol id=\"icon-shujuku3\" viewBox=\"0 0 1029 1024\"><path d=\"M32.2592 294.4l409.6 211.2c19.2 12.8 44.8 12.8 70.4 12.8s51.2-6.4 70.4-12.8l409.6-211.2c38.4-19.2 38.4-51.2 0-70.4l-409.6-211.2c-19.2-6.4-44.8-12.8-70.4-12.8s-51.2 6.4-70.4 12.8l-409.6 211.2c-38.4 19.2-38.4 51.2 0 70.4z m441.6-204.8c12.8-6.4 25.6-6.4 38.4-6.4s32 6.4 38.4 6.4l339.2 172.8-339.2 172.8c-6.4 6.4-19.2 6.4-38.4 6.4-12.8 0-32-6.4-38.4-6.4l-339.2-172.8 339.2-172.8z\"  ></path><path d=\"M19.4592 550.4l403.2 204.8c25.6 12.8 57.6 19.2 89.6 19.2s64-6.4 89.6-19.2l403.2-204.8c12.8-6.4 25.6-32 12.8-51.2s-32-32-51.2-19.2l-403.2 204.8c-32 12.8-83.2 12.8-108.8 0l-403.2-204.8c-12.8-12.8-25.6-12.8-38.4 0-6.4 12.8-12.8 25.6-12.8 38.4 0 19.2 6.4 25.6 19.2 32z\"  ></path><path d=\"M1024.2592 748.8c-12.8-19.2-32-32-51.2-19.2l-403.2 204.8c-32 12.8-83.2 12.8-108.8 0l-403.2-204.8c-12.8-6.4-38.4 6.4-51.2 19.2-12.8 12.8-6.4 38.4 12.8 51.2l403.2 204.8c25.6 12.8 57.6 19.2 89.6 19.2s64-6.4 96-19.2l403.2-204.8c12.8-6.4 25.6-32 12.8-51.2z\"  ></path></symbol><symbol id=\"icon-shujukushuju\" viewBox=\"0 0 1030 1024\"><path d=\"M966.4 435.2c-12.8 0-19.2 0-25.6 6.4L518.4 646.4 102.4 441.6c-12.8-6.4-19.2-6.4-32-6.4-38.4 0-64 25.6-64 64 0 25.6 12.8 44.8 38.4 57.6l448 224c6.4 6.4 19.2 6.4 25.6 6.4s19.2 0 25.6-6.4l448-224c25.6-12.8 38.4-38.4 38.4-57.6 0-38.4-25.6-64-64-64z m0 217.6c-12.8 0-19.2 0-25.6 6.4L518.4 870.4l-422.4-211.2c-6.4-6.4-19.2-6.4-32-6.4-38.4 0-64 25.6-64 64 0 25.6 12.8 44.8 38.4 57.6l448 224c6.4 6.4 19.2 6.4 25.6 6.4s19.2 0 25.6-6.4l448-224c19.2-12.8 38.4-32 38.4-57.6 6.4-32-19.2-64-57.6-64zM44.8 326.4l448 224c6.4 6.4 12.8 12.8 25.6 12.8s19.2 0 25.6-6.4l448-224c25.6-12.8 38.4-38.4 38.4-64s-12.8-51.2-38.4-57.6l-448-192C537.6 19.2 524.8 12.8 518.4 12.8c-6.4 0-19.2 0-25.6 6.4l-448 192c-25.6 12.8-38.4 32-38.4 57.6s12.8 51.2 38.4 57.6z\"  ></path></symbol><symbol id=\"icon-shujuku1\" viewBox=\"0 0 1024 1024\"><path d=\"M939.24010667 337.77618489V143.57481245c0-21.44847645-17.3876717-38.83614815-38.83614815-38.83614815h-776.80791704c-21.44847645 0-38.83614815 17.3876717-38.83614815 38.83614815v194.20137244c0 19.44598755 14.29170252 35.55328 32.94518992 38.3919597-18.65348741 2.8386797-32.94518992 18.94597215-32.94518992 38.39195971v194.20137245c0 20.53461333 15.94102518 37.33731555 36.1224723 38.731776-20.18144711 1.39446045-36.1224723 18.19594903-36.1224723 38.731776v194.20258607c0 21.44847645 17.3876717 38.83614815 38.83614815 38.83614815h776.80791704c21.44847645 0 38.83614815-17.3876717 38.83614815-38.83614815v-194.20501334c0-20.53461333-15.94223882-37.33731555-36.1224723-38.731776 20.18144711-1.39446045 36.1224723-18.19594903 36.1224723-38.731776V414.55889067c0-19.44598755-14.29291615-35.55328-32.94518992-38.3919597 18.65227378-2.83746608 32.94518992-18.94597215 32.94518992-38.39074608z m-776.80791704-155.3652243h699.13562074v116.52907616h-699.13562074v-116.52907616z m699.13562074 659.17807882h-699.13562074v-116.53028978h699.13562074v116.53028978z m0-271.66492444h-699.13562074v-116.52907615h699.13562074v116.52907615z\"  ></path></symbol><symbol id=\"icon-peizhishujuyuan\" viewBox=\"0 0 1024 1024\"><path d=\"M383.977281 638.040122C248.113773 629.720642 131.961033 595.162802 63.99728 546.909818V639.960002c0 63.420036 138.359353 116.088744 319.980001 126.200113v63.868008c-135.863509-8.31948-252.016249-42.813324-319.980001-91.130304V831.948003H0.00128V223.986001C0.00128 100.281732 200.564745 0 447.973282 0s447.972002 100.281732 447.972001 223.986001v202.099369h-63.996V339.434785C753.554183 404.454722 610.907098 447.972002 447.973282 447.972002s-305.580901-43.51728-383.976002-108.537217V447.972002c0 63.420036 138.359353 116.088744 319.980001 126.200112v63.868008zM447.973282 1023.936004c-247.408537 0-447.972002-85.946628-447.972002-191.988001h63.996c0 70.71558 171.893257 127.992 383.976002 127.992001v63.996zM447.973282 383.976001c212.082745 0 383.976001-71.67552 383.976001-159.99S660.056027 63.996 447.973282 63.996 63.99728 135.671521 63.99728 223.986001 235.890537 383.976001 447.973282 383.976001z m556.893194 419.813762l-44.541216-25.91838c2.495844-13.631148 4.095744-27.51828 4.095744-41.91738a231.665521 231.665521 0 0 0-4.095744-41.91738l44.541216-25.982376a38.589588 38.589588 0 0 0 13.951128-52.476721l-38.07762-66.491844a37.885632 37.885632 0 0 0-52.028748-14.07912l-45.053185 26.23836a227.825761 227.825761 0 0 0-71.547528-42.23736V486.369602c0-21.182676-17.022936-38.3976-38.07762-38.3976h-76.15524a38.269608 38.269608 0 0 0-38.07762 38.3976v32.63796a227.825761 227.825761 0 0 0-71.547529 42.23736l-45.053184-26.23836a37.885632 37.885632 0 0 0-52.028748 14.07912L453.092962 615.641522a38.589588 38.589588 0 0 0 13.951128 52.476721l44.541216 25.91838a231.665521 231.665521 0 0 0-4.095744 41.91738c0 14.3991 1.5999 28.286232 4.095744 41.91738l-44.541216 25.982376a38.589588 38.589588 0 0 0-13.951128 52.47672l38.07762 66.491845a37.885632 37.885632 0 0 0 52.028748 14.07912l45.053184-26.23836c21.054684 18.046872 44.925192 32.765952 71.547529 42.23736v32.63796c0 21.182676 17.022936 38.3976 38.07762 38.3976h76.15524a38.269608 38.269608 0 0 0 38.07762-38.3976v-32.63796a227.825761 227.825761 0 0 0 71.547528-42.23736l45.053185 26.23836a37.949628 37.949628 0 0 0 52.028748-14.07912l38.07762-66.555841a38.589588 38.589588 0 0 0-13.951128-52.47672z m-47.293044 37.821636l-17.534904 29.886132a17.726892 17.726892 0 0 1-23.9985 6.271608l-48.764953-27.646272a175.989001 175.989001 0 0 1-96.185988 54.652585v37.949628c0 9.471408-7.871508 17.214924-17.5989 17.214924h-35.069808a17.406912 17.406912 0 0 1-17.5989-17.27892v-37.885632a175.989001 175.989001 0 0 1-96.185989-54.652585l-48.764952 27.646272a17.726892 17.726892 0 0 1-23.9985-6.271608l-17.534904-29.886132a17.022936 17.022936 0 0 1 6.3996-23.486532l48.95694-27.774264a168.245485 168.245485 0 0 1-9.27942-54.3966c0-19.070808 3.455784-37.245672 9.343416-54.3966l-49.020936-27.774264a17.022936 17.022936 0 0 1-6.3996-23.486533l17.534904-29.886132a17.726892 17.726892 0 0 1 23.9985-6.271608l48.764952 27.646272a175.989001 175.989001 0 0 1 96.185989-54.652584v-37.949628c0-9.535404 7.871508-17.214924 17.5989-17.214924h35.069808c9.727392 0 17.5989 7.67952 17.5989 17.27892v37.885632a175.989001 175.989001 0 0 1 96.185988 54.652584l48.764953-27.646272a17.726892 17.726892 0 0 1 23.9985 6.271608l17.534904 29.886132a17.022936 17.022936 0 0 1-6.3996 23.486533l-48.95694 27.774264c5.75964 17.150928 9.27942 35.325792 9.27942 54.3966 0 19.070808-3.455784 37.245672-9.27942 54.3966l48.95694 27.774264a17.022936 17.022936 0 0 1 6.3996 23.486532zM735.955283 639.960002a95.994 95.994 0 1 0 0 191.988001 95.994 95.994 0 0 0 0-191.988001z m0 127.992001a31.998 31.998 0 1 1 0-63.996 31.998 31.998 0 0 1 0 63.996z\"  ></path></symbol><symbol id=\"icon-SQLlishichaxun\" viewBox=\"0 0 1024 1024\"><path d=\"M604.16 707.072c0-22.016-4.608-39.424-13.312-51.712-8.704-12.288-20.48-18.432-35.84-18.432s-27.136 6.144-35.84 18.432c-8.704 12.288-12.8 29.696-12.8 51.712 0 23.552 4.096 41.984 12.8 55.296 8.704 13.312 20.48 19.456 35.84 19.456s27.136-6.656 35.84-19.456c9.216-13.312 13.312-31.744 13.312-55.296z\"  ></path><path d=\"M836.096 34.304H187.904c-51.2 0-93.696 42.496-93.696 93.696v768c0 51.2 42.496 93.696 93.696 93.696h648.704c51.2 0 93.696-42.496 93.696-93.696V128c0-51.2-43.008-93.696-94.208-93.696zM400.384 805.376c-16.384 14.336-37.888 21.504-64.512 21.504-33.28 0-61.44-10.752-85.504-32.768l31.232-37.888c18.432 15.36 36.864 23.552 55.808 23.552 19.968 0 29.696-6.656 29.696-19.968 0-6.144-3.072-10.752-8.704-14.848-3.072-2.048-10.752-5.632-23.552-11.264l-29.184-12.288c-30.72-12.288-46.592-33.28-46.592-62.464 0-19.456 7.68-35.84 23.552-48.64 15.872-13.312 35.84-20.48 59.392-20.48 29.696 0 54.272 10.24 74.24 30.208l-27.136 34.304c-15.36-11.776-31.232-17.408-47.104-17.408-18.432 0-27.136 6.144-27.136 18.944 0 5.632 3.072 10.24 9.728 14.336 3.584 2.048 11.776 6.144 25.088 11.264l28.672 11.264c30.208 12.288 45.056 32.768 45.056 62.464-0.512 19.968-7.68 36.864-23.04 50.176zM252.416 418.304c0-18.944 15.36-34.304 34.304-34.304h252.416a34.304 34.304 0 0 1 0 68.608H286.72c-18.944-0.512-34.304-15.872-34.304-34.304z m379.392 468.992c-48.128 0-81.92-20.992-100.864-62.464-24.576-5.632-44.032-18.432-57.856-38.4-14.336-20.992-22.016-47.104-22.016-78.848 0-36.864 9.728-66.048 28.672-87.04 18.944-20.48 44.032-30.208 75.776-30.208 31.744 0 56.832 10.24 75.776 30.72 19.456 20.992 28.672 50.176 28.672 86.528 0 29.696-6.656 55.296-19.456 75.776-12.8 19.456-30.208 32.768-52.736 39.424 9.728 14.336 26.112 21.504 49.664 21.504 9.216 0 17.408-1.536 25.6-4.096l9.728 39.936c-9.216 4.608-23.04 7.168-40.96 7.168z m42.496-583.68H286.72a34.304 34.304 0 0 1 0-68.608h387.584c18.944 0 34.304 15.36 34.304 34.304-0.512 18.944-15.872 34.304-34.304 34.304z m172.032 519.168h-143.872V593.92H757.76v182.272h88.576v46.592z\"  ></path></symbol><symbol id=\"icon-zhongmingming\" viewBox=\"0 0 1024 1024\"><path d=\"M950.857143 950.857143H73.142857V73.142857h373.028572v73.142857H146.285714v731.428572h731.428572V548.571429h73.142857z\"  ></path><path d=\"M899.657143 160.914286l-51.2-51.2L292.571429 665.6l51.2 51.2 555.885714-555.885714z\"  ></path></symbol><symbol id=\"icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun\" viewBox=\"0 0 1024 1024\"><path d=\"M420 960z m0-48z\"  ></path><path d=\"M200 240h496a21.28 21.28 0 0 1 24 24 21.28 21.28 0 0 1-24 24h-496a21.28 21.28 0 0 1-24-24 21.28 21.28 0 0 1 24-24zM200 400h496a21.28 21.28 0 0 1 24 24 21.28 21.28 0 0 1-24 24h-496a21.28 21.28 0 0 1-24-24 21.28 21.28 0 0 1 24-24zM200 576h240a21.28 21.28 0 0 1 24 24 21.28 21.28 0 0 1-24 24h-240a24 24 0 0 1 0-48z\"  ></path><path d=\"M955.36 932.64l-96-96a160 160 0 1 0-22.56 22.56l96 96a16 16 0 0 0 22.72-22.72zM608 736a128 128 0 1 1 128 128 128 128 0 0 1-128-128z\"  ></path><path d=\"M832 128v392a24 24 0 0 1-48 0V128a16 16 0 0 0-16-16H128a16 16 0 0 0-16 16v768a16 16 0 0 0 16 16h392a24 24 0 0 1 0 48H128a64 64 0 0 1-64-64V128a64 64 0 0 1 64-64h640a64 64 0 0 1 64 64z\"  ></path></symbol><symbol id=\"icon-clickhouse-yunshujukuClickHouse\" viewBox=\"0 0 1024 1024\"><path d=\"M864.213333 591.082667l61.226667 28.053333L517.333333 981.333333 106.666667 619.136l61.226666-28.053333 349.44 311.168 346.88-311.168zM440.832 318.144v385.152h-58.666667V318.144h58.666667z m104.576 0v385.152h-58.666667V318.144h58.666667z m102.037333 0v385.152h-58.666666V318.144h58.666666z m107.136 219.370667v114.773333h-58.666666v-114.773333h58.666666z m-415.786666-153.045334v267.818667h-58.666667V384.469333h58.666667z m415.786666 0v114.773334h-58.666666v-114.773334h58.666666zM517.333333 42.666667l408.106667 359.658666-61.226667 30.592L517.333333 121.749333 167.893333 432.917333 106.666667 402.346667 517.333333 42.666667z\"  ></path></symbol><symbol id=\"icon-rds_mariadb\" viewBox=\"0 0 1024 1024\"><path d=\"M912.896 456.704s-2.048 0 0 0c-47.104-4.096-80.896 8.704-102.4 36.352-4.096 6.144-10.752 12.8-14.848 21.504-4.096 8.704-8.704 16.896-12.8 27.648-2.048 4.096-8.704 21.504-10.752 23.552-4.096 10.752-8.704 16.896-12.8 21.504l-6.656 6.144c-19.456 19.456-34.304 27.648-57.344 29.696-36.352 4.096-51.2 6.656-74.752 12.8-25.6 8.704-49.152 21.504-68.096 40.448-10.752 10.752-19.456 21.504-25.6 31.744-4.096 6.656-10.752 21.504-12.8 21.504-4.096 6.144-4.096 6.144-12.8 6.144-25.6 0-45.056 21.504-49.152 47.104-4.096 29.696 14.848 51.2 49.152 49.152 27.648-2.048 49.152-10.752 74.752-27.648 4.096-2.048 19.456-12.8 21.504-14.848 6.144-4.096 10.752-6.656 12.8-6.656 12.8-4.096 70.656-10.752 93.696-12.8v2.048c-2.048 12.8-8.704 21.504-14.848 27.648-8.704 8.704-12.8 19.456-4.096 29.696 4.096 6.144 12.8 8.704 21.504 8.704 10.752 0 25.6-4.096 40.448-10.752 27.648-12.8 51.2-31.744 64-59.904 12.8 10.752 25.6 14.848 38.4 12.8 19.456-2.048 31.744-10.752 34.304-25.6 2.048-6.656 0-12.8-4.096-16.896-12.8-10.752-16.896-23.552-14.848-40.448 0-4.096 2.048-10.752 4.096-14.848 0 0 6.656-10.752 8.704-16.896 8.704-16.896 14.848-36.352 16.896-66.048v-4.096c2.048-27.648 8.704-42.496 16.896-47.104 16.896-8.704 29.696-29.696 31.744-53.248 6.144-15.36-2.56-38.4-30.208-38.4z\"  ></path><path d=\"M473.6 722.944l-40.448-68.096-57.344 96.256H210.944L128 614.4l80.896-136.704h132.096L270.848 360.448l80.896-136.704h166.4L599.04 360.448l-70.656 117.248h125.952l68.096 113.152c6.144-4.096 10.752-8.704 19.456-14.848l6.656-6.656s2.048-4.096 6.144-10.752L682.496 435.2h-74.752l45.056-74.752-106.496-179.2H328.704l-106.496 179.2L266.752 435.2H185.344l-106.496 179.2 106.496 179.2h215.552l31.744-53.248 10.752 16.896c8.704-14.848 17.408-25.6 30.208-34.304z\"  ></path></symbol><symbol id=\"icon-jianshaojianqujianhao\" viewBox=\"0 0 1024 1024\"><path d=\"M308.7 470.9h406.2V552H308.7v-81.1z m203.1 405.5c201.9 0 365.6-163.4 365.6-365s-163.7-365-365.6-365c-201.9 0-365.6 163.4-365.6 365s163.7 365 365.6 365z m0 81.1C265 957.5 65 757.8 65 511.4c0-246.3 200-446 446.8-446s446.8 199.7 446.8 446.1c0 246.3-200 446-446.8 446z m0 0\"  ></path></symbol><symbol id=\"icon-sqlserver\" viewBox=\"0 0 1024 1024\"><path d=\"M215.846 671.657c54.004-35.684 107.171-74.018 165.288-102.444 60.464-26.253 120.071-47.82 182.395-70.003 60.822-22.361 122.782-40.591 185.705-56.6 62.935-15.947 126.791-28.496 191.238-37.383l-9.76 19.962c-14.888-24.35-34.334-45.821-56.344-64.332-22.02-18.564-46.653-34.11-72.414-47.6a497.77 497.77 0 0 0-19.584-9.685c-6.597-3.106-13.241-6.132-19.963-9.003a599.898 599.898 0 0 0-20.331-8.266 695.449 695.449 0 0 0-20.608-7.678c-27.652-9.841-55.948-18.185-84.619-25.233-28.659-7.003-57.794-12.756-86.917-16.841-3.636-0.498-7.247-0.97-10.797-1.349l-11.274-1.236a2146.98 2146.98 0 0 1-22.539-2.722c-15.023-1.862-30.02-4.012-44.999-6.311-29.939-4.727-59.814-10.263-89.313-17.815-7.368-1.917-14.71-3.951-22.012-6.155-7.301-2.205-14.569-4.561-21.728-7.285-7.159-2.713-14.259-5.65-21.132-9.179-3.424-1.788-6.796-3.715-10.028-5.926-1.614-1.111-3.196-2.288-4.697-3.616a27.685 27.685 0 0 1-2.167-2.143c-0.347-0.386-0.683-0.79-1.006-1.225a11.139 11.139 0 0 1-0.477-0.691l-0.232-0.388c-0.081-0.149-0.142-0.252-0.271-0.547a1.62 1.62 0 1 1 2.971-1.294c-0.026-0.083 0.031 0.018 0.078 0.066l0.166 0.193c0.121 0.135 0.257 0.276 0.4 0.415 0.286 0.279 0.601 0.555 0.932 0.822a23.997 23.997 0 0 0 2.122 1.519c1.484 0.962 3.066 1.832 4.69 2.633 3.253 1.602 6.669 2.955 10.136 4.177 6.931 2.464 14.076 4.447 21.288 6.168 14.442 3.405 29.1 6.078 43.857 8.208 29.503 4.34 59.261 7.279 89.071 9.662 14.902 1.23 29.833 2.21 44.766 3.183l22.415 1.309 11.216 0.625c3.95 0.212 7.823 0.513 11.665 0.833 15.366 1.279 30.547 3.217 45.704 5.485 15.153 2.25 30.215 5.067 45.224 8.19 29.998 6.318 59.705 14.396 88.786 24.637 7.262 2.579 14.496 5.261 21.674 8.104a633.708 633.708 0 0 1 21.366 9.004c7.068 3.145 14.1 6.396 21.039 9.854a530.944 530.944 0 0 1 20.593 10.872 434.182 434.182 0 0 1 39.592 24.811c12.771 8.993 25.085 18.719 36.728 29.26 23.299 21.032 43.896 45.489 59.789 72.911l10.27 17.718-20.028 2.244c-63.62 7.125-126.857 18.122-189.391 32.393-62.549 14.216-124.444 32.854-185.355 53.341-60.944 20.386-120.986 42.426-179.757 68.681-29.383 13.128-58.455 26.986-87.121 41.661-14.349 7.303-28.602 14.803-42.719 22.554-14.101 7.766-28.14 15.716-41.803 24.169l-0.029 0.018a1.623 1.623 0 0 1-1.749-2.732z\"  ></path><path d=\"M209.963 677.652c20.688-16.411 40.803-33.515 59.903-51.661 19.143-18.088 37.364-37.122 54.246-57.217 8.426-10.058 16.51-20.385 24.139-31.006 7.608-10.626 14.793-21.563 21.103-32.862a183.354 183.354 0 0 0 4.47-8.515l0.506-1.056 0.557-1.205 0.991-2.27 1.889-4.479a313.186 313.186 0 0 0 3.579-9.053 301.38 301.38 0 0 0 3.343-9.108 325.21 325.21 0 0 0 5.77-18.427c1.738-6.176 3.177-12.413 4.475-18.649a227.377 227.377 0 0 0 3.041-18.802c0.772-6.281 1.302-12.572 1.521-18.859 0.115-3.143 0.151-6.285 0.117-9.42-0.01-3.136-0.095-6.268-0.254-9.392a192.937 192.937 0 0 0-1.856-18.635c-3.512-24.695-11.826-48.505-23.843-70.371-5.981-10.957-12.992-21.366-20.633-31.296-7.681-9.896-16.184-19.212-25.226-27.776a175.341 175.341 0 0 0-6.899-6.159l-1.733-1.436-1.576-1.252c0.077 0.06-0.885-0.679-0.535-0.417l-0.117-0.102-0.233-0.204-0.466-0.407-0.948-0.874-1.88-1.762-3.716-3.564a282.594 282.594 0 0 1-3.577-3.748c-2.395-2.494-4.61-5.203-6.828-7.915-4.306-5.549-8.327-11.507-11.268-18.321-1.43-3.413-2.653-7.043-3.015-10.975-0.187-1.948-0.123-4.001 0.423-5.963 0.294-0.976 0.716-1.918 1.291-2.729 0.559-0.808 1.319-1.497 2.181-1.788-0.704 1.638-0.39 3.185 0.184 4.521 0.554 1.347 1.395 2.552 2.299 3.71 1.866 2.293 4.085 4.373 6.421 6.364 4.685 3.976 9.759 7.672 14.905 11.32 2.526 1.876 5.178 3.629 7.736 5.496 1.294 0.917 2.601 1.824 3.917 2.721l3.889 2.773 3.523 2.455 1.247 0.924 1.1 0.842 2.118 1.674a198.672 198.672 0 0 1 8.03 6.844c10.402 9.296 19.787 19.517 28.375 30.409 8.616 10.868 16.206 22.564 22.811 34.816 6.636 12.24 11.99 25.197 16.188 38.51 4.164 13.33 6.91 27.101 8.338 40.964 2.73 27.772-0.331 55.83-8.101 82.046-1.896 6.572-4.157 13.016-6.587 19.364-2.426 6.352-5.143 12.571-8.058 18.671a308.25 308.25 0 0 1-4.513 9.065 325.117 325.117 0 0 1-4.848 8.863l-2.528 4.382-1.267 2.135-0.513 1.027-0.6 1.157c-1.605 3.046-3.298 5.961-5.037 8.852-6.976 11.521-14.706 22.418-22.84 32.979-8.137 10.557-16.735 20.716-25.658 30.556-35.729 39.361-76.427 73.677-119.473 104.235z\"  ></path><path d=\"M690.811 462.863c-11.202-10.033-23.855-19.852-35.554-29.38-11.68-9.552-23.726-18.717-36.167-27.376-6.239-4.303-12.542-8.533-18.979-12.561a597.859 597.859 0 0 0-19.576-11.684c-26.471-14.997-54.155-28.04-82.668-39.175-28.453-11.315-57.758-20.741-87.595-28.368-29.823-7.76-27.023-23.895 4.021-18.259 31.023 5.783 61.779 13.468 91.98 23.226 30.136 9.932 59.694 21.938 88.283 36.152a624.38 624.38 0 0 1 21.287 11.007c7.038 3.795 14.057 7.649 20.979 11.689 13.88 8.018 27.484 16.59 40.701 25.777 13.191 9.221 25.917 19.161 37.968 29.873a391.554 391.554 0 0 1 17.569 16.581\"  ></path><path d=\"M504.265 210.272c7.783 20.086 13.193 41.187 16.299 62.71 3.1 21.525 3.785 43.508 1.912 65.352a297.933 297.933 0 0 1-1.9 16.374 214.153 214.153 0 0 1-1.403 8.137 249.382 249.382 0 0 1-1.724 8.121c-2.505 10.788-5.811 21.432-10.021 31.704-4.19 10.278-9.157 20.225-14.752 29.741-5.613 9.508-11.797 18.612-18.305 27.395-6.501 8.792-13.396 17.21-20.378 25.483-7.015 8.273-14.046 16.353-21.409 24.36-14.654 15.997-30.276 31.101-46.621 45.297-16.339 14.21-33.456 27.443-51.069 39.884a713.556 713.556 0 0 1-26.824 18.032 1231.056 1231.056 0 0 1-13.691 8.56c-4.612 2.777-9.218 5.555-13.899 8.215l12.484-10.215c4.177-3.373 8.331-6.772 12.459-10.198a1921.952 1921.952 0 0 0 24.653-20.604c16.29-13.859 32.193-28.084 47.546-42.809a789.044 789.044 0 0 0 44.143-45.921c3.516-3.964 6.976-7.995 10.467-12.047 3.489-4.036 6.954-8.08 10.37-12.151 6.84-8.134 13.449-16.402 19.757-24.824 6.299-8.429 12.219-17.076 17.55-26.025 5.357-8.931 10.134-18.162 14.155-27.707 4.007-9.548 7.361-19.368 9.895-29.436a234.407 234.407 0 0 0 1.772-7.585c0.542-2.582 1.021-4.99 1.513-7.687a294.292 294.292 0 0 0 2.503-15.502c2.848-20.812 3.639-41.967 2.602-63.149-1.024-21.194-3.927-42.38-8.084-63.505zM654.02 238.096c0.518 2.054 0.892 4.13 1.276 6.211 0.327 2.083 0.623 4.175 0.888 6.277 0.492 4.202 0.758 8.433 0.847 12.671a159.8 159.8 0 0 1-1.553 25.397c-0.272 2.107-0.622 4.201-0.985 6.293l-0.568 3.133-0.146 0.783-0.037 0.196-0.048 0.217-0.089 0.377-0.355 1.51c-0.983 4.118-2.055 8.2-3.201 12.268a305.387 305.387 0 0 1-7.876 24.147c-6.003 15.864-13.405 31.254-22.155 45.773-8.718 14.542-18.58 28.298-29.112 41.369a580.201 580.201 0 0 1-16.155 19.239l-8.222 9.268-8.147 9.285c-5.462 6.183-10.864 12.463-16.47 18.629a647.404 647.404 0 0 1-8.477 9.225 474.458 474.458 0 0 1-8.775 9.014 324.52 324.52 0 0 1-38.248 32.636c-6.761 4.911-13.643 9.618-20.685 14.061a599.722 599.722 0 0 1-10.618 6.553c-3.593 2.097-7.168 4.209-10.821 6.199l9.5-8.051a724.68 724.68 0 0 0 9.454-8.048c6.244-5.421 12.45-10.848 18.486-16.427 12.113-11.12 23.596-22.724 34.324-35.011 10.743-12.269 20.933-25.157 31.472-37.996 2.617-3.221 5.307-6.392 7.984-9.578 2.676-3.174 5.42-6.377 8.074-9.454 5.335-6.216 10.599-12.472 15.667-18.857 10.133-12.769 19.664-25.921 28.028-39.733 8.354-13.812 15.638-28.22 21.504-43.251a291.694 291.694 0 0 0 7.984-22.877 337.49 337.49 0 0 0 3.316-11.68l0.398-1.5 0.1-0.375 0.1-0.34 0.219-0.725 0.86-2.906 1.649-5.834c2.136-7.801 4.107-15.67 5.84-23.667a524.034 524.034 0 0 0 2.471-12.092c0.412-2.032 0.794-4.076 1.145-6.13l1.157-6.199zM822.932 290.223c-3.896 19.357-10.441 38.246-19.119 56.222-8.687 17.978-19.675 34.894-32.108 50.501-12.437 15.627-26.202 30.026-40.509 43.656-7.143 6.636-14.486 13.05-22.189 19.208a221.19 221.19 0 0 1-11.889 8.92c-4.093 2.849-8.298 5.586-12.806 7.972 2.541-4.421 5.308-8.567 8.153-12.638 2.847-4.068 5.8-8.029 8.868-11.893 6.117-7.736 12.646-15.163 19.583-22.159 13.807-13.665 27.259-27.554 39.798-42.145 12.549-14.581 24.136-29.908 34.447-46.224a393.993 393.993 0 0 0 14.589-25.138c4.585-8.592 8.915-17.377 13.182-26.282z\"  ></path><path d=\"M345.965 547.814c33.042-32.345 69.5-61.194 107.723-87.398 38.283-26.148 78.514-49.519 120.168-69.979 41.652-20.465 84.758-37.989 128.859-52.278 44.111-14.228 89.215-25.373 134.961-32.052-44.042 13.988-87.524 28.726-130.216 45.387a1606.714 1606.714 0 0 0-125.675 55.097 1600.647 1600.647 0 0 0-120.477 65.695c-39.289 23.588-77.555 48.94-115.343 75.528zM393.713 444.275a383.308 383.308 0 0 1 14.563-17.172c4.999-5.596 10.141-11.057 15.365-16.442 10.466-10.75 21.354-21.084 32.536-31.091 22.4-19.971 46.024-38.562 70.553-55.831a836.583 836.583 0 0 1 76.195-47.736c13.12-7.242 26.423-14.146 39.945-20.604 6.768-3.216 13.57-6.355 20.448-9.339a446.32 446.32 0 0 1 20.835-8.515 987.665 987.665 0 0 1-18.4 12.902l-18.481 12.625-36.948 24.996c-24.595 16.627-49.021 33.367-73.24 50.387a3321.594 3321.594 0 0 0-71.992 52.044c-11.912 8.828-23.761 17.766-35.621 26.754l-17.811 13.515a1597.439 1597.439 0 0 1-17.947 13.507zM394.681 318.676c1.843-3.003 3.827-5.863 5.873-8.676 2.037-2.817 4.156-5.553 6.308-8.26 4.318-5.397 8.851-10.586 13.516-15.641 9.362-10.074 19.302-19.556 29.712-28.485a363.39 363.39 0 0 1 32.68-25.008c5.703-3.84 11.517-7.531 17.503-10.982 3.002-1.715 6.025-3.397 9.116-4.987 3.085-1.597 6.214-3.131 9.461-4.503-2.156 2.79-4.425 5.426-6.72 8.021-2.288 2.605-4.634 5.128-6.985 7.641-4.72 5.001-9.559 9.832-14.452 14.583-9.814 9.467-19.885 18.567-30.198 27.384-10.311 8.819-20.847 17.371-31.695 25.625-5.44 4.109-10.947 8.157-16.598 12.07-2.835 1.947-5.68 3.887-8.595 5.762-2.906 1.879-5.851 3.734-8.926 5.456z\"  ></path><path d=\"M377.385 198.376c7.314 10.42 13.067 21.728 18.192 33.278 2.505 5.8 4.857 11.673 6.954 17.642 0.531 1.49 1.042 2.987 1.542 4.489 0.529 1.601 1.014 2.927 1.52 4.539 0.979 3.108 1.859 6.175 2.617 9.317 3.056 12.519 4.389 25.521 3.754 38.388a123.34 123.34 0 0 1-2.46 19.107c-1.343 6.278-3.107 12.454-5.732 18.401-1.064-6.414-1.678-12.653-2.379-18.819a1281.01 1281.01 0 0 0-2.105-18.259c-1.469-12.039-3.251-23.862-5.484-35.695l-1.735-8.858c-0.27-1.405-0.624-3.078-0.93-4.478l-1.037-4.493c-1.406-5.988-2.791-12.004-4.25-18.017-2.814-12.069-5.852-24.104-8.467-36.542zM400.461 418.669c3.467 0.23 6.864 0.806 10.224 1.573 3.365 0.756 6.67 1.755 9.938 2.907 6.52 2.334 12.803 5.438 18.746 9.173 11.894 7.464 22.255 17.681 30.145 29.499 7.903 11.82 13.317 25.195 15.93 38.873 1.309 6.843 1.981 13.755 1.945 20.638-0.039 3.445-0.206 6.874-0.597 10.289-0.379 3.412-0.928 6.805-1.798 10.171-1.359-3.2-2.545-6.363-3.734-9.485l-3.545-9.241c-2.362-6.078-4.739-12.002-7.301-17.747-5.113-11.491-10.798-22.307-17.533-32.398-6.715-10.105-14.476-19.442-23.241-28.313-4.378-4.444-9.038-8.73-13.914-12.995-2.432-2.145-4.94-4.247-7.484-6.394-2.553-2.137-5.163-4.266-7.781-6.55zM213.98 674.63s-101.966 69.187-52.733 141.79c0 0 35.523 62.631 161.722 96.598 0 0 144.272 33.34 219.991 29.601 0 0 9.038-1.869-16.203-6.854 0 0-72.877-10.811-146.453-25.862-51.476-10.532-100.322-31.201-122.46-45.183 0 0-129.315-71.046-68.864-149.258 0 0 20.914-32.032 52.056-54.967\"  ></path><path d=\"M535.012 942.074s48.847-108.205 75.021-251.542c0 0 17.688-104.204 0-192.57l19.944-7.041s19.941 83.071-3.116 230.147c0 0-20.753 119.558-82.45 220.829\"  ></path><path d=\"M542.96 941.841s-1.385 0.233-3.722 0.233c-3.706 0-3.725-1.792-3.725-1.792M165.298 756.591s225.6-62.942 291.66-90.364c0 0 127.756-49.232 165.772-87.871v13.088s-109.256 83.509-222.27 112.177c0 0-131.71 39.884-235.163 59.205v-6.235z\"  ></path><path d=\"M224.502 848.203s129.095-167.642 164.884-266.732l25.194-11.84S384.845 679.34 303.65 759.708c0 0-57.958 72.292-74.162 95.351l-4.986-6.856zM501.206 532.861s14.539 90.365-58.479 216.252c0 0-63.669 121.524-102.308 162.034h11.841s63.547-54.22 132.733-205.034c0 0 44.714-112.178 32.958-178.237l-16.745 4.985zM359.115 911.147s196.478-57.505 215.629-65.438v6.232c0 0.001-149.569 59.83-215.629 59.206z\"  ></path><path d=\"M581.599 842.594s-136.481-18.072-211.889-88.495c0 0-83.509-62.943-50.167-143.337l-27.733 6.232s-20.566 76.654 39.262 129.626c-0.001 0 87.247 98.466 250.527 95.974zM414.58 567.138s28.667 115.293 199.425 145.207l8.726-3.74s-192.562-64.189-192.562-149.777l-15.589 8.31z\"  ></path><path d=\"M235.098 855.059s199.9-66.06 244.919-86.626c0 0 82.264-26.798 142.714-66.059l-4.985 9.971s-56.089 38.639-150.815 75.407c0 0-100.336 41.755-236.819 72.915l4.986-5.608zM519.92 530.369s99.071 59.827 110.913 60.451v-4.363s-96.444-57.958-105.322-64.19l-5.591 8.102zM549.186 219.389s-55.458-79.146-70.416-150.815c0 0-206.281 68.553-194.44 105.322l25.76 12.556 4.154-1.961s-19.942-18.696 48.61-56.089c0 0 66.06-36.146 112.177-51.103 0 0 15.58 108.438 52.973 138.975l21.182 3.115z\"  ></path><path d=\"M341.042 134.633l50.526 67.306 24.944 2.493-75.47-76.654z\"  ></path><path d=\"M501.206 162.054s-160.164-11.217-153.309-34.276h8.102s136.751 26.976 145.207 25.552v8.724zM456.766 206.301s48.179-37.954 50.048-38.607l-3.116-5.64s-61.386 34.808-72.759 37.658c-11.374 2.851 25.827 6.589 25.827 6.589z\"  ></path><path d=\"M215.846 671.657c54.004-35.684 107.171-74.018 165.288-102.444 60.464-26.253 120.071-47.82 182.395-70.003 60.822-22.361 122.782-40.591 185.705-56.6 62.935-15.947 126.791-28.496 191.238-37.383l-9.76 19.962c-14.888-24.35-34.334-45.821-56.344-64.332-22.02-18.564-46.653-34.11-72.414-47.6a497.77 497.77 0 0 0-19.584-9.685c-6.597-3.106-13.241-6.132-19.963-9.003a599.898 599.898 0 0 0-20.331-8.266 695.449 695.449 0 0 0-20.608-7.678c-27.652-9.841-55.948-18.185-84.619-25.233-28.659-7.003-57.794-12.756-86.917-16.841-3.636-0.498-7.247-0.97-10.797-1.349l-11.274-1.236a2146.98 2146.98 0 0 1-22.539-2.722c-15.023-1.862-30.02-4.012-44.999-6.311-29.939-4.727-59.814-10.263-89.313-17.815-7.368-1.917-14.71-3.951-22.012-6.155-7.301-2.205-14.569-4.561-21.728-7.285-7.159-2.713-14.259-5.65-21.132-9.179-3.424-1.788-6.796-3.715-10.028-5.926-1.614-1.111-3.196-2.288-4.697-3.616a27.685 27.685 0 0 1-2.167-2.143c-0.347-0.386-0.683-0.79-1.006-1.225a11.139 11.139 0 0 1-0.477-0.691l-0.232-0.388c-0.081-0.149-0.142-0.252-0.271-0.547a1.62 1.62 0 1 1 2.971-1.294c-0.026-0.083 0.031 0.018 0.078 0.066l0.166 0.193c0.121 0.135 0.257 0.276 0.4 0.415 0.286 0.279 0.601 0.555 0.932 0.822a23.997 23.997 0 0 0 2.122 1.519c1.484 0.962 3.066 1.832 4.69 2.633 3.253 1.602 6.669 2.955 10.136 4.177 6.931 2.464 14.076 4.447 21.288 6.168 14.442 3.405 29.1 6.078 43.857 8.208 29.503 4.34 59.261 7.279 89.071 9.662 14.902 1.23 29.833 2.21 44.766 3.183l22.415 1.309 11.216 0.625c3.95 0.212 7.823 0.513 11.665 0.833 15.366 1.279 30.547 3.217 45.704 5.485 15.153 2.25 30.215 5.067 45.224 8.19 29.998 6.318 59.705 14.396 88.786 24.637 7.262 2.579 14.496 5.261 21.674 8.104a633.708 633.708 0 0 1 21.366 9.004c7.068 3.145 14.1 6.396 21.039 9.854a530.944 530.944 0 0 1 20.593 10.872 434.182 434.182 0 0 1 39.592 24.811c12.771 8.993 25.085 18.719 36.728 29.26 23.299 21.032 43.896 45.489 59.789 72.911l10.27 17.718-20.028 2.244c-63.62 7.125-126.857 18.122-189.391 32.393-62.549 14.216-124.444 32.854-185.355 53.341-60.944 20.386-120.986 42.426-179.757 68.681-29.383 13.128-58.455 26.986-87.121 41.661-14.349 7.303-28.602 14.803-42.719 22.554-14.101 7.766-28.14 15.716-41.803 24.169l-0.029 0.018a1.623 1.623 0 0 1-1.749-2.732z\"  ></path><path d=\"M209.963 677.652c41.157-33.063 79.92-69.152 113.493-109.449 8.364-10.084 16.385-20.433 23.952-31.061 7.543-10.635 14.661-21.577 20.888-32.849a181.168 181.168 0 0 0 4.4-8.473l1.057-2.271 0.956-2.286 1.811-4.49a309.195 309.195 0 0 0 3.422-9.077 297.476 297.476 0 0 0 3.198-9.113 337.505 337.505 0 0 0 5.489-18.396c1.652-6.155 2.99-12.367 4.214-18.561a224.385 224.385 0 0 0 2.809-18.658c0.702-6.223 1.173-12.449 1.331-18.663 0.085-3.106 0.092-6.209 0.031-9.306-0.033-3.095-0.14-6.185-0.318-9.267a189.717 189.717 0 0 0-1.964-18.362c-3.604-24.317-11.887-47.708-23.759-69.25-5.903-10.798-12.843-21.048-20.365-30.862-7.569-9.773-15.963-18.979-24.838-27.453a174.746 174.746 0 0 0-6.759-6.075l-1.686-1.406-1.482-1.188c0.106 0.081-1.082-0.831-0.637-0.496l-0.115-0.103-0.231-0.206-0.462-0.414-0.945-0.897-1.87-1.811-3.696-3.662-3.535-3.875c-2.375-2.572-4.537-5.397-6.716-8.212-4.201-5.783-8.093-12.021-10.82-19.185-1.318-3.587-2.418-7.409-2.595-11.542-0.101-2.046 0.081-4.204 0.753-6.241 0.363-1.011 0.828-1.993 1.505-2.808 0.654-0.811 1.506-1.478 2.423-1.709-0.584 1.762-0.026 3.268 0.639 4.522 0.68 1.271 1.639 2.372 2.629 3.432 2.051 2.092 4.394 3.979 6.841 5.796 4.9 3.625 10.104 7.044 15.353 10.458 2.564 1.773 5.27 3.408 7.849 5.198l3.958 2.594 3.911 2.675 3.442 2.293 1.311 0.968 1.13 0.863 2.166 1.703a202.806 202.806 0 0 1 8.169 6.928c10.57 9.387 20.062 19.719 28.763 30.734 8.736 10.982 16.397 22.838 23.08 35.25 6.721 12.396 12.093 25.551 16.321 39.05 4.184 13.521 6.898 27.496 8.289 41.545 2.614 28.153-0.677 56.531-8.782 82.9-1.97 6.613-4.333 13.083-6.848 19.452-2.511 6.374-5.327 12.602-8.339 18.702a306.657 306.657 0 0 1-4.658 9.061 327.575 327.575 0 0 1-5.004 8.84l-2.606 4.37-1.301 2.118-1.119 2.175c-1.632 3.072-3.347 5.995-5.107 8.893-7.058 11.548-14.855 22.44-23.054 32.993-8.199 10.549-16.86 20.686-25.844 30.5-17.956 19.641-37.226 37.955-57.332 55.201a801.43 801.43 0 0 1-62.796 48.463z\"  ></path><path d=\"M690.811 462.863c-11.202-10.033-23.855-19.852-35.554-29.38-11.68-9.552-23.726-18.717-36.167-27.376-6.239-4.303-12.542-8.533-18.979-12.561a597.859 597.859 0 0 0-19.576-11.684c-26.471-14.997-54.155-28.04-82.668-39.175-28.453-11.315-57.758-20.741-87.595-28.368-29.823-7.76-27.023-23.895 4.021-18.259 31.023 5.783 61.779 13.468 91.98 23.226 30.136 9.932 59.694 21.938 88.283 36.152a624.38 624.38 0 0 1 21.287 11.007c7.038 3.795 14.057 7.649 20.979 11.689 13.88 8.018 27.484 16.59 40.701 25.777 13.191 9.221 25.917 19.161 37.968 29.873a391.554 391.554 0 0 1 17.569 16.581\"  ></path><path d=\"M504.265 210.272c8.23 19.958 13.948 41.048 17.311 62.613a281.744 281.744 0 0 1 2.469 65.623 298.03 298.03 0 0 1-1.825 16.481c-0.375 2.582-0.886 5.504-1.393 8.193a245.919 245.919 0 0 1-1.716 8.188c-2.502 10.876-5.801 21.622-10.037 31.982-4.21 10.369-9.201 20.403-14.825 29.992-5.648 9.578-11.865 18.737-18.398 27.563-6.525 8.837-13.455 17.274-20.455 25.565-7.036 8.293-14.069 16.366-21.479 24.381-14.725 16.011-30.459 31.089-46.925 45.221-16.46 14.145-33.728 27.258-51.503 39.521a661.29 661.29 0 0 1-27.092 17.716 1221.997 1221.997 0 0 1-13.843 8.358c-4.669 2.697-9.327 5.403-14.073 7.968l24.617-20.859a2431.026 2431.026 0 0 0 24.386-20.923c16.127-14.034 31.881-28.382 47.111-43.169a828.927 828.927 0 0 0 43.838-45.999c3.495-3.96 6.938-7.99 10.421-12.042 3.481-4.033 6.939-8.071 10.345-12.135a573.112 573.112 0 0 0 19.681-24.744c6.273-8.385 12.16-16.977 17.458-25.855 5.328-8.86 10.081-18.002 14.081-27.457 3.982-9.458 7.342-19.177 9.879-29.155a230.665 230.665 0 0 0 1.778-7.52c0.546-2.57 1.021-4.922 1.525-7.631a295.142 295.142 0 0 0 2.579-15.396c2.967-20.684 3.939-41.738 3.158-62.878-0.768-21.152-3.362-42.347-7.073-63.602zM654.02 238.096c2.406 8.22 3.54 16.717 3.97 25.251 0.398 8.536 0.044 17.126-1.024 25.611-0.237 2.126-0.563 4.238-0.905 6.348l-0.531 3.162-0.138 0.789-0.033 0.198-0.05 0.225-0.088 0.378-0.35 1.511a310.785 310.785 0 0 1-3.186 12.34 307.855 307.855 0 0 1-7.864 24.303c-6.02 15.967-13.436 31.479-22.234 46.085-8.762 14.632-18.667 28.462-29.248 41.57a572.384 572.384 0 0 1-16.213 19.286l-8.241 9.245-8.168 9.25c-10.93 12.319-21.915 24.936-33.998 36.728a314.736 314.736 0 0 1-38.731 32.343 373.459 373.459 0 0 1-20.948 13.78 357.946 357.946 0 0 1-21.753 12.328c12.455-11.08 24.957-21.916 36.863-33.229 11.941-11.279 23.257-22.964 33.842-35.304 10.596-12.322 20.698-25.239 31.193-38.135 2.603-3.238 5.293-6.415 7.964-9.615 2.671-3.183 5.418-6.41 8.057-9.477 5.314-6.208 10.563-12.445 15.607-18.81 10.083-12.732 19.574-25.81 27.895-39.532 8.304-13.725 15.574-28.012 21.423-42.94a289.39 289.39 0 0 0 7.998-22.721 341.978 341.978 0 0 0 3.33-11.608l0.403-1.498 0.102-0.375 0.026-0.093c0.009-0.039 0.002 0.001 0.02-0.059l0.055-0.179 0.228-0.718 0.897-2.877 1.73-5.779c2.254-7.726 4.402-15.517 6.368-23.453 1.998-7.938 3.735-16.007 5.732-24.329zM822.932 290.223c-3.33 19.547-9.583 38.631-18.054 56.813-8.486 18.183-19.401 35.294-31.819 51.028-12.424 15.755-26.229 30.216-40.597 43.842-7.168 6.592-14.612 12.882-22.511 18.844-3.939 2.983-8.016 5.854-12.26 8.554-4.248 2.697-8.631 5.262-13.38 7.397 2.298-4.671 4.889-8.992 7.58-13.212a197.146 197.146 0 0 1 8.496-12.259c5.923-7.931 12.35-15.483 19.262-22.523 13.745-13.669 27.159-27.496 39.711-41.958 12.563-14.455 24.224-29.585 34.734-45.696 10.567-16.088 19.774-33.202 28.838-50.83z\"  ></path><path d=\"M345.965 547.814a554.561 554.561 0 0 1 25.007-24.114c8.562-7.806 17.35-15.363 26.277-22.762 17.879-14.767 36.438-28.729 55.501-41.985 38.164-26.459 78.401-50.008 120.13-70.507 41.727-20.505 84.978-37.926 129.251-51.931 22.144-6.98 44.542-13.115 67.147-18.258 11.308-2.555 22.654-4.9 34.056-6.94a594.59 594.59 0 0 1 34.343-5.212l-32.763 11.264c-10.886 3.781-21.745 7.563-32.546 11.472-21.612 7.778-43.068 15.82-64.323 24.294-42.53 16.878-84.302 35.349-125.282 55.445-40.977 20.104-81.146 41.848-120.515 65.167-19.703 11.632-39.191 23.681-58.536 36.055-19.37 12.348-38.491 25.146-57.747 38.012zM393.713 444.275c9.035-12.098 18.943-23.434 29.211-34.426 10.293-10.968 21.066-21.47 32.155-31.625 22.229-20.251 45.81-38.995 70.375-56.296a759.91 759.91 0 0 1 76.561-47.41c13.221-7.112 26.646-13.838 40.313-20.063 6.844-3.096 13.723-6.107 20.693-8.933a383.03 383.03 0 0 1 21.133-7.977c-11.965 9.213-24.18 17.829-36.34 26.475-12.179 8.606-24.398 17.063-36.577 25.538-24.396 16.885-48.696 33.723-72.875 50.714a5613.08 5613.08 0 0 0-72.171 51.577l-36 26.22-18.112 13.153a989.284 989.284 0 0 1-18.366 13.053zM394.681 318.676c1.69-3.162 3.557-6.145 5.497-9.073 1.928-2.933 3.958-5.767 6.025-8.568 4.152-5.583 8.573-10.903 13.13-16.087 9.161-10.32 19.057-19.883 29.473-28.832 10.428-8.937 21.396-17.241 32.981-24.723a248.85 248.85 0 0 1 17.886-10.531c3.083-1.617 6.188-3.195 9.378-4.66 3.183-1.474 6.417-2.872 9.798-4.069-2.021 2.964-4.184 5.736-6.381 8.455-2.19 2.729-4.453 5.356-6.722 7.967a360.601 360.601 0 0 1-14.071 15.035c-9.607 9.7-19.605 18.857-29.898 27.669-10.304 8.8-20.885 17.27-31.935 25.277-5.548 3.979-11.166 7.897-16.984 11.624-2.919 1.854-5.854 3.696-8.877 5.454-3.01 1.766-6.073 3.497-9.3 5.062z\"  ></path><path d=\"M377.385 198.376c7.893 10.171 13.98 21.39 19.39 32.877 2.634 5.773 5.105 11.629 7.288 17.595a208.038 208.038 0 0 1 1.603 4.488c0.557 1.625 1.058 2.91 1.593 4.547 1.024 3.13 1.943 6.208 2.726 9.374 3.157 12.603 4.434 25.751 3.541 38.719a112.472 112.472 0 0 1-3.022 19.212c-1.595 6.292-3.653 12.46-6.732 18.349-1.518-6.472-2.425-12.718-3.378-18.871l-2.668-18.153c-1.728-11.938-3.566-23.616-5.697-35.365l-1.626-8.801a186.252 186.252 0 0 0-0.857-4.471l-0.974-4.493-3.917-18.063c-2.531-12.133-5.235-24.257-7.27-36.944zM400.461 418.669c3.572-0.022 7.066 0.362 10.524 0.96 3.467 0.584 6.87 1.447 10.241 2.477a89.806 89.806 0 0 1 19.348 8.694c12.266 7.308 22.957 17.621 30.989 29.654 8.052 12.031 13.415 25.727 15.745 39.667 1.187 6.97 1.588 14.02 1.273 20.998-0.19 3.494-0.52 6.966-1.107 10.417-0.573 3.448-1.335 6.87-2.48 10.256-1.634-3.18-3.034-6.314-4.417-9.401l-4.055-9.112a633.294 633.294 0 0 0-7.974-17.387c-5.396-11.228-11.132-21.724-17.718-31.604-6.573-9.891-14.004-19.131-22.397-28.158a343.862 343.862 0 0 0-13.311-13.475c-2.33-2.267-4.739-4.505-7.182-6.824-2.453-2.305-4.965-4.626-7.479-7.162zM213.98 674.63s-101.966 69.187-52.733 141.79c0 0 35.523 62.631 161.722 96.598 0 0 144.272 33.34 219.991 29.601 0 0 9.038-1.869-16.203-6.854 0 0-72.877-10.811-146.453-25.862-51.476-10.532-100.322-31.201-122.46-45.183 0 0-129.315-71.046-68.864-149.258 0 0 20.914-32.032 52.056-54.967\"  ></path><path d=\"M535.012 942.074s48.847-108.205 75.021-251.542c0 0 17.688-104.204 0-192.57l19.944-7.041s19.941 83.071-3.116 230.147c0 0-20.753 119.558-82.45 220.829\"  ></path><path d=\"M542.96 941.841s-1.385 0.233-3.722 0.233c-3.706 0-3.725-1.792-3.725-1.792M165.298 756.591s225.6-62.942 291.66-90.364c0 0 127.756-49.232 165.772-87.871v13.088s-109.256 83.509-222.27 112.177c0 0-131.71 39.884-235.163 59.205v-6.235z\"  ></path><path d=\"M224.502 848.203s129.095-167.642 164.884-266.732l25.194-11.84S384.845 679.34 303.65 759.708c0 0-57.958 72.292-74.162 95.351l-4.986-6.856zM501.206 532.861s14.539 90.365-58.479 216.252c0 0-63.669 121.524-102.308 162.034h11.841s63.547-54.22 132.733-205.034c0 0 44.714-112.178 32.958-178.237l-16.745 4.985zM359.115 911.147s196.478-57.505 215.629-65.438v6.232c0 0.001-149.569 59.83-215.629 59.206z\"  ></path><path d=\"M581.599 842.594s-136.481-18.072-211.889-88.495c0 0-83.509-62.943-50.167-143.337l-27.733 6.232s-20.566 76.654 39.262 129.626c-0.001 0 87.247 98.466 250.527 95.974zM414.58 567.138s28.667 115.293 199.425 145.207l8.726-3.74s-192.562-64.189-192.562-149.777l-15.589 8.31z\"  ></path><path d=\"M235.098 855.059s199.9-66.06 244.919-86.626c0 0 82.264-26.798 142.714-66.059l-4.985 9.971s-56.089 38.639-150.815 75.407c0 0-100.336 41.755-236.819 72.915l4.986-5.608zM519.92 530.369s99.071 59.827 110.913 60.451v-4.363s-96.444-57.958-105.322-64.19l-5.591 8.102zM549.186 219.389s-55.458-79.146-70.416-150.815c0 0-206.281 68.553-194.44 105.322l25.76 12.556 4.154-1.961s-19.942-18.696 48.61-56.089c0 0 66.06-36.146 112.177-51.103 0 0 15.58 108.438 52.973 138.975l21.182 3.115z\"  ></path><path d=\"M341.042 134.633l50.526 67.306 24.944 2.493-75.47-76.654z\"  ></path><path d=\"M501.206 162.054s-160.164-11.217-153.309-34.276h8.102s136.751 26.976 145.207 25.552v8.724zM456.766 206.301s48.179-37.954 50.048-38.607l-3.116-5.64s-61.386 34.808-72.759 37.658c-11.374 2.851 25.827 6.589 25.827 6.589z\"  ></path></symbol><symbol id=\"icon-sqlite\" viewBox=\"0 0 1024 1024\"><path d=\"M884.736 22.528c-44.032-38.912-97.28-23.552-149.504 23.552-8.192 7.168-15.36 14.336-23.552 22.528-12.288 13.312-24.576 27.648-35.84 43.008-12.288-5.12-25.6-8.192-39.936-8.192H199.68c-57.344 0-104.448 47.104-104.448 104.448v436.224c0 57.344 47.104 104.448 104.448 104.448h307.2c4.096 20.48 6.144 34.816 6.144 34.816s0 4.096 1.024 9.216c-2.048 38.912-1.024 79.872 2.048 116.736 4.096 49.152 11.264 91.136 21.504 113.664l6.144-3.072c-14.336-44.032-20.48-102.4-17.408-168.96 4.096-102.4 27.648-225.28 70.656-354.304 73.728-194.56 175.104-350.208 269.312-423.936-86.016 76.8-200.704 325.632-235.52 417.792-38.912 103.424-65.536 199.68-82.944 292.864 28.672-87.04 120.832-123.904 120.832-123.904s45.056-55.296 98.304-135.168c-31.744 7.168-82.944 19.456-100.352 26.624-25.6 11.264-32.768 14.336-32.768 14.336S716.8 515.072 788.48 491.52c96.256-154.624 203.776-373.76 96.256-468.992zM512 475.136c10.24 20.48 18.432 47.104 23.552 66.56 1.024 5.12 2.048 10.24 3.072 14.336 2.048 9.216 3.072 16.384 3.072 16.384l-4.096-12.288c-1.024-2.048-1.024-4.096-2.048-6.144 0-1.024-1.024-2.048-1.024-3.072-6.144-13.312-22.528-41.984-29.696-55.296-6.144 18.432-11.264 34.816-16.384 50.176 20.48 37.888 32.768 102.4 32.768 102.4s-1.024-4.096-6.144-18.432c-4.096-13.312-27.648-53.248-32.768-62.464-9.216 33.792-13.312 57.344-9.216 63.488 5.12 8.192 9.216 21.504 14.336 35.84H199.68c-11.264 0-21.504-9.216-21.504-21.504V207.872c0-11.264 9.216-21.504 21.504-21.504H624.64c-53.248 91.136-95.232 199.68-112.64 288.768z\"  ></path></symbol><symbol id=\"icon-queshengye_zanwushuju\" viewBox=\"0 0 1024 1024\"><path d=\"M102.4 896a409.6 51.2 0 1 0 819.2 0 409.6 51.2 0 1 0-819.2 0Z\"  ></path><path d=\"M116.736 376.832c0 8.704 6.656 15.36 15.36 15.36s15.36-6.656 15.36-15.36-6.656-15.36-15.36-15.36c-8.192 0-15.36 7.168-15.36 15.36zM926.72 832c-19.456 5.12-23.552 9.216-28.16 28.16-5.12-19.456-9.216-23.552-28.16-28.16 18.944-5.12 23.552-9.216 28.16-28.16 4.608 18.944 8.704 23.552 28.16 28.16zM202.24 323.072c-25.088 6.656-30.208 11.776-36.864 36.864-6.656-25.088-11.776-30.208-36.864-36.864 25.088-6.656 30.208-12.288 36.864-36.864 6.144 25.088 11.776 30.208 36.864 36.864zM816.64 235.008c-15.36 4.096-18.432 7.168-22.528 22.528-4.096-15.36-7.168-18.432-22.528-22.528 15.36-4.096 18.432-7.168 22.528-22.528 3.584 15.36 7.168 18.432 22.528 22.528zM882.688 156.16c-39.936 10.24-48.128 18.944-58.88 58.88-10.24-39.936-18.944-48.128-58.88-58.88 39.936-10.24 48.128-18.944 58.88-58.88 10.24 39.424 18.944 48.128 58.88 58.88z\"  ></path><path d=\"M419.84 713.216v4.096l33.792 31.232 129.536-62.976L465.92 760.832v36.864l18.944-18.432v-0.512 0.512l18.944 18.432 100.352-122.88v-4.096z\"  ></path><path d=\"M860.16 551.936v-1.024c0-1.024-0.512-1.536-0.512-2.56v-0.512l-110.08-287.232c-15.872-48.64-60.928-81.408-112.128-81.408H387.072c-51.2 0-96.256 32.768-112.128 81.408L164.864 547.84v0.512c-0.512 1.024-0.512 1.536-0.512 2.56V757.76c0 65.024 52.736 117.76 117.76 117.76h460.8c65.024 0 117.76-52.736 117.76-117.76v-204.8c-0.512-0.512-0.512-0.512-0.512-1.024zM303.616 271.36s0-0.512 0.512-0.512C315.392 233.984 349.184 209.92 387.072 209.92h249.856c37.888 0 71.68 24.064 83.456 60.416 0 0 0 0.512 0.512 0.512l101.888 266.24H588.8c-8.704 0-15.36 6.656-15.36 15.36 0 33.792-27.648 61.44-61.44 61.44s-61.44-27.648-61.44-61.44c0-8.704-6.656-15.36-15.36-15.36H201.728L303.616 271.36zM829.44 757.76c0 48.128-38.912 87.04-87.04 87.04H281.6c-48.128 0-87.04-38.912-87.04-87.04v-189.44h226.816c7.168 43.52 45.056 76.8 90.624 76.8s83.456-33.28 90.624-76.8H829.44v189.44z\"  ></path><path d=\"M512 578.56c-14.336 0-25.6-11.264-25.6-25.6V501.76H253.44l83.968-219.136 0.512-1.024c7.168-21.504 26.624-35.84 49.152-35.84h249.856c22.528 0 41.984 14.336 49.152 35.84l0.512 1.024L770.56 501.76H537.6v51.2c0 14.336-11.264 25.6-25.6 25.6z\"  ></path></symbol><symbol id=\"icon-weiwancheng\" viewBox=\"0 0 1024 1024\"><path d=\"M512 0C229.003636 0 0 229.003636 0 512s229.003636 512 512 512 512-229.003636 512-512S794.996364 0 512 0z m2.792727 791.272727c-33.512727 0-61.44-26.996364-61.44-59.578182 0-32.581818 26.996364-59.578182 61.44-59.578181 33.512727 0 61.44 26.996364 61.44 59.578181 0 33.512727-27.927273 59.578182-61.44 59.578182z m58.647273-352.814545c-10.24 82.850909-27.927273 151.738182-42.821818 174.08-2.792727 6.516364-9.309091 11.170909-16.756364 11.170909-8.378182 0-14.894545-5.585455-16.756363-13.032727-13.963636-23.272727-30.72-91.229091-40.96-172.218182-7.447273-56.785455-9.309091-108.916364-7.447273-143.36 0-2.792727-0.930909-6.516364-0.930909-9.309091 0-35.374545 29.789091-64.232727 66.094545-64.232727 23.272727 0 43.752727 12.101818 55.854546 29.789091 2.792727 1.861818 4.654545 5.585455 6.516363 12.101818 2.792727 7.447273 3.723636 14.894545 3.723637 22.341818v4.654545c3.723636 35.374545 0.930909 88.436364-6.516364 148.014546z\"  ></path></symbol><symbol id=\"icon-wancheng-\" viewBox=\"0 0 1024 1024\"><path d=\"M725.333 318.293L442.88 630.613 322.987 512a34.133 34.133 0 0 0-48.214 48.213L419.84 705.28a34.133 34.133 0 0 0 49.493 0l306.774-338.773a34.133 34.133 0 1 0-50.774-48.214zM512 981.333A469.333 469.333 0 1 1 981.333 512 469.333 469.333 0 0 1 512 981.333z\"  ></path></symbol><symbol id=\"icon-chenggong1\" viewBox=\"0 0 1024 1024\"><path d=\"M196.92 272.14l1.57 342.23c0.16 34.91 16.1 69.97 44.34 104.41 28.19 34.38 66.78 65.83 107.4 92.72 40.44 26.77 81.7 48.25 114.15 62.97 16.22 7.36 29.97 12.92 40.07 16.56 4.5 1.62 8.04 2.78 10.61 3.53 2.6-0.71 6.19-1.82 10.74-3.36 10.03-3.41 23.67-8.63 39.72-15.6 32.13-13.95 72.91-34.49 112.8-60.69 40.06-26.31 78.02-57.55 105.64-92.58 27.53-34.9 43.57-71.93 43.39-110.85l-1.57-342.23c-45.01-28.45-115.14-61.96-180.25-89.75-35.71-15.25-69.12-28.44-94.8-37.75-12.88-4.67-23.52-8.26-31.41-10.63-3.47-1.05-6.15-1.78-8.1-2.25-1.96 0.49-4.65 1.25-8.14 2.33-7.89 2.45-18.55 6.14-31.44 10.93-25.7 9.55-59.11 23.05-94.82 38.62-65.09 28.39-135.14 62.54-179.9 91.39zM496.97 65.78c3.16-0.75 8.53-1.94 13.97-1.97 5.45-0.02 10.82 1.12 13.99 1.84 3.93 0.9 8.38 2.13 13.09 3.55 9.47 2.85 21.35 6.88 34.81 11.76 27 9.79 61.54 23.44 98.15 39.07C743.42 150.96 827.22 191 876.57 225.3a32.326 32.326 0 0 1 13.88 26.39l1.65 359.5c0.27 57.82-23.76 108.67-57.27 151.16-33.42 42.38-77.49 78.06-120.92 106.59-43.6 28.64-87.78 50.86-122.56 65.96-17.4 7.56-32.71 13.44-44.67 17.51-5.95 2.02-11.34 3.69-15.91 4.89-3.39 0.89-9.67 2.48-15.72 2.51-6.53 0.03-13.21-1.8-16.51-2.72-4.63-1.3-10.08-3.09-16.06-5.24-12.02-4.33-27.4-10.58-44.88-18.51-34.93-15.86-79.31-38.93-123.13-67.94-43.65-28.9-87.96-64.47-121.72-105.65-33.71-41.11-58.74-90.12-59-145.08l-1.65-359.5a32.295 32.295 0 0 1 13.64-26.51c49.06-34.77 132.78-75.58 205.19-107.16 36.6-15.96 71.14-29.93 98.15-39.97 13.47-5.01 25.35-9.14 34.82-12.08 4.71-1.47 9.15-2.73 13.07-3.67z\"  ></path><path d=\"M601.6 399.71c10.62-14.36 30.89-17.41 45.27-6.8 14.38 10.61 17.43 30.85 6.81 45.21l-26.04-19.21c26.04 19.21 26.04 19.2 26.04 19.21l-5.95 8.02c-3.78 5.09-9.19 12.36-15.7 21.07-13.02 17.43-30.48 40.69-48.23 63.98-17.71 23.23-35.86 46.7-50.21 64.45-7.12 8.82-13.6 16.59-18.75 22.31-2.5 2.78-5.19 5.64-7.76 8.02-1.22 1.13-3.12 2.81-5.44 4.41-1.15 0.8-3.06 2.03-5.55 3.17-2.05 0.94-7.1 3.07-13.89 3.1-9.42 0.04-16.36-3.9-18.23-4.96-2.96-1.68-5.5-3.58-7.3-5.03-3.7-2.96-7.49-6.59-10.91-10.04-7-7.07-15.06-16.11-22.41-24.64-7.45-8.64-14.58-17.23-19.82-23.62-2.63-3.21-4.8-5.88-6.32-7.77l-1.77-2.2-0.48-0.59-0.16-0.2c-11.15-13.96-8.87-34.32 5.11-45.45 13.98-11.13 34.34-8.84 45.49 5.12l0.13 0.16 0.42 0.53 1.65 2.05c1.43 1.78 3.51 4.33 6.02 7.4 5.05 6.15 11.81 14.3 18.78 22.39 2.65 3.08 5.28 6.08 7.82 8.92 0.97-1.18 1.97-2.41 2.99-3.68 13.75-17.02 31.46-39.9 49.08-63.01 17.58-23.06 34.91-46.15 47.85-63.48 6.47-8.66 11.84-15.88 15.59-20.93l5.87-7.91 22.49 16.58-22.49-16.58z\"  ></path></symbol><symbol id=\"icon-jiqiren\" viewBox=\"0 0 1024 1024\"><path d=\"M683.7 922.7h-345c-73.5 0-133.3-59.8-133.3-133.3V459.8c0-73.5 59.8-133.3 133.3-133.3h345c73.5 0 133.3 59.8 133.3 133.3v329.6c0 73.5-59.8 133.3-133.3 133.3z m-345-506.9c-24.3 0-44.1 19.8-44.1 44.1v329.6c0 24.3 19.8 44.1 44.1 44.1h345c24.3 0 44.1-19.8 44.1-44.1V459.8c0-24.3-19.8-44.1-44.1-44.1h-345zM914.3 759.6c-24.6 0-44.6-20-44.6-44.6V534.3c0-24.6 20-44.6 44.6-44.6s44.6 20 44.6 44.6V715c0 24.7-20 44.6-44.6 44.6zM111.7 759.6c-24.6 0-44.6-20-44.6-44.6V534.3c0-24.6 20-44.6 44.6-44.6s44.6 20 44.6 44.6V715c0 24.7-19.9 44.6-44.6 44.6z\"  ></path><path d=\"M511.2 415.8c-24.6 0-44.6-20-44.6-44.6V239.3c0-24.6 20-44.6 44.6-44.6s44.6 20 44.6 44.6v131.9c0 24.6-20 44.6-44.6 44.6z\"  ></path><path d=\"M511.2 276.6c-49.2 0-89.2-40-89.2-89.2s40-89.2 89.2-89.2 89.2 40 89.2 89.2-40 89.2-89.2 89.2z m0-89.2h0.2-0.2z m0 0h0.2-0.2z m0 0h0.2-0.2z m0 0h0.2-0.2z m0 0z m0 0h0.2-0.2z m0 0h0.2-0.2z m0-0.1h0.2-0.2zM399 675.5c-28.1 0-50.9-22.8-50.9-50.9 0-28.1 22.8-50.9 50.9-50.9s50.9 22.8 50.9 50.9c0 28.1-22.8 50.9-50.9 50.9zM622.9 675.5c-28.1 0-50.9-22.8-50.9-50.9 0-28.1 22.8-50.9 50.9-50.9 28.1 0 50.9 22.8 50.9 50.9 0 28.1-22.8 50.9-50.9 50.9z\"  ></path></symbol><symbol id=\"icon-huanyihuan\" viewBox=\"0 0 1024 1024\"><path d=\"M512 76.0832a428.7488 428.7488 0 0 0-248.32 78.4384l21.8112-97.5872a27.136 27.136 0 0 0-52.9408-11.8784L196.9152 204.8a27.136 27.136 0 0 0 20.48 32.3584l159.6416 35.6352a26.8288 26.8288 0 0 0 5.9392 0.6144 27.136 27.136 0 0 0 6.144-53.6576l-94.0032-20.48A375.0912 375.0912 0 0 1 512 130.2528a379.6992 379.6992 0 0 1 327.68 570.368 27.136 27.136 0 1 0 46.7968 27.3408A433.8688 433.8688 0 0 0 512 76.0832zM801.792 787.6608l-158.9248-38.2976a27.136 27.136 0 1 0-12.6976 52.736l93.5936 22.6304a371.4048 371.4048 0 0 1-218.2144 64.8192 379.6992 379.6992 0 0 1-318.3616-576.1024 27.136 27.136 0 0 0-46.3872-28.16 433.8688 433.8688 0 0 0 363.8272 658.3296H512a428.544 428.544 0 0 0 241.9712-74.24l-23.2448 97.28a27.136 27.136 0 0 0 20.48 32.6656 26.4192 26.4192 0 0 0 6.3488 0.7168 27.136 27.136 0 0 0 26.3168-20.48l38.4-158.9248a27.136 27.136 0 0 0-20.48-32.9728z\"  ></path></symbol><symbol id=\"icon-icon_infomation\" viewBox=\"0 0 1024 1024\"><path d=\"M539.19999969 192.00000031a86.92000031 86.92000031 0 1 0 86.64-80.00000062 81.40000031 81.40000031 0 0 0-86.64 80.00000062z m-187.63999969 273.67999969a361.68 361.68 0 0 0 0.19999969 47.20000031l75.6-84.68000062c15.67999969-16.00000031 33.76000031-27.12 43.00000031-24a17.92000031 17.92000031 0 0 1 11.32000031 22.56L356.52000031 812.40000031c-14.4 45.07999969 12.84 89.35999969 79.23999938 99.43999969 97.27999969 0 155.08000031-61.24000031 212.00000062-140.59999969a358.2 358.2 0 0 0 1.32-48.64000031l-75.6 84.67999969c-15.67999969 16.00000031-35.16 27.12-44.4 24a17.88 17.88 0 0 1-12-20.43999938l126-387.52000031a80.23999969 80.23999969 0 0 0-78.20000062-99.72c-63.12 0.12-156.43999969 62.80000031-213.31999969 142.15999969z\"  ></path></symbol><symbol id=\"icon-key1\" viewBox=\"0 0 1024 1024\"><path d=\"M443.25546667 486.74133333c-29.35466667-47.104-41.09653333-99.9424-41.09653334-158.72A291.0208 291.0208 0 0 1 695.97866667 34.13333333 291.0208 291.0208 0 0 1 989.86666667 328.02133333a291.0208 291.0208 0 0 1-293.888 293.81973334c-64.64853333 0-117.48693333-17.6128-170.3936-52.90666667l-135.168 135.168 64.64853333 64.7168-82.3296 82.26133333-64.64853333-64.64853333-41.09653334 41.1648 64.64853334 64.64853333-82.3296 82.26133334L102.4 827.5968l340.85546667-340.92373333z m252.7232-335.0528c-99.87413333 0-176.26453333 76.45866667-176.26453334 176.3328s76.3904 176.26453333 176.26453334 176.26453334c99.9424 0 176.3328-76.3904 176.3328-176.26453334 0-99.9424-82.26133333-176.3328-176.3328-176.3328z\" fill=\"#262626\" ></path></symbol><symbol id=\"icon-mysql\" viewBox=\"0 0 1024 1024\"><path d=\"M1001.632 793.792c-7.84-13.856-26.016-37.536-93.12-83.2a1096.224 1096.224 0 0 0-125.152-74.144c-30.592-82.784-89.824-190.112-176.256-319.36-93.056-139.168-201.12-197.792-321.888-174.56a756.608 756.608 0 0 0-40.928-37.696C213.824 78.688 139.2 56.48 96.32 60.736c-19.424 1.952-34.016 9.056-43.36 21.088-21.664 27.904-14.432 68.064 85.504 198.912 19.008 55.616 23.072 84.672 23.072 99.296 0 30.912 15.968 66.368 49.984 110.752l-32 109.504c-28.544 97.792 23.328 224.288 71.616 268.384 25.76 23.552 47.456 20.032 58.176 15.84 21.504-8.448 38.848-29.472 50.048-89.504 5.728 14.112 11.808 29.312 18.208 45.6 34.56 87.744 68.352 136.288 106.336 152.736a32.032 32.032 0 0 0 25.44-58.688c-9.408-4.096-35.328-23.712-72.288-117.504-31.168-79.136-53.856-132.064-69.376-161.856a32.224 32.224 0 0 0-35.328-16.48 32.032 32.032 0 0 0-25.024 29.92c-3.872 91.04-13.056 130.4-19.2 147.008-26.496-30.464-68.128-125.984-47.232-197.536 20.768-71.232 32.992-112.928 36.64-125.248a31.936 31.936 0 0 0-5.888-29.28c-41.664-51.168-46.176-75.584-46.176-83.712 0-29.472-9.248-70.4-28.288-125.152a31.104 31.104 0 0 0-4.768-8.896c-53.824-70.112-73.6-105.216-80.832-121.888 25.632 1.216 74.336 15.04 91.008 29.376a660.8 660.8 0 0 1 49.024 46.304c8 8.448 19.968 11.872 31.232 8.928 100.192-25.92 188.928 21.152 271.072 144 87.808 131.328 146.144 238.048 173.408 317.216a32 32 0 0 0 16.384 18.432 1004.544 1004.544 0 0 1 128.8 75.264c7.392 5.024 14.048 9.696 20.064 14.016h-98.848a32.032 32.032 0 0 0-24.352 52.736 3098.752 3098.752 0 0 0 97.856 110.464 32 32 0 1 0 46.56-43.872 2237.6 2237.6 0 0 1-50.08-55.328h110.08a32.032 32.032 0 0 0 27.84-47.776z\"  ></path><path d=\"M320 289.472c12.672 21.76 22.464 37.344 29.344 46.784 8.288 16.256 21.184 29.248 29.44 45.536l2.016-1.984c14.528-9.952 25.92-49.504 2.752-75.488-12.032-18.176-51.04-17.664-63.552-14.848z\"  ></path></symbol><symbol id=\"icon-oracle\" viewBox=\"0 0 1024 1024\"><path d=\"M700.245333 188.245333h-376.32a323.754667 323.754667 0 0 0-0.341333 647.509334h376.661333a323.754667 323.754667 0 0 0 0-647.509334z m-8.234666 533.418667H332.202667a209.706667 209.706667 0 0 1 0-419.328h359.808a209.664 209.664 0 1 1 0 419.328z\"  ></path></symbol><symbol id=\"icon-postgresql\" viewBox=\"0 0 1024 1024\"><path d=\"M730.794667 0a432.384 432.384 0 0 0-117.546667 17.194667l-2.688 0.853333A466.005333 466.005333 0 0 0 537.6 11.008C487.338667 10.154667 444.16 22.357333 409.344 42.666667 375.04 30.762667 303.872 10.24 228.864 14.336 176.64 17.194667 119.637333 33.066667 77.397333 77.653333 35.285333 122.24 13.013333 191.232 17.706667 285.098667c1.28 25.898667 8.661333 68.138667 20.906666 122.837333s29.44 118.741333 50.901334 177.152c21.461333 58.453333 44.970667 110.933333 81.706666 146.602667 18.346667 17.877333 43.605333 32.896 73.386667 31.658666 20.906667-0.853333 39.808-10.026667 56.106667-23.552 7.936 10.453333 16.426667 15.018667 24.149333 19.242667 9.728 5.333333 19.2 8.96 29.013333 11.349333 17.621333 4.394667 47.786667 10.282667 83.114667 4.266667 12.032-2.005333 24.704-5.930667 37.333333-11.52 0.469333 14.08 1.024 27.861333 1.578667 41.813333 1.749333 44.202667 2.858667 85.034667 16.128 120.832 2.133333 5.845333 7.978667 35.968 31.018667 62.549334 23.04 26.624 68.181333 43.221333 119.594666 32.213333 36.266667-7.765333 82.389333-21.76 113.024-65.365333 30.293333-43.093333 43.946667-104.917333 46.634667-205.184a201.813333 201.813333 0 0 1 2.346667-14.336l7.210666 0.64h0.853334c38.698667 1.749333 80.682667-3.754667 115.754666-20.053334 31.061333-14.378667 54.570667-28.928 71.68-54.741333 4.266667-6.4 8.96-14.122667 10.24-27.434667s-6.357333-34.133333-19.029333-43.733333c-25.386667-19.285333-41.344-11.946667-58.453333-8.405333a267.52 267.52 0 0 1-51.285334 6.229333c49.322667-83.072 84.693333-171.306667 104.874667-249.386667 11.946667-46.08 18.645333-88.576 19.2-125.738666 0.554667-37.162667-2.474667-70.058667-24.746667-98.517334C911.36 25.6 813.525333 1.024 737.834667 0.170667c-2.346667-0.042667-4.693333-0.085333-7.04-0.042667z m-2.005334 27.306667c71.594667-0.682667 163.072 19.413333 228.736 103.338666 14.762667 18.858667 19.157333 46.421333 18.645334 80.384-0.554667 33.92-6.826667 74.538667-18.304 119.04-22.272 86.186667-64.341333 186.666667-123.605334 276.821334a32.256 32.256 0 0 0 6.741334 3.669333c12.373333 5.12 40.576 9.514667 96.853333-2.048 14.165333-2.986667 24.533333-4.992 35.285333 3.2a22.186667 22.186667 0 0 1 7.808 18.133333 30.037333 30.037333 0 0 1-5.546666 14.336c-10.88 16.341333-32.341333 31.829333-59.861334 44.586667-24.362667 11.349333-59.306667 17.28-90.282666 17.621333-15.530667 0.170667-29.866667-1.024-42.026667-4.821333l-0.768-0.298667c-4.693333 45.226667-15.488 134.528-22.528 175.274667-5.632 32.853333-15.488 58.965333-34.304 78.506667-18.773333 19.541333-45.354667 31.317333-81.109333 38.997333-44.288 9.514667-76.586667-0.725333-97.408-18.261333-20.778667-17.493333-30.293333-40.704-36.010667-54.912-3.925333-9.813333-5.973333-22.528-7.936-39.509334-1.962667-16.981333-3.413333-37.76-4.394667-61.184a2194.176 2194.176 0 0 1-1.28-107.648 130.602667 130.602667 0 0 1-66.218666 32.426667c-29.397333 4.992-55.637333 0.085333-71.296-3.84a97.109333 97.109333 0 0 1-22.186667-8.576c-7.253333-3.882667-14.165333-8.277333-18.773333-16.938667a23.893333 23.893333 0 0 1-2.432-16.256 26.026667 26.026667 0 0 1 9.301333-14.122666c8.448-6.869333 19.626667-10.709333 36.48-14.208 30.677333-6.314667 41.386667-10.624 47.914667-15.786667 5.546667-4.437333 11.818667-13.397333 22.912-26.538667a49.493333 49.493333 0 0 1-0.128-1.749333 126.293333 126.293333 0 0 1-56.746667-15.274667c-6.4 6.741333-39.082667 41.301333-78.933333 89.258667-16.768 20.053333-35.285333 31.573333-54.826667 32.384-19.541333 0.853333-37.205333-9.002667-52.224-23.552-29.994667-29.141333-53.930667-79.274667-74.794667-135.936-20.821333-56.661333-37.76-119.765333-49.792-173.525333-12.074667-53.76-19.2-97.109333-20.224-118.016-4.48-88.832 16.298667-148.693333 51.925334-186.453334 35.669333-37.76 84.565333-52.053333 132.224-54.784 85.546667-4.906667 166.784 24.917333 183.210666 31.317334 31.658667-21.504 72.448-34.901333 123.392-34.048a315.306667 315.306667 0 0 1 71.722667 9.301333l0.853333-0.384a292.437333 292.437333 0 0 1 31.530667-9.130667A410.709333 410.709333 0 0 1 728.746667 27.392z m6.485334 28.586666h-6.229334a372.906667 372.906667 0 0 0-72.704 8.192c53.162667 23.552 93.312 59.818667 121.6 96a360.106667 360.106667 0 0 1 48.298667 81.92c4.693333 11.264 7.850667 20.778667 9.642667 28.16 0.896 3.712 1.493333 6.826667 1.706666 10.069334a18.773333 18.773333 0 0 1-0.512 6.144c0 0.128-0.213333 0.426667-0.256 0.554666 1.28 37.376-7.978667 62.72-9.088 98.389334-0.853333 25.856 5.76 56.234667 7.381334 89.386666 1.536 31.146667-2.218667 65.365333-22.442667 98.944 1.706667 2.048 3.242667 4.096 4.864 6.144 53.504-84.266667 92.074667-177.493333 112.64-256.981333 11.008-42.794667 16.853333-81.578667 17.365333-112.298667 0.426667-30.72-5.290667-52.992-12.586666-62.293333-57.258667-73.216-134.741333-91.861333-199.68-92.373333z m-204.373334 10.922667c-50.432 0.128-86.613333 15.36-114.048 38.186667-28.288 23.594667-47.274667 55.893333-59.733333 88.96-14.805333 39.253333-19.882667 77.226667-21.888 102.997333l0.554667-0.341333c15.232-8.533333 35.242667-17.066667 56.661333-22.016 21.418667-4.906667 44.501333-6.442667 65.408 1.664s38.186667 27.178667 44.458667 56.106666c30.037333 138.965333-9.344 190.634667-23.850667 229.632a410.026667 410.026667 0 0 0-14.122667 43.221334c1.834667-0.426667 3.669333-0.938667 5.504-1.109334 10.24-0.853333 18.261333 2.56 23.04 4.608 14.592 6.058667 24.618667 18.773333 30.037334 33.28 1.408 3.797333 2.432 7.893333 3.029333 12.117334a14.336 14.336 0 0 1 0.853333 5.418666 2352.64 2352.64 0 0 0 0.554667 159.488c0.981333 22.954667 2.432 43.178667 4.266667 59.136 1.834667 15.914667 4.437333 28.032 6.101333 32.128 5.461333 13.653333 13.44 31.530667 27.861333 43.690667 14.421333 12.117333 35.114667 20.224 72.917334 12.117333 32.768-7.04 52.992-16.810667 66.517333-30.848 13.482667-14.037333 21.546667-33.578667 26.709333-63.488 7.722667-44.8 23.253333-174.72 25.130667-199.168-0.853333-18.432 1.877333-32.597333 7.765333-43.392 6.058667-11.093333 15.445333-17.877333 23.552-21.546666 4.053333-1.834667 7.850667-3.072 10.965334-3.968a254.122667 254.122667 0 0 0-10.368-13.866667 190.122667 190.122667 0 0 1-28.416-46.890667 353.962667 353.962667 0 0 0-10.965334-20.608c-5.674667-10.24-12.842667-23.04-20.352-37.418666-15.018667-28.8-31.36-63.701333-39.850666-97.706667-8.448-33.962667-9.685333-69.12 11.989333-93.909333 19.2-22.016 52.906667-31.146667 103.509333-26.026667-1.493333-4.48-2.389333-8.192-4.906666-14.165333a333.525333 333.525333 0 0 0-44.416-75.264c-42.88-54.826667-112.298667-109.184-219.562667-110.933334h-4.906667z m-283.392 2.218667c-5.418667 0-10.837333 0.170667-16.213333 0.469333-43.093333 2.474667-83.84 14.976-112.981333 45.866667-29.184 30.890667-48.384 81.536-44.202667 165.376 0.810667 15.872 7.722667 60.330667 19.584 113.152 11.818667 52.821333 28.586667 114.986667 48.725333 169.898666 20.181333 54.912 44.629333 102.698667 67.84 125.312 11.690667 11.306667 21.845333 15.872 31.061334 15.488 9.258667-0.426667 20.394667-5.76 34.005333-22.101333a1845.077333 1845.077333 0 0 1 77.226667-87.381333 149.205333 149.205333 0 0 1-49.792-134.4c4.394667-31.530667 4.992-61.013333 4.48-84.309334-0.512-22.698667-2.133333-37.802667-2.133334-47.232a14.336 14.336 0 0 1 0-0.810666v-0.213334l-0.042666-0.256v-0.042666a422.101333 422.101333 0 0 1 25.258666-144.042667c11.946667-31.744 29.738667-64 56.405334-90.112-26.197333-8.618667-72.704-21.76-123.050667-24.234667a324.394667 324.394667 0 0 0-16.213333-0.426666zM776.490667 294.4c-28.970667 0.384-45.226667 7.850667-53.76 17.621333-12.074667 13.866667-13.226667 38.186667-5.717334 68.138667 7.466667 29.994667 22.912 63.530667 37.418667 91.392 7.253333 13.952 14.293333 26.496 19.968 36.693333 5.717333 10.24 9.898667 17.493333 12.458667 23.68 2.346667 5.717333 4.949333 10.752 7.594666 15.445334 11.221333-23.68 13.226667-46.933333 12.074667-71.168-1.493333-29.994667-8.448-60.672-7.424-91.733334 1.152-36.309333 8.32-59.946667 8.96-88.021333a247.168 247.168 0 0 0-31.573333-2.048z m-351.317334 4.906667a120.32 120.32 0 0 0-26.282666 3.157333 199.04 199.04 0 0 0-49.194667 19.157333 103.125333 103.125333 0 0 0-14.890667 9.728l-0.938666 0.853334c0.256 6.229333 1.493333 21.333333 2.005333 43.562666 0.512 24.32-0.085333 55.338667-4.778667 88.917334-10.197333 72.96 42.752 133.376 104.96 133.461333 3.626667-14.976 9.6-30.165333 15.573334-46.165333 17.322667-46.677333 51.413333-80.725333 22.698666-213.589334-4.693333-21.76-13.994667-30.549333-26.794666-35.498666a62.890667 62.890667 0 0 0-22.357334-3.584z m337.792 8.704h2.133334a37.461333 37.461333 0 0 1 7.68 0.938666c2.304 0.512 4.266667 1.28 5.888 2.346667a6.997333 6.997333 0 0 1 3.2 4.693333l-0.042667 0.341334h0.042667-0.042667a10.24 10.24 0 0 1-1.493333 5.76 28.501333 28.501333 0 0 1-4.693334 6.4 28.885333 28.885333 0 0 1-16.469333 9.045333 25.173333 25.173333 0 0 1-17.493333-4.394667 25.941333 25.941333 0 0 1-5.546667-5.034666 11.093333 11.093333 0 0 1-2.688-5.418667 7.253333 7.253333 0 0 1 1.792-5.461333 16.384 16.384 0 0 1 4.992-3.84c4.096-2.304 9.642667-4.010667 15.914667-4.949334 2.346667-0.341333 4.650667-0.512 6.826666-0.554666z m-333.653333 7.168c2.261333 0 4.650667 0.213333 7.082667 0.554666 6.528 0.896 12.330667 2.645333 16.768 5.205334a19.029333 19.029333 0 0 1 5.674666 4.522666 9.514667 9.514667 0 0 1 2.304 7.253334 12.885333 12.885333 0 0 1-3.2 6.570666 27.690667 27.690667 0 0 1-6.101333 5.546667 27.306667 27.306667 0 0 1-19.114667 4.821333 31.061333 31.061333 0 0 1-17.92-9.728 30.293333 30.293333 0 0 1-5.034666-6.997333 11.946667 11.946667 0 0 1-1.749334-7.552c0.64-4.608 4.437333-6.997333 8.149334-8.32a36.949333 36.949333 0 0 1 13.098666-1.706667z m386.56 313.301333l-0.128 0.042667c-6.272 2.261333-11.434667 3.2-15.786667 5.12a19.285333 19.285333 0 0 0-10.197333 9.130666c-2.688 4.906667-4.992 13.610667-4.309333 28.416a21.76 21.76 0 0 0 6.314666 2.986667c7.296 2.218667 19.541333 3.669333 33.194667 3.456 27.221333-0.298667 60.714667-6.656 78.506667-14.933333a168.533333 168.533333 0 0 0 40.234666-26.24h-0.042666c-59.434667 12.288-93.013333 9.002667-113.621334 0.512a56.106667 56.106667 0 0 1-14.165333-8.533334z m-342.656 4.010667h-0.896c-2.261333 0.213333-5.546667 0.981333-11.904 8.021333-14.848 16.64-20.053333 27.093333-32.298667 36.864-12.245333 9.728-28.16 14.933333-59.946666 21.461333a81.834667 81.834667 0 0 0-19.669334 6.144c1.237333 1.024 1.109333 1.28 2.986667 2.261334 4.650667 2.56 10.624 4.821333 15.445333 6.058666 13.653333 3.413333 36.096 7.381333 59.52 3.413334 23.424-4.010667 47.786667-15.232 68.565334-44.373334 3.584-5.034667 3.968-12.458667 1.024-20.437333-2.986667-7.978667-9.514667-14.848-14.122667-16.768a27.861333 27.861333 0 0 0-8.704-2.56z\"  ></path></symbol><symbol id=\"icon-h2\" viewBox=\"0 0 1024 1024\"><path d=\"M88 448h400V172c0-24.3 19.7-44 44-44s44 19.7 44 44v680c0 24.3-19.7 44-44 44s-44-19.7-44-44V536H88v316c0 24.3-19.7 44-44 44S0 876.3 0 852V172c0-24.3 19.7-44 44-44s44 19.7 44 44v276z m935.282 448H680c0.479-41.591 10.533-77.923 30.163-108.997 19.63-31.074 46.44-58.084 80.434-81.031 16.279-11.952 33.275-23.544 50.99-34.779 17.714-11.234 33.993-23.305 48.835-36.213 14.842-12.907 27.05-26.89 36.626-41.95 9.576-15.058 14.603-32.388 15.081-51.988 0-9.083-1.077-18.764-3.231-29.042-2.155-10.278-6.344-19.84-12.568-28.683-6.224-8.845-14.842-16.254-25.854-22.23-11.012-5.976-25.375-8.964-43.09-8.964-16.278 0-29.803 3.227-40.576 9.68-10.772 6.455-19.39 15.299-25.854 26.533-6.463 11.235-11.251 24.5-14.363 39.798-3.112 15.298-4.908 31.791-5.386 49.48h-81.87c0-27.728 3.71-53.423 11.13-77.087 7.422-23.664 18.553-44.101 33.395-61.311 14.842-17.21 32.916-30.715 54.222-40.516 21.305-9.8 46.081-14.7 74.33-14.7 30.641 0 56.255 5.02 76.843 15.059 20.587 10.04 37.224 22.707 49.912 38.005 12.688 15.298 21.665 31.91 26.931 49.838 5.267 17.927 7.9 35.018 7.9 51.272 0 20.078-3.112 38.244-9.336 54.498-6.224 16.254-14.603 31.193-25.136 44.818-10.533 13.625-22.502 26.174-35.908 37.647a538.302 538.302 0 0 0-41.653 32.27 1122.27 1122.27 0 0 0-43.09 28.683c-14.364 9.083-27.65 18.166-39.858 27.249-12.209 9.083-22.862 18.525-31.958 28.325-9.097 9.8-15.321 20.198-18.673 31.193h244.894V896z\"  ></path></symbol><symbol id=\"icon-cc-schema\" viewBox=\"0 0 1024 1024\"><path d=\"M575.901891 898.426003c6.281053-6.282077 9.699919-14.842033 9.699919-24.541953l0-80.46359c0-9.699919-3.419889-18.260899-9.699919-24.539906-6.260587-6.281053-15.380292-9.720386-24.499997-9.720386l0 0-80.406285 0 0 0c-9.680477 0-18.241456 3.439332-24.502044 9.720386l0 0c-6.260587 6.28003-10.260691 14.84101-10.260691 24.539906l0 80.46359c0 9.699919 4.000104 18.260899 10.260691 24.541953l0 0c6.260587 6.28003 14.820544 10.279111 24.502044 10.279111l0 0 80.406285 0 0 0C560.521598 908.705114 569.641303 904.706033 575.901891 898.426003L575.901891 898.426003zM470.995609 707.23659l80.406285 0c23.361057 0 45.042869 9.700943 61.002352 25.101701 15.382339 15.980973 25.084305 37.10099 25.084305 61.081147l0 0 0 80.46359 0 0c0 23.980157-9.700943 45.661969-25.084305 61.08217l0 0c-15.960507 15.401782-37.641295 25.100678-61.002352 25.100678l0 0-80.406285 0 0 0c-23.921829 0-45.604664-9.699919-60.984956-25.100678l0 0c-15.962553-15.420201-25.082258-37.103036-25.082258-61.08217l0-80.46359c0-23.981181 9.120728-45.100174 25.082258-61.081147C425.390945 716.938556 447.072757 707.23659 470.995609 707.23659L470.995609 707.23659zM897.466654 898.426003c6.260587-6.282077 10.261715-14.842033 10.261715-24.541953l0 0 0-80.46359 0 0c0-9.699919-4.001127-18.260899-10.261715-24.539906-6.281053-6.281053-14.821567-9.720386-24.521486-9.720386l0 0-80.386842 0 0 0c-9.699919 0-18.241456 3.439332-24.520463 9.720386-6.261611 6.28003-9.682523 14.84101-9.682523 24.539906l0 80.46359c0 9.699919 3.420913 18.260899 9.682523 24.541953l0 0c6.279007 6.28003 14.81952 10.279111 24.520463 10.279111l0 0 80.386842 0 0 0C882.645087 908.705114 891.186623 904.706033 897.466654 898.426003L897.466654 898.426003zM792.559349 707.23659l80.386842 0c23.941272 0 45.602617 9.700943 61.003376 25.101701 15.400758 15.980973 25.083282 37.10099 25.083282 61.081147l0 80.46359c0 23.980157-9.682523 45.661969-25.083282 61.08217-15.400758 15.401782-37.062104 25.100678-61.003376 25.100678l0 0-80.386842 0 0 0c-23.941272 0-45.043892-9.699919-61.006446-25.100678-15.399735-15.420201-25.080212-37.103036-25.080212-61.08217l0 0 0-80.46359 0 0c0-23.981181 9.680477-45.100174 25.080212-61.081147C747.515457 716.938556 768.617054 707.23659 792.559349 707.23659L792.559349 707.23659zM253.797845 898.426003c6.261611-6.282077 10.241248-14.842033 10.241248-24.541953l0 0 0-80.46359 0 0c0-9.699919-3.980661-18.260899-10.241248-24.539906-6.281053-6.281053-14.84101-9.720386-24.52251-9.720386l0 0-80.405261 0 0 0c-9.680477 0-18.241456 3.439332-24.502044 9.720386l0 0c-6.260587 6.28003-9.699919 14.84101-9.699919 24.539906l0 0 0 80.46359 0 0c0 9.699919 3.439332 18.260899 9.699919 24.541953l0 0c6.261611 6.28003 15.382339 10.279111 24.502044 10.279111l0 0L229.275335 908.705114l0 0C238.956835 908.705114 247.516792 904.706033 253.797845 898.426003L253.797845 898.426003zM148.870074 707.23659 229.275335 707.23659c23.942295 0 45.603641 9.700943 61.005422 25.101701 15.960507 15.980973 25.082258 37.10099 25.082258 61.081147l0 80.46359c0 23.980157-9.121751 45.661969-25.082258 61.08217-15.400758 15.401782-37.062104 25.100678-61.005422 25.100678l0 0-80.405261 0 0 0c-23.922852 0-45.023426-9.699919-60.984956-25.100678l0 0c-15.400758-15.420201-25.082258-37.103036-25.082258-61.08217l0-80.46359c0-23.981181 9.6815-45.100174 25.082258-61.081147C103.846648 716.938556 124.947222 707.23659 148.870074 707.23659L148.870074 707.23659zM207.61399 670.695349c-4.559852 4.580319-11.400654 7.421017-18.241456 7.421017l0 0 0 0c-7.419993 0-13.681604-2.840698-18.241456-7.421017-4.560876-4.559852-7.419993-10.840906-7.419993-18.259876l0 0 0 0L163.711084 491.488851l-0.579191 0c0-6.850012 3.439332-13.700023 8.000208-18.260899l0 0c4.559852-4.570085 10.821463-7.41897 18.241456-7.41897l295.881824 0 0-95.313809c0-6.851035 2.861164-13.131065 7.42204-18.261922l0 0c4.560876-4.569062 11.399631-7.419993 18.240433-7.419993 7.401574 0 13.681604 2.850931 18.241456 7.419993 4.561899 5.130857 7.981788 11.410887 7.981788 18.261922l0 95.313809 295.900244 0 0 0c6.840802 0 13.102413 2.848884 18.24248 7.41897 4.560876 4.561899 7.401574 11.410887 7.401574 18.260899l0 160.946622c0 7.41897-2.840698 13.700023-7.401574 18.259876-5.140067 4.580319-11.402701 8.001231-18.24248 8.001231-7.419993 0-13.680581-3.421936-18.240433-8.001231-5.140067-4.559852-7.979742-10.840906-7.979742-18.259876L806.821167 517.170766 536.57828 517.170766l0 135.26573 0 0c0 7.41897-2.858094 13.700023-7.419993 18.259876-4.559852 4.580319-10.839883 8.001231-18.241456 8.001231-6.841825 0-13.679557-3.421936-18.240433-8.001231l0 0c-4.560876-4.559852-7.42204-10.840906-7.42204-18.259876L485.254358 517.170766 215.015563 517.170766l0 135.26573C215.015563 659.854443 212.174865 666.135496 207.61399 670.695349L207.61399 670.695349zM446.493565 124.505665c-6.260587 6.279007-10.260691 15.409968-10.260691 24.539906l0 80.472799c0 9.700943 4.000104 18.260899 10.260691 24.540929 6.260587 6.28003 14.820544 10.280134 24.502044 10.280134l0 0 80.406285 0 0 0c9.119705 0 18.23941-4.000104 24.499997-10.280134l0 0c6.281053-6.28003 9.699919-14.839986 9.699919-24.540929l0-80.472799c0-9.129938-3.419889-18.261922-9.699919-24.539906-6.260587-6.281053-15.380292-9.700943-24.499997-9.700943l0 0-80.406285 0 0 0C461.314109 114.804722 452.753129 118.794593 446.493565 124.505665L446.493565 124.505665zM551.401894 315.702242l-80.406285 0 0 0c-23.921829 0-45.604664-9.128915-60.984956-25.110911l0 0c-15.962553-15.409968-25.082258-37.10099-25.082258-61.071937l0 0 0-80.472799c0-23.400966 9.120728-45.091988 25.082258-61.072961 15.380292-15.409968 37.063127-25.110911 60.984956-25.110911l0 0 80.406285 0 0 0c23.361057 0 45.042869 9.700943 61.002352 25.110911 15.382339 15.97995 25.084305 37.670971 25.084305 61.072961l0 0 0 80.472799 0 0c0 23.970948-9.700943 45.662992-25.084305 61.071937l0 0C596.443739 306.573327 574.762951 315.702242 551.401894 315.702242L551.401894 315.702242z\" fill=\"#231915\" ></path></symbol><symbol id=\"icon-xinjianbiaoge\" viewBox=\"0 0 1024 1024\"><path d=\"M432.128 174.08h-286.72c-34.816 0-61.44 26.624-61.44 61.44V552.96h348.16v-378.88z m40.96 0V552.96h348.16v-169.984c0-12.288 8.192-20.48 20.48-20.48s20.48 8.192 20.48 20.48v528.384c0 57.344-45.056 102.4-102.4 102.4h-614.4c-57.344 0-102.4-45.056-102.4-102.4v-675.84c0-57.344 45.056-102.4 102.4-102.4h448.512c12.288 0 20.48 8.192 20.48 20.48s-8.192 20.48-20.48 20.48h-120.832z m-40.96 798.72V593.92h-348.16v317.44c0 34.816 26.624 61.44 61.44 61.44h286.72z m40.96 0h286.72c34.816 0 61.44-26.624 61.44-61.44V593.92h-348.16v378.88z m346.112-839.68v-102.4c0-12.288 8.192-20.48 20.48-20.48s20.48 8.192 20.48 20.48v102.4h102.4c12.288 0 20.48 8.192 20.48 20.48s-8.192 20.48-20.48 20.48h-102.4v102.4c0 12.288-8.192 20.48-20.48 20.48s-20.48-8.192-20.48-20.48v-102.4h-102.4c-12.288 0-20.48-8.192-20.48-20.48s8.192-20.48 20.48-20.48h102.4z\"  ></path></symbol><symbol id=\"icon-export\" viewBox=\"0 0 1024 1024\"><path d=\"M883.3 228.2l-96.4-86.8c-15.9-15.9-42-15.9-58 0v57.8c-37.6 4.3-98.9 16-160.6 47.7-88.8 45.7-194.7 141.4-203.4 339-0.8 11.2-0.9 18-0.9 19h0.4v2.5c-0.1 27.5 22 49.9 49.5 50.2 27.6 0.3 50.2-21.9 50.5-49.5 0-5.5 0.2-10.9 0.4-16.2 2.1-25.5 10.1-84 40.5-141 22.5-42.1 52.8-75.5 90.4-99.4 37-23.5 81.7-38.2 133.3-43.8v64.6c15.9 15.9 42 15.9 58 0l96.4-86.1c15.9-16 15.9-42.1-0.1-58z\"  ></path><path d=\"M834.8 932.4H188.4c-25.4 0-49.1-9.9-66.9-27.8C103.8 886.7 94 863.1 94 838V185.7c0-52 42.3-94.3 94.4-94.3H353c19.3 0 35 15.7 35 35s-15.7 35-35 35H188.4c-13.5 0-24.4 10.9-24.4 24.3V838c0 6.4 2.6 12.7 7.3 17.4 4.5 4.5 10.6 7 17.1 7h646.4c6.4 0 12.6-2.6 17.2-7.1 4.5-4.5 7-10.7 7-17.2V674c0-19.3 15.7-35 35-35s35 15.7 35 35v164c0 25.3-9.8 49-27.5 66.7-17.6 17.6-41.9 27.7-66.7 27.7z\"  ></path></symbol><symbol id=\"icon-jiaoseguanli\" viewBox=\"0 0 1024 1024\"><path d=\"M750.231313 668.295071c-55.296189 0-100.285853 45.0045-100.285853 100.300689 0 55.30608 44.989664 100.310581 100.285853 100.310581 55.311026 0 100.300689-45.0045 100.300689-100.310581C850.532003 713.299572 805.542339 668.295071 750.231313 668.295071zM750.231313 817.220403c-26.809824 0-48.614752-21.814819-48.614752-48.624643 0-26.799933 21.804928-48.614752 48.614752-48.614752 26.814769 0 48.629588 21.814819 48.629588 48.614752C798.860901 795.405585 777.046083 817.220403 750.231313 817.220403zM1015.415634 716.192718c-3.763563-19.861327-16.508244-32.699973-32.873068-32.699973l-2.270007 0c-31.305328 0-56.789745-25.46958-56.789745-56.769963 0-8.219503 3.595414-18.318315 4.960386-21.483467 7.749676-17.45779 1.785343-38.88191-15.440006-50.810576l-66.398948-36.765215c-5.202718-2.245279-10.825808-3.392647-16.715957-3.392647-12.027576 0-23.911732 4.90104-31.785047 13.061196-8.273904 8.526127-30.242035 26.122392-45.375417 26.122392-15.271857 0-37.378463-17.937508-45.667204-26.59222-7.912879-8.362924-19.905837-13.357929-32.066943-13.357929-5.825857 0-11.364873 1.112749-16.340096 3.264063l-1.241333 0.509392-67.571043 37.111403-1.607304 1.038565c-15.707065 10.929664-21.706017 32.343894-14.005796 49.648371 0.514337 1.236387 5.029624 12.225398 5.029624 21.64667 0 31.300383-25.479471 56.769963-56.799636 56.769963l-2.645869 0c-15.966212 0-28.720784 12.838647-32.482369 32.719755-0.573684 3.05635-5.603308 30.518986-5.603308 52.719558 0 22.210463 5.029624 49.668154 5.603308 52.754177 3.760596 19.856381 16.505277 32.695028 32.878013 32.695028l2.245279 0c31.320165 0 56.804582 25.474525 56.804582 56.769963 0 8.219503-3.595414 18.318315-4.960386 21.488413-7.739785 17.433062-1.795234 38.8374 15.370768 50.80563l65.147724 36.300333c5.192827 2.255171 10.815917 3.42232 16.720903 3.42232 12.245181 0 24.25792-5.123589 32.106507-13.684335 8.021681-8.709113 30.805828-27.784097 46.369472-27.784097 15.726847 0 38.253825 19.114549 46.676096 28.352835 7.902988 8.709113 19.994857 13.90194 32.338948 13.90194 5.806075 0 11.335199-1.112749 18.056201-4.089969l65.869774-36.394299 1.57763-1.043511c15.687283-10.904937 21.671398-32.299384 13.981068-49.623644-0.514337-1.241333-5.029624-12.225398-5.029624-21.651616 0-31.295437 25.484417-56.769963 56.789745-56.769963l2.616196 0c15.998853 0 28.75837-12.838647 32.507097-32.645572 0.049455-0.276951 5.618144-29.445802 5.618144-52.803632C1021.013996 746.731486 1015.989318 719.26885 1015.415634 716.192718zM966.055093 803.625088c-53.486118 6.997953-94.237446 52.670102-94.237446 107.526137 0 12.665552 3.05635 24.693129 5.746729 32.833503l-47.531676 26.275704c-4.272955-4.18888-11.092868-10.464783-19.544812-16.730794-21.008694-15.588372-41.35963-23.521033-60.469234-23.521033-18.951346 0-39.139079 7.779349-60.019189 23.08088-8.328305 6.12259-15.07898 12.195725-19.421173 16.404388l-45.434763-25.420124c2.695324-8.155211 5.776402-20.256971 5.776402-32.922523 0-54.856035-40.741437-100.528185-94.2325-107.526137-1.369917-8.852534-3.28879-23.283647-3.28879-34.69303 0-11.458838 1.908982-25.850387 3.28879-34.678193 53.481172-7.002898 94.2325-52.684939 94.2325-107.531083 0-12.630933-3.05635-24.653564-5.741783-32.798884l49.059851-26.923571c4.258118 4.005895 11.087922 10.03452 19.480519 16.008744 20.598214 14.683336 40.45954 22.13628 59.010297 22.13628 18.367771 0 38.060949-7.309522 58.520687-21.720853 8.32336-5.84564 15.064144-11.691279 19.356881-15.677392l46.977775 26.10261c-2.690379 8.150266-5.761565 20.207515-5.761565 32.873068 0 54.856035 40.736491 100.528185 94.222609 107.531083 1.399591 9.010791 3.303627 23.372667 3.303627 34.688084C969.34784 780.33155 967.449738 794.648915 966.055093 803.625088zM70.034283 950.48318l0-56.799636c0-193.123708 148.930278-352.285338 338.673208-370.222846l-0.082096-0.039564c0 0 0.190898 0 0.494555 0.009891 11.631932-1.092966 23.399373-1.706215 35.313202-1.706215 1.793256 0 3.560796 0.098911 5.346139 0.118693 41.962988-3.254172 105.458898-13.113619 138.643535-42.734493l16.241185-12.657639c63.441509-49.541548 99.830862-123.556641 99.830862-203.053382C704.494871 120.948359 587.814522 5.064243 444.381717 5.064243S184.269552 120.948359 184.269552 263.396999c0 79.496741 36.391331 153.511835 99.83284 203.053382l16.236239 12.657639-19.131364 7.596364C112.198059 553.806589 2.990435 713.551795 2.990435 893.683544l0 90.028783c0 18.328206 15.026558 33.239038 33.515989 33.239038l489.766669 0c-15.944452-20.420174-29.534822-42.694929-40.520865-66.468185L70.034283 950.48318 70.034283 950.48318zM251.313399 263.396999c0-105.80014 86.606463-191.869517 193.068318-191.869517 106.459877 0 193.081176 86.069376 193.081176 191.869517 0 105.802119-86.621299 191.867538-193.081176 191.867538C337.919862 455.263548 251.313399 369.199118 251.313399 263.396999z\"  ></path></symbol><symbol id=\"icon-console\" viewBox=\"0 0 1024 1024\"><path d=\"M170.666667 896c-46.933333 0-85.333333-38.4-85.333334-85.333333V213.333333c0-46.933333 38.4-85.333333 85.333334-85.333333h682.666666c46.933333 0 85.333333 38.4 85.333334 85.333333v597.333334c0 46.933333-38.4 85.333333-85.333334 85.333333H170.666667z m0-85.333333h682.666666V298.666667H170.666667v512z m405.333333-85.333334c-12.8 0-21.333333-8.533333-21.333333-21.333333v-42.666667c0-12.8 8.533333-21.333333 21.333333-21.333333h170.666667c12.8 0 21.333333 8.533333 21.333333 21.333333v42.666667c0 12.8-8.533333 21.333333-21.333333 21.333333h-170.666667z m-337.066667 0l170.666667-170.666666-170.666667-170.666667h119.466667l140.8 140.8c17.066667 17.066667 17.066667 42.666667 0 59.733333L358.4 725.333333H238.933333z\"  ></path></symbol><symbol id=\"icon-24gf-folderMinus\" viewBox=\"0 0 1024 1024\"><path d=\"M970.666667 213.333333H546.586667a10.573333 10.573333 0 0 1-7.54-3.126666L429.793333 100.953333A52.986667 52.986667 0 0 0 392.08 85.333333H96a53.393333 53.393333 0 0 0-53.333333 53.333334v704a53.393333 53.393333 0 0 0 53.333333 53.333333h874.666667a53.393333 53.393333 0 0 0 53.333333-53.333333V266.666667a53.393333 53.393333 0 0 0-53.333333-53.333334zM661.333333 554.666667H405.333333a21.333333 21.333333 0 0 1 0-42.666667h256a21.333333 21.333333 0 0 1 0 42.666667z\"  ></path></symbol><symbol id=\"icon-chakan\" viewBox=\"0 0 1024 1024\"><path d=\"M928.842245 512.091074c0-5.006014-0.846274-9.193383-1.086751-9.691733-0.182149-2.480494-1.028423-7.001461-1.815345-9.374508-0.210801-0.590448-0.484024-1.209548-0.724501-1.799996-0.424672-1.360997-0.876973-2.691295-1.390673-3.749394-76.871785-168.137395-242.376213-281.144168-411.782507-281.144168-169.375595 0-334.865697 112.902396-411.388535 280.130072-0.921999 1.815345-1.572822 3.553942-1.981121 5.066389-0.181125 0.49835-0.39295 0.967024-0.558725 1.406023-1.512447 4.430916-1.542122 7.514137-1.421372 6.712889-0.710175 3.251044-1.360997 9.722432-1.360997 9.722432-0.181125 1.949398-0.181125 3.50687 0.030699 5.442966 0 0 0.649799 5.65479 0.968048 6.80294 0.090051 1.602498 0.483001 3.931542 0.951675 6.048763l-0.030699 0c0.408299 1.814322 0.968048 3.568269 1.738597 5.291516 0.393973 1.330298 0.862647 2.570545 1.270946 3.507894 76.976162 168.166047 242.436588 281.20352 411.781484 281.20352 169.436994 0 334.941422-112.945375 410.936233-279.328823 1.177825-2.177596 1.935072-4.233418 2.448772-6.018064 0.2415-0.543376 0.454348-1.027399 0.604774-1.511423 1.331321-3.872191 1.602498-7.227612 1.481747-7.227612l-0.028653 0.029676C928.027693 520.921183 928.842245 516.89959 928.842245 512.091074zM872.717993 514.146896c-0.029676 0.121773-0.091074 0.272199-0.151449 0.393973-0.090051 0.36225-0.240477 0.785899-0.332575 1.209548-68.403926 147.420561-212.830293 246.337431-360.191502 246.337431-146.997935 0-291.168476-98.642624-360.252901-246.578931-0.166799-0.5137-0.287549-0.998747-0.468674-1.481747-0.030699-0.484024-0.12075-0.876973-0.150426-1.150196-0.060375-0.300852-0.12075-0.724501-0.166799-1.088798l0-0.3776c0.166799-0.620124 0.286526-1.239224 0.347924-1.919722 0.12075-0.36225 0.211824-0.710175 0.347924-1.103124C220.132094 360.89042 364.680235 261.928524 512.041444 261.928524c147.420561 0 291.940049 99.051947 360.161826 246.322082 0.060375 0.287549 0.121773 0.530073 0.212848 0.726547 0.060375 0.2415 0.119727 0.484024 0.240477 0.740874 0.151449 1.104147 0.272199 2.192945 0.423649 2.736321C872.899118 513.028423 872.809067 513.572822 872.717993 514.146896z\"  ></path><path d=\"M512.041444 373.060601c-76.598562 0-138.954749 62.325487-138.954749 138.939399 0 76.598562 62.356187 138.954749 138.954749 138.954749 76.598562 0 138.954749-62.356187 138.954749-138.954749C650.996193 435.386088 588.640006 373.060601 512.041444 373.060601zM512.041444 595.372849c-45.935192 0-83.371826-37.406958-83.371826-83.371826 0-45.950542 37.436634-83.356476 83.371826-83.356476 45.964868 0 83.373873 37.406958 83.373873 83.356476C595.414293 557.965891 558.006312 595.372849 512.041444 595.372849z\"  ></path></symbol><symbol id=\"icon-fuzhi_o\" viewBox=\"0 0 1024 1024\"><path d=\"M725.333333 341.333333h128v512H341.333333v-128H213.333333V213.333333h512v128z m0 42.666667v341.333333H384v85.333334h426.666667V384h-85.333334zM256 256v426.666667h426.666667V256H256z\"  ></path></symbol><symbol id=\"icon-zhihang\" viewBox=\"0 0 1024 1024\"><path d=\"M912 512l-800 448V64z\"  ></path></symbol><symbol id=\"icon-m-geshihuawenzi\" viewBox=\"0 0 1024 1024\"><path d=\"M122.88 171.52h778.24c-9.216 0-16.384-7.168-16.384-16.384v713.728c0-9.216 7.168-16.384 16.384-16.384H122.88c9.216 0 16.384 7.168 16.384 16.384V155.648c0 8.192-6.656 15.872-16.384 15.872z m-32.768 684.544c0 26.112 20.992 47.104 47.104 47.104h750.08c26.112 0 47.104-20.992 47.104-47.104V167.936c0-26.112-20.992-47.104-47.104-47.104H137.216c-26.112 0-47.104 20.992-47.104 47.104v688.128z\"  ></path><path d=\"M644.608 550.912v-45.056l-83.968 56.32 83.968 56.32V573.44zM784.896 450.048v131.584h-140.288v-37.888H747.52V450.048zM250.368 394.24h323.584v56.32H250.368zM250.368 539.136h248.832v56.32H250.368zM250.368 249.856h510.976v56.32H250.368zM250.368 684.032h510.976v56.32H250.368z\"  ></path></symbol><symbol id=\"icon-github-fill\" viewBox=\"0 0 1024 1024\"><path d=\"M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9 23.5 23.2 38.1 55.4 38.1 91v112.5c0.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z\"  ></path></symbol><symbol id=\"icon-baocun2\" viewBox=\"0 0 1024 1024\"><path d=\"M579.7 291.4c18.8 0 34.1-15.3 34.1-34.1v-34.1c0-18.8-15.4-34.1-34.1-34.1-18.8 0-34.1 15.3-34.1 34.1v34.1c0 18.7 15.4 34.1 34.1 34.1z\"  ></path><path d=\"M944.7 216.3L808.2 79.9c-6.8-6.8-15.3-10.2-23.9-10.2H170.4c-56.3 0-102.3 46-102.3 102.3v682.1c0 56.3 46 102.3 102.3 102.3H852.5c56.3 0 102.3-46 102.3-102.3V240.2c0.1-8.5-3.3-17-10.1-23.9zM358 137.9h307v182.5c0 11.9-10.2 22.2-22.2 22.2H380.2c-11.9 0-22.2-10.2-22.2-22.2V137.9z m358.1 750.3H306.9V652.9c0-20.5 17.1-37.5 37.5-37.5h334.2c20.5 0 37.5 17 37.5 37.5v235.3z m170.6-34.1c0 18.8-15.3 34.1-34.1 34.1h-66.5V652.9c0-58-47.7-105.7-105.7-105.7h-336c-58 0-105.7 47.7-105.7 105.7v235.3h-68.2c-18.8 0-34.1-15.3-34.1-34.1V172c0-18.8 15.3-34.1 34.1-34.1h119.4v182.5c0 49.5 40.9 90.4 90.4 90.4h262.6c49.5 0 90.4-40.9 90.4-90.4V137.9h37.5l116 116v600.2z\"  ></path></symbol><symbol id=\"icon-jiantou_xiangzuoliangci_o\" viewBox=\"0 0 1024 1024\"><path d=\"M170.666667 533.333333L490.666667 213.333333l29.866666 29.866667-290.133333 290.133333 290.133333 290.133334-29.866666 29.866666L170.666667 533.333333z m298.666666 0L789.333333 213.333333l29.866667 29.866667-290.133333 290.133333 290.133333 290.133334-29.866667 29.866666-320-320z\"  ></path></symbol><symbol id=\"icon-xinjianchuangkou\" viewBox=\"0 0 1026 1024\"><path d=\"M190.464 764.928q0 26.624 18.944 45.056t45.568 18.432l254.976 0 0 128-319.488 0q-26.624 0-49.664-10.24t-40.448-27.648-27.648-40.448-10.24-49.664l0-640q0-26.624 10.24-49.664t27.648-40.448 40.448-27.648 49.664-10.24l704.512 0q26.624 0 49.664 10.24t40.448 27.648 27.648 40.448 10.24 49.664l0 253.952-128 3.072 0-64.512q0-26.624-18.944-45.568t-45.568-18.944l-575.488 0q-26.624 0-45.568 18.944t-18.944 45.568l0 384zM799.744 507.904q46.08 0 87.04 17.408t71.68 48.128 48.64 71.68 17.92 88.064-17.92 88.064-48.64 71.68-71.68 48.128-87.04 17.408q-47.104 0-88.064-17.408t-71.68-48.128-48.64-71.68-17.92-88.064 17.92-88.064 48.64-71.68 71.68-48.128 88.064-17.408zM958.464 764.928l0-64.512-128 0 0-128-63.488 0 0 128-128 0 0 64.512 128 0 0 128 63.488 0 0-128 128 0z\"  ></path></symbol><symbol id=\"icon-loading2\" viewBox=\"0 0 1024 1024\"><path d=\"M980.752 313.697c-25.789-60.972-62.702-115.725-109.713-162.736-47.012-47.011-101.764-83.924-162.736-109.713C645.161 14.542 578.106 1 509 1c-2.242 0-4.48 0.015-6.715 0.043-16.567 0.211-29.826 13.812-29.615 30.38 0.209 16.438 13.599 29.618 29.99 29.618l0.39-0.002c1.98-0.026 3.963-0.039 5.95-0.039 61.033 0 120.224 11.947 175.93 35.508 53.82 22.764 102.162 55.359 143.683 96.879s74.115 89.862 96.88 143.683C949.054 392.776 961 451.967 961 513c0 16.568 13.432 30 30 30s30-13.432 30-30c0-69.106-13.541-136.162-40.248-199.303z\"  ></path></symbol><symbol id=\"icon-lianjiekelong\" viewBox=\"0 0 1024 1024\"><path d=\"M908.8 64h-345.6c-25.6 0-51.2 25.6-51.2 51.2v339.2c0 32 19.2 51.2 51.2 51.2h345.6c25.6 0 51.2-25.6 51.2-51.2v-339.2c0-25.6-19.2-51.2-51.2-51.2z m-64 307.2c0 12.8-12.8 25.6-25.6 25.6h-179.2c-12.8 0-19.2-12.8-19.2-25.6V198.4c0-12.8 12.8-25.6 19.2-25.6h179.2c12.8 0 25.6 12.8 25.6 25.6v172.8zM192 454.4l128-166.4h-76.8c0-96 57.6-115.2 153.6-115.2h57.6v-115.2h-57.6c-160 0-249.6 70.4-249.6 224h-83.2l128 172.8z m640 115.2l-128 166.4h76.8c0 96-57.6 115.2-153.6 115.2h-57.6v115.2h57.6c160 0 249.6-70.4 249.6-224h83.2l-128-172.8z m-371.2-57.6h-345.6c-25.6 0-51.2 25.6-51.2 51.2v339.2c0 32 19.2 51.2 51.2 51.2h345.6c25.6 0 51.2-25.6 51.2-51.2v-339.2c0-25.6-19.2-51.2-51.2-51.2z m-64 307.2c0 12.8-12.8 25.6-25.6 25.6h-179.2c-12.8 0-25.6-12.8-25.6-25.6V646.4c0-12.8 12.8-25.6 25.6-25.6h179.2c12.8 0 25.6 12.8 25.6 25.6v172.8z\"  ></path></symbol><symbol id=\"icon-SQLshengjiwenjian\" viewBox=\"0 0 1024 1024\"><path d=\"M659.911111 102.4H204.8v394.24h52.337778v-341.333333h365.226666V367.502222h212.764445v501.76H257.137778V707.356444H204.8V921.6h682.666667V329.955556z m14.791111 212.764444V191.260444L798.606222 315.164444z\"  ></path><path d=\"M136.533333 477.866667v250.311111h568.888889V477.866667z m177.720889 181.248a49.834667 49.834667 0 0 1-22.755555 19.114666 98.531556 98.531556 0 0 1-37.205334 6.257778 74.069333 74.069333 0 0 1-49.834666-15.018667A65.308444 65.308444 0 0 1 183.296 625.777778l34.133333-3.299556a40.504889 40.504889 0 0 0 12.288 24.917334 37.319111 37.319111 0 0 0 25.031111 7.850666 38.343111 38.343111 0 0 0 24.689778-6.712889 20.935111 20.935111 0 0 0 8.419556-16.497777 15.132444 15.132444 0 0 0-3.527111-10.24A29.013333 29.013333 0 0 0 271.928889 614.4c-4.096-1.365333-13.312-3.868444-27.648-7.395556A94.776889 94.776889 0 0 1 204.8 589.596444a44.487111 44.487111 0 0 1-15.701333-34.133333 43.235556 43.235556 0 0 1 7.395555-24.462222 45.511111 45.511111 0 0 1 21.276445-17.294222 85.674667 85.674667 0 0 1 34.133333-6.030223 72.476444 72.476444 0 0 1 48.469333 14.108445 49.265778 49.265778 0 0 1 17.066667 37.660444l-34.133333 1.592889a29.468444 29.468444 0 0 0-10.24-19.228444 34.133333 34.133333 0 0 0-21.845334-5.802667 39.708444 39.708444 0 0 0-23.552 6.257778 12.401778 12.401778 0 0 0-5.461333 10.581333 12.970667 12.970667 0 0 0 5.12 10.353778 88.291556 88.291556 0 0 0 31.744 11.377778 168.96 168.96 0 0 1 37.205333 12.288 47.672889 47.672889 0 0 1 19.000889 17.408 53.703111 53.703111 0 0 1-1.365333 54.727111zM504.035556 698.595556a80.782222 80.782222 0 0 1-14.791112-6.485334c-1.137778 0-8.760889-5.575111-22.755555-15.132444a92.387556 92.387556 0 0 1-37.205333 7.395555 81.806222 81.806222 0 0 1-61.781334-22.755555 90.112 90.112 0 0 1-22.755555-65.194667 90.112 90.112 0 0 1 22.755555-65.194667 80.213333 80.213333 0 0 1 60.757334-22.755555 79.644444 79.644444 0 0 1 60.416 22.755555 91.022222 91.022222 0 0 1 22.755555 65.194667 113.777778 113.777778 0 0 1-6.144 38.912A79.644444 79.644444 0 0 1 489.244444 659.911111a106.723556 106.723556 0 0 0 27.648 14.449778z m154.624-17.180445H538.282667V512h34.133333v140.629333h85.902222z\"  ></path><path d=\"M427.349333 537.031111a43.918222 43.918222 0 0 0-34.133333 14.677333 65.308444 65.308444 0 0 0-13.084444 44.259556 66.673778 66.673778 0 0 0 13.084444 44.828444 42.666667 42.666667 0 0 0 33.223111 14.904889 41.301333 41.301333 0 0 0 14.108445-2.503111 98.190222 98.190222 0 0 0-21.276445-10.695111l9.671111-19.797333a120.718222 120.718222 0 0 1 32.540445 17.521778 50.744889 50.744889 0 0 0 9.898666-18.887112 86.698667 86.698667 0 0 0 3.413334-25.372444 65.422222 65.422222 0 0 0-13.084445-44.373333 44.032 44.032 0 0 0-34.360889-14.563556z\"  ></path></symbol><symbol id=\"icon-sql\" viewBox=\"0 0 1024 1024\"><path d=\"M776.533333 755.456H598.903467c-14.421333 0-26.112 12.475733-26.112 27.869867v168.618666h28.433066L776.533333 760.439467v-4.983467zM776.533333 699.733333v-88.183466c0-15.189333 11.4688-27.477333 25.6-27.477334s25.6 12.288 25.6 27.477334v160.136533c0 7.2192-2.645333 14.1312-7.338666 19.285333L630.2208 998.7072a24.746667 24.746667 0 0 1-18.261333 8.226133H228.693333C163.669333 1006.933333 110.933333 950.306133 110.933333 880.452267V143.530667C110.933333 73.693867 163.669333 17.066667 228.693333 17.066667h481.28C774.997333 17.066667 827.733333 73.693867 827.733333 143.547733v48.930134c0 15.189333-11.4688 27.477333-25.6 27.477333s-25.6-12.288-25.6-27.477333V143.530667c0-39.4752-29.7984-71.492267-66.56-71.492267H228.693333c-36.7616 0-66.56 32.017067-66.56 71.509333v736.887467c0 39.4752 29.7984 71.492267 66.56 71.492267H520.533333v-168.618667C520.533333 737.160533 555.6224 699.733333 598.903467 699.733333H776.533333zM367.7696 280.576c27.272533 0 47.530667 5.512533 60.416 16.896 13.687467 11.997867 19.1488 30.8224 16.093867 56.149333h-37.666134c-0.238933-14.2848-4.215467-24.6784-11.3664-30.839466-7.0656-6.485333-19.456-9.4208-36.317866-9.4208-14.609067 0-26.0608 1.9456-34.747734 6.178133-10.752 4.864-17.015467 12.970667-19.336533 24.0128-2.048 9.745067 1.4336 17.8688 11.246933 23.722667 4.317867 2.577067 16.605867 7.458133 36.608 14.267733 29.44 9.728 47.9232 17.527467 55.927467 22.7328 17.578667 12.014933 24.1664 28.5696 19.6608 50.005333-4.369067 20.770133-15.9744 37.3248-34.730667 49.322667-18.688 11.690667-43.042133 17.8688-72.584533 17.8688-28.5696 0-49.8176-5.5296-63.402667-16.554667-16.605867-13.653333-22.801067-35.072-18.210133-64.6144h37.666133c-1.092267 17.527467 2.389333 30.208 10.888534 37.666134 7.7312 6.485333 20.9408 10.069333 40.106666 10.069333 16.878933 0 31.112533-2.935467 42.018134-8.448 10.973867-5.8368 17.749333-13.312 19.797333-23.04 2.594133-12.356267-2.833067-22.084267-15.616-29.2352-4.061867-2.269867-17.902933-7.458133-41.915733-15.2576-26.658133-9.079467-43.144533-15.581867-49.152-19.473067-15.581867-10.717867-21.077333-26.299733-16.776534-46.762666 4.3008-20.445867 16.162133-36.676267 36.164267-48.366934 18.602667-11.3664 40.226133-16.878933 65.2288-16.878933z m249.122133 0c35.3792 0 60.910933 11.3664 76.509867 34.4064 14.8992 21.76 18.568533 50.653867 11.042133 86.357333-7.304533 34.747733-22.664533 62.976-45.346133 84.411734 6.109867 11.042133 12.424533 22.7328 18.261333 35.072l-29.184 21.418666a712.5504 712.5504 0 0 0-18.1248-35.703466c-18.944 9.728-40.482133 14.933333-63.863466 14.933333-35.703467 0-61.166933-11.690667-76.509867-34.423467-14.848-22.0672-18.244267-50.6368-10.8544-85.7088 7.441067-35.3792 22.869333-63.9488 47.0016-86.016 25.0368-23.381333 55.3472-34.747733 91.067733-34.747733z m-7.048533 33.450667c-24.0128 0-44.2368 8.106667-60.962133 24.337066-15.940267 15.581867-26.4704 36.352-32.085334 62.976-5.5296 26.299733-3.7376 47.086933 5.649067 62.6688 9.642667 15.906133 26.760533 24.029867 50.7904 24.029867a90.794667 90.794667 0 0 0 41.2672-9.4208c-8.704-14.2848-17.066667-26.948267-25.378133-38.314667l26.5728-21.418666a704.085333 704.085333 0 0 1 25.838933 37.649066c11.451733-14.2848 19.899733-32.785067 24.610133-55.1936 5.7344-27.255467 3.754667-48.6912-5.632-64.273066-9.710933-15.581867-26.624-23.04-50.670933-23.04z m171.434667-28.910934h37.666133l-41.949867 199.338667h125.320534l-6.826667 32.477867h-162.986667l48.776534-231.816534z\"  ></path></symbol><symbol id=\"icon-lianjieliu\" viewBox=\"0 0 1024 1024\"><path d=\"M280.224 425.856h348.608a29.536 29.536 0 1 0 0-59.072H280.224c-52.448 0-93.152-34.304-93.152-73.856s40.704-73.856 93.152-73.856h140.128C432.096 258.816 468.448 288 512 288s79.904-29.184 91.648-68.928h178.08a29.536 29.536 0 1 0 0-59.072h-179.584C588.896 122.784 553.728 96 512 96s-76.896 26.784-90.112 64H280.224C197.184 160 128 218.272 128 292.928s69.184 132.928 152.224 132.928z\"  ></path><path d=\"M895.936 415.2A96 96 0 1 0 800 512c30.656 0 57.632-14.624 75.2-36.992 10.56 12.064 16.832 26.56 16.832 41.92 0 39.552-40.704 73.856-93.152 73.856H306.016A95.584 95.584 0 0 0 224 544a95.68 95.68 0 0 0-95.232 88.352C89.888 656.224 64 695.424 64 740.928c0 74.656 69.184 132.928 152.224 132.928h241.728A95.808 95.808 0 0 0 544 928a96 96 0 1 0 0-192 95.904 95.904 0 0 0-94.272 78.752H216.224c-52.448 0-93.152-34.304-93.152-73.856 0-17.504 8.32-33.792 21.76-46.72A95.808 95.808 0 0 0 224 736a95.68 95.68 0 0 0 95.008-86.144h479.84c83.072 0 152.224-58.272 152.224-132.928 0.032-41.536-21.824-77.568-55.136-101.728z\"  ></path></symbol><symbol id=\"icon-tiaozhuan\" viewBox=\"0 0 1024 1024\"><path d=\"M802.56 192c30.805333 0 54.912 25.984 57.6 57.941333l0.298667 6.058667v512c0 32.64-22.272 60.544-52.224 63.701333l-5.674667 0.298667H363.690667c-30.848 0-54.912-25.984-57.642667-57.941333L305.792 768v-128a21.333333 21.333333 0 0 1 42.325333-3.84l0.341334 3.84v128c0 11.093333 5.973333 19.328 12.714666 20.992l2.517334 0.341333H802.56c6.741333 0 13.525333-7.04 14.933333-17.322666l0.298667-4.010667V256c0-11.093333-6.016-19.328-12.714667-20.992l-2.56-0.341333H363.733333c-6.784 0-13.568 7.04-14.976 17.322666L348.458667 256v128a21.333333 21.333333 0 0 1-42.325334 3.84L305.792 384V256c0-32.64 22.229333-60.544 52.181333-63.701333L363.690667 192H802.56z\"  ></path><path d=\"M610.56 519.125333a21.333333 21.333333 0 0 0-17.493333-20.992l-3.84-0.341333h-384a21.333333 21.333333 0 0 0-3.84 42.325333l3.84 0.341334h384a21.333333 21.333333 0 0 0 21.333333-21.333334z\"  ></path><path d=\"M464 371.541333a21.333333 21.333333 0 0 1 27.008-4.266666l3.114667 2.261333 164.608 144.042667-164.608 144.042666a21.333333 21.333333 0 0 1-30.762667-29.312l2.645333-2.773333L593.92 513.578667 466.005333 401.621333a21.333333 21.333333 0 0 1-4.266666-26.965333l2.261333-3.114667z\"  ></path></symbol><symbol id=\"icon-key\" viewBox=\"0 0 1024 1024\"><path d=\"M675 135c-116.897 0-212 95.103-212 212 0 46.416 15.006 89.387 40.404 124.341L188.373 786.373c-12.497 12.496-12.497 32.758 0 45.254C194.621 837.876 202.811 841 211 841s16.379-3.124 22.627-9.373l89.631-89.631 107.707 107.706c6.249 6.248 14.438 9.372 22.627 9.372s16.379-3.124 22.627-9.373c12.497-12.496 12.497-32.758 0-45.255L368.514 696.74 410.255 655l74.708 74.708c6.249 6.249 14.438 9.373 22.627 9.373s16.379-3.124 22.627-9.373c12.497-12.496 12.497-32.758 0-45.254l-74.708-74.709 92.837-92.837C583.708 543.335 627.559 559 675 559c116.897 0 212-95.103 212-212s-95.103-212-212-212z m0 360c-39.781 0-75.942-15.781-102.564-41.406a32.26 32.26 0 0 0-2.81-3.222 32.28 32.28 0 0 0-2.809-2.49C542.132 421.428 527 385.951 527 347c0-81.607 66.393-148 148-148s148 66.393 148 148-66.393 148-148 148z\"  ></path></symbol><symbol id=\"icon-bofangjilu\" viewBox=\"0 0 1024 1024\"><path d=\"M822.496 473.152l52.053333 29.290667C869.461333 306.56 709.098667 149.333333 512 149.333333c-200.298667 0-362.666667 162.368-362.666667 362.666667s162.368 362.666667 362.666667 362.666667c122.538667 0 234.645333-61.194667 301.578667-161.152a32 32 0 1 1 53.173333 35.616C788.064 866.634667 656.117333 938.666667 512 938.666667 276.362667 938.666667 85.333333 747.637333 85.333333 512S276.362667 85.333333 512 85.333333s426.666667 191.029333 426.666667 426.666667c0 10.954667-0.853333 26.357333-2.517334 46.528-1.930667 23.242667-27.274667 36.682667-47.594666 25.248l-97.450667-54.848a32 32 0 1 1 31.392-55.786667z m-493.12 176.213333L480 498.762667V320a32 32 0 0 1 64 0v192a32 32 0 0 1-9.376 22.624l-160 160a32 32 0 1 1-45.248-45.248z\" fill=\"#000000\" ></path></symbol><symbol id=\"icon-chenggong\" viewBox=\"0 0 1024 1024\"><path d=\"M512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024z m-49.590857-377.197714L315.977143 498.614857 219.428571 590.848c70.217143 37.814857 168.594286 106.788571 252.854858 213.723429C531.821714 692.662857 715.337143 463.725714 804.571429 443.245714c-14.409143-57.709714-22.528-166.034286 0-223.817143-183.003429 120.685714-342.162286 427.373714-342.162286 427.373715z\"  ></path></symbol><symbol id=\"icon-shibai\" viewBox=\"0 0 1024 1024\"><path d=\"M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m253.44 693.248l-72.192 72.192L512 584.192l-181.248 181.248-72.192-72.192L439.808 512 258.56 330.752l72.192-72.192L512 439.808l181.248-181.248 72.192 72.192L584.192 512z\"  ></path></symbol><symbol id=\"icon-shouhuishangxia\" viewBox=\"0 0 1024 1024\"><path d=\"M454.464 654.016l-253.44 253.44c-12.032 12.032-12.032 31.36 0 43.392 11.968 11.968 31.36 11.968 43.328 0l237.376-237.376 237.376 237.376c11.968 11.968 31.36 11.968 43.328 0 12.032-12.032 12.032-31.36 0-43.392l-253.44-253.44C491.264 636.288 478.016 634.304 454.464 654.016zM507.968 436.928l252.48-253.376c11.968-11.968 11.968-31.36 0-43.392-11.968-11.968-31.296-11.968-43.264 0L480.704 377.536 244.16 140.16c-11.968-11.968-31.296-11.968-43.264 0-11.968 12.032-11.968 31.424 0 43.392l252.48 253.376C465.984 449.344 491.136 453.824 507.968 436.928z\"  ></path></symbol><symbol id=\"icon-zhankaishangxia\" viewBox=\"0 0 1024 1024\"><path d=\"M454.464 143.68l-253.44 253.44c-12.032 12.032-12.032 31.36 0 43.392 11.968 11.968 31.36 11.968 43.328 0l237.376-237.376 237.376 237.376c11.968 11.968 31.36 11.968 43.328 0 12.032-12.032 12.032-31.36 0-43.392l-253.44-253.44C491.264 126.016 478.016 124.032 454.464 143.68zM507.968 947.648l252.48-253.376c11.968-11.968 11.968-31.36 0-43.392-11.968-11.968-31.296-11.968-43.264 0l-236.48 237.376L244.16 650.88c-11.968-11.968-31.296-11.968-43.264 0-11.968 12.032-11.968 31.424 0 43.392l252.48 253.376C465.984 960 491.136 964.48 507.968 947.648z\"  ></path></symbol><symbol id=\"icon-shujuku\" viewBox=\"0 0 1024 1024\"><path d=\"M870.4 57.6C780.8 19.2 652.8 0 512 0 371.2 0 243.2 19.2 153.6 57.6 51.2 102.4 0 153.6 0 211.2l0 595.2c0 57.6 51.2 115.2 153.6 153.6C243.2 1004.8 371.2 1024 512 1024c140.8 0 268.8-19.2 358.4-57.6 96-38.4 153.6-96 153.6-153.6L1024 211.2C1024 153.6 972.8 102.4 870.4 57.6L870.4 57.6zM812.8 320C729.6 352 614.4 364.8 512 364.8 403.2 364.8 294.4 352 211.2 320 115.2 294.4 70.4 256 70.4 211.2c0-38.4 51.2-76.8 140.8-108.8C294.4 76.8 403.2 64 512 64c102.4 0 217.6 19.2 300.8 44.8 89.6 32 140.8 70.4 140.8 108.8C953.6 256 908.8 294.4 812.8 320L812.8 320zM819.2 505.6C736 531.2 620.8 550.4 512 550.4c-108.8 0-217.6-19.2-307.2-44.8C115.2 473.6 64 435.2 64 396.8L64 326.4C128 352 172.8 384 243.2 396.8 326.4 416 416 428.8 512 428.8c96 0 185.6-12.8 268.8-32C851.2 384 896 352 960 326.4l0 76.8C960 435.2 908.8 473.6 819.2 505.6L819.2 505.6zM819.2 710.4c-83.2 25.6-198.4 44.8-307.2 44.8-108.8 0-217.6-19.2-307.2-44.8C115.2 684.8 64 646.4 64 601.6L64 505.6c64 32 108.8 57.6 179.2 76.8C326.4 601.6 416 614.4 512 614.4c96 0 185.6-12.8 268.8-32C851.2 563.2 896 537.6 960 505.6l0 96C960 646.4 908.8 684.8 819.2 710.4L819.2 710.4zM512 960c-108.8 0-217.6-19.2-307.2-44.8C115.2 889.6 64 851.2 64 812.8l0-96c64 32 108.8 57.6 179.2 76.8 76.8 19.2 172.8 32 262.4 32 96 0 185.6-12.8 268.8-32 76.8-19.2 121.6-44.8 185.6-76.8l0 96c0 38.4-51.2 76.8-140.8 108.8C736 947.2 614.4 960 512 960L512 960zM512 960\"  ></path></symbol><symbol id=\"icon-baocun\" viewBox=\"0 0 1024 1024\"><path d=\"M814.805 128a51.179 51.179 0 0 1 51.179 51.179V844.01a51.179 51.179 0 0 1-51.179 51.157H201.173a51.179 51.179 0 0 1-51.178-51.157V179.179A51.179 51.179 0 0 1 201.173 128h613.654zM329.024 434.837a51.093 51.093 0 0 1-51.179-51.093V179.157h-76.672v664.854h613.76V179.179H738.22v204.48a51.179 51.179 0 0 1-51.179 51.178H329.024z m0-51.093h357.995V179.157H329.024v204.587z m357.91 204.501a25.557 25.557 0 1 1 0.085 51.072H329.024a25.536 25.536 0 1 1 0-51.072h357.91z\"  ></path></symbol><symbol id=\"icon-chaxun\" viewBox=\"0 0 1024 1024\"><path d=\"M896 64H128c-35.296 0-64 28.704-64 64v768c0 35.296 28.704 64 64 64h592a32 32 0 1 0 0-64H128V128h768v592a32 32 0 1 0 64 0V128c0-35.296-28.704-64-64-64z\"  ></path><path d=\"M791.744 746.496A206.752 206.752 0 0 0 832 624c0-114.688-93.312-208-208-208S416 509.312 416 624s93.312 208 208 208a206.752 206.752 0 0 0 122.496-40.256l110.88 110.88a31.904 31.904 0 0 0 45.248 0 31.968 31.968 0 0 0 0-45.248l-110.88-110.88zM480 624c0-79.392 64.608-144 144-144s144 64.608 144 144-64.608 144-144 144-144-64.608-144-144zM800 264a32 32 0 0 0-32-32H256a32 32 0 0 0 0 64h512a32 32 0 0 0 32-32zM256 422.656a32 32 0 0 0 0 64h96a32 32 0 0 0 0-64H256z\"  ></path></symbol><symbol id=\"icon-duigou11\" viewBox=\"0 0 1024 1024\"><path d=\"M60.217477 633.910561c0 0 250.197342 104.557334 374.563838 330.628186 149.378146-279.762705 436.109566-540.713972 521.05012-560.013527 0-115.776863 0-163.394371 0-341.442486-342.237595 226.070852-506.576477 642.342604-506.576477 642.342604l-180.049702-191.614086L60.217477 633.910561z\"  ></path></symbol><symbol id=\"icon-check1\" viewBox=\"0 0 1024 1024\"><path d=\"M425.8526953 826.55769585a38.84331305 38.84331305 0 0 1-25.33951459-9.3706519L124.73319186 581.13118267c-16.3600663-14.00434391-18.27113049-38.61855307-4.26678657-54.97983391 14.00434391-16.35885175 38.61976761-18.27234504 54.9798351-4.26678776L421.71833418 732.68230648l415.37390325-481.50788719c14.06508937-16.30903964 38.69023306-18.12412564 54.99684359-4.05539142 16.30539479 14.06630393 18.12169535 38.68901851 4.05417688 54.99562904l-440.74743601 510.91980169c-7.70864588 8.93692586-18.59429777 13.52323606-29.54312659 13.52323725z\"  ></path></symbol><symbol id=\"icon-gailan\" viewBox=\"0 0 1024 1024\"><path d=\"M128 469.33h341.33V128H128v341.33z m85.33-256H384V384H213.33V213.33zM938.16 300.03L724.4 86.27 509.54 301.12 723.3 514.88l214.86-214.85zM724.4 206.95l93.08 93.08-94.18 94.17-93.08-93.08 94.18-94.17zM128 896h341.33V554.67H128V896z m85.33-256H384v170.67H213.33V640zM554.67 896H896V554.67H554.67V896zM640 640h170.67v170.67H640V640z\"  ></path></symbol><symbol id=\"icon-huaban2\" viewBox=\"0 0 1024 1024\"><path d=\"M405.333333 192v213.333333h-213.333333v-213.333333h213.333333M426.666667 128H170.666667a42.666667 42.666667 0 0 0-42.666667 42.666667v256a42.666667 42.666667 0 0 0 42.666667 42.666666h256a42.666667 42.666667 0 0 0 42.666666-42.666666V170.666667a42.666667 42.666667 0 0 0-42.666666-42.666667zM725.333333 175.786667L848.213333 298.666667 725.333333 421.546667 602.453333 298.666667 725.333333 175.786667m0-72.96a42.666667 42.666667 0 0 0-30.293333 12.8l-152.746667 152.746666a42.666667 42.666667 0 0 0 0 60.586667l152.746667 152.746667a42.666667 42.666667 0 0 0 60.586667 0l152.746666-152.746667a42.666667 42.666667 0 0 0 0-60.586667l-152.746666-152.746666a42.666667 42.666667 0 0 0-30.293334-12.8zM405.333333 618.666667v213.333333h-213.333333v-213.333333h213.333333M426.666667 554.666667H170.666667a42.666667 42.666667 0 0 0-42.666667 42.666666v256a42.666667 42.666667 0 0 0 42.666667 42.666667h256a42.666667 42.666667 0 0 0 42.666666-42.666667v-256a42.666667 42.666667 0 0 0-42.666666-42.666666zM832 618.666667v213.333333h-213.333333v-213.333333h213.333333M853.333333 554.666667h-256a42.666667 42.666667 0 0 0-42.666666 42.666666v256a42.666667 42.666667 0 0 0 42.666666 42.666667h256a42.666667 42.666667 0 0 0 42.666667-42.666667v-256a42.666667 42.666667 0 0 0-42.666667-42.666666z\"  ></path></symbol><symbol id=\"icon-bianji\" viewBox=\"0 0 1024 1024\"><path d=\"M358.165868 554.624796c-0.533143 0.680499-1.066285 1.391696-1.303692 2.251274l-41.104163 150.700257c-2.400676 8.772804 0.059352 18.226107 6.550183 24.892947 4.860704 4.742001 11.261485 7.350408 18.077727 7.350408 2.252297 0 4.504594-0.267083 6.727215-0.860601l149.630902-40.808428c0.23843 0 0.357134 0.207731 0.534166 0.207731 1.718131 0 3.408633-0.62217 4.683672-1.927909l400.113747-400.054395c11.883655-11.897981 18.404162-28.109198 18.404162-45.74281 0-19.989263-8.476045-39.963177-23.324218-54.767348l-37.786605-37.844933c-14.81645-14.848173-34.822087-23.338544-54.797024-23.338544-17.631566 0-33.842783 6.520507-45.75816 18.388812L358.758362 553.232077C358.344946 553.615816 358.462626 554.179658 358.165868 554.624796M862.924953 257.19778l-39.742143 39.71349-64.428382-65.451688 39.180348-39.179324c6.193049-6.222725 18.194384-5.318122 25.308409 1.822508l37.813211 37.845956c3.943822 3.941775 6.195096 9.18622 6.195096 14.372336C867.223862 250.574942 865.710392 254.42769 862.924953 257.19778M429.322487 560.907896l288.712541-288.728914 64.459081 65.49569L494.314711 625.838721 429.322487 560.907896zM376.718409 677.970032l20.863167-76.580143 55.656601 55.657624L376.718409 677.970032z\"  ></path><path d=\"M888.265084 415.735539c-15.144932 0-27.562752 12.313443-27.620058 27.665083l0 372.98283c0 19.559475-15.885805 35.444257-35.475979 35.444257L194.220958 851.827709c-19.559475 0-35.504632-15.883759-35.504632-35.444257L158.716326 207.602222c0-19.575848 15.945157-35.474956 35.504632-35.474956l406.367171 0c15.231913 0 27.592428-12.371772 27.592428-27.606755 0-15.202237-12.360516-27.590382-27.592428-27.590382L190.013123 116.930129c-47.684022 0-86.49291 38.779212-86.49291 86.49291L103.520213 820.59231c0 47.713698 38.808888 86.47756 86.49291 86.47756l639.334083 0c47.715745 0 86.509283-38.763862 86.509283-86.47756L915.856489 443.222567C915.794068 428.048983 903.408993 415.735539 888.265084 415.735539\"  ></path></symbol><symbol id=\"icon-shuaxin1\" viewBox=\"0 0 1024 1024\"><path d=\"M960 416V192l-73.056 73.056a447.712 447.712 0 0 0-373.6-201.088C265.92 63.968 65.312 264.544 65.312 512S265.92 960.032 513.344 960.032a448.064 448.064 0 0 0 415.232-279.488 38.368 38.368 0 1 0-71.136-28.896 371.36 371.36 0 0 1-344.096 231.584C308.32 883.232 142.112 717.024 142.112 512S308.32 140.768 513.344 140.768c132.448 0 251.936 70.08 318.016 179.84L736 416h224z\"  ></path></symbol><symbol id=\"icon-caidan\" viewBox=\"0 0 1024 1024\"><path d=\"M256 475.428571m18.102857 0l731.794286 0q18.102857 0 18.102857 18.102858l0 36.937142q0 18.102857-18.102857 18.102858l-731.794286 0q-18.102857 0-18.102857-18.102858l0-36.937142q0-18.102857 18.102857-18.102858Z\"  ></path><path d=\"M256 146.285714m18.102857 0l731.794286 0q18.102857 0 18.102857 18.102857l0 36.937143q0 18.102857-18.102857 18.102857l-731.794286 0q-18.102857 0-18.102857-18.102857l0-36.937143q0-18.102857 18.102857-18.102857Z\"  ></path><path d=\"M256 804.571429m18.102857 0l731.794286 0q18.102857 0 18.102857 18.102857l0 36.937143q0 18.102857-18.102857 18.102857l-731.794286 0q-18.102857 0-18.102857-18.102857l0-36.937143q0-18.102857 18.102857-18.102857Z\"  ></path><path d=\"M73.142857 182.857143m-73.142857 0a73.142857 73.142857 0 1 0 146.285714 0 73.142857 73.142857 0 1 0-146.285714 0Z\"  ></path><path d=\"M73.142857 512m-73.142857 0a73.142857 73.142857 0 1 0 146.285714 0 73.142857 73.142857 0 1 0-146.285714 0Z\"  ></path><path d=\"M73.142857 841.142857m-73.142857 0a73.142857 73.142857 0 1 0 146.285714 0 73.142857 73.142857 0 1 0-146.285714 0Z\"  ></path></symbol><symbol id=\"icon-biaoge\" viewBox=\"0 0 1024 1024\"><path d=\"M959.825022 384.002258V191.939717C959.825022 121.2479 902.517291 63.940169 831.825474 63.940169H191.939717C121.2479 63.940169 63.940169 121.2479 63.940169 191.939717v639.885757C63.940169 902.517291 121.2479 959.825022 191.939717 959.825022h639.885757c70.691817 0 127.999548-57.307731 127.999548-127.999548V384.002258zM146.66502 146.66502a63.737872 63.737872 0 0 1 45.336109-18.784682h639.997742A63.961844 63.961844 0 0 1 895.884854 192.001129V320.062089H127.880338V192.001129A63.737872 63.737872 0 0 1 146.66502 146.66502z m269.1267 461.308451v-223.971213h192.181751v223.971213h-192.181751z m192.181751 63.940169v223.971214h-192.181751v-223.971214h192.181751z m-256.12192-63.940169H127.880338v-223.971213h223.971213v223.971213z m-205.186531 269.235073a63.466939 63.466939 0 0 1-18.784682-45.209673V671.91364h223.971213v223.971214H192.001129a63.625887 63.625887 0 0 1-45.336109-18.67631z m749.219834-45.209673A63.763159 63.763159 0 0 1 831.998871 895.884854H671.91364v-223.971214h223.971214v160.085231z m0-224.0254h-223.971214v-223.971213h223.971214v223.971213z\"  ></path></symbol><symbol id=\"icon-zhankai\" viewBox=\"0 0 1024 1024\"><path d=\"M70.4 153.6c0-45.9264 37.2736-83.2 83.2-83.2h716.8c45.9264 0 83.2 37.2736 83.2 83.2v716.8c0 45.9264-37.2736 83.2-83.2 83.2H153.6A83.2 83.2 0 0 1 70.4 870.4V153.6z m64 0v716.8c0 10.5984 8.6016 19.2 19.2 19.2h716.8a19.2 19.2 0 0 0 19.2-19.2V153.6A19.2 19.2 0 0 0 870.4 134.4H153.6A19.2 19.2 0 0 0 134.4 153.6z\"  ></path><path d=\"M307.2 544a32 32 0 1 1 0-64h409.6a32 32 0 1 1 0 64H307.2z\"  ></path><path d=\"M480 307.2a32 32 0 1 1 64 0v409.6a32 32 0 1 1-64 0V307.2z\"  ></path></symbol><symbol id=\"icon-shouqi\" viewBox=\"0 0 1024 1024\"><path d=\"M70.4 153.6c0-45.9264 37.2736-83.2 83.2-83.2h716.8c45.9264 0 83.2 37.2736 83.2 83.2v716.8c0 45.9264-37.2736 83.2-83.2 83.2H153.6A83.2 83.2 0 0 1 70.4 870.4V153.6z m64 0v716.8c0 10.5984 8.6016 19.2 19.2 19.2h716.8a19.2 19.2 0 0 0 19.2-19.2V153.6A19.2 19.2 0 0 0 870.4 134.4H153.6A19.2 19.2 0 0 0 134.4 153.6z\"  ></path><path d=\"M307.2 544a32 32 0 1 1 0-64h409.6a32 32 0 1 1 0 64H307.2z\"  ></path></symbol><symbol id=\"icon-zhuti_o\" viewBox=\"0 0 1024 1024\"><path d=\"M507.733333 529.066667l76.8 136.533333 115.2-106.666667 153.6-34.133333L789.333333 384l17.066667-153.6-153.6 17.066667L512 183.466667l-34.133333 153.6-106.666667 115.2 136.533333 76.8z m-46.933333 21.333333l-157.866667-89.6 132.266667-145.066667 42.666667-192 179.2 81.066667 196.266666-25.6-21.333333 196.266667 81.066667 179.2-192 42.666666-145.066667 132.266667-85.333333-149.333333-281.6 281.6-29.866667-29.866667 281.6-281.6z\"  ></path></symbol><symbol id=\"icon-duankailianjie\" viewBox=\"0 0 1024 1024\"><path d=\"M816.952281 614.064456l-45.118858-45.118857L895.99028 447.98866a223.99433 223.99433 0 0 0-316.791981-316.791981l-124.156857 120.956938-45.118858-45.438849 122.556898-122.236906a287.99271 287.99271 0 0 1 407.029697 407.029697zM288.00567 1023.974081a287.99271 287.99271 0 0 1-203.514849-491.507559l122.556898-122.556898 45.118858 45.118858-122.556898 122.556898A223.99433 223.99433 0 0 0 448.00162 895.977321l122.556898-122.556898 45.118858 45.118858-122.556898 122.556898A285.752767 285.752767 0 0 1 288.00567 1023.974081zM137.257486 182.555379L182.50434 137.276525l255.897523 255.897523-45.246855 45.246854zM585.214147 630.544039l45.246854-45.246854 255.897523 255.897522-45.246855 45.246855z\"  ></path></symbol><symbol id=\"icon-xiugai\" viewBox=\"0 0 1024 1024\"><path d=\"M846.8 255L744.5 152.8c-33.1-33.1-87.9-33.1-122.5 0L149.6 625.3c-10.1 10.1-15.8 24.5-15.8 38.9L128 814v4.3c1.4 15.8 7.2 30.2 18.8 40.3 10.1 10.1 24.5 14.4 38.9 14.4h2.9l146.9-5.8c14.4 0 27.3-7.2 37.4-17.3l472.5-472.4c36-34.6 36-89.3 1.4-122.5z m-675 574.3l4.4-124.3 118.3 118.3-122.7 6z m181.1-34.5L204.7 646.7 597 254.4l148.2 148.2-392.3 392.2z m465.8-465.5l-31.5 31.4-148.4-148.2 31.6-31.4c6-6 13.5-9 21-9s15 3 21 9l106.3 106.3c11.9 11.9 11.9 29.9 0 41.9zM907.9 819.8H457.4c-16.6 0-30 13.4-30 30s13.4 30 30 30h450.5c16.6 0 30-13.4 30-30s-13.4-30-30-30zM497.9 769.6c0 16.6 13.4 30 30 30h380c16.6 0 30-13.4 30-30s-13.4-30-30-30h-380c-16.5 0-30 13.4-30 30z\"  ></path></symbol><symbol id=\"icon-delete\" viewBox=\"0 0 1024 1024\"><path d=\"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zM864 256H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z\"  ></path></symbol><symbol id=\"icon-gengduo1\" viewBox=\"0 0 1024 1024\"><path d=\"M746.662019 512c0 51.835575 42.044582 93.865831 93.865831 93.865831 51.851948 0 93.865831-42.029232 93.865831-93.865831 0-51.836599-42.013883-93.865831-93.865831-93.865831C788.706601 418.135192 746.662019 460.163401 746.662019 512z\"  ></path><path d=\"M89.604272 512c0 51.835575 42.043558 93.865831 93.864808 93.865831 51.822272 0 93.865831-42.029232 93.865831-93.865831 0-51.836599-42.043558-93.865831-93.865831-93.865831C131.648854 418.135192 89.604272 460.163401 89.604272 512z\"  ></path><path d=\"M418.132634 512c0 51.835575 42.013883 93.865831 93.866854 93.865831 51.821249 0 93.864808-42.029232 93.864808-93.865831 0-51.836599-42.043558-93.865831-93.864808-93.865831C460.146517 418.135192 418.132634 460.163401 418.132634 512z\"  ></path></symbol><symbol id=\"icon-jianshao\" viewBox=\"0 0 1024 1024\"><path d=\"M908.5625 428.84375H115.4375C69.6875 428.84375 32.28125 466.25 32.28125 512c0 45.75 37.40625 83.15625 83.15625 83.15625h793.125c45.75 0 83.15625-37.40625 83.15625-83.15625 0-45.75-37.40625-83.15625-83.15625-83.15625z\"  ></path></symbol><symbol id=\"icon-jia\" viewBox=\"0 0 1024 1024\"><path d=\"M870.75195313 448.82421875H575.20214844V153.27441406a63.17578125 63.17578125 0 0 0-126.3515625 0v295.54980469H153.30078125a63.17578125 63.17578125 0 0 0 0 126.3515625h295.54980469v295.54980469a63.17578125 63.17578125 0 0 0 126.3515625 0V575.17578125h295.54980469a63.14941406 63.14941406 0 0 0 0-126.3515625z\"  ></path></symbol><symbol id=\"icon-hao\" viewBox=\"0 0 1027 1024\"><path d=\"M853.95951925 475.5066105h-310.08967642v-300.83326917c0-13.88461268-13.88461268-32.39742862-32.39742936-32.39742862-18.51281666 0-32.39742862 18.51281666-32.39742861 37.02563333v300.83326844H178.2417157c-18.51281666-4.62820399-37.02563333 13.88461268-37.02563262 32.39742934s18.51281666 32.39742862 32.39742863 32.39742861h300.83326916v300.83326845c4.62820399 18.51281666 18.51281666 37.02563333 37.02563261 37.02563334s32.39742862-18.51281666 32.39742935-32.39742862v-305.46147317h305.46147243c18.51281666 0 32.39742862-18.51281666 32.39742934-32.39742861s-9.25640796-37.02563333-27.76922535-37.02563333z\"  ></path></symbol><symbol id=\"icon-right\" viewBox=\"0 0 1024 1024\"><path d=\"M677.83 468.75l-263.9-235.83c-18.16-18.15-49.82-18.17-68 0a48 48 0 0 0 0.33 68.29l229.35 202L346 718.31l49.45 86.38L678 537.14l0.14-0.13a48.22 48.22 0 0 0-0.31-68.26z\"  ></path></symbol><symbol id=\"icon-search1\" viewBox=\"0 0 1024 1024\"><path d=\"M938.37 880.82l-184-186.28c53.44-69.13 81.69-154.4 79.64-240.86a377.12 377.12 0 0 0-62.15-199 31.72 31.72 0 1 0-53 34.92 315.75 315.75 0 0 1-53.73 408.79c-67.87 60.74-155.38 88.6-246.48 78.43C274 760.73 157.19 643.94 141 499.17c-10.17-91.07 17.68-178.63 78.42-246.53a315.75 315.75 0 0 1 386-67.39l7.69 4.19 17.64-62.63-5.15-2.58A381.71 381.71 0 0 0 454.84 83.7C245.7 83.7 75.54 253.86 75.54 463a380.32 380.32 0 0 0 123.09 279.46c79.24 72.72 182 107.64 289.59 98.42 81.59-7 157-39.84 218.55-95L888.59 930a35 35 0 1 0 49.78-49.15z\"  ></path></symbol><symbol id=\"icon-download1\" viewBox=\"0 0 1024 1024\"><path d=\"M856.68085106 655.61702128c-15.31914923 0-28.72340426 13.40425502-28.72340425 28.72340425v114.89361702c0 5.74468085-3.82978753 9.57446838-9.57446838 9.57446838H205.61702157c-5.74468085 0-9.57446838-3.82978753-9.57446838-9.57446838v-114.89361702c0-15.31914923-13.40425502-28.72340426-28.72340425-28.72340425s-28.72340426 13.40425502-28.72340426 28.72340425v114.89361702c0 36.38297843 30.63829758 67.02127689 67.02127689 67.0212769h612.76595686c36.38297843 0 67.02127689-30.63829758 67.02127689-67.0212769v-114.89361702c0-15.31914923-13.40425502-28.72340426-28.72340426-28.72340425z\"  ></path><path d=\"M489.53191458 718.29787266c6.12765957 6.12765957 14.29787202 8.17021245 22.46808542 8.17021245s16.34042585-2.04255287 22.46808542-8.17021245l204.25531884-204.25531979c12.25531915-12.25531915 12.25531915-30.63829787 0-42.89361702-12.25531915-12.25531915-30.63829787-12.25531915-42.89361703 0l-151.14893648 151.14893649V185.19148968c0-16.34042585-14.29787202-30.63829787-30.63829788-30.63829787s-32.68085075 14.29787202-32.68085074 30.63829787v437.10638266L330.21276564 471.14893585c-12.25531915-12.25531915-30.63829787-12.25531915-42.89361702 0-12.25531915 12.25531915-12.25531915 30.63829787 0 42.89361702l202.21276596 204.25531979z\"  ></path></symbol><symbol id=\"icon-xiangyoujiantou\" viewBox=\"0 0 1024 1024\"><path d=\"M867.88 483.41L655.09 270.64c-16.81-16.8-45.37-17.86-60.74 0.26a39.82 39.82 0 0 0 2.25 53.88l144.65 144.64a0.61 0.61 0 0 1-0.43 1H166.45l-22.16 79.69h600a0.6 0.6 0 0 1 0.43 1L596.6 699.23a39.82 39.82 0 0 0-2.25 53.88c15.37 18.12 43.94 17.06 60.74 0.26l212.79-212.79a40.42 40.42 0 0 0 0-57.17z\"  ></path></symbol><symbol id=\"icon-shanchuxianxing\" viewBox=\"0 0 1024 1024\"><path d=\"M775.38417778 844.03768889a57.30986667 57.30986667 0 0 1-57.18471111 57.26435556H305.78915555a57.4464 57.4464 0 0 1-57.26435555-57.26435556V236.52124445h526.85937778v607.57333333zM362.95111111 133.75715555a11.62808889 11.62808889 0 0 1 11.78737778-11.776h275.11466666a11.55982222 11.55982222 0 0 1 11.69635556 11.776v34.53155556H362.95111111z m584.03271111 34.53155556h-217.088v-34.53155556a80.1792 80.1792 0 0 0-80.01991111-80.00853333h-275.11466666a80.1792 80.1792 0 0 0-80.01991112 80.00853333v34.53155556H76.89102222a34.48604445 34.48604445 0 1 0 0 68.97208889h102.66168889v606.77688889a126.29333333 126.29333333 0 0 0 126.29333334 126.23644444h412.2624a126.29333333 126.29333333 0 0 0 126.29333333-126.23644444V236.52124445h102.75271111a34.48604445 34.48604445 0 0 0 34.53155556-34.53155556c-0.05688889-19.1488-15.53066667-33.70097778-34.67946667-33.70097778zM512 797.80977778a34.47466667 34.47466667 0 0 0 34.52017778-34.52017778V396.45866667a34.53155555 34.53155555 0 1 0-69.05173333 0v366.83093333A34.54293333 34.54293333 0 0 0 512 797.80977778m-160.67697778 0a34.48604445 34.48604445 0 0 0 34.53155556-34.52017778V396.45866667a34.53155555 34.53155555 0 1 0-69.06311111 0v366.83093333c0.73955555 19.05777778 16.21333333 34.52017778 34.53155555 34.52017778m321.34257778 0a34.48604445 34.48604445 0 0 0 34.53155555-34.52017778V396.45866667a34.53155555 34.53155555 0 1 0-69.05173333 0v366.83093333a34.58844445 34.58844445 0 0 0 34.52017778 34.52017778\"  ></path></symbol><symbol id=\"icon-cross-copy\" viewBox=\"0 0 1024 1024\"><path d=\"M220.4 138.20000029L512 429.80000029l290.40000029-290.40000029A55.19999971 55.19999971 0 0 1 842.00000029 122.00000029a60.00000029 60.00000029 0 0 1 59.99999942 59.99999942 54 54 0 0 1-16.2 39.6L592.39999971 512l293.4 293.4A54 54 0 0 1 901.99999971 842.00000029a60.00000029 60.00000029 0 0 1-59.99999942 59.99999942 55.19999971 55.19999971 0 0 1-41.4-16.2L512 594.19999971l-290.99999971 291.00000058A55.19999971 55.19999971 0 0 1 181.99999971 901.99999971a60.00000029 60.00000029 0 0 1-59.99999942-59.99999942 54 54 0 0 1 16.2-39.6L431.60000029 512 138.20000029 218.6A54 54 0 0 1 122.00000029 181.99999971a60.00000029 60.00000029 0 0 1 59.99999942-59.99999942c14.4 0.18 28.19999971 6.00000029 38.40000029 16.2z\"  ></path></symbol><symbol id=\"icon-shuaxin\" viewBox=\"0 0 1024 1024\"><path d=\"M1017.576727 326.493091l-87.226182 151.272727c0.256 3.677091 0.558545 7.284364 0.558546 10.961455h-6.842182a46.545455 46.545455 0 0 1-28.392727 21.736727h-0.395637a46.289455 46.289455 0 0 1-8.424727 1.419636h-2.327273a29.137455 29.137455 0 0 1-11.264-1.210181c-1.466182-0.325818-2.955636-0.512-4.398545-1.000728a46.685091 46.685091 0 0 1-8.634182-3.886545l-161.908363-93.626182a46.801455 46.801455 0 0 1 46.731636-81.082182l70.842182 40.96A325.352727 325.352727 0 0 0 217.949091 349.090909H209.454545a46.545455 46.545455 0 1 1-78.08-34.048 417.861818 417.861818 0 0 1 771.048728 23.994182l34.210909-59.345455a46.754909 46.754909 0 1 1 80.942545 46.801455z m-124.951272 382.464a417.838545 417.838545 0 0 1-771.048728-23.994182l-34.210909 59.345455A46.778182 46.778182 0 1 1 6.4 697.483636l87.249455-151.272727C93.393455 542.557091 93.090909 538.949818 93.090909 535.272727h6.842182a46.545455 46.545455 0 0 1 28.369454-21.736727h0.395637A47.104 47.104 0 0 1 137.146182 512h2.327273a29.137455 29.137455 0 0 1 11.264 1.210182c1.466182 0.325818 2.955636 0.512 4.398545 1.000727a46.685091 46.685091 0 0 1 8.634182 3.886546l161.908363 93.626181a46.801455 46.801455 0 0 1-46.731636 81.082182l-70.842182-40.96A325.352727 325.352727 0 0 0 806.050909 674.909091H814.545455a46.545455 46.545455 0 1 1 78.08 34.048z\"  ></path></symbol><symbol id=\"icon-tixing\" viewBox=\"0 0 1024 1024\"><path d=\"M511.40266667 846.016c-96.21333333 0-192.42666667-0.49066667-288.62933334 0.27733333-40.352 0.32-75.33866667-8.448-95.44533333-46.93333333-19.2-36.81066667-11.12533333-71.86133333 14.63466667-101.33333333 50.13333333-57.39733333 64.192-122.784 59.18933333-197.728-4.26666667-63.14666667-4.01066667-128.07466667 5.78133333-190.4C231.09333333 156.01066667 375.92533333 43.47733333 527.06133333 54.31466667c164.384 11.73333333 290.05866667 135.89333333 296.75733334 296.24533333 3.04 72.68266667-1.06666667 145.67466667 2.02666666 218.34666667 1.06666667 25.41866667 11.57333333 51.584 22.656 75.104 10.95466667 23.25333333 28.8 43.15733333 42.336 65.312 40.29866667 65.87733333 3.2 134.83733333-75.21066666 136.224-101.376 1.80266667-202.816 0.448-304.224 0.46933333zM398.18666667 889.06666667H625.06666667c-18.816 50.944-60.896 80.90666667-112 81.35466666-49.824 0.43733333-92.032-28.50133333-114.88-81.35466666z\"  ></path></symbol><symbol id=\"icon-shezhixitongshezhigongnengshezhishuxing\" viewBox=\"0 0 1024 1024\"><path d=\"M981.01333333 423.25333333c-0.21333333-1.92-0.42666667-3.52-0.74666666-4.48v-0.85333333l-0.42666667-2.34666667c-7.57333333-36.90666667-32-60.69333333-62.4-60.69333333h-5.01333333c-51.84 0-93.86666667-42.24-93.86666667-93.86666667 0-11.94666667 5.54666667-28.90666667 7.89333333-34.88 14.72-34.34666667-0.96-73.49333333-37.33333333-93.44L674.66666667 67.94666667l-2.13333334-0.74666667c-8.53333333-2.77333333-18.45333333-6.08-29.54666666-6.08-20.69333333 0-43.94666667 9.6-58.34666667 24-18.02666667 17.81333333-54.61333333 44.37333333-76.37333333 44.37333333-21.65333333 0-58.34666667-26.45333333-76.37333334-44.37333333-15.36-15.14666667-36.58666667-24-58.34666666-24-11.41333333 0-21.01333333 3.2-29.54666667 6.08l-1.92 0.74666667-120 64.96-0.74666667 0.42666666c-29.12 18.24-40.96 60.05333333-26.13333333 93.22666667l0.21333333 0.42666667 0.21333334 0.42666666c2.34666667 5.22666667 9.6 22.93333333 9.6 38.4 0 51.84-42.24 93.86666667-93.86666667 93.86666667h-5.01333333c-31.78666667 0-55.57333333 23.46666667-62.4 61.12l-0.42666667 2.13333333v0.74666667c0 1.06666667-0.42666667 2.56-0.74666667 4.48-2.66666667 16.10666667-9.06666667 54.08-9.06666666 85.12 0 31.04 6.29333333 69.01333333 9.06666666 85.12 0.21333333 1.92 0.42666667 3.52 0.74666667 4.48v0.85333333l0.42666667 2.34666667c7.57333333 36.90666667 32 60.69333333 62.4 60.69333333h2.56c51.84 0 93.86666667 42.24 93.86666666 93.86666667 0 11.94666667-5.54666667 28.90666667-7.89333333 34.88-14.18666667 32.32-0.74666667 73.81333333 30.61333333 94.72l0.85333334 0.42666667 113.06666666 62.93333333 2.13333334 0.74666667c8.53333333 2.77333333 18.24 6.08 29.33333333 6.08 23.68 0 45.01333333-9.06666667 58.34666667-24 1.28-0.96 2.56-2.24 4.05333333-3.52 13.65333333-11.94666667 50.34666667-43.52 74.56-43.52 18.02666667 0 48.21333333 18.88 78.61333333 49.28 15.36 15.14666667 36.58666667 24 58.34666667 24 14.72 0 25.6-4.05333333 37.97333333-10.13333334l0.42666667-0.21333333 115.94666667-64.10666667 0.42666666-0.42666666c29.12-18.24 40.96-60.05333333 26.13333334-93.22666667l-0.21333334-0.42666667-0.21333333-0.42666666c-0.21333333-0.10666667-9.28-18.98666667-7.57333333-35.94666667l0.21333333-1.06666667v-1.06666666c0-51.84 42.24-93.86666667 93.86666667-93.86666667h5.33333333c31.78666667 0 55.57333333-23.46666667 62.4-61.12l0.42666667-2.13333333v-0.74666667c0.21333333-0.85333333 0.42666667-2.13333333 0.74666666-3.84 2.77333333-15.68 9.17333333-52.26666667 9.17333334-85.76 0.21333333-30.93333333-6.08-68.8-8.85333334-84.90666667z m-469.33333333 237.22666667c-82.02666667 0-148.48-66.45333333-148.48-148.48s66.45333333-148.48 148.48-148.48S660.16 429.97333333 660.16 512s-66.45333333 148.48-148.48 148.48z\"  ></path></symbol><symbol id=\"icon-zhihangsqljiaoben\" viewBox=\"0 0 1024 1024\"><path d=\"M224 194.95h56v56h-56zM320.26 194.95h56v56h-56zM415.74 194.95h56v56h-56z\"  ></path><path d=\"M888 64H136a72 72 0 0 0-72 72v752a72 72 0 0 0 72 72h752a72 72 0 0 0 72-72V136a72 72 0 0 0-72-72z m-752 72h752v169.85H136V136z m752 752H136V377.85h752V888z\"  ></path><path d=\"M618.7 566.21l101.81 101.82-101.82 101.83 39.59 39.6 101.83-101.83h0.01l39.59-39.6-141.42-141.42-39.59 39.6zM404.39 566.21l-39.59-39.6-141.42 141.42 39.59 39.6h0.01l101.83 101.83 39.59-39.6-101.82-101.83 101.81-101.82z\"  ></path><path d=\"M434.106 818.885l98.35-367.052 54.093 14.494-98.352 367.052z\"  ></path></symbol><symbol id=\"icon-xunishujukuguanli\" viewBox=\"0 0 1024 1024\"><path d=\"M721 633c50.18 0 92.57 42.39 92.57 92.57v29.35h29.35a51 51 0 0 1 37.59 17.29c4.9 5.55 12.83 17.2 10.87 32.84l-0.23 1.81v1.83c0 23.23-10.54 38.23-19.38 46.71a66.19 66.19 0 0 1-45.48 18.15H510.37a70.4 70.4 0 1 1 0-140.8h22.14l6.08-21.29c7.72-27 29.79-45.21 54.9-45.21 9.73 0 15.47 0.11 20.13 2.44l16.8 8.39 14.66-11.73C671.17 644.5 698.12 633 721 633m0-29.35c-33.25 0-66.5 16.63-94.21 38.79-11.09-5.54-22.17-5.54-33.25-5.54-38.79 0-72 27.71-83.13 66.5a99.75 99.75 0 1 0 0 199.51h315.85c49.88 0 94.21-38.79 94.21-94.21 5.54-44.34-33.25-83.13-77.59-83.13 0-66.51-55.42-121.92-121.92-121.92zM868.08 362.45H143.86a30.32 30.32 0 0 1-30.28-30.28V152.5a30.32 30.32 0 0 1 30.28-30.28h724.22a30.32 30.32 0 0 1 30.28 30.28v179.67a30.32 30.32 0 0 1-30.28 30.28z m-695.8-58.71h667.38V180.93H172.29z\"  ></path><path d=\"M854.34 631.1h-38v-58.71h23.29V449.58H172.29v122.81h330.05v58.71H157.61a44.08 44.08 0 0 1-44-44V434.9a44.08 44.08 0 0 1 44-44h696.73a44.08 44.08 0 0 1 44 44v152.17a44.08 44.08 0 0 1-44 44.03zM235.81 212.98h50.37v58.71h-50.37z\"  ></path><path d=\"M235.81 481.63h50.37v58.71h-50.37zM397 899.75H157.61a44.08 44.08 0 0 1-44-44v-152.2a44.08 44.08 0 0 1 44-44h234.63v58.71h-220V841H397z\"  ></path><path d=\"M235.81 750.28h50.37v58.71h-50.37z\"  ></path></symbol></svg>',function(h){var a=(a=document.getElementsByTagName(\"script\"))[a.length-1],c=a.getAttribute(\"data-injectcss\"),a=a.getAttribute(\"data-disable-injectsvg\");if(!a){var l,t,i,o,v,z=function(a,c){c.parentNode.insertBefore(a,c)};if(c&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write(\"<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>\")}catch(a){console&&console.log(a)}}l=function(){var a,c=document.createElement(\"div\");c.innerHTML=h._iconfont_svg_string_3633546,(c=c.getElementsByTagName(\"svg\")[0])&&(c.setAttribute(\"aria-hidden\",\"true\"),c.style.position=\"absolute\",c.style.width=0,c.style.height=0,c.style.overflow=\"hidden\",c=c,(a=document.body).firstChild?z(c,a.firstChild):a.appendChild(c))},document.addEventListener?~[\"complete\",\"loaded\",\"interactive\"].indexOf(document.readyState)?setTimeout(l,0):(t=function(){document.removeEventListener(\"DOMContentLoaded\",t,!1),l()},document.addEventListener(\"DOMContentLoaded\",t,!1)):document.attachEvent&&(i=l,o=h.document,v=!1,s(),o.onreadystatechange=function(){\"complete\"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){v||(v=!0,i())}function s(){try{o.documentElement.doScroll(\"left\")}catch(a){return void setTimeout(s,50)}p()}}(window);"
  },
  {
    "path": "chat2db-client/src/assets/font/iconfont.json",
    "content": "{\n  \"id\": \"3633546\",\n  \"name\": \"chat2db\",\n  \"font_family\": \"iconfont\",\n  \"css_prefix_text\": \"icon-\",\n  \"description\": \"\",\n  \"glyphs\": [\n    {\n      \"icon_id\": \"38884431\",\n      \"name\": \"right_on_5\",\n      \"font_class\": \"right_on_5\",\n      \"unicode\": \"e672\",\n      \"unicode_decimal\": 58994\n    },\n    {\n      \"icon_id\": \"38884432\",\n      \"name\": \"right_off_5-01\",\n      \"font_class\": \"right_off_5-01\",\n      \"unicode\": \"e673\",\n      \"unicode_decimal\": 58995\n    },\n    {\n      \"icon_id\": \"38883914\",\n      \"name\": \"left_on_2\",\n      \"font_class\": \"a-left_on_huaban11\",\n      \"unicode\": \"e674\",\n      \"unicode_decimal\": 58996\n    },\n    {\n      \"icon_id\": \"38882655\",\n      \"name\": \"left_off\",\n      \"font_class\": \"a-left_off_huaban1\",\n      \"unicode\": \"e670\",\n      \"unicode_decimal\": 58992\n    },\n    {\n      \"icon_id\": \"38873585\",\n      \"name\": \"minimize21\",\n      \"font_class\": \"minimize21\",\n      \"unicode\": \"e671\",\n      \"unicode_decimal\": 58993\n    },\n    {\n      \"icon_id\": \"38872025\",\n      \"name\": \"restore\",\n      \"font_class\": \"restore_button2\",\n      \"unicode\": \"e66b\",\n      \"unicode_decimal\": 58987\n    },\n    {\n      \"icon_id\": \"38872027\",\n      \"name\": \"resize\",\n      \"font_class\": \"resize_button2\",\n      \"unicode\": \"e66e\",\n      \"unicode_decimal\": 58990\n    },\n    {\n      \"icon_id\": \"38872028\",\n      \"name\": \"close\",\n      \"font_class\": \"close_button2\",\n      \"unicode\": \"e66f\",\n      \"unicode_decimal\": 58991\n    },\n    {\n      \"icon_id\": \"8721198\",\n      \"name\": \"筛选\",\n      \"font_class\": \"shaixuan\",\n      \"unicode\": \"e66a\",\n      \"unicode_decimal\": 58986\n    },\n    {\n      \"icon_id\": \"38644725\",\n      \"name\": \"排序\",\n      \"font_class\": \"a-44tubiao-122\",\n      \"unicode\": \"e69a\",\n      \"unicode_decimal\": 59034\n    },\n    {\n      \"icon_id\": \"1727525\",\n      \"name\": \"305信息-线性圆框\",\n      \"font_class\": \"xinxi-xianxingyuankuang\",\n      \"unicode\": \"e8e8\",\n      \"unicode_decimal\": 59624\n    },\n    {\n      \"icon_id\": \"577312\",\n      \"name\": \"加号\",\n      \"font_class\": \"jiahao\",\n      \"unicode\": \"e726\",\n      \"unicode_decimal\": 59174\n    },\n    {\n      \"icon_id\": \"5961366\",\n      \"name\": \"列表\",\n      \"font_class\": \"liebiao\",\n      \"unicode\": \"ec6b\",\n      \"unicode_decimal\": 60523\n    },\n    {\n      \"icon_id\": \"6318169\",\n      \"name\": \"减去\",\n      \"font_class\": \"jianqu\",\n      \"unicode\": \"e65d\",\n      \"unicode_decimal\": 58973\n    },\n    {\n      \"icon_id\": \"5729483\",\n      \"name\": \"database\",\n      \"font_class\": \"database\",\n      \"unicode\": \"e669\",\n      \"unicode_decimal\": 58985\n    },\n    {\n      \"icon_id\": \"2076240\",\n      \"name\": \"筛选\",\n      \"font_class\": \"shaixuan1\",\n      \"unicode\": \"e888\",\n      \"unicode_decimal\": 59528\n    },\n    {\n      \"icon_id\": \"2787037\",\n      \"name\": \"刷新\",\n      \"font_class\": \"shuaxin2\",\n      \"unicode\": \"e668\",\n      \"unicode_decimal\": 58984\n    },\n    {\n      \"icon_id\": \"5387866\",\n      \"name\": \"加号_o\",\n      \"font_class\": \"jiahao_o\",\n      \"unicode\": \"eb78\",\n      \"unicode_decimal\": 60280\n    },\n    {\n      \"icon_id\": \"10594610\",\n      \"name\": \"数据库_jurassic\",\n      \"font_class\": \"jurassic_data\",\n      \"unicode\": \"e6a9\",\n      \"unicode_decimal\": 59049\n    },\n    {\n      \"icon_id\": \"12515461\",\n      \"name\": \"权限\",\n      \"font_class\": \"quanxian\",\n      \"unicode\": \"e667\",\n      \"unicode_decimal\": 58983\n    },\n    {\n      \"icon_id\": \"10573069\",\n      \"name\": \"sharpicons_add-database\",\n      \"font_class\": \"sharpicons_add-database\",\n      \"unicode\": \"e816\",\n      \"unicode_decimal\": 59414\n    },\n    {\n      \"icon_id\": \"6789097\",\n      \"name\": \"组织管理\",\n      \"font_class\": \"zuzhiguanli-\",\n      \"unicode\": \"e663\",\n      \"unicode_decimal\": 58979\n    },\n    {\n      \"icon_id\": \"15568801\",\n      \"name\": \"空间\",\n      \"font_class\": \"moxing-miaobian\",\n      \"unicode\": \"e691\",\n      \"unicode_decimal\": 59025\n    },\n    {\n      \"icon_id\": \"37968169\",\n      \"name\": \"下箭头-copy\",\n      \"font_class\": \"xiajiantou1-copy\",\n      \"unicode\": \"100be\",\n      \"unicode_decimal\": 65726\n    },\n    {\n      \"icon_id\": \"22941156\",\n      \"name\": \"查看\",\n      \"font_class\": \"chakan2\",\n      \"unicode\": \"e788\",\n      \"unicode_decimal\": 59272\n    },\n    {\n      \"icon_id\": \"7735962\",\n      \"name\": \"clone\",\n      \"font_class\": \"clone\",\n      \"unicode\": \"e8db\",\n      \"unicode_decimal\": 59611\n    },\n    {\n      \"icon_id\": \"15488863\",\n      \"name\": \"提交\",\n      \"font_class\": \"tijiao\",\n      \"unicode\": \"e687\",\n      \"unicode_decimal\": 59015\n    },\n    {\n      \"icon_id\": \"12331656\",\n      \"name\": \"查看\",\n      \"font_class\": \"chakan1\",\n      \"unicode\": \"e665\",\n      \"unicode_decimal\": 58981\n    },\n    {\n      \"icon_id\": \"5993150\",\n      \"name\": \"复制\",\n      \"font_class\": \"fuzhi\",\n      \"unicode\": \"ec7a\",\n      \"unicode_decimal\": 60538\n    },\n    {\n      \"icon_id\": \"1624922\",\n      \"name\": \"icon_answer\",\n      \"font_class\": \"icon_answer\",\n      \"unicode\": \"e686\",\n      \"unicode_decimal\": 59014\n    },\n    {\n      \"icon_id\": \"1625000\",\n      \"name\": \"icon_question\",\n      \"font_class\": \"icon_question\",\n      \"unicode\": \"e6a8\",\n      \"unicode_decimal\": 59048\n    },\n    {\n      \"icon_id\": \"29290564\",\n      \"name\": \"发送\",\n      \"font_class\": \"fasong\",\n      \"unicode\": \"100bd\",\n      \"unicode_decimal\": 65725\n    },\n    {\n      \"icon_id\": \"8765142\",\n      \"name\": \"重启\",\n      \"font_class\": \"zhongqi\",\n      \"unicode\": \"e662\",\n      \"unicode_decimal\": 58978\n    },\n    {\n      \"icon_id\": \"673801\",\n      \"name\": \"提醒\",\n      \"font_class\": \"tixing2\",\n      \"unicode\": \"e6cc\",\n      \"unicode_decimal\": 59084\n    },\n    {\n      \"icon_id\": \"13646873\",\n      \"name\": \"提醒\",\n      \"font_class\": \"tixing3\",\n      \"unicode\": \"e661\",\n      \"unicode_decimal\": 58977\n    },\n    {\n      \"icon_id\": \"3457210\",\n      \"name\": \"提醒\",\n      \"font_class\": \"tixing1\",\n      \"unicode\": \"e716\",\n      \"unicode_decimal\": 59158\n    },\n    {\n      \"icon_id\": \"3814162\",\n      \"name\": \"升级\",\n      \"font_class\": \"shengji\",\n      \"unicode\": \"e69c\",\n      \"unicode_decimal\": 59036\n    },\n    {\n      \"icon_id\": \"3141572\",\n      \"name\": \"全局_升级\",\n      \"font_class\": \"quanju_shengji\",\n      \"unicode\": \"e659\",\n      \"unicode_decimal\": 58969\n    },\n    {\n      \"icon_id\": \"4171088\",\n      \"name\": \"关于我们\",\n      \"font_class\": \"guanyuwomen1\",\n      \"unicode\": \"e65c\",\n      \"unicode_decimal\": 58972\n    },\n    {\n      \"icon_id\": \"1285185\",\n      \"name\": \"ico版本更新\",\n      \"font_class\": \"icobanbengengxin\",\n      \"unicode\": \"e67d\",\n      \"unicode_decimal\": 59005\n    },\n    {\n      \"icon_id\": \"5203239\",\n      \"name\": \"对话气泡\",\n      \"font_class\": \"duihuaqipao\",\n      \"unicode\": \"e657\",\n      \"unicode_decimal\": 58967\n    },\n    {\n      \"icon_id\": \"12694416\",\n      \"name\": \"角色权限\",\n      \"font_class\": \"jiaosequanxian\",\n      \"unicode\": \"e658\",\n      \"unicode_decimal\": 58968\n    },\n    {\n      \"icon_id\": \"1462912\",\n      \"name\": \"preview\",\n      \"font_class\": \"preview1\",\n      \"unicode\": \"e654\",\n      \"unicode_decimal\": 58964\n    },\n    {\n      \"icon_id\": \"11399566\",\n      \"name\": \"导入\",\n      \"font_class\": \"daoru\",\n      \"unicode\": \"e653\",\n      \"unicode_decimal\": 58963\n    },\n    {\n      \"icon_id\": \"10933419\",\n      \"name\": \"终止\",\n      \"font_class\": \"zhongzhi\",\n      \"unicode\": \"e652\",\n      \"unicode_decimal\": 58962\n    },\n    {\n      \"icon_id\": \"29570629\",\n      \"name\": \"退出\",\n      \"font_class\": \"tuichu\",\n      \"unicode\": \"e6b2\",\n      \"unicode_decimal\": 59058\n    },\n    {\n      \"icon_id\": \"25071396\",\n      \"name\": \"控桩终端\",\n      \"font_class\": \"kongzhuangzhongduan\",\n      \"unicode\": \"e6bb\",\n      \"unicode_decimal\": 59067\n    },\n    {\n      \"icon_id\": \"4890374\",\n      \"name\": \"撤销\",\n      \"font_class\": \"chexiao1\",\n      \"unicode\": \"e6e2\",\n      \"unicode_decimal\": 59106\n    },\n    {\n      \"icon_id\": \"1204\",\n      \"name\": \"向上\",\n      \"font_class\": \"xiangshang\",\n      \"unicode\": \"e650\",\n      \"unicode_decimal\": 58960\n    },\n    {\n      \"icon_id\": \"5793838\",\n      \"name\": \"查看\",\n      \"font_class\": \"chakan-copy\",\n      \"unicode\": \"e651\",\n      \"unicode_decimal\": 58961\n    },\n    {\n      \"icon_id\": \"10792673\",\n      \"name\": \"编辑数据_编辑录入操作_jurassic\",\n      \"font_class\": \"jurassic_edit-data\",\n      \"unicode\": \"e6f2\",\n      \"unicode_decimal\": 59122\n    },\n    {\n      \"icon_id\": \"10792678\",\n      \"name\": \"编辑表格_编辑录入操作_jurassic\",\n      \"font_class\": \"jurassic_edit-table\",\n      \"unicode\": \"e6f3\",\n      \"unicode_decimal\": 59123\n    },\n    {\n      \"icon_id\": \"10465899\",\n      \"name\": \"报表数据录入\",\n      \"font_class\": \"baobiaoshujuluru\",\n      \"unicode\": \"e7b5\",\n      \"unicode_decimal\": 59317\n    },\n    {\n      \"icon_id\": \"1004675\",\n      \"name\": \"播放5\",\n      \"font_class\": \"bofang5\",\n      \"unicode\": \"e656\",\n      \"unicode_decimal\": 58966\n    },\n    {\n      \"icon_id\": \"34080274\",\n      \"name\": \"清空@3x\",\n      \"font_class\": \"a-qingkong3x\",\n      \"unicode\": \"e64f\",\n      \"unicode_decimal\": 58959\n    },\n    {\n      \"icon_id\": \"4880413\",\n      \"name\": \"删除\",\n      \"font_class\": \"shanchu\",\n      \"unicode\": \"e64e\",\n      \"unicode_decimal\": 58958\n    },\n    {\n      \"icon_id\": \"586995\",\n      \"name\": \"new-document-worksheet\",\n      \"font_class\": \"newdocumentworksheet\",\n      \"unicode\": \"e792\",\n      \"unicode_decimal\": 59282\n    },\n    {\n      \"icon_id\": \"4766469\",\n      \"name\": \"file-excel\",\n      \"font_class\": \"file-excel\",\n      \"unicode\": \"e7b7\",\n      \"unicode_decimal\": 59319\n    },\n    {\n      \"icon_id\": \"4766474\",\n      \"name\": \"file-markdown\",\n      \"font_class\": \"file-markdown\",\n      \"unicode\": \"e7b8\",\n      \"unicode_decimal\": 59320\n    },\n    {\n      \"icon_id\": \"4766477\",\n      \"name\": \"file-word\",\n      \"font_class\": \"file-word\",\n      \"unicode\": \"e7ba\",\n      \"unicode_decimal\": 59322\n    },\n    {\n      \"icon_id\": \"4936958\",\n      \"name\": \"HTML5\",\n      \"font_class\": \"HTML\",\n      \"unicode\": \"e87d\",\n      \"unicode_decimal\": 59517\n    },\n    {\n      \"icon_id\": \"12904096\",\n      \"name\": \"HTML\",\n      \"font_class\": \"HTML1\",\n      \"unicode\": \"e64d\",\n      \"unicode_decimal\": 58957\n    },\n    {\n      \"icon_id\": \"15838516\",\n      \"name\": \"pdf\",\n      \"font_class\": \"pdf\",\n      \"unicode\": \"e67a\",\n      \"unicode_decimal\": 59002\n    },\n    {\n      \"icon_id\": \"37118827\",\n      \"name\": \"个人用户\",\n      \"font_class\": \"gerenyonghu\",\n      \"unicode\": \"e64c\",\n      \"unicode_decimal\": 58956\n    },\n    {\n      \"icon_id\": \"37118908\",\n      \"name\": \"后台管理\",\n      \"font_class\": \"houtaiguanli\",\n      \"unicode\": \"e64b\",\n      \"unicode_decimal\": 58955\n    },\n    {\n      \"icon_id\": \"6337464\",\n      \"name\": \"字体代码\",\n      \"font_class\": \"zitidaima\",\n      \"unicode\": \"ec83\",\n      \"unicode_decimal\": 60547\n    },\n    {\n      \"icon_id\": \"27193693\",\n      \"name\": \"版本\",\n      \"font_class\": \"banben\",\n      \"unicode\": \"e70c\",\n      \"unicode_decimal\": 59148\n    },\n    {\n      \"icon_id\": \"10021618\",\n      \"name\": \"车位管理\",\n      \"font_class\": \"cheweiguanli\",\n      \"unicode\": \"e73c\",\n      \"unicode_decimal\": 59196\n    },\n    {\n      \"icon_id\": \"31775147\",\n      \"name\": \"dictate\",\n      \"font_class\": \"dianzhelidaochu\",\n      \"unicode\": \"e64a\",\n      \"unicode_decimal\": 58954\n    },\n    {\n      \"icon_id\": \"33841930\",\n      \"name\": \"circle-f\",\n      \"font_class\": \"circle-f\",\n      \"unicode\": \"e76a\",\n      \"unicode_decimal\": 59242\n    },\n    {\n      \"icon_id\": \"2966901\",\n      \"name\": \"图表-函数\",\n      \"font_class\": \"tubiao-hanshu\",\n      \"unicode\": \"e6fd\",\n      \"unicode_decimal\": 59133\n    },\n    {\n      \"icon_id\": \"11121428\",\n      \"name\": \"视图管理器\",\n      \"font_class\": \"shituguanliqi\",\n      \"unicode\": \"e647\",\n      \"unicode_decimal\": 58951\n    },\n    {\n      \"icon_id\": \"12685114\",\n      \"name\": \"回车\",\n      \"font_class\": \"huiche\",\n      \"unicode\": \"e643\",\n      \"unicode_decimal\": 58947\n    },\n    {\n      \"icon_id\": \"36566502\",\n      \"name\": \"缺省\",\n      \"font_class\": \"quesheng\",\n      \"unicode\": \"e642\",\n      \"unicode_decimal\": 58946\n    },\n    {\n      \"icon_id\": \"2076256\",\n      \"name\": \"进入箭头\",\n      \"font_class\": \"jinrujiantou\",\n      \"unicode\": \"e88e\",\n      \"unicode_decimal\": 59534\n    },\n    {\n      \"icon_id\": \"12754142\",\n      \"name\": \"右箭头\",\n      \"font_class\": \"youjiantou_huaban\",\n      \"unicode\": \"e641\",\n      \"unicode_decimal\": 58945\n    },\n    {\n      \"icon_id\": \"630094\",\n      \"name\": \"向右箭头\",\n      \"font_class\": \"xiangyoujiantou1\",\n      \"unicode\": \"e660\",\n      \"unicode_decimal\": 58976\n    },\n    {\n      \"icon_id\": \"11520190\",\n      \"name\": \"数据源\",\n      \"font_class\": \"shujuyuan\",\n      \"unicode\": \"e640\",\n      \"unicode_decimal\": 58944\n    },\n    {\n      \"icon_id\": \"11891865\",\n      \"name\": \"question\",\n      \"font_class\": \"question\",\n      \"unicode\": \"e67c\",\n      \"unicode_decimal\": 59004\n    },\n    {\n      \"icon_id\": \"32077818\",\n      \"name\": \"星星-copy\",\n      \"font_class\": \"xingxing\",\n      \"unicode\": \"e63a\",\n      \"unicode_decimal\": 58938\n    },\n    {\n      \"icon_id\": \"6560687\",\n      \"name\": \"控制台\",\n      \"font_class\": \"kongzhitai\",\n      \"unicode\": \"e69f\",\n      \"unicode_decimal\": 59039\n    },\n    {\n      \"icon_id\": \"22785129\",\n      \"name\": \"星系\",\n      \"font_class\": \"xingxi\",\n      \"unicode\": \"e639\",\n      \"unicode_decimal\": 58937\n    },\n    {\n      \"icon_id\": \"36197034\",\n      \"name\": \"暂无数据 (1)\",\n      \"font_class\": \"a-zanwushuju1\",\n      \"unicode\": \"e638\",\n      \"unicode_decimal\": 58936\n    },\n    {\n      \"icon_id\": \"33940358\",\n      \"name\": \"开始\",\n      \"font_class\": \"kaishi\",\n      \"unicode\": \"e637\",\n      \"unicode_decimal\": 58935\n    },\n    {\n      \"icon_id\": \"5643512\",\n      \"name\": \"关闭\",\n      \"font_class\": \"guanbi\",\n      \"unicode\": \"e634\",\n      \"unicode_decimal\": 58932\n    },\n    {\n      \"icon_id\": \"4175511\",\n      \"name\": \"下箭头\",\n      \"font_class\": \"xiajiantou\",\n      \"unicode\": \"eb6d\",\n      \"unicode_decimal\": 60269\n    },\n    {\n      \"icon_id\": \"5978833\",\n      \"name\": \"more\",\n      \"font_class\": \"gengduo\",\n      \"unicode\": \"e633\",\n      \"unicode_decimal\": 58931\n    },\n    {\n      \"icon_id\": \"36150033\",\n      \"name\": \"设置\",\n      \"font_class\": \"shezhi\",\n      \"unicode\": \"e630\",\n      \"unicode_decimal\": 58928\n    },\n    {\n      \"icon_id\": \"36137656\",\n      \"name\": \"对话-未选\",\n      \"font_class\": \"duihua-weixuan\",\n      \"unicode\": \"e628\",\n      \"unicode_decimal\": 58920\n    },\n    {\n      \"icon_id\": \"36137657\",\n      \"name\": \"图表-未选\",\n      \"font_class\": \"tubiao-weixuan\",\n      \"unicode\": \"e629\",\n      \"unicode_decimal\": 58921\n    },\n    {\n      \"icon_id\": \"36137693\",\n      \"name\": \"编组 13备份 3\",\n      \"font_class\": \"a-bianzu13beifen3\",\n      \"unicode\": \"e62b\",\n      \"unicode_decimal\": 58923\n    },\n    {\n      \"icon_id\": \"36134221\",\n      \"name\": \"编组备份\",\n      \"font_class\": \"bianzubeifen\",\n      \"unicode\": \"e616\",\n      \"unicode_decimal\": 58902\n    },\n    {\n      \"icon_id\": \"36134222\",\n      \"name\": \"表格\",\n      \"font_class\": \"biaoge1\",\n      \"unicode\": \"e618\",\n      \"unicode_decimal\": 58904\n    },\n    {\n      \"icon_id\": \"36134223\",\n      \"name\": \"收藏 (1)\",\n      \"font_class\": \"a-shoucang1\",\n      \"unicode\": \"e61d\",\n      \"unicode_decimal\": 58909\n    },\n    {\n      \"icon_id\": \"36134224\",\n      \"name\": \"guthub-未选\",\n      \"font_class\": \"guthub-weixuan1\",\n      \"unicode\": \"e621\",\n      \"unicode_decimal\": 58913\n    },\n    {\n      \"icon_id\": \"36134225\",\n      \"name\": \"数据-未选\",\n      \"font_class\": \"shuju-weixuan\",\n      \"unicode\": \"e622\",\n      \"unicode_decimal\": 58914\n    },\n    {\n      \"icon_id\": \"36134226\",\n      \"name\": \"编组 4\",\n      \"font_class\": \"a-bianzu4\",\n      \"unicode\": \"e624\",\n      \"unicode_decimal\": 58916\n    },\n    {\n      \"icon_id\": \"36134227\",\n      \"name\": \"编组 14备份\",\n      \"font_class\": \"a-bianzu14beifen\",\n      \"unicode\": \"e627\",\n      \"unicode_decimal\": 58919\n    },\n    {\n      \"icon_id\": \"36134208\",\n      \"name\": \"guthub-未选\",\n      \"font_class\": \"guthub-weixuan\",\n      \"unicode\": \"e615\",\n      \"unicode_decimal\": 58901\n    },\n    {\n      \"icon_id\": \"7594805\",\n      \"name\": \"24gl-folderMinus\",\n      \"font_class\": \"24gl-folderMinus\",\n      \"unicode\": \"eabe\",\n      \"unicode_decimal\": 60094\n    },\n    {\n      \"icon_id\": \"7594806\",\n      \"name\": \"24gl-folderOpen\",\n      \"font_class\": \"24gl-folderOpen\",\n      \"unicode\": \"eabf\",\n      \"unicode_decimal\": 60095\n    },\n    {\n      \"icon_id\": \"7594875\",\n      \"name\": \"24gf-folderOpen\",\n      \"font_class\": \"24gf-folderOpen\",\n      \"unicode\": \"eac7\",\n      \"unicode_decimal\": 60103\n    },\n    {\n      \"icon_id\": \"2611804\",\n      \"name\": \"云数据库\",\n      \"font_class\": \"yunshujuku\",\n      \"unicode\": \"e744\",\n      \"unicode_decimal\": 59204\n    },\n    {\n      \"icon_id\": \"6607912\",\n      \"name\": \"报表\",\n      \"font_class\": \"baobiao\",\n      \"unicode\": \"e612\",\n      \"unicode_decimal\": 58898\n    },\n    {\n      \"icon_id\": \"6977892\",\n      \"name\": \"工作台\",\n      \"font_class\": \"gongzuotai\",\n      \"unicode\": \"e614\",\n      \"unicode_decimal\": 58900\n    },\n    {\n      \"icon_id\": \"15378610\",\n      \"name\": \"mongodb\",\n      \"font_class\": \"mongodb\",\n      \"unicode\": \"ec21\",\n      \"unicode_decimal\": 60449\n    },\n    {\n      \"icon_id\": \"3172491\",\n      \"name\": \"Redis\",\n      \"font_class\": \"Redis\",\n      \"unicode\": \"e6a2\",\n      \"unicode_decimal\": 59042\n    },\n    {\n      \"icon_id\": \"16300754\",\n      \"name\": \"HIVE_2\",\n      \"font_class\": \"HIVE\",\n      \"unicode\": \"e60e\",\n      \"unicode_decimal\": 58894\n    },\n    {\n      \"icon_id\": \"35572498\",\n      \"name\": \"Kingbase\",\n      \"font_class\": \"Kingbase\",\n      \"unicode\": \"e6a0\",\n      \"unicode_decimal\": 59040\n    },\n    {\n      \"icon_id\": \"35892480\",\n      \"name\": \"仪表盘\",\n      \"font_class\": \"yibiaopan\",\n      \"unicode\": \"e60d\",\n      \"unicode_decimal\": 58893\n    },\n    {\n      \"icon_id\": \"13487185\",\n      \"name\": \"presto\",\n      \"font_class\": \"presto_sql\",\n      \"unicode\": \"e60b\",\n      \"unicode_decimal\": 58891\n    },\n    {\n      \"icon_id\": \"5978710\",\n      \"name\": \"DB2\",\n      \"font_class\": \"shujukuleixingtubiao-kuozhan-\",\n      \"unicode\": \"e60a\",\n      \"unicode_decimal\": 58890\n    },\n    {\n      \"icon_id\": \"35222282\",\n      \"name\": \"oceanbase\",\n      \"font_class\": \"oceanbase\",\n      \"unicode\": \"e982\",\n      \"unicode_decimal\": 59778\n    },\n    {\n      \"icon_id\": \"15192821\",\n      \"name\": \"达梦\",\n      \"font_class\": \"dameng1\",\n      \"unicode\": \"e655\",\n      \"unicode_decimal\": 58965\n    },\n    {\n      \"icon_id\": \"2995206\",\n      \"name\": \"proxy\",\n      \"font_class\": \"proxy\",\n      \"unicode\": \"e63f\",\n      \"unicode_decimal\": 58943\n    },\n    {\n      \"icon_id\": \"33483666\",\n      \"name\": \"openai\",\n      \"font_class\": \"openai\",\n      \"unicode\": \"e646\",\n      \"unicode_decimal\": 58950\n    },\n    {\n      \"icon_id\": \"11542846\",\n      \"name\": \"关于\",\n      \"font_class\": \"guanyu\",\n      \"unicode\": \"e60c\",\n      \"unicode_decimal\": 58892\n    },\n    {\n      \"icon_id\": \"1301391\",\n      \"name\": \"衣服\",\n      \"font_class\": \"yifu\",\n      \"unicode\": \"e666\",\n      \"unicode_decimal\": 58982\n    },\n    {\n      \"icon_id\": \"6850413\",\n      \"name\": \"数据库\",\n      \"font_class\": \"shujuku4\",\n      \"unicode\": \"e609\",\n      \"unicode_decimal\": 58889\n    },\n    {\n      \"icon_id\": \"12993182\",\n      \"name\": \"数据源配置\",\n      \"font_class\": \"shujuyuanpeizhi\",\n      \"unicode\": \"e649\",\n      \"unicode_decimal\": 58953\n    },\n    {\n      \"icon_id\": \"10594601\",\n      \"name\": \"服务器_数据库_jurassic\",\n      \"font_class\": \"jurassic_server\",\n      \"unicode\": \"e6a6\",\n      \"unicode_decimal\": 59046\n    },\n    {\n      \"icon_id\": \"1817682\",\n      \"name\": \"数据库\",\n      \"font_class\": \"shujuku2\",\n      \"unicode\": \"e607\",\n      \"unicode_decimal\": 58887\n    },\n    {\n      \"icon_id\": \"8765123\",\n      \"name\": \"数据库\",\n      \"font_class\": \"shujuku3\",\n      \"unicode\": \"e625\",\n      \"unicode_decimal\": 58917\n    },\n    {\n      \"icon_id\": \"9710796\",\n      \"name\": \"数据库数据\",\n      \"font_class\": \"shujukushuju\",\n      \"unicode\": \"e63c\",\n      \"unicode_decimal\": 58940\n    },\n    {\n      \"icon_id\": \"1305114\",\n      \"name\": \"数据库\",\n      \"font_class\": \"shujuku1\",\n      \"unicode\": \"e636\",\n      \"unicode_decimal\": 58934\n    },\n    {\n      \"icon_id\": \"1472570\",\n      \"name\": \"配置数据源\",\n      \"font_class\": \"peizhishujuyuan\",\n      \"unicode\": \"e62f\",\n      \"unicode_decimal\": 58927\n    },\n    {\n      \"icon_id\": \"31104431\",\n      \"name\": \"SQL历史查询\",\n      \"font_class\": \"SQLlishichaxun\",\n      \"unicode\": \"e80a\",\n      \"unicode_decimal\": 59402\n    },\n    {\n      \"icon_id\": \"6550626\",\n      \"name\": \"重命名\",\n      \"font_class\": \"zhongmingming\",\n      \"unicode\": \"e623\",\n      \"unicode_decimal\": 58915\n    },\n    {\n      \"icon_id\": \"6607690\",\n      \"name\": \"ico_数据查询与统计_预约情况查询\",\n      \"font_class\": \"ico_shujuchaxunyutongji_yuyueqingkuangchaxun\",\n      \"unicode\": \"e8ff\",\n      \"unicode_decimal\": 59647\n    },\n    {\n      \"icon_id\": \"27571255\",\n      \"name\": \"clickhouse-云数据库ClickHouse\",\n      \"font_class\": \"clickhouse-yunshujukuClickHouse\",\n      \"unicode\": \"e8f4\",\n      \"unicode_decimal\": 59636\n    },\n    {\n      \"icon_id\": \"13592725\",\n      \"name\": \"rds_mariadb\",\n      \"font_class\": \"rds_mariadb\",\n      \"unicode\": \"e6f5\",\n      \"unicode_decimal\": 59125\n    },\n    {\n      \"icon_id\": \"8817896\",\n      \"name\": \"减少减去减号\",\n      \"font_class\": \"jianshaojianqujianhao\",\n      \"unicode\": \"e62a\",\n      \"unicode_decimal\": 58922\n    },\n    {\n      \"icon_id\": \"7119672\",\n      \"name\": \"sqlserver\",\n      \"font_class\": \"sqlserver\",\n      \"unicode\": \"e664\",\n      \"unicode_decimal\": 58980\n    },\n    {\n      \"icon_id\": \"12600909\",\n      \"name\": \"sqlite\",\n      \"font_class\": \"sqlite\",\n      \"unicode\": \"e65a\",\n      \"unicode_decimal\": 58970\n    },\n    {\n      \"icon_id\": \"26519722\",\n      \"name\": \"缺省页_暂无数据\",\n      \"font_class\": \"queshengye_zanwushuju\",\n      \"unicode\": \"e760\",\n      \"unicode_decimal\": 59232\n    },\n    {\n      \"icon_id\": \"1330772\",\n      \"name\": \"未完成\",\n      \"font_class\": \"weiwancheng\",\n      \"unicode\": \"e755\",\n      \"unicode_decimal\": 59221\n    },\n    {\n      \"icon_id\": \"5979977\",\n      \"name\": \"完成-01\",\n      \"font_class\": \"wancheng-\",\n      \"unicode\": \"e62e\",\n      \"unicode_decimal\": 58926\n    },\n    {\n      \"icon_id\": \"24056325\",\n      \"name\": \"成功\",\n      \"font_class\": \"chenggong1\",\n      \"unicode\": \"e620\",\n      \"unicode_decimal\": 58912\n    },\n    {\n      \"icon_id\": \"16323142\",\n      \"name\": \"机器人\",\n      \"font_class\": \"jiqiren\",\n      \"unicode\": \"e70e\",\n      \"unicode_decimal\": 59150\n    },\n    {\n      \"icon_id\": \"6234556\",\n      \"name\": \"换一换\",\n      \"font_class\": \"huanyihuan\",\n      \"unicode\": \"e635\",\n      \"unicode_decimal\": 58933\n    },\n    {\n      \"icon_id\": \"26130627\",\n      \"name\": \"icon_infomation\",\n      \"font_class\": \"icon_infomation\",\n      \"unicode\": \"e65b\",\n      \"unicode_decimal\": 58971\n    },\n    {\n      \"icon_id\": \"6150969\",\n      \"name\": \"key\",\n      \"font_class\": \"key1\",\n      \"unicode\": \"e775\",\n      \"unicode_decimal\": 59253\n    },\n    {\n      \"icon_id\": \"5961392\",\n      \"name\": \"mysql\",\n      \"font_class\": \"mysql\",\n      \"unicode\": \"ec6d\",\n      \"unicode_decimal\": 60525\n    },\n    {\n      \"icon_id\": \"15378663\",\n      \"name\": \"oracle\",\n      \"font_class\": \"oracle\",\n      \"unicode\": \"ec48\",\n      \"unicode_decimal\": 60488\n    },\n    {\n      \"icon_id\": \"15378704\",\n      \"name\": \"postgresql\",\n      \"font_class\": \"postgresql\",\n      \"unicode\": \"ec5d\",\n      \"unicode_decimal\": 60509\n    },\n    {\n      \"icon_id\": \"19657927\",\n      \"name\": \"h2\",\n      \"font_class\": \"h2\",\n      \"unicode\": \"e61c\",\n      \"unicode_decimal\": 58908\n    },\n    {\n      \"icon_id\": \"372246\",\n      \"name\": \"cc-schema\",\n      \"font_class\": \"cc-schema\",\n      \"unicode\": \"e696\",\n      \"unicode_decimal\": 59030\n    },\n    {\n      \"icon_id\": \"1789259\",\n      \"name\": \"新建表格\",\n      \"font_class\": \"xinjianbiaoge\",\n      \"unicode\": \"e6b6\",\n      \"unicode_decimal\": 59062\n    },\n    {\n      \"icon_id\": \"15550146\",\n      \"name\": \"export\",\n      \"font_class\": \"export\",\n      \"unicode\": \"e613\",\n      \"unicode_decimal\": 58899\n    },\n    {\n      \"icon_id\": \"693661\",\n      \"name\": \"角色管理\",\n      \"font_class\": \"jiaoseguanli\",\n      \"unicode\": \"e66d\",\n      \"unicode_decimal\": 58989\n    },\n    {\n      \"icon_id\": \"11348953\",\n      \"name\": \"console\",\n      \"font_class\": \"console\",\n      \"unicode\": \"e619\",\n      \"unicode_decimal\": 58905\n    },\n    {\n      \"icon_id\": \"7594874\",\n      \"name\": \"24gf-folderMinus\",\n      \"font_class\": \"24gf-folderMinus\",\n      \"unicode\": \"eac5\",\n      \"unicode_decimal\": 60101\n    },\n    {\n      \"icon_id\": \"201556\",\n      \"name\": \"查看\",\n      \"font_class\": \"chakan\",\n      \"unicode\": \"e606\",\n      \"unicode_decimal\": 58886\n    },\n    {\n      \"icon_id\": \"5387764\",\n      \"name\": \"复制_o\",\n      \"font_class\": \"fuzhi_o\",\n      \"unicode\": \"eb4e\",\n      \"unicode_decimal\": 60238\n    },\n    {\n      \"icon_id\": \"10936703\",\n      \"name\": \"执行\",\n      \"font_class\": \"zhihang\",\n      \"unicode\": \"e626\",\n      \"unicode_decimal\": 58918\n    },\n    {\n      \"icon_id\": \"21164516\",\n      \"name\": \"m-格式化文字\",\n      \"font_class\": \"m-geshihuawenzi\",\n      \"unicode\": \"e7f8\",\n      \"unicode_decimal\": 59384\n    },\n    {\n      \"icon_id\": \"4937000\",\n      \"name\": \"github-fill\",\n      \"font_class\": \"github-fill\",\n      \"unicode\": \"e885\",\n      \"unicode_decimal\": 59525\n    },\n    {\n      \"icon_id\": \"8626141\",\n      \"name\": \"保存\",\n      \"font_class\": \"baocun2\",\n      \"unicode\": \"e645\",\n      \"unicode_decimal\": 58949\n    },\n    {\n      \"icon_id\": \"5387931\",\n      \"name\": \"箭头_向左两次_o\",\n      \"font_class\": \"jiantou_xiangzuoliangci_o\",\n      \"unicode\": \"eb93\",\n      \"unicode_decimal\": 60307\n    },\n    {\n      \"icon_id\": \"1371\",\n      \"name\": \"新建窗口\",\n      \"font_class\": \"xinjianchuangkou\",\n      \"unicode\": \"e603\",\n      \"unicode_decimal\": 58883\n    },\n    {\n      \"icon_id\": \"4503683\",\n      \"name\": \"loading\",\n      \"font_class\": \"loading2\",\n      \"unicode\": \"e6cd\",\n      \"unicode_decimal\": 59085\n    },\n    {\n      \"icon_id\": \"2505938\",\n      \"name\": \"链接克隆\",\n      \"font_class\": \"lianjiekelong\",\n      \"unicode\": \"e6ca\",\n      \"unicode_decimal\": 59082\n    },\n    {\n      \"icon_id\": \"11661836\",\n      \"name\": \"SQL升级文件\",\n      \"font_class\": \"SQLshengjiwenjian\",\n      \"unicode\": \"e63b\",\n      \"unicode_decimal\": 58939\n    },\n    {\n      \"icon_id\": \"15214527\",\n      \"name\": \"sql\",\n      \"font_class\": \"sql\",\n      \"unicode\": \"e610\",\n      \"unicode_decimal\": 58896\n    },\n    {\n      \"icon_id\": \"5961312\",\n      \"name\": \"连接流\",\n      \"font_class\": \"lianjieliu\",\n      \"unicode\": \"ec57\",\n      \"unicode_decimal\": 60503\n    },\n    {\n      \"icon_id\": \"16853978\",\n      \"name\": \"跳转/退出\",\n      \"font_class\": \"tiaozhuan\",\n      \"unicode\": \"e685\",\n      \"unicode_decimal\": 59013\n    },\n    {\n      \"icon_id\": \"1433771\",\n      \"name\": \"key\",\n      \"font_class\": \"key\",\n      \"unicode\": \"e648\",\n      \"unicode_decimal\": 58952\n    },\n    {\n      \"icon_id\": \"11372646\",\n      \"name\": \"播放记录\",\n      \"font_class\": \"bofangjilu\",\n      \"unicode\": \"e8ad\",\n      \"unicode_decimal\": 59565\n    },\n    {\n      \"icon_id\": \"3640170\",\n      \"name\": \"成功\",\n      \"font_class\": \"chenggong\",\n      \"unicode\": \"e605\",\n      \"unicode_decimal\": 58885\n    },\n    {\n      \"icon_id\": \"9626932\",\n      \"name\": \"失败\",\n      \"font_class\": \"shibai\",\n      \"unicode\": \"e87c\",\n      \"unicode_decimal\": 59516\n    },\n    {\n      \"icon_id\": \"688104\",\n      \"name\": \"收回 上下\",\n      \"font_class\": \"shouhuishangxia\",\n      \"unicode\": \"e790\",\n      \"unicode_decimal\": 59280\n    },\n    {\n      \"icon_id\": \"688143\",\n      \"name\": \"展开 上下\",\n      \"font_class\": \"zhankaishangxia\",\n      \"unicode\": \"e7b1\",\n      \"unicode_decimal\": 59313\n    },\n    {\n      \"icon_id\": \"970749\",\n      \"name\": \"数据库\",\n      \"font_class\": \"shujuku\",\n      \"unicode\": \"e62c\",\n      \"unicode_decimal\": 58924\n    },\n    {\n      \"icon_id\": \"5399558\",\n      \"name\": \"保存\",\n      \"font_class\": \"baocun\",\n      \"unicode\": \"e936\",\n      \"unicode_decimal\": 59702\n    },\n    {\n      \"icon_id\": \"5961297\",\n      \"name\": \"查询\",\n      \"font_class\": \"chaxun\",\n      \"unicode\": \"ec4c\",\n      \"unicode_decimal\": 60492\n    },\n    {\n      \"icon_id\": \"150096\",\n      \"name\": \"对勾\",\n      \"font_class\": \"duigou11\",\n      \"unicode\": \"e61f\",\n      \"unicode_decimal\": 58911\n    },\n    {\n      \"icon_id\": \"1288113\",\n      \"name\": \"check\",\n      \"font_class\": \"check1\",\n      \"unicode\": \"e617\",\n      \"unicode_decimal\": 58903\n    },\n    {\n      \"icon_id\": \"14151855\",\n      \"name\": \"概览\",\n      \"font_class\": \"gailan\",\n      \"unicode\": \"e632\",\n      \"unicode_decimal\": 58930\n    },\n    {\n      \"icon_id\": \"21918566\",\n      \"name\": \"概览\",\n      \"font_class\": \"huaban2\",\n      \"unicode\": \"e63d\",\n      \"unicode_decimal\": 58941\n    },\n    {\n      \"icon_id\": \"201638\",\n      \"name\": \"编辑\",\n      \"font_class\": \"bianji\",\n      \"unicode\": \"e602\",\n      \"unicode_decimal\": 58882\n    },\n    {\n      \"icon_id\": \"4686545\",\n      \"name\": \"刷新\",\n      \"font_class\": \"shuaxin1\",\n      \"unicode\": \"ec08\",\n      \"unicode_decimal\": 60424\n    },\n    {\n      \"icon_id\": \"8084544\",\n      \"name\": \"菜单/列表\",\n      \"font_class\": \"caidan\",\n      \"unicode\": \"e611\",\n      \"unicode_decimal\": 58897\n    },\n    {\n      \"icon_id\": \"1305461\",\n      \"name\": \"表格\",\n      \"font_class\": \"biaoge\",\n      \"unicode\": \"e63e\",\n      \"unicode_decimal\": 58942\n    },\n    {\n      \"icon_id\": \"4867114\",\n      \"name\": \"展开\",\n      \"font_class\": \"zhankai\",\n      \"unicode\": \"e65f\",\n      \"unicode_decimal\": 58975\n    },\n    {\n      \"icon_id\": \"4942664\",\n      \"name\": \"收起\",\n      \"font_class\": \"shouqi\",\n      \"unicode\": \"e61e\",\n      \"unicode_decimal\": 58910\n    },\n    {\n      \"icon_id\": \"5387845\",\n      \"name\": \"主题_o\",\n      \"font_class\": \"zhuti_o\",\n      \"unicode\": \"eb6f\",\n      \"unicode_decimal\": 60271\n    },\n    {\n      \"icon_id\": \"11195023\",\n      \"name\": \"断开连接\",\n      \"font_class\": \"duankailianjie\",\n      \"unicode\": \"e65e\",\n      \"unicode_decimal\": 58974\n    },\n    {\n      \"icon_id\": \"6010907\",\n      \"name\": \"修改\",\n      \"font_class\": \"xiugai\",\n      \"unicode\": \"e60f\",\n      \"unicode_decimal\": 58895\n    },\n    {\n      \"icon_id\": \"10142371\",\n      \"name\": \"删除\",\n      \"font_class\": \"delete\",\n      \"unicode\": \"e604\",\n      \"unicode_decimal\": 58884\n    },\n    {\n      \"icon_id\": \"77822\",\n      \"name\": \"更多\",\n      \"font_class\": \"gengduo1\",\n      \"unicode\": \"e601\",\n      \"unicode_decimal\": 58881\n    },\n    {\n      \"icon_id\": \"4511969\",\n      \"name\": \"减少\",\n      \"font_class\": \"jianshao\",\n      \"unicode\": \"e644\",\n      \"unicode_decimal\": 58948\n    },\n    {\n      \"icon_id\": \"5334173\",\n      \"name\": \"加\",\n      \"font_class\": \"jia\",\n      \"unicode\": \"e61b\",\n      \"unicode_decimal\": 58907\n    },\n    {\n      \"icon_id\": \"7424757\",\n      \"name\": \"加号\",\n      \"font_class\": \"hao\",\n      \"unicode\": \"e631\",\n      \"unicode_decimal\": 58929\n    },\n    {\n      \"icon_id\": \"11364467\",\n      \"name\": \"arrow drop down\",\n      \"font_class\": \"right\",\n      \"unicode\": \"e608\",\n      \"unicode_decimal\": 58888\n    },\n    {\n      \"icon_id\": \"11931077\",\n      \"name\": \"search\",\n      \"font_class\": \"search1\",\n      \"unicode\": \"e600\",\n      \"unicode_decimal\": 58880\n    },\n    {\n      \"icon_id\": \"15838464\",\n      \"name\": \"download\",\n      \"font_class\": \"download1\",\n      \"unicode\": \"e66c\",\n      \"unicode_decimal\": 58988\n    },\n    {\n      \"icon_id\": \"22303251\",\n      \"name\": \"向右箭头\",\n      \"font_class\": \"xiangyoujiantou\",\n      \"unicode\": \"e79c\",\n      \"unicode_decimal\": 59292\n    },\n    {\n      \"icon_id\": \"27901498\",\n      \"name\": \"删除线型\",\n      \"font_class\": \"shanchuxianxing\",\n      \"unicode\": \"e6a7\",\n      \"unicode_decimal\": 59047\n    },\n    {\n      \"icon_id\": \"28093125\",\n      \"name\": \"cross\",\n      \"font_class\": \"cross-copy\",\n      \"unicode\": \"ec8e\",\n      \"unicode_decimal\": 60558\n    },\n    {\n      \"icon_id\": \"5667975\",\n      \"name\": \"刷新\",\n      \"font_class\": \"shuaxin\",\n      \"unicode\": \"e62d\",\n      \"unicode_decimal\": 58925\n    },\n    {\n      \"icon_id\": \"22303207\",\n      \"name\": \"提醒\",\n      \"font_class\": \"tixing\",\n      \"unicode\": \"e913\",\n      \"unicode_decimal\": 59667\n    },\n    {\n      \"icon_id\": \"6129178\",\n      \"name\": \"138设置、系统设置、功能设置、属性\",\n      \"font_class\": \"shezhixitongshezhigongnengshezhishuxing\",\n      \"unicode\": \"e795\",\n      \"unicode_decimal\": 59285\n    },\n    {\n      \"icon_id\": \"19113301\",\n      \"name\": \"执行sql脚本\",\n      \"font_class\": \"zhihangsqljiaoben\",\n      \"unicode\": \"e759\",\n      \"unicode_decimal\": 59225\n    },\n    {\n      \"icon_id\": \"20104543\",\n      \"name\": \"虚拟数据库管理\",\n      \"font_class\": \"xunishujukuguanli\",\n      \"unicode\": \"e61a\",\n      \"unicode_decimal\": 58906\n    }\n  ]\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/AppTitleBar/index.less",
    "content": "@import '../../styles/var.less';\n\n.appTitleBar {\n  background-color: var(--color-bg-subtle);\n  -webkit-app-region: drag;\n  -webkit-user-select: none;\n  border-bottom: 1px solid var(--color-border);\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  height: 30px;\n  .appTitleBarGlobal{\n    flex: 1;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 0px 10px;\n    height: 100%;\n    .appName {\n      flex: 1;\n      text-align: start;\n      font-weight: bold;\n      padding-left: 6px;\n    }\n    .rightSlot,.leftSlot{\n      height: 100%;\n      display: flex;\n      align-items: center;\n      -webkit-app-region: no-drag;\n    }\n  }\n  .spacer {\n    width: 126px;\n    flex-shrink: 0;\n  }\n  .windowsCloseBar {\n    display: flex;\n    height: 100%;\n    flex-shrink: 0;\n    width: 126px;\n    -webkit-app-region: no-drag;\n    .windowsCloseBarItem {\n      width: 42px;\n      height: 100%;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      cursor: pointer;\n      &:hover {\n        background-color: var(--color-hover-bg);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/AppTitleBar/index.tsx",
    "content": "import React, { memo, useState, useMemo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { useCommonStore } from '@/store/common';\nimport Iconfont from '@/components/Iconfont';\nimport BrandLogo from '@/components/BrandLogo';\n\ninterface IProps {\n  className?: string;\n}\n\nexport default memo<IProps>((props) => {\n  const { className } = props;\n  const [isMaximized, setIsMaximize] = useState(window.electronApi?.isMaximized());\n\n  const { appTitleBarRightComponent } = useCommonStore((state) => {\n    return {\n      appTitleBarRightComponent: state.appTitleBarRightComponent,\n    };\n  });\n\n  const isMac = useMemo(() => {\n    return window.electronApi?.getPlatform().isMac;\n  }, []);\n\n  // const isMac = false;\n\n  const handleDoubleClick = () => {\n    window.electronApi?.setMaximize();\n    setIsMaximize(!isMaximized);\n  };\n\n  const handelMinimizeWindow = (e) => {\n    e.stopPropagation();\n    window.electronApi?.minimizeWindow();\n  };\n\n  const handelMaximize = (e) => {\n    e.stopPropagation();\n    window.electronApi?.setMaximize();\n    setIsMaximize(!isMaximized);\n  };\n\n  const handelCloseWindow = (e) => {\n    e.stopPropagation();\n    window.electronApi?.closeWindow();\n  };\n\n  if(isMac === void 0){\n    return false\n  }\n\n  return (\n    <div className={classnames(styles.appTitleBar, className)} onDoubleClick={handleDoubleClick}>\n      <div className={styles.appTitleBarGlobal}>\n        <div className={classnames(styles.leftSlot)}>\n          <BrandLogo size={20} className={styles.brandLogo} />\n        </div>\n        <div className={styles.appName}>Chat2DB</div>\n        <div className={styles.rightSlot}>{appTitleBarRightComponent}</div>\n      </div>\n      {(!isMac && isMac !== void 0) && (\n        <div className={styles.windowsCloseBar}>\n          <div className={styles.windowsCloseBarItem} onClick={handelMinimizeWindow}>\n            <Iconfont size={13} code=\"&#xe671;\" />\n          </div>\n          <div className={styles.windowsCloseBarItem} onClick={handelMaximize}>\n            {isMaximized ? <Iconfont size={13} code=\"&#xe66e;\" /> : <Iconfont size={12} code=\"&#xe66b;\" />}\n          </div>\n          <div className={styles.windowsCloseBarItem} onClick={handelCloseWindow}>\n            <Iconfont size={12} code=\"&#xe66f;\" />\n          </div>\n        </div>\n      )}\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/blocks/CreateConnection/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n  width: 100%;\n  height: 100%;\n  overflow-y: auto;\n}\n\n.dataBaseListBox{\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  min-height: 100%;\n}\n\n.dataBaseList {\n  display: flex;\n  justify-content: space-between;\n  flex-wrap: wrap;\n  max-width: 800px;\n}\n\n.databaseItem {\n  flex-grow: 1;\n  border-radius: 4px;\n  height: 50px;\n  width: 210px;\n  margin: 10px 20px;\n  padding: 0px 16px;\n  border-radius: 8px;\n  overflow: hidden;\n  box-sizing: border-box;\n  border: 1px solid var(--color-border);\n\n  .databaseItemMain {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    height: 50px;\n    border-radius: 8px;\n  }\n\n  .databaseItemLeft {\n    display: flex;\n    align-items: center;\n  }\n\n  .databaseItemRight {\n    display: none;\n\n    i {\n      font-size: 16px;\n    }\n  }\n\n  &:hover {\n    background-color: var(--color-bg-medium);\n    color: var(--color-primary);\n    border: 1px solid var(--color-primary);\n    cursor: pointer;\n\n    .databaseItemRight {\n      display: block;\n\n      i {\n        color: var(--color-primary);\n      }\n    }\n  }\n\n  .logoBox {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    height: 28px;\n    width: 28px;\n    border-radius: 8px;\n    margin-right: 16px;\n\n    i {\n      font-size: 16px;\n    }\n  }\n}\n\n.databaseItemSpacer {\n  flex-grow: 1;\n  width: 210px;\n  margin: 0px 20px;\n  padding: 0px 16px;\n  box-sizing: border-box;\n}\n\n.createConnections {\n  min-height: 100%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: var(--color-bg);\n  transform: scale(0.2);\n  transition: 0.1s ease-in-out;\n}\n\n.showCreateConnections {\n  transform: scale(1);\n  transition: transform 0.3s ease-in-out;\n}\n\n.notPermission {\n  display: flex;\n  flex-direction: column;\n  .notPermissionIconTips {\n    color: var(--color-text-tertiary);\n    text-align: center;\n    width: 340px;\n  }\n  .notPermissionIconBox,\n  .connectButtonBox {\n    display: flex;\n    justify-content: center;\n  }\n  .notPermissionIcon {\n    font-size: 200px;\n    color: var(--color-text-quaternary);\n    margin: 20px 0px;\n  }\n  .connectButton {\n    margin-top: 20px;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/CreateConnection/index.tsx",
    "content": "import React, { memo, useEffect, useState } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { IConnectionDetails, IDatabase } from '@/typings';\nimport ConnectionEdit from '@/components/ConnectionEdit';\nimport { databaseTypeList } from '@/constants';\nimport Iconfont from '@/components/Iconfont';\nimport i18n from '@/i18n';\nimport FileUploadModal from '@/components/ImportConnection';\nimport {getConnectionList} from '@/pages/main/store/connection';\n\n// IConnectionDetails 全部信息代表修改\n// null 展示因增列表\n//  { type: string } 只有数据库类型代表新增\ntype IEditConnectionDetail = IConnectionDetails | null | Pick<IConnectionDetails, 'type'>;\n\ninterface IProps {\n  className?: string;\n  onSubmit?: (data: IConnectionDetails) => Promise<any>; // 点击保存或修改的回调，我会把数据给你\n  connectionDetail: IEditConnectionDetail | null | undefined;\n  noPermission?: boolean;\n}\n\nexport default memo<IProps>((props) => {\n  const { className, onSubmit, connectionDetail: externalConnectionDetail } = props;\n  const [connectionDetail, setConnectionDetail] = useState<IEditConnectionDetail | null | undefined>(\n    externalConnectionDetail,\n  );\n  const [isFileUploadModalOpen, setIsFileUploadModalOpen] = useState(false);\n\n\n  useEffect(() => {\n    setConnectionDetail(externalConnectionDetail);\n  }, [externalConnectionDetail]);\n\n  function handleCreateConnections(database: IDatabase) {\n    setConnectionDetail({\n      type: database.code,\n    });\n  }\n\n  // function handleSubmit(data: IConnectionDetails) {\n  //   return onSubmit?.(data);\n  // }\n\n  return (\n    <>\n      <div className={classnames(styles.box, className)}>\n        {connectionDetail && (\n          <div\n            className={classnames(styles.createConnections, {\n              [styles.showCreateConnections]: connectionDetail,\n            })}\n          >\n            <ConnectionEdit\n              closeCreateConnection={() => {\n                setConnectionDetail(null);\n              }}\n              connectionData={connectionDetail as any}\n              submit={onSubmit}\n            />\n          </div>\n        )}\n        {connectionDetail === null && (\n          <div className={styles.dataBaseListBox}>\n            <div className={styles.dataBaseList}>\n              {databaseTypeList.map((t) => {\n                return (\n                  <div key={t.code} className={styles.databaseItem} onClick={handleCreateConnections.bind(null, t)}>\n                    <div className={styles.databaseItemMain}>\n                      <div className={styles.databaseItemLeft}>\n                        <div className={styles.logoBox}>\n                          <Iconfont code={t.icon} />\n                        </div>\n                        {t.name}\n                      </div>\n                      <div className={styles.databaseItemRight}>\n                        <Iconfont code=\"&#xe631;\" />\n                      </div>\n                    </div>\n                  </div>\n                );\n              })}\n              <div className={styles.databaseItem} onClick={() => {setIsFileUploadModalOpen(true)}}>\n                <div className={styles.databaseItemMain}>\n                  <div className={styles.databaseItemLeft}>\n                    <div className={styles.logoBox}>\n                      <Iconfont code=\"&#xe66c;\" />\n                    </div>\n                    {i18n('connection.title.importConnection')}\n                  </div>\n                  <div className={styles.databaseItemRight}>\n                    <Iconfont code=\"&#xe631;\" />\n                  </div>\n                </div>\n              </div>\n              {Array.from({ length: 20 }).map((t, index) => {\n                return <div key={index} className={styles.databaseItemSpacer} />;\n              })}\n            </div>\n          </div>\n        )}\n      </div>\n      <FileUploadModal\n        open={isFileUploadModalOpen}\n        onClose={() => {\n          setIsFileUploadModalOpen(false);\n        }}\n        onConfirm={() => {\n          setIsFileUploadModalOpen(false);\n          getConnectionList()\n        }}\n      />\n    </>\n  );\n});\n\n{\n  /* <div className={styles.notPermission}>\n  <div className={styles.notPermissionIconBox}>\n    <Iconfont className={styles.notPermissionIcon} code=\"&#xe658;\" />\n  </div>\n  <div className={styles.notPermissionIconTips}>{i18n('connection.tips.noConnectionTips')}</div>\n  <div className={styles.connectButtonBox}>\n    <Button\n      type=\"primary\"\n      className={styles.connectButton}\n      icon={<Iconfont code=\"&#xec57;\" />}\n      onClick={() => {\n        handleMenuItemDoubleClick(curConnection);\n      }}\n    >\n      {i18n('connection.button.connect')}\n    </Button>\n  </div>\n</div> */\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.less",
    "content": "@import '../../../styles/var.less';\n\n.baseInfo {\n  padding: 20px 10px 0px;\n  display: flex;\n  // justify-content: center;\n  height: 100%;\n}\n\n.formBox {\n  width: 50%;\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx",
    "content": "import React, { useContext, useEffect, useImperativeHandle, ForwardedRef, forwardRef } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Form, Input } from 'antd';\nimport { Context } from '../index';\nimport { IBaseInfo } from '@/typings';\nimport { DatabaseTypeCode } from '@/constants';\nimport i18n from '@/i18n';\n\nexport interface IBaseInfoRef {\n  getBaseInfo: () => IBaseInfo;\n}\n\ninterface IProps {\n  className?: string;\n}\n\nconst BaseInfo = forwardRef((props: IProps, ref: ForwardedRef<IBaseInfoRef>) => {\n  const { className } = props;\n  const { tableDetails, databaseType } = useContext(Context);\n  const [form] = Form.useForm();\n\n  useEffect(() => {\n    form.setFieldsValue({\n      name: tableDetails.name,\n      comment: tableDetails.comment,\n      charset: tableDetails.charset,\n      engine: tableDetails.engine,\n      incrementValue: tableDetails.incrementValue,\n    });\n  }, [tableDetails]);\n\n  function getBaseInfo(): IBaseInfo {\n    return form.getFieldsValue();\n  }\n\n  useImperativeHandle(ref, () => ({\n    getBaseInfo,\n  }));\n\n  return (\n    <div className={classnames(className, styles.baseInfo)}>\n      <div className={styles.formBox}>\n        <Form\n          layout=\"vertical\"\n          form={form}\n          initialValues={{ remember: true }}\n          autoComplete=\"off\"\n          className={styles.form}\n        >\n          <Form.Item label={`${i18n('editTable.label.tableName')}:`} name=\"name\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={`${i18n('editTable.label.comment')}:`} name=\"comment\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          {databaseType === DatabaseTypeCode.MYSQL && (\n            <>\n              <Form.Item label={`${i18n('editTable.label.characterSet')}:`} name=\"charset\">\n                <Input autoComplete=\"off\" />\n              </Form.Item>\n              <Form.Item label={`${i18n('editTable.label.engine')}:`} name=\"engine\">\n                <Input autoComplete=\"off\" />\n              </Form.Item>\n              <Form.Item label={`${i18n('editTable.label.incrementValue')}:`} name=\"incrementValue\">\n                <Input autoComplete=\"off\" />\n              </Form.Item>\n            </>\n          )}\n        </Form>\n      </div>\n    </div>\n  );\n});\n\nexport default BaseInfo;\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.less",
    "content": "@import '../../../styles/var.less';\n\n.columnList {\n  height: 100%;\n  padding: 10px;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n}\n\n.columnListHeader {\n  margin: 0px -5px 10px;\n  flex-shrink: 0;\n  button {\n    margin: 0px 5px;\n  }\n}\n\n.formBox {\n  height: 0px;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n}\n\n.tableBox {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  border-radius: 8px 8px 0px 0px;\n  overflow: hidden;\n}\n\n.addColumnButton {\n  flex-shrink: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border: 1px dashed var(--color-border);\n  line-height: 30px;\n  margin-top: 10px;\n  color: var(--color-text-secondary);\n  cursor: pointer;\n  i {\n    margin-right: 5px;\n  }\n  &:hover {\n    color: var(--color-primary);\n    border-color: var(--color-primary);\n  }\n}\n\n.otherInfo {\n  flex-shrink: 0;\n  margin: 10px -10px 0px;\n  padding: 10px;\n  height: 200px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border-top: 1px solid var(--color-border);\n}\n\n.otherInfoFormBox {\n  min-height: 140px;\n  width: 400px;\n}\n\n.editableCell {\n  height: 28px;\n  line-height: 26px;\n  padding: 0px 7px;\n  box-sizing: border-box;\n  border: 1px solid transparent;\n  .f-single-line();\n  width: 100%;\n  cursor: pointer;\n}\n\n// .cellContent {\n//   border: 1px solid transparent;\n//   margin: -1px;\n//   &:hover {\n//     border: 1px solid var(--color-primary);\n//   }\n// }\n\n.keyBox {\n  width: 26px;\n  height: 26px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  cursor: pointer;\n  position: relative;\n  i {\n    color: var(--color-warning);\n  }\n  span {\n    position: absolute;\n    font-weight: bold;\n    right: 4px;\n    bottom: -2px;\n    transform: scale(0.8);\n  }\n}\n\n.disabledKeyBox {\n  cursor: default;\n}\n\n.operationBar {\n  display: flex;\n  justify-content: end;\n  .deleteIconBox {\n    height: 26px;\n    width: 26px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n    &:hover {\n      color: var(--color-primary);\n    }\n  }\n}\n.columnList {\n  :global {\n    .ant-table-body {\n      border: 1px solid var(--color-border);\n      border-top: 0px;\n      border-bottom: 0px;\n    }\n    .ant-table-header {\n      border: 1px solid var(--color-border);\n      border-bottom: 0px;\n    }\n    .ant-table {\n      border-radius: 10px;\n      border-bottom: 0px;\n    }\n    .ant-table-wrapper .ant-table-tbody > tr > td {\n      // border: 0px;\n      padding: 4px 2px;\n    }\n    .ant-table-wrapper .ant-table-thead > tr > th {\n      padding: 8px 4px;\n      &::before {\n        display: none;\n      }\n    }\n    .ant-table-wrapper .ant-table-thead > tr > td {\n      &::before {\n        display: none;\n      }\n    }\n    // antd无法设置最小宽度，所以在这里设置最小列宽为100px\n    colgroup col:nth-last-child(2) {\n      min-width: 100px;\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx",
    "content": "import React, { useContext, useEffect, useState, useRef, forwardRef, ForwardedRef, useImperativeHandle } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { MenuOutlined } from '@ant-design/icons';\nimport { DndContext, type DragEndEvent } from '@dnd-kit/core';\nimport { restrictToVerticalAxis } from '@dnd-kit/modifiers';\nimport { Table, InputNumber, Input, Form, Select, Checkbox } from 'antd';\nimport { v4 as uuidv4 } from 'uuid';\nimport { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';\nimport { CSS } from '@dnd-kit/utilities';\nimport { Context } from '../index';\nimport { IColumnItemNew, IColumnTypes } from '@/typings';\nimport i18n from '@/i18n';\nimport { EditColumnOperationType, DatabaseTypeCode, NullableType } from '@/constants';\nimport CustomSelect from '@/components/CustomSelect';\nimport Iconfont from '@/components/Iconfont';\n\ninterface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {\n  'data-row-key': string;\n}\n\ninterface IProps {}\n\n// 编辑配置\ninterface IEditingConfig extends IColumnTypes {\n  editKey: string;\n}\n\n// 本组件暴露给父组件的方法\nexport interface IColumnListRef {\n  getColumnListInfo: () => IColumnItemNew[];\n}\n\nconst Row = ({ children, ...props }: RowProps) => {\n  const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({\n    id: props['data-row-key'],\n  });\n\n  const style: React.CSSProperties = {\n    ...props.style,\n    transform: CSS.Transform.toString(transform && { ...transform, scaleY: 1 }),\n    transition,\n    ...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),\n  };\n\n  return (\n    <tr {...props} ref={setNodeRef} style={style} {...attributes}>\n      {React.Children.map(children, (child) => {\n        if ((child as React.ReactElement).key === 'sort') {\n          return React.cloneElement(child as React.ReactElement, {\n            children: (\n              <MenuOutlined ref={setActivatorNodeRef} style={{ touchAction: 'none', cursor: 'move' }} {...listeners} />\n            ),\n          });\n        }\n        return child;\n      })}\n    </tr>\n  );\n};\n\n// 创建一个空的数据结构\nconst createInitialData = () => {\n  return {\n    key: uuidv4(),\n    oldName: null,\n    name: null,\n    tableName: null,\n    columnType: null,\n    dataType: null,\n    defaultValue: null,\n    autoIncrement: null,\n    comment: null,\n    primaryKey: null,\n    primaryKeyOrder: null,\n    schemaName: null,\n    databaseName: null,\n    typeName: null,\n    columnSize: null,\n    bufferLength: null,\n    decimalDigits: null,\n    numPrecRadix: null,\n    nullableInt: null,\n    sqlDataType: null,\n    sqlDatetimeSub: null,\n    charOctetLength: null,\n    ordinalPosition: null,\n    nullable: null,\n    generatedColumn: null,\n    charSetName: null,\n    collationName: null,\n    value: null,\n    editStatus: EditColumnOperationType.Add,\n  };\n};\n\nconst ColumnList = forwardRef((props: IProps, ref: ForwardedRef<IColumnListRef>) => {\n  const { databaseSupportField, databaseName, schemaName, tableDetails, databaseType } = useContext(Context);\n  const [dataSource, setDataSource] = useState<IColumnItemNew[]>([createInitialData()]);\n  const [form] = Form.useForm();\n  const [editingData, setEditingData] = useState<IColumnItemNew | null>(null);\n  const [editingConfig, setEditingConfig] = useState<IEditingConfig | null>(null);\n  const tableRef = useRef<HTMLDivElement>(null);\n\n  const isEditing = (record: IColumnItemNew) => record.key === editingData?.key;\n\n  const edit = (record: IColumnItemNew) => {\n    if (record.key) {\n      form.setFieldsValue({ ...record });\n      setEditingData(record);\n      // 根据当前字段类型，设置编辑配置\n      databaseSupportField.columnTypes.forEach((i) => {\n        if (i.typeName === record.columnType) {\n          setEditingConfig({\n            ...i,\n            editKey: record.key!,\n          });\n        }\n      });\n    }\n  };\n\n  // 整理服务端返回的数据，构造为前端需要的数据结构\n  useEffect(() => {\n    if (tableDetails) {\n      const list =\n        tableDetails?.columnList?.map((t) => {\n          return {\n            ...t,\n            oldName: t.name,\n            key: uuidv4(),\n          };\n        }) || [];\n      setEditingConfig(null);\n      setDataSource(list);\n    }\n  }, [tableDetails]);\n\n  const columns = [\n    {\n      key: 'sort',\n      width: '40px',\n      align: 'center',\n      fixed: 'left',\n    },\n    // {\n    //   title: 'O T',\n    //   dataIndex: 'editStatus',\n    //   width: '60px',\n    //   align: 'center',\n    //   render: (text: EditColumnOperationType) => {\n    //     return text === EditColumnOperationType.Add ? (\n    //       <span style={{ color: '#52c41a' }}>新</span>\n    //     ) : (\n    //       <span style={{ color: '#f5222d' }}>原</span>\n    //     );\n    //   },\n    // },\n    {\n      title: i18n('editTable.label.columnName'),\n      dataIndex: 'name',\n      width: '160px',\n      fixed: 'left',\n      render: (text: string, record: IColumnItemNew) => {\n        const editable = isEditing(record);\n        return (\n          <div className={styles.cellContent}>\n            {editable ? (\n              <Form.Item name=\"name\" style={{ margin: 0 }}>\n                <Input autoComplete=\"off\" />\n              </Form.Item>\n            ) : (\n              <div className={styles.editableCell}>{text}</div>\n            )}\n          </div>\n        );\n      },\n    },\n    {\n      title: i18n('editTable.label.columnType'),\n      dataIndex: 'columnType',\n      width: '200px',\n      render: (text: string, record: IColumnItemNew) => {\n        const editable = isEditing(record);\n        return (\n          <div>\n            {editable ? (\n              <Form.Item name=\"columnType\" style={{ margin: 0, maxWidth: '184px' }}>\n                <Select showSearch options={databaseSupportField.columnTypes} />\n              </Form.Item>\n            ) : (\n              <div style={{ maxWidth: '184px' }} className={styles.editableCell}>\n                {text}\n              </div>\n            )}\n          </div>\n        );\n      },\n    },\n    {\n      title: i18n('editTable.label.columnSize'),\n      dataIndex: 'columnSize',\n      width: '120px',\n      render: (text: string, record: IColumnItemNew) => {\n        const editable = isEditing(record);\n        return editable ? (\n          <Form.Item name=\"columnSize\" style={{ margin: 0 }}>\n            <InputNumber disabled={!editingConfig?.supportLength} />\n          </Form.Item>\n        ) : (\n          <div className={styles.editableCell}>{text}</div>\n        );\n      },\n    },\n    {\n      title: i18n('editTable.label.nullable'),\n      dataIndex: 'nullable',\n      width: '100px',\n      render: (nullable: NullableType | null, record: IColumnItemNew) => {\n        // const editable = isEditing(record);\n        return (\n          <div>\n            <Checkbox\n              onChange={() => {\n                if (databaseType === DatabaseTypeCode.SQLITE && record.editStatus !== EditColumnOperationType.Add) {\n                  return null;\n                }\n                handelNullable(record);\n              }}\n              checked={nullable === NullableType.Null}\n              disabled={\n                editingConfig?.supportNullable === false ||\n                !!record.primaryKey ||\n                (databaseType === DatabaseTypeCode.SQLITE && record.editStatus !== EditColumnOperationType.Add)\n              }\n            />\n          </div>\n        );\n      },\n    },\n    {\n      title: i18n('editTable.label.primaryKey'),\n      dataIndex: 'primaryKey',\n      width: '50px',\n      render: (primaryKey: boolean, record: IColumnItemNew) => {\n        return (\n          <div>\n            <div\n              className={classnames(styles.keyBox, {\n                [styles.disabledKeyBox]:\n                  databaseType === DatabaseTypeCode.SQLITE && record.editStatus !== EditColumnOperationType.Add,\n              })}\n              onClick={() => {\n                if (databaseType === DatabaseTypeCode.SQLITE && record.editStatus !== EditColumnOperationType.Add) {\n                  return null;\n                }\n                handelPrimaryKey(record);\n              }}\n            >\n              {primaryKey && <Iconfont code=\"&#xe775;\" />}\n              {primaryKey && <span>{record.primaryKeyOrder}</span>}\n            </div>\n          </div>\n        );\n      },\n    },\n    {\n      title: i18n('editTable.label.comment'),\n      dataIndex: 'comment',\n      render: (text: string, record: IColumnItemNew) => {\n        const editable = isEditing(record);\n        return editable ? (\n          <Form.Item name=\"comment\" style={{ margin: 0 }}>\n            <Input autoComplete=\"off\" disabled={!editingConfig?.supportComments} />\n          </Form.Item>\n        ) : (\n          <div className={styles.editableCell}>{text}</div>\n        );\n      },\n    },\n    {\n      width: '40px',\n      render: (text: string, record: IColumnItemNew) => {\n        // sqlLite不支持删除字段，新增的字段可以删除\n        if (databaseType === DatabaseTypeCode.SQLITE && record.editStatus !== EditColumnOperationType.Add) {\n          return null;\n        }\n        return (\n          <div\n            className={styles.operationBar}\n            onClick={() => {\n              deleteData(record);\n            }}\n          >\n            <div className={styles.deleteIconBox}>\n              <Iconfont code=\"&#xe64e;\" />\n            </div>\n          </div>\n        );\n      },\n    },\n  ];\n\n  const handelPrimaryKey = (_data: IColumnItemNew) => {\n    const newData = dataSource.map((item) => {\n      let primaryKeyOrder: null | number = item.primaryKeyOrder;\n\n      // 取消主键if\n      if (_data.primaryKey) {\n        // 如果取消的时当前的字段，主键顺序为null\n        if (_data.key === item.key) {\n          primaryKeyOrder = null;\n        } else {\n          // 如果当前字段是主键，取消主键的时候，比当前字段顺序大的字段顺序-1\n          if (_data.primaryKeyOrder && item.primaryKeyOrder && item.primaryKeyOrder >= _data.primaryKeyOrder) {\n            primaryKeyOrder = item.primaryKeyOrder - 1;\n          }\n        }\n      } else {\n        // 增加主键if\n        // 增加主键的时候，主键顺序为当前表的最大主键顺序+1\n        if (_data.key === item.key) {\n          primaryKeyOrder =\n            Math.max(\n              ...dataSource.map((i) => {\n                return i.primaryKeyOrder || 0;\n              }),\n            ) + 1;\n        }\n        // 对于当前字段之前的字段，主键顺序不变\n      }\n\n      if (item.key === _data?.key) {\n        // 判断当前数据是新增的数据还是编辑后的数据\n        let editStatus = item.editStatus;\n        if (editStatus !== EditColumnOperationType.Add) {\n          editStatus = EditColumnOperationType.Modify;\n        }\n\n        const editingDataItem = {\n          ...item,\n          primaryKey: !item.primaryKey,\n          primaryKeyOrder,\n          nullable: !item.primaryKey ? NullableType.NotNull : item.nullable,\n          editStatus,\n        };\n        return editingDataItem;\n      }\n\n      return {\n        ...item,\n        primaryKeyOrder,\n      };\n    });\n    setDataSource(newData);\n  };\n\n  const handelNullable = (_data: IColumnItemNew) => {\n    const newData = dataSource.map((item) => {\n      if (item.key === _data?.key) {\n        // 判断当前数据是新增的数据还是编辑后的数据\n        let editStatus = item.editStatus;\n        if (editStatus !== EditColumnOperationType.Add) {\n          editStatus = EditColumnOperationType.Modify;\n        }\n        const editingDataItem = {\n          ...item,\n          nullable: !item.nullable ? NullableType.Null : NullableType.NotNull,\n          editStatus,\n        };\n        return editingDataItem;\n      }\n      return item;\n    });\n    setDataSource(newData);\n  };\n\n  const onDragEnd = ({ active, over }: DragEndEvent) => {\n    if (active.id !== over?.id) {\n      setDataSource((previous) => {\n        const activeIndex = previous.findIndex((i) => i.key === active.id);\n        const overIndex = previous.findIndex((i) => i.key === over?.id);\n        return arrayMove(previous, activeIndex, overIndex);\n      });\n    }\n  };\n\n  const handleFieldsChange = (field: any) => {\n    let { value } = field[0];\n    const { name: nameList } = field[0];\n    const name = nameList[0];\n    if (name === 'nullable') {\n      value = value ? NullableType.Null : NullableType.NotNull;\n    }\n\n    const newData = dataSource.map((item) => {\n      if (item.key === editingData?.key) {\n        // 判断当前数据是新增的数据还是编辑后的数据\n        let editStatus = item.editStatus;\n        if (editStatus !== EditColumnOperationType.Add) {\n          editStatus = EditColumnOperationType.Modify;\n        }\n        const editingDataItem = {\n          ...item,\n          [name]: value,\n          editStatus,\n        };\n\n        if (name === 'columnType') {\n          // 根据当前字段类型，设置编辑配置\n          databaseSupportField.columnTypes.forEach((i) => {\n            if (i.typeName === value) {\n              setEditingConfig({\n                ...editingConfig!,\n                ...i,\n              });\n            }\n          });\n          // 特殊处理VARCHAR的默认长度 为255\n          if (value === 'VARCHAR' && editingDataItem.columnSize === null) {\n            editingDataItem.columnSize = 255;\n            form.setFieldsValue({\n              columnSize: 255,\n            });\n          }\n        }\n        return editingDataItem;\n      }\n      return item;\n    });\n    setDataSource(newData);\n  };\n\n  const addData = () => {\n    const newData = {\n      ...createInitialData(),\n    };\n    setDataSource([...dataSource, newData]);\n    edit(newData);\n    setTimeout(() => {\n      tableRef.current?.scrollTo(0, tableRef.current?.scrollHeight + 100);\n    }, 0);\n  };\n\n  const deleteData = (record) => {\n    let list: any = [];\n    if (record?.editStatus === EditColumnOperationType.Add) {\n      list = dataSource.filter((i) => i.key !== record?.key);\n    } else {\n      list = dataSource.map((i) => {\n        if (i.key === record?.key) {\n          setEditingData(null);\n          setEditingConfig(null);\n          return {\n            ...i,\n            editStatus: EditColumnOperationType.Delete,\n          };\n        }\n        return i;\n      });\n    }\n    setDataSource(list);\n  };\n\n  function getColumnListInfo(): IColumnItemNew[] {\n    return dataSource.map((i) => {\n      const data = {\n        ...i,\n        tableName: tableDetails?.name,\n        databaseName,\n        schemaName: schemaName || null,\n      };\n      delete data.key;\n      return data;\n    });\n  }\n\n  useImperativeHandle(ref, () => ({\n    getColumnListInfo,\n  }));\n\n  const renderOtherInfoForm = () => {\n    const labelCol = {\n      style: { width: 100 },\n    };\n\n    return (\n      <>\n        {editingConfig?.supportAutoIncrement && (\n          <Form.Item\n            labelCol={labelCol}\n            label={i18n('editTable.label.autoIncrement')}\n            name=\"autoIncrement\"\n            valuePropName=\"checked\"\n          >\n            <Checkbox />\n          </Form.Item>\n        )}\n        {databaseType === DatabaseTypeCode.SQLSERVER && (\n          <Form.Item labelCol={labelCol} label={i18n('editTable.label.sparse')} name=\"sparse\" valuePropName=\"checked\">\n            <Checkbox />\n          </Form.Item>\n        )}\n        {editingConfig?.supportDefaultValue && (\n          <Form.Item labelCol={labelCol} label={i18n('editTable.label.defaultValue')} name=\"defaultValue\">\n            <CustomSelect options={databaseSupportField.defaultValues} />\n          </Form.Item>\n        )}\n        {editingConfig?.supportCharset && (\n          <Form.Item labelCol={labelCol} label={i18n('editTable.label.characterSet')} name=\"charSetName\">\n            <CustomSelect options={databaseSupportField.charsets} />\n          </Form.Item>\n        )}\n        {editingConfig?.supportCollation && (\n          <Form.Item labelCol={labelCol} label={i18n('editTable.label.collation')} name=\"collationName\">\n            <CustomSelect options={databaseSupportField.collations} />\n          </Form.Item>\n        )}\n        {editingConfig?.supportScale && (\n          <Form.Item labelCol={labelCol} label={i18n('editTable.label.decimalPoint')} name=\"decimalDigits\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n        )}\n        {editingConfig?.supportUnit && (\n          <Form.Item labelCol={labelCol} label={i18n('editTable.label.unit')} name=\"unit\">\n            <Select style={{ width: '100%' }}>\n              {['CHAR', 'BYTE'].map((i) => (\n                <Select.Option key={i} value={i}>\n                  {i}\n                </Select.Option>\n              ))}\n            </Select>\n          </Form.Item>\n        )}\n        {editingConfig?.supportValue && (\n          <Form.Item labelCol={labelCol} label={i18n('editTable.label.value')} name=\"value\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n        )}\n      </>\n    );\n  };\n\n  const onRow = (record: any) => {\n    return {\n      onClick: () => {\n        // sqlLite不支持修改字段,新增的字段可以修改\n        if (databaseType === DatabaseTypeCode.SQLITE && record.editStatus !== EditColumnOperationType.Add) {\n          return;\n        }\n        if (editingData?.key !== record.key) {\n          edit(record);\n        }\n      },\n    };\n  };\n\n  return (\n    <div className={styles.columnList}>\n      {/* <div className={styles.columnListHeader}>\n        <Button onClick={addData}>{i18n('editTable.button.add')}</Button>\n        <Button onClick={deleteData}>{i18n('editTable.button.delete')}</Button>\n        <Button onClick={moveData.bind(null, 'up')}>{i18n('editTable.button.up')}</Button>\n        <Button onClick={moveData.bind(null, 'down')}>{i18n('editTable.button.down')}</Button>\n      </div> */}\n      <Form className={styles.formBox} form={form} onFieldsChange={handleFieldsChange}>\n        <div className={styles.tableBox}>\n          <DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>\n            <SortableContext items={dataSource.map((i) => i.key!)} strategy={verticalListSortingStrategy}>\n              <Table\n                ref={tableRef}\n                components={{\n                  body: {\n                    row: Row,\n                  },\n                }}\n                style={{\n                  maxHeight: '100%',\n                  overflow: 'auto',\n                }}\n                sticky\n                onRow={onRow}\n                pagination={false}\n                rowKey=\"key\"\n                columns={columns as any}\n                scroll={{ x: '100%' }}\n                dataSource={dataSource.filter((i) => i.editStatus !== EditColumnOperationType.Delete)}\n              />\n            </SortableContext>\n          </DndContext>\n          <div onClick={addData} className={styles.addColumnButton}>\n            <Iconfont code=\"&#xe631;\" />\n            {i18n('editTable.button.addColumn')}\n          </div>\n        </div>\n\n        <div className={styles.otherInfo}>\n          <div className={styles.otherInfoFormBox}>{renderOtherInfoForm()}</div>\n        </div>\n      </Form>\n    </div>\n  );\n});\n\nexport default ColumnList;\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/IncludeCol/index.less",
    "content": "@import '../../../styles/var.less';\n\n.includeCol {\n  max-height: 40vh;\n  display: flex;\n  flex-direction: column;\n  overflow: hidden;\n}\n\n.ant-input-number {\n  width: 100%;\n}\n\n.formBox {\n  flex: 1;\n  height: 0;\n  overflow: hidden;\n  display: flex;\n}\n\n.indexListHeader {\n  flex-shrink: 0;\n  margin: 0px -10px 10px;\n  button {\n    margin: 0px 10px;\n  }\n}\n\n.editableCell {\n  height: 28px;\n  line-height: 26px;\n  padding: 0px 7px;\n  box-sizing: border-box;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  .f-single-line();\n  width: 100%;\n  cursor: pointer;\n  &:hover {\n    border: 1px solid var(--color-border);\n  }\n}\n\n.operationBar {\n  display: flex;\n  justify-content: end;\n  .deleteIconBox {\n    height: 26px;\n    width: 26px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n    &:hover {\n      color: var(--color-primary);\n    }\n  }\n}\n\n.includeCol {\n  :global {\n    .ant-table-body {\n      border: 1px solid var(--color-border);\n      border-top: 0px;\n      border-bottom: 0px;\n    }\n    .ant-table-header {\n      border: 1px solid var(--color-border);\n      border-bottom: 0px;\n    }\n    .ant-table {\n      border-radius: 10px;\n      border-bottom: 0px;\n    }\n    .ant-table-wrapper .ant-table-tbody > tr > td {\n      // border: 0px;\n      padding: 4px 2px;\n    }\n    .ant-table-wrapper .ant-table-thead > tr > th {\n      padding: 8px 4px;\n      &::before {\n        display: none;\n      }\n    }\n    .ant-table-wrapper .ant-table-thead > tr > td {\n      &::before {\n        display: none;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/IncludeCol/index.tsx",
    "content": "/**\n * 这个组件只负责拿到用户选择的表名\n *  */\nimport React, {\n  useMemo,\n  useState,\n  useRef,\n  useContext,\n  useEffect,\n  forwardRef,\n  ForwardedRef,\n  useImperativeHandle,\n} from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Table, Form, Select, Button } from 'antd';\nimport { v4 as uuidv4 } from 'uuid';\nimport { Context } from '../index';\nimport { IColumnItemNew, IIndexIncludeColumnItem } from '@/typings';\nimport { DatabaseTypeCode } from '@/constants';\nimport i18n from '@/i18n';\nimport lodash from 'lodash';\nimport Iconfont from '@/components/Iconfont';\n\ninterface IProps {\n  includedColumnList: IIndexIncludeColumnItem[];\n}\n\nconst createInitialData = () => {\n  return {\n    key: uuidv4(),\n    ascOrDesc: null, // 升序还是降序\n    cardinality: null, // 基数\n    collation: null, // 排序规则\n    columnName: null, // 列名\n    comment: null, // 注释\n    filterCondition: null, // 过滤条件\n    indexName: null, // 索引名\n    indexQualifier: null, // 索引限定符\n    nonUnique: null, // 是否唯一\n    ordinalPosition: null, // 位置\n    schemaName: null, // 模式名\n    type: null, // 类型\n    pages: null, // 页数\n\n    databaseName: null, // 数据库名\n    tableName: null, // 表名\n  };\n};\n\nexport interface IIncludeColRef {\n  getIncludeColInfo: () => IIndexIncludeColumnItem[];\n}\n\nconst IncludeCol = forwardRef((props: IProps, ref: ForwardedRef<IIncludeColRef>) => {\n  const { includedColumnList } = props;\n  const { columnListRef, databaseType } = useContext(Context);\n  const [dataSource, setDataSource] = useState<IIndexIncludeColumnItem[]>([createInitialData()]);\n  const [form] = Form.useForm();\n  const [editingKey, setEditingKey] = useState<string | null>(null);\n  const isEditing = (record: IIndexIncludeColumnItem) => record.key === editingKey;\n  const tableRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    if (includedColumnList.length) {\n      setDataSource(\n        includedColumnList.map((t) => {\n          return {\n            ...t,\n            key: uuidv4(),\n          };\n        }),\n      );\n    }\n  }, [includedColumnList]);\n\n  const columnList: IColumnItemNew[] = useMemo(() => {\n    const columnListInfo = columnListRef.current?.getColumnListInfo()?.filter((i) => i.name !== null);\n    return columnListInfo || [];\n  }, []);\n\n  const edit = (record: any) => {\n    form.setFieldsValue({ ...record });\n    setEditingKey(record.key || null);\n  };\n\n  const addData = () => {\n    const newData = createInitialData();\n    setDataSource([...dataSource, newData]);\n    edit(newData);\n    setTimeout(() => {\n      tableRef.current?.scrollTo(0, tableRef.current?.scrollHeight + 100);\n    }, 0);\n  };\n\n  const deleteData = (record) => {\n    setDataSource(dataSource.filter((i) => i.key !== record.key));\n  };\n\n  const columns = [\n    {\n      title: i18n('editTable.label.index'),\n      dataIndex: 'index',\n      width: '50px',\n      align: 'center',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        return dataSource.findIndex((i) => i.key === record.key) + 1;\n      },\n    },\n    {\n      title: i18n('editTable.label.columnName'),\n      dataIndex: 'columnName',\n      // width: '45%',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        const editable = isEditing(record);\n        return editable ? (\n          <Form.Item name=\"columnName\" style={{ margin: 0 }}>\n            <Select options={columnList.map((i) => ({ label: i.name, value: i.name }))} />\n          </Form.Item>\n        ) : (\n          <div className={styles.editableCell} onClick={() => edit(record)}>\n            {text}\n          </div>\n        );\n      },\n    },\n    {\n      title: i18n('editTable.label.order'),\n      dataIndex: 'ascOrDesc',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        const editable = isEditing(record);\n        return editable ? (\n          <Form.Item name=\"ascOrDesc\" style={{ margin: 0 }}>\n            <Select\n              options={[\n                { label: 'ASC', value: 'ASC' },\n                { label: 'DESC', value: 'DESC' },\n              ]}\n            />\n          </Form.Item>\n        ) : (\n          <div className={styles.editableCell} onClick={() => edit(record)}>\n            {text}\n          </div>\n        );\n      },\n    },\n    {\n      width: '40px',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        return (\n          <div\n            className={styles.operationBar}\n            onClick={() => {\n              deleteData(record);\n            }}\n          >\n            <div className={styles.deleteIconBox}>\n              <Iconfont code=\"&#xe64e;\" />\n            </div>\n          </div>\n        );\n      },\n    },\n\n    // {\n    //   title: i18n('editTable.label.prefixLength'),\n    //   dataIndex: 'prefixLength',\n    //   width: '45%',\n    //   render: (text: string, record: IIndexIncludeColumnItem) => {\n    //     const editable = isEditing(record);\n    //     return editable ? (\n    //       <Form.Item name=\"prefixLength\" style={{ margin: 0 }}>\n    //         <InputNumber style={{ width: '100%' }} />\n    //       </Form.Item>\n    //     ) : (\n    //       <div className={styles.editableCell} onClick={() => edit(record)}>\n    //         {text}\n    //       </div>\n    //     );\n    //   },\n    // },\n  ];\n  // sqlLite 添加排序规则\n  if (databaseType === DatabaseTypeCode.SQLITE) {\n    columns.splice(2, 0, {\n      title: i18n('editTable.label.collation'),\n      dataIndex: 'collation',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        const editable = isEditing(record);\n        return editable ? (\n          <Form.Item name=\"collation\" style={{ margin: 0 }}>\n            <Select\n              options={[\n                { label: 'BINARY', value: 'BINARY' },\n                { label: 'NOCASE', value: 'NOCASE' },\n                { label: 'RTRIM', value: 'RTRIM' },\n              ]}\n            />\n          </Form.Item>\n        ) : (\n          <div className={styles.editableCell} onClick={() => edit(record)}>\n            {text}\n          </div>\n        );\n      },\n    });\n  }\n\n  const handleFieldsChange = (field: any) => {\n    const { value } = field[0];\n    const { name: nameList } = field[0];\n    const name = nameList[0];\n    const newData = dataSource.map((item) => {\n      if (item.key === editingKey) {\n        return {\n          ...item,\n          [name]: value,\n        };\n      }\n      return item;\n    });\n    setDataSource(newData);\n  };\n\n  const getIncludeColInfo = (): IIndexIncludeColumnItem[] => {\n    return dataSource\n      .map((t) => {\n        return lodash.omit(t, 'key');\n      })\n      .filter((t) => t.columnName);\n  };\n\n  useImperativeHandle(ref, () => ({\n    getIncludeColInfo,\n  }));\n\n  return (\n    <div className={classnames(styles.includeCol)}>\n      <div className={styles.indexListHeader}>\n        <Button onClick={addData}>{i18n('editTable.button.add')}</Button>\n        {/* <Button onClick={deleteData}>{i18n('editTable.button.delete')}</Button> */}\n      </div>\n      <Form className={styles.formBox} form={form} onFieldsChange={handleFieldsChange}>\n        <Table\n          ref={tableRef}\n          style={{\n            maxHeight: '100%',\n            overflow: 'auto',\n          }}\n          sticky\n          pagination={false}\n          rowKey=\"key\"\n          columns={columns as any}\n          dataSource={dataSource}\n        />\n      </Form>\n    </div>\n  );\n});\n\nexport default IncludeCol;\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/IndexList/index.less",
    "content": "@import '../../../styles/var.less';\n\n.indexList {\n  height: 100%;\n  padding: 10px;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n}\n\n.indexListHeader {\n  margin: 0px -5px 10px;\n  button {\n    margin: 0px 5px;\n  }\n}\n\n.formBox {\n  height: 0px;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n}\n\n.tableBox {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  overflow: hidden;\n  border-radius: 8px 8px 0px 0px;\n  overflow: hidden;\n}\n\n.addColumnButton {\n  flex-shrink: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border: 1px dashed var(--color-border);\n  line-height: 30px;\n  margin-top: 10px;\n  color: var(--color-text-secondary);\n  cursor: pointer;\n  i {\n    margin-right: 5px;\n  }\n  &:hover {\n    color: var(--color-primary);\n    border-color: var(--color-primary);\n  }\n}\n\n.editableCell {\n  height: 28px;\n  line-height: 26px;\n  padding: 0px 7px;\n  box-sizing: border-box;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  .f-single-line();\n  width: 100%;\n  cursor: pointer;\n  &:hover {\n    border: 1px solid var(--color-border);\n  }\n}\n\n.columnListCell {\n  span {\n    margin-right: 8px;\n    color: var(--color-primary);\n    cursor: pointer;\n    &:hover {\n      color: var(--color-primary-hover);\n    }\n  }\n}\n\n.operationBar {\n  display: flex;\n  justify-content: end;\n  .deleteIconBox {\n    height: 26px;\n    width: 26px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n    &:hover {\n      color: var(--color-primary);\n    }\n  }\n}\n\n.indexList {\n  :global {\n    .ant-table-body {\n      border: 1px solid var(--color-border);\n      border-top: 0px;\n      border-bottom: 0px;\n    }\n    .ant-table-header {\n      border: 1px solid var(--color-border);\n      border-bottom: 0px;\n    }\n    .ant-table {\n      border-radius: 10px;\n      border-bottom: 0px;\n    }\n    .ant-table-wrapper .ant-table-tbody > tr > td {\n      // border: 0px;\n      padding: 4px 2px;\n    }\n    .ant-table-wrapper .ant-table-thead > tr > th {\n      padding: 8px 4px;\n      &::before {\n        display: none;\n      }\n    }\n    .ant-table-wrapper .ant-table-thead > tr > td {\n      &::before {\n        display: none;\n      }\n    }\n    // antd无法设置最小宽度，所以在这里设置最小列宽为100px\n    colgroup col:nth-last-child(2) {\n      min-width: 100px;\n    }\n    colgroup col:nth-last-child(3) {\n      min-width: 100px;\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/IndexList/index.tsx",
    "content": "import React, {\n  useState,\n  forwardRef,\n  ForwardedRef,\n  useImperativeHandle,\n  useContext,\n  useRef,\n  useMemo,\n  useEffect,\n} from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { MenuOutlined } from '@ant-design/icons';\nimport { type DragEndEvent, DndContext } from '@dnd-kit/core';\nimport { restrictToVerticalAxis } from '@dnd-kit/modifiers';\nimport { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';\nimport { CSS } from '@dnd-kit/utilities';\nimport { Table, Input, Form, Select, Modal } from 'antd';\nimport { v4 as uuidv4 } from 'uuid';\nimport IncludeCol, { IIncludeColRef } from '../IncludeCol';\nimport { IIndexItem, IIndexIncludeColumnItem } from '@/typings';\nimport { EditColumnOperationType, DatabaseTypeCode } from '@/constants';\nimport Iconfont from '@/components/Iconfont';\nimport { Context } from '../index';\nimport i18n from '@/i18n';\nimport lodash from 'lodash';\n\ninterface IProps {}\n\nexport type IIndexListInfo = IIndexItem[];\n\nexport interface IIndexListRef {\n  getIndexListInfo: () => IIndexListInfo;\n}\n\nconst createInitialData = (): IIndexItem => {\n  return {\n    key: uuidv4(),\n    columnList: [],\n    name: '',\n    type: null,\n    comment: null,\n    editStatus: EditColumnOperationType.Add,\n  };\n};\n\ninterface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {\n  'data-row-key': string;\n}\n\nconst Row = ({ children, ...props }: RowProps) => {\n  const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({\n    id: props['data-row-key'],\n  });\n\n  const style: React.CSSProperties = {\n    ...props.style,\n    transform: CSS.Transform.toString(transform && { ...transform, scaleY: 1 }),\n    transition,\n    ...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),\n  };\n\n  return (\n    <tr {...props} ref={setNodeRef} style={style} {...attributes}>\n      {React.Children.map(children, (child) => {\n        if ((child as React.ReactElement).key === 'sort') {\n          return React.cloneElement(child as React.ReactElement, {\n            children: (\n              <MenuOutlined ref={setActivatorNodeRef} style={{ touchAction: 'none', cursor: 'move' }} {...listeners} />\n            ),\n          });\n        }\n        return child;\n      })}\n    </tr>\n  );\n};\n\nconst IndexList = forwardRef((props: IProps, ref: ForwardedRef<IIndexListRef>) => {\n  const { databaseSupportField, tableDetails, databaseType } = useContext(Context);\n  const [dataSource, setDataSource] = useState<IIndexItem[]>([createInitialData()]);\n  const [oldDataSource, setOldDataSource] = useState<IIndexItem[]>([]); // 用于记录上一次的数据，用于对比是否有变化\n  const [form] = Form.useForm();\n  const [editingData, setEditingData] = useState<IIndexItem | null>(null);\n  const [includeColModalOpen, setIncludeColModalOpen] = useState(false);\n  const includeColRef = useRef<IIncludeColRef>(null);\n  const tableRef = useRef<HTMLDivElement>(null);\n\n  const isEditing = (record: IIndexItem) => record.key === editingData?.key;\n\n  const edit = (record: IIndexItem) => {\n    form.setFieldsValue({ ...record });\n    if (record.key !== editingData?.key) {\n      setEditingData(record || null);\n    }\n  };\n\n  useEffect(() => {\n    const data = tableDetails.indexList?.map((i) => {\n      const key = uuidv4();\n      return {\n        ...i,\n        oldName: i.name,\n        key,\n      };\n    });\n\n    setOldDataSource(lodash.cloneDeep(data) || []);\n    setDataSource(lodash.cloneDeep(data) || []);\n  }, [tableDetails]);\n\n  const addData = () => {\n    const newData = createInitialData();\n    setDataSource([...dataSource, newData]);\n    edit(newData);\n    setTimeout(() => {\n      tableRef.current?.scrollTo(0, tableRef.current?.scrollHeight + 100);\n    }, 0);\n  };\n\n  const deleteData = (record) => {\n    const newList: any[] = dataSource\n      ?.map((i) => {\n        if (i.key === record?.key) {\n          setEditingData(null);\n          if (i.editStatus === EditColumnOperationType.Add) {\n            return null;\n          }\n          return {\n            ...i,\n            editStatus: EditColumnOperationType.Delete,\n          };\n        }\n        return i;\n      })\n      ?.filter((i) => i);\n    setDataSource(newList || []);\n  };\n\n  const handleFieldsChange = (field: any) => {\n    let { value } = field[0];\n    const { name: nameList } = field[0];\n    const name = nameList[0];\n    if (name === 'nullable') {\n      value = value ? 1 : 0;\n    }\n    const newData = dataSource.map((item) => {\n      if (item.key === editingData?.key) {\n        let editStatus = item.editStatus;\n        if (editStatus !== EditColumnOperationType.Add) {\n          editStatus = EditColumnOperationType.Modify;\n        }\n        return {\n          ...item,\n          [name]: value,\n          editStatus,\n        };\n      }\n      return item;\n    });\n    setDataSource(newData);\n  };\n\n  const onDragEnd = ({ active, over }: DragEndEvent) => {\n    if (active.id !== over?.id) {\n      setDataSource((previous) => {\n        const activeIndex = previous.findIndex((i) => i.key === active.id);\n        const overIndex = previous.findIndex((i) => i.key === over?.id);\n        return arrayMove(previous, activeIndex, overIndex);\n      });\n    }\n  };\n\n  function getIndexListInfo(): IIndexListInfo {\n    return dataSource.map((i) => {\n      return lodash.omit(i, 'key');\n    });\n  }\n\n  useImperativeHandle(ref, () => ({\n    getIndexListInfo,\n  }));\n\n  const columns = useMemo(() => {\n    const _columns = [\n      {\n        key: 'sort',\n        width: '40px',\n        align: 'center',\n        fixed: 'left',\n      },\n      // {\n      //   title: i18n('editTable.label.index'),\n      //   width: '70px',\n      //   align: 'center',\n      //   render: (text: string, record: IIndexItem) => {\n      //     return dataSource.findIndex((i) => i.key === record.key) + 1;\n      //   },\n      // },\n      {\n        title: i18n('editTable.label.indexName'),\n        dataIndex: 'name',\n        width: '180px',\n        fixed: 'left',\n        render: (text: string, record: IIndexItem) => {\n          const editable = isEditing(record);\n          return editable ? (\n            <Form.Item name=\"name\" style={{ margin: 0 }}>\n              <Input autoComplete=\"off\" />\n            </Form.Item>\n          ) : (\n            <div className={styles.editableCell}>{text}</div>\n          );\n        },\n      },\n      {\n        title: i18n('editTable.label.indexType'),\n        dataIndex: 'type',\n        width: '100px',\n        render: (text: string, record: IIndexItem) => {\n          const editable = isEditing(record);\n          return editable ? (\n            <Form.Item name=\"type\" style={{ margin: 0 }}>\n              <Select style={{ width: '100%' }}>\n                {databaseSupportField?.indexTypes?.map((i) => (\n                  <Select.Option key={i.value} value={i.value}>\n                    {i.label}\n                  </Select.Option>\n                ))}\n              </Select>\n            </Form.Item>\n          ) : (\n            <div className={styles.editableCell}>{text}</div>\n          );\n        },\n      },\n      {\n        title: i18n('editTable.label.includeColumn'),\n        dataIndex: 'columnList',\n        render: (columnList: IIndexIncludeColumnItem[], record: IIndexItem) => {\n          const editable = isEditing(record);\n          const text = columnList\n            ?.map((t) => {\n              return `${t.columnName}`;\n            })\n            .join(',');\n          return editable ? (\n            <div className={styles.columnListCell}>\n              <span\n                onClick={() => {\n                  setIncludeColModalOpen(true);\n                }}\n              >\n                {i18n('common.button.edit')}\n              </span>\n              {text}\n            </div>\n          ) : (\n            <div className={styles.editableCell}>{text}</div>\n          );\n        },\n      },\n      {\n        title: i18n('editTable.label.comment'),\n        dataIndex: 'comment',\n        render: (text: string, record: IIndexItem) => {\n          const editable = isEditing(record);\n          return editable ? (\n            <Form.Item name=\"comment\" style={{ margin: 0 }}>\n              <Input autoComplete=\"off\" />\n            </Form.Item>\n          ) : (\n            <div className={styles.editableCell} onClick={() => edit(record)}>\n              {text}\n            </div>\n          );\n        },\n      },\n      {\n        width: '40px',\n        render: (text: string, record: IIndexItem) => {\n          return (\n            <div\n              className={styles.operationBar}\n              onClick={() => {\n                deleteData(record);\n              }}\n            >\n              <div className={styles.deleteIconBox}>\n                <Iconfont code=\"&#xe64e;\" />\n              </div>\n            </div>\n          );\n        },\n      },\n    ];\n    if (databaseType === DatabaseTypeCode.MYSQL) {\n      _columns.splice(3, 0, {\n        title: i18n('editTable.label.indexMethod'),\n        dataIndex: 'method',\n        width: '120px',\n        render: (text: string, record: IIndexItem) => {\n          const editable = isEditing(record);\n          return editable ? (\n            <Form.Item name=\"method\" style={{ margin: 0 }}>\n              <Select style={{ width: '100%' }}>\n                {['HASH', 'BTREE'].map((i) => (\n                  <Select.Option key={i} value={i}>\n                    {i}\n                  </Select.Option>\n                ))}\n              </Select>\n            </Form.Item>\n          ) : (\n            <div className={styles.editableCell}>{text}</div>\n          );\n        },\n      });\n    }\n    if (databaseType === DatabaseTypeCode.ORACLE) {\n      _columns.splice(-2, 1);\n    }\n    return _columns;\n    // TODO: isEditing 每次都会变所以这里是无意义的，后续在优化一下\n  }, [isEditing]);\n\n  const getIncludeColInfo = () => {\n    setDataSource(\n      dataSource.map((i) => {\n        const columnList = includeColRef.current?.getIncludeColInfo();\n        // 对比新老的IncludeColInfo有没有变化\n        if (i.key === editingData?.key && columnList) {\n          i.columnList = columnList;\n          oldDataSource.map((old) => {\n            if (old.key === editingData?.key) {\n              if (!lodash.isEqual(old.columnList, columnList)) {\n                i.editStatus = EditColumnOperationType.Modify;\n              } else {\n                i.editStatus = null;\n              }\n            }\n          });\n        }\n        return i;\n      }),\n    );\n\n    setIncludeColModalOpen(false);\n  };\n\n  const onRow = (record: any) => {\n    return {\n      onClick: () => {\n        if (editingData?.key !== record.key) {\n          edit(record);\n        }\n      },\n    };\n  };\n\n  const indexIncludedColumnList: IIndexIncludeColumnItem[] = useMemo(() => {\n    let list: IIndexIncludeColumnItem[] = [];\n    dataSource.forEach((i) => {\n      if (i.key === editingData?.key) {\n        list = i.columnList || [];\n      }\n    });\n    return list;\n  }, [includeColModalOpen]);\n\n  return (\n    <div className={classnames(styles.indexList)}>\n      {/* <div className={styles.indexListHeader}>\n        <Button onClick={addData}>{i18n('editTable.button.add')}</Button>\n        <Button onClick={deleteData}>{i18n('editTable.button.delete')}</Button>\n        <Button onClick={moveData.bind(null, 'up')}>上移</Button>\n        <Button onClick={moveData.bind(null, 'down')}>下移</Button>\n      </div> */}\n      <Form className={styles.formBox} form={form} onFieldsChange={handleFieldsChange}>\n        <div className={styles.tableBox}>\n          <DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>\n            <SortableContext items={dataSource.map((i) => i.key!)} strategy={verticalListSortingStrategy}>\n              <Table\n                ref={tableRef}\n                components={{\n                  body: {\n                    row: Row,\n                  },\n                }}\n                style={{\n                  maxHeight: '100%',\n                  overflow: 'auto',\n                }}\n                sticky\n                onRow={onRow}\n                pagination={false}\n                rowKey=\"key\"\n                columns={columns as any}\n                scroll={{ x: '100%' }}\n                dataSource={dataSource.filter((i) => i.editStatus !== EditColumnOperationType.Delete)}\n              />\n            </SortableContext>\n          </DndContext>\n          <div onClick={addData} className={styles.addColumnButton}>\n            <Iconfont code=\"&#xe631;\" />\n            {i18n('editTable.button.addIndex')}\n          </div>\n        </div>\n      </Form>\n      <Modal\n        className={styles.includeColModal}\n        open={includeColModalOpen}\n        width={460}\n        title={i18n('editTable.label.includeColumn')}\n        onOk={getIncludeColInfo}\n        onCancel={() => {\n          setIncludeColModalOpen(false);\n        }}\n        maskClosable={false}\n        destroyOnClose={true}\n      >\n        <IncludeCol includedColumnList={indexIncludedColumnList} ref={includeColRef} />\n      </Modal>\n    </div>\n  );\n});\n\nexport default IndexList;\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/RealTimeSQL/index.less",
    "content": "@import '../../../styles/var.less';\n\n.realTimeSQL {\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/RealTimeSQL/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\n\ninterface IProps {\n  className?: string;\n}\n\nexport default memo<IProps>((props) => {\n  const { className } = props;\n  return (\n    <div className={classnames(styles.realTimeSQL, className)}>\n      <div className={styles.title}>实时 SQL</div>\n      <div className={styles.content}>实时 SQL</div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n.header {\n  // position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin: 10px 10px 0px 10px;\n  box-sizing: border-box;\n  overflow: hidden;\n}\n\n.saveButton {\n  // position: absolute;\n  // right: 20px;\n}\n\n.tabList {\n  display: flex;\n  border-bottom: 0;\n  font-size: 14px;\n  border-radius: 6px;\n  background-color: var(--color-bg-subtle);\n  position: relative;\n  &::after {\n    position: absolute;\n    left: calc(82px * var(--i));\n    content: '';\n    height: 26px;\n    width: 82px;\n    border-radius: 4px;\n    color: var(--color-primary);\n    background-color: var(--color-primary-bg);\n    z-index: 0;\n    transition: left 0.2s ease-out;\n  }\n}\n\n.tabItem {\n  position: relative;\n  z-index: 1;\n  height: 26px;\n  width: 82px;\n  text-align: center;\n  line-height: 26px;\n  border-radius: 4px;\n  cursor: pointer;\n  font-size: 13px;\n  user-select: none;\n  &:hover {\n    color: var(--color-primary);\n  }\n}\n\n.currentTab {\n  color: var(--color-primary);\n}\n\n.main {\n  flex: 1;\n  position: relative;\n  overflow: hidden;\n}\n\n.tab {\n  z-index: 2;\n  position: absolute;\n  left: 0px;\n  top: 0px;\n  width: 100%;\n  height: 100%;\n}\n\n.hidden {\n  z-index: 1;\n  opacity: 0;\n}\n\n.monacoEditorModal {\n  display: flex;\n  height: 400px;\n  border: 1px solid var(--color-border);\n  border-radius: 4px;\n\n  .monacoEditorHeader {\n    height: 38px;\n    padding: 0px 10px;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    border-bottom: 1px solid var(--color-border);\n    .formatButton {\n      border-radius: 3px;\n      background-color: var(--color-fill-quaternary);\n      height: 24px;\n      padding: 0px 7px;\n      cursor: pointer;\n      i {\n        margin-right: 6px;\n      }\n      &:hover {\n        color: var(--color-primary);\n      }\n    }\n    .executeButton {\n      height: 24px;\n      line-height: 24px;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      i {\n        margin-right: 4px;\n      }\n    }\n  }\n  .monacoEditorContent {\n    width: 0px;\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    padding: 0px -6px;\n    border-right: 1px solid var(--color-border);\n    .monacoEditor {\n      flex: 1;\n      margin: 0px 6px;\n    }\n  }\n  .result {\n    width: 0px;\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    .resultHeader {\n      width: 100%;\n      line-height: 38px;\n      text-align: center;\n      border-bottom: 1px solid var(--color-border);\n    }\n    .resultContent {\n      flex: 1;\n      padding: 0px 10px;\n      overflow-y: auto;\n      .errorTitle {\n        display: flex;\n        align-items: center;\n        padding: 4px 10px;\n        // background-color: var(--color-bg-subtle);\n        border-radius: 8px;\n        margin-top: 10px;\n        i {\n          color: var(--color-error);\n          margin-right: 4px;\n        }\n      }\n\n      .errorMessage {\n        margin: 10px 0px;\n        padding: 4px 10px;\n        background-color: var(--color-bg-subtle);\n        border-radius: 8px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/DatabaseTableEditor/index.tsx",
    "content": "import React, { memo, useRef, useState, createContext, useEffect, useMemo } from 'react';\nimport { Button, Modal, message } from 'antd';\nimport i18n from '@/i18n';\nimport lodash from 'lodash';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport IndexList, { IIndexListRef } from './IndexList';\nimport ColumnList, { IColumnListRef } from './ColumnList';\nimport BaseInfo, { IBaseInfoRef } from './BaseInfo';\nimport sqlService, { IModifyTableSqlParams } from '@/service/sql';\nimport ExecuteSQL from '@/components/ExecuteSQL';\nimport { IEditTableInfo, IWorkspaceTab, IColumnTypes } from '@/typings';\nimport { DatabaseTypeCode, WorkspaceTabType } from '@/constants';\nimport LoadingContent from '@/components/Loading/LoadingContent';\ninterface IProps {\n  dataSourceId: number;\n  databaseName: string;\n  schemaName?: string | null;\n  tableName?: string;\n  databaseType: DatabaseTypeCode;\n  changeTabDetails: (data: IWorkspaceTab) => void;\n  tabDetails: IWorkspaceTab;\n  submitCallback: () => void;\n}\n\ninterface ITabItem {\n  index: number;\n  title: string;\n  key: string;\n  component: any; // TODO: 组件的Ts是什么\n}\n\ninterface IContext extends IProps {\n  tableDetails: IEditTableInfo;\n  baseInfoRef: React.RefObject<IBaseInfoRef>;\n  columnListRef: React.RefObject<IColumnListRef>;\n  indexListRef: React.RefObject<IIndexListRef>;\n  databaseSupportField: IDatabaseSupportField;\n}\n\nexport const Context = createContext<IContext>({} as any);\n\ninterface IOption {\n  label: string;\n  value: string | number | null;\n}\n\n// 列字段类型，select组件的options需要的数据结构\ninterface IColumnTypesOption extends IColumnTypes {\n  label: string;\n  value: string | number | null;\n}\nexport interface IDatabaseSupportField {\n  columnTypes: IColumnTypesOption[];\n  charsets: IOption[];\n  collations: IOption[];\n  indexTypes: IOption[];\n  defaultValues: IOption[];\n}\n\nexport default memo((props: IProps) => {\n  const {\n    databaseName,\n    dataSourceId,\n    tableName,\n    schemaName,\n    changeTabDetails,\n    tabDetails,\n    databaseType,\n    submitCallback,\n  } = props;\n  const [tableDetails, setTableDetails] = useState<IEditTableInfo>({} as any);\n  const [oldTableDetails, setOldTableDetails] = useState<IEditTableInfo>({} as any);\n  const [viewSqlModal, setViewSqlModal] = useState<boolean>(false);\n  const baseInfoRef = useRef<IBaseInfoRef>(null);\n  const columnListRef = useRef<IColumnListRef>(null);\n  const indexListRef = useRef<IIndexListRef>(null);\n  const [appendValue, setAppendValue] = useState<string>('');\n  const tabList = useMemo(() => {\n    return [\n      {\n        index: 0,\n        title: i18n('editTable.tab.basicInfo'),\n        key: 'basic',\n        component: <BaseInfo ref={baseInfoRef} />,\n      },\n      {\n        index: 1,\n        title: i18n('editTable.tab.columnInfo'),\n        key: 'column',\n        component: <ColumnList ref={columnListRef} />,\n      },\n      {\n        index: 2,\n        title: i18n('editTable.tab.indexInfo'),\n        key: 'index',\n        component: <IndexList ref={indexListRef} />,\n      },\n    ];\n  }, []);\n  const [currentTab, setCurrentTab] = useState<ITabItem>(tabList[0]);\n  const [databaseSupportField, setDatabaseSupportField] = useState<IDatabaseSupportField>({\n    columnTypes: [],\n    charsets: [],\n    collations: [],\n    indexTypes: [],\n    defaultValues: [],\n  });\n  const [isLoading, setIsLoading] = useState<boolean>(false);\n\n  function changeTab(item: ITabItem) {\n    setCurrentTab(item);\n  }\n\n  useEffect(() => {\n    if (tableName) {\n      getTableDetails();\n    }\n    getDatabaseFieldTypeList();\n  }, []);\n\n  // 获取数据库字段类型列表\n  const getDatabaseFieldTypeList = () => {\n    sqlService\n      .getDatabaseFieldTypeList({\n        dataSourceId,\n        databaseName,\n      })\n      .then((res) => {\n        const columnTypes =\n          res?.columnTypes?.map((i) => {\n            return {\n              ...i,\n              value: i.typeName,\n              label: i.typeName,\n            };\n          }) || [];\n\n        const charsets =\n          res?.charsets?.map((i) => {\n            return {\n              value: i.charsetName,\n              label: i.charsetName,\n            };\n          }) || [];\n\n        const collations =\n          res?.collations?.map((i) => {\n            return {\n              value: i.collationName,\n              label: i.collationName,\n            };\n          }) || [];\n\n        const indexTypes =\n          res?.indexTypes?.map((i) => {\n            return {\n              value: i.typeName,\n              label: i.typeName,\n            };\n          }) || [];\n\n        const defaultValues =\n          res?.defaultValues?.map((i) => {\n            return {\n              value: i.defaultValue,\n              label: i.defaultValue,\n            };\n          }) || [];\n\n        setDatabaseSupportField({\n          columnTypes,\n          charsets,\n          collations,\n          indexTypes,\n          defaultValues,\n        });\n      });\n  };\n\n  const getTableDetails = (myParams?: { tableNameProps?: string }) => {\n    const { tableNameProps } = myParams || {};\n    const myTableName = tableNameProps || tableName;\n    if (myTableName) {\n      const params = {\n        databaseName,\n        dataSourceId,\n        tableName: myTableName,\n        schemaName,\n        refresh: true,\n      };\n      setIsLoading(true);\n      sqlService\n        .getTableDetails(params)\n        .then((res) => {\n          const newTableDetails = lodash.cloneDeep(res);\n          setTableDetails(newTableDetails || {});\n          setOldTableDetails(res);\n        })\n        .finally(() => {\n          setIsLoading(false);\n        });\n    }\n  };\n\n  function submit() {\n    if (baseInfoRef.current && columnListRef.current && indexListRef.current) {\n      const newTable = {\n        ...oldTableDetails,\n        ...baseInfoRef.current.getBaseInfo(),\n        columnList: columnListRef.current.getColumnListInfo()!,\n        indexList: indexListRef.current.getIndexListInfo()!,\n      };\n\n      const params: IModifyTableSqlParams = {\n        databaseName,\n        dataSourceId,\n        schemaName,\n        refresh: true,\n        newTable,\n      };\n\n      if (tableName) {\n        // params.tableName = tableName;\n        params.oldTable = oldTableDetails;\n      }\n      sqlService.getModifyTableSql(params).then((res) => {\n        setViewSqlModal(true);\n        setAppendValue(res?.[0].sql);\n      });\n    }\n  }\n\n  const executeSuccessCallBack = () => {\n    setViewSqlModal(false);\n    message.success(i18n('common.text.successfulExecution'));\n    const newTableName = baseInfoRef.current?.getBaseInfo().name;\n    getTableDetails({ tableNameProps: newTableName });\n    if (!tableName) {\n      changeTabDetails({\n        ...tabDetails,\n        title: `${newTableName}`,\n        type: WorkspaceTabType.EditTable,\n        uniqueData: {\n          ...(tabDetails.uniqueData || {}),\n          tableName: newTableName,\n        },\n      });\n    }\n    // 保存成功后，刷新左侧树\n    submitCallback?.();\n  };\n\n  return (\n    <Context.Provider\n      value={{\n        ...props,\n        tableDetails,\n        baseInfoRef,\n        columnListRef,\n        indexListRef,\n        databaseSupportField,\n        databaseType,\n      }}\n    >\n      <LoadingContent coverLoading isLoading={isLoading} className={classnames(styles.box)}>\n        <div className={styles.header}>\n          <div className={styles.tabList} style={{ '--i': currentTab.index } as any}>\n            {tabList.map((item) => {\n              return (\n                <div\n                  key={item.key}\n                  onClick={changeTab.bind(null, item)}\n                  className={classnames(styles.tabItem, currentTab.key == item.key ? styles.currentTab : '')}\n                >\n                  {item.title}\n                </div>\n              );\n            })}\n          </div>\n          <div className={styles.saveButton}>\n            <Button type=\"primary\" onClick={submit}>\n              {i18n('common.button.save')}\n            </Button>\n          </div>\n        </div>\n        <div className={styles.main}>\n          {tabList.map((t) => {\n            return (\n              <div key={t.key} className={classnames(styles.tab, { [styles.hidden]: currentTab.key !== t.key })}>\n                {t.component}\n              </div>\n            );\n          })}\n        </div>\n      </LoadingContent>\n\n      <Modal\n        title={i18n('editTable.title.sqlPreview')}\n        open={!!viewSqlModal}\n        onCancel={() => {\n          setViewSqlModal(false);\n        }}\n        width=\"60vw\"\n        maskClosable={false}\n        footer={false}\n        destroyOnClose={true}\n      >\n        <ExecuteSQL\n          initSql={appendValue}\n          databaseName={databaseName}\n          dataSourceId={dataSourceId}\n          tableName={tableName}\n          schemaName={schemaName}\n          databaseType={databaseType}\n          executeSuccessCallBack={executeSuccessCallBack}\n        />\n      </Modal>\n    </Context.Provider>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/blocks/SequenceEditor/BaseInfo/index.less",
    "content": "@import '../../../styles/var.less';\n\n.baseInfo {\n  padding: 20px 10px 0px;\n  display: flex;\n  // justify-content: center;\n  height: 100%;\n}\n\n.formBox {\n  width: 50%;\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/SequenceEditor/BaseInfo/index.tsx",
    "content": "import React, { useContext, useEffect, useImperativeHandle, ForwardedRef, forwardRef, useState } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Form, Input, Switch, Select } from 'antd';\nimport { Context } from '../index';\nimport { ISequenceInfo } from '@/typings';\nimport sqlService from '@/service/sql';\nimport i18n from '@/i18n';\n\nexport interface ISequenceInfoRef {\n  getSequenceInfo: () => ISequenceInfo;\n}\n\ninterface IProps {\n  className?: string;\n}\n\nconst BaseInfo = forwardRef((props: IProps, ref: ForwardedRef<ISequenceInfoRef>) => {\n  const { className } = props;\n  const { sequenceDetails,databaseType,databaseName,dataSourceId,schemaName } = useContext(Context);\n  const [userOptions, setUserOptions] = useState<{ value: string; label: string }[]>([]);\n  const [form] = Form.useForm();\n  useEffect(() => {\n    form.setFieldsValue({\n      comment: sequenceDetails.comment,\n      relname: sequenceDetails.relname,\n      typname: sequenceDetails.typname,\n      seqcache: sequenceDetails.seqcache,\n      rolname: sequenceDetails.rolname,\n      seqstart: sequenceDetails.seqstart,\n      seqincrement: sequenceDetails.seqincrement,\n      seqmax: sequenceDetails.seqmax,\n      seqmin: sequenceDetails.seqmin,\n      seqcycle: sequenceDetails.seqcycle,\n    });\n    const params = {\n      databaseType,\n      databaseName,\n      dataSourceId,\n      schemaName,\n      refresh: true,\n    };\n    sqlService.\n      getDatabaseUserNameList(params).then((res) => {\n        setUserOptions(res.map(user => ({value: user,label: user})));\n      });\n  }, [sequenceDetails]);\n\n  function getSequenceInfo(): ISequenceInfo {\n    return form.getFieldsValue();\n  }\n\n  function onChange(checked: boolean) {\n    form.setFieldsValue({\n      seqcycle: checked,\n    });\n  }\n\n  function handleChange(value: string) {\n    form.setFieldsValue({\n      typname: value,\n    });\n  }\n  function rolnameChange(value: string) {\n    form.setFieldsValue({\n      rolname: value,\n    });\n  }\n\n  useImperativeHandle(ref, () => ({\n    getSequenceInfo,\n  }));\n\n  return (\n    <div className={classnames(className, styles.baseInfo)}>\n      <div className={styles.formBox}>\n        <Form\n          layout=\"vertical\"\n          form={form}\n          initialValues={{ remember: true }}\n          autoComplete=\"off\"\n          className={styles.form}\n        >\n          <Form.Item label={`${i18n('editSequence.label.relname')}:`} name=\"relname\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.comment')}:`} name=\"comment\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.typname')}:`} name=\"typname\">\n            <Select\n              defaultValue={sequenceDetails?.typname ?? 'INTEGER'}\n              onChange={handleChange}\n              options={[\n                { value: 'SMALLINT', label: 'SMALLINT' },\n                { value: 'BIGINT', label: 'BIGINT' },\n                { value: 'INTEGER', label: 'INTEGER' },\n              ]}\n            />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.seqcache')}:`} name=\"seqcache\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.rolname')}:`} name=\"rolname\">\n            <Select\n              defaultValue={sequenceDetails?.rolname ?? ''}\n              onChange={rolnameChange}\n              options={userOptions}\n            />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.seqstart')}:`} name=\"seqstart\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.seqincrement')}:`} name=\"seqincrement\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.seqmax')}:`} name=\"seqmax\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.seqmin')}:`} name=\"seqmin\">\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={`${i18n('editSequence.label.seqcycle')}:`}>\n            <Switch defaultChecked onChange={onChange} />\n          </Form.Item>\n        </Form>\n      </div>\n    </div>\n  );\n});\n\nexport default BaseInfo;\n"
  },
  {
    "path": "chat2db-client/src/blocks/SequenceEditor/IncludeCol/index.less",
    "content": "@import '../../../styles/var.less';\n\n.includeCol {\n  max-height: 40vh;\n  display: flex;\n  flex-direction: column;\n  overflow: hidden;\n}\n\n.ant-input-number {\n  width: 100%;\n}\n\n.formBox {\n  flex: 1;\n  height: 0;\n  overflow: hidden;\n  display: flex;\n}\n\n.indexListHeader {\n  flex-shrink: 0;\n  margin: 0px -10px 10px;\n  button {\n    margin: 0px 10px;\n  }\n}\n\n.editableCell {\n  height: 28px;\n  line-height: 26px;\n  padding: 0px 7px;\n  box-sizing: border-box;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  .f-single-line();\n  width: 100%;\n  cursor: pointer;\n  &:hover {\n    border: 1px solid var(--color-border);\n  }\n}\n\n.operationBar {\n  display: flex;\n  justify-content: end;\n  .deleteIconBox {\n    height: 26px;\n    width: 26px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n    &:hover {\n      color: var(--color-primary);\n    }\n  }\n}\n\n.includeCol {\n  :global {\n    .ant-table-body {\n      border: 1px solid var(--color-border);\n      border-top: 0px;\n      border-bottom: 0px;\n    }\n    .ant-table-header {\n      border: 1px solid var(--color-border);\n      border-bottom: 0px;\n    }\n    .ant-table {\n      border-radius: 10px;\n      border-bottom: 0px;\n    }\n    .ant-table-wrapper .ant-table-tbody > tr > td {\n      // border: 0px;\n      padding: 4px 2px;\n    }\n    .ant-table-wrapper .ant-table-thead > tr > th {\n      padding: 8px 4px;\n      &::before {\n        display: none;\n      }\n    }\n    .ant-table-wrapper .ant-table-thead > tr > td {\n      &::before {\n        display: none;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/SequenceEditor/IncludeCol/index.tsx",
    "content": "/**\n * 这个组件只负责拿到用户选择的表名\n *  */\nimport React, {\n  useMemo,\n  useState,\n  useRef,\n  useContext,\n  useEffect,\n  forwardRef,\n  ForwardedRef,\n  useImperativeHandle,\n} from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Table, Form, Select, Button } from 'antd';\nimport { v4 as uuidv4 } from 'uuid';\nimport { Context } from '../index';\nimport { IColumnItemNew, IIndexIncludeColumnItem } from '@/typings';\nimport { DatabaseTypeCode } from '@/constants';\nimport i18n from '@/i18n';\nimport lodash from 'lodash';\nimport Iconfont from '@/components/Iconfont';\n\ninterface IProps {\n  includedColumnList: IIndexIncludeColumnItem[];\n}\n\nconst createInitialData = () => {\n  return {\n    key: uuidv4(),\n    ascOrDesc: null, // 升序还是降序\n    cardinality: null, // 基数\n    collation: null, // 排序规则\n    columnName: null, // 列名\n    comment: null, // 注释\n    filterCondition: null, // 过滤条件\n    indexName: null, // 索引名\n    indexQualifier: null, // 索引限定符\n    nonUnique: null, // 是否唯一\n    ordinalPosition: null, // 位置\n    schemaName: null, // 模式名\n    type: null, // 类型\n    pages: null, // 页数\n\n    databaseName: null, // 数据库名\n    tableName: null, // 表名\n  };\n};\n\nexport interface IIncludeColRef {\n  getIncludeColInfo: () => IIndexIncludeColumnItem[];\n}\n\nconst IncludeCol = forwardRef((props: IProps, ref: ForwardedRef<IIncludeColRef>) => {\n  const { includedColumnList } = props;\n  const { columnListRef, databaseType } = useContext(Context);\n  const [dataSource, setDataSource] = useState<IIndexIncludeColumnItem[]>([createInitialData()]);\n  const [form] = Form.useForm();\n  const [editingKey, setEditingKey] = useState<string | null>(null);\n  const isEditing = (record: IIndexIncludeColumnItem) => record.key === editingKey;\n  const tableRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    if (includedColumnList.length) {\n      setDataSource(\n        includedColumnList.map((t) => {\n          return {\n            ...t,\n            key: uuidv4(),\n          };\n        }),\n      );\n    }\n  }, [includedColumnList]);\n\n  const columnList: IColumnItemNew[] = useMemo(() => {\n    const columnListInfo = columnListRef.current?.getColumnListInfo()?.filter((i) => i.name !== null);\n    return columnListInfo || [];\n  }, []);\n\n  const edit = (record: any) => {\n    form.setFieldsValue({ ...record });\n    setEditingKey(record.key || null);\n  };\n\n  const addData = () => {\n    const newData = createInitialData();\n    setDataSource([...dataSource, newData]);\n    edit(newData);\n    setTimeout(() => {\n      tableRef.current?.scrollTo(0, tableRef.current?.scrollHeight + 100);\n    }, 0);\n  };\n\n  const deleteData = (record) => {\n    setDataSource(dataSource.filter((i) => i.key !== record.key));\n  };\n\n  const columns = [\n    {\n      title: i18n('editTable.label.index'),\n      dataIndex: 'index',\n      width: '50px',\n      align: 'center',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        return dataSource.findIndex((i) => i.key === record.key) + 1;\n      },\n    },\n    {\n      title: i18n('editTable.label.columnName'),\n      dataIndex: 'columnName',\n      // width: '45%',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        const editable = isEditing(record);\n        return editable ? (\n          <Form.Item name=\"columnName\" style={{ margin: 0 }}>\n            <Select options={columnList.map((i) => ({ label: i.name, value: i.name }))} />\n          </Form.Item>\n        ) : (\n          <div className={styles.editableCell} onClick={() => edit(record)}>\n            {text}\n          </div>\n        );\n      },\n    },\n    {\n      title: i18n('editTable.label.order'),\n      dataIndex: 'ascOrDesc',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        const editable = isEditing(record);\n        return editable ? (\n          <Form.Item name=\"ascOrDesc\" style={{ margin: 0 }}>\n            <Select\n              options={[\n                { label: 'ASC', value: 'ASC' },\n                { label: 'DESC', value: 'DESC' },\n              ]}\n            />\n          </Form.Item>\n        ) : (\n          <div className={styles.editableCell} onClick={() => edit(record)}>\n            {text}\n          </div>\n        );\n      },\n    },\n    {\n      width: '40px',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        return (\n          <div\n            className={styles.operationBar}\n            onClick={() => {\n              deleteData(record);\n            }}\n          >\n            <div className={styles.deleteIconBox}>\n              <Iconfont code=\"&#xe64e;\" />\n            </div>\n          </div>\n        );\n      },\n    },\n\n    // {\n    //   title: i18n('editTable.label.prefixLength'),\n    //   dataIndex: 'prefixLength',\n    //   width: '45%',\n    //   render: (text: string, record: IIndexIncludeColumnItem) => {\n    //     const editable = isEditing(record);\n    //     return editable ? (\n    //       <Form.Item name=\"prefixLength\" style={{ margin: 0 }}>\n    //         <InputNumber style={{ width: '100%' }} />\n    //       </Form.Item>\n    //     ) : (\n    //       <div className={styles.editableCell} onClick={() => edit(record)}>\n    //         {text}\n    //       </div>\n    //     );\n    //   },\n    // },\n  ];\n  // sqlLite 添加排序规则\n  if (databaseType === DatabaseTypeCode.SQLITE) {\n    columns.splice(2, 0, {\n      title: i18n('editTable.label.collation'),\n      dataIndex: 'collation',\n      render: (text: string, record: IIndexIncludeColumnItem) => {\n        const editable = isEditing(record);\n        return editable ? (\n          <Form.Item name=\"collation\" style={{ margin: 0 }}>\n            <Select\n              options={[\n                { label: 'BINARY', value: 'BINARY' },\n                { label: 'NOCASE', value: 'NOCASE' },\n                { label: 'RTRIM', value: 'RTRIM' },\n              ]}\n            />\n          </Form.Item>\n        ) : (\n          <div className={styles.editableCell} onClick={() => edit(record)}>\n            {text}\n          </div>\n        );\n      },\n    });\n  }\n\n  const handleFieldsChange = (field: any) => {\n    const { value } = field[0];\n    const { name: nameList } = field[0];\n    const name = nameList[0];\n    const newData = dataSource.map((item) => {\n      if (item.key === editingKey) {\n        return {\n          ...item,\n          [name]: value,\n        };\n      }\n      return item;\n    });\n    setDataSource(newData);\n  };\n\n  const getIncludeColInfo = (): IIndexIncludeColumnItem[] => {\n    return dataSource\n      .map((t) => {\n        return lodash.omit(t, 'key');\n      })\n      .filter((t) => t.columnName);\n  };\n\n  useImperativeHandle(ref, () => ({\n    getIncludeColInfo,\n  }));\n\n  return (\n    <div className={classnames(styles.includeCol)}>\n      <div className={styles.indexListHeader}>\n        <Button onClick={addData}>{i18n('editTable.button.add')}</Button>\n        {/* <Button onClick={deleteData}>{i18n('editTable.button.delete')}</Button> */}\n      </div>\n      <Form className={styles.formBox} form={form} onFieldsChange={handleFieldsChange}>\n        <Table\n          ref={tableRef}\n          style={{\n            maxHeight: '100%',\n            overflow: 'auto',\n          }}\n          sticky\n          pagination={false}\n          rowKey=\"key\"\n          columns={columns as any}\n          dataSource={dataSource}\n        />\n      </Form>\n    </div>\n  );\n});\n\nexport default IncludeCol;\n"
  },
  {
    "path": "chat2db-client/src/blocks/SequenceEditor/RealTimeSQL/index.less",
    "content": "@import '../../../styles/var.less';\n\n.realTimeSQL {\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/SequenceEditor/RealTimeSQL/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\n\ninterface IProps {\n  className?: string;\n}\n\nexport default memo<IProps>((props) => {\n  const { className } = props;\n  return (\n    <div className={classnames(styles.realTimeSQL, className)}>\n      <div className={styles.title}>实时 SQL</div>\n      <div className={styles.content}>实时 SQL</div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/blocks/SequenceEditor/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n.header {\n  // position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin: 10px 10px 0px 10px;\n  box-sizing: border-box;\n  overflow: hidden;\n}\n\n.saveButton {\n  // position: absolute;\n  // right: 20px;\n}\n\n.tabList {\n  display: flex;\n  border-bottom: 0;\n  font-size: 14px;\n  border-radius: 6px;\n  background-color: var(--color-bg-subtle);\n  position: relative;\n  &::after {\n    position: absolute;\n    left: calc(82px * var(--i));\n    content: '';\n    height: 26px;\n    width: 82px;\n    border-radius: 4px;\n    color: var(--color-primary);\n    background-color: var(--color-primary-bg);\n    z-index: 0;\n    transition: left 0.2s ease-out;\n  }\n}\n\n.tabItem {\n  position: relative;\n  z-index: 1;\n  height: 26px;\n  width: 82px;\n  text-align: center;\n  line-height: 26px;\n  border-radius: 4px;\n  cursor: pointer;\n  font-size: 13px;\n  user-select: none;\n  &:hover {\n    color: var(--color-primary);\n  }\n}\n\n.currentTab {\n  color: var(--color-primary);\n}\n\n.main {\n  flex: 1;\n  position: relative;\n  overflow: hidden;\n}\n\n.tab {\n  z-index: 2;\n  position: absolute;\n  left: 0px;\n  top: 0px;\n  width: 100%;\n  height: 100%;\n}\n\n.hidden {\n  z-index: 1;\n  opacity: 0;\n}\n\n.monacoEditorModal {\n  display: flex;\n  height: 400px;\n  border: 1px solid var(--color-border);\n  border-radius: 4px;\n\n  .monacoEditorHeader {\n    height: 38px;\n    padding: 0px 10px;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    border-bottom: 1px solid var(--color-border);\n    .formatButton {\n      border-radius: 3px;\n      background-color: var(--color-fill-quaternary);\n      height: 24px;\n      padding: 0px 7px;\n      cursor: pointer;\n      i {\n        margin-right: 6px;\n      }\n      &:hover {\n        color: var(--color-primary);\n      }\n    }\n    .executeButton {\n      height: 24px;\n      line-height: 24px;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      i {\n        margin-right: 4px;\n      }\n    }\n  }\n  .monacoEditorContent {\n    width: 0px;\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    padding: 0px -6px;\n    border-right: 1px solid var(--color-border);\n    .monacoEditor {\n      flex: 1;\n      margin: 0px 6px;\n    }\n  }\n  .result {\n    width: 0px;\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    .resultHeader {\n      width: 100%;\n      line-height: 38px;\n      text-align: center;\n      border-bottom: 1px solid var(--color-border);\n    }\n    .resultContent {\n      flex: 1;\n      padding: 0px 10px;\n      overflow-y: auto;\n      .errorTitle {\n        display: flex;\n        align-items: center;\n        padding: 4px 10px;\n        // background-color: var(--color-bg-subtle);\n        border-radius: 8px;\n        margin-top: 10px;\n        i {\n          color: var(--color-error);\n          margin-right: 4px;\n        }\n      }\n\n      .errorMessage {\n        margin: 10px 0px;\n        padding: 4px 10px;\n        background-color: var(--color-bg-subtle);\n        border-radius: 8px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/SequenceEditor/index.tsx",
    "content": "import React, { memo, useRef, useState, createContext, useEffect } from 'react';\nimport { Button, Modal, message } from 'antd';\nimport i18n from '@/i18n';\nimport lodash from 'lodash';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport BaseInfo, { ISequenceInfoRef } from './BaseInfo';\nimport sqlService, { IModifySequenceSqlParams } from '@/service/sql';\nimport ExecuteSQL from '@/components/ExecuteSQL';\nimport { ISequenceInfo, IWorkspaceTab, IColumnTypes } from '@/typings';\nimport { DatabaseTypeCode, WorkspaceTabType } from '@/constants';\nimport LoadingContent from '@/components/Loading/LoadingContent';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\ninterface IProps {\n  dataSourceId: number;\n  databaseName: string;\n  schemaName?: string | null;\n  tableName?: string;\n  databaseType: DatabaseTypeCode;\n  changeTabDetails: (data: IWorkspaceTab) => void;\n  tabDetails: IWorkspaceTab;\n  submitCallback: () => void;\n}\n\ninterface IContext extends IProps {\n  sequenceDetails: ISequenceInfo;\n  sequenceInfoRef: React.RefObject<ISequenceInfoRef>;\n}\n\nexport const Context = createContext<IContext>({} as any);\n\ninterface IOption {\n  label: string;\n  value: string | number | null;\n}\n\n// 列字段类型，select组件的options需要的数据结构\ninterface IColumnTypesOption extends IColumnTypes {\n  label: string;\n  value: string | number | null;\n}\nexport interface IDatabaseSupportField {\n  columnTypes: IColumnTypesOption[];\n  charsets: IOption[];\n  collations: IOption[];\n  indexTypes: IOption[];\n  defaultValues: IOption[];\n}\n\nexport default memo((props: IProps) => {\n  const {\n    databaseName,\n    dataSourceId,\n    tableName,\n    schemaName,\n    changeTabDetails,\n    tabDetails,\n    databaseType,\n    submitCallback,\n  } = props;\n  const [sequenceDetails, setSequenceDetails] = useState<ISequenceInfo>({} as any);\n  const [oldSequenceDetails, setOldSequenceDetails] = useState<ISequenceInfo>({} as any);\n  const [viewSqlModal, setViewSqlModal] = useState<boolean>(false);\n  const sequenceInfoRef = useRef<ISequenceInfoRef>(null);\n  const [appendValue, setAppendValue] = useState<string>('');\n  const [isLoading, setIsLoading] = useState<boolean>(false);\n  const user = useWorkspaceStore.getState().currentConnectionDetails\n\n  useEffect(() => {\n    if (tableName) {\n      getSequenceDetails();\n    }else{\n      const newSequenceDetails = { ...sequenceDetails, rolname: useWorkspaceStore.getState().currentConnectionDetails?.user ?? '' };\n      setSequenceDetails(newSequenceDetails)\n    }\n    // getDatabaseFieldTypeList();\n  }, []);\n\n  const getSequenceDetails = (myParams?: { sequenceNameProps?: string }) => {\n    const { sequenceNameProps } = myParams || {};\n    const mySequenceName = sequenceNameProps || tableName;\n    if (mySequenceName) {\n      const params = {\n        databaseName,\n        dataSourceId,\n        sequenceName: mySequenceName,\n        schemaName,\n        refresh: true,\n      };\n      setIsLoading(true);\n      sqlService\n        .getSequenceDetails(params)\n        .then((res) => {\n          const newSequenceDetails = lodash.cloneDeep(res);\n          setSequenceDetails(newSequenceDetails || {});\n          setOldSequenceDetails(res);\n        })\n        .finally(() => {\n          setIsLoading(false);\n        });\n    }\n  };\n\n  function submit() {\n    const sequenceInfo = sequenceInfoRef.current.getSequenceInfo();\n    if (sequenceInfo.seqmin > sequenceInfo.seqstart) {\n      message.error(\"最小值不能大于起始值\"); // 或其他提示逻辑\n      return;\n    }\n    if (sequenceInfoRef.current) {\n      const newSequence = {\n        ...oldSequenceDetails,\n        ...sequenceInfoRef.current.getSequenceInfo(),\n      };\n      newSequence.nspname = props?.schemaName as String;\n      const params: IModifySequenceSqlParams = {\n        databaseName,\n        dataSourceId,\n        schemaName,\n        refresh: true,\n        newSequence,\n      };\n\n      if (tableName) {\n        // params.tableName = tableName;\n        params.oldSequence = oldSequenceDetails;\n      }\n      sqlService.getModifySequenceSql(params).then((res) => {\n        setViewSqlModal(true);\n        setAppendValue(res?.[0].sql);\n      });\n    }\n  }\n\n  const executeSuccessCallBack = () => {\n    setViewSqlModal(false);\n    message.success(i18n('common.text.successfulExecution'));\n    const newTableName = sequenceInfoRef.current?.getSequenceInfo().nspname;\n    // getTableDetails({ tableNameProps: newTableName });\n    if (!tableName) {\n      changeTabDetails({\n        ...tabDetails,\n        title: `${newTableName}`,\n        type: WorkspaceTabType.EditSequence,\n        uniqueData: {\n          ...(tabDetails.uniqueData || {}),\n          tableName: newTableName,\n        },\n      });\n    }\n    // 保存成功后，刷新左侧树\n    submitCallback?.();\n  };\n\n  return (\n    <Context.Provider\n      value={{\n        ...props,\n        sequenceDetails,\n        sequenceInfoRef,\n        dataSourceId,\n        databaseName,\n        schemaName,\n      }}\n    >\n      <LoadingContent coverLoading isLoading={isLoading} className={classnames(styles.box)}>\n        <div className={styles.header}>\n          <div className={styles.saveButton}>\n            <Button type=\"primary\" onClick={submit}>\n              {i18n('common.button.save')}\n            </Button>\n          </div>\n        </div>\n        <div className={styles.main}>\n          <BaseInfo ref={sequenceInfoRef} />\n        </div>\n      </LoadingContent>\n\n      <Modal\n        title={i18n('editSequence.title.sqlPreview')}\n        open={!!viewSqlModal}\n        onCancel={() => {\n          setViewSqlModal(false);\n        }}\n        width=\"60vw\"\n        maskClosable={false}\n        footer={false}\n        destroyOnClose={true}\n      >\n        <ExecuteSQL\n          initSql={appendValue}\n          databaseName={databaseName}\n          dataSourceId={dataSourceId}\n          tableName={tableName}\n          schemaName={schemaName}\n          databaseType={databaseType}\n          executeSuccessCallBack={executeSuccessCallBack}\n        />\n      </Modal>\n    </Context.Provider>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/About/index.less",
    "content": ".aboutUs {\n  .versionsInfo {\n    display: flex;\n    align-items: center;\n    padding-bottom: 10px;\n    border-bottom: 1px solid var(--color-border);\n    margin-bottom: 10px;\n  }\n  .brandLogo {\n    margin-right: 15px;\n    flex-shrink: 0;\n  }\n  .currentVersion {\n    font-size: 20px;\n    line-height: 24px;\n    .appName {\n      margin-right: 10px;\n    }\n  }\n  .newVersion {\n    color: var(--color-text-tertiary);\n    font-size: 14px;\n    margin-top: 2px;\n  }\n  .updateButton {\n    margin-top: 10px;\n    button:nth-of-type(1) {\n      margin-right: 20px;\n    }\n  }\n  .updateRule {\n    .updateRuleTitle {\n      font-size: 16px;\n      margin-bottom: 10px;\n    }\n    .updateRuleGroup {\n      display: flex;\n      flex-direction: column;\n      .updateRuleRadioContent {\n        display: flex;\n        align-items: center;\n        i {\n          margin-left: 4px;\n          color: var(--color-text-tertiary);\n          font-size: 12px;\n        }\n      }\n    }\n  }\n  .holdingService{\n    margin-top: 20px;\n  }\n  .brief {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n\n    .env,\n    .version {\n      margin: 4px 0px;\n      color: var(--color-text-45);\n    }\n\n    .log {\n      margin-top: 4px;\n      color: var(--color-primary);\n      cursor: pointer;\n\n      &:hover {\n        text-decoration: underline;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/About/index.tsx",
    "content": "import React, { useEffect, useMemo } from 'react';\nimport styles from './index.less';\nimport i18n from '@/i18n';\nimport classnames from 'classnames';\nimport BrandLogo from '@/components/BrandLogo';\nimport { APP_NAME, WEBSITE_DOC } from '@/constants/appConfig';\nimport { Button, Radio, Space, Checkbox } from 'antd';\nimport configService from '@/service/config';\nimport { DownloadOutlined } from '@ant-design/icons';\nimport { IUpdateDetectionData } from '../index';\nimport { IUpdateDetectionRef, UpdatedStatusEnum } from '../UpdateDetection';\nimport Iconfont from '@/components/Iconfont';\nimport { useSettingStore, setHoldingService } from '@/store/setting';\nimport s from '@/service/misc'\n\ninterface IProps {\n  updateDetectionData: IUpdateDetectionData | null;\n  updateDetectionRef: React.MutableRefObject<IUpdateDetectionRef> | null;\n}\n\n// 关于我们\nexport default function AboutUs(props: IProps) {\n  const { updateDetectionData, updateDetectionRef } = props;\n  const [updateRule, setUpdateRule] = React.useState<'manual' | 'auto'>(updateDetectionData?.type || 'manual');\n\n  const { holdingService } = useSettingStore((state) => {\n    return {\n      holdingService: state.holdingService,\n    };\n  });\n\n  const onChangeUpdateRul = (e) => {\n    configService.setAppUpdateType(e.target.value).then(() => {\n      setUpdateRule(e.target.value);\n    });\n  };\n\n  useEffect(() => {\n    setUpdateRule(updateDetectionData?.type || 'manual');\n  }, [updateDetectionData?.type]);\n\n  const jumpDoc = () => {\n    window.open(WEBSITE_DOC, '_blank');\n  };\n\n  const restartApp = () => {\n    window.electronApi?.quitApp();\n  };\n\n  const updateButton = useMemo(() => {\n    if (!updateDetectionData?.needUpdate) {\n      return false;\n    }\n    switch (updateDetectionData?.updatedStatusEnum) {\n      case UpdatedStatusEnum.NOT_UPDATED:\n        return (\n          <Button\n            onClick={() => {\n              updateDetectionRef?.current?.openDownload(updateDetectionData);\n            }}\n            icon={<DownloadOutlined />}\n            type=\"primary\"\n          >\n            {i18n('setting.button.startDownloading')}\n          </Button>\n        );\n      case UpdatedStatusEnum.UPDATING:\n        return (\n          <Button type=\"primary\" loading>\n            {i18n('setting.button.beDownloading')}\n          </Button>\n        );\n      case UpdatedStatusEnum.TIMEOUT:\n        return (\n          <Button\n            onClick={() => {\n              updateDetectionRef?.current?.openDownload(updateDetectionData);\n            }}\n            icon={<DownloadOutlined />}\n            type=\"primary\"\n          >\n            {i18n('setting.button.redownload')}\n          </Button>\n        );\n      case UpdatedStatusEnum.UPDATED:\n        return (\n          <Button icon={<Iconfont code=\"&#xe662;\" />} type=\"primary\" onClick={restartApp}>\n            {i18n('setting.button.restart')}\n          </Button>\n        );\n      // case UpdatedStatusEnum.UPDATED:\n      //   return (\n      //     <Button icon={<RedoOutlined />} type=\"primary\">\n      //       {i18n('setting.button.restart')}\n      //     </Button>\n      //   );\n      default:\n        return false;\n    }\n  }, [updateDetectionData]);\n\n  const changeHoldingService = (e) => {\n    setHoldingService(e.target.checked);\n    window.electronApi?.setForceQuitCode?.(e.target.checked);\n  };\n\n  return (\n    <div className={styles.aboutUs}>\n      <div className={styles.versionsInfo}>\n        <BrandLogo size={98} className={styles.brandLogo} />\n        <div>\n          <div className={styles.currentVersion}>\n            <span className={styles.appName}>{APP_NAME}</span>\n            <span>{__APP_VERSION__}</span>\n          </div>\n          <div className={styles.newVersion}>\n            {updateDetectionData?.needUpdate ? (\n              UpdatedStatusEnum.UPDATED === updateDetectionData?.updatedStatusEnum ? (\n                <span>{i18n('setting.text.newEditionIsReady')}</span>\n              ) : (\n                <span>{i18n('setting.text.discoverNewVersion', updateDetectionData?.version)}</span>\n              )\n            ) : (\n              <span>{i18n('setting.text.isLatestVersion')}</span>\n            )}\n          </div>\n          {updateDetectionData?.desktop && (\n            <div className={styles.updateButton}>\n              {updateButton}\n              <Button onClick={jumpDoc}>{i18n('setting.button.changeLog')}</Button>\n            </div>\n          )}\n        </div>\n      </div>\n      <div className={styles.updateRule}>\n        <div className={styles.updateRuleTitle}>{i18n('setting.title.updateRule')}</div>\n        <Radio.Group className={styles.updateRuleGroup} onChange={onChangeUpdateRul} value={updateRule}>\n          <Space direction=\"vertical\">\n            <Radio className={styles.updateRuleRadio} value=\"auto\">\n              {i18n('setting.text.autoUpdate')}\n            </Radio>\n            <Radio className={styles.updateRuleRadio} value=\"manual\">\n              {i18n('setting.text.manualUpdate')}\n            </Radio>\n          </Space>\n        </Radio.Group>\n      </div>\n      {/* <div className={classnames(styles.updateRule, styles.holdingService) }>\n        <div className={styles.updateRuleTitle}>{i18n('setting.title.holdingService')}</div>\n        <Checkbox checked={holdingService} onChange={changeHoldingService}>{i18n('setting.text.holdingService')}</Checkbox>\n      </div> */}\n      {/* <div className={styles.brief}>\n        <div className={styles.appName}>{APP_NAME}</div>\n        <div className={styles.env}>\n          {i18n('setting.text.currentEnv')}:{__ENV__}\n        </div>\n        <div className={styles.version}>\n          {i18n('setting.text.currentVersion')}:v{__APP_VERSION__} build\n          {formatDate(getUserTimezoneTimestamp(__BUILD_TIME__), 'yyyyMMddhhmmss')}\n        </div>\n        <a target=\"blank\" href={GITHUB_URL} className={styles.log}>\n          {i18n('setting.text.viewingUpdateLogs')}\n        </a>\n      </div> */}\n    </div>\n  );\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/AiSetting/aiTypeConfig.ts",
    "content": "import i18n from '@/i18n';\nimport { IAiConfig } from '@/typings';\nimport { AIType } from '@/typings/ai';\n\nexport type IAiConfigBooleans = {\n  [K in keyof IAiConfig]?: boolean | string;\n};\n\nconst AITypeName = {\n  [AIType.CHAT2DBAI]: 'Chat2DB',\n  [AIType.ZHIPUAI]: i18n('setting.tab.aiType.zhipu'),\n  [AIType.BAICHUANAI]: i18n('setting.tab.aiType.baichuan'),\n  [AIType.WENXINAI]: i18n('setting.tab.aiType.wenxin'),\n  [AIType.TONGYIQIANWENAI]: i18n('setting.tab.aiType.tongyiqianwen'),\n  [AIType.OPENAI]: 'Open AI',\n  [AIType.AZUREAI]: 'Azure AI',\n  [AIType.RESTAI]: i18n('setting.tab.custom'),\n};\n\nconst AIFormConfig: Record<AIType, IAiConfigBooleans> = {\n  [AIType.CHAT2DBAI]: {\n    apiKey: true,\n  },\n  [AIType.ZHIPUAI]: {\n    apiKey: true,\n    apiHost: 'https://open.bigmodel.cn/api/paas/v4/chat/completions',\n    model: 'codegeex-4',\n  },\n  [AIType.BAICHUANAI]: {\n    apiKey: true,\n    secretKey: true,\n    apiHost: 'https://api.baichuan-ai.com/v1/stream/chat/',\n    model: 'Baichuan2-53B',\n  },\n  [AIType.WENXINAI]: {\n    apiKey: true,\n    apiHost: true,\n  },\n  [AIType.TONGYIQIANWENAI]: {\n    apiKey: true,\n    apiHost: true,\n    model: true,\n  },\n  [AIType.OPENAI]: {\n    apiKey: true,\n    apiHost: 'https://api.openai.com/',\n    httpProxyHost: true,\n    httpProxyPort: true,\n    // model: 'gpt-3.5-turbo',\n  },\n  [AIType.AZUREAI]: {\n    apiKey: true,\n    apiHost: true,\n    model: true,\n  },\n  [AIType.RESTAI]: {\n    apiKey: true,\n    apiHost: true,\n    model: true,\n  },\n};\n\nexport { AIFormConfig, AITypeName };\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/AiSetting/index.less",
    "content": ".aiSqlSource {\n  display: flex;\n  margin-bottom: 20px;\n}\n\n.aiSqlSourceTitle {\n  margin-right: 20px;\n  min-width: 50px;\n}\n\n.title {\n  font-size: 14px;\n  margin-bottom: 12px;\n\n  i {\n    margin-left: 10px;\n    color: var(--color-primary);\n  }\n  &  label{\n    font-size: 14px !important;\n  }\n}\n\n.content {\n  margin-bottom: 15px;\n}\n\n.bottomButton {\n  display: flex;\n  justify-content: flex-end;\n  margin-top: 20px;\n}"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/AiSetting/index.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport configService from '@/service/config';\nimport { AIType } from '@/typings/ai';\nimport { Alert, Button, Flex, Form, Input, Radio, RadioChangeEvent } from 'antd';\nimport i18n from '@/i18n';\nimport { IAiConfig } from '@/typings/setting';\nimport { IRole } from '@/typings/user';\nimport { AIFormConfig, AITypeName } from './aiTypeConfig';\nimport styles from './index.less';\nimport { useUserStore } from '@/store/user';\nimport { getLinkBasedOnTimezone } from '@/utils/timezone';\n\ninterface IProps {\n  handleApplyAiConfig: (aiConfig: IAiConfig) => void;\n  aiConfig: IAiConfig;\n}\n\nfunction capitalizeFirstLetter(string) {\n  return string.charAt(0).toUpperCase() + string.slice(1);\n}\n\n// openAI 的设置项\nexport default function SettingAI(props: IProps) {\n  const [aiConfig, setAiConfig] = useState<IAiConfig>();\n  const { userInfo } = useUserStore((state) => {\n    return {\n      userInfo: state.curUser,\n    };\n  });\n\n  useEffect(() => {\n    setAiConfig(props.aiConfig);\n  }, [props.aiConfig]);\n\n  if (!aiConfig) {\n    return <Alert description={i18n('setting.ai.tips')} type=\"warning\" showIcon />;\n  }\n\n  if (userInfo?.roleCode && userInfo?.roleCode === IRole.USER) {\n    // 如果是用户，不能配置ai\n    return <Alert description={i18n('setting.ai.user.hidden')} type=\"warning\" showIcon />;\n  }\n\n  const handleAiTypeChange = async (e: RadioChangeEvent) => {\n    const aiSqlSource = e.target.value;\n\n    // 查询对应ai类型的配置\n    const res = await configService.getAiSystemConfig({\n      aiSqlSource,\n    });\n    setAiConfig(res);\n  };\n\n  /** 应用Ai配置 */\n  const handleApplyAiConfig = () => {\n    const newAiConfig = { ...aiConfig };\n    /*if (newAiConfig.apiHost && !newAiConfig.apiHost?.endsWith('/')) {\n      newAiConfig.apiHost = newAiConfig.apiHost + '/';\n    }*/\n    if (aiConfig?.aiSqlSource === AIType.CHAT2DBAI) {\n      newAiConfig.apiHost = `${window._appGatewayParams.baseUrl || 'http://test.sqlgpt.cn/gateway'}${'/model/'}`;\n    }\n\n    if (props.handleApplyAiConfig) {\n      props.handleApplyAiConfig(newAiConfig);\n    }\n  };\n\n  const renderAIConfig = () => {\n    if (aiConfig?.aiSqlSource === AIType.CHAT2DBAI) {\n      return (\n        <Flex justify=\"center\">\n          <Button\n            type=\"primary\"\n            onClick={() => {\n              const link = getLinkBasedOnTimezone();\n              window.open(link, '_blank');\n            }}\n          >\n            {i18n('setting.chat2db.ai.button')}\n          </Button>\n        </Flex>\n      );\n    }\n    return (\n      <>\n        <Form layout=\"vertical\">\n          {Object.keys(AIFormConfig[aiConfig?.aiSqlSource]).map((key: string) => (\n            <Form.Item\n              key={key}\n              required={key === 'apiKey' || key === 'secretKey'}\n              label={capitalizeFirstLetter(key)}\n              className={styles.title}\n            >\n              <Input\n                autoComplete=\"off\"\n                value={aiConfig[key]}\n                placeholder={AIFormConfig[aiConfig?.aiSqlSource]?.[key]}\n                onChange={(e) => {\n                  setAiConfig({ ...aiConfig, [key]: e.target.value });\n                }}\n              />\n            </Form.Item>\n          ))}\n        </Form>\n        {aiConfig.aiSqlSource === AIType.RESTAI && (\n          <div style={{ margin: '32px 0 ', fontSize: '12px', opacity: '0.5' }}>{`Tips: ${i18n(\n            'setting.tab.aiType.custom.tips',\n          )}`}</div>\n        )}\n        <div className={styles.bottomButton}>\n          <Button type=\"primary\" onClick={handleApplyAiConfig}>\n            {i18n('setting.button.apply')}\n          </Button>\n        </div>\n      </>\n    );\n  };\n\n  return (\n    <>\n      <div className={styles.aiSqlSource}>\n        <div className={styles.aiSqlSourceTitle}>{i18n('setting.title.aiSource')}:</div>\n        <Radio.Group onChange={handleAiTypeChange} value={aiConfig?.aiSqlSource}>\n          {Object.keys(AIType).map((key) => (\n            <Radio key={key} value={key} style={{ marginBottom: '8px' }}>\n              {AITypeName[key]}\n            </Radio>\n          ))}\n        </Radio.Group>\n      </div>\n\n      {renderAIConfig()}\n\n      {/* {aiConfig?.aiSqlSource === AIType.CHAT2DBAI && !aiConfig.apiKey && <Popularize source=\"setting\" />} */}\n    </>\n  );\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/BaseSetting/index.less",
    "content": ".title {\n  font-size: 14px;\n  margin-bottom: 10px;\n\n  i {\n    margin-left: 10px;\n    color: var(--color-primary);\n  }\n}\n\n\n.backgroundList {\n  display: flex;\n  margin: 0px 0px 20px -7px;\n\n  .themeItemBox {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    margin: 0px 7px;\n\n    .themeImg {\n      margin-bottom: 6px;\n      width: 64px;\n      height: 48px;\n      overflow: hidden;\n      border-radius: 2px;\n      background-size: cover;\n      background-repeat: no-repeat;\n      cursor: pointer;\n    }\n\n    .themeName {\n      text-align: center;\n      font-size: 12px;\n    }\n\n    .current {\n      box-sizing: border-box;\n      border: 2px solid var(--color-primary);\n    }\n  }\n\n  filter: brightness(var(--filter-brightness));\n}\n\n.langBox {\n  margin-bottom: 20px;\n}\n\n.sqlEditorFontSize {\n  margin-bottom: 20px;\n}\n\n.primaryColorList {\n  display: flex;\n  margin-bottom: 20px;\n\n  .themeColorItem {\n    margin-right: 20px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n  }\n\n  .colorLump {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 32px;\n    height: 32px;\n    border: 2px solid transparent;\n    border-radius: 50%;\n    cursor: pointer;\n    border: 1px solid var(--color-bg-hover);\n  }\n\n  i {\n    color: #fff;\n  }\n\n  .colorName {\n    margin-top: 6px;\n    font-size: 12px;\n  }\n}"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/BaseSetting/index.tsx",
    "content": "import React, { useState } from 'react';\nimport { LangType, ThemeType } from '@/constants';\nimport i18n, { currentLang } from '@/i18n';\nimport classnames from 'classnames';\nimport themeDarkImg from '@/assets/img/theme-dark.png';\nimport themeLightImg from '@/assets/img/theme-light.png';\nimport themeAutoImg from '@/assets/img/theme-auto.png';\nimport { Select } from 'antd';\nimport Iconfont from '@/components/Iconfont';\nimport { setLang as setLangLocalStorage } from '@/utils/localStorage';\nimport { useTheme } from '@/hooks/useTheme';\n\nimport styles from './index.less';\n\nconst themeList = [\n  {\n    code: ThemeType.Light,\n    name: i18n('setting.text.light'),\n    img: themeLightImg,\n  },\n  {\n    code: ThemeType.Dark,\n    name: i18n('setting.text.dark'),\n    img: themeDarkImg,\n  },\n  {\n    code: ThemeType.DarkDimmed,\n    name: i18n('setting.text.dark2'),\n    img: themeDarkImg\n  },\n  {\n    code: ThemeType.FollowOs,\n    name: i18n('setting.text.followOS'),\n    img: themeAutoImg,\n  },\n  // {\n  //   code: 'eyeshield',\n  //   name: '护眼',\n  //   img: 'https://img.alicdn.com/imgextra/i1/O1CN01KGCqY21uJpuFjEQW2_!!6000000006017-2-tps-181-135.png'\n  // },\n];\n\nconst languageOptions = [\n  { value: LangType.ZH_CN, label: '简体中文' },\n  { value: LangType.EN_US, label: 'English' },\n  { value: LangType.TR_TR, label: 'Turkish' },\n  { value: LangType.JA_JP, label: '日本語' },\n]\n\nconst colorList = [\n  {\n    code: 'golden-purple',\n    name: i18n('setting.label.violet'),\n    color: '#9373ee',\n  },\n  {\n    code: 'polar-blue',\n    name: i18n('setting.label.blue'),\n    color: '#1a90ff',\n  },\n  {\n    code: 'blue2',\n    name: i18n('setting.label.violet'),\n    color: '#00c3ee',\n  },\n  {\n    code: 'polar-green',\n    name: i18n('setting.label.green'),\n    color: '#039e74',\n  },\n  {\n    code: 'gold',\n    name: i18n('setting.label.violet'),\n    color: '#9a7d56',\n  },\n  {\n    code: 'silver',\n    name: i18n('setting.label.violet'),\n    color: '#8e8374',\n  },\n  {\n    code: 'red',\n    name: i18n('setting.label.violet'),\n    color: '#fd6874',\n  },\n  {\n    code: 'orange',\n    name: i18n('setting.label.violet'),\n    color: '#fa8c16',\n  },\n];\n\n// baseBody 基础设置\nexport default function BaseSetting() {\n  const [appTheme, setAppTheme] = useTheme();\n  const [currentTheme, setCurrentTheme] = useState<ThemeType>(appTheme.backgroundColor);\n  const [currentPrimaryColor, setCurrentPrimaryColor] = useState(localStorage.getItem('primary-color'));\n\n  const changePrimaryColor = (item: any) => {\n    const html = document.documentElement;\n    html.setAttribute('primary-color', item.code);\n    localStorage.setItem('primary-color', item.code);\n    setCurrentPrimaryColor(item.code);\n    setAppTheme({\n      ...appTheme,\n      primaryColor: item.code,\n    });\n  };\n\n  function changeLang(e: any) {\n    setLangLocalStorage(e);\n    //切换语言时，需要设置cookie，用来改变后台服务的Locale\n    const date = new Date('2030-12-30 12:30:00').toUTCString();\n    document.cookie = `CHAT2DB.LOCALE=${e};Expires=${date}`;\n    location.reload();\n  }\n\n  function handleChangeTheme(backgroundColor: any) {\n    setAppTheme({\n      ...appTheme,\n      backgroundColor,\n    });\n    setCurrentTheme(backgroundColor);\n  }\n\n  // const changeSqlEditorFontSize = (e: any) => {\n  // }\n\n  return (\n    <>\n      <div className={styles.title}>{i18n('setting.title.backgroundColor')}</div>\n      <ul className={styles.backgroundList}>\n        {themeList.map((t) => {\n          return (\n            <div key={t.code} className={styles.themeItemBox}>\n              <div\n                className={classnames({ [styles.current]: currentTheme == t.code }, styles.themeImg)}\n                onClick={handleChangeTheme.bind(null, t.code)}\n                style={{ backgroundImage: `url(${t.img})` }}\n              />\n              <div className={styles.themeName}>{t.name}</div>\n            </div>\n          );\n        })}\n      </ul>\n      <div className={styles.title}>{i18n('setting.title.language')}</div>\n      <div className={styles.langBox}>\n        {/* <Radio.Group onChange={changeLang} value={currentLang}>\n          <Radio value={LangType.ZH_CN}>简体中文</Radio>\n          <Radio value={LangType.EN_US}>English</Radio>\n          <Radio value={LangType.TR_TR}>Turkish</Radio>\n        </Radio.Group> */}\n        <Select\n          style={{ width: 120 }}\n          onChange={changeLang}\n          value={currentLang}\n          options={languageOptions}\n        />\n      </div>\n      <div className={styles.title}>{i18n('setting.title.themeColor')}</div>\n      <ul className={styles.primaryColorList}>\n        {colorList.map((item) => {\n          return (\n            <div key={item.code} className={styles.themeColorItem}>\n              <div\n                className={styles.colorLump}\n                key={item.code}\n                onClick={changePrimaryColor.bind(null, item)}\n                style={{ backgroundColor: item.color }}\n              >\n                {currentPrimaryColor == item.code && <Iconfont code=\"&#xe617;\" />}\n              </div>\n              {/* <div className={styles.colorName}>{item.name}</div> */}\n            </div>\n          );\n        })}\n        {/* <ColorPicker placement='bottomLeft' onChange={setCustomColor}>\n          <div className={classnames(styles.themeColorItem, styles.customColorItem) }>\n            <div\n              className={styles.colorLump}\n              onClick={()=>{}}\n            >\n              自定义\n            </div>\n          </div>\n        </ColorPicker> */}\n      </ul>\n      {/* <div className={styles.title}>{i18n('setting.title.sqlEditorFontSize')}</div>\n      <div className={styles.sqlEditorFontSize}>\n        <Radio.Group onChange={changeSqlEditorFontSize}>\n          <Radio value={12}>12</Radio>\n          <Radio value={14}>14</Radio>\n          <Radio value={16}>16</Radio>\n        </Radio.Group>\n      </div> */}\n      \n    </>\n  );\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/ProxySetting/index.less",
    "content": ".title {\n  font-size: 14px;\n  margin-bottom: 10px;\n\n  i {\n    margin-left: 10px;\n    color: var(--color-primary);\n  }\n}\n\n.content {\n  margin-bottom: 15px;\n}\n\n.bottomButton {\n  display: flex;\n  justify-content: flex-end;\n  margin-top: 20px;\n}"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/ProxySetting/index.tsx",
    "content": "import React, { useState } from 'react';\nimport i18n from '@/i18n';\nimport { Button, Input, message } from 'antd';\nimport classnames from 'classnames';\nimport styles from './index.less';\nimport outSideService from '@/service/outside';\n\n// 代理设置\nexport default function ProxyBody() {\n  const [apiPrefix, setApiPrefix] = useState(window._BaseURL);\n\n  function updateApi(e: any) {\n    setApiPrefix(e.target.value);\n  }\n\n  function affirmUpdateApi() {\n    if (!apiPrefix) {\n      return;\n    }\n    outSideService.dynamicUrl(`${apiPrefix}/api/system/get-version-a`).then((res: any) => {\n      localStorage.setItem('_BaseURL', apiPrefix);\n      location.reload();\n    }).catch((err: any) => {\n      message.error(i18n('setting.message.urlTestError'))\n    });\n    // try {\n    //   const xhr = new XMLHttpRequest();\n    //   xhr.withCredentials = true;\n    //   xhr.open('GET', `${apiPrefix}/api/system/get-version-a`);\n    //   xhr.onload = function () {\n    //     if (xhr.status === 200) {\n    //       localStorage.setItem('_BaseURL', apiPrefix);\n    //       location.reload();\n    //     } else {\n    //       message.error(i18n('setting.message.urlTestError'));\n    //     }\n    //   };\n    //   xhr.send();\n    // } catch {\n    //   message.error(i18n('setting.message.urlTestError'));\n    // }\n  }\n\n  return (\n    <>\n      <div className={styles.title}>{i18n('setting.label.serviceAddress')}</div>\n      <div className={classnames(styles.content, styles.chatGPTKey)}>\n        <Input value={apiPrefix} onChange={updateApi} />\n      </div>\n      <div className={styles.bottomButton}>\n        <Button type=\"primary\" onClick={affirmUpdateApi}>\n          {i18n('setting.button.apply')}\n        </Button>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/UpdateDetection/index.less",
    "content": "@import '../../../styles/var.less';\n.versionText{\n  color: var(--color-primary);\n}\n\n.notificationBtnBox{\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n\n.updateReminder{\n  display: flex;\n  align-items: center;\n  .bell{\n    // 做个动画，当前div像铃铛一样摇晃，以div的上中心点为中心，动画循环10次以后停止\n    animation: shake 0.2s linear 10;\n    margin-right: 4px;\n  }\n  i{\n    color: var(--color-primary);\n  }\n}\n\n\n.notification{\n  z-index: 999 !important;\n  width: 300px !important;\n  font-size: 12px !important;\n  padding: 8px 12px !important;\n  :global{\n    .ant-notification-notice-close{\n      top: 10px !important;\n      inset-inline-end: 8px !important;\n    }\n  } \n}\n// 做个动画，当前div像铃铛一样摇晃，以div的上中心点为中心，左右3px晃动\n@keyframes shake {\n  0% {\n    transform: rotate(0deg);\n  }\n  25% {\n    transform: rotate(8deg);\n  }\n  50% {\n    transform: rotate(0deg);\n  }\n  75% {\n    transform: rotate(-8deg);\n  }\n  100% {\n    transform: rotate(0deg);\n  }\n}\n\n\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/UpdateDetection/index.tsx",
    "content": "import React, { memo, useEffect, forwardRef, ForwardedRef, useImperativeHandle } from 'react';\nimport configService from '@/service/config';\nimport styles from './index.less';\nimport { notification, Button, Space } from 'antd';\nimport { compareVersion } from '@/utils';\nimport { IUpdateDetectionData } from '../index';\nimport i18n, { i18nElement } from '@/i18n';\nimport Iconfont from '@/components/Iconfont';\n\nexport enum UpdatedStatusEnum {\n  // 未更新\n  NOT_UPDATED = 'NOT_UPDATED',\n  // 更新中\n  UPDATING = 'UPDATING',\n  // 更新完成\n  UPDATED = 'UPDATED',\n  // 更新失败\n  UPDATE_FAILED = 'UPDATE_FAILED',\n  // 超时\n  TIMEOUT = 'TIMEOUT',\n}\n\ninterface IProps {\n  openSettingModal: (number) => void;\n  updateDetectionData: IUpdateDetectionData | null;\n  setUpdateDetectionData: (data: IUpdateDetectionData) => void;\n}\n\nexport interface IUpdateDetectionRef {\n  openDownload: (data: IUpdateDetectionData) => void;\n}\n\n// 轮训间隔时间\nconst INTERVAL_TIME = 5000;\n// 最大轮训次数\nconst MAX_TIMES = 200;\n\nconst UpdateDetection = memo(\n  forwardRef((props: IProps, ref: ForwardedRef<IUpdateDetectionRef>) => {\n    const { openSettingModal, setUpdateDetectionData } = props;\n    const [notificationApi, notificationDom] = notification.useNotification();\n    const timesRef = React.useRef(0);\n\n    // useEffect(() => {\n    //   checkUpdate();\n    // }, []);\n\n    const close = () => {};\n\n    // 检测是否有可更新的版本\n    function checkUpdate() {\n      configService\n        .getLatestVersion({\n          currentVersion: __APP_VERSION__,\n        })\n        .then((res) => {\n          // 如果是服务端，那么就不用更新\n          if(!res) {\n            return\n          }\n          if (res.desktop === false) {\n            return;\n          }\n\n          const _updateDetectionData = {\n            ...res,\n            needUpdate: compareVersion(res.version, __APP_VERSION__) === 1,\n            updatedStatusEnum: UpdatedStatusEnum.NOT_UPDATED,\n          };\n\n          setUpdateDetectionData(_updateDetectionData);\n\n          // 如果监测到localStorage里面有存的老版本，那么就提示首次更新\n          const lastLatestVersion = localStorage.getItem('last-latest-version');\n          if (lastLatestVersion && compareVersion(lastLatestVersion, __APP_VERSION__) === -1) {\n            openNotificationUpdated();\n          }\n\n          // 最新版本存入localStorage\n          localStorage.setItem('last-latest-version', __APP_VERSION__);\n\n          // 如果是最新版本，那么就不用更新\n          if (compareVersion(res.version, __APP_VERSION__) !== 1) {\n            return;\n          }\n\n          // 如果用户点过知道那么，就不用提示更新\n          if (localStorage.getItem('i-see-latest-version') === res.version && res.type === 'manual') {\n            return;\n          }\n\n          // 如果是自动更新那么就轮询调后端接口，判断是否更新完成\n          if (res.type === 'auto') {\n            timesRef.current = 0;\n            openDownload(_updateDetectionData);\n          } else {\n            // 如果是手动更新，那么就提示下载\n            if (res.version) {\n              openNotificationManual(res);\n            }\n          }\n        });\n    }\n\n    function isUpdateSuccess(_updateDetectionData) {\n      if (timesRef.current > MAX_TIMES) {\n        setUpdateDetectionData({\n          ..._updateDetectionData!,\n          updatedStatusEnum: UpdatedStatusEnum.TIMEOUT,\n        });\n        return;\n      }\n      timesRef.current = timesRef.current + 1;\n      if (!_updateDetectionData?.version) {\n        return;\n      }\n      configService\n        .isUpdateSuccess({\n          version: _updateDetectionData.version,\n        })\n        .then((res) => {\n          if (res) {\n            setUpdateDetectionData({\n              ..._updateDetectionData!,\n              updatedStatusEnum: UpdatedStatusEnum.UPDATED,\n            });\n            openNotificationAuto(_updateDetectionData);\n          } else {\n            setTimeout(() => {\n              isUpdateSuccess(_updateDetectionData);\n            }, INTERVAL_TIME);\n          }\n        });\n    }\n\n    // function go() {\n    //   // window.open(responseText.downloadLink);\n    //   notificationApi.destroy();\n    // }\n\n    const handleISee = (_updateDetectionData) => {\n      // 存入localStorage\n      localStorage.setItem('i-see-latest-version', _updateDetectionData?.version || '');\n      notificationApi.destroy();\n    };\n\n    const openNotificationAuto = (_updateDetectionData) => {\n      const key = `open${Date.now()}`;\n      const btn = (\n        <Space>\n          <Button\n            type=\"link\"\n            size=\"small\"\n            onClick={() => {\n              handleISee(_updateDetectionData);\n            }}\n          >\n            {i18n('setting.button.iSee')}\n          </Button>\n          {/* <Button\n            type=\"primary\"\n            size=\"small\"\n            onClick={() => {\n              go();\n            }}\n          >\n            {i18n('setting.button.restart')}\n          </Button> */}\n        </Space>\n      );\n      notificationApi.open({\n        className: styles.notification,\n        duration: null,\n        message: (\n          <div className={styles.updateReminder}>\n            <div className={styles.bell}>\n              <Iconfont code=\"&#xe661;\" />\n            </div>\n            {i18n('common.text.updateReminder')}\n          </div>\n        ),\n        description: i18n('setting.text.newEditionIsReady'),\n        style: {\n          width: 260,\n          backgroundColor: 'var(--color-bg-subtle)',\n        },\n        btn,\n        key,\n        onClose: close,\n      });\n    };\n\n    const openNotificationManual = (_updateDetectionData) => {\n      const key = `open${Date.now()}`;\n      const btn = (\n        <div className={styles.notificationBtnBox}>\n          <Button\n            type=\"link\"\n            size=\"small\"\n            onClick={() => {\n              handleISee(_updateDetectionData);\n            }}\n          >\n            {i18n('setting.button.iSee')}\n          </Button>\n          <Button\n            type=\"link\"\n            size=\"small\"\n            onClick={() => {\n              openSettingModal(3);\n              notificationApi.destroy();\n            }}\n          >\n            {i18n('setting.button.goToUpdate')}\n          </Button>\n        </div>\n      );\n      notificationApi.open({\n        duration: null,\n        className: styles.notification,\n        message: (\n          <div className={styles.updateReminder}>\n            <div className={styles.bell}>\n              <Iconfont code=\"&#xe661;\" />\n            </div>\n            {i18n('common.text.updateReminder')}\n          </div>\n        ),\n        description: i18nElement(\n          'setting.text.discoverNewVersion',\n          <span className={styles.versionText}>v{_updateDetectionData.version}</span>,\n        ),\n        style: {\n          width: 260,\n          backgroundColor: 'var(--color-bg-subtle)',\n        },\n        btn,\n        key,\n        onClose: close,\n      });\n    };\n\n    // 首次更新完成提示\n    const openNotificationUpdated = () => {\n      const key = `open${Date.now()}`;\n      notificationApi.open({\n        className: styles.notification,\n        duration: 6,\n        message: (\n          <div className={styles.updateReminder}>\n            <div className={styles.bell}>\n              <Iconfont code=\"&#xe661;\" />\n            </div>\n            {i18n('common.text.updateReminder')}\n          </div>\n        ),\n        description: i18n('setting.text.UpdatedLatestVersion', `v${__APP_VERSION__}`),\n        style: {\n          width: 310,\n          backgroundColor: 'var(--color-bg-subtle)',\n        },\n        btn: null,\n        key,\n        onClose: close,\n      });\n    };\n\n    const openDownload = (_updateDetectionData: IUpdateDetectionData) => {\n      if (!_updateDetectionData) {\n        return;\n      }\n      configService.updateDesktopVersion(_updateDetectionData).then(() => {\n        timesRef.current = 0;\n        isUpdateSuccess(_updateDetectionData);\n        setUpdateDetectionData({\n          ..._updateDetectionData,\n          updatedStatusEnum: UpdatedStatusEnum.UPDATING,\n        });\n      });\n    };\n\n    useImperativeHandle(ref, () => ({\n      openDownload,\n    }));\n\n    return <>{notificationDom}</>;\n  }),\n);\n\nexport default UpdateDetection;\n"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n  .f-icon-button();\n}\n\n.settingIcon {\n  color: var(--custom-color-icon);\n\n  &:hover {\n    color: var(--color-primary);\n  }\n}\n\n// .setText {\n//   color: var(--custom-primary);\n//   font-size: 16px;\n\n//   &:hover {\n//     text-decoration: underline;\n//   }\n// }\n\n.title {\n  font-size: 14px;\n  margin-bottom: 10px;\n\n  i {\n    margin-left: 10px;\n    color: var(--color-primary);\n  }\n}\n\n.content {\n  margin-bottom: 15px;\n}\n\n.modalBox {\n  display: flex;\n  margin: -16px;\n  height: 70vh;\n  overflow-y: auto;\n  border-radius: var(--border-radius-l-g);\n}\n\n.menus {\n  width: 200px;\n  background-color: var(--color-bg-subtle);\n  padding: 20px;\n  position: sticky;\n  top: 0;\n\n  .menusTitle {\n    font-size: 16px;\n    padding-bottom: 10px;\n  }\n\n  .menuItem {\n    margin: 10px 0px;\n    padding: 10px;\n    cursor: pointer;\n    border-radius: 4px;\n    display: flex;\n    justify-content: flex-start;\n    align-items: center;\n\n    .prefixIcon {\n      font-size: 16px;\n      margin-right: 10px;\n    }\n\n    .rightSlot {\n      flex: 1;\n      display: flex;\n      justify-content: flex-end;\n    }\n\n    .rightSlotAbout {\n      i {\n        font-size: 18px;\n        color: var(--color-error);\n      }\n    }\n  }\n\n  .activeMenu {\n    color: var(--color-primary);\n    background-color: var(--color-hover-bg);\n  }\n}\n\n.menuContent {\n  min-height: 400px;\n  padding: 20px;\n  flex: 1;\n\n  .menuContentTitle {\n    font-size: 16px;\n    padding-bottom: 20px;\n  }\n}"
  },
  {
    "path": "chat2db-client/src/blocks/Setting/index.tsx",
    "content": "import React, { ReactNode, useEffect, useState } from 'react';\nimport classnames from 'classnames';\nimport Iconfont from '@/components/Iconfont';\nimport { Modal, Tooltip } from 'antd';\nimport i18n from '@/i18n';\nimport BaseSetting from './BaseSetting';\nimport AISetting from './AiSetting';\nimport ProxySetting from './ProxySetting';\nimport About from './About';\nimport styles from './index.less';\nimport { ILatestVersion } from '@/service/config';\nimport UpdateDetection, { IUpdateDetectionRef, UpdatedStatusEnum } from '@/blocks/Setting/UpdateDetection';\n\n// ---- store -----\nimport { useSettingStore, getAiSystemConfig, setAiSystemConfig } from '@/store/setting';\n\ninterface IProps {\n  className?: string;\n  render?: ReactNode;\n  noLogin?: boolean; // 用于在没有登录的页面使用，不显示ai设置等需要登录的功能\n  defaultArouse?: boolean; // 是否默认弹出\n  defaultMenu?: number; // 默认选中的菜单\n}\nexport interface IUpdateDetectionData extends ILatestVersion {\n  updatedStatusEnum: UpdatedStatusEnum;\n  needUpdate: boolean;\n}\n\nfunction Setting(props: IProps) {\n  const { className, noLogin = false, defaultArouse, defaultMenu = 0 } = props;\n  const [isModalVisible, setIsModalVisible] = useState(false);\n  const [currentMenu, setCurrentMenu] = useState<number>(defaultMenu);\n  const [updateDetectionData, setUpdateDetectionData] = useState<IUpdateDetectionData | null>(null);\n  const updateDetectionRef = React.useRef<IUpdateDetectionRef>(null);\n  const aiConfig = useSettingStore((state) => state.aiConfig);\n\n  useEffect(() => {\n    if (defaultArouse) {\n      showModal();\n    }\n  }, []);\n\n  useEffect(() => {\n    if (isModalVisible && !noLogin) {\n      getAiSystemConfig();\n    }\n  }, [isModalVisible]);\n\n  useEffect(() => {\n    if (!noLogin) {\n      getAiSystemConfig();\n    }\n  }, []);\n\n  const showModal = (_currentMenu: number = 0) => {\n    setCurrentMenu(_currentMenu);\n    setIsModalVisible(true);\n  };\n\n  const handleOk = () => {\n    setIsModalVisible(false);\n  };\n\n  const handleCancel = () => {\n    setIsModalVisible(false);\n  };\n\n  function changeMenu(t: any) {\n    setCurrentMenu(t);\n  }\n\n  const menusList = [\n    {\n      label: i18n('setting.nav.basic'),\n      icon: '\\ue795',\n      body: <BaseSetting />,\n      code: 'basic',\n    },\n    {\n      label: i18n('setting.nav.customAi'),\n      icon: '\\ue646',\n      body: <AISetting aiConfig={aiConfig} handleApplyAiConfig={setAiSystemConfig} />,\n      code: 'ai',\n    },\n    {\n      label: i18n('setting.nav.proxy'),\n      icon: '\\ue63f',\n      body: <ProxySetting />,\n      code: 'proxy',\n    },\n    {\n      label: i18n('setting.nav.aboutUs'),\n      icon: '\\ue65c',\n      rightSlot: updateDetectionData?.needUpdate && (\n        <div className={classnames(styles.rightSlot, styles.rightSlotAbout)}>\n          <Tooltip title={`发现新版本v${updateDetectionData?.version}`}>\n            <Iconfont code=\"&#xe69c;\" />\n          </Tooltip>\n        </div>\n      ),\n      body: <About updateDetectionRef={updateDetectionRef as any} updateDetectionData={updateDetectionData} />,\n      code: 'about',\n    },\n  ];\n\n  return (\n    <>\n      <Tooltip placement=\"right\" title={i18n('setting.title.setting')}>\n        <div\n          className={classnames(className, styles.box)}\n          onClick={() => {\n            showModal();\n          }}\n        >\n          {props.render ? props.render : <Iconfont className={styles.settingIcon} code=\"&#xe630;\" />}\n        </div>\n      </Tooltip>\n\n      <UpdateDetection\n        setUpdateDetectionData={setUpdateDetectionData}\n        updateDetectionData={updateDetectionData}\n        openSettingModal={showModal}\n        ref={updateDetectionRef}\n      />\n      <Modal\n        open={isModalVisible}\n        onOk={handleOk}\n        onCancel={handleCancel}\n        footer={false}\n        width={800}\n        maskClosable={false}\n      >\n        <div className={styles.modalBox}>\n          <div className={styles.menus}>\n            <div className={classnames(styles.menusTitle)}>{i18n('setting.title.setting')}</div>\n            {menusList.map((t, index) => {\n              // 如果是没有登录的页面，不显示ai设置等需要登录的功能\n              if (noLogin && index === 1) {\n                return false;\n              }\n              return (\n                <div\n                  key={index}\n                  onClick={changeMenu.bind(null, index)}\n                  className={classnames(styles.menuItem, {\n                    [styles.activeMenu]: t.label === menusList[currentMenu].label,\n                  })}\n                >\n                  <Iconfont className={styles.prefixIcon} code={t.icon} />\n                  {t.label}\n                  {t.rightSlot}\n                </div>\n              );\n            })}\n          </div>\n          <div className={styles.menuContent}>\n            <div className={classnames(styles.menuContentTitle)}>{menusList[currentMenu].label}</div>\n            {menusList[currentMenu].body}\n          </div>\n        </div>\n      </Modal>\n    </>\n  );\n}\n\nexport default Setting;\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/functions/deleteSequence.tsx",
    "content": "// 置顶表格\nimport React, { useState } from 'react';\nimport mysqlService from '@/service/sql';\nimport { Button, Checkbox } from 'antd';\nimport { openModal } from '@/store/common/components';\nimport styles from './deleteTable.less';\nimport i18n from '@/i18n';\n\nexport const deleteSequence = (treeNodeData,loadData) => {\n  openModal({\n    width: '450px',\n    content: <DeleteModalContent treeNodeData={treeNodeData} loadData={loadData} openModal={openModal} />,\n  });\n};\n\nexport const DeleteModalContent = (params: { treeNodeData: any; openModal: any; loadData: any }) => {\n  const { treeNodeData,loadData } = params;\n  // 禁用确定按钮\n  const [userChecked, setUserChecked] = useState<boolean>(false);\n\n  const onOk = () => {\n    const p: any = {\n      dataSourceId: treeNodeData.extraParams.dataSourceId,\n      databaseName: treeNodeData.extraParams.databaseName,\n      schemaName: treeNodeData.extraParams.schemaName,\n      tableName: treeNodeData.name,\n      sequenceName: treeNodeData.sequenceName\n    };\n    mysqlService.deleteSequence(p).then(() => {\n      loadData({\n        refresh: true,\n        treeNodeData: treeNodeData.parentNode\n      });\n      openModal(false);\n    });\n  };\n\n  return (\n    <div className={styles.deleteModalContent}>\n      <div className={styles.title}>{i18n('workspace.tree.delete.sequence.tip', `\"${treeNodeData.name}\"`)}</div>\n      <div className={styles.checkContainer}>\n        <Checkbox\n          value={userChecked}\n          onChange={(e) => {\n            setUserChecked(e.target.checked);\n          }}\n        >\n          {i18n('workspace.tree.delete.tip')}\n        </Checkbox>\n      </div>\n      <div className={styles.deleteTableFooter}>\n        <Button\n          type=\"primary\"\n          onClick={() => {\n            openModal(false);\n          }}\n        >\n          {i18n('common.button.cancel')}\n        </Button>\n        <Button disabled={!userChecked} onClick={onOk}>\n          {i18n('common.button.affirm')}\n        </Button>\n      </div>\n    </div>\n  );\n};\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/functions/deleteTable.less",
    "content": ".deleteTableFooter{\n  display: flex;\n  justify-content: center;\n  button{\n    margin: 0px 15px;\n  }\n\n}\n\n.checkContainer{\n  margin: 15px 0px 25px;\n}\n\n.deleteModalContent{\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  font-size: 16px;\n  font-weight: 500;\n  text-align: center;\n}"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/functions/deleteTable.tsx",
    "content": "// 置顶表格\nimport React, { useState } from 'react';\nimport mysqlService from '@/service/sql';\nimport { Button, Checkbox } from 'antd';\nimport { openModal } from '@/store/common/components';\nimport styles from './deleteTable.less';\nimport i18n from '@/i18n';\n\nexport const deleteTable = (treeNodeData,loadData) => {\n  openModal({\n    width: '450px',\n    content: <DeleteModalContent treeNodeData={treeNodeData} loadData={loadData} openModal={openModal} />,\n  });\n};\n\nexport const DeleteModalContent = (params: { treeNodeData: any; openModal: any; loadData: any }) => {\n  const { treeNodeData,loadData } = params;\n  // 禁用确定按钮\n  const [userChecked, setUserChecked] = useState<boolean>(false);\n\n  const onOk = () => {\n    const p: any = {\n      dataSourceId: treeNodeData.extraParams.dataSourceId,\n      databaseName: treeNodeData.extraParams.databaseName,\n      schemaName: treeNodeData.extraParams.schemaName,\n      tableName: treeNodeData.name,\n    };\n    mysqlService.deleteTable(p).then(() => {\n      loadData({\n        refresh: true,\n        treeNodeData: treeNodeData.parentNode\n      });\n      openModal(false);\n    });\n  };\n\n  return (\n    <div className={styles.deleteModalContent}>\n      <div className={styles.title}>{i18n('workspace.tree.delete.table.tip', `\"${treeNodeData.name}\"`)}</div>\n      <div className={styles.checkContainer}>\n        <Checkbox\n          value={userChecked}\n          onChange={(e) => {\n            setUserChecked(e.target.checked);\n          }}\n        >\n          {i18n('workspace.tree.delete.tip')}\n        </Checkbox>\n      </div>\n      <div className={styles.deleteTableFooter}>\n        <Button\n          type=\"primary\"\n          onClick={() => {\n            openModal(false);\n          }}\n        >\n          {i18n('common.button.cancel')}\n        </Button>\n        <Button disabled={!userChecked} onClick={onOk}>\n          {i18n('common.button.affirm')}\n        </Button>\n      </div>\n    </div>\n  );\n};\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/functions/openAsyncSql.ts",
    "content": "import { WorkspaceTabType } from '@/constants';\nimport sqlService from '@/service/sql';\nimport {createConsole} from '@/pages/main/workspace/store/console'\n\nexport const openView = (props:{\n  addWorkspaceTab: any;\n  treeNodeData: any;\n}) => {\n  const { treeNodeData } = props;\n  createConsole({\n    name: treeNodeData.name,\n    operationType: WorkspaceTabType.VIEW,\n    dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n    dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n    databaseType: treeNodeData.extraParams!.databaseType!,\n    databaseName: treeNodeData.extraParams?.databaseName,\n    schemaName: treeNodeData.extraParams?.schemaName,\n    loadSQL: ()=>{\n      return new Promise((resolve) => {\n        sqlService\n        .getViewDetail({\n          dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n          databaseType: treeNodeData.extraParams!.databaseType!,\n          databaseName: treeNodeData.extraParams!.databaseName!,\n          schemaName: treeNodeData.extraParams?.schemaName,\n          tableName: treeNodeData.name\n        } as any)\n        .then((res) => {\n          // 更新ddl\n          resolve(res.ddl);\n        });\n      });\n    }\n  })\n}\n\nexport const openFunction = (props:{\n  treeNodeData: any;\n}) => {\n  const { treeNodeData } = props;\n  createConsole({\n    name: treeNodeData.name,\n    operationType: WorkspaceTabType.FUNCTION,\n    dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n    dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n    databaseType: treeNodeData.extraParams!.databaseType!,\n    databaseName: treeNodeData.extraParams?.databaseName,\n    schemaName: treeNodeData.extraParams?.schemaName,\n    loadSQL: ()=>{\n      return new Promise((resolve) => {\n        sqlService\n        .getFunctionDetail({\n          name:treeNodeData.name,\n          dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n          databaseType: treeNodeData.extraParams!.databaseType!,\n          databaseName: treeNodeData.extraParams!.databaseName!,\n          schemaName: treeNodeData.extraParams?.schemaName,\n          functionName: treeNodeData.name\n        } as any)\n        .then((res) => {\n          // 更新ddl\n          resolve(res.functionBody);\n        });\n      });\n    }\n  })\n}\n\nexport const openProcedure = (props:{\n  treeNodeData: any;\n}) => {\n  const { treeNodeData } = props;\n  createConsole({\n    name: treeNodeData.name,\n    operationType: WorkspaceTabType.PROCEDURE,\n    dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n    dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n    databaseType: treeNodeData.extraParams!.databaseType!,\n    databaseName: treeNodeData.extraParams?.databaseName,\n    schemaName: treeNodeData.extraParams?.schemaName,\n    loadSQL: ()=>{\n      return new Promise((resolve) => {\n        sqlService\n        .getProcedureDetail({\n          dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n          databaseType: treeNodeData.extraParams!.databaseType!,\n          databaseName: treeNodeData.extraParams!.databaseName!,\n          schemaName: treeNodeData.extraParams?.schemaName,\n          procedureName: treeNodeData.name\n        } as any)\n        .then((res) => {\n          // 更新ddl\n          resolve(res.procedureBody);\n        });\n      });\n    }\n  })\n}\n\nexport const openTrigger = (props:{\n  treeNodeData: any;\n}) => {\n  const {treeNodeData } = props;\n  createConsole({\n    name: treeNodeData.name,\n    operationType: WorkspaceTabType.TRIGGER,\n    dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n    dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n    databaseType: treeNodeData.extraParams!.databaseType!,\n    databaseName: treeNodeData.extraParams?.databaseName,\n    schemaName: treeNodeData.extraParams?.schemaName,\n    loadSQL: ()=>{\n      return new Promise((resolve) => {\n        sqlService\n        .getTriggerDetail({\n          dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n          databaseType: treeNodeData.extraParams!.databaseType!,\n          databaseName: treeNodeData.extraParams!.databaseName!,\n          schemaName: treeNodeData.extraParams?.schemaName,\n          triggerName: treeNodeData.name\n        } as any)\n        .then((res) => {\n          // 更新ddl\n          resolve(res.triggerBody);\n        });\n      });\n    }\n  })\n}\n\nexport const openSequence = (props:{\n  treeNodeData: any;\n}) => {\n  const { treeNodeData } = props;\n  createConsole({\n    name: treeNodeData.name,\n    operationType: WorkspaceTabType.SEQUENCE,\n    dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n    dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n    databaseType: treeNodeData.extraParams!.databaseType!,\n    databaseName: treeNodeData.extraParams?.databaseName,\n    schemaName: treeNodeData.extraParams?.schemaName,\n    loadSQL: ()=>{\n      return new Promise((resolve) => {\n        sqlService\n        .exportCreateSequenceSql({\n          dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n          databaseType: treeNodeData.extraParams!.databaseType!,\n          databaseName: treeNodeData.extraParams!.databaseName!,\n          schemaName: treeNodeData.extraParams?.schemaName,\n          name: treeNodeData.name\n        } as any)\n        .then((res) => {\n          // 更新ddl\n          resolve(res);\n        });\n      });\n    }\n  })\n}\n\n\n\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/functions/pinTable.ts",
    "content": "// 置顶表格\nimport mysqlService from '@/service/sql';\nexport const handelPinTable = ({ treeNodeData,loadData }) => {\n  const api = treeNodeData.pinned ? 'deleteTablePin' : 'addTablePin';\n  mysqlService[api]({\n    dataSourceId: treeNodeData.extraParams.dataSourceId,\n    databaseName: treeNodeData.extraParams.databaseName,\n    schemaName: treeNodeData.extraParams.schemaName,\n    tableName: treeNodeData.name,\n  }).then(()=>{\n    loadData({\n      refresh: true,\n    })\n  })\n};\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/functions/refresh.ts",
    "content": "import { ITreeNode } from '@/typings';\n\nexport const refreshTreeNode = (props:{\n  treeNodeData: ITreeNode;\n}) => {\n  const { treeNodeData } = props;\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/functions/viewDDL.less",
    "content": ".monacoEditorBox{\n  border: 1px solid var(--color-border);\n  border-radius: 4px;\n  height: 60vh;\n  overflow: hidden;\n}"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/functions/viewDDL.tsx",
    "content": "// 置顶表格\nimport React from 'react';\nimport mysqlService from '@/service/sql';\nimport { v4 as uuid } from 'uuid';\nimport styles from './viewDDL.less';\n\nimport { openModal } from '@/store/common/components';\n\nimport MonacoEditor from '@/components/MonacoEditor';\n\nexport const viewDDL = (treeNodeData) => {\n  const getSql = () => {\n    return new Promise((resolve) => {\n      mysqlService\n        .exportCreateTableSql({\n          dataSourceId: treeNodeData.extraParams.dataSourceId,\n          databaseName: treeNodeData.extraParams.databaseName,\n          schemaName: treeNodeData.extraParams.schemaName,\n          name: treeNodeData.name,\n        })\n        .then((res) => {\n          resolve(res);\n        });\n    });\n  };\n\n  openModal({\n    title: `DDL-${treeNodeData.name}`,\n    width: '60%',\n    height: '60%',\n    footer: false,\n    content: (\n      <div className={styles.monacoEditorBox}>\n        <MonacoEditorAsync getSql={getSql} />\n      </div>\n    ),\n  });\n};\n\nexport const MonacoEditorAsync = (params: { getSql: any }) => {\n  const { getSql } = params;\n  const monacoEditorRef = React.useRef<any>();\n  getSql().then((sql) => {\n    monacoEditorRef.current.setValue(sql);\n  });\n  return <MonacoEditor id={uuid()} ref={monacoEditorRef} />;\n};\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/hooks/useGetRightClickMenu.ts",
    "content": "import { ITreeNode } from '@/typings';\nimport { OperationColumn, WorkspaceTabType, TreeNodeType } from '@/constants';\nimport i18n from '@/i18n';\nimport { v4 as uuid } from 'uuid';\n\n// ----- components -----\nimport { dataSourceFormConfigs } from '@/components/ConnectionEdit/config/dataSource';\nimport { IConnectionConfig } from '@/components/ConnectionEdit/config/types';\n\n// ----- config -----\nimport { ITreeConfigItem, treeConfig } from '../treeConfig';\nimport { useMemo } from 'react';\n\n// ----- store -----\nimport { createConsole, addWorkspaceTab } from '@/pages/main/workspace/store/console';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\n\n// ---- functions -----\nimport { openView, openFunction, openProcedure, openTrigger, openSequence} from '../functions/openAsyncSql';\nimport { handelPinTable } from '../functions/pinTable';\nimport { viewDDL } from '../functions/viewDDL';\nimport { deleteTable } from '../functions/deleteTable';\nimport { deleteSequence } from '../functions/deleteSequence';\n\n// ----- utils -----\nimport { compatibleDataBaseName } from '@/utils/database';\n\ninterface IProps {\n  treeNodeData: ITreeNode;\n  loadData: any;\n}\n\ninterface IOperationColumnConfigItem {\n  text: string;\n  icon: string;\n  doubleClickTrigger?: boolean;\n  handle: (treeNodeData: ITreeNode) => void;\n  discard?: boolean;\n}\n\ninterface IRightClickMenu {\n  key: number;\n  onClick: (treeNodeData: ITreeNode) => void;\n  type: OperationColumn;\n  doubleClickTrigger?: boolean;\n  labelProps: {\n    icon: string;\n    label: string;\n  };\n}\n\nexport const useGetRightClickMenu = (props: IProps) => {\n  const { treeNodeData, loadData } = props;\n\n  const { openCreateDatabaseModal, currentConnectionDetails } = useWorkspaceStore((state) => {\n    return {\n      openCreateDatabaseModal: state.openCreateDatabaseModal,\n      currentConnectionDetails: state.currentConnectionDetails,\n    };\n  });\n\n  const handelOpenCreateDatabaseModal = (type: 'database' | 'schema') => {\n\n    const relyOnParams = {\n      databaseType: treeNodeData.extraParams!.databaseType,\n      dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n      databaseName: treeNodeData.name,\n    }\n\n    openCreateDatabaseModal?.({\n      type,\n      relyOnParams,\n      executedCallback: () => {\n        loadData({\n          refresh: true,\n        });\n      },\n    });\n  };\n\n  const rightClickMenu = useMemo(() => {\n    // 拿出当前节点的配置\n    const treeNodeConfig: ITreeConfigItem = treeConfig[treeNodeData.treeNodeType];\n    const { operationColumn } = treeNodeConfig;\n\n    const dataSourceFormConfig = dataSourceFormConfigs.find((t: IConnectionConfig) => {\n      return t.type === treeNodeData.extraParams?.databaseType;\n    })!;\n\n    // 有些数据库不支持的操作，需要排除掉\n    function excludeSomeOperation() {\n      const excludes = dataSourceFormConfig.baseInfo.excludes;\n      const newOperationColumn: OperationColumn[] = [];\n      operationColumn?.map((item: OperationColumn) => {\n        let flag = false;\n        excludes?.map((t) => {\n          if (item === t) {\n            flag = true;\n          }\n        });\n        if (!flag) {\n          newOperationColumn.push(item);\n        }\n      });\n      return newOperationColumn;\n    }\n\n    const operationColumnConfig: { [key in string]: IOperationColumnConfigItem } = {\n      // 刷新\n      [OperationColumn.Refresh]: {\n        text: i18n('common.button.refresh'),\n        icon: '\\uec08',\n        handle: () => {\n          loadData?.({\n            refresh: true,\n          });\n        },\n      },\n\n      // 创建console\n      [OperationColumn.CreateConsole]: {\n        text: i18n('workspace.menu.queryConsole'),\n        icon: '\\ue619',\n        handle: () => {\n          createConsole({\n            dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n            dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n            databaseType: treeNodeData.extraParams!.databaseType!,\n            databaseName: treeNodeData.extraParams?.databaseName,\n            schemaName: treeNodeData.extraParams?.schemaName,\n          });\n        },\n      },\n\n      // 查看所有表\n      [OperationColumn.ViewAllTable]: {\n        text: i18n('workspace.menu.viewAllTable'),\n        icon: '\\ue611',\n        handle: () => {\n          addWorkspaceTab({\n            id: uuid(),\n            type: WorkspaceTabType.ViewAllTable,\n            title: `${treeNodeData.extraParams!.databaseName!}-tables`,\n            uniqueData: {\n              dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n              dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n              databaseType: treeNodeData.extraParams!.databaseType!,\n              databaseName: treeNodeData.extraParams?.databaseName,\n              schemaName: treeNodeData.extraParams?.schemaName,\n            },\n          })\n          \n        },\n      },\n\n      // 创建表\n      [OperationColumn.CreateTable]: {\n        text: i18n('editTable.button.createTable'),\n        icon: '\\ue792',\n        handle: () => {\n          addWorkspaceTab({\n            id: uuid(),\n            title: i18n('editTable.button.createTable'),\n            type: WorkspaceTabType.CreateTable,\n            uniqueData: {\n              dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n              databaseType: treeNodeData.extraParams!.databaseType!,\n              databaseName: treeNodeData.extraParams?.databaseName,\n              schemaName: treeNodeData.extraParams?.schemaName,\n              submitCallback: () => {loadData?.({refresh: true})},\n            },\n          });\n        },\n        discard: (treeNodeData.treeNodeType === TreeNodeType.DATABASE && currentConnectionDetails?.supportSchema),\n      },\n\n      // 删除表\n      [OperationColumn.DeleteTable]: {\n        text: i18n('workspace.menu.deleteTable'),\n        icon: '\\ue6a7',\n        handle: () => {\n          deleteTable(treeNodeData,loadData);\n        },\n      },\n\n      // 查看ddl\n      [OperationColumn.ViewDDL]: {\n        text: i18n('workspace.menu.ViewDDL'),\n        icon: '\\ue665',\n        handle: () => {\n          viewDDL(treeNodeData)\n        },\n      },\n\n      // 置顶\n      [OperationColumn.Pin]: {\n        text: treeNodeData.pinned ? i18n('workspace.menu.unPin') : i18n('workspace.menu.pin'),\n        icon: treeNodeData.pinned ? '\\ue61d' : '\\ue627',\n        handle: () => {\n          handelPinTable({\n            treeNodeData,\n            loadData: () => {\n              loadData({treeNodeData:treeNodeData.parentNode})\n            }\n          });\n        },\n      },\n\n      // 编辑表\n      [OperationColumn.EditTable]: {\n        text: i18n('workspace.menu.editTable'),\n        icon: '\\ue602',\n        handle: () => {\n          addWorkspaceTab({\n            id: `${OperationColumn.EditTable}-${treeNodeData.uuid}`,\n            title: treeNodeData?.name,\n            type: WorkspaceTabType.EditTable,\n            uniqueData: {\n              dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n              databaseType: treeNodeData.extraParams!.databaseType!,\n              databaseName: treeNodeData.extraParams?.databaseName,\n              schemaName: treeNodeData.extraParams?.schemaName,\n              tableName: treeNodeData?.name,\n              submitCallback: () => {\n                loadData({\n                  treeNodeData: treeNodeData.parentNode,\n                  refresh: true\n                })\n              },\n            },\n          });\n        },\n      },\n\n      // 复制名称\n      [OperationColumn.CopyName]: {\n        text: i18n('common.button.copyName'),\n        icon: '\\uec7a',\n        handle: () => {\n          navigator.clipboard.writeText(treeNodeData.name);\n        },\n      },\n\n      // 打开表\n      [OperationColumn.OpenTable]: {\n        text: i18n('workspace.menu.openTable'),\n        icon: '\\ue618',\n        doubleClickTrigger: true,\n        handle: () => {\n          const databaseName = compatibleDataBaseName(treeNodeData.name!, treeNodeData.extraParams!.databaseType);\n          addWorkspaceTab({\n            id: `${OperationColumn.OpenTable}-${treeNodeData.uuid}`,\n            title: treeNodeData.name,\n            type: WorkspaceTabType.EditTableData,\n            uniqueData: {\n              dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n              databaseType: treeNodeData.extraParams!.databaseType!,\n              databaseName: treeNodeData.extraParams?.databaseName,\n              schemaName: treeNodeData.extraParams?.schemaName,\n              tableName: treeNodeData.name,\n              sql: 'select * from ' + databaseName,\n            },\n          });\n        },\n      },\n\n      // 打开视图\n      [OperationColumn.OpenView]: {\n        text: i18n('workspace.menu.view'),\n        icon: '\\ue651',\n        doubleClickTrigger: true,\n        handle: () => {\n          openView({\n            addWorkspaceTab,\n            treeNodeData,\n          });\n        },\n      },\n\n      // 打开函数\n      [OperationColumn.OpenFunction]: {\n        text: i18n('workspace.menu.view'),\n        icon: '\\ue651',\n        doubleClickTrigger: true,\n        handle: () => {\n          openFunction({\n            addWorkspaceTab,\n            treeNodeData,\n          });\n        },\n      },\n\n      // 打开存储过程\n      [OperationColumn.OpenProcedure]: {\n        text: i18n('workspace.menu.view'),\n        icon: '\\ue651',\n        doubleClickTrigger: true,\n        handle: () => {\n          openProcedure({\n            addWorkspaceTab,\n            treeNodeData,\n          });\n        },\n      },\n\n      // 打开触发器\n      [OperationColumn.OpenTrigger]: {\n        text: i18n('workspace.menu.view'),\n        icon: '\\ue651',\n        doubleClickTrigger: true,\n        handle: () => {\n          openTrigger({\n            addWorkspaceTab,\n            treeNodeData,\n          });\n        },\n      },\n\n      // 打开序列\n      [OperationColumn.OpenSequence]: {\n        text: i18n('workspace.menu.view'),\n        icon: '\\ue651',\n        doubleClickTrigger: true,\n        handle: () => {\n          openSequence({\n            addWorkspaceTab,\n            treeNodeData,\n          });\n        },\n      },\n      //创建序列\n      [OperationColumn.CreateSequence]:{\n        text: i18n('editSequence.button.createSequence'),\n        icon: '\\ue792',\n        handle: () => {\n          addWorkspaceTab({\n            id: uuid(),\n            title: i18n('editSequence.button.createSequence'),\n            type: WorkspaceTabType.CreateSequence,\n            uniqueData: {\n              dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n              databaseType: treeNodeData.extraParams!.databaseType!,\n              databaseName: treeNodeData.extraParams?.databaseName,\n              schemaName: treeNodeData.extraParams?.schemaName,\n              submitCallback: () => {loadData?.({refresh: true})},\n            },\n          });\n        },\n        discard: (treeNodeData.treeNodeType === TreeNodeType.SEQUENCE && currentConnectionDetails?.supportSchema),\n      },\n      // 编辑序列\n      [OperationColumn.EditSequence]: {\n        text: i18n('workspace.menu.editSequence'),\n        icon: '\\ue602',\n        handle: () => {\n          addWorkspaceTab({\n            id: `${OperationColumn.EditSequence}-${treeNodeData.uuid}`,\n            title: treeNodeData?.name,\n            type: WorkspaceTabType.EditSequence,\n            uniqueData: {\n              dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n              databaseType: treeNodeData.extraParams!.databaseType!,\n              databaseName: treeNodeData.extraParams?.databaseName,\n              schemaName: treeNodeData.extraParams?.schemaName,\n              tableName: treeNodeData?.name,\n              submitCallback: () => {\n                loadData({\n                  treeNodeData: treeNodeData.parentNode,\n                  refresh: true\n                })\n              },\n            },\n          });\n        },\n      },\n      // 删除序列\n      [OperationColumn.DeleteSequence]: {\n        text: i18n('workspace.menu.deleteSequence'),\n        icon: '\\ue6a7',\n        handle: () => {\n          deleteSequence(treeNodeData,loadData);\n        },\n      },\n\n      // 创建数据库\n      [OperationColumn.CreateDatabase]: {\n        text: i18n('workspace.menu.createDatabase'),\n        icon: '\\ue816',\n        handle: () => {\n          handelOpenCreateDatabaseModal('database');\n        },\n      },\n\n      // 创建schema\n      [OperationColumn.CreateSchema]: {\n        text: i18n('workspace.menu.createSchema'),\n        icon: '\\ue696',\n        handle: () => {\n          handelOpenCreateDatabaseModal('schema');\n        },\n        discard: !currentConnectionDetails?.supportSchema,\n      },\n    };\n\n    // 根据配置生成右键菜单\n    const finalList: IRightClickMenu[] = [];\n    excludeSomeOperation().forEach((t, i) => {\n      const concrete = operationColumnConfig[t];\n      if (!concrete.discard) {\n        finalList.push({\n          key: i,\n          onClick: concrete?.handle,\n          type: t,\n          doubleClickTrigger: concrete.doubleClickTrigger,\n          labelProps: {\n            icon: concrete?.icon,\n            label: concrete?.text,\n          },\n        });\n      }\n    });\n    return finalList;\n  }, [treeNodeData]);\n\n  return rightClickMenu;\n};\n\nexport const getRightClickMenu = (props: IProps) => {\n  const { treeNodeData, loadData } = props;\n\n  const openCreateDatabaseModal = useWorkspaceStore.getState().openCreateDatabaseModal;\n  const currentConnectionDetails = useWorkspaceStore.getState().currentConnectionDetails;\n\n  const handelOpenCreateDatabaseModal = (type: 'database' | 'schema') => {\n\n    const relyOnParams = {\n      databaseType: treeNodeData.extraParams!.databaseType,\n      dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n      databaseName: treeNodeData.name,\n    }\n\n    openCreateDatabaseModal?.({\n      type,\n      relyOnParams,\n      executedCallback: () => {\n        loadData({\n          refresh: true,\n        });\n      },\n    });\n  };\n\n  // 拿出当前节点的配置\n  const treeNodeConfig: ITreeConfigItem = treeConfig[treeNodeData.treeNodeType];\n  const { operationColumn } = treeNodeConfig;\n\n  const dataSourceFormConfig = dataSourceFormConfigs.find((t: IConnectionConfig) => {\n    return t.type === treeNodeData.extraParams?.databaseType;\n  })!;\n\n  // 有些数据库不支持的操作，需要排除掉\n  function excludeSomeOperation() {\n    const excludes = dataSourceFormConfig.baseInfo.excludes;\n    const newOperationColumn: OperationColumn[] = [];\n    operationColumn?.map((item: OperationColumn) => {\n      let flag = false;\n      excludes?.map((t) => {\n        if (item === t) {\n          flag = true;\n        }\n      });\n      if (!flag) {\n        newOperationColumn.push(item);\n      }\n    });\n    return newOperationColumn;\n  }\n\n  const operationColumnConfig: { [key in string]: IOperationColumnConfigItem } = {\n    // 刷新\n    [OperationColumn.Refresh]: {\n      text: i18n('common.button.refresh'),\n      icon: '\\uec08',\n      handle: () => {\n        loadData?.({\n          refresh: true,\n        });\n      },\n    },\n\n    // 创建console\n    [OperationColumn.CreateConsole]: {\n      text: i18n('workspace.menu.queryConsole'),\n      icon: '\\ue619',\n      handle: () => {\n        createConsole({\n          dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n          dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n          databaseType: treeNodeData.extraParams!.databaseType!,\n          databaseName: treeNodeData.extraParams?.databaseName,\n          schemaName: treeNodeData.extraParams?.schemaName,\n        });\n      },\n    },\n\n    // 查看所有表\n    [OperationColumn.ViewAllTable]: {\n      text: i18n('workspace.menu.viewAllTable'),\n      icon: '\\ue611',\n      handle: () => {\n        addWorkspaceTab({\n          id: uuid(),\n          type: WorkspaceTabType.ViewAllTable,\n          title: `${treeNodeData.extraParams!.databaseName!}-tables`,\n          uniqueData: {\n            dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n            dataSourceName: treeNodeData.extraParams!.dataSourceName!,\n            databaseType: treeNodeData.extraParams!.databaseType!,\n            databaseName: treeNodeData.extraParams?.databaseName,\n            schemaName: treeNodeData.extraParams?.schemaName,\n          },\n        })\n        \n      },\n    },\n\n    // 创建表\n    [OperationColumn.CreateTable]: {\n      text: i18n('editTable.button.createTable'),\n      icon: '\\ue792',\n      handle: () => {\n        addWorkspaceTab({\n          id: uuid(),\n          title: i18n('editTable.button.createTable'),\n          type: WorkspaceTabType.CreateTable,\n          uniqueData: {\n            dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n            databaseType: treeNodeData.extraParams!.databaseType!,\n            databaseName: treeNodeData.extraParams?.databaseName,\n            schemaName: treeNodeData.extraParams?.schemaName,\n            submitCallback: () => {treeNodeData.loadData?.({refresh: true})},\n          },\n        });\n      },\n      discard: (treeNodeData.treeNodeType === TreeNodeType.DATABASE && currentConnectionDetails?.supportSchema),\n    },\n\n    // 删除表\n    [OperationColumn.DeleteTable]: {\n      text: i18n('workspace.menu.deleteTable'),\n      icon: '\\ue6a7',\n      handle: () => {\n        deleteTable(treeNodeData);\n      },\n    },\n\n    // 查看ddl\n    [OperationColumn.ViewDDL]: {\n      text: i18n('workspace.menu.ViewDDL'),\n      icon: '\\ue665',\n      handle: () => {\n        viewDDL(treeNodeData)\n      },\n    },\n\n    // 置顶\n    [OperationColumn.Pin]: {\n      text: treeNodeData.pinned ? i18n('workspace.menu.unPin') : i18n('workspace.menu.pin'),\n      icon: treeNodeData.pinned ? '\\ue61d' : '\\ue627',\n      handle: () => {\n        handelPinTable({treeNodeData, loadData: treeNodeData.parentNode!.loadData!});\n      },\n    },\n\n    // 编辑表\n    [OperationColumn.EditTable]: {\n      text: i18n('workspace.menu.editTable'),\n      icon: '\\ue602',\n      handle: () => {\n        addWorkspaceTab({\n          id: `${OperationColumn.EditTable}-${treeNodeData.uuid}`,\n          title: treeNodeData?.name,\n          type: WorkspaceTabType.EditTable,\n          uniqueData: {\n            dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n            databaseType: treeNodeData.extraParams!.databaseType!,\n            databaseName: treeNodeData.extraParams?.databaseName,\n            schemaName: treeNodeData.extraParams?.schemaName,\n            tableName: treeNodeData?.name,\n            submitCallback: () => {treeNodeData.parentNode?.loadData?.({refresh: true})},\n          },\n        });\n      },\n    },\n\n    // 复制名称\n    [OperationColumn.CopyName]: {\n      text: i18n('common.button.copyName'),\n      icon: '\\uec7a',\n      handle: () => {\n        navigator.clipboard.writeText(treeNodeData.name);\n      },\n    },\n\n    // 打开表\n    [OperationColumn.OpenTable]: {\n      text: i18n('workspace.menu.openTable'),\n      icon: '\\ue618',\n      doubleClickTrigger: true,\n      handle: () => {\n        const databaseName = compatibleDataBaseName(treeNodeData.name!, treeNodeData.extraParams!.databaseType);\n        addWorkspaceTab({\n          id: `${OperationColumn.OpenTable}-${treeNodeData.uuid}`,\n          title: treeNodeData.name,\n          type: WorkspaceTabType.EditTableData,\n          uniqueData: {\n            dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n            databaseType: treeNodeData.extraParams!.databaseType!,\n            databaseName: treeNodeData.extraParams?.databaseName,\n            schemaName: treeNodeData.extraParams?.schemaName,\n            tableName: treeNodeData.name,\n            sql: 'select * from ' + databaseName,\n          },\n        });\n      },\n    },\n\n    // 打开视图\n    [OperationColumn.OpenView]: {\n      text: i18n('workspace.menu.view'),\n      icon: '\\ue651',\n      doubleClickTrigger: true,\n      handle: () => {\n        openView({\n          addWorkspaceTab,\n          treeNodeData,\n        });\n      },\n    },\n\n    // 打开函数\n    [OperationColumn.OpenFunction]: {\n      text: i18n('workspace.menu.view'),\n      icon: '\\ue651',\n      doubleClickTrigger: true,\n      handle: () => {\n        openFunction({\n          addWorkspaceTab,\n          treeNodeData,\n        });\n      },\n    },\n    \n    // 打开序列\n    [OperationColumn.OpenSequence]: {\n      text: i18n('workspace.menu.view'),\n      icon: '\\ue651',\n      doubleClickTrigger: true,\n      handle: () => {\n        openSequence({\n          addWorkspaceTab,\n          treeNodeData,\n        });\n      },\n    },\n\n    // 删除序列\n    [OperationColumn.DeleteSequence]: {\n      text: i18n('workspace.menu.deleteSequence'),\n      icon: '\\ue6a7',\n      handle: () => {\n        deleteSequence(treeNodeData);\n      },\n    },\n    // 创建序列\n    [OperationColumn.CreateSequence]: {\n      text: i18n('editSequence.button.createSequence'),\n      icon: '\\ue792',\n      handle: () => {\n        addWorkspaceTab({\n          id: uuid(),\n          title: i18n('editSequence.button.createSequence'),\n          type: WorkspaceTabType.CreateSequence,\n          uniqueData: {\n            dataSourceId: treeNodeData.extraParams!.dataSourceId!,\n            databaseType: treeNodeData.extraParams!.databaseType!,\n            databaseName: treeNodeData.extraParams?.databaseName,\n            schemaName: treeNodeData.extraParams?.schemaName,\n            submitCallback: () => {treeNodeData.loadData?.({refresh: true})},\n          },\n        });\n      },\n      discard: (treeNodeData.treeNodeType === TreeNodeType.SEQUENCES && currentConnectionDetails?.supportSchema),\n    },\n    // 打开存储过程\n    [OperationColumn.OpenProcedure]: {\n      text: i18n('workspace.menu.view'),\n      icon: '\\ue651',\n      doubleClickTrigger: true,\n      handle: () => {\n        openProcedure({\n          addWorkspaceTab,\n          treeNodeData,\n        });\n      },\n    },\n\n    // 打开触发器\n    [OperationColumn.OpenTrigger]: {\n      text: i18n('workspace.menu.view'),\n      icon: '\\ue651',\n      doubleClickTrigger: true,\n      handle: () => {\n        openTrigger({\n          addWorkspaceTab,\n          treeNodeData,\n        });\n      },\n    },\n\n    // 创建数据库\n    [OperationColumn.CreateDatabase]: {\n      text: i18n('workspace.menu.createDatabase'),\n      icon: '\\ue816',\n      handle: () => {\n        handelOpenCreateDatabaseModal('database');\n      },\n    },\n\n    // 创建schema\n    [OperationColumn.CreateSchema]: {\n      text: i18n('workspace.menu.createSchema'),\n      icon: '\\ue696',\n      handle: () => {\n        handelOpenCreateDatabaseModal('schema');\n      },\n      discard: !currentConnectionDetails?.supportSchema,\n    },\n  };\n  \n  // 根据配置生成右键菜单\n  const finalList: IRightClickMenu[] = [];\n  excludeSomeOperation().forEach((t,i) => {\n    const concrete = operationColumnConfig[t];\n    if (!concrete.discard) {\n      finalList.push({\n        key: i,\n        onClick: concrete?.handle,\n        type: t,\n        doubleClickTrigger: concrete.doubleClickTrigger,\n        labelProps: {\n          icon: concrete?.icon,\n          label: concrete?.text,\n        },\n      });\n    }\n  });\n  return finalList;\n};\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/hooks/useTreeNodeFocus.ts",
    "content": "import { useTreeStore } from '../treeStore';\n\nexport const useTreeNodeFocus = (treeId) => {\n  const focusId = useTreeStore((state) => state.focusId);\n  return focusId === treeId;\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/index.less",
    "content": "@import '../../styles/var.less';\n\n.scrollBox {\n  overflow-y: auto;\n  padding: 2px 6px 10px;\n  box-sizing: border-box;\n  height: 100%;\n}\n\n.treeBox {\n  overflow: hidden;\n  height: 100%;\n}\n\n.treeListHolder {\n  height: calc(var(--tree-node-count) * 26px);\n  min-height: 100%;\n}\n\n// 如果treeBox滚动的高度>0那么久加一个上边框\n.treeBoxScroll {\n  border-top: 1px solid var(--color-border-secondary);\n}\n\n.leftModuleTitleShadow {\n  border-top: 1px solid red;\n}\n\n.treeNode {\n  display: flex;\n  align-items: center;\n  overflow: hidden;\n  height: 26px;\n  border-radius: 4px;\n  opacity: 1;\n  cursor: pointer;\n  transition: opacity 0.05s ease-in, height 0.1s ease-in;\n  user-select: none;\n  padding-right: 6px;\n\n  &:hover {\n  }\n}\n\n.treeNodeFocus {\n  // background-color: var(--color-hover-bg);\n  // background-color: var(--color-primary-bg-hover);\n  background-color: var(--color-primary-hover);\n  // background-color: var(--color-primary);\n  .right {\n    // color: var(--color-primary);\n    color: var(--color-bg-base);\n  }\n  .indent {\n    &::before {\n      opacity: 0;\n    }\n  }\n  .type {\n    color: var(--color-bg-base);\n  }\n}\n\n.left {\n  display: flex;\n  align-items: center;\n  height: 100%;\n  flex-shrink: 0;\n}\n\n.right {\n  flex: 1;\n  display: flex;\n  align-items: center;\n  height: 100%;\n  padding-left: 4px;\n  border-radius: 2px;\n  .moreBox {\n    width: 22px;\n    height: 100%;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    opacity: 0;\n  }\n\n  .moreButton {\n    flex-shrink: 0;\n    transform: rotate(90deg);\n  }\n}\n\n.arrows {\n  flex-shrink: 0;\n  height: 20px;\n  width: 18px;\n  display: flex;\n  align-items: center;\n  transform: rotate(0deg);\n  transition: transform 0.2s ease-in;\n}\n\n.loadingArrows {\n  display: flex;\n}\n\n.arrowsIcon {\n  display: inline-block;\n  transform: rotate(0deg);\n  font-size: 12px;\n}\n\n.rotateArrowsIcon {\n  transform: rotate(90deg);\n}\n\n.dblclickArea {\n  display: flex;\n  flex: 1;\n  height: 26px;\n}\n\n.typeIcon {\n  height: 26px;\n  display: flex;\n  align-items: center;\n  width: 20px;\n}\n\n.typeImg {\n  height: 20px;\n  width: 20px;\n  background-repeat: no-repeat;\n  background-size: 20px 20px;\n}\n\n.contentText {\n  width: 0;\n  flex: 1;\n  display: flex;\n  align-items: center;\n}\n\n.name {\n  .f-lines(1);\n  line-height: 20px;\n}\n\n.type {\n  font-size: 11px;\n  flex-shrink: 0;\n  line-height: 20px;\n  color: var(--color-text-tertiary);\n  margin-left: 10px;\n}\n\n.describe {\n  flex: 1;\n  font-size: 10px;\n  margin-left: 20px;\n  color: var(--color-text-tertiary);\n  .f-single-line();\n}\n\n.indent {\n  width: 20px;\n  height: 100%;\n  position: relative;\n  &::before {\n    position: absolute;\n    top: 0;\n    right: 9px;\n    bottom: -4px;\n    border-right: 1px solid var(--color-border);\n    content: '';\n  }\n}\n\n.hiddenTreeNode {\n  height: 0;\n  opacity: 0;\n  transition: opacity 0.2s ease-in, height 0.1s ease-in;\n\n  .arrows {\n    transform: rotate(0deg);\n    transition: transform 0.2s ease-in;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/index.tsx",
    "content": "import React, { memo, useEffect, useMemo, useState, createContext, useContext } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport Iconfont from '@/components/Iconfont';\nimport { Tooltip, Dropdown } from 'antd';\nimport { ITreeNode } from '@/typings';\nimport { TreeNodeType, databaseMap } from '@/constants';\nimport { treeConfig, switchIcon, ITreeConfigItem } from './treeConfig';\nimport { useCommonStore } from '@/store/common';\nimport { setCurrentWorkspaceGlobalExtend } from '@/pages/main/workspace/store/common';\nimport LoadingGracile from '@/components/Loading/LoadingGracile';\nimport { setFocusId, setFocusTreeNode, useTreeStore, clearTreeStore } from './treeStore';\nimport { useGetRightClickMenu } from './hooks/useGetRightClickMenu';\nimport MenuLabel from '@/components/MenuLabel';\nimport LoadingContent from '@/components/Loading/LoadingContent';\nimport { cloneDeep } from 'lodash';\n// import { flushSync } from 'react-dom';\n\ninterface IProps {\n  className?: string;\n  treeData: ITreeNode[] | null;\n  searchValue: string;\n}\n\ninterface TreeNodeIProps {\n  data: ITreeNode;\n  level: number;\n}\n\ninterface IContext {\n  treeData: ITreeNode[];\n  setTreeData: (value: ITreeNode[] | null) => void;\n  searchTreeData: ITreeNode[] | null;\n  setSearchTreeData: (value: ITreeNode[] | null) => void;\n}\n\nexport const Context = createContext<IContext>({} as any);\n\n// 树转平级\nconst smoothTree = (treeData: ITreeNode[], result: ITreeNode[] = [], parentNode?: ITreeNode) => {\n  treeData.forEach((item) => {\n    if (parentNode) {\n      item.parentNode = parentNode;\n      item.level = (parentNode.level || 0) + 1;\n    }\n    result.push(item);\n    if (item.children) {\n      smoothTree(item.children, result, item);\n    }\n  });\n  return result;\n};\n\n// 平级转树\nfunction tranListToTreeData(list:ITreeNode[], rootValue) {\n  const arr:ITreeNode[] = []\n  list.forEach((item:ITreeNode) => {\n    if (item.parentNode?.uuid === rootValue) {\n      arr.push(item)\n      const children = tranListToTreeData(list, item.uuid)\n      if (children.length) {\n        item.children = children\n      }\n    }\n  })\n  return arr\n}\n\n// 判断是否匹配\nconst isMatch = (target: string, searchValue: string) => {\n  const reg = new RegExp(searchValue, 'i');\n  return reg.test(target || '');\n};\n\n// 树结构搜索\nfunction searchTree(treeData: ITreeNode[], searchValue: string): ITreeNode[] {\n  let result: ITreeNode[] = [];\n\n  // 深度优先遍历\n  function dfs(node: ITreeNode, path: ITreeNode[] = []) {\n    if (isMatch(node.name, searchValue)) {\n      result = [...result, ...path, node];\n      return;\n    }\n    if (!node.children) return;\n    node.children.forEach((child) => {\n      dfs(child, [...path, node]);\n    });\n  }\n\n  // 遍历树\n  treeData.forEach((node) => dfs(node));\n\n  // 根据uuid去重\n  const deWeightList: ITreeNode[] = [];\n  result.forEach((item) => {\n    // 如果不匹配，说明该节点为path，不需要保留该节点的子元素，就把children置空\n    if (!isMatch(item.name, searchValue)) {\n      item.children = null;\n    }\n    deWeightList.findIndex((i) => i.uuid === item.uuid) === -1 && deWeightList.push(item);\n  });\n\n  return tranListToTreeData(deWeightList, undefined);\n}\n\nconst itemHeight = 26; // 每个 item 的高度\nconst paddingCount = 2;\n\nconst Tree = (props: IProps) => {\n  const { className, treeData: outerTreeData, searchValue } = props;\n  const [treeData, setTreeData] = useState<ITreeNode[] | null>(null);\n  const [smoothTreeData, setSmoothTreeData] = useState<ITreeNode[]>([]);\n  const [searchTreeData, setSearchTreeData] = useState<ITreeNode[] | null>(null); // 搜索结果\n  const [searchSmoothTreeData, setSearchSmoothTreeData] = useState<ITreeNode[] | null>(null); // 搜索结果 平级\n\n  const [scrollTop, setScrollTop] = useState(0); // 滚动位置 // 继续需要渲染的 item 索引有哪些\n\n  const startIdx = useMemo(() => {\n    let _startIdx = Math.floor(scrollTop / itemHeight);\n    _startIdx = Math.max(_startIdx - paddingCount, 0); // 处理越界情况\n    return _startIdx;\n  }, [scrollTop]);\n\n  const top = itemHeight * startIdx; // 第一个渲染的 item 到顶部距离\n\n  // 清空treeStore\n  useEffect(() => {\n    return () => {\n      clearTreeStore();\n    }\n  }, [searchValue]);\n\n  useEffect(() => {\n    setTreeData(outerTreeData);\n    setScrollTop(0);\n  }, [outerTreeData]);\n\n  useEffect(() => {\n    if (treeData) {\n      const result: ITreeNode[] = [];\n      smoothTree(treeData, result);\n      setSmoothTreeData(result);\n    } else {\n      setSmoothTreeData([]);\n    }\n  }, [treeData]);\n\n  // 搜索结果转平级\n  useEffect(() => {\n    if (searchTreeData) {\n      const result: ITreeNode[] = [];\n      smoothTree(searchTreeData, result);\n      setSearchSmoothTreeData(result);\n    } else {\n      setSearchSmoothTreeData(null);\n    }\n  }, [searchTreeData]);\n\n  const treeNodes = useMemo(() => {\n    const realNodeList = (searchSmoothTreeData || smoothTreeData).slice(startIdx, startIdx + 50);\n    return realNodeList.map((item) => {\n      return <TreeNode key={item.uuid} level={item.level || 0} data={item} />;\n    });\n  }, [smoothTreeData, searchSmoothTreeData, startIdx]);\n\n  useEffect(() => {\n    if (searchValue && treeData) {\n      const _searchTreeData = searchTree(cloneDeep(treeData), searchValue);\n      setSearchTreeData(_searchTreeData);\n      setScrollTop(0);\n    } else {\n      setSearchTreeData(null);\n    }\n  }, [searchValue]);\n\n  return (\n    <LoadingContent isLoading={!treeData} className={classnames(className)}>\n      <Context.Provider\n        value={{\n          treeData: treeData!,\n          setTreeData: setTreeData!,\n          searchTreeData, \n          setSearchTreeData\n        }}\n      >\n        <div\n          className={classnames(styles.scrollBox)}\n          onScroll={(e: any) => {\n            setScrollTop(e.target.scrollTop);\n          }}\n        >\n          <div\n            className={styles.treeListHolder}\n            style={{ '--tree-node-count': (searchSmoothTreeData || smoothTreeData)?.length } as any}\n          >\n            <div style={{ height: top }} />\n            {treeNodes}\n          </div>\n        </div>\n      </Context.Provider>\n    </LoadingContent>\n  );\n};\n\nconst TreeNode = memo((props: TreeNodeIProps) => {\n  const { data: treeNodeData, level } = props;\n  const [isLoading, setIsLoading] = useState(false);\n  const indentArr = new Array(level).fill('indent');\n  const { treeData, setTreeData, searchTreeData, setSearchTreeData } = useContext(Context);\n\n  // 加载数据\n  function loadData(_props?: { refresh: boolean; pageNo: number; treeNodeData?: ITreeNode }) {\n    const _treeNodeData = _props?.treeNodeData || props.data;\n    const treeNodeConfig: ITreeConfigItem = treeConfig[_treeNodeData.pretendNodeType || _treeNodeData.treeNodeType];\n    setIsLoading(true);\n    if (_props?.pageNo === 1 || !_props?.pageNo) {\n      insertData(treeData!, _treeNodeData.uuid!, null,[treeData, setTreeData]);\n      if(searchTreeData){\n        insertData(searchTreeData!, _treeNodeData.uuid!, null,[searchTreeData, setSearchTreeData]);\n      }\n    }\n\n    treeNodeConfig\n      .getChildren?.({\n        ..._treeNodeData.extraParams,\n        extraParams: {\n          ..._treeNodeData.extraParams,\n        },\n        refresh: _props?.refresh || false,\n        pageNo: _props?.pageNo || 1,\n      })\n      .then((res: any) => {\n        if (res.length || res.data) {\n          if (res.data) {\n            insertData(treeData!, _treeNodeData.uuid!, res.data, [treeData, setTreeData]);\n            if(searchTreeData){\n              insertData(searchTreeData!, _treeNodeData.uuid!, res.data,[searchTreeData, setSearchTreeData]);\n            }\n            if (res.hasNextPage) {\n              loadData({\n                refresh: _props?.refresh || false,\n                pageNo: res.pageNo + 1,\n              });\n            }\n          } else {\n            insertData(treeData!, _treeNodeData.uuid!, res,[treeData, setTreeData]);\n            if(searchTreeData){\n              insertData(searchTreeData!, _treeNodeData.uuid!, res,[searchTreeData, setSearchTreeData]);\n            }\n          }\n          setIsLoading(false);\n        } else {\n          // 处理树可能出现不连续的情况\n          if (treeNodeConfig.next) {\n            _treeNodeData.pretendNodeType = treeNodeConfig.next;\n            loadData();\n          } else {\n            insertData(treeData!, _treeNodeData.uuid!, [],[treeData, setTreeData]);\n            if(searchTreeData){\n              insertData(searchTreeData!, _treeNodeData.uuid!, [],[searchTreeData, setSearchTreeData]);\n            }\n            setIsLoading(false);\n          }\n        }\n      })\n      .catch(() => {\n        setIsLoading(false);\n      });\n  }\n\n  // 当前节点是否是focus\n  const isFocus = useTreeStore((state) => state.focusId) === treeNodeData.uuid;\n\n  //  在treeData中找到对应的节点，插入数据\n  const insertData = (_treeData: ITreeNode[], uuid: string, data: any, originalDataList:any): ITreeNode | null => {\n    const [originalData,setOriginalData] = originalDataList\n    let result: ITreeNode | null = null;\n    for (let i = 0; i < _treeData?.length; i++) {\n      if (_treeData[i].uuid === uuid) {\n        result = _treeData[i];\n        if (data) {\n          data.map((item: any) => {\n            item.parentNode = result;\n          });\n          result.children = [...(result.children || []), ...(data || [])];\n        } else {\n          result.children = null;\n        }\n        setOriginalData?.(cloneDeep([...(originalData || [])]));\n        break;\n      } else {\n        if (_treeData[i].children) {\n          result = insertData(_treeData[i].children!, uuid, data, originalDataList);\n          if (result) {\n            break;\n          }\n        }\n      }\n    }\n    return result;\n  };\n\n  //展开-收起\n  const handleClick = () => {\n    if (treeNodeData?.children) {\n      insertData(treeData!, treeNodeData.uuid!, null,[treeData, setTreeData]);\n      if(searchTreeData){\n        insertData(searchTreeData!, treeNodeData.uuid!, null,[searchTreeData, setSearchTreeData]);\n      }\n    } else {\n      loadData();\n    }\n  };\n\n  // 找到对应的icon\n  const recognizeIcon = (treeNodeType: TreeNodeType) => {\n    if (treeNodeType === TreeNodeType.DATA_SOURCE) {\n      return databaseMap[treeNodeData.extraParams!.databaseType!]?.icon;\n    } else {\n      return (\n        switchIcon[treeNodeType]?.[treeNodeData.children ? 'unfoldIcon' : 'icon'] || switchIcon[treeNodeType]?.icon\n      );\n    }\n  };\n\n  // 点击节点\n  const handelClickTreeNode = () => {\n    useCommonStore.setState({\n      focusedContent: (treeNodeData.name || '') as any,\n    });\n    if(treeNodeData.treeNodeType === TreeNodeType.TABLE){\n      setCurrentWorkspaceGlobalExtend({\n        code: 'viewDDL',\n        uniqueData: {\n          dataSourceId: treeNodeData.extraParams?.dataSourceId,\n          dataSourceName: treeNodeData.extraParams?.dataSourceName,\n          databaseName: treeNodeData.extraParams?.databaseName,\n          databaseType: treeNodeData.extraParams?.databaseType,\n          schemaName: treeNodeData.extraParams?.schemaName,\n          tableName: treeNodeData.name,\n        }\n      });\n    }\n    setFocusId(treeNodeData.uuid || '');\n\n    setFocusTreeNode({\n      dataSourceId: treeNodeData.extraParams!.dataSourceId,\n      dataSourceName: treeNodeData.extraParams!.dataSourceName,\n      databaseType: treeNodeData.extraParams!.databaseType,\n      databaseName: treeNodeData.extraParams?.databaseName,\n      schemaName: treeNodeData.extraParams?.schemaName,\n    });\n  };\n\n  // 双击节点\n  const handelDoubleClickTreeNode = () => {\n    if (\n      treeNodeData.treeNodeType === TreeNodeType.TABLE ||\n      treeNodeData.treeNodeType === TreeNodeType.VIEW ||\n      treeNodeData.treeNodeType === TreeNodeType.PROCEDURE ||\n      treeNodeData.treeNodeType === TreeNodeType.FUNCTION ||\n      treeNodeData.treeNodeType === TreeNodeType.SEQUENCE ||\n      treeNodeData.treeNodeType === TreeNodeType.TRIGGER\n    ) {\n      rightClickMenu.find((item) => item.doubleClickTrigger)?.onClick(treeNodeData);\n    } else {\n      handleClick();\n    }\n  };\n\n  const rightClickMenu = useGetRightClickMenu({\n    treeNodeData,\n    loadData,\n  });\n\n  const treeNodeDom = useMemo(() => {\n    const dropdownsItems: any = rightClickMenu.map((item) => {\n      return {\n        key: item.key,\n        onClick: () => {\n          item.onClick(treeNodeData);\n        },\n        label: <MenuLabel icon={item.labelProps.icon} label={item.labelProps.label} />,\n      };\n    });\n    return (\n      <Dropdown\n        trigger={['contextMenu']}\n        menu={{\n          items: dropdownsItems,\n          style: dropdownsItems?.length ? {} : { display: 'none' }, // 有菜单项才显示\n        }}\n        overlayStyle={{\n          zIndex: 1080,\n        }}\n      >\n        <Tooltip placement=\"right\" color={window._AppThemePack?.colorPrimary} title={treeNodeData.comment}>\n          <div\n            className={classnames(styles.treeNode, { [styles.treeNodeFocus]: isFocus })}\n            onClick={handelClickTreeNode}\n            onContextMenu={handelClickTreeNode}\n            onDoubleClick={handelDoubleClickTreeNode}\n            data-chat2db-general-can-copy-element\n          >\n            <div className={styles.left}>\n              {indentArr.map((item, i) => {\n                return <div key={i} className={styles.indent} />;\n              })}\n            </div>\n            <div className={styles.right}>\n              {!treeNodeData.isLeaf && (\n                <div onClick={handleClick} className={classnames(styles.arrows, { [styles.loadingArrows]: isLoading })}>\n                  {isLoading ? (\n                    <LoadingGracile />\n                  ) : (\n                    <Iconfont\n                      className={classnames(styles.arrowsIcon, {\n                        [styles.rotateArrowsIcon]: treeNodeData.children,\n                      })}\n                      code=\"&#xe641;\"\n                    />\n                  )}\n                </div>\n              )}\n              <div className={styles.dblclickArea}>\n                <div className={styles.typeIcon}>\n                  <Iconfont code={recognizeIcon(treeNodeData.treeNodeType)!} />\n                </div>\n                <div className={styles.contentText}>\n                  <div className={styles.name} dangerouslySetInnerHTML={{ __html: treeNodeData.name }} />\n                  {treeNodeData.treeNodeType === TreeNodeType.COLUMN && (\n                    <div className={styles.type}>\n                      {/* 转小写 */}\n                      {treeNodeData.columnType?.toLowerCase()}\n                    </div>\n                  )}\n                </div>\n              </div>\n            </div>\n          </div>\n        </Tooltip>\n      </Dropdown>\n    );\n  }, [isFocus, isLoading, rightClickMenu, treeNodeData.children]);\n\n  return treeNodeDom;\n});\n\nexport default memo(Tree);\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/treeConfig.tsx",
    "content": "import { ITreeNode, IConnectionDetails } from '@/typings';\nimport { TreeNodeType, OperationColumn } from '@/constants';\nimport connectionService from '@/service/connection';\nimport { v4 as uuid } from 'uuid';\n\nimport mysqlServer from '@/service/sql';\n\nexport type ITreeConfig = Partial<{ [key in TreeNodeType]: ITreeConfigItem }>;\n\nexport const switchIcon: Partial<{ [key in TreeNodeType]: { icon: string; unfoldIcon?: string } }> = {\n  [TreeNodeType.DATABASE]: {\n    icon: '\\ue669',\n  },\n  [TreeNodeType.SCHEMAS]: {\n    icon: '\\ue696',\n  },\n  [TreeNodeType.TABLE]: {\n    icon: '\\ue63e',\n  },\n  [TreeNodeType.TABLES]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.COLUMNS]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.COLUMN]: {\n    icon: '\\ue611',\n  },\n  [TreeNodeType.KEYS]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.KEY]: {\n    icon: '\\ue775',\n  },\n  [TreeNodeType.INDEXES]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.INDEX]: {\n    icon: '\\ue65b',\n  },\n  [TreeNodeType.VIEWS]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.VIEW]: {\n    icon: '\\ue70c',\n  },\n  [TreeNodeType.FUNCTION]: {\n    icon: '\\ue76a',\n  },\n  [TreeNodeType.PROCEDURE]: {\n    icon: '\\ue73c',\n  },\n  [TreeNodeType.TRIGGER]: {\n    icon: '\\ue64a',\n  },\n  [TreeNodeType.VIEWCOLUMNS]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.VIEWCOLUMN]: {\n    icon: '\\ue647',\n  },\n  [TreeNodeType.FUNCTIONS]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.PROCEDURES]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.TRIGGERS]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.SEQUENCES]: {\n    icon: '\\ueabe',\n    unfoldIcon: '\\ueabf',\n  },\n  [TreeNodeType.SEQUENCE]: {\n    icon: '\\ue611',\n  },\n};\n\nexport interface ITreeConfigItem {\n  icon?: string;\n  getChildren?: (params: any, options?: any) => Promise<ITreeNode[]>;\n  next?: TreeNodeType;\n  operationColumn?: OperationColumn[];\n}\n\nexport const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = {\n  [TreeNodeType.DATA_SOURCES]: {\n    getChildren: () => {\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        const p = {\n          pageNo: 1,\n          pageSize: 1000,\n        };\n        connectionService\n          .getList(p)\n          .then((res) => {\n            const data: ITreeNode[] = res.data.map((t: IConnectionDetails) => {\n              return {\n                uuid: uuid(),\n                key: t.id,\n                name: t.alias,\n                treeNodeType: TreeNodeType.DATA_SOURCE,\n                extraParams: {\n                  databaseType: t.type,\n                  dataSourceId: t.id,\n                  dataSourceName: t.alias,\n                },\n              };\n            });\n            r(data);\n          })\n          .catch(() => {\n            j();\n          });\n      });\n    },\n  },\n\n  [TreeNodeType.DATA_SOURCE]: {\n    getChildren: (params: { dataSourceId: number; dataSourceName: string; extraParams: any }) => {\n      return new Promise((r, j) => {\n        const _extraParams = params.extraParams;\n        delete params.extraParams;\n        connectionService\n          .getDatabaseList(params)\n          .then((res) => {\n            const data: ITreeNode[] = res.map((t: any) => {\n              return {\n                uuid: uuid(),\n                key: t.name,\n                name: t.name,\n                treeNodeType: TreeNodeType.DATABASE,\n                extraParams: {\n                  ..._extraParams,\n                  databaseName: t.name,\n                },\n              };\n            });\n            r(data);\n          })\n          .catch(() => {\n            j();\n          });\n      });\n    },\n    operationColumn: [OperationColumn.EditSource, OperationColumn.Refresh, OperationColumn.ShiftOut],\n    next: TreeNodeType.DATABASE,\n  },\n\n  [TreeNodeType.DATABASE]: {\n    icon: '\\ue62c',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[], b?: any) => void, j) => {\n        connectionService\n          .getSchemaList(params)\n          .then((res) => {\n            const data: ITreeNode[] = res.map((t: any) => {\n              return {\n                uuid: uuid(),\n                key: t.name,\n                name: t.name,\n                treeNodeType: TreeNodeType.SCHEMAS,\n                schemaName: t.name,\n                extraParams: {\n                  ..._extraParams,\n                  schemaName: t.name,\n                },\n              };\n            });\n            r(data);\n          })\n          .catch(() => {\n            j();\n          });\n      });\n    },\n    operationColumn: [\n      OperationColumn.CreateConsole,\n      OperationColumn.CreateSchema,\n      // OperationColumn.CreateTable,\n      OperationColumn.CopyName,\n      OperationColumn.Refresh,\n    ],\n    next: TreeNodeType.SCHEMAS,\n  },\n\n  [TreeNodeType.SCHEMAS]: {\n    icon: '\\ue696',\n    getChildren: (parentData: ITreeNode) => {\n      const { dataSourceId, databaseName, schemaName } = parentData.extraParams!;\n      const preCode = [dataSourceId, databaseName, schemaName].join('-');\n      return new Promise((r: (value: ITreeNode[]) => void) => {\n        const data = [\n          {\n            uuid: uuid(),\n            key: `${preCode}-tables`,\n            name: 'tables',\n            treeNodeType: TreeNodeType.TABLES,\n            extraParams: parentData.extraParams,\n          },\n          {\n            uuid: uuid(),\n            key: `${preCode}-views`,\n            name: 'view',\n            treeNodeType: TreeNodeType.VIEWS,\n            extraParams: parentData.extraParams,\n          },\n          {\n            uuid: uuid(),\n            key: `${preCode}-functions`,\n            name: 'functions',\n            treeNodeType: TreeNodeType.FUNCTIONS,\n            extraParams: parentData.extraParams,\n          },\n          {\n            uuid: uuid(),\n            key: `${preCode}-procedures`,\n            name: 'procedures',\n            treeNodeType: TreeNodeType.PROCEDURES,\n            extraParams: parentData.extraParams,\n          },\n          {\n            uuid: uuid(),\n            key: `${preCode}-triggers`,\n            name: 'triggers',\n            treeNodeType: TreeNodeType.TRIGGERS,\n            extraParams: parentData.extraParams,\n          },\n        ];\n        if((parentData.extraParams?.databaseType === 'POSTGRESQL'|| parentData.extraParams?.databaseType === 'ORACLE')&& schemaName==='public'){\n          data.push({\n            uuid: uuid(),\n            key: `${preCode}-sequences`,\n            name: 'sequences',\n            treeNodeType: TreeNodeType.SEQUENCES,\n            extraParams: parentData.extraParams,\n          });\n        }\n        r(data);\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh],\n  },\n\n  [TreeNodeType.TABLES]: {\n    icon: '\\ueac5',\n    getChildren: (params, options) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      params.pageSize = 1000;\n      return new Promise((r, j) => {\n        mysqlServer\n          .getTableList(params, options)\n          .then((res) => {\n            const tableList: ITreeNode[] = res.data?.map((t: any) => {\n              return {\n                uuid: uuid(),\n                name: t.name,\n                treeNodeType: TreeNodeType.TABLE,\n                key: t.name,\n                pinned: t.pinned,\n                comment: t.comment,\n                extraParams: {\n                  ..._extraParams,\n                  tableName: t.name,\n                },\n              };\n            });\n            r({\n              data: tableList,\n              pageNo: res.pageNo,\n              pageSize: res.pageSize,\n              total: res.total,\n              hasNextPage: res.hasNextPage,\n            } as any);\n          })\n          .catch((error) => {\n            j(error);\n          });\n      });\n    },\n    operationColumn: [\n      OperationColumn.CreateConsole,\n      OperationColumn.ViewAllTable,\n      OperationColumn.CreateTable,\n      OperationColumn.Refresh,\n    ],\n  },\n\n  [TreeNodeType.TABLE]: {\n    icon: '\\ue63e',\n    getChildren: (params) => {\n      return new Promise((r: (value: ITreeNode[]) => void) => {\n        const { dataSourceId, databaseName, schemaName, tableName } = params.extraParams!;\n        const preCode = [dataSourceId, databaseName, schemaName, tableName].join('-');\n        const list = [\n          {\n            uuid: uuid(),\n            key: `${preCode}-columns`,\n            name: 'columns',\n            treeNodeType: TreeNodeType.COLUMNS,\n            extraParams: params.extraParams,\n          },\n          {\n            uuid: uuid(),\n            key: `${preCode}-keys`,\n            name: 'keys',\n            treeNodeType: TreeNodeType.KEYS,\n            extraParams: params.extraParams,\n          },\n          {\n            uuid: uuid(),\n            key: `${preCode}-indexs`,\n            name: 'indexs',\n            treeNodeType: TreeNodeType.INDEXES,\n            extraParams: params.extraParams,\n          },\n        ];\n\n        r(list);\n      });\n    },\n    operationColumn: [\n      OperationColumn.OpenTable,\n      OperationColumn.CreateConsole,\n      OperationColumn.Pin,\n      OperationColumn.ViewDDL,\n      OperationColumn.EditTable,\n      OperationColumn.CopyName,\n      OperationColumn.Refresh,\n      OperationColumn.DeleteTable,\n    ],\n  },\n\n  [TreeNodeType.VIEWS]: {\n    icon: '\\ue70c',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getViewList(params)\n          .then((res) => {\n            const viewList: ITreeNode[] = res.data?.map((t: any) => {\n              return {\n                uuid: uuid(),\n                name: t.name,\n                treeNodeType: TreeNodeType.VIEW,\n                key: t.name,\n                pinned: t.pinned,\n                comment: t.comment,\n                extraParams: {\n                  ..._extraParams,\n                  tableName: t.name,\n                },\n              };\n            });\n            r(viewList);\n          })\n          .catch((error) => {\n            j(error);\n          });\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh],\n  },\n\n  [TreeNodeType.FUNCTIONS]: {\n    icon: '\\ue76a',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getFunctionList(params)\n          .then((res) => {\n            const list: ITreeNode[] = res.data?.map((t: any) => {\n              return {\n                uuid: uuid(),\n                name: t.functionName,\n                treeNodeType: TreeNodeType.FUNCTION,\n                key: t.name,\n                pinned: t.pinned,\n                comment: t.comment,\n                isLeaf: true,\n                extraParams: {\n                  ..._extraParams,\n                  functionName: t.functionName,\n                },\n              };\n            });\n            r(list);\n          })\n          .catch((error) => {\n            j(error);\n          });\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh],\n  },\n\n  [TreeNodeType.FUNCTION]: {\n    icon: '\\ue76a',\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.OpenFunction, OperationColumn.CopyName],\n  },\n\n  [TreeNodeType.PROCEDURES]: {\n    icon: '\\ue73c',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getProcedureList(params)\n          .then((res) => {\n            const list: ITreeNode[] = res.data?.map((t: any) => {\n              return {\n                uuid: uuid(),\n                name: t.procedureName,\n                treeNodeType: TreeNodeType.PROCEDURE,\n                key: t.name,\n                pinned: t.pinned,\n                comment: t.comment,\n                isLeaf: true,\n                extraParams: {\n                  ..._extraParams,\n                  procedureName: t.procedureName,\n                },\n              };\n            });\n            r(list);\n          })\n          .catch((error) => {\n            j(error);\n          });\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh],\n  },\n\n  [TreeNodeType.PROCEDURE]: {\n    icon: '\\ue73c',\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.OpenProcedure, OperationColumn.CopyName],\n  },\n\n  [TreeNodeType.TRIGGERS]: {\n    icon: '\\ue64a',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getTriggerList(params)\n          .then((res) => {\n            const list: ITreeNode[] = res.data?.map((t: any) => {\n              return {\n                uuid: uuid(),\n                name: t.triggerName,\n                treeNodeType: TreeNodeType.TRIGGER,\n                key: t.name,\n                pinned: t.pinned,\n                comment: t.comment,\n                isLeaf: true,\n                extraParams: {\n                  ..._extraParams,\n                  triggerName: t.triggerName,\n                },\n              };\n            });\n            r(list);\n          })\n          .catch((error) => {\n            j(error);\n          });\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh],\n  },\n\n  [TreeNodeType.TRIGGER]: {\n    icon: '\\ue64a',\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.OpenTrigger, OperationColumn.CopyName],\n  },\n\n  [TreeNodeType.VIEW]: {\n    icon: '\\ue70c',\n    getChildren: (params) => {\n      return new Promise((r: (value: ITreeNode[]) => void) => {\n        const list = [\n          {\n            uuid: uuid(),\n            name: 'columns',\n            treeNodeType: TreeNodeType.COLUMNS,\n            key: 'columns',\n            extraParams: params.extraParams,\n          },\n        ];\n        r(list);\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.OpenView, OperationColumn.CopyName],\n  },\n\n  [TreeNodeType.VIEWCOLUMNS]: {\n    icon: '\\ue647',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getViewColumnList(params)\n          .then((res) => {\n            const list: ITreeNode[] = res.data?.map((t: any) => {\n              return {\n                uuid: uuid(),\n                name: t.name,\n                treeNodeType: TreeNodeType.VIEWCOLUMN,\n                key: t.name,\n                pinned: t.pinned,\n                comment: t.comment,\n                isLeaf: true,\n                extraParams: _extraParams,\n              };\n            });\n            r(list);\n          })\n          .catch((error) => {\n            j(error);\n          });\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName, OperationColumn.Refresh],\n  },\n\n  [TreeNodeType.VIEWCOLUMN]: {\n    icon: '\\ue647',\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName],\n  },\n\n  [TreeNodeType.COLUMNS]: {\n    icon: '\\ueac5',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getColumnList(params)\n          .then((res) => {\n            const tableList: ITreeNode[] = res?.map((item) => {\n              return {\n                uuid: uuid(),\n                name: item.name,\n                treeNodeType: TreeNodeType.COLUMN,\n                key: item.name,\n                isLeaf: true,\n                columnType: item.columnType,\n                comment: item.comment,\n                extraParams: _extraParams,\n              };\n            });\n            r(tableList);\n          })\n          .catch(() => {\n            j();\n          });\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh],\n  },\n  [TreeNodeType.COLUMN]: {\n    icon: '\\ue611',\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName],\n  },\n  [TreeNodeType.KEYS]: {\n    icon: '\\ueac5',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getKeyList(params)\n          .then((res) => {\n            const tableList: ITreeNode[] = res?.map((item) => {\n              return {\n                uuid: uuid(),\n                name: item.name,\n                treeNodeType: TreeNodeType.KEY,\n                key: item.name,\n                isLeaf: true,\n                extraParams: _extraParams,\n              };\n            });\n            r(tableList);\n          })\n          .catch(() => {\n            j();\n          });\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName, OperationColumn.Refresh],\n  },\n  [TreeNodeType.KEY]: {\n    icon: '\\ue775',\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName],\n  },\n  [TreeNodeType.INDEXES]: {\n    icon: '\\ueac5',\n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getIndexList(params)\n          .then((res) => {\n            const tableList: ITreeNode[] = res?.map((item) => {\n              return {\n                uuid: uuid(),\n                name: item.name,\n                treeNodeType: TreeNodeType.INDEX,\n                key: item.name,\n                isLeaf: true,\n                extraParams: _extraParams,\n              };\n            });\n            r(tableList);\n          })\n          .catch(() => {\n            j();\n          });\n      });\n    },\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName, OperationColumn.Refresh],\n  },\n  [TreeNodeType.INDEX]: {\n    icon: '\\ue65b',\n    operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName],\n  },\n  [TreeNodeType.SEQUENCES]: {\n    icon: '\\ueabe', // 使用现有图标（如文件夹折叠）\n   \n    getChildren: (params) => {\n      const _extraParams = params.extraParams;\n      delete params.extraParams;\n      return new Promise((r: (value: ITreeNode[]) => void, j) => {\n        mysqlServer\n          .getSequenceList(params)\n          .then((res) => {\n            const data: ITreeNode[] = res?.map((item:any) => {\n              return {\n                uuid: uuid(),\n                key: item.name,\n                name: item.name,\n                treeNodeType: TreeNodeType.SEQUENCE,\n                sequenceName: item.name,\n                isLeaf: true,\n                extraParams: {\n                  ..._extraParams,\n                  sequenceName: item.name,\n                },\n              };\n            });\n            r(data);\n          })\n          .catch((error) => {\n            j(error);\n          });\n      })\n    },\n    operationColumn: [\n      OperationColumn.CreateSequence,\n      OperationColumn.CopyName,\n      OperationColumn.Refresh,\n    ],\n  },\n  [TreeNodeType.SEQUENCE]: {\n    icon: '\\ue611',\n    operationColumn: [OperationColumn.OpenSequence, OperationColumn.EditSequence, OperationColumn.CopyName,OperationColumn.DeleteSequence],\n  },\n};\n"
  },
  {
    "path": "chat2db-client/src/blocks/Tree/treeStore.ts",
    "content": "/**\n * 树的store\n */\nimport { create, UseBoundStore, StoreApi } from 'zustand';\nimport { devtools } from 'zustand/middleware';\n\nexport interface ITreeStore {\n  focusId: number | string | null;\n  focusTreeNode: {\n    dataSourceId: number;\n    dataSourceName: string;\n    databaseType: string,\n    databaseName?: string;\n    schemaName?: string,\n    tableName?: string,\n  } | null;\n}\n\nconst treeStore = {\n  focusId: null,\n  focusTreeNode: null,\n}\n\nexport const useTreeStore: UseBoundStore<StoreApi<ITreeStore>> = create(\n  devtools(() => (treeStore)),\n);\n\nexport const setFocusId = (focusId: ITreeStore['focusId']) => {\n  useTreeStore.setState({ focusId });\n}\n\nexport const setFocusTreeNode = (focusTreeNode: ITreeStore['focusTreeNode']) => {\n  useTreeStore.setState({ focusTreeNode });\n}\n\n// 清除treeStore\nexport const clearTreeStore = () => {\n  useTreeStore.setState(treeStore);\n}\n"
  },
  {
    "path": "chat2db-client/src/components/BrandLogo/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n  display: flex;\n  justify-content: center;\n  border-radius: 10%;\n  overflow: hidden;\n  img {\n    display: block;\n    height: 100%;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/BrandLogo/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport logo from '@/assets/logo/logo.webp';\n\ninterface IProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {\n  className?: any;\n  size?: number;\n}\n\nexport default memo<IProps>(({ className, size = 48, ...res }) => {\n  return (\n    <div {...res} className={classnames(className, styles.box)} style={{ height: `${size}px`, width: `${size}px` }}>\n      <img src={logo} alt=\"\" />\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/CascaderDB/index.less",
    "content": ".cascaderDB {\n  display: inline-flex;\n  align-items: center;\n  border-radius: 16px;\n  padding: 4px 8px 4px 0;\n  background-color: var(--color-bg-subtle);\n\n  &:hover {\n    background-color: var(--color-hover-bg);\n  }\n}\n\n.optionItem {\n  display: flex;\n  align-items: center;\n  height: 26px;\n  cursor: pointer;\n  padding: 0px 6px;\n\n\n  .optionItemIcon {\n    margin-right: 10px;\n    font-weight: 400;\n    color: var(--color-primary);\n  }\n\n  .optionItemText {\n    font-weight: bold;\n  }\n\n\n}\n\n.select {\n  :global {\n    .ant-select-selector {\n      caret-color: var(--color-text-placeholder);\n      // caret-width: 4px;\n      // width: 4px;\n    }\n  }\n}"
  },
  {
    "path": "chat2db-client/src/components/CascaderDB/index.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { Divider, Select, Typography } from 'antd';\nimport connection from '@/service/connection';\nimport cs from 'classnames';\nimport styles from './index.less';\nimport Iconfont from '../Iconfont';\nimport { databaseMap } from '@/constants/database';\n\ninterface IProps {\n  className?: string;\n  curConnectionId?: number;\n  onChange?: (value: { dataSourceId: number; databaseName: string; schemaName: string }) => void;\n}\n\ninterface IOption {\n  label: string | React.ReactNode;\n  value: number | string;\n}\n\nfunction CascaderDB(props: IProps) {\n  const [dataSourceOptions, setDataSourceOptions] = useState<IOption[]>([]);\n  const [curDataSourceId, setCurDataSourceId] = useState<number | undefined>(props.curConnectionId);\n\n  const [databaseOptions, setDatabaseOptions] = useState<IOption[]>([]);\n  const [curDatabaseName, setCurDatabaseName] = useState<string>('');\n\n  const [schemaOptions, setSchemaOptions] = useState<IOption[]>([]);\n  const [curSchemeName, setCurSchemeName] = useState<string>('');\n\n  useEffect(() => {\n    loadDataSource();\n  }, []);\n  useEffect(() => {\n    loadDatabase();\n  }, [curDataSourceId]);\n  useEffect(() => {\n    loadSchema();\n  }, [curDatabaseName]);\n\n  const handleChangeDataSource = (value) => {\n    setCurDataSourceId(value);\n    setDatabaseOptions([]);\n    setSchemaOptions([]);\n    setCurDatabaseName('');\n    setCurSchemeName('');\n\n    props.onChange &&\n      props.onChange({\n        dataSourceId: value,\n        databaseName: '',\n        schemaName: '',\n      });\n  };\n  const handleChangeDatabase = (value) => {\n    setCurDatabaseName(value);\n    setSchemaOptions([]);\n    setCurSchemeName('');\n\n    props.onChange &&\n      props.onChange({\n        dataSourceId: curDataSourceId!,\n        databaseName: value,\n        schemaName: '',\n      });\n  };\n\n  const handleChangeSchema = (value) => {\n    setCurSchemeName(value);\n\n    props.onChange &&\n      props.onChange({ dataSourceId: curDataSourceId!, databaseName: curDatabaseName, schemaName: value });\n  };\n\n  /** 加载DataSource数据 */\n  const loadDataSource = async () => {\n    // 请求 dataSource 数据\n    const dataSourceList = await connection.getList({\n      pageNo: 1,\n      pageSize: 999,\n      refresh: true,\n    });\n    const formattedData = (dataSourceList?.data || []).map((item) => ({\n      ...item,\n      key: `dataSource-${item.id}`,\n      value: item.id,\n      label: (\n        <div className={styles.optionItem}>\n          <Iconfont className={styles.optionItemIcon} code={databaseMap[item.type]?.icon} />\n          <div className={styles.optionItemText}>{item.alias}</div>\n        </div>\n      ),\n    }));\n    setDataSourceOptions(formattedData);\n\n    if (curDataSourceId === undefined) {\n      setCurDataSourceId(formattedData[0]?.value);\n    }\n  };\n\n  /** 加载Database数据 */\n  const loadDatabase = async () => {\n    if (curDataSourceId === undefined) {\n      return;\n    }\n    const databaseList = await connection.getDatabaseList({\n      dataSourceId: curDataSourceId,\n    });\n\n    const formattedData = (databaseList || []).map((item) => ({\n      ...item,\n      key: `database-${item.name}`,\n      value: item.name,\n      label: (\n        <div className={styles.optionItem}>\n          <Iconfont className={styles.optionItemIcon} code=\"&#xe744;\" />\n          <div className={styles.optionItemText}>{item.name}</div>\n        </div>\n      ),\n    }));\n\n    setDatabaseOptions(formattedData);\n    if (!curDatabaseName) {\n      setCurDatabaseName(formattedData[0]?.value);\n    }\n  };\n\n  const loadSchema = async () => {\n    if (curDataSourceId === undefined || !curDatabaseName) {\n      return;\n    }\n    const schemaList = await connection.getSchemaList({\n      dataSourceId: curDataSourceId,\n      databaseName: curDatabaseName,\n      refresh: false,\n    });\n\n    const formattedData = (schemaList || []).map((item) => ({\n      ...item,\n      key: `schema-${item.name}`,\n      value: item.name,\n      label: (\n        <div className={styles.optionItem}>\n          <Iconfont className={styles.optionItemIcon} code=\"&#xe696;\" />\n          <div className={styles.optionItemText}>{item.name}</div>\n        </div>\n      ),\n    }));\n\n    setSchemaOptions(formattedData);\n    if (!curSchemeName) {\n      setCurSchemeName(formattedData[0]?.value);\n    }\n  };\n\n  return (\n    <div className={cs(props.className, styles.cascaderDB)}>\n      <Select\n        bordered={false}\n        placeholder=\"请选择链接\"\n        showSearch\n        popupMatchSelectWidth={false}\n        options={dataSourceOptions}\n        value={curDataSourceId}\n        onChange={handleChangeDataSource}\n        className={styles.select}\n        dropdownRender={(menu) => (\n          <>\n            <div style={{ padding: '8px 16px' }}>\n              <Typography.Title level={4}>链接源</Typography.Title>\n              <Divider style={{ margin: '4px 0' }} />\n            </div>\n            {menu}\n          </>\n        )}\n      />\n      <Select\n        bordered={false}\n        placeholder=\"请选择数据库\"\n        showSearch\n        popupMatchSelectWidth={false}\n        options={databaseOptions}\n        value={curDatabaseName}\n        onChange={handleChangeDatabase}\n        className={styles.select}\n        dropdownRender={(menu) => (\n          <>\n            <div style={{ padding: '8px 16px' }}>\n              <Typography.Title level={4}>数据库</Typography.Title>\n              <Divider style={{ margin: '4px 0' }} />\n            </div>\n            {menu}\n          </>\n        )}\n      />\n      {!!schemaOptions.length && (\n        <Select\n          bordered={false}\n          placeholder=\"请选择Schema\"\n          showSearch\n          popupMatchSelectWidth={false}\n          options={schemaOptions}\n          value={curSchemeName}\n          onChange={handleChangeSchema}\n          className={styles.select}\n          dropdownRender={(menu) => (\n            <>\n              <div style={{ padding: '8px 16px' }}>\n                <Typography.Title level={4}>Schema</Typography.Title>\n                <Divider style={{ margin: '4px 0' }} />\n              </div>\n              {menu}\n            </>\n          )}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default CascaderDB;\n"
  },
  {
    "path": "chat2db-client/src/components/ConnectionEdit/components/Driver/index.less",
    "content": "@import '../../../../styles/var.less';\n\n.box {\n  :global {\n    .ant-form-item-row{\n      display: flex;\n    }\n    .ant-form-item-control{\n      flex: 1;\n    }\n    .ant-form-item-label {\n      width: 54px;\n    }\n  }\n}\n\n.downloadDriveFooter{\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  \n  .downloadDrive{\n    display: flex;\n    .downloadText{\n      margin-right: 4px;\n      color: var(--color-primary);\n    }\n    .downloadTextDownload{\n      cursor: pointer;\n    }\n    .downloadTextError{\n      color: var(--color-error-text);\n      cursor: pointer;\n    }\n    .downloadTextLoading{\n      display: flex;\n      align-items: center;\n      .text{\n        margin-left: 4px;\n      }\n    }\n    .downloadTextSuccess{\n      color: var(--color-success-text);\n    }\n  }\n  \n  .uploadCustomDrive{\n    cursor: pointer;\n    &:hover{\n      color: var(--color-primary);\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ConnectionEdit/components/Driver/index.tsx",
    "content": "import React, { memo, useState, useEffect } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { i18n } from '@/i18n';\nimport { Form, Modal, Input, Select } from 'antd';\nimport connectionService, { IDriverResponse } from '@/service/connection';\nimport UploadDriver from '@/components/UploadDriver';\nimport LoadingGracile from '@/components/Loading/LoadingGracile';\nconst { Option } = Select;\n\ninterface IProps {\n  className?: string;\n  onChange: (data: any) => void;\n  backfillData: any;\n}\n\nenum DownloadStatus {\n  Default,\n  Loading,\n  Error,\n  Success,\n}\n\nexport default memo<IProps>((props) => {\n  const { className, backfillData, onChange } = props;\n  const [downloadStatus, setDownloadStatus] = useState<DownloadStatus>(DownloadStatus.Default);\n  const [driverForm] = Form.useForm();\n  const [driverObj, setDriverObj] = useState<IDriverResponse>();\n  const [uploadDriverModal, setUploadDriverModal] = useState(false);\n  const [driverSaved, setDriverSaved] = useState<any>({});\n\n  useEffect(() => {\n    if (backfillData) {\n      getDriverList();\n    }\n  }, [backfillData?.type]);\n\n  useEffect(() => {\n    if (backfillData) {\n      const data = {\n        jdbcDriverClass: backfillData?.driverConfig?.jdbcDriverClass,\n        jdbcDriver: backfillData?.driverConfig?.jdbcDriver,\n      };\n      driverForm.setFieldsValue(data);\n      onChange(data);\n    }\n  }, [backfillData?.driverConfig, backfillData?.id]);\n\n  function getDriverList() {\n    connectionService.getDriverList({ dbType: backfillData.type }).then((res) => {\n      if (!res) {\n        return;\n      }\n      setDriverObj({\n        ...res,\n        driverConfigList: res.driverConfigList || [],\n      });\n      if (res.driverConfigList?.length && !backfillData?.driverConfig?.jdbcDriver) {\n        const data = {\n          jdbcDriverClass: res.driverConfigList[0]?.jdbcDriverClass,\n          jdbcDriver: res.driverConfigList[0]?.jdbcDriver,\n        };\n        driverForm.setFieldsValue(data);\n        onChange(data);\n      }\n    });\n  }\n\n  function formChange(data: any) {\n    setDriverSaved(data);\n  }\n\n  function saveDriver() {\n    connectionService.saveDriver(driverSaved).then(() => {\n      setUploadDriverModal(false);\n      getDriverList();\n    });\n  }\n\n  function downloadDrive() {\n    setDownloadStatus(DownloadStatus.Loading);\n    connectionService\n      .downloadDriver({ dbType: backfillData.type })\n      .then(() => {\n        setDownloadStatus(DownloadStatus.Success);\n        getDriverList();\n      })\n      .catch(() => {\n        setDownloadStatus(DownloadStatus.Error);\n      });\n  }\n\n  function onValuesChange(data: any) {\n    const selected = driverObj?.driverConfigList.find((t) => t.jdbcDriver === data.jdbcDriver);\n    driverForm.setFieldsValue({\n      jdbcDriverClass: selected?.jdbcDriverClass,\n    });\n    onChange({\n      jdbcDriverClass: selected?.jdbcDriverClass,\n      jdbcDriver: data.jdbcDriver,\n    });\n  }\n\n  return (\n    <div className={classnames(styles.box, className)}>\n      <Form form={driverForm} onValuesChange={onValuesChange} colon={false}>\n        <Form.Item labelAlign=\"left\" name=\"jdbcDriver\" label={i18n('connection.title.driver')}>\n          <Select>\n            {driverObj?.driverConfigList?.map((t) => (\n              <Option key={t.jdbcDriver} value={t.jdbcDriver}>\n                {t.jdbcDriver}\n              </Option>\n            ))}\n          </Select>\n        </Form.Item>\n        <Form.Item labelAlign=\"left\" name=\"jdbcDriverClass\" label=\"Class\">\n          <Input disabled />\n        </Form.Item>\n      </Form>\n      <div className={styles.downloadDriveFooter}>\n        {(driverObj?.driverConfigList && !driverObj?.driverConfigList?.length) ||\n        downloadStatus === DownloadStatus.Success ? (\n          <div onClick={downloadDrive} className={styles.downloadDrive}>\n            {downloadStatus === DownloadStatus.Default && (\n              <div className={classnames(styles.downloadText, styles.downloadTextDownload)}>\n                {i18n('connection.text.downloadDriver')}\n              </div>\n            )}\n            {downloadStatus === DownloadStatus.Loading && (\n              <div className={classnames(styles.downloadText, styles.downloadTextLoading)}>\n                <LoadingGracile />\n                <div className={styles.text}>{i18n('connection.text.downloading')}</div>\n              </div>\n            )}\n            {downloadStatus === DownloadStatus.Error && (\n              <div className={classnames(styles.downloadText, styles.downloadTextError)}>\n                {i18n('connection.text.tryAgainDownload')}\n              </div>\n            )}\n            {downloadStatus === DownloadStatus.Success && (\n              <div className={classnames(styles.downloadText, styles.downloadTextSuccess)}>\n                {i18n('connection.text.downloadSuccess')}\n              </div>\n            )}\n          </div>\n        ) : (\n          <div />\n        )}\n        {backfillData.isAdmin !== false && (\n          <div\n            className={styles.uploadCustomDrive}\n            onClick={() => {\n              setUploadDriverModal(true);\n            }}\n          >\n            {i18n('connection.tips.customUpload')}\n          </div>\n        )}\n      </div>\n      <Modal\n        destroyOnClose={true}\n        title={i18n('connection.title.uploadDriver')}\n        open={uploadDriverModal}\n        onOk={() => {\n          saveDriver();\n        }}\n        onCancel={() => {\n          setUploadDriverModal(false);\n        }}\n      >\n        <UploadDriver\n          jdbcDriverClass={driverObj?.defaultDriverConfig?.jdbcDriverClass}\n          formChange={formChange}\n          databaseType={backfillData.type}\n        />\n      </Modal>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/ConnectionEdit/config/dataSource.ts",
    "content": "import { DatabaseTypeCode, OperationColumn } from '@/constants';\nimport { IConnectionConfig } from './types';\nimport { InputType, AuthenticationType } from './enum';\nimport i18n from '@/i18n';\n\nexport const sshConfig: IConnectionConfig['ssh'] = {\n  items: [\n    {\n      defaultValue: false,\n      inputType: InputType.SELECT,\n      labelNameCN: '使用SSH',\n      labelNameEN: 'USE SSH',\n      name: 'use',\n      required: false,\n      selects: [\n        {\n          label: 'false',\n          value: false,\n        },\n        {\n          label: 'true',\n          value: true,\n        },\n      ],\n    },\n    {\n      defaultValue: '',\n      inputType: InputType.INPUT,\n      labelNameCN: 'SSH 主机',\n      labelNameEN: 'SSH Hostname',\n      name: 'hostName',\n      required: false,\n      styles: {\n        width: '70%',\n      },\n    },\n    {\n      defaultValue: '22',\n      inputType: InputType.INPUT,\n      labelNameCN: 'SSH 端口',\n      labelNameEN: 'Port',\n      name: 'port',\n      required: false,\n      styles: {\n        width: '30%',\n        labelWidthEN: '40px',\n        labelWidthCN: '70px',\n        labelAlign: 'right',\n      },\n    },\n    {\n      defaultValue: '',\n      inputType: InputType.INPUT,\n      labelNameCN: '用户名',\n      labelNameEN: 'SSH UserName',\n      name: 'userName',\n      required: false,\n      styles: {\n        width: '70%',\n      },\n    },\n    {\n      defaultValue: '',\n      inputType: InputType.INPUT,\n      labelNameCN: '本地端口',\n      labelNameEN: 'LocalPort',\n      name: 'localPort',\n      placeholder: '不必填',\n      placeholderEN: 'Need not fill in',\n      required: false,\n      styles: {\n        width: '30%',\n        labelWidthEN: '70px',\n        labelWidthCN: '70px',\n        labelAlign: 'right',\n      },\n    },\n    {\n      defaultValue: 'password',\n      inputType: InputType.SELECT,\n      labelNameCN: '身份验证',\n      labelNameEN: 'Authentication',\n      name: 'authenticationType',\n      required: true,\n      selects: [\n        {\n          items: [\n            {\n              defaultValue: '',\n              inputType: InputType.PASSWORD,\n              labelNameCN: '密码',\n              labelNameEN: 'Password',\n              name: 'password',\n              required: true,\n            },\n          ],\n          label: 'password',\n          value: 'password',\n        },\n        {\n          items: [\n            {\n              defaultValue: '',\n              inputType: InputType.INPUT,\n              labelNameCN: '密钥文件',\n              labelNameEN: 'Private key file',\n              name: 'keyFile',\n              required: true,\n              placeholder: '/user/userName/.ssh/xxxx',\n              placeholderEN: '/user/userName/.ssh/xxxx',\n            },\n            {\n              defaultValue: '',\n              inputType: InputType.PASSWORD,\n              labelNameCN: '密码短语',\n              labelNameEN: 'Passphrase',\n              name: 'passphrase',\n              required: true,\n            },\n          ],\n          label: 'Private key',\n          value: 'keyFile',\n        },\n      ],\n      styles: {\n        width: '50%',\n      },\n    },\n  ],\n};\n\nconst envItem = {\n  defaultValue: '',\n  inputType: InputType.SELECT,\n  labelNameCN: '环境',\n  labelNameEN: 'Env',\n  name: 'environmentId',\n  required: true,\n  selects: [],\n  styles: {\n    width: '50%',\n  },\n};\n\nexport const dataSourceFormConfigs: IConnectionConfig[] = [\n  // MYSQL\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '3306',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n        },\n        {\n          defaultValue: 'jdbc:mysql://localhost:3306',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:mysql:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:mysql://{host}:{port}/{database}',\n    },\n    ssh: sshConfig,\n    extendInfo: [\n      {\n        key: 'zeroDateTimeBehavior',\n        value: 'convertToNull',\n      },\n      {\n        key: 'useInformationSchema',\n        value: 'true',\n      },\n      {\n        key: 'tinyInt1isBit',\n        value: 'false',\n      },\n    ],\n    type: DatabaseTypeCode.MYSQL,\n  },\n  // POSTGRESQL\n  {\n    type: DatabaseTypeCode.POSTGRESQL,\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '5432',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: 'postgres',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n        },\n        {\n          defaultValue: 'jdbc:postgresql://localhost:5432',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:postgresql:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:postgresql://{host}:{port}/{database}',\n    },\n    ssh: sshConfig,\n  },\n  // ORACLE\n  {\n    type: DatabaseTypeCode.ORACLE,\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '1521',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: 'sid',\n          inputType: InputType.SELECT,\n          labelNameCN: '链接类型',\n          labelNameEN: 'Service type',\n          name: 'serviceType',\n          required: true,\n          selects: [\n            {\n              label: 'SID',\n              value: 'sid',\n              items: [\n                {\n                  defaultValue: 'XE',\n                  inputType: InputType.INPUT,\n                  labelNameCN: 'SID',\n                  labelNameEN: 'SID',\n                  name: 'sid',\n                  required: true,\n                  styles: {\n                    width: '70%',\n                  },\n                },\n              ],\n              onChange: (data: IConnectionConfig) => {\n                data.baseInfo.pattern = /jdbc:oracle:(.*):@(.*):(\\d+):(.*)/;\n                data.baseInfo.template = 'jdbc:oracle:{driver}:@{host}:{port}:{sid}';\n                return data;\n              },\n            },\n            {\n              label: 'Service',\n              value: 'service',\n              items: [\n                {\n                  defaultValue: 'XE',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '服务名',\n                  labelNameEN: 'Service name',\n                  name: 'serviceName',\n                  required: true,\n                  styles: {\n                    width: '70%',\n                  },\n                },\n              ],\n              onChange: (data: IConnectionConfig) => {\n                data.baseInfo.pattern = /jdbc:oracle:(.*):@\\/\\/(.*):(\\d+)\\/(.*)/;\n                data.baseInfo.template = 'jdbc:oracle:{driver}:@//{host}:{port}/{serviceName}';\n                return data;\n              },\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: 'thin',\n          inputType: InputType.SELECT,\n          labelNameCN: '驱动',\n          labelNameEN: 'Driver',\n          name: 'driver',\n          required: true,\n          labelTextAlign: 'right',\n          selects: [\n            {\n              value: 'thin',\n              label: 'thin',\n            },\n            {\n              value: 'oci',\n              label: 'oci',\n            },\n            {\n              value: 'oci8',\n              label: 'oci8',\n            },\n          ],\n          styles: {\n            width: '30%',\n            labelWidthEN: '70px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: 'jdbc:oracle:thin:@localhost:1521:XE',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:oracle:(.*):@(.*):(\\d+):(.*)/,\n      template: 'jdbc:oracle:{driver}:@{host}:{port}:{sid}',\n    },\n    ssh: sshConfig,\n  },\n  // H2\n  {\n    type: DatabaseTypeCode.H2,\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '9092',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: 'TCP',\n          inputType: InputType.SELECT,\n          labelNameCN: '链接类型',\n          labelNameEN: 'Service type',\n          name: 'serviceType',\n          required: true,\n          selects: [\n            {\n              label: i18n('common.label.tcp'),\n              value: 'TCP',\n              items: [],\n              onChange: (data: IConnectionConfig) => {\n                data.baseInfo.pattern = /jdbc:h2:tcp:\\/\\/(.*):(\\d+)(\\/(\\w+))?/;\n                data.baseInfo.template = 'jdbc:h2:tcp://{host}:{port}/{database}';\n                return data;\n              },\n            },\n            {\n              label: i18n('common.label.LocalFile'),\n              value: 'LocalFile',\n              items: [\n                {\n                  defaultValue: '',\n                  inputType: InputType.INPUT,\n                  labelNameCN: 'File',\n                  labelNameEN: 'File',\n                  name: 'file',\n                  required: true,\n                },\n              ],\n              onChange: (data: IConnectionConfig) => {\n                data.baseInfo.pattern = /jdbc:h2:(.*)?/;\n                data.baseInfo.template = 'jdbc:h2:{file}';\n                return data;\n              },\n            },\n          ],\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n        },\n        {\n          defaultValue: 'jdbc:h2:tcp://localhost:9092',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:h2:tcp:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:h2:tcp://{host}:{port}/{database}',\n    },\n    ssh: sshConfig,\n  },\n  // SQLSERVER encrypt=true;trustServerCertificate=true;integratedSecurity=false;Trusted_Connection=yes\n  {\n    type: DatabaseTypeCode.SQLSERVER,\n    extendInfo: [\n      {\n        key: 'encrypt',\n        value: 'false',\n      },\n      {\n        key: 'trustServerCertificate',\n        value: 'true',\n      },\n      {\n        key: 'integratedSecurity',\n        value: 'false',\n      },\n      {\n        key: 'Trusted_Connection',\n        value: 'yes',\n      },\n    ],\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '1433',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: 'Instance',\n          labelNameEN: 'Instance',\n          name: 'instance',\n          required: false,\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n        },\n        {\n          defaultValue: 'jdbc:sqlserver://localhost:1433',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:sqlserver:\\/\\/(.*):(\\d+)(;database=(\\w+))?/,\n      template: 'jdbc:sqlserver://{host}:{port};database={database}',\n    },\n    ssh: sshConfig,\n  },\n  // SQLITE\n  {\n    type: DatabaseTypeCode.SQLITE,\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'identifier.sqlite',\n          inputType: InputType.INPUT,\n          labelNameCN: 'File',\n          labelNameEN: 'File',\n          name: 'file',\n          required: true,\n        },\n        {\n          defaultValue: 'jdbc:sqlite:identifier.sqlite',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:sqlite:(.*)?/,\n      template: 'jdbc:sqlite:{file}',\n    },\n    ssh: sshConfig,\n  },\n  // MARIADB\n  {\n    type: DatabaseTypeCode.MARIADB,\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '3306',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n        },\n        {\n          defaultValue: 'jdbc:mariadb://localhost:3306',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:mariadb:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:mariadb://{host}:{port}/{database}',\n    },\n    ssh: sshConfig,\n  },\n  // CLICKHOUSE\n  {\n    type: DatabaseTypeCode.CLICKHOUSE,\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '8123',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n        },\n        {\n          defaultValue: 'jdbc:clickhouse://localhost:8123',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:clickhouse:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:clickhouse://{host}:{port}/{database}',\n      excludes: [OperationColumn.ViewDDL, OperationColumn.CreateTable, OperationColumn.EditTable],\n      //排除掉导出ddl 和 创建表功能 支持的功能见 ./enum.ts => OperationColumn\n    },\n    ssh: sshConfig,\n  },\n  // DM\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '5236',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n        },\n        {\n          defaultValue: 'jdbc:dm://localhost:5236',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:dm:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:dm://{host}:{port}/{database}',\n      // excludes: [OperationColumn.EditTable]\n    },\n    ssh: sshConfig,\n    extendInfo: [\n      {\n        key: 'zeroDateTimeBehavior',\n        value: 'convertToNull',\n      },\n    ],\n    type: DatabaseTypeCode.DM,\n  },\n  //DB2\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '50000',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n          styles: {\n            width: '100%',\n          },\n        },\n        {\n          defaultValue: 'jdbc:db2://localhost:50000',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n      ],\n      pattern: /jdbc:db2:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:db2://{host}:{port}/{database}',\n      // excludes: [OperationColumn.EditTable]\n    },\n    ssh: sshConfig,\n    extendInfo: [],\n    type: DatabaseTypeCode.DB2,\n  },\n  //presto\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '8080',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n          styles: {\n            width: '100%',\n          },\n        },\n        {\n          defaultValue: 'jdbc:presto://localhost:8080',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n      ],\n      pattern: /jdbc:presto:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:presto://{host}:{port}/{database}',\n      // excludes: [OperationColumn.EditTable]\n    },\n    ssh: sshConfig,\n    extendInfo: [],\n    type: DatabaseTypeCode.PRESTO,\n  },\n  //oceanbase\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '2883',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n          styles: {\n            width: '100%',\n          },\n        },\n        {\n          defaultValue: 'jdbc:oceanbase://localhost:2883',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n      ],\n      pattern: /jdbc:oceanbase:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:oceanbase://{host}:{port}/{database}',\n      // excludes: [OperationColumn.EditTable]\n    },\n    ssh: sshConfig,\n    extendInfo: [],\n    type: DatabaseTypeCode.OCEANBASE,\n  },\n  //redis\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '6379',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n          styles: {\n            width: '100%',\n          },\n        },\n        {\n          defaultValue: 'jdbc:redis://localhost:6379',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n      ],\n      pattern: /jdbc:redis:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:redis://{host}:{port}/{database}',\n    },\n    ssh: sshConfig,\n    extendInfo: [],\n    type: DatabaseTypeCode.REDIS,\n  },\n  //hive\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '10000',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n          styles: {\n            width: '100%',\n          },\n        },\n        {\n          defaultValue: 'jdbc:hive2://localhost:10000',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n      ],\n      pattern: /jdbc:hive2:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:hive2://{host}:{port}/{database}',\n      // excludes: [OperationColumn.EditTable]\n    },\n    ssh: sshConfig,\n    extendInfo: [],\n    type: DatabaseTypeCode.HIVE,\n  },\n  //KINGBASE\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '54321',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n          styles: {\n            width: '100%',\n          },\n        },\n        {\n          defaultValue: 'jdbc:kingbase8://127.0.0.1:54321',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n      ],\n      pattern: /jdbc:kingbase8:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:kingbase8://{host}:{port}/{database}',\n      // excludes: [OperationColumn.EditTable]\n    },\n    ssh: sshConfig,\n    extendInfo: [],\n    type: DatabaseTypeCode.KINGBASE,\n  },\n  //MONGODB\n  {\n    baseInfo: {\n      items: [\n        {\n          defaultValue: '@localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '27017',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'root',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                  styles: {\n                    width: '100%',\n                  },\n                },\n              ],\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n          styles: {\n            width: '100%',\n          },\n        },\n        {\n          defaultValue: 'mongodb://localhost:27017',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n          styles: {\n            width: '100%',\n          },\n        },\n      ],\n      pattern: /mongodb:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'mongodb://{host}:{port}/{database}',\n      excludes: [OperationColumn.ViewDDL, OperationColumn.CreateTable, OperationColumn.EditTable],\n    },\n    ssh: sshConfig,\n    extendInfo: [],\n    type: DatabaseTypeCode.MONGODB,\n  },\n  // TIMEPLUS\n  {\n    type: DatabaseTypeCode.TIMEPLUS,\n    baseInfo: {\n      items: [\n        {\n          defaultValue: 'timeplus',\n          inputType: InputType.INPUT,\n          labelNameCN: '名称',\n          labelNameEN: 'Name',\n          name: 'alias',\n          required: true,\n        },\n        envItem,\n        {\n          defaultValue: 'localhost',\n          inputType: InputType.INPUT,\n          labelNameCN: '主机',\n          labelNameEN: 'Host',\n          name: 'host',\n          required: true,\n          styles: {\n            width: '70%',\n          },\n        },\n        {\n          defaultValue: '7587',\n          inputType: InputType.INPUT,\n          labelNameCN: '端口',\n          labelNameEN: 'Port',\n          name: 'port',\n          labelTextAlign: 'right',\n          required: true,\n          styles: {\n            width: '30%',\n            labelWidthEN: '40px',\n            labelWidthCN: '40px',\n            labelAlign: 'right',\n          },\n        },\n        {\n          defaultValue: AuthenticationType.USERANDPASSWORD,\n          inputType: InputType.SELECT,\n          labelNameCN: '身份验证',\n          labelNameEN: 'Authentication',\n          name: 'authenticationType',\n          required: true,\n          selects: [\n            {\n              items: [\n                {\n                  defaultValue: 'default',\n                  inputType: InputType.INPUT,\n                  labelNameCN: '用户名',\n                  labelNameEN: 'User',\n                  name: 'user',\n                  required: true,\n                },\n                {\n                  defaultValue: '',\n                  inputType: InputType.PASSWORD,\n                  labelNameCN: '密码',\n                  labelNameEN: 'Password',\n                  name: 'password',\n                  required: true,\n                },\n              ],\n\n              label: 'User&Password',\n              value: AuthenticationType.USERANDPASSWORD,\n            },\n            {\n              label: 'NONE',\n              value: AuthenticationType.NONE,\n              items: [],\n            },\n          ],\n          styles: {\n            width: '50%',\n          },\n        },\n        {\n          defaultValue: '',\n          inputType: InputType.INPUT,\n          labelNameCN: '数据库',\n          labelNameEN: 'Database',\n          name: 'database',\n          required: false,\n        },\n        {\n          defaultValue: 'jdbc:timeplus://localhost:7587',\n          inputType: InputType.INPUT,\n          labelNameCN: 'URL',\n          labelNameEN: 'URL',\n          name: 'url',\n          required: true,\n        },\n      ],\n      pattern: /jdbc:timeplus:\\/\\/(.*):(\\d+)(\\/(\\w+))?/,\n      template: 'jdbc:timeplus://{host}:{port}/{database}',\n      excludes: [OperationColumn.ViewDDL, OperationColumn.CreateTable, OperationColumn.EditTable],\n    },\n    ssh: sshConfig,\n  },\n];\n"
  },
  {
    "path": "chat2db-client/src/components/ConnectionEdit/config/enum.ts",
    "content": "export enum InputType {\n  INPUT = 'input',\n  PASSWORD = 'password',\n  SELECT = 'select',\n}\n\nexport enum AuthenticationType {\n  USERANDPASSWORD = '1',\n  NONE = '2',\n}\n\nexport enum SSHAuthenticationType {\n  PASSWORD = 1,\n  KEYPAIR = 2,\n  OPENSSH = 3\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ConnectionEdit/config/types.ts",
    "content": "import { InputType, AuthenticationType, SSHAuthenticationType } from './enum';\nimport { DatabaseTypeCode, OperationColumn } from '@/constants';\n\nexport type ISelect = {\n  value?: AuthenticationType | SSHAuthenticationType | string | boolean;\n  label?: string;\n  onChange?: (value: IConnectionConfig) => IConnectionConfig;\n  rest?: {\n    [key in string]: any\n  }\n  items?: IFormItem[];\n};\n\nexport interface IFormItem {\n  defaultValue: any;\n  inputType: InputType;\n  labelNameCN: string;\n  labelNameEN: string;\n  name: string;\n  required: boolean;\n  selected?: any;\n  selects?: ISelect[];\n  labelTextAlign?: 'right';\n  placeholder?: string;\n  placeholderEN?: string;\n  styles?: {\n    width?: string; // 表单占用的长度 推荐百分比 默认值为 100%\n    labelWidthEN?: string; // 英文环境下表单label的长度 推荐px 默认值为 70px\n    labelWidthCN?: string; // 中文环境下表单label的长度 推荐px 默认值为 100px\n    labelAlign?: 'left' | 'right'; // label的对齐方式 默认值为左对齐\n  },\n}\n\n// 配置链接数据源表单 Json\nexport type IConnectionConfig = {\n  type: DatabaseTypeCode;\n  baseInfo: {\n    items: IFormItem[];\n    pattern: RegExp;\n    template: string;\n    excludes?: OperationColumn[];\n  },\n  driver?: {\n    items: IFormItem[];\n  }\n  ssh: {\n    items: IFormItem[];\n  },\n  extendInfo?: {\n    key: string;\n    value: any;\n  }[],\n  // TODO: 先取form里的配置，在取form.item的配置, 最后取默认值，目前没有取全局的\n  styles?: {\n    width?: string; // 表单占用的长度 推荐百分比 默认值为 100%\n    labelWidthEN?: string; // 英文环境下表单label的长度 推荐px 默认值为 70px\n    labelWidthCN?: string; // 中文环境下表单label的长度 推荐px 默认值为 100px\n    labelAlign?: 'left' | 'right'; // label的对齐方式 默认值为左对齐\n  }\n};\n"
  },
  {
    "path": "chat2db-client/src/components/ConnectionEdit/index.less",
    "content": "@import '../../styles/var.less';\n\n.connectionBox {\n  width: 100%;\n  box-sizing: border-box;\n  flex-shrink: 0;\n  padding: 20px 20%;\n  height: auto;\n}\n\n.title {\n  // position: sticky;\n  // top: 0;\n  // z-index: 1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background-color: var(--color-bg);\n  font-size: 26px;\n  text-align: center;\n  margin: 0 0px 30px 0px;\n  i {\n    font-size: 20px;\n    margin-right: 10px;\n    color: var(--color-primary);\n  }\n}\n\n.form {\n  :global {\n    display: flex;\n    justify-content: space-between;\n    flex-wrap: wrap;\n    .ant-form-item-label {\n      width: var(--form-label-width);\n    }\n  }\n}\n\n.extendInfoBox .form {\n  :global {\n    .custom-form-item {\n      > div > div:nth-of-type(1) {\n        width: 200px;\n      }\n    }\n  }\n}\n\n.labelTextAlign {\n  :global {\n    .custom-form-item {\n      > div > div:nth-of-type(1) {\n        text-align: right;\n      }\n    }\n  }\n}\n\n.formFooter {\n  width: 100%;\n  display: flex;\n  justify-content: space-between;\n  margin-top: 20px;\n  margin-bottom: 50px;\n  .cancel {\n    margin-right: 20px;\n    .f-button();\n  }\n\n  .rightButton {\n    display: flex;\n  }\n\n  .test,\n  .save,\n  .cancel {\n    .f-button();\n  }\n}\n\n.tabsBox {\n  margin-bottom: 20px;\n}\n\n.testSSHConnect {\n  margin: 10px 0px;\n  display: flex;\n  align-items: center;\n  i {\n    margin-right: 10px;\n    color: var(--success-color);\n  }\n  .testSSHConnectText {\n    margin-left: 4px;\n    font-size: 12px;\n    color: var(--color-primary);\n    cursor: pointer;\n    &:hover {\n      text-decoration: underline;\n    }\n  }\n}\n\n.extendTable {\n  max-height: 300px;\n  overflow-y: auto;\n  input {\n    border: 0px;\n  }\n  :global {\n    .custom-input[disabled],\n    .custom-input-disabled {\n      background-color: transparent;\n    }\n    .custom-table-tbody > tr.custom-table-row:hover > td {\n      background-color: transparent;\n    }\n    .custom-input {\n      padding: 0px;\n    }\n    .custom-input:focus {\n      border: 0px;\n      box-shadow: none;\n    }\n    thead .custom-table-cell {\n      box-sizing: content-box;\n      height: 28px;\n    }\n  }\n}\n\n.optionItem{\n  display: flex;\n  align-items: center;\n}\n\n.envTag {\n  flex-shrink: 0;\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  background-color: var(--color-primary);\n  margin-right: 8px;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ConnectionEdit/index.tsx",
    "content": "import React, { useEffect, useMemo, useState, Fragment, forwardRef, ForwardedRef, useImperativeHandle } from 'react';\nimport { i18n, isEn } from '@/i18n';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport connectionService from '@/service/connection';\nimport { ConnectionEnvType, databaseMap } from '@/constants';\nimport { dataSourceFormConfigs } from './config/dataSource';\nimport { IConnectionConfig, IFormItem, ISelect } from './config/types';\nimport { InputType } from './config/enum';\nimport { IConnectionDetails } from '@/typings';\nimport { deepClone } from '@/utils';\nimport { Select, Form, Input, message, Table, Button, Collapse } from 'antd';\nimport Iconfont from '@/components/Iconfont';\nimport LoadingGracile from '@/components/Loading/LoadingGracile';\nimport Driver from './components/Driver';\n\n// ----- store -----\nimport { useConnectionStore, getConnectionList } from '@/pages/main/store/connection';\n\nconst { Option } = Select;\n\ntype ITabsType = 'ssh' | 'baseInfo' | 'driver';\n\nexport enum submitType {\n  UPDATE = 'update',\n  SAVE = 'save',\n  TEST = 'test',\n}\n\ninterface IProps {\n  closeCreateConnection: () => void;\n  connectionData: IConnectionDetails;\n  submit?: (data: IConnectionDetails) => Promise<any>;\n}\n\nexport interface ICreateConnectionFunction {\n  getData: () => IConnectionDetails;\n}\n\nconst ConnectionEdit = forwardRef((props: IProps, ref: ForwardedRef<ICreateConnectionFunction>) => {\n  const { closeCreateConnection, connectionData, submit } = props;\n  const [baseInfoForm] = Form.useForm();\n  const [sshForm] = Form.useForm();\n  const [driveData, setDriveData] = useState<any>({});\n  const [backfillData, setBackfillData] = useState<IConnectionDetails>(connectionData);\n  const [loadings, setLoading] = useState({\n    confirmButton: false,\n    testButton: false,\n    sshTestLoading: false,\n  });\n\n  const { connectionEnvList } = useConnectionStore((state) => {\n    return {\n      connectionEnvList: state.connectionEnvList,\n    };\n  });\n\n  const [envList, setEnvList] = useState<{ value: number; label: string }[]>([]);\n\n  useEffect(() => {\n    const _envList = connectionEnvList?.map((t) => {\n      return {\n        value: t.id,\n        label: t.name,\n        color: t.color,\n      };\n    })\n\n    if(_envList){\n      setEnvList(_envList);\n    }\n  }, [connectionEnvList]);\n\n  const dataSourceFormConfigPropsMemo = useMemo<IConnectionConfig>(() => {\n    const deepCloneDataSourceFormConfigs = deepClone(dataSourceFormConfigs);\n    const data = deepCloneDataSourceFormConfigs.find((t: IConnectionConfig) => {\n      return t.type === backfillData.type;\n    });\n    data.baseInfo.items.forEach((t: IFormItem) => {\n      if (t.name === 'environmentId' && envList?.length) {\n        t.selects = envList;\n        t.defaultValue = envList[0].value;\n      }\n    });\n    return data;\n  }, [backfillData, envList]);\n\n  useEffect(() => {\n    setBackfillData(props.connectionData);\n  }, [props.connectionData]);\n\n\n  function driverFormChange(data: any) {\n    setDriveData(data);\n  }\n\n  const getItems = () => [\n    {\n      forceRender: true,\n      key: 'driver',\n      label: i18n('connection.title.driver'),\n      children: <Driver backfillData={backfillData} onChange={driverFormChange} />,\n    },\n    {\n      key: 'ssh',\n      forceRender: true,\n      label: i18n('connection.label.sshConfiguration'),\n      children: (\n        <div className={styles.sshBox}>\n          <RenderForm\n            dataSourceFormConfigProps={dataSourceFormConfigPropsMemo}\n            backfillData={backfillData!}\n            form={sshForm}\n            tab=\"ssh\"\n            disabled={backfillData.isAdmin === false}\n          />\n          <div className={styles.testSSHConnect}>\n            {loadings.sshTestLoading && <LoadingGracile />}\n            <div onClick={testSSH} className={styles.testSSHConnectText}>\n              {i18n('connection.message.testSshConnection')}\n            </div>\n          </div>\n        </div>\n      ),\n    },\n    {\n      forceRender: true,\n      key: 'extendInfo',\n      label: i18n('connection.label.advancedConfiguration'),\n      children: (\n        <div className={styles.extendInfoBox}>\n          <RenderExtendTable backfillData={backfillData!} />\n        </div>\n      ),\n    },\n  ];\n\n  useImperativeHandle(ref, () => ({\n    getData,\n  }));\n\n  function getData() {\n    const ssh = sshForm.getFieldsValue();\n    const baseInfo = baseInfoForm.getFieldsValue();\n    const extendInfo: any = [];\n    extendTableData.map((t: any) => {\n      if (t.label || t.value) {\n        extendInfo.push({\n          key: t.label,\n          value: t.value,\n        });\n      }\n    });\n\n    const data = {\n      ssh,\n      driverConfig: driveData,\n      ...baseInfo,\n      extendInfo,\n      connectionEnvType: ConnectionEnvType.DAILY,\n      type: backfillData.type,\n    };\n\n    if (backfillData.id) {\n      data.id = backfillData.id;\n    }\n\n    return data;\n  }\n\n  // 测试、保存、修改连接\n  function saveConnection(type: submitType) {\n    const p = getData();\n\n    if (type !== submitType.SAVE) {\n      p.id = backfillData.id;\n    }\n\n    // TODO: 如果用户没选环境,默认选第一个。这里应该直接默认给用户选上的，但是动态表单现在有点问题，后续解决\n    if (!p.environmentId) {\n      p.environmentId = envList[0].value;\n    }\n\n    const loadingsButton = type === submitType.TEST ? 'testButton' : 'confirmButton';\n\n    setLoading({\n      ...loadings,\n      [loadingsButton]: true,\n    });\n\n    if ((type === submitType.SAVE) && submit) {\n      submit?.(p).finally(() => {\n        setLoading({\n          ...loadings,\n          [loadingsButton]: false,\n        });\n      });\n      return;\n    }\n\n    const api: any = connectionService[type](p);\n    api\n      .then((res: any) => {\n        if (type === submitType.TEST) {\n          message.success(\n            res === false\n              ? i18n('connection.message.testConnectResult', i18n('common.text.failure'))\n              : i18n('connection.message.testConnectResult', i18n('common.text.successful')),\n          );\n        } else {\n          message.success(\n            type === submitType.UPDATE\n              ? i18n('common.message.modifySuccessfully')\n              : i18n('common.message.addedSuccessfully'),\n          );\n\n          getConnectionList();\n          \n          if (type === submitType.SAVE) {\n            setBackfillData({\n              ...backfillData,\n              id: res,\n            });\n          }\n        }\n      })\n      .finally(() => {\n        setLoading({\n          ...loadings,\n          [loadingsButton]: false,\n        });\n      });\n  }\n\n  function onCancel() {\n    closeCreateConnection();\n  }\n\n  function testSSH() {\n    const p = sshForm.getFieldsValue();\n    setLoading({\n      ...loadings,\n      sshTestLoading: true,\n    });\n    connectionService\n      .testSSH(p)\n      .then(() => {\n        message.success(i18n('connection.message.testConnectResult', i18n('common.text.successful')));\n      })\n      .finally(() => {\n        setLoading({\n          ...loadings,\n          sshTestLoading: false,\n        });\n      });\n  }\n\n  return (\n    <div ref={ref as any}  className={styles.connectionBox}>\n      <div className={styles.title}>\n        <Iconfont code={databaseMap[backfillData.type]?.icon} />\n        <div>{databaseMap[backfillData.type]?.name}</div>\n      </div>\n      <div className={styles.baseInfoBox}>\n        <RenderForm\n          dataSourceFormConfigProps={dataSourceFormConfigPropsMemo}\n          backfillData={backfillData!}\n          form={baseInfoForm}\n          tab=\"baseInfo\"\n          disabled={backfillData.isAdmin === false}\n        />\n      </div>\n      <Collapse defaultActiveKey={['driver']} items={getItems()} />\n      <div className={styles.formFooter}>\n        <div className={styles.test}>\n          {\n            <Button\n              loading={loadings.testButton}\n              onClick={saveConnection.bind(null, submitType.TEST)}\n              className={styles.test}\n            >\n              {i18n('connection.button.testConnection')}\n            </Button>\n          }\n        </div>\n        <div className={styles.rightButton}>\n          <Button onClick={onCancel} className={styles.cancel}>\n            {i18n('common.button.cancel')}\n          </Button>\n          <Button\n            className={styles.save}\n            type=\"primary\"\n            loading={loadings.confirmButton}\n            onClick={saveConnection.bind(null, backfillData.id ? submitType.UPDATE : submitType.SAVE)}\n          >\n            {backfillData.id ? i18n('common.button.modify') : i18n('common.button.save')}\n          </Button>\n        </div>\n      </div>\n    </div>\n  );\n});\n\nexport default ConnectionEdit;\n\ninterface IRenderFormProps {\n  tab: ITabsType;\n  form: any;\n  backfillData: IConnectionDetails;\n  dataSourceFormConfigProps: IConnectionConfig;\n  disabled: boolean\n}\n\nfunction RenderForm(props: IRenderFormProps) {\n  const { tab, form, backfillData, dataSourceFormConfigProps } = props;\n\n  let aliasChanged = false;\n\n  const [dataSourceFormConfig, setDataSourceFormConfig] = useState<IConnectionConfig>(dataSourceFormConfigProps);\n  const formDataRef = React.useRef<any>(null);\n\n  useEffect(() => {\n    form.resetFields();\n    changeDataSourceFormConfig(backfillData);\n    formDataRef.current = backfillData;\n  }, [backfillData.id, backfillData.type]);\n\n  useEffect(() => {\n    setDataSourceFormConfig(dataSourceFormConfigProps);\n  }, [dataSourceFormConfigProps]);\n\n  const initialValuesMemo = useMemo(() => {\n    return initialFormData(dataSourceFormConfigProps[tab]?.items);\n  }, []);\n\n  const [initialValues] = useState(initialValuesMemo);\n\n  useEffect(() => {\n    if (!backfillData) {\n      return;\n    }\n    if (tab === 'baseInfo') {\n      regEXFormatting({ url: backfillData.url }, backfillData);\n    }\n    if (tab === 'ssh') {\n      regEXFormatting({}, backfillData.ssh || {});\n    }\n    if (tab === 'driver') {\n      regEXFormatting({}, backfillData.driverConfig || {});\n    }\n  }, [backfillData]);\n\n  function changeDataSourceFormConfig(_backfillData: any) {\n    // 这里应该循环一遍所有的\n    dataSourceFormConfig.ssh.items.forEach((t: IFormItem) => {\n      if (t.selects) {\n        t.defaultValue = _backfillData?.ssh?.[t.name] || t.defaultValue;\n      }\n    });\n    dataSourceFormConfig.baseInfo.items.forEach((t: IFormItem) => {\n      if (t.selects) {\n        t.defaultValue = _backfillData[t.name] || t.defaultValue;\n        t.selects.forEach((selectItem: ISelect) => {\n          // 调用select内的回掉函数\n          if (selectItem.value === t.defaultValue) {\n            if(selectItem.onChange){\n              setDataSourceFormConfig(selectItem.onChange({...dataSourceFormConfig}))\n            }\n          }\n        });\n      }\n    });\n  }\n\n  function initialFormData(_dataSourceFormConfig: IFormItem[] | undefined) {\n    let initValue: any = {};\n    _dataSourceFormConfig?.map((t) => {\n      initValue[t.name] = t.defaultValue;\n      if (t.selects?.length) {\n        t.selects?.map((item) => {\n          if (item.value === t.defaultValue) {\n            initValue = {\n              ...initValue,\n              ...initialFormData(item.items),\n            };\n          }\n        });\n      }\n    });\n    return initValue;\n  }\n\n  function onFieldsChange(data: any, datas: any) {\n    // 将antd的格式转换为正常的对象格式\n    if (!data.length) {\n      return;\n    }\n    const keyName = data[0].name[0];\n    const keyValue = data[0].value;\n    const variableData = {\n      [keyName]: keyValue,\n    };\n    const dataObj: any = {};\n    datas.map((t: any) => {\n      dataObj[t.name[0]] = t.value;\n    });\n\n    const finalData = {\n      ...(formDataRef.current || {}),\n      ...dataObj,\n    }\n\n    formDataRef.current = finalData;\n\n    // 正则拆分url/组建url\n    if (tab === 'baseInfo') {\n      regEXFormatting(variableData, finalData);\n    }\n  }\n\n  function extractObj(url: any) {\n    const { template, pattern } = dataSourceFormConfig.baseInfo;\n    // 提取关键词对应的内容 value\n    const matches = url.match(pattern)!;\n    // 提取花括号内的关键词 key\n    const reg = /{(.*?)}/g;\n    let match;\n    const arr = [];\n    while ((match = reg.exec(template)) !== null) {\n      arr.push(match[1]);\n    }\n    // key与value一一对应\n    const newExtract: any = {};\n    arr.map((t, i) => {\n      newExtract[t] = t === 'database' ? matches[i + 2] || '' : matches[i + 1];\n    });\n    return newExtract;\n  }\n\n  function regEXFormatting(\n    variableData: { [key: string]: any },\n    dataObj: { [key: string]: any },\n    _dataSourceFormConfig?: IConnectionConfig,\n  ) {\n    const { template, pattern } = (_dataSourceFormConfig || dataSourceFormConfig).baseInfo;\n    const keyName = Object.keys(variableData)[0];\n    const keyValue = variableData[Object.keys(variableData)[0]];\n    let newData: any = {};\n\n    if (keyName === 'url') {\n      //先判断url是否符合规定的正则\n      if (pattern.test(keyValue)) {\n        newData = extractObj(keyValue);\n      }\n    } else if (keyName === 'alias') {\n      aliasChanged = true;\n    } else {\n      // 改变上边url动\n      let url = template;\n      Object.keys(dataObj).map((t) => {\n        url = url.replace(`{${t}}`, dataObj[t] || '');\n      });\n      newData = {\n        url,\n      };\n    }\n    \n    if (keyName === 'host' && !aliasChanged) {\n      newData.alias = '@' + keyValue;\n    }\n\n    form.setFieldsValue({\n      ...dataObj,\n      ...newData,\n    });\n  }\n\n  function renderFormItem(t: IFormItem): React.ReactNode {\n    const label = isEn ? t.labelNameEN : t.labelNameCN;\n    const name = t.name;\n    const width = t?.styles?.width || '100%';\n    const labelWidth = isEn ? t?.styles?.labelWidthEN || '100px' : t?.styles?.labelWidthCN || '70px';\n    const placeholder = isEn ? t.placeholderEN : t.placeholder;\n    const labelAlign = t?.styles?.labelAlign || 'left';\n\n    const FormItemTypes: { [key in InputType]: () => React.ReactNode } = {\n      [InputType.INPUT]: () => (\n        <Form.Item\n          label={label}\n          name={name}\n          style={{ '--form-label-width': labelWidth } as any}\n          labelAlign={labelAlign}\n        >\n          <Input placeholder={placeholder} />\n        </Form.Item>\n      ),\n\n      [InputType.SELECT]: () => (\n        <Form.Item\n          label={label}\n          name={name}\n          style={{ '--form-label-width': labelWidth } as any}\n          labelAlign={labelAlign}\n        >\n          <Select\n            placeholder={placeholder}\n            value={t.defaultValue}\n            onChange={(e) => {\n              t.selects?.forEach((selectItem) => {\n                if (selectItem.value === e) {\n                  let _dataSourceFormConfig = { ...dataSourceFormConfigProps };\n                  if (selectItem.onChange) {\n                    _dataSourceFormConfig = selectItem.onChange(_dataSourceFormConfig);\n                  }\n\n                  _dataSourceFormConfig[tab]?.items.map((j) => {\n                    if (j.name === name) {\n                      j.defaultValue = selectItem.value;\n                    }\n                  });\n                  setDataSourceFormConfig(_dataSourceFormConfig);\n                  regEXFormatting({ [name]: e }, formDataRef.current , _dataSourceFormConfig);\n                }\n              });\n            }}\n          >\n            {t.selects?.map((selectItem: any) => (\n              <Option  key={selectItem.value?.toString()} value={selectItem.value}>\n                <div className={styles.optionItem}>\n                  {\n                    selectItem?.color &&\n                    <div className={styles.envTag} style={{ background: selectItem?.color.toLocaleLowerCase() }} />\n                  }\n                  {selectItem.label}\n                </div>\n              </Option>\n            ))}\n          </Select>\n        </Form.Item>\n      ),\n\n      [InputType.PASSWORD]: () => (\n        <Form.Item\n          label={label}\n          name={name}\n          style={{ '--form-label-width': labelWidth } as any}\n          labelAlign={labelAlign}\n        >\n          <Input.Password />\n        </Form.Item>\n      ),\n    };\n\n    return (\n      <Fragment key={t.name}>\n        <div\n          key={t.name}\n          className={classnames({ [styles.labelTextAlign]: t.labelTextAlign })}\n          style={{ width: width }}\n        >\n          {FormItemTypes[t.inputType]()}\n        </div>\n        {t.selects?.map((item) => {\n          if (t.defaultValue === item.value) {\n            return item.items?.map((t) => {\n              return renderFormItem(t);\n            });\n          }\n        })}\n      </Fragment>\n    );\n  }\n\n  return (\n    <Form\n      colon={false}\n      name={tab}\n      form={form}\n      initialValues={initialValues}\n      className={styles.form}\n      autoComplete=\"off\"\n      labelAlign=\"left\"\n      onFieldsChange={onFieldsChange}\n      disabled={props.disabled}\n    >\n      {dataSourceFormConfig[tab]!.items.map((t) => renderFormItem(t))}\n    </Form>\n  );\n}\n\ninterface IRenderExtendTableProps {\n  backfillData: IConnectionDetails;\n}\n\nlet extendTableData: any = [];\n\ninterface IExtendTable {\n  key: number;\n  label: string;\n  value: string;\n}\n\nfunction RenderExtendTable(props: IRenderExtendTableProps) {\n  const { backfillData } = props;\n  const databaseType = backfillData.type;\n  const [data, setData] = useState<IExtendTable[]>([{ key: 0, label: '', value: '' }]);\n  const dataSourceFormConfigMemo = useMemo<IConnectionConfig>(() => {\n    return deepClone(dataSourceFormConfigs).find((t: IConnectionConfig) => {\n      return t.type === databaseType;\n    });\n  }, [backfillData.type]);\n  // 禁止修改\n  const disabled = backfillData.isAdmin === false;\n\n  useEffect(() => {\n    const extendInfoList = backfillData?.extendInfo?.length\n      ? backfillData?.extendInfo\n      : dataSourceFormConfigMemo.extendInfo;\n\n    const extendInfo =\n      extendInfoList?.map((t, i) => {\n        return {\n          key: i,\n          label: t.key,\n          value: t.value,\n        };\n      }) || [];\n\n    setData([...extendInfo, { key: extendInfo.length, label: '', value: '' }]);\n  }, [dataSourceFormConfigMemo, backfillData]);\n\n  useEffect(() => {\n    extendTableData = data;\n  }, [data]);\n\n  const columns: any = [\n    {\n      title: i18n('connection.tableHeader.name'),\n      dataIndex: 'label',\n      width: '60%',\n      render: (value: any, row: any, index: number) => {\n        let isCustomLabel = true;\n\n        dataSourceFormConfigMemo.extendInfo?.map((item) => {\n          if (item.key === row.label) {\n            isCustomLabel = false;\n          }\n        });\n\n        function change(e: any) {\n          const newData = [...data];\n          newData[index] = {\n            key: index,\n            label: e.target.value,\n            value: '',\n          };\n          setData(newData);\n        }\n\n        function blur() {\n          const newData = [];\n          data.map((t) => {\n            if (t.label) {\n              newData.push(t);\n            }\n          });\n          if (index === data.length - 1 && row.label) {\n            newData[index] = {\n              key: index,\n              label: row.label,\n              value: '',\n            };\n          }\n          setData([...newData, { key: newData.length, label: '', value: '' }]);\n        }\n\n        if (index === data.length - 1 || isCustomLabel) {\n          return (\n            <Input\n              disabled={disabled}\n              onBlur={blur}\n              placeholder={index === data.length - 1 ? i18n('common.text.custom') : ''}\n              onChange={change}\n              value={value}\n            />\n          );\n        } else {\n          return <span>{value}</span>;\n        }\n      },\n    },\n    {\n      title: i18n('connection.tableHeader.value'),\n      dataIndex: 'value',\n      width: '40%',\n      render: (value: any, row: any, index: number) => {\n        function change(e: any) {\n          const newData = [...data];\n          newData[index] = {\n            key: index,\n            label: row.label,\n            value: e.target.value,\n          };\n          setData(newData);\n        }\n\n        if (index === data.length - 1) {\n          return <Input onBlur={blur} disabled placeholder=\"<value>\" onChange={change} value={value} />;\n        } else {\n          return <Input  disabled={disabled} onChange={change} value={value} />;\n        }\n      },\n    },\n  ];\n\n  return (\n    <div className={styles.extendTable}>\n      <Table bordered size=\"small\" pagination={false} columns={columns} dataSource={data} />\n    </div>\n  );\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/components/ChatInput/index.less",
    "content": ".chatWrapper {\n  display: flex;\n  align-items: center;\n  padding: 2px 4px 2px 20px;\n  height: 42px;\n  box-sizing: border-box;\n  border-bottom: 1px solid var(--color-border-secondary);\n}\n\n.chatShortcut {\n  padding: 4px 8px;\n  color: #8a9099;\n  font-size: 12px;\n  line-height: 1;\n  white-space: nowrap;\n  background-color: #fffc;\n  border-radius: 4px;\n  border: 1px solid #d0d5d8;\n\n  margin-right: 10px;\n}\n\n.chatAi {\n  width: 16px;\n  height: 16px;\n  margin-right: 14px;\n}\n\n.suffixBlock {\n  display: flex;\n  align-items: center;\n}\n\n.enter {\n  width: 32px;\n  height: 24px;\n  padding: 0;\n  margin-right: 16px;\n\n  .enterIcon {\n    font-size: 12px;\n  }\n}\n\n.stop {\n  font-size: 16px;\n  color: red;\n  cursor: pointer;\n  padding: 2px 8px;\n  margin-right: 4px;\n  border-radius: 4px;\n  &:hover {\n    background-color: var(--color-hover-bg);\n  }\n}\n\n\n.tableSelectBlock {\n  // margin-right: 8px;\n  cursor: pointer;\n  padding: 4px 8px;\n  border-radius: 8px;\n\n  &:hover {\n    background-color: var(--color-bg-subtle);\n  }\n}\n\n.remainBlock {\n  border-radius: 16px;\n  background-color: var(--color-bg-subtle);\n  padding: 4px 12px;\n\n  &:hover {\n    //cursor: pointer;\n  }\n}\n\n.aiSelectedTable {\n  display: flex;\n  flex-direction: column;\n  max-width: 280px;\n}\n\n.aiSelectedTableTips {\n  padding-bottom: 4px;\n  border-bottom: 1px solid var(--color-border-secondary);\n  margin-bottom: 4px;\n}"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/components/ChatInput/index.tsx",
    "content": "import React, { useState } from 'react';\nimport styles from './index.less';\nimport AIImg from '@/assets/img/ai.svg';\nimport { Button, Input, Popover, Select, Radio, Space } from 'antd';\nimport i18n from '@/i18n/';\nimport Iconfont from '@/components/Iconfont';\nimport { AIType } from '@/typings/ai';\n\nexport const enum SyncModelType {\n  AUTO = 0,\n  MANUAL = 1,\n}\n\ninterface IProps {\n  value?: string;\n  result?: string;\n  tables?: string[];\n  syncTableModel: number;\n  selectedTables?: string[];\n  aiType: AIType;\n  disabled?: boolean;\n  isStream?: boolean;\n  onPressEnter: (value: string) => void;\n  onSelectTableSyncModel: (model: number) => void;\n  onSelectTables?: (tables: string[]) => void;\n  // onClickRemainBtn: Function;\n  onCancelStream: () => void;\n}\n\nconst ChatInput = (props: IProps) => {\n  const [value, setValue] = useState(props.value);\n\n  const onPressEnter = (e: any) => {\n    if (!e.target.value) {\n      return;\n    }\n    if (e.nativeEvent.isComposing && e.key === 'Enter') {\n      e.preventDefault();\n      return;\n    }\n    props.onPressEnter && props.onPressEnter(e.target.value);\n  };\n\n  const renderSelectTable = () => {\n    const { tables, onSelectTableSyncModel, selectedTables, onSelectTables } = props;\n    const options = (tables || []).map((t) => ({ value: t, label: t }));\n    return (\n      <div className={styles.aiSelectedTable}>\n        <Radio.Group\n          onChange={(v) => onSelectTableSyncModel(v.target.value)}\n          // value={syncTableModel}\n          value={SyncModelType.MANUAL}\n          style={{ marginBottom: '8px' }}\n        >\n          <Space direction=\"horizontal\">\n            {/* <Radio value={SyncModelType.AUTO}>自动</Radio> */}\n            <Radio value={SyncModelType.MANUAL}>手动</Radio>\n          </Space>\n        </Radio.Group>\n        {/* {syncTableModel === 0 ? (\n          i18n('chat.input.syncTable.tips')\n        ) : (\n        )} */}\n        <>\n          <span className={styles.aiSelectedTableTips}>{i18n('chat.input.remain.tooltip')}</span>\n          <Select\n            showSearch\n            mode=\"multiple\"\n            allowClear\n            options={options}\n            placeholder={i18n('chat.input.tableSelect.placeholder')}\n            value={selectedTables}\n            onChange={(v) => {\n              onSelectTables && onSelectTables(v);\n            }}\n          />\n        </>\n      </div>\n    );\n  };\n\n  const renderSuffix = () => {\n    return (\n      <div className={styles.suffixBlock}>\n        {props.isStream ? (\n          <Iconfont\n            onClick={() => {\n              props.onCancelStream && props.onCancelStream();\n            }}\n            code=\"&#xe652;\"\n            className={styles.stop}\n          />\n        ) : (\n          <Button\n            type=\"primary\"\n            className={styles.enter}\n            onClick={() => {\n              if (value) {\n                props.onPressEnter && props.onPressEnter(value);\n              }\n            }}\n          >\n            <Iconfont code=\"&#xe643;\" className={styles.enterIcon} />\n          </Button>\n        )}\n        {/* <Tooltip\n          title={<span style={{ color: window._AppThemePack.colorText }}>{i18n('chat.input.syncTable.tempTips')}</span>}\n          defaultOpen={!hasBubble}\n          color={window._AppThemePack.colorBgBase}\n          trigger={'contextMenu'}\n          onOpenChange={() => {\n            localStorage.setItem('syncTableBubble', 'true');\n          }}\n        >\n        </Tooltip> */}\n        <div className={styles.tableSelectBlock}>\n          <Popover content={renderSelectTable()} placement=\"bottomLeft\">\n            <Iconfont code=\"&#xe618;\" />\n          </Popover>\n        </div>\n      </div>\n    );\n  };\n\n  return (\n    <div className={styles.chatWrapper}>\n      <img className={styles.chatAi} src={AIImg} />\n      <Input\n        disabled={props.disabled}\n        value={value}\n        onChange={(e) => setValue(e.target.value)}\n        bordered={false}\n        placeholder={i18n('workspace.ai.input.placeholder')}\n        onPressEnter={onPressEnter}\n        suffix={renderSuffix()}\n      />\n    </div>\n  );\n};\n\nexport default ChatInput;\n"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/components/OperationLine/index.less",
    "content": "\n.consoleOptionsWrapper {\n  position: absolute;\n  bottom: 0px;\n  left: 0;\n  right: 0;\n  height: 40px;\n  display: flex;\n  align-items: center;\n\n  display: flex;\n  margin: 0 12px;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.consoleOptionsLeft {\n  display: flex;\n  align-items: center;\n  margin: 0px -6px;\n  button {\n    margin: 0px 6px;\n  }\n}\n\n.runButton {\n  display: flex;\n  align-items: center;\n  margin-right: 20px;\n  height: 28px;\n\n  i {\n    margin-right: 4px;\n  }\n}\n\n.saveButton {\n  width: 70px;\n  height: 28px;\n}"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/components/OperationLine/index.tsx",
    "content": "import React from 'react';\nimport i18n from '@/i18n';\nimport { Button, Popover } from 'antd';\nimport { IBoundInfo } from '@/typings/workspace';\nimport styles from './index.less';\nimport Iconfont from '@/components/Iconfont';\nimport SelectBoundInfo from '../SelectBoundInfo';\nimport { formatSql } from '@/utils/sql';\nimport { osNow } from '@/utils';\n\ninterface IProps {\n  boundInfo: IBoundInfo;\n  saveConsole: (sql: string) => void;\n  executeSQL: () => void;\n  setBoundInfo: (boundInfo: IBoundInfo) => void;\n  editorRef: any;\n  hasSaveBtn: boolean;\n}\n\nconst keyboardKey = (function () {\n  if (osNow().isMac) {\n    return {\n      command: 'Cmd',\n      Shift: 'Shift',\n    };\n  }\n  return {\n    command: 'Ctrl',\n    Shift: 'Shift',\n  };\n})();\n\nconst OperationLine = (props: IProps) => {\n  const { boundInfo, saveConsole, editorRef, hasSaveBtn, executeSQL, setBoundInfo } = props;\n\n  /**\n   * 格式化sql\n   */\n  const handleSQLFormat = () => {\n    let setValueType = 'select';\n    let sql = editorRef?.current?.getCurrentSelectContent();\n    if (!sql) {\n      sql = editorRef?.current?.getAllContent() || '';\n      setValueType = 'cover';\n    }\n    formatSql(sql, boundInfo.databaseType!).then((res) => {\n      editorRef?.current?.setValue(res, setValueType);\n    });\n  };\n\n  return (\n    <div className={styles.consoleOptionsWrapper}>\n      <div className={styles.consoleOptionsLeft}>\n        <Popover mouseEnterDelay={0.8} content={[keyboardKey.command, 'R'].join('+')} trigger=\"hover\">\n          <Button type=\"primary\" className={styles.runButton} onClick={() => executeSQL()}>\n            <Iconfont code=\"&#xe637;\" />\n            {i18n('common.button.execute')}\n          </Button>\n        </Popover>\n        {hasSaveBtn && (\n          <Popover mouseEnterDelay={0.8} content={[keyboardKey.command, 'S'].join('+')} trigger=\"hover\">\n            <Button\n              type=\"default\"\n              className={styles.saveButton}\n              onClick={() => saveConsole(editorRef?.current?.getAllContent())}\n            >\n              {i18n('common.button.save')}\n            </Button>\n          </Popover>\n        )}\n        <Button type=\"default\" onClick={handleSQLFormat}>\n          {i18n('common.button.format')}\n        </Button>\n      </div>\n      <SelectBoundInfo setBoundInfo={setBoundInfo} boundInfo={boundInfo} />\n    </div>\n  );\n};\n\nexport default OperationLine;\n"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/components/SelectBoundInfo/index.less",
    "content": "@import '../../../../styles/var.less';\n\n.consoleOptionsRight {\n  flex: 1;\n  display: flex;\n  justify-content: flex-end;\n}\n\n.boundInfoBoxSpacer {\n  flex: 1;\n}\n\n.boundInfoBox {\n  max-width: 160px;\n  display: flex;\n  align-items: center;\n  padding: 5px 6px 5px 8px;\n  border-radius: 4px;\n  cursor: pointer;\n\n  .boundInfoName {\n    .f-single-line();\n    margin: 0px 6px 0px 4px;\n  }\n\n  &:hover {\n    background-color: var(--color-hover-bg);\n  }\n}\n\n:global {\n  .ant-dropdown-menu-title-content {\n    height: 20px;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/components/SelectBoundInfo/index.tsx",
    "content": "import React, { useEffect, useMemo, useState, memo, useContext } from 'react';\nimport { IntelligentEditorContext } from '../../index';\nimport { Dropdown } from 'antd';\nimport { useConnectionStore } from '@/pages/main/store/connection';\nimport connectionService from '@/service/connection';\nimport historyService from '@/service/history';\nimport Iconfont from '@/components/Iconfont';\nimport { databaseMap } from '@/constants/database';\nimport styles from './index.less';\nimport sqlService from '@/service/sql';\nimport { IBoundInfo } from '@/typings';\n\nimport {\n  registerIntelliSenseField,\n  registerIntelliSenseKeyword,\n  registerIntelliSenseTable,\n  registerIntelliSenseDatabase,\n  resetSenseKeyword,\n  resetSenseTable,\n  resetSenseDatabase,\n  resetSenseField,\n} from '@/utils/IntelliSense';\n\ninterface IProps {\n  boundInfo: IBoundInfo;\n  setBoundInfo: (params: IBoundInfo) => void;\n}\n\ninterface IOption<T> {\n  key: string;\n  label: string;\n  value: T;\n}\n\nconst emptyOption = {\n  key: '',\n  label: '',\n  value: '',\n};\n\nconst SelectBoundInfo = memo((props: IProps) => {\n  const { boundInfo, setBoundInfo } = props;\n  const { setSelectedTables, setTableNameList, isActive } = useContext(IntelligentEditorContext);\n  const connectionList = useConnectionStore((state) => state.connectionList);\n  const [databaseNameList, setDatabaseNameList] = useState<IOption<string>[]>([emptyOption]);\n  const [schemaList, setSchemaList] = useState<IOption<string>[]>([emptyOption]);\n  const [allTableList, setAllTableList] = useState<any>([]);\n\n  useEffect(() => {\n    if(!isActive){\n      resetSenseKeyword();\n      resetSenseTable();\n      resetSenseDatabase();\n      resetSenseField();\n    }\n  }, [isActive]);\n\n  const dataSourceList = useMemo(() => {\n    return (\n      connectionList?.map((item) => ({\n        key: item.id.toString(),\n        label: item.alias,\n        value: item.id,\n        type: item.type,\n      })) || []\n    );\n  }, [connectionList]);\n\n  const supportDatabase = useMemo(() => {\n    return connectionList?.find((item) => item.id === boundInfo.dataSourceId)?.supportDatabase;\n  }, [boundInfo.dataSourceId, connectionList]);\n\n  const supportSchema = useMemo(() => {\n    return connectionList?.find((item) => item.id === boundInfo.dataSourceId)?.supportSchema;\n  }, [boundInfo.dataSourceId, connectionList]);\n\n  // 编辑器绑定的数据库类型变化时，重新注册智能提示\n  useEffect(() => {\n    if(!isActive){\n      return\n    }\n    registerIntelliSenseKeyword(boundInfo.databaseType);\n  }, [boundInfo.dataSourceId, isActive]);\n\n  // 当数据源变化时，重新获取数据库列表\n  useEffect(() => {\n    if (!isActive || boundInfo.connectable === false) {\n      return;\n    }\n    if (supportDatabase) {\n      setSchemaList([]);\n      setDatabaseNameList([]);\n      getDatabaseList();\n    }\n  }, [boundInfo.dataSourceId, isActive]);\n\n  // 当数据库名变化时，重新获取schema列表\n  useEffect(() => {\n    if (!isActive || boundInfo.connectable === false) {\n      return;\n    }\n    if (supportSchema) {\n      setSchemaList([]);\n      getSchemaList();\n    }\n    if (!supportSchema && boundInfo.databaseName) {\n      getAllTableNameList(boundInfo.dataSourceId, boundInfo.databaseName);\n    }\n  }, [boundInfo.databaseName, isActive, supportSchema]);\n\n  useEffect(() => {\n    if (!isActive || boundInfo.connectable === false) {\n      return;\n    }\n    if (supportSchema && boundInfo.schemaName) {\n      getAllTableNameList(boundInfo.dataSourceId, boundInfo.databaseName, boundInfo.schemaName);\n    }\n  }, [boundInfo.schemaName, isActive, supportSchema]);\n\n  // 获取数据库列表\n  const getDatabaseList = () => {\n    if (boundInfo.dataSourceId === undefined || boundInfo.dataSourceId === null) {\n      return;\n    }\n    connectionService\n      .getDatabaseList({\n        dataSourceId: boundInfo.dataSourceId,\n      })\n      .then((res) => {\n        const editorDatabaseTips: any = [];\n        const _databaseNameList = res.map((item) => {\n          editorDatabaseTips.push({\n            name: item.name,\n            dataSourceName: boundInfo.dataSourceName,\n          });\n          return {\n            key: item.name,\n            label: item.name,\n            value: item.name,\n          };\n        });\n        if (!_databaseNameList.length) {\n          getSchemaList();\n        }\n        setDatabaseNameList([emptyOption, ..._databaseNameList]);\n      });\n  };\n\n  // 获取schema列表\n  const getSchemaList = () => {\n    if (boundInfo.dataSourceId === undefined || boundInfo.dataSourceId === null) {\n      return;\n    }\n    connectionService\n      .getSchemaList({\n        dataSourceId: boundInfo.dataSourceId!,\n        databaseName: boundInfo?.databaseName,\n      })\n      .then((res: any) => {\n        const _schemaList = res.map((item) => ({\n          key: item.name,\n          label: item.name,\n          value: item.name,\n        }));\n        setSchemaList([emptyOption, ..._schemaList]);\n      });\n  };\n\n  // 注册表名\n  useEffect(() => {\n    if (isActive) {\n      const tableNameListTemp = allTableList.map((t) => t.name);\n      setTableNameList(tableNameListTemp);\n      registerIntelliSenseTable(\n        allTableList,\n        boundInfo.databaseType,\n        boundInfo.dataSourceId,\n        boundInfo.databaseName,\n        boundInfo.schemaName,\n      );\n      registerIntelliSenseField(\n        tableNameListTemp,\n        boundInfo.dataSourceId,\n        boundInfo.databaseName,\n        boundInfo.schemaName,\n      );\n      setSelectedTables(tableNameListTemp.slice(0, 1));\n    }\n  }, [allTableList, isActive]);\n\n  // 注册数据库名\n  useEffect(() => {\n    const editorDatabaseTips = databaseNameList.map((item) => ({\n      name: item.value,\n      dataSourceName: boundInfo.dataSourceName,\n    }));\n    registerIntelliSenseDatabase(editorDatabaseTips);\n  }, [databaseNameList]);\n\n  // 选择数据源\n  const changeDataSource = (item) => {\n    const currentData = dataSourceList.find((i) => i.key === item.key)!;\n    setBoundInfo({\n      ...boundInfo,\n      dataSourceId: currentData.value,\n      dataSourceName: currentData.label,\n      databaseType: currentData.type,\n      databaseName: void 0,\n      schemaName: void 0,\n    });\n    if (boundInfo.consoleId) {\n      historyService.updateSavedConsole({\n        id: boundInfo.consoleId,\n        dataSourceId: currentData.value,\n        dataSourceName: currentData.label,\n        type: currentData.type,\n      });\n    }\n  };\n\n  // 选择数据库\n  const changeDataBase = (item) => {\n    const _databaseName = databaseNameList?.find((i) => i.key === item.key)?.value;\n\n    setBoundInfo({\n      ...boundInfo,\n      databaseName: _databaseName,\n      schemaName: void 0,\n    });\n\n    if (boundInfo.consoleId) {\n      historyService.updateSavedConsole({\n        id: boundInfo.consoleId,\n        databaseName: _databaseName,\n      });\n    }\n  };\n\n  // 选择schema\n  const changeSchema = (item) => {\n    const _schemaName = schemaList?.find((i) => i.key === item.key)?.value;\n    setBoundInfo({\n      ...boundInfo,\n      schemaName: _schemaName,\n    });\n\n    if (boundInfo.consoleId) {\n      historyService.updateSavedConsole({\n        id: boundInfo.consoleId,\n        schemaName: _schemaName,\n      });\n    }\n  };\n\n  const getAllTableNameList = (dataSourceId, databaseName, schemaName?) => {\n    sqlService\n      .getAllTableList({\n        dataSourceId,\n        databaseName,\n        schemaName,\n      })\n      .then((data) => {\n        setAllTableList(data);\n      });\n  };\n\n  return (\n    <div className={styles.consoleOptionsRight}>\n      <div className={styles.boundInfoBoxSpacer} />\n      <Dropdown\n        menu={{\n          items: dataSourceList,\n          onClick: changeDataSource,\n        }}\n        trigger={['click']}\n      >\n        <div className={styles.boundInfoBox}>\n          <Iconfont code={databaseMap[boundInfo.databaseType!]?.icon} />\n          <div className={styles.boundInfoName}>{boundInfo.dataSourceName || `<${'dataSource'}>`}</div>\n          <Iconfont code=\"&#x100be;\" />\n        </div>\n      </Dropdown>\n\n      {supportDatabase && (\n        <Dropdown\n          menu={{\n            items: databaseNameList,\n            onClick: changeDataBase,\n          }}\n          trigger={['click']}\n        >\n          <div className={styles.boundInfoBox}>\n            <Iconfont code=\"&#xe669;\" />\n            <div className={styles.boundInfoName}>{boundInfo.databaseName || `<${'database'}>`}</div>\n            <Iconfont code=\"&#x100be;\" />\n          </div>\n        </Dropdown>\n      )}\n\n      {supportSchema && (\n        <Dropdown\n          menu={{\n            items: schemaList,\n            onClick: changeSchema,\n          }}\n          trigger={['click']}\n        >\n          <div className={styles.boundInfoBox}>\n            <Iconfont code=\"&#xe663;\" />\n            <div className={styles.boundInfoName}>{boundInfo.schemaName || `<${'schema'}>`}</div>\n            <Iconfont code=\"&#x100be;\" />\n          </div>\n        </Dropdown>\n      )}\n    </div>\n  );\n});\n\nexport default SelectBoundInfo;\n"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/hooks/useModuleData.ts",
    "content": "import {useState, useEffect} from 'react';\nimport sqlService from '@/service/sql';\nimport {IBoundInfo} from '../index'\n\ninterface IProps {\n  boundInfo: IBoundInfo;\n}\n\nexport const useModuleData = (props: IProps) => {\n  const { boundInfo } = props;\n  const [selectedTables, setSelectedTables] = useState<string[]>([]);\n  const [tableNameList, setTableNameList] = useState<string[]>([]);\n\n  useEffect(() => {\n    const { dataSourceId, databaseName, schemaName } = boundInfo;\n\n    if( !databaseName && !schemaName){\n      setTableNameList([]);\n      setSelectedTables([]);\n      return\n    }\n\n    sqlService\n      .getAllTableList({\n        dataSourceId,\n        databaseName,\n        schemaName,\n      })\n      .then((data) => {\n        const tableNameListTemp = data.map((t) => t.name);\n        setTableNameList(tableNameListTemp);\n\n        if (selectedTables.length === 0) {\n          setSelectedTables(tableNameListTemp.slice(0, 1));\n        }\n      });\n  }, []);\n\n  return {\n    selectedTables,\n    setSelectedTables,\n    tableNameList,\n    setTableNameList\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/hooks/useSaveEditorData.ts",
    "content": "import {useState, useEffect, useRef} from 'react';\nimport { ConsoleStatus } from '@/constants';\nimport { message } from 'antd';\nimport indexedDB from '@/indexedDB';\nimport historyServer from '@/service/history';\nimport i18n from '@/i18n';\nimport { getCookie } from '@/utils';\nimport { getSavedConsoleList } from '@/pages/main/workspace/store/console';\n\n\ninterface IProps {\n  isActive?: boolean;\n  source?: string;\n  editorRef: any;\n  boundInfo: any;\n  defaultValue?: string;\n}\n\nexport const useSaveEditorData = (props: IProps) => {\n  const { isActive, source, editorRef, boundInfo, defaultValue } = props;\n  const timerRef = useRef<any>();\n    // 上一次同步的console数据\n  const lastSyncConsole = useRef<any>(defaultValue);\n  const [saveStatus, setSaveStatus] = useState<ConsoleStatus>(boundInfo.status || ConsoleStatus.DRAFT);\n\n  const saveConsole = (value?: string, noPrompting?: boolean) => {\n    const p: any = {\n      id: boundInfo.consoleId,\n      status: ConsoleStatus.RELEASE,\n      ddl: value,\n    };\n\n    historyServer.updateSavedConsole(p).then(() => {\n      getSavedConsoleList();\n      indexedDB.deleteData('chat2db', 'workspaceConsoleDDL', boundInfo.consoleId!);\n      lastSyncConsole.current = value;\n      setSaveStatus(ConsoleStatus.RELEASE);\n      if (noPrompting) {\n        return;\n      }\n      message.success(i18n('common.tips.saveSuccessfully'));\n      timingAutoSave(ConsoleStatus.RELEASE);\n    });\n  };\n\n  function timingAutoSave(_status?: ConsoleStatus) {\n    if (timerRef.current) {\n      clearInterval(timerRef.current);\n    }\n    timerRef.current = setInterval(() => {\n      const curValue = editorRef?.current?.getAllContent();\n      if (curValue === lastSyncConsole.current) {\n        return;\n      }\n      if (saveStatus === ConsoleStatus.RELEASE || _status === ConsoleStatus.RELEASE) {\n        saveConsole(curValue, true);\n      } else {\n        indexedDB\n          .updateData('chat2db', 'workspaceConsoleDDL', {\n            consoleId: boundInfo.consoleId!,\n            ddl: curValue,\n            userId: getCookie('CHAT2DB.USER_ID'),\n          })\n          .then(() => {\n            lastSyncConsole.current = curValue;\n          });\n      }\n    }, 5000);\n  }\n\n  useEffect(() => {\n    if (source !== 'workspace') {\n      return;\n    }\n    // 离开时保存\n    if (!isActive) {\n      // 离开时清除定时器\n      if (timerRef.current) {\n        clearInterval(timerRef.current);\n      }\n      const curValue = editorRef?.current?.getAllContent();\n      if (curValue === lastSyncConsole.current) {\n        return;\n      }\n      if (saveStatus === ConsoleStatus.RELEASE) {\n        saveConsole(curValue, true);\n      } else {\n        indexedDB\n          .updateData('chat2db', 'workspaceConsoleDDL', {\n            consoleId: boundInfo.consoleId!,\n            ddl: curValue,\n            userId: getCookie('CHAT2DB.USER_ID'),\n          })\n          .then(() => {\n            lastSyncConsole.current = curValue;\n          });\n      }\n    } else {\n      timingAutoSave();\n    }\n    return () => {\n      if (timerRef.current) {\n        clearInterval(timerRef.current);\n      }\n    };\n  }, [isActive]);\n\n  useEffect(() => {\n    if (saveStatus === ConsoleStatus.RELEASE) {\n      editorRef?.current?.setValue(defaultValue, 'cover');\n    } else {\n      indexedDB\n        .getDataByCursor('chat2db', 'workspaceConsoleDDL', {\n          consoleId: boundInfo.consoleId!,\n          userId: getCookie('CHAT2DB.USER_ID'),\n        })\n        .then((res: any) => {\n          // oldValue是为了处理函数视图等，他们是带着值来的，不需要去数据库取值\n          const oldValue = editorRef?.current?.getAllContent();\n          if (!oldValue) {\n            editorRef?.current?.setValue(res?.[0]?.ddl || '', 'cover');\n          }\n        });\n    }\n  }, []);\n\n  return {saveConsole, saveStatus}\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/index.less",
    "content": ".console {\n  position: relative;\n  height: 100%;\n\n  :global {\n    .ant-spin-nested-loading {\n      height: 100%;\n    }\n\n    .ant-spin-container {\n      height: calc(100% - 40px);\n    }\n  }\n}\n\n.consoleEditor {\n  height: 100%;\n}\n\n.consoleEditorWithChat {\n  height: calc(100% - 42px);\n}\n\n.aiBlock {\n  font-size: 14px;\n  line-height: 20px;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ConsoleEditor/index.tsx",
    "content": "import React, {\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  useImperativeHandle,\n  ForwardedRef,\n  forwardRef,\n  createContext,\n} from 'react';\nimport { formatParams } from '@/utils/url';\nimport connectToEventSource from '@/utils/eventSource';\nimport { Spin, Drawer, Modal } from 'antd';\nimport ChatInput, { SyncModelType } from './components/ChatInput';\nimport MonacoEditor, { IEditorOptions, IExportRefFunction, IRangeType } from '../MonacoEditor';\nimport aiServer from '@/service/ai';\nimport { v4 as uuidv4 } from 'uuid';\nimport { IAiConfig, IBoundInfo } from '@/typings';\nimport Popularize from '@/components/Popularize';\nimport OperationLine from './components/OperationLine';\nimport { chatErrorForKey, chatErrorToLogin } from '@/constants/chat';\nimport { AIType } from '@/typings/ai';\nimport i18n from '@/i18n';\nimport configService from '@/service/config';\nimport styles from './index.less';\n\n// ----- hooks -----\nimport { useSaveEditorData } from './hooks/useSaveEditorData';\n\n// ----- store -----\nimport { useSettingStore, fetchRemainingUse, setAiConfig } from '@/store/setting';\n\n// ----- function -----\nimport { handelCreateConsole } from '@/pages/main/workspace/functions/shortcutKeyCreateConsole';\n\nenum IPromptType {\n  NL_2_SQL = 'NL_2_SQL',\n  SQL_EXPLAIN = 'SQL_EXPLAIN',\n  SQL_OPTIMIZER = 'SQL_OPTIMIZER',\n  SQL_2_SQL = 'SQL_2_SQL',\n  ChatRobot = 'ChatRobot',\n}\n\nexport type IAppendValue = {\n  text: any;\n  range?: IRangeType;\n};\n\ninterface IProps {\n  /** 调用来源 */\n  source?: 'workspace';\n  isActive: boolean;\n  /** 添加或修改的内容 */\n  appendValue?: IAppendValue;\n  defaultValue?: string;\n  /** 是否开启AI输入 */\n  hasAiChat: boolean;\n  /** 是否可以开启SQL转到自然语言的相关ai操作 */\n  hasAi2Lang?: boolean;\n  /** 是否有 */\n  hasSaveBtn?: boolean;\n  value?: string;\n  boundInfo: IBoundInfo;\n  setBoundInfo: (params: IBoundInfo) => void;\n  editorOptions?: IEditorOptions;\n  onExecuteSQL: (sql: string) => void;\n}\n\nexport interface IConsoleRef {\n  editorRef: IExportRefFunction | undefined;\n}\n\ninterface IIntelligentEditorContext {\n  isActive: boolean;\n  tableNameList: string[];\n  setTableNameList: (tables: string[]) => void;\n  selectedTables: string[];\n  setSelectedTables: (tables: string[]) => void;\n}\n\nexport const IntelligentEditorContext = createContext<IIntelligentEditorContext>({} as any);\n\nfunction ConsoleEditor(props: IProps, ref: ForwardedRef<IConsoleRef>) {\n  const {\n    hasAiChat = true,\n    boundInfo,\n    setBoundInfo,\n    appendValue,\n    hasSaveBtn = true,\n    source,\n    defaultValue,\n    isActive,\n  } = props;\n  const uid = useMemo(() => uuidv4(), []);\n  const chatResult = useRef('');\n  const editorRef = useRef<IExportRefFunction>();\n  const [selectedTables, setSelectedTables] = useState<string[]>([]);\n  const [tableNameList, setTableNameList] = useState<string[]>([]);\n  const [syncTableModel, setSyncTableModel] = useState<number>(0);\n  const [isLoading, setIsLoading] = useState(false);\n  const [aiContent, setAiContent] = useState('');\n  const [isAiDrawerOpen, setIsAiDrawerOpen] = useState(false);\n  const [isAiDrawerLoading, setIsAiDrawerLoading] = useState(false);\n  const [popularizeModal, setPopularizeModal] = useState(false);\n  const [modalProps, setModalProps] = useState({});\n  const [isStream, setIsStream] = useState(false);\n  const aiFetchIntervalRef = useRef<any>();\n  const closeEventSource = useRef<any>();\n  const { aiConfig, hasWhite, remainingUse } = useSettingStore((state) => {\n    return {\n      aiConfig: state.aiConfig,\n      hasWhite: state.hasWhite,\n      remainingUse: state.remainingUse,\n    };\n  });\n\n  // ---------------- new-code ----------------\n  const { saveConsole } = useSaveEditorData({\n    editorRef,\n    isActive,\n    boundInfo: props.boundInfo,\n    source,\n    defaultValue,\n  });\n  // ---------------- new-code ----------------\n\n  /**\n   * 当前选择的AI类型是Chat2DBAI\n   */\n  const isChat2DBAI = useMemo(() => aiConfig?.aiSqlSource === AIType.CHAT2DBAI, [aiConfig?.aiSqlSource]);\n\n  useEffect(() => {\n    handleSelectTableSyncModel();\n  }, [hasWhite, localStorage.getItem('syncTableModel')]);\n\n  useEffect(() => {\n    if (appendValue) {\n      editorRef?.current?.setValue(appendValue.text, appendValue.range);\n    }\n  }, [appendValue]);\n\n  useImperativeHandle(\n    ref,\n    () => ({\n      editorRef: editorRef?.current,\n    }),\n    [editorRef?.current],\n  );\n\n  const handleApiKeyEmptyOrGetQrCode = async (shouldPoll?: boolean) => {\n    setIsLoading(true);\n    try {\n      const { wechatQrCodeUrl, token, tip } = await aiServer.getLoginQrCode({});\n      setIsLoading(false);\n\n      setPopularizeModal(true);\n      setModalProps({\n        imageUrl: wechatQrCodeUrl,\n        token,\n        tip,\n      });\n      if (shouldPoll) {\n        let pollCnt = 0;\n        aiFetchIntervalRef.current = setInterval(async () => {\n          const { apiKey } = (await aiServer.getLoginStatus({ token })) || {};\n          pollCnt++;\n          if (apiKey || pollCnt >= 60) {\n            clearInterval(aiFetchIntervalRef.current);\n          }\n          if (apiKey) {\n            setPopularizeModal(false);\n\n            setAiConfig({\n              ...(aiConfig || {}),\n              apiKey,\n            });\n\n            fetchRemainingUse(apiKey);\n          }\n        }, 3000);\n      }\n    } catch (e) {\n      setIsLoading(false);\n    }\n  };\n\n  const handleAIChatInEditor = async (content: string, promptType: IPromptType, ext?: string) => {\n    const _aiConfig = await configService.getAiSystemConfig({});\n    handleAiChat(content, promptType, _aiConfig, ext);\n  };\n\n  const handleAiChat = async (content: string, promptType: IPromptType, _aiConfig?: IAiConfig, ext?: string) => {\n    const { apiKey } = _aiConfig || aiConfig || {};\n    if (!apiKey && isChat2DBAI) {\n      handleApiKeyEmptyOrGetQrCode(true);\n      return;\n    }\n\n    const { dataSourceId, databaseName, schemaName } = boundInfo;\n    const isNL2SQL = promptType === IPromptType.NL_2_SQL;\n    if (isNL2SQL) {\n      setIsLoading(true);\n    } else {\n      setAiContent('');\n      setIsAiDrawerOpen(true);\n      setIsAiDrawerLoading(true);\n    }\n\n    const params = formatParams({\n      message: content,\n      promptType,\n      dataSourceId,\n      databaseName,\n      schemaName,\n      tableNames: syncTableModel ? selectedTables : null,\n      ext,\n    });\n\n    const handleMessage = (_message: string) => {\n      setIsLoading(false);\n      setIsAiDrawerLoading(false);\n      try {\n        const isEOF = _message === '[DONE]';\n        if (isEOF) {\n          closeEventSource.current();\n          setIsStream(false);\n          if (isChat2DBAI) {\n            fetchRemainingUse(apiKey);\n          }\n          if (isNL2SQL) {\n            editorRef?.current?.setValue('\\n');\n          } else {\n            setIsAiDrawerLoading(false);\n            chatResult.current += '\\n';\n            setAiContent(chatResult.current);\n            chatResult.current = '';\n          }\n          return;\n        }\n\n        let hasErrorToLogin = false;\n        chatErrorToLogin.forEach((err) => {\n          if (_message.includes(err)) {\n            hasErrorToLogin = true;\n          }\n        });\n        let hasKeyLimitedOrExpired = false;\n        chatErrorForKey.forEach((err) => {\n          if (_message.includes(err)) {\n            hasKeyLimitedOrExpired = true;\n          }\n        });\n\n        if (hasKeyLimitedOrExpired) {\n          closeEventSource.current();\n          setIsLoading(false);\n          handlePopUp();\n          return;\n        }\n\n        if (hasErrorToLogin) {\n          closeEventSource.current();\n          setIsLoading(false);\n          hasErrorToLogin && handleApiKeyEmptyOrGetQrCode(true);\n          // hasErrorToInvite && handleClickRemainBtn();\n          fetchRemainingUse(apiKey);\n          return;\n        }\n\n        if (isNL2SQL) {\n          editorRef?.current?.setValue(JSON.parse(_message).content);\n        } else {\n          chatResult.current += JSON.parse(_message).content;\n          setAiContent(chatResult.current);\n        }\n      } catch (error) {\n        setIsLoading(false);\n        setIsStream(false);\n        setIsAiDrawerLoading(false);\n        closeEventSource.current();\n      }\n    };\n\n    const handleError = (error: any) => {\n      console.error('Error:', error);\n      setIsLoading(false);\n      setIsAiDrawerLoading(false);\n      setIsStream(false);\n      closeEventSource.current();\n    };\n\n    closeEventSource.current = connectToEventSource({\n      url: `/api/ai/chat?${params}`,\n      uid,\n      onOpen: () => {\n        setIsStream(true);\n      },\n      onMessage: handleMessage,\n      onError: handleError,\n    });\n  };\n\n  const executeSQL = (sql?: string) => {\n    const sqlContent = sql || editorRef?.current?.getCurrentSelectContent() || editorRef?.current?.getAllContent();\n\n    if (!sqlContent) {\n      return;\n    }\n    props.onExecuteSQL && props.onExecuteSQL(sqlContent);\n  };\n\n  const addAction = [\n    {\n      id: 'explainSQL',\n      label: i18n('common.text.explainSQL'),\n      action: (selectedText: string) => handleAIChatInEditor(selectedText, IPromptType.SQL_EXPLAIN),\n    },\n    {\n      id: 'optimizeSQL',\n      label: i18n('common.text.optimizeSQL'),\n      action: (selectedText: string) => handleAIChatInEditor(selectedText, IPromptType.SQL_OPTIMIZER),\n    },\n    {\n      id: 'changeSQL',\n      label: i18n('common.text.conversionSQL'),\n      action: (selectedText: string, ext?: string) => {\n        handleAIChatInEditor(selectedText, IPromptType.SQL_2_SQL, ext);\n      },\n    },\n  ];\n\n  /**\n   * 弹框 关注公众号\n   */\n  const handlePopUp = () => {\n    setModalProps({\n      imageUrl:\n        'http://oss.sqlgpt.cn/static/chat2db-wechat.jpg?x-oss-process=image/auto-orient,1/resize,m_lfit,w_256/quality,Q_80/format,webp',\n      tip: (\n        <>\n          {remainingUse?.remainingUses === 0 && <p>Key次数用完或者过期</p>}\n          <p>微信扫描二维码并关注公众号获得 AI 使用机会。</p>\n        </>\n      ),\n    });\n    setPopularizeModal(true);\n  };\n\n  const handleSelectTableSyncModel = () => {\n    const syncModel = localStorage.getItem('syncTableModel');\n    const hasAiAccess = hasWhite;\n    if (syncModel !== null) {\n      setSyncTableModel(Number(syncModel));\n      return;\n    }\n\n    setSyncTableModel(hasAiAccess ? SyncModelType.AUTO : SyncModelType.MANUAL);\n  };\n\n  // 注册快捷键\n  const registerShortcutKey = (editor, monaco) => {\n    // 保存\n    editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {\n      const value = editor?.getValue();\n      saveConsole(value || '');\n    });\n\n    // 执行\n    editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyR, () => {\n      const value = editorRef.current?.getCurrentSelectContent();\n      executeSQL(value);\n    });\n\n    // 执行\n    editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyL, () => {\n      handelCreateConsole();\n    });\n  };\n\n  return (\n    <IntelligentEditorContext.Provider\n      value={{\n        isActive,\n        tableNameList,\n        setTableNameList,\n        selectedTables,\n        setSelectedTables,\n      }}\n    >\n      <div className={styles.console} ref={ref as any}>\n        <Spin spinning={isLoading} style={{ height: '100%' }}>\n          {hasAiChat && (\n            <ChatInput\n              isStream={isStream}\n              disabled={isLoading}\n              aiType={aiConfig?.aiSqlSource}\n              tables={tableNameList}\n              onPressEnter={(value: string) => {\n                handleAiChat(value, IPromptType.NL_2_SQL);\n              }}\n              selectedTables={selectedTables}\n              onSelectTables={(tables: string[]) => {\n                setSelectedTables(tables);\n              }}\n              syncTableModel={syncTableModel}\n              onSelectTableSyncModel={(model: number) => {\n                setSyncTableModel(model);\n                localStorage.setItem('syncTableModel', String(model));\n              }}\n              onCancelStream={() => {\n                closeEventSource.current();\n                setIsStream(false);\n                setIsLoading(false);\n              }}\n            />\n          )}\n          <MonacoEditor\n            id={uid}\n            defaultValue={defaultValue}\n            ref={editorRef as any}\n            className={hasAiChat ? styles.consoleEditorWithChat : styles.consoleEditor}\n            addAction={addAction}\n            options={props.editorOptions}\n            shortcutKey={registerShortcutKey}\n            isActive={isActive}\n          />\n          <Drawer\n            open={isAiDrawerOpen}\n            getContainer={false}\n            mask={false}\n            onClose={() => {\n              try {\n                setIsAiDrawerOpen(false);\n                setIsAiDrawerLoading(false);\n                setIsStream(false);\n                closeEventSource.current && closeEventSource.current();\n              } catch (error) {\n                // console.log('close drawer', error);\n              }\n            }}\n          >\n            <Spin spinning={isAiDrawerLoading} style={{ height: '100%' }}>\n              <div className={styles.aiBlock}>{aiContent}</div>\n            </Spin>\n          </Drawer>\n        </Spin>\n        <OperationLine\n          boundInfo={boundInfo}\n          saveConsole={saveConsole}\n          executeSQL={executeSQL}\n          editorRef={editorRef}\n          setBoundInfo={setBoundInfo}\n          hasSaveBtn={hasSaveBtn}\n        />\n        <Modal\n          open={popularizeModal}\n          footer={false}\n          onCancel={() => {\n            aiFetchIntervalRef.current && clearInterval(aiFetchIntervalRef.current);\n            setPopularizeModal(false);\n          }}\n        >\n          <Popularize {...modalProps} />\n        </Modal>\n      </div>\n    </IntelligentEditorContext.Provider>\n  );\n}\n\nexport default forwardRef(ConsoleEditor);\n"
  },
  {
    "path": "chat2db-client/src/components/CreateDatabase/index.less",
    "content": "@import '../../styles/var.less';\n\n.monacoEditorBox {\n  height: 200px;\n  border: 1px solid var(--color-border);\n  border-radius: 4px;\n  overflow: hidden;\n}\n\n.errorBox {\n  margin-top: 10px;\n}\n\n.previewBox {\n  // 伪元素画一条剧中的线条\n  position: relative;\n  display: flex;\n  margin-bottom: 4px;\n  .previewText {\n    background: var(--color-bg-base);\n    flex-shrink: 0;\n    margin-right: 10px;\n  }\n  .previewLine {\n    flex: 1;\n    position: relative;\n    &::after {\n      position: absolute;\n      left: 0;\n      right: 0px;\n      top: 50%;\n      content: '';\n      width: 100%;\n      height: 1px;\n      background: var(--color-border);\n      transform: translateY(-50%);\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/CreateDatabase/index.tsx",
    "content": "import React, { useCallback, useMemo, useState, useEffect } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Form, Input, Modal } from 'antd';\nimport MonacoEditor, { IExportRefFunction } from '@/components/MonacoEditor';\nimport { v4 as uuid } from 'uuid';\nimport sqlService from '@/service/sql';\nimport i18n from '@/i18n';\nimport { debounce } from 'lodash';\nimport { DatabaseTypeCode } from '@/constants';\nimport { setOpenCreateDatabaseModal } from '@/pages/main/workspace/store/modal';\n\ninterface IProps {\n  relyOnParams: {\n    databaseType: DatabaseTypeCode;\n    dataSourceId: number;\n    databaseName?: string;\n  };\n  executedCallback?: () => void;\n}\n\nexport type CreateType = 'database' | 'schema';\n\nexport interface ICreateDatabase {\n  databaseName?: string;\n  schemaName?: string;\n  comment?: string;\n}\n\n// 创建database不支持注释的数据库\nconst noCommentDatabase = [DatabaseTypeCode.MYSQL];\n\nconst CreateDatabase = () => {\n  const [form] = Form.useForm<ICreateDatabase>();\n  const monacoEditorUuid = useMemo(() => uuid(), []);\n  const monacoEditorRef = React.useRef<IExportRefFunction>(null);\n  const [open, setOpen] = useState(false);\n  const [errorMessage, setErrorMessage] = useState<{ success: boolean; message: string; originalSql: string } | null>(\n    null,\n  );\n  const [confirmLoading, setConfirmLoading] = useState(false);\n  const [createType, setCreateType] = useState<CreateType>('database');\n  const [relyOnParams, setRelyOnParams] = useState<IProps['relyOnParams'] | null>(null);\n  const executedCallbackRef = React.useRef<IProps['executedCallback']>();\n\n  useEffect(() => {\n    if (!open) {\n      setErrorMessage(null);\n      form.resetFields();\n      monacoEditorRef.current?.setValue('', 'cover');\n    }\n  }, [open]);\n\n  const config = useMemo(() => {\n    return createType === 'database'\n      ? {\n          title: `${i18n('common.title.create')} Database`,\n          api: sqlService.getCreateDatabaseSql,\n          formName: 'databaseName',\n        }\n      : {\n          title: `${i18n('common.title.create')} Schema`,\n          api: sqlService.getCreateSchemaSql,\n          formName: 'schemaName',\n        };\n  }, [createType]);\n\n  const labelCol = { flex: '70px' };\n\n  const handleFieldsChange = useCallback(\n    debounce(() => {\n      const formData: ICreateDatabase = form.getFieldsValue();\n      if (!formData.databaseName && createType === 'database') {\n        return;\n      }\n      if (!formData.schemaName && createType === 'schema') {\n        return;\n      }\n      const params = {\n        databaseType: relyOnParams?.databaseType,\n        dataSourceId: relyOnParams?.dataSourceId,\n        databaseName: relyOnParams?.databaseName,\n        ...formData,\n      };\n      config.api(params as any).then((res) => {\n        const { sql } = res;\n        monacoEditorRef.current?.setValue(sql, 'cover');\n      });\n    }, 500),\n    [relyOnParams, createType, monacoEditorRef, config],\n  );\n\n  const executeUpdateDataSql = (sql: string) => {\n    const params: any = {\n      dataSourceId: relyOnParams?.dataSourceId,\n      databaseType: relyOnParams?.databaseType,\n      databaseName: relyOnParams?.databaseName,\n      sql,\n    };\n    setConfirmLoading(true);\n    setErrorMessage(null);\n    return sqlService\n      .executeDDL(params)\n      .then((res) => {\n        if (res.success) {\n          setOpen(false);\n          executedCallbackRef.current?.();\n        } else {\n          setErrorMessage(res);\n        }\n      })\n      .finally(() => {\n        setConfirmLoading(false);\n      });\n  };\n\n  const onOk = () => {\n    const sql = monacoEditorRef.current?.getAllContent() || '';\n    executeUpdateDataSql(sql);\n  };\n\n  const openCreateDatabaseModal = (params: {\n    type: CreateType;\n    relyOnParams: {\n      databaseType: DatabaseTypeCode;\n      dataSourceId: number;\n      databaseName?: string;\n    };\n    executedCallback?: () => void;\n  }) => {\n    setOpen(true);\n    setCreateType(params.type);\n    setRelyOnParams(params.relyOnParams);\n    executedCallbackRef.current = params.executedCallback;\n  };\n\n  useEffect(() => {\n    setOpenCreateDatabaseModal(openCreateDatabaseModal);\n  }, []);\n\n  return (!!relyOnParams && (\n    <Modal\n      onCancel={() => {\n        setOpen(false);\n      }}\n      title={config.title}\n      destroyOnClose\n      confirmLoading={confirmLoading}\n      open={open}\n      onOk={onOk}\n    >\n      <div className={styles.createDatabaseDom}>\n        <Form labelAlign=\"left\" form={form} labelCol={labelCol} onFieldsChange={handleFieldsChange} name=\"create\">\n          <Form.Item label={i18n('common.label.name')} name={config.formName}>\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          {noCommentDatabase.includes(relyOnParams.databaseType) ? null : (\n            <Form.Item label={i18n('common.label.comment')} name=\"comment\">\n              <Input autoComplete=\"off\" />\n            </Form.Item>\n          )}\n        </Form>\n        <div className={styles.previewBox}>\n          <div className={styles.previewText}>{i18n('common.title.preview')}</div>\n          <div className={styles.previewLine} />\n        </div>\n        <div className={styles.monacoEditorBox}>\n          <MonacoEditor\n            ref={monacoEditorRef}\n            options={{\n              lineNumbers: 'off',\n            }}\n            id={monacoEditorUuid}\n          />\n        </div>\n        {errorMessage && (\n          <>\n            <div className={classnames(styles.previewBox, styles.errorBox)}>\n              <div className={styles.previewText}>{i18n('common.title.errorMessage')}</div>\n              <div className={styles.previewLine} />\n            </div>\n            <div>{errorMessage.message}</div>\n          </>\n        )}\n      </div>\n    </Modal>\n  ))\n\n};\n\nexport default CreateDatabase;\n"
  },
  {
    "path": "chat2db-client/src/components/CustomLayout/index.less",
    "content": "@import '../../styles/var.less';\n\n.customLayout {\n  display: flex;\n  align-items: center;\n  margin: 0px -3px;\n  .iconPanel {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    margin: 0px 3px;\n    border-radius: 3px;\n    height: 20px;\n    width: 20px;\n    cursor: pointer;\n    &:hover {\n      background-color: var(--color-hover-bg);  \n    }\n    i {\n      font-size: 15px;\n      color: var(--color-text-secondary);\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/CustomLayout/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport { togglePanelLeft, togglePanelRight } from '@/pages/main/workspace/store/config';\nimport Iconfont from '@/components/Iconfont';\n\ninterface IProps {\n  className?: string;\n}\n\nexport default memo<IProps>((props) => {\n  const { className } = props;\n  const { panelLeft, panelRight } = useWorkspaceStore((state) => {\n    return {\n      panelLeft: state.layout.panelLeft,\n      panelRight: state.layout.panelRight,\n    };\n  });\n\n  // 阻止事件冒泡\n  const stopPropagation = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {\n    e.stopPropagation();\n  };\n\n  return (\n    <div className={classnames(styles.customLayout, className)}>\n      <div className={classnames(styles.iconPanel)} onClick={togglePanelLeft} onDoubleClick={stopPropagation}>\n        {panelLeft ? <Iconfont code=\"&#xe674;\" /> : <Iconfont code=\"&#xe670;\" />}\n      </div>\n      <div className={classnames(styles.iconPanel)} onClick={togglePanelRight} onDoubleClick={stopPropagation}>\n        {panelRight ? <Iconfont code=\"&#xe672;\" /> : <Iconfont code=\"&#xe673;\" />}\n      </div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/CustomSelect/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n}\n"
  },
  {
    "path": "chat2db-client/src/components/CustomSelect/index.tsx",
    "content": "import React, { memo, useEffect, useState } from 'react';\nimport { Select } from 'antd';\n\ninterface IOption {\n  label: string;\n  value: string | number | null;\n}\n\ninterface IProps {\n  className?: string;\n  options: IOption[];\n  onChange?: any;\n  value?: any;\n}\n\nconst CustomSelect = memo<IProps>((props: IProps) => {\n  const { options, onChange, value } = props;\n  const [customOptions, setCustomOptions] = useState<IOption[]>([]);\n  const [customValue, setCustomValue] = useState<string>('');\n  const [curSearch, setCurSearch] = useState<string | null>(null);\n\n  useEffect(() => {\n    setCustomOptions([...options, { label: '', value: null }]);\n  }, [options]);\n\n  useEffect(() => {\n    setCustomValue(value);\n  }, [value]);\n\n  // 1. 如果自定义的节点为null就过滤掉自定义节点\n  // 2. 如果自定义节点的值和前面的节点的值相同就过滤掉自定义节点\n  const filtrationCustomOptions = (list: IOption[]) => {\n    const newList = [...list];\n    const lastItem = newList[newList.length - 1];\n    newList.forEach((item, index) => {\n      if ((lastItem.value === item.value && index !== list.length - 1) || !item.value) {\n        newList.pop();\n      }\n    });\n    return newList;\n  };\n\n  const onSearch = (v: string) => {\n    setCurSearch(v);\n  };\n  const customChange = (v: string) => {\n    setCurSearch(null);\n    setCustomValue(v);\n    onChange?.(v);\n  };\n  const onBlur = () => {\n    if (curSearch) {\n      onChange?.(curSearch);\n    }\n    setCurSearch(null);\n  };\n\n  return (\n    <Select\n      onBlur={onBlur}\n      allowClear\n      onChange={customChange}\n      value={customValue}\n      showSearch\n      onSearch={onSearch}\n      options={filtrationCustomOptions(customOptions)}\n      notFoundContent={false}\n    />\n  );\n});\n\nexport default CustomSelect;\n"
  },
  {
    "path": "chat2db-client/src/components/DraggableContainer/index.less",
    "content": "// @import '../../var.less';\n\n.box {\n  display: flex;\n  .divider {\n    width: 0px;\n    position: relative;\n  }\n\n  .dividerCenter {\n    position: absolute;\n    width: 6px;\n    top: 0px;\n    bottom: 0px;\n    z-index: 100;\n    transform: translateX(-50%);\n    &:hover {\n      background-color: var(--color-primary-bg);\n      cursor: col-resize;\n    }\n  }\n  .dragging {\n  }\n}\n\n.box_column {\n  height: 100%;\n  flex-direction: column;\n  .divider {\n    height: 0px;\n    width: 100%;\n    position: relative;\n  }\n\n  .dividerCenter {\n    height: 6px;\n    width: auto;\n    top: 0px;\n    right: 0px;\n    left: 0;\n    z-index: 100;\n    transform: translateY(-50%);\n    &:hover {\n      cursor: row-resize;\n    }\n  }\n}\n\n.displayDivider {\n  display: none;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/DraggableContainer/index.tsx",
    "content": "import React, { memo, useRef, useEffect, useState } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\n\ninterface IProps {\n  className?: string;\n  children: any; //TODO: TS，约定接受一个数组\n  min?: number;\n  layout?: 'row' | 'column';\n  onResize?: (data: number) => void;\n  showLine?: boolean;\n}\n\nexport default memo<IProps>((props: IProps) => {\n  const { children, showLine = true, onResize, min, className, layout = 'row' } = props;\n  const volatileRef = children[0]?.ref || children[1]?.ref;\n\n  const dividerRef = useRef<HTMLDivElement | null>(null);\n  const dividerLine = useRef<HTMLDivElement | null>(null);\n  const [dragging, setDragging] = useState(false);\n\n  const isRow = layout === 'row';\n\n  useEffect(() => {\n    if (!dividerRef.current) {\n      return;\n    }\n\n    dividerRef.current.onmousedown = (e) => {\n      if (!volatileRef?.current) return;\n      e.preventDefault();\n      setDragging(true);\n      const clientStart = isRow ? e.clientX : e.clientY;\n      const volatileBoxXY = isRow ? volatileRef.current.offsetWidth : volatileRef.current.offsetHeight;\n      document.onmousemove = (_e) => {\n        moveHandle(isRow ? _e.clientX : _e.clientY, volatileRef.current, clientStart, volatileBoxXY);\n      };\n      document.onmouseup = () => {\n        setDragging(false);\n        document.onmouseup = null;\n        document.onmousemove = null;\n      };\n    };\n  }, []);\n\n  const moveHandle = (nowClientXY: any, leftDom: any, clientStart: any, volatileBoxXY: any) => {\n    const computedXY = nowClientXY - clientStart;\n    let finalXY = 0;\n\n    // children 如果第一个是可变的，那么就是+ 如果第二个是可变的，那么就是-\n    finalXY = children[0]?.ref ? volatileBoxXY + computedXY : volatileBoxXY - computedXY;\n\n    if (min && finalXY < min) {\n      return;\n    }\n    if (isRow) {\n      leftDom.style.width = finalXY + 'px';\n    } else {\n      leftDom.style.height = finalXY + 'px';\n    }\n    onResize && onResize(finalXY);\n  };\n\n  return (\n    <div className={classnames(styles.box, { [styles.box_column]: !isRow }, className)}>\n      {children[0]}\n      {\n        <div\n          style={{ display: showLine ? 'block' : 'none' }}\n          ref={dividerLine}\n          className={classnames(styles.divider, { [styles.displayDivider]: !children[1] })}\n        >\n          <div ref={dividerRef} className={classnames(styles.dividerCenter, { [styles.dragging]: dragging })} />\n        </div>\n      }\n      {children[1]}\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/EditDialog/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n}\n"
  },
  {
    "path": "chat2db-client/src/components/EditDialog/index.tsx",
    "content": "import React, { memo, useEffect, useRef, useState, ForwardedRef } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport MonacoEditor, { IExportRefFunction, IRangeType } from '@/components/MonacoEditor';\nimport { Modal } from 'antd'\n\ninterface IProps {\n  className?: string;\n  verifyDialog: boolean;\n  title: string;\n  value: {\n    text: string;\n    range: IRangeType;\n  }\n}\n\nexport default memo<IProps>(function EditDialog(props) {\n  const { className, verifyDialog, value, title } = props;\n  const [open, setOpen] = useState<boolean>();\n  const monacoEditorRef = useRef<any>();\n\n  useEffect(() => {\n    setOpen(verifyDialog)\n  }, [verifyDialog])\n\n\n  useEffect(() => {\n    if (monacoEditorRef.current) {\n      monacoEditorRef.current?.setValue(value.text, value.range)\n    }\n  }, [value])\n\n  return <div className={classnames(styles.box, className)}>\n    <Modal\n      title={title}\n      open={open}\n      width={800}\n    // onCancel={(() => { setVerifyDialog(false) })}\n    >\n      <MonacoEditor id='edit-dialog' ref={monacoEditorRef}></MonacoEditor>\n    </Modal>\n  </div>\n})\n"
  },
  {
    "path": "chat2db-client/src/components/ExecuteSQL/index.less",
    "content": "@import '../../styles/var.less';\n\n.executeSQL {\n  .monacoEditorModal {\n    display: flex;\n    height: 400px;\n    border: 1px solid var(--color-border);\n    border-radius: 4px;\n\n    .monacoEditorHeader {\n      height: 38px;\n      padding: 0px 10px;\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      border-bottom: 1px solid var(--color-border);\n      .formatButton {\n        border-radius: 3px;\n        background-color: var(--color-fill-quaternary);\n        height: 24px;\n        padding: 0px 7px;\n        cursor: pointer;\n        i {\n          margin-right: 6px;\n        }\n        &:hover {\n          color: var(--color-primary);\n        }\n      }\n      .executeButton {\n        height: 24px;\n        line-height: 24px;\n        display: flex;\n        justify-content: center;\n        align-items: center;\n        i {\n          margin-right: 4px;\n        }\n      }\n    }\n    .monacoEditorContent {\n      width: 0px;\n      flex: 1;\n      display: flex;\n      flex-direction: column;\n      padding: 0px -6px;\n      border-right: 1px solid var(--color-border);\n      .monacoEditor {\n        flex: 1;\n        margin: 0px 6px;\n      }\n    }\n    .result {\n      width: 0px;\n      flex: 1;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      .resultHeader {\n        width: 100%;\n        line-height: 38px;\n        text-align: center;\n        border-bottom: 1px solid var(--color-border);\n      }\n      .resultContent {\n        flex: 1;\n        padding: 0px 10px;\n        overflow-y: auto;\n        .errorTitle {\n          display: flex;\n          align-items: center;\n          padding: 4px 10px;\n          // background-color: var(--color-bg-subtle);\n          border-radius: 8px;\n          margin-top: 10px;\n          i {\n            color: var(--color-error);\n            margin-right: 4px;\n          }\n        }\n\n        .errorMessage {\n          margin: 10px 0px;\n          padding: 4px 10px;\n          background-color: var(--color-bg-subtle);\n          border-radius: 8px;\n          .f-doc-en-break();\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ExecuteSQL/index.tsx",
    "content": "import React, { memo, useRef, useState, useMemo, useEffect } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport Iconfont from '@/components/Iconfont';\nimport MonacoEditor, { IExportRefFunction } from '@/components/MonacoEditor';\nimport i18n from '@/i18n';\nimport { Button } from 'antd';\nimport { formatSql } from '@/utils/sql';\nimport sqlService, { IExecuteSqlParams } from '@/service/sql';\nimport { DatabaseTypeCode } from '@/constants';\n\ninterface IProps {\n  className?: string;\n  initSql: string | null;\n  initError?: string | null;\n  databaseType: DatabaseTypeCode;\n  databaseName: string;\n  dataSourceId: number;\n  schemaName?: string | null;\n  tableName?: string;\n  executeSuccessCallBack: () => void;\n  executeSqlApi?: 'executeUpdateDataSql'; // 两个地方用到了这个组件，但是两个需要的执行sql的接口不一样\n}\n\nexport default memo<IProps>((props) => {\n  const {\n    className,\n    initSql = null,\n    initError = null,\n    databaseType,\n    databaseName,\n    dataSourceId,\n    schemaName,\n    tableName,\n    executeSqlApi = 'executeDDL',\n    executeSuccessCallBack,\n  } = props;\n  const monacoEditorRef = useRef<IExportRefFunction>(null);\n  const [executeLoading, setExecuteLoading] = useState<boolean>(false);\n  const [appendValue, setAppendValue] = useState<string>('');\n  const [executeSqlResult, setExecuteSqlResult] = useState<string | null>(null);\n\n  useEffect(() => {\n    setAppendValue(initSql || '');\n    setExecuteSqlResult(initError);\n  }, []);\n\n  const handleFormatSql = () => {\n    const sql = monacoEditorRef.current?.getAllContent() || '';\n    formatSql(sql, databaseType).then((res) => {\n      setAppendValue(res);\n    });\n  };\n\n  const executeSql = () => {\n    const executeSQLParams: IExecuteSqlParams = {\n      sql: monacoEditorRef.current?.getAllContent() || '',\n      dataSourceId,\n      databaseName,\n      schemaName,\n      tableName,\n    };\n    setExecuteLoading(true);\n    sqlService[executeSqlApi](executeSQLParams)\n      .then((res) => {\n        if (res.success) {\n          executeSuccessCallBack?.();\n        } else {\n          setExecuteSqlResult(res.message);\n        }\n      })\n      .finally(() => {\n        setExecuteLoading(false);\n      });\n  };\n\n  const renderMonacoEditor = useMemo(() => {\n    return (\n      <MonacoEditor\n        className={styles.monacoEditor}\n        id=\"view_sql\"\n        ref={monacoEditorRef}\n        appendValue={{\n          text: appendValue,\n          range: 'reset',\n        }}\n      />\n    );\n  }, [appendValue]);\n\n  return (\n    <div className={classnames(styles.executeSQL, className)}>\n      <div className={styles.monacoEditorModal}>\n        <div className={styles.monacoEditorContent}>\n          <div className={styles.monacoEditorHeader}>\n            <div className={styles.formatButton} onClick={handleFormatSql}>\n              <Iconfont code=\"&#xe64f;\" />\n              {i18n('common.button.format')}\n            </div>\n            <Button className={styles.executeButton} type=\"primary\" onClick={executeSql} loading={executeLoading}>\n              <Iconfont code=\"&#xe656;\" />\n              {i18n('common.button.execute')}\n            </Button>\n          </div>\n          {renderMonacoEditor}\n        </div>\n        {executeSqlResult && (\n          <div className={styles.result}>\n            <div className={styles.resultHeader}>{i18n('common.text.errorMessage')}</div>\n            <div className={styles.resultContent}>\n              <div className={styles.errorMessage}>{executeSqlResult}</div>\n            </div>\n          </div>\n        )}\n      </div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/Iconfont/index.less",
    "content": "@font-face {\n  font-family: 'iconfont'; /* Project id 3633546 */\n  src: url('../../assets/font/iconfont.woff2') format('woff2'), url('../../assets/font/iconfont.woff') format('woff'),\n    url('../../assets/font/iconfont.ttf') format('truetype');\n}\n\n.iconBox {\n  height: var(--icon-box-size);\n  width: var(--icon-box-size);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border-radius: 4px;\n  cursor: pointer;\n  &:hover {\n    background-color: var(--color-hover-bg);\n    .iconfont {\n      color: var(--color-primary);\n    }\n  }\n}\n\n.activeIconBox {\n  background-color: var(--color-hover-bg);\n  .iconfont {\n    color: var(--color-primary);\n  }\n}\n\n.iconfont {\n  font-family: 'iconfont' !important;\n  font-size: var(--icon-size);\n  font-style: normal;\n  user-select: none;\n  -webkit-font-smoothing: antialiased;\n  -webkit-text-stroke-width: 0.2px;\n  -moz-osx-font-smoothing: grayscale;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/Iconfont/index.tsx",
    "content": "import React from 'react';\nimport classnames from 'classnames';\n// import desktopStyle from './desktop.less';\nimport styles from './index.less';\n\n// 只有本地开发时使用cdn，发布线上时要下载iconfont到 /assets/font\nif (__ENV__ === 'local') {\n  const container = `\n  /* 在线链接服务仅供平台体验和调试使用，平台不承诺服务的稳定性，企业客户需下载字体包自行发布使用并做好备份。 */\n  @font-face {\n    font-family: 'iconfont';  /* Project id 3633546 */\n    src: url('//at.alicdn.com/t/c/font_3633546_n88tvocxfkj.woff2?t=1704794450779') format('woff2'),\n         url('//at.alicdn.com/t/c/font_3633546_n88tvocxfkj.woff?t=1704794450779') format('woff'),\n         url('//at.alicdn.com/t/c/font_3633546_n88tvocxfkj.ttf?t=1704794450779') format('truetype');\n  }\n  `;\n  const style = document.createElement('style');\n  style.type = 'text/css';\n  document.head.appendChild(style);\n  style.appendChild(document.createTextNode(container));\n}\n\ninterface IProps extends React.HTMLAttributes<HTMLElement> {\n  code: string;\n  box?: boolean;\n  boxSize?: number;\n  size?: number;\n  className?: string;\n  classNameBox?: string;\n  active?: boolean;\n}\n\nconst Iconfont = (props: IProps) => {\n  // console.log(active);\n  const { box, boxSize = 32, size = 14, className, classNameBox, active, ...args } = props;\n  return box ? (\n    <div\n      {...args}\n      style={\n        {\n          '--icon-box-size': `${boxSize}px`,\n          '--icon-size': `${size}px`,\n        } as any\n      }\n      className={classnames(classNameBox, styles.iconBox, { [styles.activeIconBox]: active })}\n    >\n      <i className={classnames(className, styles.iconfont)}>{props.code}</i>\n    </div>\n  ) : (\n    <i\n      style={\n        {\n          '--icon-size': `${size}px`,\n        } as any\n      }\n      className={classnames(className, styles.iconfont)}\n      {...args}\n    >\n      {props.code}\n    </i>\n  );\n};\n\nexport default Iconfont;\n"
  },
  {
    "path": "chat2db-client/src/components/ImportBlock/index.tsx",
    "content": "import React from 'react';\nimport { Modal, Upload, Button } from 'antd';\nimport { UploadOutlined } from '@ant-design/icons';\nimport i18n from '@/i18n';\n\ninterface IImportBlockProps {\n  children: React.ReactNode;\n  title?: string;\n  accept: string;\n  maxCount?: number;\n  onConfirm?: (fileList: Array<File> | File) => Promise<boolean>;\n}\n\nfunction ImportBlock(props: IImportBlockProps) {\n  const { title, children, accept, maxCount = 1 } = props;\n\n  const [open, setOpen] = React.useState(false);\n  const [selectedFile, setSelectedFile] = React.useState<Array<File> | null>(null);\n\n  const onClose = () => {\n    setOpen(false);\n  };\n\n  const onOk = async () => {\n    if (!selectedFile) return;\n\n    if (props.onConfirm) {\n      const success = await props.onConfirm(maxCount === 1 ? selectedFile?.[0] : selectedFile);\n      if (success) {\n        setOpen(false);\n      }\n    }\n  };\n\n  const handleBeforeUpload = (file: File, FileList: File[]) => {\n    setSelectedFile(FileList);\n    return false; // 停止自动上传\n  };\n\n  return (\n    <div>\n      <div onClick={() => setOpen(true)}>{children}</div>\n\n      <Modal title={title} open={open} onCancel={onClose} onOk={onOk}>\n        <Upload\n          name=\"file\"\n          accept={accept}\n          beforeUpload={handleBeforeUpload}\n          maxCount={maxCount}\n          fileList={selectedFile ? [...selectedFile] : null}\n        >\n          <Button icon={<UploadOutlined />}>{i18n('common.text.selectFile')}</Button>\n        </Upload>\n      </Modal>\n    </div>\n  );\n}\n\nexport default ImportBlock;\n"
  },
  {
    "path": "chat2db-client/src/components/ImportConnection/index.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { Modal, Upload, Button, message } from 'antd';\nimport { UploadOutlined } from '@ant-design/icons';\nimport i18n from '@/i18n';\n\nconst uploadFileType = {\n  ncx: {\n    accept: '.ncx',\n    uploadUrl: window._BaseURL + '/api/converter/ncx/upload',\n  },\n  dbp: {\n    accept: '.dbp',\n    uploadUrl: window._BaseURL + '/api/converter/dbp/upload',\n  },\n};\n\ninterface IImportConnectionProps {\n  open: boolean;\n  onClose: () => void;\n  onConfirm?: () => void;\n}\n\nconst ImportConnection: React.FC<IImportConnectionProps> = ({ open, onClose, onConfirm }) => {\n  const [selectedFile, setSelectedFile] = useState<File | null>(null);\n  const [uploading, setUploading] = useState(false);\n\n  useEffect(() => {\n    if (open) {\n      setSelectedFile(null);\n    }\n  }, [open]);\n\n  const handleBeforeUpload = (file: File) => {\n    setSelectedFile(file);\n    return false; // 停止自动上传\n  };\n\n  const handleConfirmUpload = async () => {\n    if (!selectedFile) return;\n\n    const formData = new FormData();\n    formData.append('file', selectedFile);\n\n    const fileExtension = selectedFile.name.split('.').pop() || '';\n\n    const { uploadUrl } = uploadFileType[fileExtension] || {};\n\n    try {\n      setUploading(true);\n\n      const response = await fetch(uploadUrl, {\n        method: 'POST',\n        body: formData,\n      });\n      if (response.ok) {\n        message.success(`${selectedFile.name} 导入数据源成功`);\n        onConfirm && onConfirm();\n      } else {\n        message.error(`${selectedFile.name} 导入数据源失败`);\n      }\n    } catch (error) {\n      message.error(`Error: ${error}`);\n    } finally {\n      setUploading(false);\n    }\n  };\n\n  return (\n    <Modal\n      title={i18n('connection.title.importTitle')}\n      open={open}\n      onCancel={onClose}\n      onOk={handleConfirmUpload}\n      confirmLoading={uploading}\n    >\n      <Upload\n        name=\"file\"\n        accept=\".ncx,.dbp\"\n        beforeUpload={handleBeforeUpload}\n        maxCount={1}\n        fileList={selectedFile ? [selectedFile] : []}\n      >\n        <Button icon={<UploadOutlined />}>{i18n('common.text.selectFile')}</Button>\n      </Upload>\n    </Modal>\n  );\n};\n\nexport default ImportConnection;\n"
  },
  {
    "path": "chat2db-client/src/components/LayoutBasic/index.less",
    "content": ".layoutBasic{\n  \n}"
  },
  {
    "path": "chat2db-client/src/components/LayoutBasic/index.tsx",
    "content": "import React from 'react';\nimport style from './index.less'\n\ninterface IProps{\n  className: string;\n}\n\nfunction LayoutBasic(props: IProps) {\n  return <div className={style.layoutBasic}>\n    <div></div>\n  </div>;\n}\n\nexport default LayoutBasic;\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/LazyLoading/index.less",
    "content": "@import '../../../styles/var.less';\n.box {\n  .f-fill-absolute();\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: var(--color-bg-100);\n}\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/LazyLoading/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport Loading from '@/components/Loading/Loading'\n\ninterface IProps {\n  className?: string;\n}\n\nexport default memo<IProps>(function LazyLoading({ className }) {\n  return <div className={classnames(className, styles.box)}>\n    <Loading></Loading>\n  </div>\n})\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/Loading/index.less",
    "content": ".loading-components-box{\n  display: flex;\n  justify-content: center;\n  align-items: center;\n\n  .load-container {\n    margin: 20px auto;\n    width: 30px;\n    height: 30px;\n    position: relative;\n  }\n  .load-container .container {\n    position: absolute;\n    width: 100%;\n    height: 100%;\n  }\n  .load-container .container .dot {\n    width: 8px;\n    height: 8px;\n    background-color: var(--primary-color);\n    border-radius: 100%;\n    position: absolute;\n    -webkit-animation: bouncedelay 1.2s infinite ease-in-out;\n    animation: bouncedelay 1.2s infinite ease-in-out;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n  }\n  .load-container .container .dot-1 {\n    top: 0;\n    left: 0;\n  }\n  .load-container .container .dot-2 {\n    top: 0;\n    right: 0;\n  }\n  .load-container .container .dot-3 {\n    right: 0;\n    bottom: 0;\n  }\n  .load-container .container .dot-4 {\n    left: 0;\n    bottom: 0;\n  }\n  .load-container .container-1 .dot-2 {\n    -webkit-animation-delay: -0.9s;\n    animation-delay: -0.9s;\n  }\n  .load-container .container-1 .dot-3 {\n    -webkit-animation-delay: -0.6s;\n    animation-delay: -0.6s;\n  }\n  .load-container .container-1 .dot-4 {\n    -webkit-animation-delay: -0.3s;\n    animation-delay: -0.3s;\n  }\n  .load-container .container-2 {\n    -webkit-transform: rotateZ(45deg);\n    transform: rotateZ(45deg);\n  }\n  .load-container .container-2 .dot-1 {\n    -webkit-animation-delay: -1.1s;\n    animation-delay: -1.1s;\n  }\n  .load-container .container-2 .dot-2 {\n    -webkit-animation-delay: -0.8s;\n    animation-delay: -0.8s;\n  }\n  .load-container .container-2 .dot-3 {\n    -webkit-animation-delay: -0.5s;\n    animation-delay: -0.5s;\n  }\n  .load-container .container-2 .dot-4 {\n    -webkit-animation-delay: -0.2s;\n    animation-delay: -0.2s;\n  }\n  .load-container .container-3 {\n    -webkit-transform: rotateZ(90deg);\n    transform: rotateZ(90deg);\n  }\n  .load-container .container-3 .dot-1 {\n    -webkit-animation-delay: -1s;\n    animation-delay: -1s;\n  }\n  .load-container .container-3 .dot-2 {\n    -webkit-animation-delay: -0.7s;\n    animation-delay: -0.7s;\n  }\n  .load-container .container-3 .dot-3 {\n    -webkit-animation-delay: -0.4s;\n    animation-delay: -0.4s;\n  }\n  .load-container .container-3 .dot-4 {\n    -webkit-animation-delay: -0.1s;\n    animation-delay: -0.1s;\n  }\n  \n  @-webkit-keyframes bouncedelay {\n    0%,\n    80%,\n    100% {\n      -webkit-transform: scale(0);\n    }\n    40% {\n      -webkit-transform: scale(1);\n    }\n  }\n  @keyframes bouncedelay {\n    0%,\n    80%,\n    100% {\n      transform: scale(0);\n      -webkit-transform: scale(0);\n    }\n    40% {\n      transform: scale(1);\n      -webkit-transform: scale(1);\n    }\n  }\n}\n\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/Loading/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport './index.less'\n\ninterface IProps {\n  className?: any;\n}\n\n// TODO： 首屏以及懒加载Loading效果\nexport default memo(function PageLoading(props: IProps) {\n  const { className } = props;\n  return <div className={classnames('loading-components-box', className)}>\n    <div className=\"load-container\">\n      <div className=\"container container-1\">\n        <div className=\"dot dot-1\"></div>\n        <div className=\"dot dot-2\"></div>\n        <div className=\"dot dot-3\"></div>\n        <div className=\"dot dot-4\"></div>\n      </div>\n      <div className=\"container container-2\">\n        <div className=\"dot dot-1\"></div>\n        <div className=\"dot dot-2\"></div>\n        <div className=\"dot dot-3\"></div>\n        <div className=\"dot dot-4\"></div>\n      </div>\n      <div className=\"container container-3\">\n        <div className=\"dot dot-1\"></div>\n        <div className=\"dot dot-2\"></div>\n        <div className=\"dot dot-3\"></div>\n        <div className=\"dot dot-4\"></div>\n      </div>\n    </div>\n  </div>\n\n});\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/LoadingContent/index.less",
    "content": ".loadingContent {\n  position: relative;\n}\n\n.stateIndicator {\n  width: 200px;\n  height: 200px;\n}\n\n.empty {\n  height: 100%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  img {\n    max-width: 100%;\n  }\n}\n\n.coverLoading {\n  position: absolute;\n  inset: 0;\n  background-color: rgba(0, 0, 0, 0.01);\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  z-index: 999;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/LoadingContent/index.tsx",
    "content": "import React from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport StateIndicator from '@/components/StateIndicator';\n\n// IProps继承div的原生属性\ninterface IProps<T> extends React.HTMLAttributes<HTMLDivElement> {\n  className?: string;\n  data?: T | null | undefined | true;\n  empty?: React.ReactNode;\n  handleEmpty?: boolean;\n  isLoading?: boolean;\n  coverLoading?: boolean;\n}\n\nexport default function LoadingContent<T>(props: IProps<T>) {\n  const { children, className, data = true, handleEmpty = false, empty, isLoading, coverLoading, ...args } = props;\n  const isEmpty = !isLoading && handleEmpty && !(data as any)?.length;\n\n  const renderContent = () => {\n    if ((isLoading || !data) && !coverLoading) {\n      return <StateIndicator state=\"loading\" />;\n    }\n\n    if (isEmpty) {\n      return empty || <StateIndicator state=\"empty\" />;\n    }\n\n    return (\n      <>\n        {children}\n        {(isLoading || !data) && coverLoading && (\n          <div className={styles.coverLoading}>\n            <StateIndicator state=\"loading\" />\n          </div>\n        )}\n      </>\n    );\n  };\n\n  return <div className={classnames(styles.loadingContent, className)}>{renderContent()}</div>;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/LoadingGracile/index.less",
    "content": "@import '../../../styles/var.less';\n\n.spinner {\n  font-size: 14px;\n  position: relative;\n  display: inline-block;\n  width: 1em;\n  height: 1em;\n}\n\n.spinner.center {\n  \n}\n\n.spinner .spinnerBlade {\n  position: absolute;\n  left: 0.4629em;\n  bottom: 0;\n  width: 0.074em;\n  height: 0.2777em;\n  border-radius: 0.0555em;\n  background-color: transparent;\n  -webkit-transform-origin: center -0.2222em;\n  -ms-transform-origin: center -0.2222em;\n  transform-origin: center -0.2222em;\n  animation: spinner-fade9234 1s infinite linear;\n}\n\n.spinner .spinnerBlade:nth-child(1) {\n  -webkit-animation-delay: 0s;\n  animation-delay: 0s;\n  -webkit-transform: rotate(0deg);\n  -ms-transform: rotate(0deg);\n  transform: rotate(0deg);\n}\n\n.spinner .spinnerBlade:nth-child(2) {\n  -webkit-animation-delay: 0.083s;\n  animation-delay: 0.083s;\n  -webkit-transform: rotate(30deg);\n  -ms-transform: rotate(30deg);\n  transform: rotate(30deg);\n}\n\n.spinner .spinnerBlade:nth-child(3) {\n  -webkit-animation-delay: 0.166s;\n  animation-delay: 0.166s;\n  -webkit-transform: rotate(60deg);\n  -ms-transform: rotate(60deg);\n  transform: rotate(60deg);\n}\n\n.spinner .spinnerBlade:nth-child(4) {\n  -webkit-animation-delay: 0.249s;\n  animation-delay: 0.249s;\n  -webkit-transform: rotate(90deg);\n  -ms-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n\n.spinner .spinnerBlade:nth-child(5) {\n  -webkit-animation-delay: 0.332s;\n  animation-delay: 0.332s;\n  -webkit-transform: rotate(120deg);\n  -ms-transform: rotate(120deg);\n  transform: rotate(120deg);\n}\n\n.spinner .spinnerBlade:nth-child(6) {\n  -webkit-animation-delay: 0.415s;\n  animation-delay: 0.415s;\n  -webkit-transform: rotate(150deg);\n  -ms-transform: rotate(150deg);\n  transform: rotate(150deg);\n}\n\n.spinner .spinnerBlade:nth-child(7) {\n  -webkit-animation-delay: 0.498s;\n  animation-delay: 0.498s;\n  -webkit-transform: rotate(180deg);\n  -ms-transform: rotate(180deg);\n  transform: rotate(180deg);\n}\n\n.spinner .spinnerBlade:nth-child(8) {\n  -webkit-animation-delay: 0.581s;\n  animation-delay: 0.581s;\n  -webkit-transform: rotate(210deg);\n  -ms-transform: rotate(210deg);\n  transform: rotate(210deg);\n}\n\n.spinner .spinnerBlade:nth-child(9) {\n  -webkit-animation-delay: 0.664s;\n  animation-delay: 0.664s;\n  -webkit-transform: rotate(240deg);\n  -ms-transform: rotate(240deg);\n  transform: rotate(240deg);\n}\n\n.spinner .spinnerBlade:nth-child(10) {\n  -webkit-animation-delay: 0.747s;\n  animation-delay: 0.747s;\n  -webkit-transform: rotate(270deg);\n  -ms-transform: rotate(270deg);\n  transform: rotate(270deg);\n}\n\n.spinner .spinnerBlade:nth-child(11) {\n  -webkit-animation-delay: 0.83s;\n  animation-delay: 0.83s;\n  -webkit-transform: rotate(300deg);\n  -ms-transform: rotate(300deg);\n  transform: rotate(300deg);\n}\n\n.spinner .spinnerBlade:nth-child(12) {\n  -webkit-animation-delay: 0.913s;\n  animation-delay: 0.913s;\n  -webkit-transform: rotate(330deg);\n  -ms-transform: rotate(330deg);\n  transform: rotate(330deg);\n}\n\n@keyframes spinner-fade9234 {\n  0% {\n    background-color: var(--color-text);\n  }\n\n  100% {\n    background-color: transparent;\n  }\n}"
  },
  {
    "path": "chat2db-client/src/components/Loading/LoadingGracile/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport './index.less'\n\ninterface IProps {\n  className?: any;\n}\n\nexport default memo(function LoadingGracile(props: IProps) {\n  const { className } = props;\n  return <div className={classnames(styles.spinner, styles.center)}>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n    <div className={styles.spinnerBlade}></div>\n  </div>\n\n});\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/LoadingLiquid/index.less",
    "content": ".box {\n  svg {\n    width: 0;\n    height: 0;\n  }\n}\n\n.loading {\n  width: 200px;\n  height: 200px;\n  position: relative;\n  filter: url(#gooey);\n}\n\n.loading span {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  display: block;\n  animation: loading 4s ease-in-out infinite;\n  /* var函数用来插入css变量的值，css变量名称以--开头 */\n  animation-delay: calc(0.2s * var(--i));\n}\n\n.loading span::before {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: calc(50% - 20px);\n  width: 40px;\n  height: 40px;\n  background: linear-gradient(#fce4ec, #03a9f4);\n  border-radius: 50%;\n  box-shadow: 0 0 30px #03a9f4;\n}\n\n@keyframes loading {\n  0% {\n    transform: rotate(0deg);\n  }\n\n  50%,\n  100% {\n    transform: rotate(360deg);\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/Loading/LoadingLiquid/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport './index.less'\n\ninterface IProps {\n  className?: any;\n}\n\nexport default memo(function LoadingLiquid(props: IProps) {\n  const { className } = props;\n  return <div className={styles.box}>\n    <div className={styles.loading}>\n      <span style={{ '--i': 1 } as any} ></span>\n      <span style={{ '--i': 2 } as any} ></span>\n      <span style={{ '--i': 3 } as any} ></span>\n      <span style={{ '--i': 4 } as any} ></span>\n      <span style={{ '--i': 5 } as any} ></span>\n      <span style={{ '--i': 6 } as any} ></span>\n      <span style={{ '--i': 7 } as any} ></span>\n    </div>\n    <svg>\n      <filter id=\"gooey\">\n        <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"10\" />\n        <feColorMatrix values=\"\n            1 0 0 0 0 \n            0 1 0 0 0\n            0 0 1 0 0 \n            0 0 0 20 -10\n            \" />\n      </filter>\n    </svg>\n  </div>\n\n});\n"
  },
  {
    "path": "chat2db-client/src/components/MenuLabel/index.less",
    "content": "@import '../../styles/var.less';\n\n.menuLabel {\n  display: flex;\n  align-items: center;\n  .menuLabelIconBox{\n    width: 22px;\n    display: flex;\n    align-items: center;\n  }\n  // .menuLabelIcon {\n  // }\n  // .menuLabelTitle {\n  // }\n  .menuLabelIconBright{\n    color: var(--color-primary);\n  }\n  :global {\n    .ant-dropdown {\n      z-index: 1080;\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MenuLabel/index.tsx",
    "content": "import React, { memo } from 'react';\nimport classnames from 'classnames';\nimport Iconfont from '@/components/Iconfont';\nimport styles from './index.less';\n\ninterface IProps {\n  className?: string;\n  icon?: string;\n  iconBright?: boolean;\n  label: string;\n}\n\nexport default memo<IProps>((props) => {\n  const { className, icon, label, iconBright } = props;\n  return (\n    <div className={classnames(styles.menuLabel, className)}>\n      <div className={styles.menuLabelIconBox}>\n        {icon && (\n          <Iconfont\n            className={classnames(styles.menuLabelIcon, { [styles.menuLabelIconBright]: iconBright })}\n            code={icon}\n          />\n        )}\n      </div>\n      <div className={styles.menuLabelTitle}>{label}</div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/Modal/BaseModal/index.tsx",
    "content": "import React, { memo, useEffect, useMemo, useState } from 'react';\nimport { Modal as AntdModal } from 'antd';\nimport { injectOpenModal } from '@/store/common/components';\n\nexport type IModalData =\n  | {\n      title?: string;\n      width?: string;\n      onOk?: () => void;\n      footer?: React.ReactNode | false;\n      content: React.ReactNode | false;\n    }\n  | null\n  | false;\n\nconst Modal = memo(() => {\n  const [open, setOpen] = useState(false);\n  const [modalData, setModalData] = useState<IModalData>(null);\n\n  const openModal = (params: IModalData) => {\n    if (params === false) {\n      setOpen(false);\n    } else {\n      setOpen(true);\n      setModalData(params);\n    }\n  };\n\n  useEffect(() => {\n    injectOpenModal(openModal);\n  }, []);\n\n  const footer = useMemo(() => {\n    if (modalData && modalData.footer) {\n      return {\n        footer: modalData.footer,\n        onOk: modalData.onOk,\n      };\n    } else {\n      return {\n        footer: false\n      };\n    }\n  }, [modalData]);\n\n  return (\n    !!modalData && (\n      <AntdModal\n        title={modalData.title}\n        open={open}\n        width={modalData.width}\n        onCancel={() => {\n          setOpen(false);\n        }}\n        destroyOnClose={true}\n        {...footer}\n      >\n        {modalData.content}\n      </AntdModal>\n    )\n  );\n});\n\nexport default Modal;\n"
  },
  {
    "path": "chat2db-client/src/components/Modal/MonacoEditorModal/index.tsx",
    "content": "import React, { memo } from 'react';\nimport { Modal } from 'antd';\nimport MonacoEditor, { IExportRefFunction } from '@/components/MonacoEditor';\n\nconst TriggeredModal = memo<ITriggeredModal>(() => {\n  return (\n    <Modal\n      title={`${data.key}-DDL`}\n      open={monacoVerifyDialog}\n      width=\"650px\"\n      onCancel={() => {\n        setMonacoVerifyDialog(false);\n      }}\n      footer={false}\n    >\n      <div className={styles.monacoEditorBox}>\n        <MonacoEditor id=\"edit-dialog\" ref={monacoEditorRef} />\n      </div>\n    </Modal>\n  );\n});\n\nexport default TriggeredModal;\n"
  },
  {
    "path": "chat2db-client/src/components/Modal/TriggeredModal/index.less",
    "content": "@import '../../styles/var.less';\n"
  },
  {
    "path": "chat2db-client/src/components/Modal/TriggeredModal/index.tsx",
    "content": "import React, { memo, Fragment, ReactElement, cloneElement } from 'react';\nimport { Modal } from 'antd';\n\ninterface ITriggeredModal extends React.ComponentProps<typeof Modal> {\n  children: ReactElement;\n  modalContent: React.ReactNode;\n  onOk?: any;\n}\n\nconst TriggeredModal = memo<ITriggeredModal>((props) => {\n  const { children, modalContent, ...orgs } = props;\n  const [open, setOpen] = React.useState(false);\n  const onClose = () => {\n    setOpen(false);\n  };\n\n  const onOk = () => {\n    orgs?.onOk?.(setOpen);\n  };\n\n  return (\n    <Fragment>\n      {cloneElement(children, { onClick: () => setOpen(true) })}\n      <Modal {...orgs} open={open} onCancel={onClose} onOk={onOk}>\n        {modalContent}\n      </Modal>\n    </Fragment>\n  );\n});\n\nexport default TriggeredModal;\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/index.less",
    "content": ".editorContainer {\n  height: 100%;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/index.tsx",
    "content": "import React, { ForwardedRef, forwardRef, useEffect, useImperativeHandle, useRef } from 'react';\nimport cs from 'classnames';\nimport { useTheme } from '@/hooks';\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api';\nimport { DatabaseTypeCode, EditorThemeType } from '@/constants';\nimport { editorDefaultOptions } from './monacoEditorConfig';\nimport { IQuickInputService } from 'monaco-editor/esm/vs/platform/quickinput/common/quickInput';\n\nimport styles from './index.less';\n\nexport type IEditorIns = monaco.editor.IStandaloneCodeEditor;\nexport type IEditorOptions = monaco.editor.IStandaloneEditorConstructionOptions;\nexport type IEditorContentChangeEvent = monaco.editor.IModelContentChangedEvent;\n\nexport type IAppendValue = {\n  text: any;\n  range?: IRangeType;\n};\n\nconst databaseTypeList = Object.keys(DatabaseTypeCode).map((d) => ({\n  type: d,\n  id: d,\n  label: d,\n}));\n\ninterface IProps {\n  id: string;\n  language?: string;\n  className?: string;\n  options?: IEditorOptions;\n  needDestroy?: boolean;\n  addAction?: Array<{ id: string; label: string; action: (selectedText: string, ext?: string) => void }>;\n  defaultValue?: string;\n  appendValue?: IAppendValue;\n  didMount?: (editor: IEditorIns) => any;\n  shortcutKey?: (editor, monaco, isActive: boolean) => void;\n  focusChange?: (isActive: boolean) => void;\n}\n\nexport interface IExportRefFunction {\n  getCurrentSelectContent: () => string;\n  getAllContent: () => string;\n  setValue: (text: any, range?: IRangeType) => void;\n  // toFocus: () => void;\n}\n\nfunction MonacoEditor(props: IProps, ref: ForwardedRef<IExportRefFunction>) {\n  const {\n    id,\n    className,\n    language = 'sql',\n    didMount,\n    options,\n    defaultValue,\n    appendValue,\n    shortcutKey,\n  } = props;\n  const editorRef = useRef<IEditorIns>();\n  const quickInputCommand = useRef<any>();\n  const [appTheme] = useTheme();\n  const [isActive, setIsActive] = React.useState(false);\n\n  // init\n  useEffect(() => {\n    const editorIns = monaco.editor.create(document.getElementById(`monaco-editor-${id}`)!, {\n      ...editorDefaultOptions,\n      ...options,\n      value: defaultValue || '',\n      language,\n      theme: appTheme.backgroundColor,\n    });\n    editorRef.current = editorIns;\n    didMount && didMount(editorIns);\n\n    // Add a new command, for getting an accessor.\n    quickInputCommand.current = editorIns.addCommand(0, (accessor, func) => {\n      // a hacker way to get the input service\n      const quickInputService = accessor.get(IQuickInputService);\n      func(quickInputService);\n    });\n\n    monaco.editor.defineTheme(EditorThemeType.DashboardLightTheme, {\n      base: 'vs',\n      inherit: true,\n      rules: [{ background: '#15161a' }] as any,\n      colors: {\n        'editor.foreground': '#000000',\n        'editor.background': '#f8f9fa', //背景色\n      },\n    });\n\n    monaco.editor.defineTheme(EditorThemeType.DashboardBlackTheme, {\n      base: 'vs-dark',\n      inherit: true,\n      rules: [{ background: '#15161a' }] as any,\n      colors: {\n        'editor.foreground': '#ffffff',\n        'editor.background': '#131418', //背景色\n      },\n    });\n\n    createAction(editorIns);\n\n    return () => {\n      if (props.needDestroy) {\n        editorRef.current && editorRef.current.dispose();\n      }\n    };\n  }, []);\n\n  // 如果编辑器聚焦，就设置为true\n  useEffect(() => {\n    const focus = () => {\n      setIsActive(true);\n      props.focusChange && props.focusChange(true);\n    };\n    const blur = () => {\n      setIsActive(false);\n      props.focusChange && props.focusChange(false);\n    };\n    editorRef.current?.onDidFocusEditorText(focus);\n    editorRef.current?.onDidBlurEditorText(blur);\n    // 移除监听\n    // return () => {\n    //   editorRef.current?.removeEventListener('focus', focus);\n    //   editorRef.current?.removeEventListener('blur', blur);\n    // };\n  }, []);\n\n\n  useEffect(() => {\n    if (editorRef.current) {\n      // eg:\n      // editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyL, () => {\n      // });\n      shortcutKey?.(editorRef.current, monaco, isActive);\n    }\n  }, [editorRef.current, isActive]);\n\n  useEffect(() => {\n    // 监听浏览器窗口大小变化，重新渲染编辑器\n    const resize = () => {\n      editorRef.current?.layout();\n    };\n    window.addEventListener('resize', resize);\n    return () => {\n      window.removeEventListener('resize', resize);\n    };\n  }, []);\n\n  // 设置报表里面的编辑器的主题\n  useEffect(() => {\n    if (options?.theme) {\n      monaco.editor.setTheme(options.theme);\n    }\n  }, [options?.theme]);\n\n  useImperativeHandle(ref, () => ({\n    getCurrentSelectContent,\n    getAllContent,\n    setValue,\n    // toFocus,\n  }));\n\n  useEffect(() => {\n    if (appendValue) {\n      appendMonacoValue(editorRef.current, appendValue?.text, appendValue?.range);\n    }\n  }, [appendValue]);\n\n  const setValue = (text: any, range?: IRangeType) => {\n    appendMonacoValue(editorRef.current, text, range);\n  };\n\n  // const toFocus = () => {\n  //   editorRef.current?.focus();\n  // };\n\n  /**\n   * 获取当前选中的内容\n   * @returns\n   */\n  const getCurrentSelectContent = () => {\n    const selection = editorRef.current?.getSelection();\n    if (!selection || selection.isEmpty()) {\n      return '';\n    } else {\n      const selectedText = editorRef.current?.getModel()?.getValueInRange(selection);\n      return selectedText || '';\n    }\n  };\n\n  /** 获取文本所有内容 */\n  const getAllContent = () => {\n    const model = editorRef.current?.getModel();\n    const value = model?.getValue();\n    return value || '';\n  };\n\n  const createAction = (editor: IEditorIns) => {\n    // 用于控制切换该菜单键的显示\n    editor.createContextKey('shouldShowSqlRunnerAction', true);\n\n    if (!props.addAction || !props.addAction.length) {\n      return;\n    }\n\n    props.addAction.forEach((action) => {\n      const { id: _id, label, action: runFn } = action;\n      editor.addAction({\n        id: _id,\n        label,\n        // 控制该菜单键显示\n        precondition: 'shouldShowSqlRunnerAction',\n        // 该菜单键位置\n        contextMenuGroupId: 'navigation',\n        contextMenuOrder: 1.5,\n        // 点击该菜单键后运行\n        run: (ed: IEditorIns) => {\n          const selectedText = editor.getModel()?.getValueInRange(editor.getSelection()!) || '';\n          if (_id === 'changeSQL') {\n            ed.trigger('', quickInputCommand.current, (quickInput) => {\n              quickInput.pick(databaseTypeList).then((selected) => {\n                runFn(selectedText, selected?.label);\n              });\n            });\n          } else {\n            runFn(selectedText);\n          }\n        },\n      });\n    });\n  };\n\n  return <div ref={ref as any} id={`monaco-editor-${id}`} className={cs(className, styles.editorContainer)} />;\n}\n\n// text 需要添加的文本\n// range 添加到的位置\n// 'end' 末尾\n// 'front' 开头\n// 'cover' 覆盖掉原有的文字\n// 自定义位置数组 new monaco.Range []\nexport type IRangeType = 'end' | 'front' | 'cover' | 'reset' | any;\n\nexport const appendMonacoValue = (editor: any, text: any, range: IRangeType = 'end') => {\n  if (!editor) {\n    return;\n  }\n  const model = editor?.getModel && editor.getModel(editor);\n  // 创建编辑操作，将当前文档内容替换为新内容\n  let newRange: IRangeType = range;\n  if (range === 'reset') {\n    editor.setValue(text || '');\n    return;\n  }\n  let newText = text;\n  const lastLine = editor.getModel().getLineCount();\n  const lastLineLength = editor.getModel().getLineMaxColumn(lastLine);\n\n  switch (range) {\n    // 覆盖所有内容\n    case 'cover':\n      newRange = model.getFullModelRange();\n      editor.revealLine(lastLine);\n      break;\n    // 在开头添加内容\n    case 'front':\n      newRange = new monaco.Range(1, 1, 1, 1);\n      editor.revealLine(1);\n      editor.setPosition({ lineNumber: 1, column: 1 });\n      break;\n    // 格式化选中区域的sql\n    case 'select': {\n      const selection = editor.getSelection();\n      if (selection) {\n        newRange = new monaco.Range(\n          selection.startLineNumber,\n          selection.startColumn,\n          selection.endLineNumber,\n          selection.endColumn,\n        );\n      }\n      break;\n    }\n    // 在末尾添加内容\n    case 'end':\n      newRange = new monaco.Range(lastLine, lastLineLength, lastLine, lastLineLength);\n      newText = `${text}`;\n      break;\n    // 在光标处添加内容\n    case 'cursor':\n      {\n        const position = editor.getPosition();\n        if (position) {\n          newRange = new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column);\n        }\n      }\n      break;\n    default:\n      break;\n  }\n\n  const op = {\n    range: newRange,\n    text: newText,\n  };\n\n  // decorations?: IModelDeltaDecoration[]: 一个数组类型的参数，用于指定插入的文本的装饰。可以用来设置文本的样式、颜色、背景色等。如果不需要设置装饰，可以忽略此参数。\n  const decorations = [{}]; // 解决新增的文本默认背景色为灰色\n  editor.executeEdits('setValue', [op], decorations);\n  const addedLastLine = editor.getModel().getLineCount();\n  // const addedLastLineLength = editor.getModel().getLineMaxColumn(lastLine);\n\n  if (range === 'end') {\n    setTimeout(() => {\n      editor.revealLine(addedLastLine + 1);\n      // editor.setPosition({ lineNumber: addedLastLine, column: addedLastLineLength });\n      // editor.focus();\n    }, 0);\n  }\n};\n\nexport default forwardRef(MonacoEditor);\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/monacoEditorConfig.ts",
    "content": "import { IEditorOptions } from '@/components/MonacoEditor';\n\nexport const editorDefaultOptions: IEditorOptions = {\n  fontFamily: \n   `\"Menlo\", \n    \"DejaVu Sans Mono\", \n    \"Liberation Mono\", \n    \"Consolas\", \n    \"Ubuntu Mono\", \n    \"Courier New\", \n    \"andale mono\", \n    \"lucida console\", \n    \"monospace\"`,\n  scrollBeyondLastLine: false, // 滚动超过最后一行\n  automaticLayout: true, // 自动布局\n  dragAndDrop: false, // 拖拽\n  fontSize: 12, // 字体大小\n  tabSize: 2, // tab大小\n  lineHeight: 18, // 行高\n  theme: 'vscode', // 主题\n  roundedSelection: false, // 圆角选择\n  readOnly: false, // 只读\n  folding: false, // 不显示折叠\n  insertSpaces: true, // 插入空格\n  autoClosingQuotes: 'always', // 自动闭合引号\n  detectIndentation: false, // 检测缩进\n  wordWrap: 'on', // 自动换行\n  fixedOverflowWidgets: true, // 固定溢出小部件\n  // renderLineHighlight: 'none', // 渲染行高亮\n  codeLens: false, // 代码镜头\n  scrollbar: {\n    // 滚动条\n    alwaysConsumeMouseWheel: false, // 总是消耗鼠标滚轮\n  },\n  unicodeHighlight: {\n    ambiguousCharacters: false,\n    invisibleCharacters: false,\n  },\n  // padding: {\n  //   top: 2,\n  //   bottom: 2,\n  // },\n  minimap: {\n    // 缩略图\n    enabled: false, // 启用\n  },\n};\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/index.ts",
    "content": "export * from './parser';\nexport * from './lexer';\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/lexer/index.ts",
    "content": "import { IToken } from './token';\n\nexport { IToken };\n\ninterface ILexerConfig {\n  type: string;\n  regexes: RegExp[];\n  /**\n   * Will match, by not add to token list.\n   */\n  ignore?: boolean;\n}\n\nclass Tokenizer {\n  // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-parameter-properties\n  constructor(public lexerConfig: ILexerConfig[]) {\n    //\n  }\n\n  public tokenize(input: string) {\n    const tokens = [];\n    let token: IToken;\n    let lastPosition = 0;\n\n    // Keep processing the string until it is empty\n    while (input.length) {\n      // Get the next token and the token type\n      const result = this.getNextToken(input);\n\n      if (!result || !result.token) {\n        throw Error(`Lexer: Unexpected string \"${input}\".`);\n      }\n\n      // eslint-disable-next-line prefer-destructuring\n      token = result.token;\n\n      if (!token.value) {\n        throw Error(`Lexer: Regex parse error, please check your lexer config.`);\n      }\n\n      token.position = [lastPosition, lastPosition + token.value.length - 1];\n      lastPosition += token.value.length;\n\n      // Advance the string\n      // eslint-disable-next-line no-param-reassign\n      input = input.substring(token.value.length);\n\n      if (!result.config.ignore) {\n        tokens.push(token);\n      }\n    }\n    return tokens;\n  }\n\n  private getNextToken(input: string) {\n    for (const eachLexer of this.lexerConfig) {\n      for (const regex of eachLexer.regexes) {\n        const token = this.getTokenOnFirstMatch({ input, type: eachLexer.type, regex });\n        if (token) {\n          return {\n            token,\n            config: eachLexer,\n          };\n        }\n      }\n    }\n\n    return null;\n  }\n\n  private getTokenOnFirstMatch({ input, type, regex }: { input: string; type: string; regex: RegExp }) {\n    const matches = input.match(regex);\n\n    if (matches) {\n      // eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion\n      return { type, value: matches[1] } as IToken;\n    }\n  }\n}\n\nexport type Lexer = (text: string) => IToken[];\n\nexport const createLexer = (lexerConfig: ILexerConfig[]): Lexer => {\n  return (text: string) => {\n    return new Tokenizer(lexerConfig).tokenize(text);\n  };\n};\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/lexer/token.ts",
    "content": "export interface IToken {\n  type: string;\n  value: string;\n  position?: [number, number];\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/parser/chain.ts",
    "content": "/* eslint-disable no-use-before-define */\nimport { defaults, uniq, uniqBy } from 'lodash';\nimport { Lexer } from '../lexer';\nimport { IToken } from '../lexer/token';\nimport {\n  Chain,\n  ChainFunction,\n  ChainNode,\n  ChainNodeFactory,\n  CreateParserOptions,\n  FirstOrFunctionSet,\n  FunctionNode,\n  IElement,\n  IParseResult,\n  MatchNode,\n  MAX_VISITER_CALL,\n  Node,\n  ParentNode,\n  Parser,\n  parserMap,\n  TreeNode,\n  VisiterOption,\n  VisiterStore,\n} from './define';\nimport { match, matchFalse, matchTrue } from './match';\nimport { Scanner } from './scanner';\nimport { compareIgnoreLowerCaseWhenString, getPathByCursorIndexFromAst, tailCallOptimize } from './utils';\n\nconst createNodeByElement = (element: IElement, parentNode: ParentNode, parentIndex: number, parser: Parser): Node => {\n  if (element instanceof Array) {\n    const treeNode = new TreeNode(parentIndex);\n    treeNode.parentNode = parentNode;\n    treeNode.childs = element.map((eachElement, childIndex) => {\n      return createNodeByElement(eachElement, treeNode, childIndex, parser);\n    });\n    return treeNode;\n  }\n  if (typeof element === 'string') {\n    const matchNode = new MatchNode(\n      match(element)(),\n      {\n        type: 'string',\n        value: element,\n      },\n      parentIndex,\n    );\n    matchNode.parentNode = parentNode;\n    return matchNode;\n  }\n  if (typeof element === 'boolean') {\n    if (element) {\n      const trueMatchNode = new MatchNode(\n        matchTrue,\n        {\n          type: 'loose',\n          value: true,\n        },\n        parentIndex,\n      );\n      trueMatchNode.parentNode = parentNode;\n      return trueMatchNode;\n    }\n    const falseMatchNode = new MatchNode(\n      matchFalse,\n      {\n        type: 'loose',\n        value: false,\n      },\n      parentIndex,\n    );\n    falseMatchNode.parentNode = parentNode;\n    return falseMatchNode;\n  }\n  if (typeof element === 'function') {\n    if (element.parserName === 'match') {\n      const matchNode = new MatchNode(\n        element(),\n        {\n          type: 'special',\n          value: element.displayName,\n        },\n        parentIndex,\n      );\n      matchNode.parentNode = parentNode;\n      return matchNode;\n    }\n    if (element.parserName === 'chainNodeFactory') {\n      const chainNode = element(parentNode, null, parentIndex, parser);\n      return chainNode;\n    }\n    const functionNode = new FunctionNode(element as ChainFunction, parentIndex, parser);\n    functionNode.parentNode = parentNode;\n    return functionNode;\n  }\n  throw Error(`unknow element in chain ${element}`);\n};\n\nexport const chain: Chain = (...elements) => {\n  return (\n    solveAst = args => {\n      return args;\n    },\n  ) => {\n    const chainNodeFactory: ChainNodeFactory = (parentNode, creatorFunction, parentIndex = 0, parser) => {\n      const chainNode = new ChainNode(parentIndex);\n      chainNode.parentNode = parentNode;\n      chainNode.creatorFunction = creatorFunction;\n      chainNode.solveAst = solveAst;\n\n      chainNode.childs = elements.map((element, index) => {\n        return createNodeByElement(element, chainNode, index, parser);\n      });\n\n      if (creatorFunction) {\n        generateFirstSet(chainNode, parser);\n      }\n\n      return chainNode;\n    };\n    (chainNodeFactory as any).parserName = 'chainNodeFactory';\n\n    return chainNodeFactory;\n  };\n};\n\nfunction getParser(root: ChainFunction) {\n  if (parserMap.has(root)) {\n    return parserMap.get(root);\n  }\n  const parser = new Parser();\n  parser.rootChainNode = root()(null, null, 0, parser);\n  parserMap.set(root, parser);\n  return parser;\n}\n\nfunction scannerAddCursorToken(scanner: Scanner, cursorIndex: number, options?: CreateParserOptions) {\n  let finalCursorIndex = cursorIndex;\n\n  if (cursorIndex === null) {\n    return { scanner, finalCursorIndex };\n  }\n\n  // Find where token cursorIndex is in.\n  const cursorToken = scanner.getTokenByCharacterIndex(cursorIndex);\n\n  // Generate cursor token, if cursor position is not in a token.\n  if (!cursorToken) {\n    // If cursor not on token, add a match-all token.\n    scanner.addToken({\n      type: 'cursor',\n      value: null,\n      position: [cursorIndex, cursorIndex],\n    });\n  } else if (options.cursorTokenExcludes(cursorToken)) {\n    // If cursor is exclude, add a token next!\n    scanner.addToken({\n      type: 'cursor',\n      value: null,\n      position: [cursorIndex + 1, cursorIndex + 1],\n    });\n    finalCursorIndex += 1;\n  }\n\n  return { scanner, finalCursorIndex };\n}\n\nexport const createParser = <AST = {}>(root: ChainFunction, lexer: Lexer, options?: CreateParserOptions) => {\n  return (text: string, cursorIndex: number = null): IParseResult => {\n    // eslint-disable-next-line no-param-reassign\n    options = defaults(options || {}, new CreateParserOptions());\n\n    const startTime = new Date();\n    const tokens = lexer(text);\n    const lexerTime = new Date();\n    const originScanner = new Scanner(tokens);\n    const { scanner, finalCursorIndex } = scannerAddCursorToken(new Scanner(tokens), cursorIndex, options);\n    // eslint-disable-next-line no-param-reassign\n    cursorIndex = finalCursorIndex;\n    const parser = getParser(root);\n\n    const cursorPrevToken = scanner.getPrevTokenByCharacterIndex(cursorIndex).prevToken;\n\n    // If cursorPrevToken is null, the cursor prev node is root.\n    let cursorPrevNodes: Node[] = cursorPrevToken === null ? [parser.rootChainNode] : [];\n\n    let success = false;\n    let ast: AST = null;\n    let callVisiterCount = 0;\n    let callParentCount = 0;\n    let lastMatchUnderShortestRestToken: {\n      restTokenCount: number;\n      matchNode: MatchNode;\n      token: IToken;\n    } = null;\n\n    // Parse without cursor token\n    newVisit({\n      node: parser.rootChainNode,\n      scanner: originScanner,\n      visiterOption: {\n        onCallVisiter: (node, store) => {\n          callVisiterCount += 1;\n\n          if (callVisiterCount > MAX_VISITER_CALL) {\n            // eslint-disable-next-line no-param-reassign\n            store.stop = true;\n          }\n        },\n        onVisiterNextNode: (node, store) => {\n          callParentCount += 1;\n          if (callParentCount > MAX_VISITER_CALL) {\n            // eslint-disable-next-line no-param-reassign\n            store.stop = true;\n          }\n        },\n        onMatchNode: (matchNode, store, currentVisiterOption) => {\n          const matchResult = matchNode.run(store.scanner);\n\n          if (!matchResult.match) {\n            tryChances(matchNode, store, currentVisiterOption);\n          } else {\n            const restTokenCount = store.scanner.getRestTokenCount();\n            // Last match at least token remaining, is the most readable reason for error.\n            if (\n              !lastMatchUnderShortestRestToken ||\n              (lastMatchUnderShortestRestToken && lastMatchUnderShortestRestToken.restTokenCount > restTokenCount)\n            ) {\n              lastMatchUnderShortestRestToken = {\n                matchNode,\n                token: matchResult.token,\n                restTokenCount,\n              };\n            }\n\n            visitNextNodeFromParent(matchNode, store, currentVisiterOption, {\n              token: true,\n              ...matchResult.token,\n            });\n          }\n        },\n        onSuccess: () => {\n          success = true;\n        },\n        onFail: node => {\n          success = false;\n        },\n      },\n      parser,\n    });\n\n    // Parse with curosr token\n    newVisit({\n      node: parser.rootChainNode,\n      scanner,\n      visiterOption: {\n        onCallVisiter: (node, store) => {\n          callVisiterCount += 1;\n\n          if (callVisiterCount > MAX_VISITER_CALL) {\n            // eslint-disable-next-line no-param-reassign\n            store.stop = true;\n          }\n        },\n        onVisiterNextNode: (node, store) => {\n          callParentCount += 1;\n          if (callParentCount > MAX_VISITER_CALL) {\n            // eslint-disable-next-line no-param-reassign\n            store.stop = true;\n          }\n        },\n        onSuccess: () => {\n          ast = parser.rootChainNode.solveAst\n            ? parser.rootChainNode.solveAst(parser.rootChainNode.astResults)\n            : parser.rootChainNode.astResults;\n        },\n        onMatchNode: (matchNode, store, currentVisiterOption) => {\n          const matchResult = matchNode.run(store.scanner);\n\n          if (!matchResult.match) {\n            tryChances(matchNode, store, currentVisiterOption);\n          } else {\n            // If cursor prev token isn't null, it may a cursor prev node.\n            if (cursorPrevToken !== null && matchResult.token === cursorPrevToken) {\n              cursorPrevNodes.push(matchNode);\n            }\n\n            visitNextNodeFromParent(matchNode, store, currentVisiterOption, {\n              token: true,\n              ...matchResult.token,\n            });\n          }\n        },\n      },\n      parser,\n    });\n\n    cursorPrevNodes = uniq(cursorPrevNodes);\n\n    // Get next matchings\n    let nextMatchNodes = cursorPrevNodes.reduce(\n      (all, cursorPrevNode) => {\n        return all.concat(findNextMatchNodes(cursorPrevNode, parser));\n      },\n      [] as MatchNode[],\n    );\n\n    nextMatchNodes = uniqBy(nextMatchNodes, each => {\n      return each.matching.type + each.matching.value;\n    });\n\n    // If has next token, filter nextMatchNodes by cursorNextToken\n    const cursorNextToken = scanner.getNextTokenFromCharacterIndex(cursorIndex);\n    if (cursorNextToken) {\n      nextMatchNodes = nextMatchNodes.filter(nextMatchNode => {\n        return !compareIgnoreLowerCaseWhenString(nextMatchNode.matching.value, cursorNextToken.value);\n      });\n    }\n\n    // Get error message\n    let error: IParseResult['error'] = null;\n\n    if (!success) {\n      const suggestions = uniqBy(\n        (lastMatchUnderShortestRestToken\n          ? findNextMatchNodes(lastMatchUnderShortestRestToken.matchNode, parser)\n          : findNextMatchNodes(parser.rootChainNode, parser)\n        ).map(each => {\n          return each.matching;\n        }),\n        each => {\n          return each.type + each.value;\n        },\n      );\n\n      const errorToken =\n        lastMatchUnderShortestRestToken && scanner.getNextByToken(lastMatchUnderShortestRestToken.token);\n\n      if (errorToken) {\n        error = {\n          suggestions,\n          token: errorToken,\n          reason: 'wrong',\n        };\n      } else {\n        error = {\n          suggestions,\n          token: lastMatchUnderShortestRestToken ? lastMatchUnderShortestRestToken.token : null,\n          reason: 'incomplete',\n        };\n      }\n    }\n\n    const parserTime = new Date();\n\n    // Find cursor ast from whole ast.\n    const cursorKeyPath = getPathByCursorIndexFromAst(ast, cursorIndex).split('.');\n\n    return {\n      success,\n      ast,\n      cursorKeyPath: cursorKeyPath[0] === '' ? [] : cursorKeyPath,\n      nextMatchings: nextMatchNodes\n        .reverse()\n        .map(each => {\n          return each.matching;\n        })\n        .filter(each => {\n          return !!each.value;\n        }),\n      error,\n      debugInfo: {\n        tokens,\n        callVisiterCount,\n        costs: {\n          lexer: lexerTime.getTime() - startTime.getTime(),\n          parser: parserTime.getTime() - startTime.getTime(),\n        },\n      },\n    };\n  };\n};\n\nfunction newVisit({\n  node,\n  scanner,\n  visiterOption,\n  parser,\n}: {\n  node: Node;\n  scanner: Scanner;\n  visiterOption: VisiterOption;\n  parser: Parser;\n}) {\n  const defaultVisiterOption = new VisiterOption();\n  defaults(visiterOption, defaultVisiterOption);\n\n  const newStore = new VisiterStore(scanner, parser);\n  visit({ node, store: newStore, visiterOption, childIndex: 0 });\n}\n\nconst visit = tailCallOptimize(\n  ({\n    node,\n    store,\n    visiterOption,\n    childIndex,\n  }: {\n    node: Node;\n    store: VisiterStore;\n    visiterOption: VisiterOption;\n    childIndex: number;\n  }) => {\n    if (store.stop) {\n      fail(node, store, visiterOption);\n      return;\n    }\n\n    if (!node) {\n      throw Error('no node!');\n    }\n\n    if (visiterOption.onCallVisiter) {\n      visiterOption.onCallVisiter(node, store);\n    }\n\n    if (node instanceof ChainNode) {\n      if (firstSetUnMatch(node, store, visiterOption, childIndex)) {\n        return; // If unmatch, stop!\n      }\n\n      visitChildNode({ node, store, visiterOption, childIndex });\n    } else if (node instanceof TreeNode) {\n      visitChildNode({ node, store, visiterOption, childIndex });\n    } else if (node instanceof MatchNode) {\n      if (node.matching.type === 'loose') {\n        if (node.matching.value === true) {\n          visitNextNodeFromParent(node, store, visiterOption, null);\n        } else {\n          throw Error('Not support loose false!');\n        }\n      } else {\n        visiterOption.onMatchNode(node, store, visiterOption);\n      }\n    } else if (node instanceof FunctionNode) {\n      const functionName = node.chainFunction.name;\n      const replacedNode = node.run();\n      replacedNode.functionName = functionName;\n\n      // eslint-disable-next-line no-param-reassign\n      node.parentNode.childs[node.parentIndex] = replacedNode;\n      visit({ node: replacedNode, store, visiterOption, childIndex: 0 });\n    } else {\n      throw Error(`Unexpected node type: ${node}`);\n    }\n  },\n);\n\nfunction visitChildNode({\n  node,\n  store,\n  visiterOption,\n  childIndex,\n}: {\n  node: ParentNode;\n  store: VisiterStore;\n  visiterOption: VisiterOption;\n  childIndex: number;\n}) {\n  if (node instanceof ChainNode) {\n    const child = node.childs[childIndex];\n    if (child) {\n      visit({ node: child, store, visiterOption, childIndex: 0 });\n    } else {\n      visitNextNodeFromParent(\n        node,\n        store,\n        visiterOption,\n        visiterOption.generateAst ? node.solveAst(node.astResults) : null,\n      );\n    }\n  } else {\n    // This case, Node === TreeNode\n    const child = node.childs[childIndex];\n    if (childIndex + 1 < node.childs.length) {\n      addChances({\n        node,\n        store,\n        visiterOption,\n        tokenIndex: store.scanner.getIndex(),\n        childIndex: childIndex + 1,\n        addToNextMatchNodeFinders: true,\n      });\n    }\n    if (child) {\n      visit({ node: child, store, visiterOption, childIndex: 0 });\n    } else {\n      throw Error('tree node unexpect end');\n    }\n  }\n}\n\nconst visitNextNodeFromParent = tailCallOptimize(\n  (node: Node, store: VisiterStore, visiterOption: VisiterOption, astValue: any) => {\n    if (store.stop) {\n      fail(node, store, visiterOption);\n      return;\n    }\n\n    if (visiterOption.onVisiterNextNode) {\n      visiterOption.onVisiterNextNode(node, store);\n    }\n\n    if (!node.parentNode) {\n      return noNextNode(node, store, visiterOption);\n    }\n\n    if (node.parentNode instanceof ChainNode) {\n      if (visiterOption.generateAst) {\n        // eslint-disable-next-line no-param-reassign\n        node.parentNode.astResults[node.parentIndex] = astValue;\n      }\n\n      // A       B <- next node      C\n      // └── node <- current node\n      visit({ node: node.parentNode, store, visiterOption, childIndex: node.parentIndex + 1 });\n    } else if (node.parentNode instanceof TreeNode) {\n      visitNextNodeFromParent(node.parentNode, store, visiterOption, astValue);\n    } else {\n      throw Error(`Unexpected parent node type: ${node.parentNode}`);\n    }\n  },\n);\n\nfunction noNextNode(node: Node, store: VisiterStore, visiterOption: VisiterOption) {\n  if (store.scanner.isEnd()) {\n    if (visiterOption.onSuccess) {\n      visiterOption.onSuccess();\n    }\n  } else {\n    tryChances(node, store, visiterOption);\n  }\n}\n\nfunction addChances({\n  node,\n  store,\n  visiterOption,\n  tokenIndex,\n  childIndex,\n  addToNextMatchNodeFinders,\n}: {\n  node: ParentNode;\n  store: VisiterStore;\n  visiterOption: VisiterOption;\n  tokenIndex: number;\n  childIndex: number;\n  addToNextMatchNodeFinders: boolean;\n}) {\n  const chance = {\n    node,\n    tokenIndex,\n    childIndex,\n  };\n\n  store.restChances.push(chance);\n}\n\nfunction hasParentNodeByFunctionName(node: Node, functionName: string): boolean {\n  if (node instanceof ChainNode && node.functionName === functionName) {\n    return true;\n  }\n\n  if (node.parentNode) {\n    return hasParentNodeByFunctionName(node.parentNode, functionName);\n  }\n\n  return false;\n}\n\nfunction tryChances(node: Node, store: VisiterStore, visiterOption: VisiterOption) {\n  if (store.restChances.length === 0) {\n    fail(node, store, visiterOption);\n    return;\n  }\n\n  const nextChance = store.restChances.pop();\n\n  // reset scanner index\n  store.scanner.setIndex(nextChance.tokenIndex);\n\n  visit({ node: nextChance.node, store, visiterOption, childIndex: nextChance.childIndex });\n}\n\nfunction fail(node: Node, store: VisiterStore, visiterOption: VisiterOption) {\n  if (visiterOption.onFail) {\n    visiterOption.onFail(node);\n  }\n}\n\n// Find all tokens that may appear next\nfunction findNextMatchNodes(node: Node, parser: Parser): MatchNode[] {\n  const nextMatchNodes: MatchNode[] = [];\n\n  let passCurrentNode = false;\n\n  const visiterOption: VisiterOption = {\n    generateAst: false,\n    enableFirstSet: false,\n    onMatchNode: (matchNode, store, currentVisiterOption) => {\n      if (matchNode === node && passCurrentNode === false) {\n        passCurrentNode = true;\n        visitNextNodeFromParent(matchNode, store, currentVisiterOption, null);\n      } else {\n        nextMatchNodes.push(matchNode);\n      }\n\n      // Suppose the match failed, so we can find another possible match chance!\n      tryChances(matchNode, store, currentVisiterOption);\n    },\n  };\n\n  newVisit({ node, scanner: new Scanner([]), visiterOption, parser });\n\n  return nextMatchNodes;\n}\n\n// First Set -----------------------------------------------------------------\n\nfunction firstSetUnMatch(node: ChainNode, store: VisiterStore, visiterOption: VisiterOption, childIndex: number) {\n  if (\n    visiterOption.enableFirstSet &&\n    node.creatorFunction &&\n    childIndex === 0 &&\n    store.parser.firstSet.has(node.creatorFunction)\n  ) {\n    const firstMatchNodes = store.parser.firstSet.get(node.creatorFunction);\n\n    // If not match any first match node, try chances\n    if (\n      !firstMatchNodes.some(firstMatchNode => {\n        return firstMatchNode.run(store.scanner, false).match;\n      })\n    ) {\n      tryChances(node, store, visiterOption);\n      return true; // Yes, unMatch.\n    }\n    return false; // No, Match.\n  }\n}\n\nfunction generateFirstSet(node: ChainNode, parser: Parser) {\n  if (parser.firstSet.has(node.creatorFunction)) {\n    return;\n  }\n\n  const firstMatchNodes = getFirstOrFunctionSet(node, node.creatorFunction, parser);\n  parser.firstOrFunctionSet.set(node.creatorFunction, firstMatchNodes);\n\n  solveFirstSet(node.creatorFunction, parser);\n}\n\nfunction getFirstOrFunctionSet(node: Node, creatorFunction: ChainFunction, parser: Parser): FirstOrFunctionSet[] {\n  if (node instanceof ChainNode) {\n    if (node.childs[0]) {\n      return getFirstOrFunctionSet(node.childs[0], creatorFunction, parser);\n    }\n  } else if (node instanceof TreeNode) {\n    return node.childs.reduce((all, next) => {\n      return all.concat(getFirstOrFunctionSet(next, creatorFunction, parser));\n    }, []);\n  } else if (node instanceof MatchNode) {\n    return [node];\n  } else if (node instanceof FunctionNode) {\n    if (parser.relatedSet.has(node.chainFunction)) {\n      parser.relatedSet.get(node.chainFunction).add(creatorFunction);\n    } else {\n      parser.relatedSet.set(node.chainFunction, new Set([creatorFunction]));\n    }\n\n    return [node.chainFunction];\n  } else {\n    throw Error(`Unexpected node: ${node}`);\n  }\n}\n\nfunction solveFirstSet(creatorFunction: ChainFunction, parser: Parser) {\n  if (parser.firstSet.has(creatorFunction)) {\n    return;\n  }\n\n  const firstMatchNodes = parser.firstOrFunctionSet.get(creatorFunction);\n\n  // Try if relate functionName has done first set.\n  const newFirstMatchNodes = firstMatchNodes.reduce(\n    (all, firstMatchNode) => {\n      if (typeof firstMatchNode === 'string') {\n        if (parser.firstSet.has(firstMatchNode)) {\n          // eslint-disable-next-line no-param-reassign\n          all = all.concat(parser.firstSet.get(firstMatchNode));\n        } else {\n          all.push(firstMatchNode);\n        }\n      } else {\n        all.push(firstMatchNode);\n      }\n\n      return all;\n    },\n    [] as FirstOrFunctionSet[],\n  );\n\n  parser.firstOrFunctionSet.set(creatorFunction, newFirstMatchNodes);\n\n  // If all set hasn't function node, we can solve it's relative set.\n  if (\n    newFirstMatchNodes.every(firstMatchNode => {\n      return firstMatchNode instanceof MatchNode;\n    })\n  ) {\n    parser.firstSet.set(creatorFunction, newFirstMatchNodes as MatchNode[]);\n\n    // If this functionName has related functionNames, solve them\n    if (parser.relatedSet.has(creatorFunction)) {\n      const relatedFunctionNames = parser.relatedSet.get(creatorFunction);\n      relatedFunctionNames.forEach(relatedFunctionName => {\n        return solveFirstSet;\n      });\n    }\n  }\n}\n\n// First set /////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/parser/define.ts",
    "content": "import { IToken } from '../lexer/token';\nimport { IMatch } from './match';\nimport { Scanner } from './scanner';\n\n// tslint:disable:max-classes-per-file\n\nexport interface IParseResult {\n  success: boolean;\n  ast: IAst;\n  cursorKeyPath: string[];\n  nextMatchings: IMatching[];\n  error: {\n    token: IToken;\n    reason: 'wrong' | 'incomplete';\n    suggestions: IMatching[];\n  };\n  debugInfo: {\n    tokens: IToken[];\n    callVisiterCount: number;\n    costs: {\n      lexer: number;\n      parser: number;\n    };\n  };\n}\n\nexport type FirstOrFunctionSet = MatchNode | ChainFunction;\n\nexport type IMatchFn = (scanner: Scanner, isCostToken: boolean) => IMatch;\n\n// IToken | Array<IToken> | any return object from resolveAst().\nexport type IAst = IToken | any;\n\nexport type Node = MatchNode | FunctionNode | TreeNode | ChainNode;\n\nexport type ParentNode = TreeNode | ChainNode;\n\nexport interface IMatching {\n  // loose not cost token, and result is fixed true of false.\n  type: 'string' | 'loose' | 'special';\n  value: string | boolean;\n}\n\nexport type SingleElement = string | any;\n\nexport type IElement = SingleElement | SingleElement[];\n\nexport type IElements = IElement[];\n\nexport type ISolveAst = (astResult: IAst[]) => IAst;\n\nexport type Chain = (...elements: IElements) => (solveAst?: ISolveAst) => ChainNodeFactory;\n\nexport type ChainNodeFactory = (\n  parentNode?: ParentNode,\n  // If parent node is a function, here will get it's name.\n  creatorFunction?: ChainFunction,\n  parentIndex?: number,\n  parser?: Parser,\n) => ChainNode;\n\nexport type ChainFunction = () => ChainNodeFactory;\n\nexport interface IChance {\n  node: ParentNode;\n  childIndex: number;\n  tokenIndex: number;\n}\n\n// ////////////////////////////////////// Const or Variables\n\nexport const parserMap = new Map<ChainFunction, Parser>();\nexport const MAX_VISITER_CALL = 1000000;\n\nexport class Parser {\n  public rootChainNode: ChainNode = null;\n\n  public firstSet = new Map<ChainFunction, MatchNode[]>();\n\n  public firstOrFunctionSet = new Map<ChainFunction, FirstOrFunctionSet[]>();\n\n  public relatedSet = new Map<ChainFunction, Set<ChainFunction>>();\n}\n\nexport class VisiterStore {\n  public restChances: IChance[] = [];\n\n  public stop = false;\n\n  // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-parameter-properties\n  constructor(public scanner: Scanner, public parser: Parser) {\n    //\n  }\n}\n\nexport class VisiterOption {\n  public onCallVisiter?: (node?: Node, store?: VisiterStore) => void;\n\n  public onVisiterNextNode?: (node?: Node, store?: VisiterStore) => void;\n\n  public onSuccess?: () => void;\n\n  public onFail?: (lastNode?: Node) => void;\n\n  public onMatchNode: (matchNode: MatchNode, store: VisiterStore, visiterOption: VisiterOption) => void;\n\n  public generateAst?: boolean = true;\n\n  public enableFirstSet?: boolean = true;\n}\n\nexport class ChainNode {\n  public parentNode: ParentNode;\n\n  public childs: Node[] = [];\n\n  public astResults?: IAst[] = [];\n\n  // Eg: const foo = chain => chain()(), so the chain creatorFunction is 'foo'.\n  public creatorFunction: ChainFunction = null;\n\n  // Only user function can have functionName.\n  public functionName: string;\n\n  public solveAst: ISolveAst = null;\n\n  // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-parameter-properties\n  constructor(public parentIndex: number) {\n    //\n  }\n}\n\nexport class TreeNode {\n  public parentNode: ParentNode;\n\n  public childs: Node[] = [];\n\n  // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-parameter-properties\n  constructor(public parentIndex: number) {\n    //\n  }\n}\n\nexport class FunctionNode {\n  public parentNode: ParentNode;\n\n  // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-parameter-properties\n  constructor(public chainFunction: ChainFunction, public parentIndex: number, public parser: Parser) {\n    //\n  }\n\n  public run = () => {\n    return this.chainFunction()(this.parentNode, this.chainFunction, this.parentIndex, this.parser);\n  };\n}\n\nexport class MatchNode {\n  public parentNode: ParentNode;\n\n  // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-parameter-properties\n  constructor(private matchFunction: IMatchFn, public matching: IMatching, public parentIndex: number) {\n    //\n  }\n\n  public run = (scanner: Scanner, isCostToken = true) => {\n    return this.matchFunction(scanner, isCostToken);\n  };\n}\n\nexport class CreateParserOptions {\n  public cursorTokenExcludes?: (token?: IToken) => boolean = () => {\n    return false;\n  };\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/parser/index.ts",
    "content": "export { chain, createParser } from './chain';\nexport { many, matchTokenType, optional, plus } from './match';\nexport { Scanner } from './scanner';\nexport * from './define';\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/parser/match.ts",
    "content": "import { IToken } from '../lexer/token';\nimport { chain } from './chain';\nimport { IElements } from './define';\nimport { Scanner } from './scanner';\n\nexport interface IMatch {\n  token?: IToken;\n  match: boolean;\n}\n\nfunction equalWordOrIncludeWords(str: string, word: string | string[] | null) {\n  if (typeof word === 'string') {\n    return judgeMatch(str, word);\n  }\n  return word.some(eachWord => {\n    return judgeMatch(str, eachWord);\n  });\n}\n\nfunction judgeMatch(source: string, target: string) {\n  if (source === null) {\n    return false;\n  }\n  return (source && source.toLowerCase()) === (target && target.toLowerCase());\n}\n\nfunction matchToken(scanner: Scanner, compare: (token: IToken) => boolean, isCostToken?: boolean): IMatch {\n  const token = scanner.read();\n  if (!token) {\n    return {\n      token: null,\n      match: false,\n    };\n  }\n  if (compare(token)) {\n    if (isCostToken) {\n      scanner.next();\n    }\n\n    return {\n      token,\n      match: true,\n    };\n  }\n  return {\n    token,\n    match: false,\n  };\n}\n\nfunction createMatch<T>(fn: (scanner: Scanner, arg?: T, isCostToken?: boolean) => IMatch, specialName?: string) {\n  return (arg?: T) => {\n    function foo() {\n      return (scanner: Scanner, isCostToken?: boolean) => {\n        return fn(scanner, arg, isCostToken);\n      };\n    }\n\n    foo.parserName = 'match';\n\n    foo.displayName = specialName;\n    return foo;\n  };\n}\n\nexport const match = createMatch((scanner, word: string | string[], isCostToken) => {\n  return matchToken(\n    scanner,\n    token => {\n      return equalWordOrIncludeWords(token.value, word);\n    },\n    isCostToken,\n  );\n});\n\ninterface IMatchTokenTypeOption {\n  includes?: string[];\n  excludes?: string[];\n}\n\nexport const matchTokenType = (tokenType: string, opts: IMatchTokenTypeOption = {}) => {\n  const options: IMatchTokenTypeOption = { includes: [], excludes: [], ...opts };\n\n  return createMatch((scanner, word, isCostToken) => {\n    return matchToken(\n      scanner,\n      token => {\n        if (\n          options.includes.some(includeValue => {\n            return judgeMatch(includeValue, token.value);\n          })\n        ) {\n          return true;\n        }\n\n        if (\n          options.excludes.some(includeValue => {\n            return judgeMatch(includeValue, token.value);\n          })\n        ) {\n          return false;\n        }\n\n        if (token.type !== tokenType) {\n          return false;\n        }\n\n        return true;\n      },\n      isCostToken,\n    );\n  }, tokenType)();\n};\n\nexport const matchTrue = (): IMatch => {\n  return {\n    token: null,\n    match: true,\n  };\n};\n\nexport const matchFalse = (): IMatch => {\n  return {\n    token: null,\n    match: true,\n  };\n};\n\nexport const optional = (...elements: IElements) => {\n  if (elements.length === 0) {\n    throw Error('Must have arguments!');\n  }\n\n  return chain([\n    chain(...elements)(ast => {\n      return elements.length === 1 ? ast[0] : ast;\n    }),\n    true,\n  ])(ast => {\n    return ast[0];\n  });\n};\n\nexport const plus = (...elements: IElements) => {\n  if (elements.length === 0) {\n    throw Error('Must have arguments!');\n  }\n\n  const plusFunction = () => {\n    return chain(\n      chain(...elements)(ast => {\n        return elements.length === 1 ? ast[0] : ast;\n      }),\n      optional(plusFunction),\n    )(ast => {\n      if (ast[1]) {\n        return [ast[0]].concat(ast[1]);\n      }\n      return [ast[0]];\n    });\n  };\n  return plusFunction;\n};\n\nexport const many = (...elements: IElements) => {\n  if (elements.length === 0) {\n    throw Error('Must have arguments!');\n  }\n  return optional(plus(...elements));\n};\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/parser/scanner.ts",
    "content": "import { IToken } from '../lexer/token';\n\nexport class Scanner {\n  private tokens: IToken[] = [];\n\n  private index = 0;\n\n  constructor(tokens: IToken[], index = 0) {\n    // ignore whitespace, comment\n    this.tokens = tokens.slice();\n    this.index = index;\n  }\n\n  public read = () => {\n    const token = this.tokens[this.index];\n    if (token) {\n      return token;\n    }\n    return false;\n  };\n\n  public next = () => {\n    this.index += 1;\n  };\n\n  public isEnd = () => {\n    return this.index >= this.tokens.length;\n  };\n\n  public getIndex = () => {\n    return this.index;\n  };\n\n  public setIndex = (index: number) => {\n    this.index = index;\n    return index;\n  };\n\n  public getRestTokenCount = () => {\n    return this.tokens.length - this.index - 1;\n  };\n\n  public getNextByToken = (token: IToken) => {\n    const currentTokenIndex = this.tokens.findIndex(eachToken => {\n      return eachToken === token;\n    });\n    if (currentTokenIndex > -1) {\n      if (currentTokenIndex + 1 < this.tokens.length) {\n        return this.tokens[currentTokenIndex + 1];\n      }\n      return null;\n    }\n    throw Error(`token ${token.value.toString()} not exist in scanner.`);\n  };\n\n  public getTokenByCharacterIndex = (characterIndex: number) => {\n    if (characterIndex === null) {\n      return null;\n    }\n\n    for (const token of this.tokens) {\n      if (characterIndex >= token.position[0] && characterIndex - 1 <= token.position[1]) {\n        return token;\n      }\n    }\n\n    return null;\n  };\n\n  public getPrevTokenByCharacterIndex = (characterIndex: number) => {\n    let prevToken: IToken = null;\n    let prevTokenIndex: number = null;\n\n    this.tokens.forEach((token, index) => {\n      if (token.position[1] < characterIndex - 1) {\n        prevToken = token;\n        prevTokenIndex = index;\n      }\n    });\n\n    return { prevToken, prevTokenIndex };\n  };\n\n  public getNextTokenFromCharacterIndex = (characterIndex: number) => {\n    for (const token of this.tokens) {\n      if (token.position[0] > characterIndex) {\n        return token;\n      }\n    }\n\n    return null;\n  };\n\n  public addToken = (token: IToken) => {\n    const { prevToken, prevTokenIndex } = this.getPrevTokenByCharacterIndex(token.position[0]);\n\n    if (prevToken) {\n      // prevTokenIndex\n      this.tokens.splice(prevTokenIndex + 1, 0, token);\n    } else {\n      this.tokens.unshift(token);\n    }\n  };\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/parser/utils.ts",
    "content": "import { IAst } from './define';\n\nexport const compareIgnoreLowerCaseWhenString = (source: any, target: any) => {\n  if (typeof source === 'string' && typeof target === 'string') {\n    return source.toLowerCase() === target.toLowerCase();\n  }\n  return source === target;\n};\n\nexport const binaryRecursionToArray = (ast: IAst[]) => {\n  if (ast[1]) {\n    return [ast[0]].concat(ast[1][1]);\n  }\n\n  return [ast[0]];\n};\n\nexport function tailCallOptimize<T>(f: T): T {\n  let value: any;\n  let active = false;\n  const accumulated: any[] = [];\n  return function accumulator(this: any) {\n    // eslint-disable-next-line prefer-rest-params\n    accumulated.push(arguments);\n    if (!active) {\n      active = true;\n      while (accumulated.length) {\n        // eslint-disable-next-line babel/no-invalid-this\n        value = (f as any).apply(this, accumulated.shift());\n      }\n      active = false;\n      return value;\n    }\n  } as any;\n}\n\nexport function getPathByCursorIndexFromAst(obj: any, cursorIndex: number, path?: string) {\n  // eslint-disable-next-line no-param-reassign\n  path = path || '';\n  let fullpath = '';\n  // eslint-disable-next-line guard-for-in\n  for (const key in obj) {\n    if (\n      obj[key] &&\n      obj[key].token === true &&\n      obj[key].position[0] <= cursorIndex &&\n      obj[key].position[1] + 1 >= cursorIndex\n    ) {\n      if (path === '') {\n        return key;\n      }\n      return `${path}.${key}`;\n    }\n    if (typeof obj[key] === 'object') {\n      fullpath = getPathByCursorIndexFromAst(obj[key], cursorIndex, path === '' ? key : `${path}.${key}`) || fullpath;\n    }\n  }\n  return fullpath;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/monaco-plugin/default-opts.ts",
    "content": "/* eslint-disable @typescript-eslint/no-parameter-properties */\n/* eslint-disable no-useless-constructor */\nimport * as _ from 'lodash';\nimport { IMatching, IParseResult } from '../..';\nimport { ITableInfo, ICompletionItem, IStatement, ICursorInfo } from '../sql-parser';\n\nexport type IMonacoVersion = '0.13.2' | '0.15.6';\n\nexport type IParserType = 'mysql' | 'odps' | 'blink' | 'dsql' | 'grail' | 'emcsql';\n\nexport class DefaultOpts {\n  public monacoEditorVersion: IMonacoVersion = '0.15.6';\n\n  public parserType: IParserType = 'odps';\n\n  public language = 'sql';\n\n  constructor(private monaco: any) {\n    //\n  }\n\n  public onParse = (parseResult: IParseResult) => {\n    //\n  };\n\n  public onSuggestTableNames?: (cursorInfo?: ICursorInfo<ITableInfo>) => Promise<ICompletionItem[]> = cursorInfo => {\n    return Promise.resolve(\n      ['dt', 'b2b', 'tmall'].map(name => {\n        return {\n          label: name,\n          insertText: name,\n          sortText: `A${name}`,\n          kind: this.monaco.languages.CompletionItemKind.Folder,\n        };\n      }),\n    );\n  };\n\n  public onSuggestTableFields?: (\n    tableInfo?: ITableInfo,\n    cursorValue?: string,\n    rootStatement?: IStatement,\n  ) => Promise<ICompletionItem[]> = tableInfo => {\n    return Promise.resolve(\n      ['aa', 'bb', 'cc']\n        .map(eachName => {\n          return _.get(tableInfo, 'namespace.value', '') + _.get(tableInfo, 'tableName.value', '') + eachName;\n        })\n        .map(fieldName => {\n          return {\n            label: fieldName,\n            insertText: fieldName,\n            sortText: `B${fieldName}`,\n            kind: this.monaco.languages.CompletionItemKind.Field,\n          };\n        }),\n    );\n  };\n\n  public pipeKeywords = (keywords: IMatching[]) => {\n    return keywords\n      .filter(matching => {\n        return matching.type === 'string';\n      })\n      .map(matching => {\n        const value = /[a-zA-Z]+/.test(matching.value.toString())\n          ? _.upperCase(matching.value.toString())\n          : matching.value.toString();\n        return {\n          label: value,\n          insertText: value,\n          documentation: 'documentation',\n          detail: 'detail',\n          kind: this.monaco.languages.CompletionItemKind.Keyword,\n          sortText: `W${matching.value}`,\n        };\n      });\n  };\n\n  public onSuggestFunctionName?: (inputValue?: string) => Promise<ICompletionItem[]> = inputValue => {\n    return Promise.resolve(\n      ['sum', 'count'].map(each => {\n        return {\n          label: each,\n          insertText: each,\n          sortText: `C${each}`,\n          kind: this.monaco.languages.CompletionItemKind.Function,\n        };\n      }),\n    );\n  };\n\n  public onSuggestFieldGroup?: (tableNameOrAlias?: string) => ICompletionItem = tableNameOrAlias => {\n    return {\n      label: tableNameOrAlias,\n      insertText: tableNameOrAlias,\n      sortText: `D${tableNameOrAlias}`,\n      kind: this.monaco.languages.CompletionItemKind.Folder,\n    };\n  };\n\n  public onHoverTableField?: (fieldName?: string, extra?: ICompletionItem) => Promise<any> = (...args) => {\n    return Promise.resolve([\n      { value: 'onHoverTableField' },\n      {\n        value: `\\`\\`\\`json\\n${JSON.stringify(args, null, 2)}\\n\\`\\`\\``,\n      },\n    ]);\n  };\n\n  public onHoverTableName?: (cursorInfo?: ICursorInfo) => Promise<any> = (...args) => {\n    return Promise.resolve([\n      { value: 'onHoverTableName' },\n      {\n        value: `\\`\\`\\`json\\n${JSON.stringify(args, null, 2)}\\n\\`\\`\\``,\n      },\n    ]);\n  };\n\n  public onHoverFunctionName?: (functionName?: string) => Promise<any> = (...args) => {\n    return Promise.resolve([\n      { value: 'onHoverFunctionName' },\n      {\n        value: `\\`\\`\\`json\\n${JSON.stringify(args, null, 2)}\\n\\`\\`\\``,\n      },\n    ]);\n  };\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/monaco-plugin/index.ts",
    "content": "/* eslint-disable no-restricted-globals */\n/* eslint-disable no-case-declarations */\n/* eslint-disable no-use-before-define */\n/* eslint-disable no-plusplus */\n/* eslint-disable no-param-reassign */\nimport * as _ from 'lodash';\nimport { IParseResult } from '../..';\nimport { DefaultOpts, IMonacoVersion, IParserType } from './default-opts';\nimport * as MyWorker from './parser.worker';\nimport {\n  ICompletionItem,\n  ITableInfo,\n  reader,\n  ICursorInfo,\n} from '../sql-parser';\n\nconst supportedMonacoEditorVersion = ['0.13.2', '0.15.6'];\n\nexport function monacoSqlAutocomplete(\n  monaco: any,\n  editor: any,\n  opts?: Partial<DefaultOpts>,\n) {\n  opts = _.defaults(opts || {}, new DefaultOpts(monaco));\n\n  if (supportedMonacoEditorVersion.indexOf(opts.monacoEditorVersion) === -1) {\n    throw Error(\n      `monaco-editor version ${\n        opts.monacoEditorVersion\n      } is not allowed, only support ${supportedMonacoEditorVersion.join(' ')}`,\n    );\n  }\n\n  // Get parser info and show error.\n  let currentParserPromise: any = null;\n  let editVersion = 0;\n\n  editor.onDidChangeModelContent((event: any) => {\n    editVersion++;\n    const currentEditVersion = editVersion;\n\n    currentParserPromise = new Promise((resolve) => {\n      setTimeout(() => {\n        const model = editor.getModel();\n\n        asyncParser(\n          editor.getValue(),\n          model.getOffsetAt(editor.getPosition()),\n          opts.parserType,\n        ).then((parseResult) => {\n          resolve(parseResult);\n\n          if (currentEditVersion !== editVersion) {\n            return;\n          }\n\n          opts.onParse(parseResult);\n\n          if (parseResult.error) {\n            const newReason =\n              parseResult.error.reason === 'incomplete'\n                ? `Incomplete, expect next input: \\n${parseResult.error.suggestions\n                    .map((each: any) => {\n                      return each.value;\n                    })\n                    .join('\\n')}`\n                : `Wrong input, expect: \\n${parseResult.error.suggestions\n                    .map((each: any) => {\n                      return each.value;\n                    })\n                    .join('\\n')}`;\n\n            const errorPosition = parseResult.error.token\n              ? {\n                  startLineNumber: model.getPositionAt(\n                    parseResult.error.token.position[0],\n                  ).lineNumber,\n                  startColumn: model.getPositionAt(\n                    parseResult.error.token.position[0],\n                  ).column,\n                  endLineNumber: model.getPositionAt(\n                    parseResult.error.token.position[1],\n                  ).lineNumber,\n                  endColumn:\n                    model.getPositionAt(parseResult.error.token.position[1])\n                      .column + 1,\n                }\n              : {\n                  startLineNumber: 0,\n                  startColumn: 0,\n                  endLineNumber: 0,\n                  endColumn: 0,\n                };\n\n            model.getPositionAt(parseResult.error.token);\n\n            monaco.editor.setModelMarkers(model, opts.language, [\n              {\n                ...errorPosition,\n                message: newReason,\n                severity: getSeverityByVersion(\n                  monaco,\n                  opts.monacoEditorVersion,\n                ),\n              },\n            ]);\n          } else {\n            monaco.editor.setModelMarkers(editor.getModel(), opts.language, []);\n          }\n        });\n      });\n    });\n  });\n\n  monaco.languages.registerCompletionItemProvider(opts.language, {\n    triggerCharacters:\n      ' $.:{}=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),\n    provideCompletionItems: async () => {\n      const currentEditVersion = editVersion;\n      const parseResult: IParseResult = await currentParserPromise;\n\n      if (currentEditVersion !== editVersion) {\n        return returnCompletionItemsByVersion([], opts.monacoEditorVersion);\n      }\n\n      const cursorInfo = await reader.getCursorInfo(\n        parseResult.ast,\n        parseResult.cursorKeyPath,\n      );\n\n      const parserSuggestion = opts.pipeKeywords(parseResult.nextMatchings);\n\n      if (!cursorInfo) {\n        return returnCompletionItemsByVersion(\n          parserSuggestion,\n          opts.monacoEditorVersion,\n        );\n      }\n\n      switch (cursorInfo.type) {\n        case 'tableField':\n          const cursorRootStatementFields = await reader.getFieldsFromStatement(\n            parseResult.ast,\n            parseResult.cursorKeyPath,\n            opts.onSuggestTableFields,\n          );\n\n          // group.fieldName\n          const groups = _.groupBy(\n            cursorRootStatementFields.filter((cursorRootStatementField) => {\n              return cursorRootStatementField.groupPickerName !== null;\n            }),\n            'groupPickerName',\n          );\n\n          const functionNames = await opts.onSuggestFunctionName(\n            cursorInfo.token.value,\n          );\n\n          return returnCompletionItemsByVersion(\n            cursorRootStatementFields\n              .concat(parserSuggestion)\n              .concat(functionNames)\n              .concat(\n                groups\n                  ? Object.keys(groups).map((groupName) => {\n                      return opts.onSuggestFieldGroup(groupName);\n                    })\n                  : [],\n              ),\n            opts.monacoEditorVersion,\n          );\n        case 'tableFieldAfterGroup':\n          // 字段 . 后面的部分\n          const cursorRootStatementFieldsAfter =\n            await reader.getFieldsFromStatement(\n              parseResult.ast,\n              parseResult.cursorKeyPath as any,\n              opts.onSuggestTableFields,\n            );\n\n          return returnCompletionItemsByVersion(\n            cursorRootStatementFieldsAfter\n              .filter((cursorRootStatementField: any) => {\n                return (\n                  cursorRootStatementField.groupPickerName ===\n                  (cursorInfo as ICursorInfo<{ groupName: string }>).groupName\n                );\n              })\n              .concat(parserSuggestion),\n            opts.monacoEditorVersion,\n          );\n        case 'tableName':\n          const tableNames = await opts.onSuggestTableNames(\n            cursorInfo as ICursorInfo<ITableInfo>,\n          );\n\n          return returnCompletionItemsByVersion(\n            tableNames.concat(parserSuggestion),\n            opts.monacoEditorVersion,\n          );\n        case 'functionName':\n          return opts.onSuggestFunctionName(cursorInfo.token.value);\n        default:\n          return returnCompletionItemsByVersion(\n            parserSuggestion,\n            opts.monacoEditorVersion,\n          );\n      }\n    },\n  });\n\n  monaco.languages.registerHoverProvider(opts.language, {\n    provideHover: async (model: any, position: any) => {\n      const parseResult: IParseResult = await asyncParser(\n        editor.getValue(),\n        model.getOffsetAt(position),\n        opts.parserType,\n      );\n\n      const cursorInfo = await reader.getCursorInfo(\n        parseResult.ast,\n        parseResult.cursorKeyPath,\n      );\n\n      if (!cursorInfo) {\n        return null as any;\n      }\n\n      let contents: any = [];\n\n      switch (cursorInfo.type) {\n        case 'tableField':\n          const extra = await reader.findFieldExtraInfo(\n            parseResult.ast,\n            cursorInfo,\n            opts.onSuggestTableFields,\n            parseResult.cursorKeyPath,\n          );\n          contents = await opts.onHoverTableField(\n            cursorInfo.token.value,\n            extra,\n          );\n          break;\n        case 'tableFieldAfterGroup':\n          const extraAfter = await reader.findFieldExtraInfo(\n            parseResult.ast,\n            cursorInfo,\n            opts.onSuggestTableFields,\n            parseResult.cursorKeyPath,\n          );\n          contents = await opts.onHoverTableField(\n            cursorInfo.token.value,\n            extraAfter,\n          );\n          break;\n        case 'tableName':\n          contents = await opts.onHoverTableName(cursorInfo as ICursorInfo);\n          break;\n        case 'functionName':\n          contents = await opts.onHoverFunctionName(cursorInfo.token.value);\n          break;\n        default:\n      }\n\n      return {\n        range: monaco.Range.fromPositions(\n          model.getPositionAt(cursorInfo.token.position[0]),\n          model.getPositionAt(cursorInfo.token.position[1] + 1),\n        ),\n        contents,\n      };\n    },\n  });\n}\n\n// 实例化一个 worker\nconst worker: Worker = new (MyWorker as any)();\n\nlet parserIndex = 0;\n\nconst asyncParser = async (\n  text: string,\n  index: number,\n  parserType: IParserType,\n) => {\n  parserIndex++;\n  const currentParserIndex = parserIndex;\n\n  let resolve: any = null;\n  let reject: any = null;\n\n  const promise = new Promise((promiseResolve, promiseReject) => {\n    resolve = promiseResolve;\n    reject = promiseReject;\n  });\n\n  worker.postMessage({ text, index, parserType });\n\n  worker.onmessage = (event) => {\n    if (currentParserIndex === parserIndex) {\n      resolve(event.data);\n    } else {\n      reject();\n    }\n  };\n\n  return promise as Promise<IParseResult>;\n};\n\nfunction returnCompletionItemsByVersion(\n  value: ICompletionItem[],\n  monacoVersion: IMonacoVersion,\n) {\n  switch (monacoVersion) {\n    case '0.13.2':\n      return value;\n    case '0.15.6':\n      return {\n        suggestions: value,\n      };\n    default:\n      throw Error('Not supported version');\n  }\n}\n\nfunction getSeverityByVersion(monaco: any, monacoVersion: IMonacoVersion) {\n  switch (monacoVersion) {\n    case '0.13.2':\n      return monaco.Severity.Error;\n    case '0.15.6':\n      return monaco.MarkerSeverity.Error;\n    default:\n      throw Error('Not supported version');\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/monaco-plugin/parser.worker.ts",
    "content": "import { mysqlParser } from '../sql-parser';\n\n// eslint-disable-next-line no-restricted-globals\nconst ctx: Worker = self as any;\n\nctx.onmessage = event => {\n  ctx.postMessage(mysqlParser(event.data.text, event.data.index));\n};\n\nexport default null as any;\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/base/define.ts",
    "content": "import { IToken } from '../../..';\n\nexport type IStatements = IStatement[];\n\nexport interface IStatement {\n  type: 'statement' | 'identifier';\n  variant: string;\n}\n\nexport interface ISelectStatement extends IStatement {\n  from: IFrom;\n  result: IResult[];\n}\n\nexport interface IResult extends IStatement {\n  name: IToken;\n  alias: IToken;\n}\n\nexport interface IFrom extends IStatement {\n  sources: ISource[];\n  where?: any;\n  group?: any;\n  having?: any;\n}\n\nexport interface ISource extends IStatement {\n  name: ITableInfo & IStatement;\n  alias: IToken;\n}\n\nexport interface ITableInfo {\n  tableName: IToken;\n  namespace: IToken;\n}\n\nexport interface ICompletionItem {\n  label: string;\n  kind?: string;\n  sortText?: string;\n  tableInfo?: ITableInfo;\n  groupPickerName?: string;\n  originFieldName?: string;\n  detail?: string;\n  documentation?: string;\n}\n\nexport type CursorType =\n  | 'tableField'\n  | 'tableName'\n  | 'namespace'\n  | 'namespaceOne'\n  | 'functionName'\n  | 'tableFieldAfterGroup';\n\nexport type ICursorInfo<T = {}> = {\n  token: IToken;\n  type: CursorType;\n} & T;\n\nexport type IGetFieldsByTableName = (\n  tableName: ITableInfo,\n  inputValue: string,\n  rootStatement: IStatement,\n) => Promise<ICompletionItem[]>;\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/base/four-operations.ts",
    "content": "/* eslint-disable no-use-before-define */\nimport { chain, ChainFunction, optional } from '../../..';\n\n// Four operations ---------------------------------\nexport function createFourOperations(field: ChainFunction) {\n  const addExpr = () => {\n    return chain(term, exprTail)(ast => {\n      return ast[0];\n    });\n  };\n\n  const exprTail = () => {\n    return chain(optional(addOp, term, exprTail))();\n  };\n\n  const term = () => {\n    return chain(factor, termTail)(ast => {\n      return ast[0];\n    });\n  };\n\n  const termTail = () => {\n    return chain(optional(mulOp, factor, termTail))();\n  };\n\n  const mulOp = () => {\n    return chain(['*', '/', '%'])(ast => {\n      return ast[0];\n    });\n  };\n\n  const addOp = () => {\n    return chain(['+', '-'])(ast => {\n      return ast[0];\n    });\n  };\n\n  const factor = () => {\n    return chain([\n      chain('(', addExpr, ')')(ast => {\n        return ast[1];\n      }),\n      field,\n    ])(ast => {\n      return ast[0];\n    });\n  };\n\n  return addExpr;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/base/parser.ts",
    "content": "/* eslint-disable no-use-before-define */\nimport { chain, many, matchTokenType, optional } from '../../..';\nimport { reserveKeys } from './reserve-keys';\n\n// ----------------------------------- Utils -----------------------------------\n\n// TODO: https://github.com/antlr/grammars-v4/blob/master/mysql/MySqlParser.g4#L1963\nexport const dataType = () => {\n  return chain([\n    chain(['char', 'varchar', 'tinytext', 'text', 'mediumtext', 'longtext'])(ast => {\n      return ast[0];\n    }),\n    chain(['tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint'])(ast => {\n      return ast[0];\n    }),\n    chain(['real', 'double', 'float'])(ast => {\n      return ast[0];\n    }),\n    chain(['decimal', 'numberic'])(ast => {\n      return ast[0];\n    }),\n    chain(['date', 'tinyblob', 'blob', 'mediumblob', 'longblob', 'bool', 'boolean'])(ast => {\n      return ast[0];\n    }),\n    chain(['bit', 'time', 'timestamp', 'datetime', 'binary', 'varbinary', 'year'])(ast => {\n      return ast[0];\n    }),\n    chain(['enum', 'set'])(ast => {\n      return ast[0];\n    }),\n    chain('geometrycollection', 'linestring', 'multilinestring', 'multipoint', 'multipolygon', 'point', 'polygon')(\n      ast => {\n        return ast[0];\n      },\n    ),\n  ])(ast => {\n    return ast[0];\n  });\n};\n\nexport const setValue = () => {\n  return chain(wordSym, '=', [stringSym, numberSym])();\n};\n\nexport const setValueList = () => {\n  return chain(setValue, many(',', setValue))();\n};\n\n// ----------------------------------- others -----------------------------------\n\nexport const wordSym = () => {\n  return chain([matchTokenType('cursor'), matchTokenType('word', { excludes: reserveKeys })])(ast => {\n    return ast[0];\n  });\n};\n\nexport const stringSym = () => {\n  return chain(matchTokenType('string'))(ast => {\n    return ast[0];\n  });\n};\n\nexport const numberSym = () => {\n  return chain(matchTokenType('number'))(ast => {\n    return ast[0];\n  });\n};\n\nexport const stringOrWord = () => {\n  return chain([wordSym, stringSym])(ast => {\n    return ast[0];\n  });\n};\n\nexport const stringOrWordOrNumber = () => {\n  return chain([wordSym, stringSym, numberChain])(ast => {\n    return ast[0];\n  });\n};\n\nexport const numberChain = () => {\n  return chain(optional(['-', '+']), numberSym)();\n};\n\nexport const logicalOperator = () => {\n  return chain(['and', '&&', 'xor', 'or', '||'])(ast => {\n    return ast[0];\n  });\n};\n\nexport const normalOperator = () => {\n  return chain(['&&', '||'])(ast => {\n    return ast[0];\n  });\n};\n\nexport const comparisonOperator = () => {\n  return chain(['=', '>', '<', '<=', '>=', '<>', '!=', '<=>'])(ast => {\n    return ast[0];\n  });\n};\n\nexport const notOperator = () => {\n  return chain(['not', '!'])(ast => {\n    return ast[0];\n  });\n};\n\nexport const selectSpec = () => {\n  return chain([\n    'all',\n    'distinct',\n    'distinctrow',\n    'high_priority',\n    'straight_join',\n    'sql_small_result',\n    'sql_big_result',\n    'sql_buffer_result',\n    'sql_cache',\n    'sql_no_cache',\n    'sql_calc_found_rows',\n  ])(ast => {\n    return ast[0];\n  });\n};\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/base/reader.ts",
    "content": "/* eslint-disable no-case-declarations */\nimport * as _ from 'lodash';\nimport { IToken } from '../../..';\nimport {\n  ICompletionItem,\n  ICursorInfo,\n  IGetFieldsByTableName,\n  ISelectStatement,\n  ISource,\n  IStatement,\n  IStatements,\n} from './define';\n\nexport async function getCursorInfo(rootStatement: IStatements, keyPath: string[]) {\n  if (!rootStatement) {\n    return null;\n  }\n\n  const cursorValue: IToken = _.get(rootStatement, keyPath);\n  const cursorKey = keyPath.slice().pop();\n  const parentStatement = _.get(rootStatement, keyPath.slice(0, keyPath.length - 1));\n\n  if (!parentStatement) {\n    return null;\n  }\n\n  return (await judgeStatement(parentStatement, async typePlusVariant => {\n    switch (typePlusVariant) {\n      case 'identifier.tableName':\n        return {\n          type: 'tableName',\n          variant: cursorKey,\n          token: cursorValue,\n          tableInfo: parentStatement,\n        };\n      case 'identifier.column':\n        if (cursorKey === 'name') {\n          return {\n            type: 'tableField',\n            token: cursorValue,\n          };\n        }\n        return null;\n\n      case 'identifier.columnAfterGroup':\n        return {\n          type: 'tableFieldAfterGroup',\n          token: cursorValue,\n          groupName: parentStatement.groupName.value,\n        };\n      case 'function':\n        return {\n          type: 'functionName',\n          token: cursorValue,\n        };\n      default:\n    }\n  })) as ICursorInfo;\n}\n\nexport function findNearestStatement(\n  rootStatement: IStatements,\n  keyPath: string[],\n  callback?: (value?: any) => boolean,\n): ISelectStatement {\n  if (!rootStatement) {\n    return null;\n  }\n\n  if (keyPath.length === 0) {\n    return null;\n  }\n\n  const value = _.get(rootStatement, keyPath);\n\n  if (!value) {\n    throw Error('Path not found from ast!');\n  }\n\n  if (!value.token && value.type === 'statement') {\n    if (callback) {\n      if (callback(value) === true) {\n        return value;\n      }\n    } else {\n      return value;\n    }\n  }\n\n  if (keyPath.length > 1) {\n    return findNearestStatement(rootStatement, keyPath.slice(0, keyPath.length - 1), callback);\n  }\n  return null;\n}\n\nexport async function getFieldsFromStatement(\n  rootStatement: IStatements,\n  cursorKeyPath: string[],\n  getFieldsByTableName: IGetFieldsByTableName,\n) {\n  const cursorInfo = await getCursorInfo(rootStatement, cursorKeyPath);\n  const cursorRootStatement = findNearestStatement(rootStatement, cursorKeyPath);\n\n  if (!cursorRootStatement) {\n    return [];\n  }\n\n  switch (cursorRootStatement.variant) {\n    // Select statement\n    case 'select':\n      return getFieldsByFromClauses(\n        cursorRootStatement,\n        _.get(cursorRootStatement, 'from.sources', []),\n        cursorInfo,\n        getFieldsByTableName,\n      );\n    // Join statement\n    // 字段是 source 表的（自带 + join 的表）\n    case 'join':\n      const parentCursorKeyPath = cursorKeyPath.slice();\n      parentCursorKeyPath.pop();\n\n      const parentSelectStatement = findNearestStatement(rootStatement, parentCursorKeyPath, eachStatement => {\n        return eachStatement.variant === 'select';\n      });\n\n      return getFieldsByFromClauses(\n        parentSelectStatement,\n        _.get(parentSelectStatement, 'from.sources', []),\n        cursorInfo,\n        getFieldsByTableName,\n      );\n    default:\n  }\n\n  return [];\n}\n\nasync function getFieldsByFromClauses(\n  rootStatement: IStatement,\n  fromStatements: IStatement[],\n  cursorInfo: ICursorInfo,\n  getFieldsByTableName: IGetFieldsByTableName,\n): Promise<ICompletionItem[]> {\n  const fields = await Promise.all(\n    fromStatements.map(fromStatement => {\n      return getFieldsByFromClause(rootStatement, fromStatement, cursorInfo, getFieldsByTableName);\n    }),\n  );\n\n  return _.flatten(fields).filter(item => {\n    return !!item;\n  });\n}\n\nasync function getFieldsByFromClause(\n  rootStatement: IStatement,\n  fromStatement: IStatement,\n  cursorInfo: ICursorInfo,\n  getFieldsByTableName: IGetFieldsByTableName,\n): Promise<ICompletionItem[]> {\n  return judgeStatement(fromStatement, async typePlusVariant => {\n    switch (typePlusVariant) {\n      case 'statement.tableSource':\n        // ignore joins\n        const tableSourceFields = await getFieldsByFromClause(\n          rootStatement,\n          (fromStatement as any).source,\n          cursorInfo,\n          getFieldsByTableName,\n        );\n        const joinsFields = _.isArray((fromStatement as any).joins)\n          ? await getFieldsByFromClauses(\n              rootStatement,\n              _.get(fromStatement, 'joins', []),\n              cursorInfo,\n              getFieldsByTableName,\n            )\n          : [];\n        return tableSourceFields.concat(joinsFields);\n      case 'statement.join':\n        return getFieldsByFromClause(rootStatement, (fromStatement as any).join, cursorInfo, getFieldsByTableName);\n      case 'identifier.table':\n        const itFromStatement = fromStatement as ISource;\n\n        let originFields = await getFieldsByTableName(itFromStatement.name, cursorInfo.token.value, rootStatement);\n        const tableNames: string[] = _.get(itFromStatement, 'name.tableNames', []);\n\n        let groupPickerName: string = null;\n        const tableNameAlias: string = _.get(itFromStatement, 'alias.value');\n\n        // 如果有 alias,直接作为 groupPickerName\n        if (tableNameAlias) {\n          groupPickerName = tableNameAlias;\n        } else {\n          // 实现的 tableNames 数量\n          let existKeyCount = 0;\n          tableNames.forEach(tableName => {\n            const eachTableName = _.get(itFromStatement, `name.${tableName}.value`);\n            if (eachTableName) {\n              // eslint-disable-next-line no-plusplus\n              existKeyCount++;\n              groupPickerName = eachTableName;\n            }\n          });\n\n          // 如果 existKeyCount 大于 1，则不提供 groupPickerName\n          if (existKeyCount > 1) {\n            groupPickerName = null;\n          }\n        }\n\n        originFields = originFields.map(originField => {\n          return {\n            ...originField,\n            tableInfo: itFromStatement.name,\n            // 如果仅有一个 tableNames 有值，就用那个作为 groupPickerName，否则没有\n            groupPickerName,\n            // existKeyCount\n            //     ? null\n            //     : _.get(itFromStatement, 'alias.value') || _.get(itFromStatement, 'name.tableName.value') || null,\n            originFieldName: originField.label,\n          };\n        });\n        return originFields;\n      case 'statement.select':\n        const ssFromStatement = fromStatement as ISelectStatement;\n\n        let statementSelectFields: ICompletionItem[] = [];\n\n        const fields = await getFieldsByFromClauses(\n          ssFromStatement,\n          ssFromStatement.from.sources,\n          cursorInfo,\n          getFieldsByTableName,\n        );\n\n        // If select *, return all fields\n        if (ssFromStatement.result.length === 1 && ssFromStatement.result[0].name.value === '*') {\n          statementSelectFields = fields.slice();\n        } else {\n          statementSelectFields = fields\n            .map(field => {\n              const selectedField = ssFromStatement.result.find(result => {\n                if (_.get(result.name, 'token') === true) {\n                  return result.name.value === field.label;\n                }\n\n                // Consider ${group}.${field}\n                if (\n                  _.get(result.name, 'type') === 'identifier' &&\n                  _.get(result.name, 'variant') === 'columnAfterGroup'\n                ) {\n                  return _.get(result.name, 'name.value') === field.label;\n                }\n\n                // Consider ${group}.*\n                if (_.get(result.name, 'type') === 'identifier' && _.get(result.name, 'variant') === 'groupAll') {\n                  return _.get(result.name, 'groupName.value') === field.groupPickerName;\n                }\n\n                return false;\n              });\n              if (!selectedField) {\n                return null;\n              }\n\n              if (selectedField.alias) {\n                return {\n                  ...field,\n                  label: selectedField.alias.value,\n                };\n              }\n              return field;\n            })\n            .filter(field => {\n              return field !== null;\n            })\n            .slice();\n        }\n\n        // If has alias, change\n        if (_.has(ssFromStatement, 'alias.value')) {\n          statementSelectFields = statementSelectFields.map(statementSelectField => {\n            return {\n              ...statementSelectField,\n              groupPickerName: _.get(ssFromStatement, 'alias.value'),\n            };\n          });\n        }\n\n        return statementSelectFields;\n      default:\n        return null;\n    }\n  });\n}\n\nasync function judgeStatement<T>(\n  statement: IStatement,\n  callback: (typePlusVariant?: string) => Promise<T>,\n): Promise<T> {\n  if (!statement) {\n    return null;\n  }\n\n  if (statement.variant) {\n    return callback(`${statement.type}.${statement.variant}`);\n  }\n  return callback(statement.type);\n}\n\nexport async function findFieldExtraInfo(\n  rootStatement: IStatements,\n  cursorInfo: ICursorInfo,\n  getFieldsByTableName: IGetFieldsByTableName,\n  fieldKeyPath: string[],\n): Promise<ICompletionItem> {\n  const fields = await getFieldsFromStatement(rootStatement, fieldKeyPath, getFieldsByTableName);\n  const field = fields.find(eachField => {\n    return eachField.label === cursorInfo.token.value;\n  });\n\n  if (!field) {\n    return null;\n  }\n\n  return field;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/base/reserve-keys.ts",
    "content": "export const reserveKeys = [\n  'select',\n  'create',\n  'insert',\n  'from',\n  'where',\n  'order',\n  'limit',\n  'by',\n  'sounds',\n  'like',\n  'is',\n  'in',\n  'between',\n  'regexp',\n  'isnull',\n  'lateral',\n  'left',\n  'right',\n  'inner',\n  'outer',\n  'join',\n];\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/base/utils.ts",
    "content": "export function isOkay(obj: any) {\n  return obj != null;\n}\n\nexport function flattenAll(arr: any[]) {\n  return arr\n    .filter(part => {\n      return isOkay(part);\n    })\n    .reduce((prev, cur) => {\n      return prev.concat(cur);\n    }, []);\n}\n\nexport function createTableName(tableNames: { [key: string]: string }) {\n  const returnValue: any = {\n    type: 'identifier',\n    variant: 'tableName',\n  };\n\n  Object.keys(tableNames).forEach(eachTableNamesKey => {\n    returnValue[eachTableNamesKey] = tableNames[eachTableNamesKey];\n  });\n\n  returnValue.tableNames = Object.keys(tableNames);\n\n  return returnValue;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/index.tsx",
    "content": "import * as reader from './base/reader';\n\nexport { reader };\nexport * from './base/define';\n\nexport { mysqlParser } from './mysql';\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/mysql/index.ts",
    "content": "import { createParser } from '../../..';\nimport { IStatements } from '../base/define';\nimport { sqlTokenizer } from './lexer';\nimport { root } from './parser';\n\nexport const mysqlParser = createParser<IStatements>(root, sqlTokenizer, {\n  cursorTokenExcludes: token => {\n    return token.value === '.' || token.value === ':';\n  },\n});\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/mysql/lexer.ts",
    "content": "import { createLexer } from '../../..';\n\nexport const sqlTokenizer = createLexer([\n  {\n    type: 'whitespace',\n    regexes: [/^(\\s+)/],\n    ignore: true,\n  },\n  {\n    type: 'comment',\n    regexes: [\n      /^((?:#|--).*?(?:\\n|$))/, // # --\n      /^(\\/\\*[^]*?(?:\\*\\/|$))/, // /* */\n    ],\n    ignore: true,\n  },\n  {\n    type: 'number',\n    regexes: [/^([0-9]+(\\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)\\b/],\n  },\n  {\n    type: 'word',\n    regexes: [\n      /^([a-zA-Z0-9_]+)/, // word\n      /^(\\$\\{[a-zA-Z0-9_]+\\})/, // ${word}\n    ],\n  },\n  {\n    type: 'string',\n    regexes: [\n      /^((?=\")(?:\"[^\"\\\\]*(?:\\\\[\\s\\S][^\"\\\\]*)*\"))/, // \"\"\n      /^((?=')(?:'[^'\\\\]*(?:\\\\[\\s\\S][^'\\\\]*)*'))/, // ''\n      /^((?=`)(?:`[^`\\\\]*(?:\\\\[\\s\\S][^`\\\\]*)*`))/, // ``\n    ],\n  },\n  {\n    type: 'special',\n    regexes: [\n      /^(\\(|\\))/, // '(' ')'.\n      /^(!=|<>|==|<=|>=|!<|!>|\\|\\||::|->>|->|~~\\*|~~|!~~\\*|!~~|~\\*|!~\\*|!~|.)/, // operators.\n    ],\n  },\n]);\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/syntax-parser/plugin/sql-parser/mysql/parser.ts",
    "content": "/* eslint-disable no-use-before-define */\nimport { chain, many, optional, plus } from '../../..';\nimport { createFourOperations } from '../base/four-operations';\nimport {\n  comparisonOperator,\n  dataType,\n  logicalOperator,\n  normalOperator,\n  notOperator,\n  numberSym,\n  selectSpec,\n  setValueList,\n  stringOrWord,\n  stringOrWordOrNumber,\n  stringSym,\n  wordSym,\n} from '../base/parser';\nimport { createTableName, flattenAll } from '../base/utils';\n\nexport const root = () => {\n  return chain(statements, optional(';'))(ast => {\n    return ast[0];\n  });\n};\n\nconst statements = () => {\n  return chain(\n    statement,\n    many(\n      chain(';', statement)(ast => {\n        return ast[1];\n      }),\n    ),\n  )(flattenAll);\n};\n\nconst statement = () => {\n  return chain([\n    selectStatement,\n    createTableStatement,\n    insertStatement,\n    createViewStatement,\n    setStatement,\n    createIndexStatement,\n    createFunctionStatement,\n    updateStatement,\n  ])(ast => {\n    return ast[0];\n  });\n};\n\n// ----------------------------------- select statement -----------------------------------\n\nconst selectStatement = () => {\n  return chain(\n    'select',\n    selectList,\n    optional(fromClause),\n    optional(orderByClause),\n    optional(limitClause),\n    optional(union, selectStatement),\n  )(ast => {\n    const result: any = {\n      type: 'statement',\n      variant: 'select',\n      result: ast[1],\n      from: ast[2],\n    };\n\n    if (ast[5]) {\n      // eslint-disable-next-line prefer-destructuring\n      result.union = ast[5];\n    }\n\n    return result;\n  });\n};\n\nconst union = () => {\n  return chain('union', ['all', 'distinct'])();\n};\n\nconst fromClause = () => {\n  return chain('from', tableSources, optional(whereStatement), optional(groupByStatement), optional(havingStatement))(\n    ast =>\n      // TODO: Ignore where group having\n      {\n        return {\n          sources: ast[1],\n          where: ast[2],\n          group: ast[3],\n          having: ast[4],\n        };\n      },\n  );\n};\n\nconst selectList = () => {\n  return chain(selectField, many(selectListTail))(flattenAll);\n};\n\nconst selectListTail = () => {\n  return chain(',', selectField)(ast => {\n    return ast[1];\n  });\n};\n\n// selectField\n//         ::= not? field alias?\n//         ::= not? ( field ) alias?\n//           | *\nconst selectField = () => {\n  return chain([\n    chain(\n      many('not'),\n      [\n        chain(field, optional(overClause))(ast =>\n          // TODO: Ignore overClause\n          {\n            return ast[0];\n          },\n        ),\n        chain('(', field, ')')(),\n      ],\n      optional(alias),\n    )(ast => {\n      return {\n        type: 'identifier',\n        variant: 'column',\n        name: ast[1],\n        alias: ast[2],\n      };\n    }),\n    '*',\n  ])(ast => {\n    return ast[0];\n  });\n};\n\nconst whereStatement = () => {\n  return chain('where', expression)(ast => {\n    return ast[1];\n  });\n};\n\n// fieldList\n//       ::= field (, fieldList)?\nconst fieldList = () => {\n  return chain(columnField, many(',', columnField))();\n};\n\nconst tableSources = () => {\n  return chain(\n    tableSource,\n    many(\n      chain(',', tableSource)(ast => {\n        return ast[1];\n      }),\n    ),\n  )(flattenAll);\n};\n\nconst tableSource = () => {\n  return chain(tableSourceItem, many(joinPart))(ast => {\n    return {\n      source: ast[0],\n      joins: ast[1],\n      type: 'statement',\n      variant: 'tableSource',\n    };\n  });\n};\n\nconst tableSourceItem = () => {\n  return chain([\n    chain(tableName, optional(alias))(ast => {\n      return {\n        type: 'identifier',\n        variant: 'table',\n        name: ast[0],\n        alias: ast[1],\n      };\n    }),\n    chain(\n      [\n        selectStatement,\n        chain('(', selectStatement, ')')(ast => {\n          return ast[1];\n        }),\n      ],\n      alias,\n    )(ast => {\n      return {\n        ...ast[0],\n        alias: ast[1],\n      };\n    }),\n  ])(ast => {\n    return ast[0];\n  });\n};\n\nconst joinPart = () => {\n  return chain(\n    [\n      'join',\n      'straight_join',\n      chain(['inner', 'cross', 'full'], 'join')(),\n      chain(['left', 'right'], optional('outer'), 'join')(),\n      chain('natural', optional(['left', 'right'], optional('outer')), 'join')(),\n    ],\n    tableSourceItem,\n    optional('on', expression),\n  )(ast => {\n    return {\n      type: 'statement',\n      variant: 'join',\n      join: ast[1],\n      conditions: ast[2],\n    };\n  });\n};\n\n// Alias ::= AS WordOrString\n//         | WordOrString\nconst alias = () => {\n  return chain([\n    chain('as', stringOrWord)(ast => {\n      return ast[1];\n    }),\n    stringOrWord,\n  ])(ast => {\n    return ast[0];\n  });\n};\n\n// ----------------------------------- Create table statement -----------------------------------\nconst createTableStatement = () => {\n  return chain('create', 'table', stringOrWord, '(', tableOptions, ')', optional(withStatement))();\n};\n\nconst withStatement = () => {\n  return chain('with', '(', withStatements, ')')();\n};\n\nconst withStatements = () => {\n  return chain(withStatementsTail, many(',', withStatementsTail))();\n};\n\nconst withStatementsTail = () => {\n  return chain(wordSym, '=', stringSym)();\n};\n\nconst tableOptions = () => {\n  return chain(tableOption, many(',', tableOption))();\n};\n\nconst tableOption = () => {\n  return chain(stringOrWord, dataType)();\n};\n\nconst primaryKeyList = () => {\n  return chain(wordSym, optional(',', primaryKeyList))();\n};\n\nconst tableName = () => {\n  return chain([\n    chain(stringOrWord)(),\n    chain(stringOrWord, '.', stringOrWord)(ast => {\n      return [ast[0], ast[2]];\n    }),\n  ])(ast => {\n    if (ast[0].length === 1) {\n      return createTableName({\n        namespace: null,\n        tableName: ast[0][0],\n      });\n    }\n    if (ast[0].length === 2) {\n      return createTableName({\n        namespace: ast[0][0],\n        tableName: ast[0][1],\n      });\n    }\n  });\n};\n\n// ----------------------------------- Having --------------------------------------------------\nconst havingStatement = () => {\n  return chain('having', expression)();\n};\n\n// ----------------------------------- Create view statement -----------------------------------\nconst createViewStatement = () => {\n  return chain('create', 'view', wordSym, 'as', selectStatement)();\n};\n\n// ----------------------------------- Insert statement -----------------------------------\nconst insertStatement = () => {\n  return chain('insert', optional('ignore'), 'into', tableName, optional(selectFieldsInfo), [selectStatement])(ast => {\n    return {\n      type: 'statement',\n      variant: 'insert',\n      into: {\n        type: 'indentifier',\n        variant: 'table',\n        name: ast[3],\n      },\n      result: ast[5],\n    };\n  });\n};\n\nconst selectFieldsInfo = () => {\n  return chain('(', selectFields, ')')();\n};\n\nconst selectFields = () => {\n  return chain(wordSym, many(',', wordSym))();\n};\n\n// ----------------------------------- groupBy -----------------------------------\nconst groupByStatement = () => {\n  return chain('group', 'by', fieldList)();\n};\n\n// ----------------------------------- orderBy -----------------------------------\nconst orderByClause = () => {\n  return chain('order', 'by', orderByExpressionList)();\n};\n\nconst orderByExpressionList = () => {\n  return chain(orderByExpression, many(',', orderByExpression))();\n};\n\nconst orderByExpression = () => {\n  return chain(expression, optional(['asc', 'desc']))();\n};\n\n/*\n<PARTITION BY clause> ::=  \nPARTITION BY value_expression , ... [ n ] \n*/\n\nconst partitionByClause = () => {\n  return chain([wordSym, chain('partition', 'by', expression)()])(ast => {\n    return ast;\n  });\n};\n\n/*\nOVER (   \n       [ <PARTITION BY clause> ]  \n       [ <ORDER BY clause> ]   \n       [ <ROW or RANGE clause> ]  \n      )  \n*/\nconst overClause = () => {\n  return chain('over', '(', overTailExpression, ')')();\n};\n\nconst overTailExpression = () => {\n  return chain([partitionByClause, chain(field, orderByClause)()], many(',', overTailExpression))();\n};\n\n// ----------------------------------- limit -----------------------------------\nconst limitClause = () => {\n  return chain('limit', [numberSym, chain(numberSym, ',', numberSym)(), chain(numberSym, 'offset', numberSym)()])();\n};\n\n// ----------------------------------- Function -----------------------------------\nconst functionChain = () => {\n  return chain([castFunction, normalFunction, ifFunction])(ast => {\n    return ast[0];\n  });\n};\n\nconst ifFunction = () => {\n  return chain('if', '(', predicate, ',', field, ',', field, ')')(ast => {\n    return {\n      type: 'function',\n      name: 'if',\n      args: [ast[2], ast[4], ast[6]],\n    };\n  });\n};\n\nconst castFunction = () => {\n  return chain('cast', '(', field, 'as', dataType, ')')(ast => {\n    return {\n      type: 'function',\n      name: 'cast',\n      args: [ast[2], ast[4]],\n    };\n  });\n};\n\nconst normalFunction = () => {\n  return chain(wordSym, '(', optional(functionFields), ')', optional('filter', '(', whereStatement, ')'))(ast => {\n    return {\n      type: 'function',\n      name: ast[0],\n      args: ast[2],\n    };\n  });\n};\n\nconst functionFields = () => {\n  return chain(functionFieldItem, many(',', functionFieldItem))();\n};\n\nconst functionFieldItem = () => {\n  return chain(many(selectSpec), [columnField, caseStatement])(ast => {\n    return ast;\n  });\n};\n\n// ----------------------------------- Case -----------------------------------\nconst caseStatement = () => {\n  return chain('case', plus(caseAlternative), optional('else', [columnField, 'null']), [\n    'end',\n    chain('end', 'as', wordSym)(),\n  ])();\n};\n\nconst caseAlternative = () => {\n  return chain('when', expression, 'then', fieldItem)();\n};\n\n// ----------------------------------- set statement -----------------------------------\n\nconst setStatement = () => {\n  return chain('set', variableAssignments)();\n};\n\nconst variableAssignments = () => {\n  return chain(variableAssignment, many(',', variableAssignment))();\n};\n\nconst variableAssignment = () => {\n  return chain(variableLeftValue, '=', ['true', 'false', stringSym, numberSym])();\n};\n\nconst variableLeftValue = () => {\n  return chain(wordSym, many('.', wordSym))();\n};\n\n// ----------------------------------- Expression -----------------------------------\n\n/*\n * expr:\n *   expr OR expr\n * | expr || expr\n * | expr XOR expr\n * | expr AND expr\n * | expr && expr\n * | NOT expr\n * | ! expr\n * | boolean_primary IS [NOT] {TRUE | FALSE | UNKNOWN}\n * | boolean_primary\n * */\n\nconst expression = () => {\n  return chain(expressionHead, many(logicalOperator, expression))(flattenAll);\n};\n\nconst expressionHead = () => {\n  return chain(\n    [chain('(', expression, ')')(), chain(notOperator, expression)(), chain(booleanPrimary)],\n    optional(chain('is', optional('not'), ['true', 'false', 'unknown'])()),\n  )(ast => {\n    return ast[0];\n  });\n};\n\n// /*\n//  *boolean_primary:\n//  *   boolean_primary IS [NOT] NULL\n//  * | boolean_primary <=> predicate\n//  * | boolean_primary comparison_operator predicate\n//  * | boolean_primary comparison_operator {ALL | ANY} (subquery)\n//  * | predicate\n// **/\nconst booleanPrimary = () => {\n  return chain(predicate, many(['isnull', chain([chain('is', 'not')(), 'is', 'not'], ['null', columnField])()]))(\n    flattenAll,\n  );\n};\n\n/*\n * predicate:\n *    field SOUNDS LIKE field\n *  | field [NOT] IN (subquery)\n *  | field [NOT] IN (expr [, expr] ...)\n *  | field [NOT] BETWEEN field AND predicate\n *  | field [NOT] LIKE simple_expr [ESCAPE simple_expr]\n *  | field [NOT] REGEXP field\n *  | field\n * */\nconst predicate = () => {\n  return chain([\n    chain(columnField, predicateAddonComparison)(),\n    chain('(', predicate, ')', predicateAddonComparison)(),\n  ])();\n};\n\nconst predicateAddonComparison = () => {\n  return chain(\n    optional([chain(comparisonOperator, columnField)(), chain('sounds', 'like', columnField)(), isOrNotExpression]),\n    optional(['or', predicate]),\n  )();\n};\n\nconst columnField = () => {\n  return chain(field)(ast => {\n    return {\n      type: 'identifier',\n      variant: 'column',\n      name: ast[0],\n    };\n  });\n};\n\nconst isOrNotExpression = () => {\n  return chain(optional('is'), optional('not'), [\n    chain('in', '(', [selectStatement, fieldList], ')')(),\n    chain('between', field, 'and', predicate)(),\n    chain('like', field, optional('escape', field))(),\n    chain('regexp', field)(),\n    'null',\n  ])();\n};\n\nconst fieldItem = () => {\n  return chain(fieldItemDetail, many(normalOperator, fieldItemDetail))(ast => {\n    if (!ast[1]) {\n      return ast[0];\n    }\n    return [ast[0], ast[1]];\n  });\n};\n\nconst fieldItemDetail = () => {\n  return chain([\n    functionChain,\n    caseStatement,\n    chain(\n      stringOrWordOrNumber,\n      optional([\n        chain('.', '*')(ast => {\n          return {\n            type: 'identifier',\n            variant: 'groupAll',\n          };\n        }),\n        chain(':', normalFunction)(),\n        dotStringOrWordOrNumber,\n      ]),\n    )(ast => {\n      if (!ast[1]) {\n        return ast[0];\n      }\n\n      return {\n        ...ast[1],\n        groupName: ast[0],\n      };\n    }),\n    '*',\n  ])(ast => {\n    return ast[0];\n  });\n};\n\nconst dotStringOrWordOrNumber = () => {\n  return chain('.', [\n    stringSym,\n    numberSym,\n    chain(wordSym)(ast => {\n      return {\n        type: 'identifier',\n        variant: 'columnAfterGroup',\n        name: ast[0],\n      };\n    }),\n  ])(ast => {\n    return ast[1];\n  });\n};\n\nconst field = () => {\n  return createFourOperations(fieldItem)();\n};\n\n// ----------------------------------- create index expression -----------------------------------\nconst createIndexStatement = () => {\n  return chain('create', 'index', indexItem, onStatement, whereStatement)();\n};\n\nconst indexItem = () => {\n  return chain(stringSym, many('.', stringSym))();\n};\n\nconst onStatement = () => {\n  return chain('ON', stringSym, '(', fieldForIndexList, ')')();\n};\n\nconst fieldForIndex = () => {\n  return chain(stringSym, optional(['ASC', 'DESC']))();\n};\n\nconst fieldForIndexList = () => {\n  return chain(fieldForIndex, many(',', fieldForIndex))();\n};\n\n// ----------------------------------- create function expression -----------------------------------\nconst createFunctionStatement = () => {\n  return chain('create', 'function', wordSym, 'as', stringSym)();\n};\n\n// ----------------------------------- update statement -----------------------------------\nconst updateStatement = () => {\n  return chain('UPDATE', tableSourceItem, 'SET', setValueList, optional(whereStatement))();\n};\n"
  },
  {
    "path": "chat2db-client/src/components/MonacoEditor/useMonacoTheme.ts",
    "content": "import { useEffect } from 'react';\nimport {useTheme} from '@/hooks/useTheme';\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api';\nimport { ThemeType } from '@/constants';\n \n// 如果用户点击的不是可复制的元素，就清空选中的内容\nfunction useMonacoTheme() {\n  const [appTheme] = useTheme();\n  // 监听主题色变化切换编辑器主题色\n  useEffect(() => {\n    const { colorPrimary, colorBgBase, colorTextBase  } = window._AppThemePack || {};\n\n    const colors = {\n      'editor.lineHighlightBackground': colorPrimary + '14', // 当前行背景色\n      'editor.selectionBackground': colorPrimary + '50', // 选中文本的背景色\n      // 'editorLineNumber.foreground': colorPrimary, // 行号颜色\n      'editorLineNumber.activeForeground': colorPrimary, // 当前行号颜色\n      // 'editorCursor.foreground': colorPrimary, // 光标颜色\n      'editorRuler.foreground': colorPrimary + '15', // 右侧竖线颜色\n      'editor.foreground': colorTextBase, // 文本颜色\n      'editor.background': colorBgBase, //背景色\n    };\n\n    monaco.editor.defineTheme(appTheme.backgroundColor, {\n      // base 如果appTheme.backgroundColor包含dark则为vs-dark，否则为vs\n      base: appTheme.backgroundColor.includes(ThemeType.Dark) ? 'vs-dark' : 'vs',\n      inherit: true, // 是否继承vscode默认主题\n      rules: [{ background: '#15161a' }] as any,\n      colors,\n    });\n    \n    monaco.editor.setTheme(appTheme.backgroundColor);\n  }, [appTheme.backgroundColor]);\n\n}\n\nexport default useMonacoTheme;\n"
  },
  {
    "path": "chat2db-client/src/components/MyNotification/index.less",
    "content": "@import '../../styles/var.less';\n\n.message {\n  display: flex;\n  align-items: center;\n  padding: 0px 8px;\n  i {\n    margin-right: 10px;\n    color: var(--color-error-text);\n  }\n}\n\n.modal{\n  position: relative;\n  h3{\n    background-color: #fff;\n    position: sticky;\n    top: 0;\n  }\n  :global{\n    .ant-modal-header{\n      padding: 8px 10px;\n    }\n    .ant-modal-body{\n      padding: 0px 10px 10px;\n    }\n    .ant-modal-content{\n      max-height: 80vh;\n      display: flex;\n      flex-direction: column;\n      .ant-modal-header,.ant-modal-footer{\n        flex-shrink: 0;\n      }\n      .ant-modal-body{\n        flex: 1;\n        height: 0;\n        overflow-y: auto;\n      }\n    }\n  }\n}\n\n.modalTitle{\n  display: flex;\n  align-items: center;\n  \n  i{\n    font-size: 16px ;\n    font-weight: 400;\n    color: var(--color-error-text);\n    margin-right: 10px;\n  }\n}\n\n.modalFooter{\n  text-align: start;\n  cursor: pointer;\n  &:hover{\n    color: var(--color-primary)\n  }\n  .copyErrorTips{\n    color: var(--color-error-text);\n  }\n}\n\n.description {\n  .f-lines(2);\n  word-break: break-all;\n  padding: 0px 10px;\n}\n\n.notification{\n  z-index: 999 !important;\n  width: 300px !important;\n  font-size: 12px !important;\n  padding: 4px !important;\n  :global{\n    .ant-notification-notice-close{\n      top: 8px !important;\n      inset-inline-end: 8px !important;\n    }\n  } \n}\n\n.errorDetail{\n  white-space: pre;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/MyNotification/index.tsx",
    "content": "import React, { useCallback, useState } from 'react';\nimport { Button, message, Modal, notification, Space } from 'antd';\nimport i18n from '@/i18n';\nimport { IconType } from 'antd/es/notification/interface';\nimport Iconfont from '../Iconfont';\nimport { copy, getApplicationMessage } from '@/utils';\nimport styles from './index.less';\n\ninterface IProps {\n  type?: IconType;\n  message?: React.ReactNode;\n  /** 错误代码 */\n  errorCode: string;\n  /** 错误信息 */\n  errorMessage: string;\n  /** 错误详情 */\n  errorDetail: string;\n  /** 问题wiki路径 */\n  solutionLink: string;\n  /** 请求的接口 */\n  requestUrl: string;\n  /** 请求的参数 */\n  requestParams?: string;\n}\n\nfunction MyNotification() {\n  const [notificationApi, notificationDom] = notification.useNotification({\n    maxCount: 2,\n  });\n  const [open, setOpen] = useState(false);\n  const [props, setProps] = useState<IProps>();\n\n  window._notificationApi = useCallback((myProps: IProps) => {\n    const { errorCode, errorMessage, solutionLink } = myProps;\n    setProps(myProps);\n    const btn = (\n      <Space>\n        <Button\n          type=\"link\"\n          size=\"small\"\n          onClick={() => {\n            setOpen(true);\n          }}\n        >\n          {i18n('common.notification.detail')}\n        </Button>\n        {solutionLink && (\n          <Button type=\"link\" size=\"small\" target=\"_blank\" href={solutionLink}>\n            {i18n('common.notification.solution')}\n          </Button>\n        )}\n      </Space>\n    );\n\n    const renderDescription = () => {\n      return (\n        <div className={styles.description}>\n          {errorCode} {errorMessage}\n        </div>\n      );\n    };\n\n    const renderMessage = () => {\n      return (\n        <div className={styles.message}>\n          <Iconfont code=\"&#xe60c;\" />\n          Error\n        </div>\n      );\n    };\n\n    notificationApi.open({\n      className: styles.notification,\n      message: renderMessage(),\n      description: renderDescription(),\n      placement: 'bottomRight',\n      btn,\n    });\n  }, []);\n\n  function renderModalTitle() {\n    const list = [props?.errorCode, props?.errorMessage];\n    return <div className={styles.modalTitle}>{list.filter((t) => t).join(':')}</div>;\n  }\n\n  function copyError() {\n    const errorMessage = {\n      getApplicationMessage: getApplicationMessage(),\n      ...props,\n    };\n    copy(JSON.stringify(errorMessage));\n    message.success(i18n('common.button.copySuccessfully'));\n  }\n\n  function renderModalFooter() {\n    if (props?.requestParams) {\n      return (\n        <div className={styles.modalFooter} onClick={copyError}>\n          <Iconfont code=\"&#xeb4e;\" />\n          {i18n('common.button.copyError')}\n          <span className={styles.copyErrorTips}>{i18n('common.button.copyErrorTips')}</span>\n        </div>\n      );\n    }\n    return false;\n  }\n\n  return (\n    <>\n      {notificationDom}\n      <Modal\n        className={styles.modal}\n        title={renderModalTitle()}\n        open={open}\n        width=\"70vw\"\n        footer={renderModalFooter()}\n        onCancel={() => {\n          setOpen(false);\n        }}\n        zIndex={99999}\n      >\n        <div className={styles.errorDetail}>{props?.errorDetail}</div>\n      </Modal>\n    </>\n  );\n}\n\nexport default MyNotification;\n"
  },
  {
    "path": "chat2db-client/src/components/Output/index.less",
    "content": "@import '../../styles/var.less';\n\n.output {\n  width: 100%;\n  height: 100%;\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  .outputTitle {\n    position: sticky;\n    top: 0;\n    z-index: 1;\n    display: flex;\n    align-items: center;\n\n    line-height: 32px;\n    padding: 0px 10px;\n    font-weight: bold;\n    border-bottom: 1px solid var(--color-border);\n    i {\n      margin-right: 6px;\n    }\n  }\n  .outputContent {\n    padding: 10px;\n    overflow-y: auto;\n    flex: 1;\n    height: 0px;\n    .outputItem {\n      .timeBox {\n        display: flex;\n        align-items: center;\n      }\n      .timeSpan {\n        margin-right: 4px;\n        font-weight: 500;\n      }\n      .timeBoxIcon {\n        transform: rotate(90deg);\n        margin-right: 4px;\n        i {\n          color: var(--color-success);\n        }\n        &:hover {\n          cursor: default;\n          background-color: transparent;\n        }\n      }\n      .failureIconBox {\n        i {\n          color: var(--color-error);\n        }\n      }\n      > div {\n        line-height: 22px;\n      }\n      padding: 2px 0px;\n    }\n  }\n\n  .executedDatabaseBox,.sqlBox{\n    display: flex;\n    .iconBox{\n      margin-right: 4px;\n      flex-shrink: 0;\n    }\n  }\n\n  .executedDatabaseBox{\n    .executedDatabase {\n      color: var(--color-warning-text);\n      white-space: nowrap;\n    }\n  }\n  // .sqlBox {\n  //   .sqlContent{\n  //   }\n  // }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/Output/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport Iconfont from '@/components/Iconfont';\nimport ScrollLoading from '@/components/ScrollLoading';\nimport historyService, { IHistoryRecord } from '@/service/history';\nimport * as monaco from 'monaco-editor';\nimport i18n from '@/i18n';\nimport { copy } from '@/utils';\nimport { createConsole } from '@/pages/main/workspace/store/console';\nimport { Popover } from 'antd';\n\ninterface IProps {\n  className?: string;\n}\n\ninterface IDatasource extends IHistoryRecord {\n  highlightedCode: string; // sql处理过的高亮代码\n}\n\nexport default memo<IProps>((props) => {\n  const { className } = props;\n  const [dataSource, setDataSource] = React.useState<IDatasource[]>([]);\n  const outputContentRef = React.useRef<HTMLDivElement>(null);\n  const curPageRef = React.useRef(1);\n  const finishedRef = React.useRef(false);\n\n  const getHistoryList = () => {\n    return historyService\n      .getHistoryList({\n        // dataSourceId:props.curWorkspaceParams.dataSourceId,\n        pageNo: curPageRef.current++,\n        pageSize: 40,\n      })\n      .then((res) => {\n        finishedRef.current = !res.hasNextPage;\n        const promiseList = res.data.map((item) => {\n          return new Promise((resolve) => {\n            // 不换行\n            // const ddl = (item.ddl || '')?.replace(/\\n/g, ' ');\n            const ddl = item.ddl || '';\n            monaco.editor.colorize(ddl, 'sql', {}).then((_res) => {\n              resolve({\n                ...item,\n                highlightedCode: _res,\n              });\n            });\n          });\n        });\n        Promise.all(promiseList).then((_res) => {\n          setDataSource([...dataSource, ..._res] as any);\n        });\n      });\n  };\n\n  const copySql = (text: IDatasource['ddl']) => {\n    copy(text || '');\n  }\n\n  const openSql = (data: IDatasource) => {\n    createConsole({\n      ddl: data.ddl || '',\n      dataSourceId: data.dataSourceId!,\n      dataSourceName: data.dataSourceName!,\n      databaseType: data.type!,\n      databaseName: data.databaseName!,\n      schemaName: data.schemaName!,\n      // operationType: WorkspaceTabType,\n    })\n  }\n\n  return (\n    <div className={classnames(styles.output, className)}>\n      <div className={styles.outputTitle}>\n        {/* <Iconfont code=\"&#xe8ad;\" /> */}\n        {i18n('common.title.executiveLogging')}\n      </div>\n      <div className={styles.outputContent} ref={outputContentRef}>\n        <ScrollLoading\n          onReachBottom={getHistoryList}\n          scrollerElement={outputContentRef}\n          threshold={300}\n          finished={finishedRef.current}\n        >\n          <>\n            {dataSource.map((item, index) => {\n              const nameList = [item.dataSourceName, item.databaseName, item.schemaName];\n              return (\n                <div key={index} className={styles.outputItem}>\n                  <div className={styles.timeBox}>\n                    <Iconfont classNameBox={classnames(styles.timeBoxIcon, { [styles.failureIconBox]: item.status !== 'success' })} box boxSize={20} code=\"&#xe650;\" />\n                    <span className={styles.timeSpan}>[{item.gmtCreate}]</span>\n                    {/* {!!item.operationRows && <span>{item.operationRows} rows</span>} */}\n                    {!!item.useTime && <span>{i18n('common.text.executionTime', item.useTime)}</span>}\n                  </div>\n                  <div className={styles.executedDatabaseBox}>\n                    <Popover mouseEnterDelay={0.8} content={i18n('workspace.tips.openExecutiveLogging')}>\n                      <Iconfont classNameBox={styles.iconBox} box boxSize={20} code=\"&#xe6bb;\" onClick={()=>{openSql(item)}} />\n                    </Popover>\n                    <div className={styles.executedDatabase}>\n                      {nameList.filter((name) => name).join(' > ')}\n                    </div>\n                  </div>\n                  <div className={styles.sqlBox}>\n                    <Popover mouseEnterDelay={0.8} content={i18n('common.button.copy')}>\n                      <Iconfont classNameBox={styles.iconBox} box boxSize={20} code=\"&#xec7a;\" onClick={()=>{copySql(item.ddl)}} />\n                    </Popover>\n                    <div className={styles.sqlContent} dangerouslySetInnerHTML={{ __html: item.highlightedCode }} />\n                  </div>\n                </div>\n              );\n            })}\n          </>\n        </ScrollLoading>\n      </div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/Popularize/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n  width: 100%;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n}\n\n.title {\n  font-size: 16px;\n  font-weight: bold;\n  text-align: center;\n}\n\n.text {\n  p {\n    text-align: center;\n    font-size: 14px;\n    margin-top: 10px;\n  }\n}\n\n.wechatImg {\n  margin: 20px auto 0px;\n  width: 160px;\n  height: 160px;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/Popularize/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport i18n from '@/i18n';\n\ninterface IProps {\n  className?: string;\n  source?: 'setting';\n  imageUrl?: string;\n  tip?: string;\n}\nconst url =\n  'http://oss.sqlgpt.cn/static/chat2db-wechat.jpg?x-oss-process=image/auto-orient,1/resize,m_lfit,w_256/quality,Q_80/format,webp';\nexport default memo<IProps>(function Popularize(props) {\n  const { className } = props;\n\n  const renderTip = () => {\n    if (props.tip) {\n      return <p>{props.tip}</p>;\n    }\n    let dom;\n    if (props.source === 'setting') {\n      dom = <p>{'关注公众号获取AI Key'}</p>;\n    } else {\n      dom = <p>{i18n('common.text.wechatPopularizeAi')}</p>;\n    }\n    return dom;\n  };\n\n  const renderImage = () => {\n    if (!props.source && !props.imageUrl) {\n      return null;\n    }\n    return <img className={styles.wechatImg} src={props.source ? url : props.imageUrl} />;\n  };\n  \n  return (\n    <div className={classnames(styles.box, className)}>\n      {/* <div className={styles.title}>获取更多次数</div> */}\n      {renderImage()}\n      <div className={styles.text}>{renderTip()}</div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/RefreshLoadingButton/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n}\n"
  },
  {
    "path": "chat2db-client/src/components/RefreshLoadingButton/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport Iconfont from '@/components/Iconfont';\nimport { Spin } from 'antd';\n\ninterface IProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {\n  className?: string;\n  loading: boolean;\n}\n\nexport default memo<IProps>((props) => {\n  const { className, loading, ...res } = props;\n  return (\n    <div {...res} className={classnames(styles.box, className)}>\n      {loading && <Spin size=\"small\" />}\n      {!loading && <Iconfont code=\"&#xec08;\" />}\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/ScrollLoading/index.less",
    "content": "@import '../../styles/var.less';\n\n.box{\n  .tips{\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    height: 50px;\n  }\n  // .loading{\n  //   transform: scale(0.8);\n  // }\n}"
  },
  {
    "path": "chat2db-client/src/components/ScrollLoading/index.tsx",
    "content": "import React, { memo, useRef, useCallback, useEffect } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Spin } from 'antd';\n\n\ninterface IProps {\n  className?: string;\n  children?: React.ReactChild | false; // 滚动的内容\n  onReachBottom: () => Promise<unknown>; // 触底的数据请求\n  threshold: number; // 触底阈值\n  scrollerElement: React.MutableRefObject<any>; // overflow：scroll 的盒子\n  finished: boolean; // 是否结束\n}\n\nexport default memo<IProps>(({ className, children, scrollerElement, threshold, onReachBottom, finished }) => {\n  const scroller = scrollerElement.current;\n  const scrollerRef = useRef(scroller);\n  const pendingRef = useRef(false);\n  const finishedRef = useRef(false);\n  const onBoxMounted = useRef(null);\n  const onReachBottomRef = useRef(onReachBottom);\n\n  useEffect(() => {\n    scrollerRef.current = scrollerElement.current;\n    replenishData(onBoxMounted.current!, scrollerElement.current);\n  }, []);\n\n  useEffect(() => {\n    finishedRef.current = finished;\n  }, [finished]);\n\n  useEffect(() => {\n    onReachBottomRef.current = onReachBottom;\n  }, [onReachBottom]);\n\n  const onScroll = useCallback(() => {\n    if (finishedRef.current || pendingRef.current) {\n      return;\n    }\n    const _scroller = scrollerRef.current;\n    if (_scroller) {\n      if (_scroller.scrollTop >= _scroller.scrollHeight - _scroller.clientHeight - threshold) {\n        pendingRef.current = true;\n        onReachBottomRef.current().then(() => {\n          pendingRef.current = false;\n        });\n      }\n    }\n  }, []);\n\n  useEffect(() => {\n    if (scrollerRef.current) {\n      scrollerRef.current.addEventListener('scroll', onScroll);\n      return () => {\n        scrollerRef.current.removeEventListener('scroll', onScroll);\n      };\n    }\n  }, [onScroll]);\n\n  // 填充数据\n  const replenishData = (a: HTMLElement, b: HTMLElement) => {\n    if (a.clientHeight <= b.clientHeight && !finishedRef.current) {\n      onReachBottomRef.current()\n      // .then(() => {\n      //   setTimeout(() => {\n      //     replenishData(a, b);\n      //   }, 0);\n      // });\n    }\n  };\n\n  return (\n    <div ref={onBoxMounted} className={classnames(className, styles.box)}>\n      {children}\n      <>\n        {!finished && (\n          <div className={styles.tips}>\n            <Spin className={styles.loading} />\n          </div>\n        )}\n      </>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/OperationalDataBar/index.less",
    "content": "@import '../../../../styles/var.less';\n\n.box {\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/OperationalDataBar/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\n\ninterface IProps {\n  className?: string;\n}\n\nexport default memo<IProps>((props) => {\n  const { className } = props;\n  return <div className={classnames(styles.operationalDataBar, className)}>\n    operationalDataBar\n  </div>;\n});\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/Pagination/index.less",
    "content": "@import '../../../../styles/var.less';\n.paginationWrapper {\n  display: flex;\n  justify-content: start;\n  align-items: center;\n  height: 30px;\n}\n\n.item-icon {\n  height: 24px;\n  width: 24px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n\n  font-size: 10px;\n  background-color: transparent;\n\n  border-radius: 4px;\n  outline: none;\n  transition: border 0.2s;\n\n  color: var(--color-text);\n  cursor: pointer;\n  user-select: none;\n\n  box-sizing: border-box;\n  &:hover {\n    background-color: var(--color-bg-text-hover);\n    color: var(--color-link);\n  }\n}\n\n.item-icon-disabled {\n  &,\n  &:hover {\n    cursor: not-allowed;\n    color: var(--color-text-disabled);\n  }\n}\n\n.input-number {\n  width: 32px;\n  :global {\n    .ant-input-number-input {\n      text-align: center;\n      padding: 0px;\n      // display: flex;\n      // justify-content: center;\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/Pagination/index.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { VerticalLeftOutlined, VerticalRightOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';\nimport cs from 'classnames';\nimport { Button, InputNumber, Popover, Select } from 'antd';\nimport { IResultConfig } from '@/typings';\nimport i18n from '@/i18n';\nimport _ from 'lodash';\nimport styles from './index.less';\n\ninterface IProps {\n  onPageSizeChange?: (pageSize: number) => void;\n  onPageNoChange?: (pageNo: number) => void;\n  onClickTotalBtn?: () => Promise<number | undefined>;\n  paginationConfig: IResultConfig;\n}\ntype IIconType = 'pre' | 'next' | 'first' | 'last';\nexport default function Pagination(props: IProps) {\n  const { onPageNoChange, onPageSizeChange, paginationConfig } = props;\n  const [inputValue, setInputValue] = useState<number | null>(1);\n  const [totalLoading, setTotalLoading] = useState(false);\n\n  useEffect(() => {\n    setInputValue(paginationConfig?.pageNo ?? 1);\n  }, [paginationConfig?.pageNo]);\n\n  const onInputNumberChange = (value: number | null) => {\n    setInputValue(value);\n  };\n\n  const onInputNumberBlur = () => {\n    if (_.isNumber(inputValue)) {\n      onPageNoChange && onPageNoChange(inputValue);\n    } else {\n      setInputValue(1);\n      onPageNoChange && onPageNoChange(1);\n    }\n  };\n\n  const handleClickTotalBtn = async () => {\n    if (!props.onClickTotalBtn) return;\n    setTotalLoading(true);\n\n    const res = await props.onClickTotalBtn();\n    setTotalLoading(false);\n    return res;\n  };\n\n  const handleClickIcon = async (type: IIconType) => {\n    if (!onPageNoChange || !paginationConfig) return;\n    if (handleIsDisabled(type)) return;\n    switch (type) {\n      case 'first':\n        onPageNoChange(1);\n        break;\n      case 'last':\n        {\n          const total = await handleClickTotalBtn();\n          const { pageSize } = paginationConfig || {};\n          if (_.isNumber(total) && _.isNumber(pageSize)) {\n            props.onPageNoChange && props.onPageNoChange(Math.ceil(total / pageSize));\n          }\n        }\n        break;\n      case 'pre':\n        onPageNoChange(paginationConfig?.pageNo - 1);\n        break;\n      case 'next':\n        onPageNoChange(paginationConfig?.pageNo + 1);\n        break;\n      default:\n        break;\n    }\n  };\n\n  const handleIsDisabled = (type: IIconType) => {\n    if (!paginationConfig) {\n      return false;\n    }\n    if (type === 'first') {\n      return paginationConfig?.pageNo === 1;\n    }\n    if (type === 'pre') {\n      return paginationConfig?.pageNo === 1;\n    }\n\n    const isNumber = _.isNumber(paginationConfig.total);\n    const totalShow = paginationConfig.pageNo * paginationConfig.pageSize;\n    if (type === 'next' || type === 'last') {\n      if (isNumber) {\n        return totalShow > (paginationConfig.total as number);\n      }\n      return !paginationConfig?.hasNextPage;\n    }\n\n    // if (type === 'last') {\n    //   if (isNumber) {\n    //     return totalShow > (paginationConfig.total as number);\n    //   }\n    //   return return !paginationConfig?.hasNextPage;;\n    // }\n\n    return true;\n  };\n\n  return (\n    <div className={styles.paginationWrapper}>\n      <VerticalRightOutlined\n        className={cs(styles['item-icon'], {\n          [styles['item-icon-disabled']]: handleIsDisabled('first'),\n        })}\n        onClick={() => handleClickIcon('first')}\n      />\n      <LeftOutlined\n        className={cs(styles['item-icon'], {\n          [styles['item-icon-disabled']]: handleIsDisabled('pre'),\n        })}\n        onClick={() => handleClickIcon('pre')}\n      />\n      <InputNumber\n        className={styles['input-number']}\n        size=\"small\"\n        min={1}\n        value={inputValue}\n        controls={false}\n        onPressEnter={onInputNumberBlur}\n        onBlur={onInputNumberBlur}\n        onChange={onInputNumberChange}\n      />\n\n      <RightOutlined\n        className={cs(styles['item-icon'], {\n          [styles['item-icon-disabled']]: handleIsDisabled('next'),\n        })}\n        onClick={() => handleClickIcon('next')}\n      />\n      <VerticalLeftOutlined\n        className={cs(styles['item-icon'], {\n          [styles['item-icon-disabled']]: handleIsDisabled('last'),\n        })}\n        onClick={() => handleClickIcon('last')}\n      />\n\n      <Select\n        popupMatchSelectWidth={false}\n        size=\"small\"\n        value={paginationConfig?.pageSize ?? 200}\n        onChange={onPageSizeChange}\n        options={[\n          { label: 10, value: 10 },\n          { label: 50, value: 50 },\n          { label: 100, value: 100 },\n          { label: 200, value: 200 },\n          { label: 500, value: 500 },\n          { label: 1000, value: 1000 },\n        ]}\n      />\n\n      <Popover mouseEnterDelay={0.8} content={i18n('workspace.table.total.tip')}>\n        <Button type=\"link\" loading={totalLoading} onClick={handleClickTotalBtn}>\n          {i18n('workspace.table.total')}：{paginationConfig?.total}\n        </Button>\n      </Popover>\n    </div>\n  );\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/RightClickMenu/index.less",
    "content": "@import '../../../../styles/var.less';\n\n.dropdownOverlay {\n  :global {\n    .ant-dropdown-menu-submenu-title{\n      display: flex;\n      align-items: center;\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/RightClickMenu/index.tsx",
    "content": "import React, { memo, useEffect, useMemo } from 'react';\nimport { Dropdown, ConfigProvider } from 'antd';\nimport i18n from '@/i18n';\nimport MenuLabel from '@/components/MenuLabel';\nimport styles from './index.less';\n\ninterface IProps {\n  className?: string;\n  children?: React.ReactNode;\n  menuList: IMenu[] | null;\n}\n\nexport interface IMenu {\n  key: string;\n  callback?: () => void;\n  children?: {\n    callback: () => void;\n    hide?: boolean;\n  }[];\n}\n\nexport enum AllSupportedMenusType {\n  CopyCell = 'copy-cell',\n  CopyRow = 'copy-row',\n  CloneRow = 'clone-row',\n  DeleteRow = 'delete-row',\n  SetDefault = 'set-default',\n  SetNull = 'set-null',\n  ViewData = 'view-data',\n}\n\nexport default memo<IProps>((props) => {\n  const { children, menuList } = props;\n  const [open, setOpen] = React.useState<boolean | undefined>(undefined);\n  const [canContextmenu, setCanContextmenu] = React.useState<boolean>(false);\n  useEffect(() => {\n    if (open === false) {\n      setOpen(undefined);\n    }\n  }, [open]);\n\n  useEffect(() => {\n    const handleClick = (event) => {\n      const targetElement = event.target as Element;\n      if (targetElement.closest('[data-chat2db-edit-table-data-can-right-click]')) {\n        setCanContextmenu(true);\n      } else {\n        setCanContextmenu(false);\n      }\n    };\n    document.addEventListener('contextmenu', handleClick);\n    return () => {\n      document.removeEventListener('contextmenu', handleClick);\n    };\n  }, []);\n\n  const allSupportedMenus = {\n    [AllSupportedMenusType.CopyCell]: {\n      label: <MenuLabel icon=\"&#xec7a;\" label={i18n('common.button.copy')} />,\n      key: AllSupportedMenusType.CopyCell,\n    },\n    [AllSupportedMenusType.CopyRow]: {\n      label: <MenuLabel icon=\"&#xec7a;\" label={i18n('common.button.copyRowAs')} />,\n      key: AllSupportedMenusType.CopyRow,\n      children: [\n        {\n          label: i18n('common.button.insertSql'),\n          key: 'copy-row-1',\n        },\n        {\n          label: i18n('common.button.updateSql'),\n          key: 'copy-row-2',\n        },\n        {\n          label: i18n('common.button.tabularSeparatedValues'),\n          key: 'copy-row-3',\n        },\n        {\n          label: i18n('common.button.tabularSeparatedValuesFieldName'),\n          key: 'copy-row-4',\n        },\n        {\n          label: i18n('common.button.tabularSeparatedValuesFieldNameAndData'),\n          key: 'copy-row-5',\n        },\n      ],\n    },\n    [AllSupportedMenusType.CloneRow]: {\n      label: <MenuLabel icon=\"&#xe8db;\" label={i18n('common.button.cloneRow')} />,\n      key: AllSupportedMenusType.CloneRow,\n    },\n    [AllSupportedMenusType.DeleteRow]: {\n      label: <MenuLabel icon=\"&#xe6a7;\" label={i18n('common.button.deleteRow')} />,\n      key: AllSupportedMenusType.DeleteRow,\n    },\n    [AllSupportedMenusType.SetDefault]: {\n      label: <MenuLabel label={i18n('common.button.setDefault')} />,\n      key: AllSupportedMenusType.SetDefault,\n    },\n    [AllSupportedMenusType.SetNull]: {\n      label: <MenuLabel label={i18n('common.button.setNull')} />,\n      key: AllSupportedMenusType.SetNull,\n    },\n    [AllSupportedMenusType.ViewData]: {\n      label: <MenuLabel icon=\"&#xe788;\" label={i18n('common.button.viewData')} />,\n      key: AllSupportedMenusType.ViewData,\n    },\n  };\n\n  const items = useMemo(() => {\n    return menuList?.map((menu) => {\n      return {\n        ...allSupportedMenus[menu.key],\n        onClick: () => {\n          menu.callback?.();\n          setOpen(false);\n        },\n        children: menu.children?.map((child, index) => {\n          if (child.hide) return null;\n          return {\n            ...allSupportedMenus[menu.key]['children'][index],\n            onClick: () => {\n              child.callback();\n              setOpen(false);\n            },\n          };\n        }),\n      };\n    });\n  }, [menuList]);\n\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          motion: false,\n        },\n      }}\n    >\n      <Dropdown\n        menu={{\n          items: items || [],\n          style: items ? {} : { display: 'none' },\n        }}\n        trigger={['contextMenu']}\n        overlayClassName={styles.dropdownOverlay}\n        open={open && canContextmenu}\n        onOpenChange={(_open) => {\n          setOpen(_open);\n        }}\n      >\n        {children}\n      </Dropdown>\n    </ConfigProvider>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/ScreeningResult/index.less",
    "content": ".screeningResult{\n  display: flex;\n  align-items: center;\n  height: 18px;\n  padding: 4px 0px;\n  border-bottom: 1px solid var(--color-border);\n  .whereBox,.orderByBox{\n    width: 50%;\n    display: flex;\n    align-items: center;\n    height: 100%;\n    margin: 0px 4px;\n    .titleBox{\n      display: flex;\n      align-items: center;\n      margin-right: 10px;\n      width: fit-content;\n      flex-shrink: 0;\n    }\n    .titleIcon{\n      margin-right: 4px;\n    }\n    .title{\n      color: var(--color-text-secondary);\n      flex-shrink: 0;\n      font-weight: 500;\n    }\n    .activeTitle{\n      color: var(--color-primary-text);\n    }\n    .monacoEditor{\n      flex: 1;\n      height: 100%;\n      width: 0px;\n    }\n  }\n  :global {\n    .decorationsOverviewRuler{\n      display: none !important;\n    }\n  }\n}"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/ScreeningResult/index.tsx",
    "content": "import React, { memo, useEffect, useContext } from 'react';\nimport classnames from 'classnames';\nimport styles from './index.less';\nimport Iconfont from '@/components/Iconfont';\nimport SingleFileMonacoEditor from '@/components/SingleFileMonacoEditor';\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api';\nimport { IExecuteSqlParams } from '@/service/sql';\nimport { Context } from '@/components/SearchResult';\n\ninterface IProps {\n  className?: string;\n  promptWord: any[];\n  getTableData: (params?: Partial<IExecuteSqlParams>) => void;\n}\n\nconst keywordHintList = [\n  'AND',\n  'OR',\n  'NOT',\n  'IS',\n  'NULL',\n  'IN',\n  'IS NOT NULL',\n  'IS NULL',\n  'IS NOT',\n  'NOT IN',\n  'EXISTS',\n  'BETWEEN',\n  'LIKE',\n  'ASC',\n  'DESC',\n]\n\nexport default memo<IProps>((props) => {\n  const { promptWord, getTableData } = props;\n  const { notChangedSql } = useContext(Context);\n  const [isActive, setIsActive] = React.useState(false);\n  const keywordHintRef = React.useRef<any>(null);\n  const fieldHintRef = React.useRef<any>(null);\n  const whereSingleFileMonacoEditorRef = React.useRef<any>(null);\n  const orderBySingleFileMonacoEditorRef = React.useRef<any>(null);\n\n  useEffect(() => {\n    keywordHintRef.current && keywordHintRef.current.dispose();\n    fieldHintRef.current && fieldHintRef.current.dispose();\n    if (isActive) {\n      registerPromptWord();\n    }\n  }, [promptWord, isActive]);\n\n  const registerPromptWord = () => {\n    const suggestions = promptWord.slice(1).map((item) => {\n      return {\n        insertText: item.name,\n        kind: monaco.languages.CompletionItemKind.Field,\n        label: item.name,\n      };\n    });\n\n    const provideCompletionItems: any = () => {\n      return {\n        suggestions,\n      };\n    };\n\n    fieldHintRef.current = monaco.languages.registerCompletionItemProvider('sql', {\n      provideCompletionItems,\n      triggerCharacters: [],\n    });\n\n    keywordHintRef.current = monaco.languages.registerCompletionItemProvider('sql', {\n      provideCompletionItems: () => {\n        return {\n          suggestions: keywordHintList.map((item) => {\n            return {\n              insertText: item,\n              kind: monaco.languages.CompletionItemKind.Keyword,\n              label: item,\n            };\n          }),\n        };\n      },\n      triggerCharacters: [],\n    });\n  };\n\n  const search = () => {\n    const whereValue = whereSingleFileMonacoEditorRef.current?.getAllContent().trim() || '';\n    const orderByValue = orderBySingleFileMonacoEditorRef.current?.getAllContent().trim() || '';\n    let sql = whereValue ? notChangedSql + ' WHERE ' + whereValue : notChangedSql;\n    sql = orderByValue ? sql + ' ORDER BY ' + orderByValue : sql;\n    getTableData({ sql });\n  };\n\n  const focusChange = (_isActive: boolean) => {\n    setIsActive(_isActive);\n  };\n\n  return (\n    <div className={styles.screeningResult}>\n      <div className={styles.whereBox}>\n        <div className={styles.titleBox}>\n          <Iconfont box boxSize={20} classNameBox={styles.titleIcon} code=\"&#xe66a;\" />\n          <div\n            className={classnames(styles.title, {\n              [styles.activeTitle]: true,\n            })}\n          >\n            WHERE\n          </div>\n        </div>\n        <SingleFileMonacoEditor\n          ref={whereSingleFileMonacoEditorRef}\n          focusChange={focusChange}\n          handelEnter={search}\n          className={styles.monacoEditor}\n        />\n      </div>\n      <div className={styles.orderByBox}>\n        <div className={styles.titleBox}>\n          <Iconfont box boxSize={20} classNameBox={styles.titleIcon} code=\"&#xe69a;\" />\n          <div\n            className={classnames(styles.title, {\n              [styles.activeTitle]: true,\n            })}\n          >\n            ORDER BY\n          </div>\n        </div>\n        <SingleFileMonacoEditor\n          ref={orderBySingleFileMonacoEditorRef}\n          focusChange={focusChange}\n          handelEnter={search}\n          className={styles.monacoEditor}\n        />\n      </div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/StatusBar/index.less",
    "content": "@import '../../../../styles/var.less';\n\n.statusBar {\n  height: 26px;\n  box-sizing: border-box;\n  padding: 4px 8px;\n  font-size: 12px;\n  display: flex;\n  justify-content: start;\n  align-items: center;\n  border-top: 1px solid var(--color-border-secondary);\n  background-color: var(--color-bg-subtle);\n  overflow: hidden;\n  flex-shrink: 0;\n  .f-single-line();\n  & > span {\n    margin-right: 16px;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/StatusBar/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport i18n from '@/i18n';\n\ninterface IProps {\n  className?: string;\n  description?: string;\n  duration?: number;\n  dataLength?: number;\n}\n\nexport default memo<IProps>((props) => {\n  const { className, description, duration, dataLength } = props;\n  return (\n    <div className={classnames(styles.statusBar, className)}>\n      <span>{`【${i18n('common.text.result')}】${description}.`}</span>\n      <span>{`【${i18n('common.text.timeConsuming')}】${duration}ms.`}</span>\n      {!!dataLength && <span>{`【${i18n('common.text.searchRow')}】${dataLength} ${i18n('common.text.row')}.`}</span>}\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/TableBox/index.less",
    "content": "@import '../../../../styles/var.less';\n\n.button-bar-item() {\n  width: 24px;\n  height: 24px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  cursor: pointer;\n  border-radius: 4px;\n  i {\n    color: var(--color-primary);\n  }\n  &:hover{\n    background-color: var(--color-hover-bg);\n  }\n}\n\n.tableBox {\n  height: 100%;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  .allSelectBox{\n    width: calc(100% + 8px);\n    height: 100%;\n    transform: translateX(-4px);\n    &:hover{\n      background-color: var(--color-hover-bg);\n    }\n  }\n  :global {\n    .table{\n      overflow: hidden;\n    }\n    .art-table {\n      table colgroup col:nth-of-type(1) {\n        min-width: 60px;\n      }\n      // th,\n      // td {\n      //   overflow: hidden;\n      // }\n      .fnWppk {\n        // word-break: break-all;\n        // display: -webkit-box;\n        // -webkit-box-orient: vertical;\n        // box-orient: vertical;\n        // -webkit-line-clamp: 1;\n        // line-clamp: 1;\n        // text-overflow: ellipsis;\n        overflow: hidden;\n      }\n      th {\n        font-weight: bold;\n      }\n      th.first {\n        border-left: 0;\n      }\n      td.first {\n        border-left: 0;\n      }\n      tr.first th {\n        border-top: 0;\n      }\n    }\n    .art-table-cell {\n      position: relative;\n    }\n    // .art-sticky-scroll {\n    //   display: none !important;\n    // }\n\n    .art-table-row {\n      height: 32px;\n    }\n    .art-table-header {\n      background: transparent !important;\n      overflow: hidden !important;\n      top: 0px !important;\n    }\n    .art-table-body::-webkit-scrollbar {\n      display: none;\n    }\n    .art-table-body-scroll {\n      padding-right: 6px;\n    }\n    .art-table-header-cell {\n      padding: 0px 4px;\n    }\n    .art-table-header-row .art-table-header-cell:nth-of-type(1){\n      .resize-handle{\n        display: none;\n      }\n    }\n  }\n}\n\n.noDataTableBox {\n  :global {\n    .art-loading-wrapper,\n    .art-loading-content-wrapper,\n    .art-table {\n      height: 100%;\n    }\n    .art-table-body {\n      height: calc(100% - 32px);\n      overflow-y: auto !important;\n    }\n  }\n  table {\n    height: 100%;\n  }\n}\n\n.toolBar {\n  z-index: 30;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  border-bottom: 1px solid var(--color-border-secondary);\n  background-color: var(--color-bg-subtle);\n  padding: 0px 4px 0px 0px;\n  height: 30px;\n  flex-shrink: 0;\n}\n\n.toolBarItem {\n  height: 100%;\n  display: flex;\n  justify-content: start;\n  align-items: center;\n  &:not(:last-child) {\n    border-right: 1px solid var(--color-border);\n  }\n}\n\n.toolBarRight {\n  flex: 1;\n  display: flex;\n  justify-content: end;\n  .exportBar {\n    cursor: pointer;\n  }\n}\n\n.editTableDataBar {\n  padding: 0px 4px;\n  height: 30px;\n  i {\n    color: var(--color-text-disabled);\n  }\n  .editTableDataBarItem {\n    .button-bar-item();\n  }\n  .disableBar {\n    cursor: default;\n    i {\n      color: var(--color-text-disabled);\n    }\n    &:hover{\n      background-color: transparent;\n    }\n  }\n  .viewSqlBar {\n    i {\n      font-size: 15px;\n    }\n  }\n}\n\n.refreshBar{\n  padding: 0px 4px;\n  .refreshIconBox{\n    .button-bar-item();\n  }\n}\n\n.supportBaseTableBox {\n  flex: 1;\n  overflow-y: auto;\n  height: 0px;\n  position: relative;\n  .supportBaseTableSpin {\n    position: absolute;\n    top: 0px;\n    left: 0px;\n    right: 0px;\n    bottom: 0px;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    z-index: 10;\n    background-color: var(--color-fill-quaternary);\n  }\n}\n\n.supportBaseTableBoxHidden {\n  overflow: hidden;\n}\n\n.table {\n  height: 100%;\n}\n\n.tableItem {\n  height: 31px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  position: relative;\n  padding-left: 4px;\n  user-select: none;\n  background-color: var(--color-bg-base);\n  cursor: pointer;\n\n  .tableItemContent {\n    max-height: 31px;\n    line-height: 31px;\n    flex: 1;\n    width: 100%;\n    overflow: hidden;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n  }\n\n  .previewTableItemContent{\n    display: none;\n    position: absolute;\n    left: 0px;\n    top: 0px;\n    bottom: 0px;\n    width: fit-content;\n    min-width: 100%;\n    padding: 0px 4px;\n    max-height: 31px;\n    line-height: 31px;\n    background-color: var(--color-bg-layout);\n    z-index: 2;\n    color: var(--color-text);\n    white-space: nowrap;\n  }\n\n  &:hover {\n    .previewTableItemContent {\n      display: flex;\n    }\n    .previewTableItemContent {\n      display: block;\n    }\n  }\n\n  .cellValueNull {\n    color: var(--color-text-tertiary);\n  }\n\n  input {\n    background: none;\n  }\n}\n\n.tableItemEdit {\n  background-color: var(--color-success-bg);\n  .previewTableItemContent {\n    background-color: var(--color-success-bg);\n  }\n  \n}\n.tableItemHighlight {\n  background-color: var(--color-bg-subtle);\n  .previewTableItemContent {\n    background-color: var(--color-bg-subtle);\n  }\n}\n.tableItemFocus {\n  background-color: var(--color-primary-hover);\n  color: var(--color-bg-base);\n  .previewTableItemContent {\n    background-color: var(--color-primary-hover);\n  }\n  .previewTableItemContent{\n    background-color: var(--color-primary-hover);\n  }\n  .cellValueNull {\n    color: var(--color-bg-base);\n  }\n}\n.tableItemSuccess {\n  background-color: var(--color-success-bg);\n  .previewTableItemContent {\n    background-color: var(--color-success-bg);\n  }\n}\n.tableItemError {\n  background-color: var(--color-error-bg);\n  .previewTableItemContent {\n    background-color: var(--color-error-bg);\n  }\n}\n\n.tableItemNo {\n  width: 100%;\n  text-align: center;\n}\n\n.pagination {\n  :global {\n    .ant-pagination-prev,\n    .ant-pagination-next,\n    .ant-pagination-jump-prev,\n    .ant-pagination-jump-next {\n      min-width: 20px;\n    }\n    .ant-pagination-simple-pager {\n      input {\n        padding: 0px !important;\n        font-size: 12px;\n        height: 70% !important;\n      }\n    }\n  }\n}\n\n.monacoEditor {\n  border: 1px solid var(--color-border);\n  border-radius: 4px;\n  overflow: hidden;\n  height: 60vh;\n  margin-top: -15px;\n}\n\n.errorDetail {\n  white-space: normal;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/components/TableBox/index.tsx",
    "content": "import React, { useEffect, useMemo, useState } from 'react';\nimport { Dropdown, Input, MenuProps, message, Modal, Space, Popover, Spin, Button } from 'antd';\nimport { BaseTable, ArtColumn, useTablePipeline, features, SortItem } from 'ali-react-table';\nimport styled from 'styled-components';\nimport classnames from 'classnames';\nimport lodash from 'lodash';\nimport { v4 as uuid } from 'uuid';\nimport i18n from '@/i18n';\nimport ScreeningResult from '@/components/SearchResult/components/ScreeningResult';\n// import { Context } from '@/components/SearchResult';\n\n// 样式\nimport styles from './index.less';\n\n// 工具函数\nimport { compareStrings } from '@/utils/sort';\nimport { downloadFile } from '@/utils/file';\nimport { transformInputValue } from '../../utils';\n\n// 类型定义\nimport { CRUD } from '@/constants';\nimport { TableDataType } from '@/constants/table';\nimport { IManageResultData, IResultConfig } from '@/typings/database';\nimport { ExportSizeEnum, ExportTypeEnum } from '@/typings/resultTable';\n\n// api\nimport sqlService, { IExportParams, IExecuteSqlParams } from '@/service/sql';\n\n// store\nimport { setFocusedContent } from '@/store/common/copyFocusedContent';\n\n// 依赖组件\nimport ExecuteSQL from '@/components/ExecuteSQL';\nimport { DownOutlined } from '@ant-design/icons';\nimport { copy, tableCopy } from '@/utils';\nimport Iconfont from '../../../Iconfont';\nimport StateIndicator from '../../../StateIndicator';\nimport MonacoEditor from '../../../MonacoEditor';\nimport MyPagination from '../Pagination';\nimport StatusBar from '../StatusBar';\nimport RightClickMenu, { AllSupportedMenusType } from '../RightClickMenu';\n\n// 自定义hooks\nimport useCurdTableData from '../../hooks/useCurdTableData';\nimport useMultipleSelect from '../../hooks/useMultipleSelect';\nimport usePasteData from '../../hooks/usePasteData';\n\ninterface ITableProps {\n  className?: string;\n  outerQueryResultData: IManageResultData;\n  executeSqlParams: any;\n  tableBoxId: string;\n  isActive?: boolean;\n  concealTabHeader?: boolean; // concealTabHeader 是否隐藏tab头部, 目前来说隐藏头部都是单表查询。需要显示筛选\n}\n\ninterface IViewTableCellData {\n  name: string;\n  value: any;\n  colId: string;\n  rowId: string;\n}\n\nexport interface IUpdateData {\n  oldDataList?: Array<string | null>;\n  dataList?: Array<string | null>;\n  type: CRUD;\n  rowId: string;\n}\n\nexport enum USER_FILLED_VALUE {\n  DEFAULT = 'CHAT2DB_UPDATE_TABLE_DATA_USER_FILLED_DEFAULT',\n}\n\nconst SupportBaseTable: any = styled(BaseTable)`\n  &.supportBaseTable {\n    --bgcolor: var(--color-bg-base);\n    --header-bgcolor: var(--color-bg-subtle);\n    --hover-bgcolor: transparent;\n    --header-hover-bgcolor: var(--color-bg-subtle);\n    --highlight-bgcolor: transparent;\n    --header-highlight-bgcolor: var(--color-bg-subtle);\n    --color: var(--color-text);\n    --header-color: var(--color-text);\n    --lock-shadow: rgb(37 37 37 / 0.5) 0 0 6px 2px;\n    --border-color: var(--color-border-secondary);\n    --cell-padding: 0px;\n    --row-height: 32px;\n    --lock-shadow: 0px 1px 2px 0px var(--color-border);\n  }\n`;\n\nconst preCode = '$$chat2db_';\n\n// No列的code\nconst colNoCode = `${preCode}0No.`;\n\nconst defaultPaginationConfig: IResultConfig = {\n  pageNo: 1,\n  pageSize: 200,\n  total: 0,\n  hasNextPage: true,\n};\n\nexport const TableContext = React.createContext({} as any);\n\nexport default function TableBox(props: ITableProps) {\n  // const {} = useContext(Context);\n  const { className, outerQueryResultData, isActive, concealTabHeader } = props;\n  const [viewTableCellData, setViewTableCellData] = useState<IViewTableCellData | null>(null);\n  const [, contextHolder] = message.useMessage();\n  const [paginationConfig, setPaginationConfig] = useState<IResultConfig>(defaultPaginationConfig);\n  // sql查询结果\n  const [queryResultData, setQueryResultData] = useState<IManageResultData>(outerQueryResultData);\n  // tableData：带列标识的表数据 可以传给Table组件 进行渲染\n  // 保存原始的表数据，用于撤销\n  const [oldTableData, setOldTableData] = useState<{ [key: string]: string }[]>([]);\n  // 实时更新的表数据\n  const [tableData, setTableData] = useState<{ [key: string]: string | null }[]>([]);\n  // DataList不带列标识的表数据\n  // 保存原始的表数据，用于对比新老数据看是否有变化\n  const [oldDataList, setOldDataList] = useState<string[][]>([]);\n  // 当前聚焦的单元格的坐标，以及是否正在编辑，为false时，代表正在聚焦，但是没有编辑\n  const [editingCell, setEditingCell] = useState<[string, string, boolean] | null>(null);\n  // input受控的正在编辑的数据\n  const [editingData, setEditingData] = useState<string>('');\n  // 当前选中的行号\n  const [curOperationRowNo, setCurOperationRowNo] = useState<Array<string> | null>(null);\n  // 操作过的数据列表\n  const [updateData, setUpdateData] = useState<IUpdateData[] | []>([]);\n  // 更新数据的sql\n  const [updateDataSql, setUpdateDataSql] = useState<string | null>(null);\n  // ExecuteSQL弹窗 初始化的错误信息\n  const [initError, setInitError] = useState<string | null>(null);\n  // 是否显示更新数据的sql\n  const [viewUpdateDataSqlModal, setViewUpdateDataSqlModal] = useState<boolean>(false);\n  // 用于滚动到底部\n  const tableBoxRef = React.useRef<HTMLDivElement>(null);\n  // 所有数据准备好了\n  const [allDataReady, setAllDataReady] = useState<boolean>(false);\n  // 编辑数据的inputRef\n  const editDataInputRef = React.useRef<any>(null);\n  // monacoEditorRef\n  const monacoEditorRef = React.useRef<any>(null);\n  // 表格loading\n  const [tableLoading, setTableLoading] = useState<boolean>(false);\n  // 列宽数组\n  const [columnResize, setColumnResize] = useState<number[]>([0]);\n  // 表格的宽度\n  // const [tableBoxWidth, setTableBoxWidth] = useState<number>(0);\n\n  const handleExportSQLResult = async (exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => {\n    const params: IExportParams = {\n      ...(props.executeSqlParams || {}),\n      sql: queryResultData.sql,\n      originalSql: queryResultData.originalSql,\n      exportType,\n      exportSize,\n    };\n    downloadFile(window._BaseURL + '/api/rdb/dml/export', params);\n  };\n\n  useEffect(() => {\n    let total: any = queryResultData.fuzzyTotal;\n\n    // 如果total是数字，且不为0，则还是使用原先的total\n    if (lodash.isNumber(paginationConfig.total) && paginationConfig.total !== 0) {\n      total = paginationConfig.total;\n    }\n\n    if (!lodash.isNumber(paginationConfig.total)) {\n      const oldTotal = Number(paginationConfig.total.split('+')[0]);\n      const newTotal = Number(queryResultData.fuzzyTotal.split('+')[0]);\n      if (oldTotal > newTotal) {\n        total = paginationConfig.total;\n      }\n    }\n\n    setPaginationConfig({\n      ...paginationConfig,\n      total,\n      hasNextPage: queryResultData.hasNextPage,\n    });\n  }, [queryResultData]);\n\n  useEffect(() => {\n    // 每次dataList变化，都需要重新计算tableData\n    if (!columns?.length) {\n      setTableData([]);\n    } else {\n      const newTableData = dataListTransformTableData(queryResultData.dataList);\n      setTableData(newTableData);\n      setOldTableData(newTableData);\n      setAllDataReady(true);\n    }\n    // 每次data变化，都需要重新计算oldDataList\n    if (queryResultData.dataList?.length) {\n      setOldDataList(queryResultData.dataList);\n    }\n  }, [queryResultData.dataList]);\n\n  // 导出sql的菜单项\n  const exportDropdownItems: MenuProps['items'] = useMemo(\n    () => [\n      {\n        label: i18n('workspace.table.export.all.csv'),\n        key: '1',\n        // icon: <UserOutlined />,\n        onClick: () => {\n          handleExportSQLResult(ExportTypeEnum.CSV, ExportSizeEnum.ALL);\n        },\n      },\n      {\n        label: i18n('workspace.table.export.all.insert'),\n        key: '2',\n        // icon: <UserOutlined />,\n        onClick: () => {\n          handleExportSQLResult(ExportTypeEnum.INSERT, ExportSizeEnum.ALL);\n        },\n      },\n      {\n        label: i18n('workspace.table.export.cur.csv'),\n        key: '3',\n        // icon: <UserOutlined />,\n        onClick: () => {\n          handleExportSQLResult(ExportTypeEnum.CSV, ExportSizeEnum.CURRENT_PAGE);\n        },\n      },\n      {\n        label: i18n('workspace.table.export.cur.insert'),\n        key: '4',\n        // icon: <UserOutlined />,\n        onClick: () => {\n          handleExportSQLResult(ExportTypeEnum.INSERT, ExportSizeEnum.CURRENT_PAGE);\n        },\n      },\n    ],\n    [queryResultData],\n  );\n\n  const defaultSorts: SortItem[] = useMemo(\n    () =>\n      (queryResultData.headerList || []).map((item) => ({\n        code: item.name,\n        order: 'none',\n      })),\n    [queryResultData.headerList],\n  );\n\n  function monacoEditorEditData() {\n    const editorData = monacoEditorRef?.current?.getAllContent();\n    const { rowId, colId } = viewTableCellData!;\n    oldTableData.forEach((item) => {\n      if (item[colNoCode] === rowId) {\n        if (item[colId] !== editorData) {\n          const newTableData = lodash.cloneDeep(tableData);\n          let newRowDataList: any = [];\n          newTableData.forEach((i) => {\n            if (i[colNoCode] === rowId) {\n              i[colId] = editorData;\n              newRowDataList = Object.keys(i).map((_i) => i[_i]);\n            }\n          });\n          setTableData(newTableData);\n          // 添加更新记录\n          setUpdateData([\n            ...updateData,\n            {\n              type: CRUD.UPDATE,\n              oldDataList: Object.keys(item).map((_i) => item[_i]),\n              dataList: newRowDataList,\n              rowId,\n            },\n          ]);\n        }\n        return;\n      }\n    });\n    setViewTableCellData(null);\n  }\n\n  function handleCancel() {\n    setViewTableCellData(null);\n  }\n\n  const handleClickTableItem = (colId, rowId, value, isEditing) => {\n    // 1. 如果当前单元格正在编辑，则不需要再次编辑\n    // 2. 如果当前单元格正在编辑，则不需要聚焦\n    if (editingCell?.[0] === colId && editingCell?.[1] === rowId && editingCell?.[2]) {\n      return;\n    }\n    setFocusedContent(value);\n    // 聚焦当前单元格，取消对于行的聚焦\n    setCurOperationRowNo(null);\n    // 当前聚焦或者编辑的单元格的数据\n    setEditingData(value);\n    // 如果数据不支持修改，则该单元格不支持编辑\n    if (!queryResultData.canEdit) {\n      setEditingCell([colId, rowId, false]);\n    } else {\n      setEditingCell([colId, rowId, isEditing]);\n    }\n    // 当前聚焦或者编辑的单元格的坐标\n    // 如果是编辑状态，则需要聚焦到input\n    if (isEditing) {\n      setTimeout(() => {\n        editDataInputRef?.current?.focus();\n      }, 0);\n    }\n  };\n\n  // 渲染单元格的值\n  const renderTableCellValue = (value) => {\n    if (value === null) {\n      return <span className={styles.cellValueNull}>{'<null>'}</span>;\n    } else if (value === USER_FILLED_VALUE.DEFAULT) {\n      return <span className={styles.cellValueNull}>{'<default>'}</span>;\n    } else if (!value) {\n      // 如果为空需要展位\n      return <span />;\n    } else {\n      return value;\n    }\n  };\n\n  // 每个单元格的样式\n  const tableCellStyle = (value, rowId, colId) => {\n    // 单元格的基础样式\n    const styleList = [styles.tableItem];\n    // 如果当前行中的单元格正在聚焦或编辑\n    if (editingCell?.[1] === rowId) {\n      // 设置正在编辑或聚焦的单元格所在行的样式为高亮\n      styleList.push(styles.tableItemHighlight);\n      // 精确找到列，设置正在编辑或聚焦的单元格的样式为Focus\n      if (editingCell?.[0] === colId && !editingCell?.[2]) {\n        styleList.push(styles.tableItemFocus);\n      }\n      return classnames(...styleList);\n    }\n    // 当前单元格所在的行被选中了(行聚焦)\n    if (curOperationRowNo?.includes(rowId)) {\n      // No列的高亮只需要用tableItemHighlight不需要用tableItemFocus\n      if (colId === colNoCode) {\n        styleList.push(styles.tableItemHighlight);\n      } else {\n        styleList.push(styles.tableItemFocus);\n      }\n      return classnames(...styleList);\n    }\n    // 新添加的行\n    const index2 = updateData.findIndex((item) => {\n      return item.rowId === rowId && item.type === CRUD.CREATE;\n    });\n    if (index2 !== -1) {\n      styleList.push(styles.tableItemSuccess);\n      return classnames(...styleList);\n    }\n    // 如果是删除过的行\n    const index = updateData.findIndex((item) => item.rowId === rowId && item.type === CRUD.DELETE);\n    if (index !== -1) {\n      styleList.push(styles.tableItemError);\n      return classnames(...styleList);\n    }\n    // 编辑过的单元格的样式\n    let oldValue = '';\n    oldTableData.forEach((item) => {\n      if (item[colNoCode] === rowId) {\n        oldValue = item[colId];\n      }\n    });\n\n    if (value !== oldValue && colId !== colNoCode) {\n      // console.log('colId', colId, 'rowId', rowId)\n      // console.log('oldValue', oldValue, 'value', value)\n\n      styleList.push(styles.tableItemEdit);\n    }\n    return classnames(...styleList);\n  };\n\n  // 纯数据的dataList 转换为 tableData\n  const dataListTransformTableData = (myDataList: string[][]) => {\n    const newTableData = (myDataList || []).map((item) => {\n      const rowData: any = {};\n      item.map((i: string | null, colIndex: number) => {\n        const colId = `${preCode}${colIndex}${columns[colIndex].name}`;\n        rowData[colId] = i;\n      });\n      return rowData;\n    });\n    return newTableData;\n  };\n\n  const onPageNoChange = (pageNo: number) => {\n    const config = { ...paginationConfig, pageNo };\n    setPaginationConfig(config);\n    getTableData({ pageNo });\n  };\n\n  const onPageSizeChange = (pageSize: number) => {\n    const config = { ...paginationConfig, pageSize, pageNo: 1 };\n    setPaginationConfig(config);\n    getTableData({ pageSize, pageNo: 1 });\n  };\n\n  const onClickTotalBtn = async () => {\n    const res = await sqlService.getDMLCount({\n      sql: queryResultData.originalSql,\n      ...(props.executeSqlParams || {}),\n    });\n    setPaginationConfig({ ...paginationConfig, total: res });\n    return res;\n  };\n\n  // 撤销按钮是否可用\n  const revokeDisableBarState = useMemo(() => {\n    // 如果有聚焦的行，但是没有操作过的数据，则不可用\n    const operationType = [CRUD.CREATE, CRUD.UPDATE, CRUD.DELETE];\n    if (curOperationRowNo) {\n      // 当前选中的行里面有没有操作过的数据\n      const hasOperationData = updateData.some((item) => {\n        return operationType.includes(item.type) && curOperationRowNo.includes(item.rowId);\n      });\n      if (hasOperationData) {\n        return false;\n      }\n    }\n    // 如果有聚焦的单元格\n    if (editingCell && editingCell[2] === false) {\n      const oldRowDataList = oldDataList.find((item) => item[0] === editingCell[1]);\n      const oldData = oldRowDataList?.[editingCell[0]];\n      // 如果当前单元格的数据和老数据一样，则可用\n      if (oldData !== editingData) {\n        return false;\n      }\n    }\n    // 如果都没，那撤销按钮不可用\n    return true;\n  }, [curOperationRowNo, updateData, editingCell]);\n\n  // 处理撤销\n  const handleRevoke = () => {\n    if (revokeDisableBarState) {\n      return;\n    }\n    // 多行撤销处理\n    if (curOperationRowNo?.length) {\n      const _updateData = updateData.filter((item) => !curOperationRowNo?.includes(item.rowId));\n      let _tableData = tableData.map((item) => {\n        const oldData = oldTableData.find((i) => i[colNoCode] === item[colNoCode])!;\n        return curOperationRowNo.includes(item[colNoCode]!) ? oldData : item;\n      });\n      _tableData = _tableData.filter((item) => item);\n\n      setUpdateData(_updateData);\n      setTableData(_tableData);\n      setCurOperationRowNo(null);\n      return;\n    }\n\n    // 聚焦单元格撤销\n    if (editingCell && editingCell[2] === false) {\n      const oldRowTableData = oldTableData.find((item) => item[colNoCode] === editingCell[1])!;\n      const oldData = oldRowTableData[editingCell[0]];\n      const _tableData = tableData.map((item) => {\n        if (item[colNoCode] === editingCell[1]) {\n          item[editingCell[0]] = oldData || '';\n        }\n        return item;\n      });\n\n      // 如果撤销后这一行的数据和原始数据一样，则删除这条更新记录\n      const newRowTableData = _tableData.find((item) => item[colNoCode] === editingCell[1])!;\n      if (lodash.isEqual(newRowTableData, oldRowTableData)) {\n        setUpdateData(updateData.filter((item) => item.rowId !== editingCell[1]));\n      }\n\n      setTableData(_tableData);\n    }\n  };\n\n  // 查看更新数据的sql\n  const handleViewSql = () => {\n    if (!updateData.length) {\n      return;\n    }\n    getExecuteUpdateSql().then((res) => {\n      setUpdateDataSql(res);\n      setViewUpdateDataSqlModal(true);\n    });\n  };\n\n  // 更新数据的sql\n  const handleUpdateSubmit = () => {\n    if (!updateData.length || tableLoading) {\n      return;\n    }\n    setTableLoading(true);\n    getExecuteUpdateSql()\n      .then((res) => {\n        executeUpdateDataSql(res);\n      })\n      .catch(() => {\n        setTableLoading(false);\n      });\n  };\n\n  // 获取更新数据的sql\n  const getExecuteUpdateSql = (_updateData?: any) => {\n    return new Promise<string>((resolve) => {\n      const params = {\n        databaseName: props.executeSqlParams?.databaseName,\n        dataSourceId: props.executeSqlParams?.dataSourceId,\n        schemaName: props.executeSqlParams?.schemaName,\n        type: props.executeSqlParams?.databaseType,\n        tableName: queryResultData.tableName,\n        headerList: queryResultData.headerList,\n        operations: _updateData || updateData,\n      };\n      sqlService.getExecuteUpdateSql(params).then((res) => {\n        resolve(res || '');\n      });\n    });\n  };\n\n  // 执行sql\n  const executeUpdateDataSql = (sql: string) => {\n    const executeSQLParams: IExecuteSqlParams = {\n      sql,\n      dataSourceId: props.executeSqlParams?.dataSourceId,\n      databaseName: props.executeSqlParams?.databaseName,\n      schemaName: props.executeSqlParams?.schemaName,\n      tableName: queryResultData.tableName,\n    };\n    sqlService\n      .executeUpdateDataSql(executeSQLParams)\n      .then((res) => {\n        if (res?.success) {\n          // 更新成功后，需要重新获取表格数据\n          getTableData().then(() => {\n            message.success(i18n('common.text.successfulExecution'));\n            setUpdateData([]);\n          });\n        } else {\n          setUpdateDataSql(res?.sql);\n          setViewUpdateDataSqlModal(true);\n          setInitError(res.message);\n        }\n      })\n      .finally(() => {\n        setTableLoading(false);\n      });\n  };\n\n  // 获取表格数据 接受一个参数params 包含IExecuteSqlParams中的一个或多个\n  const getTableData = (params?: Partial<IExecuteSqlParams>) => {\n    setTableLoading(true);\n    setCurOperationRowNo(null);\n    setEditingCell(null);\n    const executeSQLParams: IExecuteSqlParams = {\n      sql: queryResultData.originalSql,\n      dataSourceId: props.executeSqlParams?.dataSourceId,\n      databaseName: props.executeSqlParams?.databaseName,\n      schemaName: props.executeSqlParams?.schemaName,\n      pageNo: paginationConfig.pageNo,\n      pageSize: paginationConfig.pageSize,\n      ...(params || {}),\n    };\n\n    return sqlService.executeSql(executeSQLParams).then((res) => {\n      setTableLoading(false);\n      setQueryResultData(res?.[0]);\n      setUpdateData([]);\n    });\n  };\n\n  // sql执行成功后的回调\n  const executeSuccessCallBack = () => {\n    getTableData().then(() => {\n      setViewUpdateDataSqlModal(false);\n      message.success(i18n('common.text.successfulExecution'));\n    });\n  };\n\n  const { multipleSelect } = useMultipleSelect({\n    setCurOperationRowNo,\n    tableData,\n    colNoCode,\n    curOperationRowNo,\n    setFocusedContent,\n  });\n\n  const handelRowNoClick = (rowId: string) => {\n    multipleSelect(rowId);\n    setEditingCell(null);\n    // const newRowData = tableData.find((item) => item[colNoCode] === rowId)!;\n    // const newRowDataList = Object.keys(newRowData).map((item) => newRowData[item]);\n    // newRowDataList.splice(0, 1);\n  };\n\n  // 表格 列配置\n  const columns: ArtColumn[] = useMemo(() => {\n    return (queryResultData.headerList || []).map((item, colIndex) => {\n      const { dataType, name } = item;\n      const isNumber = dataType === TableDataType.NUMERIC;\n      const isNumericalOrder = dataType === TableDataType.CHAT2DB_ROW_NUMBER;\n      const colId = `${preCode}${colIndex}${name}`;\n\n      if (isNumericalOrder) {\n        return {\n          code: colNoCode,\n          name: 'No.',\n          title: (\n            <div\n              className={styles.allSelectBox}\n              onClick={() => {\n                setEditingCell(null);\n                if (curOperationRowNo) {\n                  setCurOperationRowNo(null);\n                  return;\n                }\n                // 全选列\n                const rowIds = tableData.map((i) => i[colNoCode]!);\n                setCurOperationRowNo(rowIds);\n              }}\n            />\n          ),\n          key: name,\n          lock: true,\n          // features: { sortable: compareStrings },\n          render: (value: any, rowData, rowIndex) => {\n            const rowId = rowData[colNoCode];\n            return (\n              <div\n                data-chat2db-general-can-copy-element\n                data-chat2db-edit-table-data-can-paste\n                data-chat2db-edit-table-data-can-right-click\n                onClick={() => {\n                  handelRowNoClick(rowId);\n                }}\n                onContextMenu={() => {\n                  if (!curOperationRowNo?.includes(rowId)) {\n                    handelRowNoClick(rowId);\n                  }\n                }}\n                className={tableCellStyle(value, rowId, colNoCode)}\n              >\n                <div className={styles.tableItemNo}>{rowIndex + 1}</div>\n              </div>\n            );\n          },\n        };\n      }\n\n      return {\n        code: colId,\n        name: name,\n        key: name,\n        // title: <div>{name}</div>,\n        render: (value: any, rowData) => {\n          const rowId = rowData[colNoCode];\n          const content = renderTableCellValue(value);\n          return (\n            <div\n              data-chat2db-general-can-copy-element\n              data-chat2db-edit-table-data-can-paste\n              data-chat2db-edit-table-data-can-right-click\n              className={tableCellStyle(value, rowId, colId)}\n              onClick={handleClickTableItem.bind(null, colId, rowId, value, false)}\n              onDoubleClick={handleClickTableItem.bind(null, colId, rowId, value, true)}\n              onContextMenu={handleClickTableItem.bind(null, colId, rowId, value, false)}\n            >\n              {editingCell?.[0] === colId && editingCell?.[1] === rowId && editingCell?.[2] ? (\n                <Input\n                  ref={editDataInputRef}\n                  value={transformInputValue(editingData) as any}\n                  onChange={(e) => {\n                    setEditingData(e.target.value);\n                  }}\n                  onBlur={() => {\n                    setEditingCell([editingCell![0], editingCell![1], false]);\n                    updateTableData('setCell', editingData);\n                  }}\n                />\n              ) : (\n                <>\n                  <div className={styles.tableItemContent}>{content}</div>\n                  <div className={styles.previewTableItemContent}>{content}</div>\n                </>\n              )}\n            </div>\n          );\n        },\n        // 如果是数字类型，因为后端返回的都是字符串，所以需要调用字符串对比函数来判断\n        features: { sortable: isNumber ? compareStrings : true },\n      };\n    });\n  }, [queryResultData.headerList, editingCell, editingData, curOperationRowNo, oldDataList]);\n\n  const { updateTableData, handleCreateData, handleDeleteData } = useCurdTableData({\n    tableData,\n    setTableData,\n    preCode,\n    editingCell,\n    columns,\n    curOperationRowNo,\n    oldDataList,\n    updateData,\n    setUpdateData,\n    queryResultData,\n    setCurOperationRowNo,\n    setEditingCell,\n    tableBoxRef,\n    oldTableData,\n    colNoCode,\n  });\n\n  // 处理粘贴的数据 hooks\n  usePasteData({ updateTableData, curOperationRowNo, editingCell });\n\n  // 表格渲染的配置\n  const pipeline = useTablePipeline()\n    .input({ dataSource: tableData, columns })\n    .use(\n      features.sort({\n        mode: 'single',\n        defaultSorts,\n        highlightColumnWhenActive: true,\n        // sorts,\n        // onChangeSorts,\n      }),\n    )\n    .use(\n      features.columnResize({\n        fallbackSize: 150,\n        // handleBackground: '#ddd',\n        handleHoverBackground: `var(--color-primary-bg-hover)`,\n        handleActiveBackground: `var(--color-primary-bg-hover)`,\n        minSize: 60,\n        maxSize: 1080,\n        sizes: columnResize,\n        onChangeSizes: (sizes) => {\n          sizes[0] = 0;\n          setColumnResize(sizes);\n        },\n      }),\n    );\n\n  const getSelectTableRowData = () => {\n    if (!curOperationRowNo && !editingCell) {\n      return [[]];\n    }\n    const rowIds = curOperationRowNo || [editingCell?.[1]];\n    const newRowDatas = tableData.filter((item) => rowIds.includes(item[colNoCode]!));\n    const newRowDatasList = newRowDatas.map((item) => {\n      const _item = lodash.cloneDeep(item);\n      delete _item[colNoCode];\n      return Object.keys(_item).map((i) => _item[i]);\n    });\n    return newRowDatasList;\n  };\n\n  // 右键菜单配置项\n  const copyRow = {\n    key: AllSupportedMenusType.CopyRow,\n    children: [\n      {\n        callback: () => {\n          const rowIds = curOperationRowNo || [editingCell![1]];\n          const newRowDatas = tableData.filter((item) => rowIds.includes(item[colNoCode]!));\n          const newRowDatasList = newRowDatas.map((item) => {\n            const _item = lodash.cloneDeep(item);\n            return Object.keys(_item).map((i) => _item[i]);\n          });\n          const _updateDatas = newRowDatasList.map((item, index) => {\n            return {\n              type: CRUD.CREATE,\n              dataList: item,\n              rowId: (tableData.length + index + 1).toString(),\n            };\n          });\n\n          getExecuteUpdateSql(_updateDatas).then((res) => {\n            copy(res);\n          });\n        },\n        hide: !queryResultData.canEdit,\n      },\n      {\n        callback: () => {\n          const rowIds = curOperationRowNo || [editingCell![1]];\n          const newRowDatas = tableData.filter((item) => rowIds.includes(item[colNoCode]!));\n          const newRowDatasList = newRowDatas.map((item) => {\n            const _item = lodash.cloneDeep(item);\n            return Object.keys(_item).map((i) => _item[i]);\n          });\n          const _updateDatas = newRowDatasList.map((item, index) => {\n            return {\n              type: CRUD.UPDATE_COPY,\n              dataList: item,\n              rowId: (tableData.length + index + 1).toString(),\n            };\n          });\n\n          getExecuteUpdateSql(_updateDatas).then((res) => {\n            copy(res);\n          });\n        },\n        hide: !queryResultData.canEdit,\n      },\n      // 复制当前行的数据\n      {\n        callback: () => {\n          const selectTableRowData = getSelectTableRowData();\n          tableCopy(selectTableRowData);\n        },\n      },\n      // 复制表头\n      {\n        callback: () => {\n          const headerList = queryResultData.headerList.map((item) => item.name);\n          // 去掉No列\n          headerList.splice(0, 1);\n          tableCopy([headerList]);\n        },\n      },\n      // 复制表头和当前行的数据\n      {\n        callback: () => {\n          const rowIds = curOperationRowNo || [editingCell![1]];\n          const newRowDatas = tableData.filter((item) => rowIds.includes(item[colNoCode]!));\n          const newRowDatasList = newRowDatas.map((item) => {\n            const _item = lodash.cloneDeep(item);\n            delete _item[colNoCode];\n            return Object.keys(_item).map((i) => _item[i]);\n          });\n          const headerList = queryResultData.headerList.map((item) => item.name);\n          // 去掉No列\n          headerList.splice(0, 1);\n          tableCopy([headerList, ...newRowDatasList]);\n        },\n      },\n    ],\n  };\n\n  const cloneRow = {\n    key: AllSupportedMenusType.CloneRow,\n    callback: () => {\n      const newTableData = lodash.cloneDeep(tableData);\n      const rowIds = curOperationRowNo || [editingCell![1]];\n      // 在newTableData中找出 rowIds中所有的行\n      const newRowDatas = newTableData.filter((item) => rowIds.includes(item[colNoCode]!));\n      newRowDatas.map((t, i) => {\n        t[colNoCode] = (newTableData.length + i + 1).toString();\n      });\n      handleCreateData(newRowDatas);\n    },\n  };\n\n  const deleteRow = {\n    key: AllSupportedMenusType.DeleteRow,\n    callback: handleDeleteData,\n  };\n\n  const copyCell = {\n    key: AllSupportedMenusType.CopyCell,\n    callback: () => {\n      copy(editingData);\n    },\n  };\n\n  const setDefault = {\n    key: AllSupportedMenusType.SetDefault,\n    callback: () => {\n      updateTableData('setCell', USER_FILLED_VALUE.DEFAULT);\n    },\n  };\n\n  const setNull = {\n    key: AllSupportedMenusType.SetNull,\n    callback: () => {\n      updateTableData('setCell', null);\n    },\n  };\n\n  const viewData = {\n    key: AllSupportedMenusType.ViewData,\n    callback: () => {\n      setViewTableCellData({\n        name: columns.find((i) => i.code === editingCell![0])!.name,\n        value: editingData,\n        colId: editingCell![0],\n        rowId: editingCell![1],\n      });\n    },\n  };\n\n  const rowRightClickMenu = useMemo(() => {\n    let rightClickMenu: any = [];\n    if (curOperationRowNo) {\n      rightClickMenu = [copyRow, cloneRow, deleteRow];\n      // 如果当前数据不可编辑，则不显示cloneRow和deleteRow\n      if (!queryResultData.canEdit) {\n        rightClickMenu = rightClickMenu.filter(\n          (i) => i.key !== AllSupportedMenusType.CloneRow && i.key !== AllSupportedMenusType.DeleteRow,\n        );\n      }\n    }\n\n    if (editingCell) {\n      rightClickMenu = [viewData, copyCell, copyRow, cloneRow, setNull, setDefault, deleteRow];\n      // 判断是否有默认值,如果没有默认值，则不显示设置默认值的菜单\n      const colId = editingCell[0];\n      const hasDefaultValue =\n        queryResultData.headerList.find((item) => {\n          return item.name === columns.find((i) => i.code === colId)?.name;\n        })?.defaultValue !== null;\n\n      if (!hasDefaultValue) {\n        rightClickMenu = rightClickMenu.filter((i) => i.key !== AllSupportedMenusType.SetDefault);\n      }\n      // 如果当前数据不可编辑，则不显示cloneRow和deleteRow\n      if (!queryResultData.canEdit) {\n        rightClickMenu = rightClickMenu.filter(\n          (i) =>\n            i.key !== AllSupportedMenusType.CloneRow &&\n            i.key !== AllSupportedMenusType.DeleteRow &&\n            i.key !== AllSupportedMenusType.SetNull,\n        );\n      }\n    }\n\n    if (!curOperationRowNo && !editingCell) {\n      return null;\n    }\n    return rightClickMenu;\n  }, [curOperationRowNo, editingCell, queryResultData]);\n\n  const renderContent = () => {\n    const bottomStatus = (\n      <div className={styles.statusBar}>\n        <span>{`【${i18n('common.text.result')}】${queryResultData.description}.`}</span>\n        <span>{`【${i18n('common.text.timeConsuming')}】${queryResultData.duration}ms.`}</span>\n        <span>{`【${i18n('common.text.searchRow')}】${tableData.length} ${i18n('common.text.row')}.`}</span>\n      </div>\n    );\n\n    if (!columns.length) {\n      return (\n        <>\n          <StateIndicator state=\"success\" text={i18n('common.text.successfulExecution')} />\n          <div style={{ position: 'absolute', bottom: 0, left: 0, right: 0 }}>{bottomStatus}</div>\n        </>\n      );\n    } else {\n      return (\n        <>\n          <div className={styles.toolBar}>\n            <div className={styles.toolBarItem}>\n              <MyPagination\n                paginationConfig={paginationConfig}\n                onPageNoChange={onPageNoChange}\n                onPageSizeChange={onPageSizeChange}\n                onClickTotalBtn={onClickTotalBtn}\n              />\n            </div>\n            <div className={classnames(styles.toolBarItem, styles.refreshBar)}>\n              {/* 刷新 */}\n              <Popover mouseEnterDelay={0.8} content={i18n('common.button.refresh')} trigger=\"hover\">\n                <div\n                  onClick={() => {\n                    getTableData();\n                  }}\n                  className={classnames(styles.refreshIconBox)}\n                >\n                  <Iconfont code=\"&#xe62d;\" />\n                </div>\n              </Popover>\n            </div>\n            {queryResultData.canEdit && (\n              <div className={classnames(styles.toolBarItem, styles.editTableDataBar)}>\n                {/* 新增行 */}\n                <Popover mouseEnterDelay={0.8} content={i18n('editTableData.tips.addRow')} trigger=\"hover\">\n                  <div\n                    onClick={() => {\n                      handleCreateData();\n                    }}\n                    className={classnames(styles.createDataBar, styles.editTableDataBarItem)}\n                  >\n                    <Iconfont code=\"&#xe61b;\" />\n                  </div>\n                </Popover>\n                {/* 删除行 */}\n                <Popover mouseEnterDelay={0.8} content={i18n('editTableData.tips.deleteRow')} trigger=\"hover\">\n                  <div\n                    onClick={() => {\n                      handleDeleteData();\n                    }}\n                    className={classnames(styles.deleteDataBar, styles.editTableDataBarItem, {\n                      [styles.disableBar]: curOperationRowNo === null,\n                    })}\n                  >\n                    <Iconfont code=\"&#xe644;\" />\n                  </div>\n                </Popover>\n                {/* 撤销 */}\n                <Popover mouseEnterDelay={0.8} content={i18n('editTableData.tips.revert')} trigger=\"hover\">\n                  <div\n                    onClick={handleRevoke}\n                    className={classnames(styles.revokeBar, styles.editTableDataBarItem, {\n                      [styles.disableBar]: revokeDisableBarState,\n                    })}\n                  >\n                    <Iconfont code=\"&#xe6e2;\" />\n                  </div>\n                </Popover>\n                {/* 查看更改sql */}\n                <Popover\n                  mouseEnterDelay={0.8}\n                  content={i18n('editTableData.tips.previewPendingChanges')}\n                  trigger=\"hover\"\n                >\n                  <div\n                    onClick={handleViewSql}\n                    className={classnames(styles.viewSqlBar, styles.editTableDataBarItem, {\n                      [styles.disableBar]: !updateData.length,\n                    })}\n                  >\n                    <Iconfont code=\"&#xe654;\" />\n                  </div>\n                </Popover>\n                {/* 提交 */}\n                <Popover mouseEnterDelay={0.8} content={i18n('editTableData.tips.submit')} trigger=\"hover\">\n                  <div\n                    onClick={handleUpdateSubmit}\n                    className={classnames(styles.updateSubmitBar, styles.editTableDataBarItem, {\n                      [styles.disableBar]: !updateData.length,\n                    })}\n                  >\n                    <Iconfont code=\"&#xe687;\" />\n                  </div>\n                </Popover>\n              </div>\n            )}\n            <div className={styles.toolBarRight}>\n              <Dropdown menu={{ items: exportDropdownItems }} trigger={['click']}>\n                <Space className={styles.exportBar}>\n                  {i18n('common.text.export')}\n                  <DownOutlined />\n                </Space>\n              </Dropdown>\n            </div>\n          </div>\n          {concealTabHeader && <ScreeningResult getTableData={getTableData} promptWord={queryResultData.headerList} />}\n          {isActive ? (\n            <RightClickMenu menuList={rowRightClickMenu}>\n              <div\n                ref={tableBoxRef}\n                className={classnames(styles.supportBaseTableBox, { [styles.supportBaseTableBoxHidden]: tableLoading })}\n              >\n                {allDataReady && (\n                  <>\n                    {tableLoading && <Spin className={styles.supportBaseTableSpin} />}\n                    <SupportBaseTable\n                      className={classnames('supportBaseTable', props.className, styles.table)}\n                      components={{ EmptyContent: () => <h2>{i18n('common.text.noData')}</h2> }}\n                      isStickyHead\n                      stickyTop={31}\n                      {...pipeline.getProps()}\n                    />\n                  </>\n                )}\n              </div>\n            </RightClickMenu>\n          ) : (\n            <div className={styles.supportBaseTableBox} />\n          )}\n          <StatusBar\n            description={queryResultData.description}\n            duration={queryResultData.duration}\n            dataLength={tableData.length}\n          />\n        </>\n      );\n    }\n  };\n\n  const renderMonacoEditor = useMemo(() => {\n    return (\n      <div className={styles.monacoEditor}>\n        <MonacoEditor\n          ref={monacoEditorRef}\n          id={`view_table-Cell_data-${uuid()}`}\n          appendValue={{\n            text: transformInputValue(viewTableCellData?.value),\n            range: 'reset',\n          }}\n          language=\"plaintext\"\n          options={{\n            lineNumbers: 'off',\n            readOnly: !queryResultData.canEdit,\n          }}\n        />\n      </div>\n    );\n  }, [queryResultData, viewTableCellData]);\n\n  return (\n    <div className={classnames(className, styles.tableBox, { [styles.noDataTableBox]: !tableData.length })}>\n      {renderContent()}\n      <Modal\n        title={viewTableCellData?.name}\n        open={!!viewTableCellData?.name}\n        onCancel={handleCancel}\n        width=\"60vw\"\n        maskClosable={false}\n        destroyOnClose={true}\n        footer={\n          queryResultData.canEdit && [\n            <Button key=\"1\" type=\"primary\" onClick={monacoEditorEditData}>\n              {i18n('common.button.modify')}\n            </Button>,\n          ]\n        }\n      >\n        {renderMonacoEditor}\n      </Modal>\n      <Modal\n        width=\"60vw\"\n        maskClosable={false}\n        title={initError ? i18n('common.button.executionError') : i18n('editTable.title.sqlPreview')}\n        open={viewUpdateDataSqlModal}\n        footer={false}\n        destroyOnClose={true}\n        onCancel={() => {\n          setViewUpdateDataSqlModal(false);\n          setUpdateDataSql('');\n          setInitError(null);\n        }}\n      >\n        <ExecuteSQL\n          initError={initError}\n          initSql={updateDataSql}\n          databaseName={props.executeSqlParams?.databaseName}\n          dataSourceId={props.executeSqlParams?.dataSourceId}\n          tableName={queryResultData.tableName}\n          schemaName={props.executeSqlParams?.schemaName}\n          databaseType={props.executeSqlParams?.databaseType}\n          executeSuccessCallBack={executeSuccessCallBack}\n          executeSqlApi=\"executeUpdateDataSql\"\n        />\n      </Modal>\n      {contextHolder}\n    </div>\n  );\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/hooks/useCurdTableData.ts",
    "content": "import lodash from 'lodash';\nimport { CRUD } from '@/constants';\nimport { USER_FILLED_VALUE, IUpdateData } from '../components/TableBox/index';\n\nexport interface IProps {\n  preCode: string;\n  //\n  tableData: { [key: string]: string | null }[];\n  setTableData: (tableData: { [key: string]: string | null }[]) => void;\n  //\n  editingCell: [string, string, boolean] | null;\n  setEditingCell: (editingCell: [string, string, boolean] | null) => void;\n  //\n  updateData: IUpdateData[];\n  setUpdateData: (updateData: IUpdateData[]) => void;\n  //\n  curOperationRowNo: Array<string> | null;\n  setCurOperationRowNo: (curOperationRowNo: Array<string> | null) => void;\n  //\n  columns;\n  oldDataList;\n  queryResultData;\n  tableBoxRef;\n  oldTableData;\n  colNoCode;\n}\n\nconst useCurdTableData = (props: IProps) => {\n  const {\n    tableData,\n    setTableData,\n    preCode,\n    editingCell,\n    columns,\n    curOperationRowNo,\n    oldDataList,\n    updateData,\n    setUpdateData,\n    queryResultData,\n    setCurOperationRowNo,\n    setEditingCell,\n    tableBoxRef,\n    oldTableData,\n    colNoCode,\n  } = props;\n\n  // 编辑数据\n  const updateTableData = (type: 'setCell' | 'setRow', _data: string | null | Array<string | null>) => {\n    const newTableData = lodash.cloneDeep(tableData);\n    let oldRowDataList: Array<string | null> = [];\n    let newRowDataList: Array<string | null> = [];\n    let curRowNo: string | null = null;\n    if (type === 'setCell' && (typeof _data === 'string' || _data === null)) {\n      const [colId, rowId] = editingCell!;\n      curRowNo = rowId;\n      newTableData.forEach((item) => {\n        if (item[colNoCode] === rowId) {\n          item[colId] = _data;\n          newRowDataList = Object.keys(item).map((i) => item[i]);\n        }\n      });\n    }\n\n    if (type === 'setRow' && Array.isArray(_data) && curOperationRowNo) {\n      curRowNo = curOperationRowNo[0];\n      _data.unshift(curOperationRowNo[0]);\n      newTableData.forEach((t) => {\n        if (t[colNoCode] === curOperationRowNo[0]) {\n          const dataLength = Object.keys(t).length;\n          Object.keys(t).forEach((item, index) => {\n            if (index > dataLength) return;\n            t[item] = _data[index] || null;\n          });\n          return;\n        }\n      });\n      newRowDataList = _data;\n    }\n\n    setTableData(newTableData);\n\n    oldDataList.forEach((item) => {\n      if (item[0] === curRowNo) {\n        oldRowDataList = item;\n      }\n    });\n\n    const index = updateData.findIndex((item) => item.rowId === curRowNo);\n    // 如果newRowDataList和oldRowDataList的数据一样，代表用户虽然编辑过，但是又改回去了，则不需要更新\n    if (oldRowDataList?.join(',') === newRowDataList?.join(',')) {\n      if (index !== -1) {\n        setUpdateData(updateData.filter((item) => item.rowId !== curRowNo && item.type !== CRUD.UPDATE));\n      }\n      return;\n    }\n\n    if (index === -1) {\n      setUpdateData([\n        ...updateData,\n        {\n          type: CRUD.UPDATE,\n          oldDataList: oldRowDataList,\n          dataList: newRowDataList,\n          rowId: curRowNo!,\n        },\n      ]);\n      return;\n    }\n\n    const newRowUpdateData = {\n      ...updateData[index],\n      dataList: newRowDataList,\n    };\n\n    // 如果是删除过的，则需要把type改为update\n    if (newRowUpdateData.type === CRUD.DELETE) {\n      newRowUpdateData.type = CRUD.UPDATE;\n    }\n\n    updateData[index] = newRowUpdateData;\n    setUpdateData([...updateData]);\n  };\n\n  // 处理创建数据\n  const handleCreateData = (_newData?: { [key in string]: any }[]) => {\n    // 正常的新增\n    const newTableData = lodash.cloneDeep(tableData);\n    let newData: { [key in string]: any }[] = [{}];\n    if (_newData) {\n      newData = _newData;\n    } else {\n      newData.forEach((newDataItem, index) => {\n        columns.forEach((t, i) => {\n          if (t.name === 'No.') {\n            newDataItem[`${preCode}${i}${t.name}`] = (newTableData.length + index + 1).toString();\n          } else {\n            // 判断是否有默认值\n            const hasDefaultValue =\n              queryResultData.headerList.find((item) => item.name === t.name)?.defaultValue !== null;\n            if (hasDefaultValue) {\n              newDataItem[`${preCode}${i}${t.name}`] = USER_FILLED_VALUE.DEFAULT;\n              return;\n            }\n            newDataItem[`${preCode}${i}${t.name}`] = null;\n          }\n        });\n      });\n    }\n\n    setTableData(newTableData.concat(newData));\n\n    const newUpdateData = newData.map((item, index) => {\n      return {\n        type: CRUD.CREATE,\n        dataList: Object.keys(item).map((i) => item[i]),\n        rowId: (newTableData.length + index + 1).toString(),\n      };\n    });\n\n    setUpdateData([...updateData, ...newUpdateData]);\n\n    setCurOperationRowNo([(newTableData.length + 1).toString()]);\n    setEditingCell(null);\n\n    // 新增一条数据，tableBox需要滚动到最下方\n    setTimeout(() => {\n      tableBoxRef.current?.scrollTo(0, tableBoxRef.current?.scrollHeight + 31);\n    }, 0);\n  };\n\n  // 处理删除数据\n  const handleDeleteData = () => {\n    if (!curOperationRowNo && !editingCell) {\n      return;\n    }\n\n    const rowIds = curOperationRowNo || [editingCell![1]];\n    const needDeleteNewData:any = [];\n    const needRecoverData:any = [];\n    const needDeleteData:any = [];\n    let _updateData = lodash.cloneDeep(updateData);\n    let _tableData = lodash.cloneDeep(tableData);\n\n\n    rowIds.forEach((rowId: string) => {\n      let flag = false\n      _updateData.forEach((item) => {\n        if (item.rowId === rowId) {\n          if (item.type === CRUD.CREATE) {\n            flag = true\n            needDeleteNewData.push(rowId);\n          } else if(item.type === CRUD.UPDATE){\n            flag = true\n            needRecoverData.push(rowId);\n          }\n        }\n      });\n      if(!flag) {\n        needDeleteData.push(rowId);\n      }\n    });\n\n    // 删除新增的行\n    needDeleteNewData.forEach((rowId) => {\n      _updateData = _updateData.filter((item) => item.rowId !== rowId);\n      _tableData = _tableData.filter((item) => item[colNoCode] !== rowId);\n    });\n\n    // 删除编辑的行，需要把这一行的数据恢复\n    needRecoverData.forEach((rowId) => {\n      const oldRowDataList = _updateData.find((item) => item.rowId === rowId)?.oldDataList;\n\n      _tableData = _tableData.map((item) => {\n        if (item[colNoCode] === rowId) {\n          return oldTableData.find((i) => item[colNoCode] === i[colNoCode])!;\n        }\n        return item;\n      })\n\n      const index = _updateData.findIndex((item) => item.rowId === rowId);\n      _updateData[index] = {\n        ..._updateData[index],\n        type: CRUD.DELETE,\n        oldDataList: oldRowDataList,\n      };\n    });\n\n    needDeleteData.forEach((rowId) => {\n      const oldRowData = oldDataList.find((item) => item[0] === rowId);\n      _updateData.push({\n        type: CRUD.DELETE,\n        oldDataList: Object.keys(oldRowData!).map((i) => oldRowData![i]),\n        rowId,\n      })\n    });\n\n    setUpdateData(_updateData);\n    setTableData(_tableData);\n    setEditingCell(null);\n    setCurOperationRowNo(null);\n  };\n\n  return {\n    updateTableData,\n    handleCreateData,\n    handleDeleteData,\n  };\n};\n\nexport default useCurdTableData;\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/hooks/useMultipleSelect.ts",
    "content": "import { useEffect, useState, useRef, useCallback } from 'react';\nimport lodash from 'lodash';\n\n// 多选行 hooks\nconst useMultipleSelect = (props: {\n  curOperationRowNo: Array<string> | null;\n  setCurOperationRowNo: (rowNo: Array<string> | null) => void;\n  tableData: { [key: string]: string | null }[];\n  colNoCode: string;\n  setFocusedContent: (content: any[][]) => void;\n}) => {\n  const { curOperationRowNo, setCurOperationRowNo, tableData, colNoCode,setFocusedContent } = props;\n  // 是否按下了shift键\n  const isShiftDownRef = useRef<boolean>(false);\n  // 是否按下了cmd键\n  const isCmdDownRef = useRef<boolean>(false);\n\n  // 如果用useState，因为异步会导致第一次点击失效\n  // const [isShiftDown, setIsShiftDown] = useState<boolean>(false);\n\n  // 第一次选中的行号，用于判断是否是连续选中\n  const [firstOperationRowNo, setFirstOperationRowNo] = useState<string | null>(null);\n\n  useEffect(()=>{\n    if(!curOperationRowNo){\n      setFirstOperationRowNo(null)\n    }\n  },[curOperationRowNo])\n\n  useEffect(() => {\n    const handleKeyDown = (event) => {\n      if (event.keyCode === 16) {\n        isShiftDownRef.current = true;\n      }\n      if (event.keyCode === 91 || event.keyCode === 17) {\n        isCmdDownRef.current = true;\n      }\n    };\n\n    const handleKeyUp = (event) => {\n      if (event.keyCode === 16) {\n        isShiftDownRef.current = false;\n      }\n      if (event.keyCode === 91) {\n        isCmdDownRef.current = false;\n      }\n    };\n\n    document.addEventListener('keydown', handleKeyDown);\n    document.addEventListener('keyup', handleKeyUp);\n    return () => {\n      document.removeEventListener('keydown', handleKeyDown);\n      document.removeEventListener('keyup', handleKeyUp);\n    };\n  }, []);\n\n  const copyTableRowData = (rowIds)=>{\n    const newRowDatas = tableData.filter((item) => rowIds.includes(item[colNoCode]!));\n    const newRowDatasList = newRowDatas.map((item) => {\n      const _item = lodash.cloneDeep(item);\n      delete _item[colNoCode];\n      return Object.keys(_item).map((i) => _item[i]);\n    });\n    setFocusedContent(newRowDatasList);\n  }\n\n  const multipleSelect = useCallback(\n    (newClickRowNo: string | null) => {\n      if (newClickRowNo === null) {\n        setCurOperationRowNo(null);\n        return;\n      }\n\n      if (isShiftDownRef.current && firstOperationRowNo) {\n        // 1. 在tableData中找到firstOperationRowNo所在的index\n        const firstOperationRowIndex = tableData.findIndex((item) => item[colNoCode] === firstOperationRowNo);\n        // 2. 在tableData中找到newClickRowNo所在的index\n        const newClickRowIndex = tableData.findIndex((item) => item[colNoCode] === newClickRowNo);\n        // 3. 从table中截取firstOperationRowIndex到newClickRowIndex的数据\n        // 前序号\n        let front = 0;\n        // 后序号\n        let back = 0;\n        if (firstOperationRowIndex < newClickRowIndex) {\n          front = firstOperationRowIndex;\n          back = newClickRowIndex;\n        } else {\n          front = newClickRowIndex;\n          back = firstOperationRowIndex;\n        }\n        const newCurOperationRowNo = tableData.slice(front, back + 1).map((item) => item[colNoCode]!);\n        setCurOperationRowNo(newCurOperationRowNo);\n        copyTableRowData(newCurOperationRowNo)\n        return;\n      }\n\n      if (isCmdDownRef.current) {\n        // 如果是cmd键，就是多选\n        if (curOperationRowNo) {\n          if (curOperationRowNo.includes(newClickRowNo)) {\n            // 如果已经选中了，就取消选中\n            const newCurOperationRowNo = curOperationRowNo.filter((item) => item !== newClickRowNo);\n            setCurOperationRowNo(newCurOperationRowNo);\n            copyTableRowData(newCurOperationRowNo)\n            return;\n          }\n          // 如果没有选中，就添加选中\n          const newCurOperationRowNo = [...curOperationRowNo, newClickRowNo];\n          setCurOperationRowNo(newCurOperationRowNo);\n          copyTableRowData(newCurOperationRowNo)\n          return;\n        }\n        // 如果没有选中，就添加选中\n        setCurOperationRowNo([newClickRowNo]);\n        copyTableRowData([newClickRowNo])\n        return;\n      }\n\n      setFirstOperationRowNo(newClickRowNo);\n      setCurOperationRowNo([newClickRowNo]);\n      copyTableRowData([newClickRowNo])\n    },\n    [isShiftDownRef.current, firstOperationRowNo,curOperationRowNo],\n  );\n\n  return {\n    multipleSelect\n  };\n};\n\nexport default useMultipleSelect;\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/hooks/usePasteData.ts",
    "content": "import { useEffect, useState } from 'react';\nimport { clipboardToArray } from '@/utils';\n\ninterface IUsePasteDataRelyData {\n  curOperationRowNo: Array<string> | null;\n  editingCell;\n  updateTableData;\n}\n\n// 处理粘贴的数据 hooks\nconst usePasteData = (props: IUsePasteDataRelyData) => {\n  const { curOperationRowNo, editingCell, updateTableData } = props;\n  const [canPaste, setCanPaste] = useState<boolean>(false);\n\n  // 判断当前是否可以粘贴\n  useEffect(() => {\n    const handleClick = (event) => {\n      const targetElement = event.target as Element;\n      if (targetElement.closest('[data-chat2db-edit-table-data-can-paste]')) {\n        setCanPaste(true);\n      } else {\n        setCanPaste(false);\n      }\n    };\n    document.addEventListener('click', handleClick);\n    document.addEventListener('contextmenu', handleClick);\n    return () => {\n      document.removeEventListener('click', handleClick);\n      document.removeEventListener('contextmenu', handleClick);\n    };\n  }, []);\n\n  // 读取剪切板数据，更新表格数据\n  useEffect(() => {\n    const handleCopy = () => {\n      if (curOperationRowNo) {\n        navigator.clipboard\n          .readText()\n          .then((text) => {\n            const array2D = clipboardToArray(text);\n            updateTableData('setRow', array2D[0]);\n          })\n          .catch((err) => {\n            console.error('Failed to read clipboard contents: ', err);\n          });\n      }\n      if (editingCell && editingCell[2] === false) {\n        navigator.clipboard\n          .readText()\n          .then((text) => {\n            updateTableData('setCell', text);\n          })\n          .catch((err) => {\n            console.error('Failed to read clipboard contents: ', err);\n          });\n      }\n    };\n    if (canPaste) {\n      document.addEventListener('paste', handleCopy);\n    } else {\n      document.removeEventListener('paste', handleCopy);\n    }\n    return () => {\n      document.removeEventListener('paste', handleCopy);\n    };\n  }, [curOperationRowNo, editingCell, canPaste]);\n};\n\nexport default usePasteData;\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/index.less",
    "content": "@import '../../styles/var.less';\n\n.searchResult {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n}\n\n.tabs {\n  height: 100%;\n}\n\n.recordIcon {\n  font-size: 16px;\n  margin-right: 4px;\n}\n\n.statusIcon {\n  margin-right: 6px;\n  font-size: 12px;\n}\n\n.successIcon {\n  color: var(--color-primary);\n}\n\n.failIcon {\n  color: var(--color-primary);\n}\n\n.tableIndex {\n  width: 50px;\n}\n\n.monacoEditor {\n  height: 300px;\n}\n\n.monacoEditor {\n  margin: -15px;\n}\n\n.cursorStateIndicator {\n  margin: 0 auto;\n  max-width: 80%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.noData {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  font-size: 12px;\n  overflow: hidden;\n}\n\n.outputPrefixIcon {\n  margin-right: 4px;\n}\n\n.stateIndicator {\n  width: 70%;\n  margin: 0 auto;\n  overflow: hidden;\n}\n\n.updateCountBox {\n  height: 100%;\n}\n\n.successResult {\n  height: 100%;\n  .successResultContent {\n    height: 100%;\n  }\n\n  .updateCount {\n    height: calc(100% - 26px);\n    display: flex;\n    justify-content: center;\n    align-items: center;\n  }\n}\n\n.tableLoading {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 9999;\n  // height: 100%;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  // background-color: var(--color-fill);\n  .stopExecuteSql {\n    cursor: pointer;\n    margin-top: 30px;\n    &:hover {\n      color: var(--color-primary);\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/index.tsx",
    "content": "import React, {\n  useCallback,\n  useEffect,\n  useMemo,\n  useState,\n  useRef,\n  forwardRef,\n  ForwardedRef,\n  useImperativeHandle,\n  Fragment,\n  createContext,\n} from 'react';\nimport classnames from 'classnames';\nimport Tabs, { ITabItem } from '@/components/Tabs';\nimport Iconfont from '@/components/Iconfont';\nimport StateIndicator from '@/components/StateIndicator';\n// import Output from '@/components/Output';\nimport { IManageResultData, IResultConfig } from '@/typings';\nimport TableBox from './components/TableBox';\nimport StatusBar from './components/StatusBar';\nimport styles from './index.less';\nimport EmptyImg from '@/assets/img/empty.svg';\nimport i18n from '@/i18n';\nimport sqlServer, { IExecuteSqlParams } from '@/service/sql';\nimport { v4 as uuidV4 } from 'uuid';\nimport { Spin } from 'antd';\n\ninterface IProps {\n  className?: string;\n  sql?: string;\n  executeSqlParams: any;\n  concealTabHeader?: boolean;\n  viewTable?: boolean;\n  isActive?: boolean;\n}\n\nconst defaultResultConfig: IResultConfig = {\n  pageNo: 1,\n  pageSize: 200,\n  total: 0,\n  hasNextPage: true,\n};\n\nexport interface ISearchResultRef {\n  handleExecuteSQL: (sql: string) => void;\n}\n\ninterface IContext {\n  // 这里不用ref的话，会导致切换时闪动\n  activeTabId: string;\n  notChangedSql: string;\n}\n\nexport const Context = createContext<IContext>({} as any);\n\nexport default forwardRef((props: IProps, ref: ForwardedRef<ISearchResultRef>) => {\n  const { className, sql, executeSqlParams, concealTabHeader, viewTable, isActive } = props;\n  const [resultDataList, setResultDataList] = useState<IManageResultData[]>();\n  const [tableLoading, setTableLoading] = useState(false);\n  const controllerRef = useRef<AbortController>();\n  const [activeTabId, setActiveTabId] = useState<string>('');\n  const [notChangedSql, setNotChangedSql] = useState<string>('');\n\n  useEffect(() => {\n    if (sql) {\n      handleExecuteSQL(sql);\n    }\n  }, [sql]);\n\n  useImperativeHandle(ref, () => ({\n    handleExecuteSQL,\n  }));\n\n  /**\n   * 执行SQL\n   * @param sql\n   */\n  const handleExecuteSQL = (_sql: string) => {\n    setTableLoading(true);\n    const api = viewTable ? sqlServer.viewTable : sqlServer.executeSql;\n\n    const executeSQLParams: IExecuteSqlParams = {\n      sql: _sql,\n      tableName: executeSqlParams?.tableName,\n      ...defaultResultConfig,\n      ...executeSqlParams,\n      type: executeSqlParams.databaseType, // 兼容写法，希望后端可以统一把type改成databaseType\n    };\n\n    controllerRef.current = new AbortController();\n    // 获取当前SQL的查询结果\n    api(executeSQLParams, {\n      signal: controllerRef.current.signal,\n    })\n      .then((res) => {\n        const sqlResult = res.map((_res) => ({\n          ..._res,\n          uuid: uuidV4(),\n        }));\n\n        setResultDataList(sqlResult);\n        if(!notChangedSql){\n          setNotChangedSql(_sql);\n        }\n      })\n      .finally(() => {\n        setTableLoading(false);\n      });\n  };\n\n  const onChange = useCallback((uuid) => {\n    // activeTabIdRef.current = uuid;\n    setActiveTabId(uuid);\n  }, []);\n\n  const renderResult = (queryResultData) => {\n    function renderSuccessResult() {\n      const needTable = queryResultData?.headerList?.length > 1;\n      return (\n        <div className={styles.successResult}>\n          <div className={styles.successResultContent}>\n            {needTable ? (\n              <TableBox\n                isActive={isActive}\n                tableBoxId={queryResultData.uuid}\n                key={queryResultData.uuid}\n                outerQueryResultData={queryResultData}\n                executeSqlParams={props.executeSqlParams}\n                concealTabHeader={concealTabHeader}\n              />\n            ) : (\n              <div className={styles.updateCountBox}>\n                <div className={styles.updateCount}>\n                  {i18n('common.text.affectedRows', queryResultData.updateCount)}\n                </div>\n                <StatusBar\n                  dataLength={queryResultData?.dataList?.length}\n                  duration={queryResultData.duration}\n                  description={queryResultData.description}\n                />\n              </div>\n            )}\n          </div>\n        </div>\n      );\n    }\n    return (\n      <Fragment key={queryResultData.uuid}>\n        {queryResultData.success ? (\n          renderSuccessResult()\n        ) : (\n          <StateIndicator\n            className={styles.stateIndicator}\n            key={queryResultData.uuid}\n            state=\"error\"\n            text={queryResultData.message}\n          />\n        )}\n      </Fragment>\n    );\n  };\n\n  const tabsList = useMemo(() => {\n    return resultDataList?.map((queryResultData, index) => {\n      return {\n        prefixIcon: (\n          <Iconfont\n            key={index}\n            className={classnames(styles[queryResultData.success ? 'successIcon' : 'failIcon'], styles.statusIcon)}\n            code={queryResultData.success ? '\\ue605' : '\\ue87c'}\n          />\n        ),\n        popover: queryResultData.originalSql,\n        label: i18n('common.text.executionResult', index + 1),\n        key: queryResultData.uuid!,\n        children: renderResult(queryResultData),\n      };\n    });\n  }, [resultDataList, isActive]);\n\n  const onEdit = useCallback(\n    (type: 'add' | 'remove', data: ITabItem[]) => {\n      if (type === 'remove') {\n        const newResultDataList = resultDataList?.filter((d) => {\n          return data.findIndex((item) => item.key === d.uuid) === -1;\n        });\n        setResultDataList(newResultDataList);\n      }\n    },\n    [resultDataList],\n  );\n\n  const stopExecuteSql = () => {\n    controllerRef.current && controllerRef.current.abort();\n    setResultDataList([]);\n    setTableLoading(false);\n  };\n\n  return (\n    <Context.Provider\n      value={{\n        activeTabId: activeTabId,\n        notChangedSql: notChangedSql,\n      }}\n    >\n      <div className={classnames(className, styles.searchResult)}>\n        {tableLoading ? (\n          <div className={styles.tableLoading}>\n            <Spin />\n            <div className={styles.stopExecuteSql} onClick={stopExecuteSql}>\n              {i18n('common.button.cancelRequest')}\n            </div>\n          </div>\n        ) : (\n          <>\n            {tabsList?.length ? (\n              <Tabs\n                hideAdd\n                className={styles.tabs}\n                onChange={onChange as any}\n                onEdit={onEdit as any}\n                items={tabsList}\n                concealTabHeader={concealTabHeader}\n                destroyInactiveTabPane={true}\n              />\n            ) : (\n              <div className={styles.noData}>\n                <img src={EmptyImg} />\n                <p>{i18n('common.text.noData')}</p>\n              </div>\n            )}\n          </>\n        )}\n      </div>\n    </Context.Provider>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/SearchResult/utils.tsx",
    "content": "import { USER_FILLED_VALUE } from './components/TableBox/index';\n\n// 在input中把USER_FILLED_VALUE转换为null\nexport const transformInputValue = (value: string) => {\n  if (value === USER_FILLED_VALUE.DEFAULT) {\n    return null;\n  }\n  return value;\n};\n"
  },
  {
    "path": "chat2db-client/src/components/ShortcutKey/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n\n  .letterpress{\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    font-size: 80px;\n    font-weight: 900;\n    color: var(--color-text);\n    opacity: 0.15;\n    overflow: hidden;\n    margin-bottom: 30px;\n  }\n\n  .shortcutsItem{\n    display: flex;\n    justify-content: center;\n    font-size: 14px;\n    margin: 10px 0px;\n    color: var(--color-text-tertiary);\n\n    .title{\n      width: 200px;\n      text-align: right;\n      margin-right: 10px;\n    }\n    .plusSignBox{\n      width: 200px;\n    }\n    .plusSign{\n      margin: 0px 4px;\n    }\n  }\n\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ShortcutKey/index.tsx",
    "content": "import React, { memo, Fragment } from 'react';\nimport i18n from '@/i18n';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { osNow } from '@/utils';\n\ninterface IProps {\n  className?: string;\n  slot: any;\n}\n\nconst keyboardKey = (function () {\n  if (osNow().isMac) {\n    return {\n      command: 'Cmd',\n      Shift: 'Shift',\n    };\n  }\n  return {\n    command: 'Ctrl',\n    Shift: 'Shift',\n  };\n})();\n\nconst shortcutsList = [\n  {\n    title: i18n('common.text.textToSQL'),\n    keys: ['Enter'],\n  },\n  {\n    title: i18n('common.text.optimizeSQL'),\n    keys: [i18n('common.text.editorRightClick')],\n  },\n  {\n    title: i18n('common.text.executeSelectedSQL'),\n    keys: [keyboardKey.command, 'R'],\n  },\n  {\n    title: i18n('common.text.saveConsole'),\n    keys: [keyboardKey.command, 'S'],\n  },\n  {\n    title: i18n('common.button.createConsole'),\n    keys: [keyboardKey.command, keyboardKey.Shift, 'L'],\n  },\n];\n\nexport default memo<IProps>((props) => {\n  const { className, slot } = props;\n  return (\n    <div className={classnames(styles.box, className)}>\n      <div className={styles.letterpress}>Chat2DB</div>\n      <div className={styles.shortcuts}>\n        {shortcutsList.map((t, i) => {\n          return (\n            <div key={i} className={styles.shortcutsItem}>\n              <div className={styles.title}>{t.title}</div>\n              <div className={styles.plusSignBox}>\n                {t.keys.map((item, index) => {\n                  return (\n                    <Fragment key={index}>\n                      <span>{item}</span>\n                      {index + 1 < t.keys.length && <span className={styles.plusSign}>+</span>}\n                    </Fragment>\n                  );\n                })}\n              </div>\n            </div>\n          );\n        })}\n      </div>\n      {slot()}\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/SingleFileMonacoEditor/index.less",
    "content": "@import '../../styles/var.less';\n\n.singleFileMonacoEditor {\n  height: 18px;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/SingleFileMonacoEditor/index.tsx",
    "content": "import React, { memo, useCallback, useMemo, ForwardedRef, forwardRef, useImperativeHandle, useRef } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport MonacoEditor, { IExportRefFunction } from '@/components/MonacoEditor';\nimport { v4 as uuid } from 'uuid';\n\ninterface IProps {\n  className?: string;\n  handelEnter?: (value: string) => void;\n  focusChange?: (isActive: boolean) => void;\n  ref: any; // ref不是写在这里吧？？？\n}\n\nexport interface ISingleFileMonacoEditorRefFunction {\n  getAllContent?: () => string;\n}\n\nconst options = {\n  lineNumbers: false,\n  renderLineHighlight: 'none',\n  scrollBeyondLastLine: false,\n  wordWrap: 'off',\n  minimap: {\n    enabled: false,\n  },\n  // 不显示滚动条\n  scrollbar: {\n    vertical: 'hidden',\n    horizontal: 'hidden',\n  },\n  overviewRulerBorder: false,\n  glyphMargin: false,\n  folding: false,\n  lineDecorationsWidth: 0, // 行号宽度\n  lineNumbersMinChars: 0, // 行号最小宽度\n};\n\nconst SingleFileMonacoEditor = memo<IProps>(\n  forwardRef((props, ref: ForwardedRef<ISingleFileMonacoEditorRefFunction>) => {\n    const { className, handelEnter, focusChange } = props;\n    const editorRef = useRef<any>(null);\n    const monacoEditorRef = useRef<IExportRefFunction>(null);\n\n    const editorId = useMemo(() => {\n      return uuid();\n    }, []);\n\n    const handleKeydown = useCallback((event) => {\n      if (event.key === 'Enter' && editorRef.current) {\n        const controller = editorRef.current.getContribution('editor.contrib.suggestController') as any;\n        const suggestWidget = controller._widget;\n        if (suggestWidget && suggestWidget.suggestWidgetVisible.get()) {\n          return;\n        }\n        // 否则，阻止回车键的默认行为\n        event.preventDefault();\n        const value = monacoEditorRef.current?.getAllContent().trim() || '';\n        handelEnter && handelEnter(value);\n      }\n    }, []);\n\n    // 监听keydown事件，阻止回车键的默认行为\n    const registerShortcutKey = useCallback((_editor, _monaco, isActive) => {\n      if (isActive) {\n        editorRef.current = _editor;\n        window.addEventListener('keydown', handleKeydown);\n      } else {\n        window.removeEventListener('keydown', handleKeydown);\n      }\n    }, []);\n\n    const getAllContent = () => {\n      return monacoEditorRef.current?.getAllContent() || '';\n    };\n\n    useImperativeHandle(ref, () => ({\n      getAllContent,\n    }));\n\n    return (\n      <div ref={ref as any} className={classnames(styles.singleFileMonacoEditor, className)}>\n        <MonacoEditor\n          ref={monacoEditorRef}\n          id={editorId}\n          options={options as any}\n          shortcutKey={registerShortcutKey}\n          focusChange={focusChange}\n        />\n      </div>\n    );\n  }),\n);\n\nexport default SingleFileMonacoEditor;\n"
  },
  {
    "path": "chat2db-client/src/components/StateIndicator/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n  height: 100%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.empty {\n  // width: 200px;\n  // height: 200px;\n  // background-image: url('../../assets/no-data.png');\n  background-size: cover;\n  background-repeat: no-repeat;\n}\n.errorBox {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n}\n.error {\n  width: 200px;\n  height: 200px;\n  // background-image: url('../../assets/error.png');\n  background-size: cover;\n  background-repeat: no-repeat;\n}\n\n.errorText {\n  font-size: 14px;\n  color: var(--color-error);\n  text-align: center;\n  transform: translateY(-20px);\n  white-space: pre-wrap;\n}\n\n.successBox {\n  .errorBox {\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n  }\n  // .success {\n  //   width: 200px;\n  //   height: 200px;\n  //   background-image: url('../../assets/error.png');\n  //   background-size: cover;\n  //   background-repeat: no-repeat;\n  // }\n\n  .successText {\n    font-size: 14px;\n    color: var(--success-color);\n    text-align: center;\n    transform: translateY(-20px);\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/components/StateIndicator/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Spin } from 'antd';\n\ninterface IProps {\n  className?: string;\n  state: 'loading' | 'empty' | 'error' | 'success';\n  text?: string;\n  image?: boolean;\n}\n\nexport const enum State {\n  LOADING = 'loading',\n  EMPTY = 'empty',\n  ERROR = 'error',\n  SUCCESS = 'success',\n}\n\nconst config = {\n  loading: {\n    icon: '\\ue6cd;',\n  },\n  empty: {\n    icon: '\\ue760',\n  },\n  error: {\n    icon: '\\ue755',\n  },\n  success: {\n    icon: '\\ue62e',\n  },\n};\n\nconst StateIndicator = ({ className, state, text, image = false }: IProps) => {\n  const renderState = () => {\n    switch (state) {\n      case 'loading':\n        return <Spin />;\n      case 'error':\n        return (\n          <div className={styles.errorBox}>\n            {image && <div className={classnames(className, styles[state])} />}\n            <div className={styles.errorText}>{text}</div>\n          </div>\n        );\n      case 'success':\n        return (\n          <div className={styles.successBox}>\n            {image && <div className={classnames(className, styles[state])} />}\n            <div className={styles.successText}>{text}</div>\n          </div>\n        );\n      default:\n        return <div className={classnames(className, styles[state])} />;\n    }\n  };\n  return <div className={classnames(className, styles.box)}>{renderState()}</div>;\n};\n\nexport default memo<IProps>(StateIndicator);\n"
  },
  {
    "path": "chat2db-client/src/components/Tabs/index.less",
    "content": "@import '../../styles/var.less';\n\n.tab-focus() {\n  background-color: var(--color-bg-subtle);\n  // 添加内阴影\n  box-shadow: inset 0px -1px 0px var(--color-primary);\n\n  .icon {\n    opacity: 1;\n  }\n}\n\n.tab-focus-line() {\n  color: var(--color-primary);\n  background-color: var(--color-bg-subtle);\n\n  .icon {\n    display: flex;\n  }\n}\n\n.tabBox {\n  display: flex;\n  flex-direction: column;\n}\n\n.tabsNav {\n  display: flex;\n  overflow-x: hidden;\n  position: relative;\n  height: 32px;\n  flex-shrink: 0;\n  background-color: var(--color-bg-base);\n  border-bottom: 1px solid var(--color-border);\n  &:hover {\n    overflow-x: scroll;\n  }\n  &::-webkit-scrollbar {\n    display: none;\n  }\n}\n\n.tabsContent {\n  flex: 1;\n  height: 0px;\n\n  .tabsContentItem {\n    height: 100%;\n    width: 100%;\n    display: none;\n    position: relative;\n  }\n\n  .tabsContentItemActive {\n    display: block;\n  }\n}\n\n.activeContent {\n  display: block;\n}\n\n.tabList {\n  display: flex;\n}\n\n.tabItem {\n  position: relative;\n  display: flex;\n  align-items: center;\n  padding-left: 10px;\n  line-height: 32px;\n  height: 32px;\n  cursor: pointer;\n  user-select: none;\n  border-right: 1px solid var(--color-border);\n\n  &:last-child {\n    border-right: 0;\n  }\n\n  .textBox {\n    flex: 1;\n    display: flex;\n    align-items: center;\n  }\n\n  .text {\n    flex: 1;\n    width: fit-content;\n    .f-single-line();\n  }\n\n  .icon {\n    flex-shrink: 0;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 20px;\n    height: 20px;\n    margin: 0px 4px;\n    border-radius: 4px;\n    cursor: pointer;\n    color: var(--color-text-secondary);\n    opacity: 0;\n\n    i {\n      font-size: 12px;\n    }\n\n    &:hover {\n      color: var(--color-primary);\n      background-color: var(--color-hover-bg);\n    }\n  }\n\n  &:hover {\n    // .tab-focus();\n    color: var(--color-primary);\n\n    .icon {\n      opacity: 1;\n    }\n  }\n}\n\n.activeTab {\n  .tab-focus();\n}\n\n.rightBox {\n  // flex: 1;\n  position: sticky;\n  right: 0;\n  top: 0;\n  bottom: 0;\n  background-color: var(--color-bg-base);\n  // box-shadow: -1px 0 4px rgba(0, 0, 0, 0.1);\n  display: flex;\n}\n\n.moreTabs {\n  width: 30px;\n  height: 100%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  cursor: pointer;\n  border-left: 1px solid var(--color-border);\n\n  &:hover {\n    color: var(--color-primary);\n  }\n\n\n}\n\n.addIcon {\n  width: 30px;\n  height: 100%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  cursor: pointer;\n  border-left: 1px solid var(--color-border);\n\n  &:hover {\n    color: var(--color-primary);\n  }\n}\n\n.addIconDisabled {\n  color: var(--color-text-disabled);\n  cursor: not-allowed;\n  &:hover {\n    color: var(--color-text-disabled);\n  }\n}\n\n.input {\n  border: 0;\n  width: 200px;\n  flex: 1;\n  height: 20px;\n  outline: none;\n  font-size: 12px;\n  font-weight: 400;\n  background-color: var(--color-bg-subtle);\n  color: var(--color-text);\n\n  input:focus {\n    outline: none;\n  }\n}\n\n.prefixIcon {\n  flex-shrink: 0;\n  margin-right: 4px;\n}"
  },
  {
    "path": "chat2db-client/src/components/Tabs/index.tsx",
    "content": "import React, { memo, useEffect, useState, useRef } from 'react';\nimport classnames from 'classnames';\nimport Iconfont from '@/components/Iconfont';\nimport { Popover, Dropdown } from 'antd';\nimport i18n from '@/i18n';\nimport { isValid } from '@/utils/check';\nimport _ from 'lodash';\nimport styles from './index.less';\n\nexport interface ITabItem {\n  prefixIcon?: string | React.ReactNode;\n  label: React.ReactNode;\n  key: number | string;\n  popover?: string | React.ReactNode;\n  children?: React.ReactNode;\n  editableName?: boolean;\n  canClosed?: boolean;\n  styles?: React.CSSProperties;\n}\n\nexport interface IOnchangeProps {\n  type: 'add' | 'delete' | 'switch';\n  data?: ITabItem;\n}\n\nconst MAX_TABS = 20;\n\ninterface IProps {\n  className?: string;\n  items?: ITabItem[];\n  activeKey?: number | string | null;\n  onChange?: (key: string | number | null) => void;\n  onEdit?: (action: 'add' | 'remove', data?: ITabItem[], list?: ITabItem[]) => void;\n  hideAdd?: boolean;\n  editableNameOnBlur?: (option: ITabItem) => void;\n  concealTabHeader?: boolean;\n  // 最后一个tab不能关闭\n  lastTabCannotClosed?: boolean;\n  destroyInactiveTabPane?: boolean;\n}\n\nexport default memo<IProps>((props) => {\n  const {\n    className,\n    items,\n    onChange,\n    onEdit,\n    activeKey,\n    hideAdd,\n    lastTabCannotClosed,\n    editableNameOnBlur,\n    concealTabHeader,\n    destroyInactiveTabPane = false,\n  } = props;\n  const [internalTabs, setInternalTabs] = useState<ITabItem[]>([]);\n  const [internalActiveTab, setInternalActiveTab] = useState<number | string | null>(null);\n  const [editingTab, setEditingTab] = useState<ITabItem['key'] | undefined>();\n  const tabListBoxRef = useRef<HTMLDivElement>(null);\n  const tabsNavRef = useRef<HTMLDivElement>(null);\n  const isNumberKey = useRef<boolean>(false);\n  const [showMoreTabs, setShowMoreTabs] = useState<boolean>(false);\n\n  useEffect(() => {\n    if (isValid(activeKey)) {\n      setInternalActiveTab(activeKey!);\n    }\n    isNumberKey.current = typeof activeKey === 'number';\n  }, [activeKey]);\n\n  useEffect(() => {\n    setInternalTabs(items || []);\n    if (items?.length && !isValid(internalActiveTab)) {\n      setInternalActiveTab(items[0]?.key);\n    }\n  }, [items]);\n\n  useEffect(() => {\n    const fn = (e) => {\n      if (e.deltaY) {\n        e.preventDefault();\n        // 鼠标滚轮事件，让tab可以横向滚动\n        if (tabsNavRef.current) {\n          tabsNavRef.current.scrollLeft -= e.deltaY;\n        }\n      }\n    };\n    tabsNavRef.current?.addEventListener('wheel', fn);\n    return () => {\n      tabsNavRef.current?.removeEventListener('wheel', fn);\n    };\n  }, []);\n\n  useEffect(() => {\n    onChange?.(internalActiveTab);\n    // 聚焦的时候，聚焦的tab要在第一个\n    if (tabListBoxRef.current) {\n      const activeTab = tabListBoxRef.current.querySelector(`.${styles.activeTab}`);\n      if (activeTab) {\n        activeTab.scrollIntoView({ block: 'nearest' });\n      }\n    }\n  }, [internalActiveTab]);\n\n  useEffect(() => {\n    // from copilot\n    if (tabListBoxRef.current) {\n      const tabsNavWidth = tabsNavRef.current?.getBoundingClientRect().width || 0;\n      const tabListBoxWidth = tabListBoxRef.current?.getBoundingClientRect().width || 0;\n      setShowMoreTabs(tabsNavWidth < tabListBoxWidth);\n    }\n  }, [internalTabs]);\n\n  const deleteTab = (data: ITabItem) => {\n    const newInternalTabs = internalTabs?.filter((t) => t.key !== data.key);\n    let activeKeyTemp = internalActiveTab;\n    // 删掉的是当前激活的tab，那么就切换到前一个,如果前一个没有就切换到后一个\n    if (data.key === internalActiveTab) {\n      const index = internalTabs.findIndex((t) => t.key === data.key);\n      if (index === 0) {\n        activeKeyTemp = internalTabs[1]?.key;\n      } else {\n        activeKeyTemp = internalTabs[index - 1]?.key;\n      }\n    }\n    changeTab(activeKeyTemp);\n    setInternalTabs(newInternalTabs);\n    onEdit?.('remove', [data], newInternalTabs);\n  };\n\n  const deleteOtherTab = (data: ITabItem) => {\n    const newInternalTabs = internalTabs?.filter((t) => t.key === data.key);\n    const deleteTabs = internalTabs?.filter((t) => t.key !== data.key);\n    changeTab(data.key);\n    setInternalTabs(newInternalTabs);\n    onEdit?.('remove', deleteTabs, newInternalTabs);\n  };\n\n  // 关闭所有tab\n  const deleteAllTab = () => {\n    changeTab(null);\n    setInternalTabs([]);\n    onEdit?.('remove', [...internalTabs]);\n  };\n\n  const changeTab = (key: string | number | null) => {\n    setInternalActiveTab(key);\n  };\n\n  const handleAdd = () => {\n    if (internalTabs.length >= MAX_TABS) {\n      return;\n    }\n    onEdit?.('add');\n  };\n\n  const onDoubleClick = (t: ITabItem) => {\n    if (t.editableName) {\n      setEditingTab(t.key);\n    }\n  };\n\n  const renderTabItem = (t: ITabItem, index: number) => {\n    function inputOnChange(value: string) {\n      internalTabs[index].label = value;\n      setInternalTabs([...internalTabs]);\n    }\n\n    function onBlur() {\n      editableNameOnBlur?.(t);\n      setEditingTab(undefined);\n    }\n\n    function showClosed() {\n      if (lastTabCannotClosed && internalTabs.length === 1) {\n        return false;\n      }\n      if (t.canClosed === true) {\n        return false;\n      }\n      return true;\n    }\n\n    const closeTabsMenu = [\n      {\n        label: i18n('common.button.close'),\n        key: 'close',\n        onClick: () => {\n          deleteTab(t);\n        },\n      },\n      {\n        label: i18n('common.button.closeOthers'),\n        key: 'closeOther',\n        onClick: () => {\n          deleteOtherTab(t);\n        },\n      },\n      {\n        label: i18n('common.button.closeAll'),\n        key: 'closeAll',\n        onClick: () => {\n          deleteAllTab();\n        },\n      },\n    ];\n\n    return (\n      <Dropdown key={t.key} menu={{ items: closeTabsMenu }} trigger={['contextMenu']}>\n        <Popover mouseEnterDelay={0.8} content={t.popover} key={t.key}>\n          <div\n            onDoubleClick={() => {\n              onDoubleClick(t);\n            }}\n            style={t.styles}\n            className={classnames(styles.tabItem, { [styles.activeTab]: t.key === internalActiveTab })}\n          >\n            {t.key === editingTab ? (\n              <input\n                value={t.label as string}\n                onChange={(e) => {\n                  inputOnChange(e.target.value);\n                }}\n                className={styles.input}\n                autoFocus\n                onBlur={onBlur}\n                type=\"text\"\n              />\n            ) : (\n              <div className={styles.textBox} key={t.key} onClick={changeTab.bind(null, t.key)}>\n                {t.prefixIcon &&\n                  (typeof t.prefixIcon == 'string' ? (\n                    <Iconfont className={styles.prefixIcon} code={t.prefixIcon} />\n                  ) : (\n                    t.prefixIcon\n                  ))}\n                <div className={styles.text}>{t.label}</div>\n              </div>\n            )}\n            {showClosed() && (\n              <div className={styles.icon} onClick={deleteTab.bind(null, t)}>\n                <Iconfont code=\"&#xe634;\" />\n              </div>\n            )}\n          </div>\n        </Popover>\n      </Dropdown>\n    );\n  };\n\n  const moreTabsMenu = (internalTabs || []).map((t) => {\n    return {\n      label: t.label,\n      key: t.key.toString(),\n      value: t.key,\n    };\n  });\n\n  return (\n    <div className={classnames(styles.tabBox, className)}>\n      {!concealTabHeader && (\n        <div className={styles.tabsNav} ref={tabsNavRef}>\n          {!!internalTabs?.length && (\n            <div className={styles.tabList} ref={tabListBoxRef}>\n              {internalTabs.map((t, index) => {\n                return renderTabItem(t, index);\n              })}\n            </div>\n          )}\n          {\n            <div className={styles.rightBox}>\n              {showMoreTabs && (\n                <div className={styles.moreTabs}>\n                  <Dropdown\n                    menu={{\n                      style: { maxHeight: '200px', overflowY: 'auto' },\n                      items: moreTabsMenu,\n                      selectable: true,\n                      selectedKeys: [`${internalActiveTab}`],\n                      onClick: (v) => {\n                        const key = moreTabsMenu.find((t) => t?.key === v.key)?.value || null;\n                        changeTab(key);\n                      },\n                    }}\n                    trigger={['click']}\n                  >\n                    <a onClick={(e) => e.preventDefault()}>\n                      <Iconfont code=\"&#xe601;\" />\n                    </a>\n                  </Dropdown>\n                </div>\n              )}\n              {!hideAdd && (\n                <div\n                  className={classnames(styles.addIcon, {\n                    [styles.addIconDisabled]: internalTabs.length >= MAX_TABS,\n                  })}\n                  onClick={handleAdd}\n                >\n                  <Iconfont code=\"&#xe631;\" />\n                </div>\n              )}\n            </div>\n          }\n        </div>\n      )}\n      {/* 隐藏的方案 */}\n      {!destroyInactiveTabPane ? (\n        <div className={styles.tabsContent}>\n          {internalTabs?.map((t) => {\n            return (\n              <div\n                key={t.key}\n                className={classnames(styles.tabsContentItem, {\n                  [styles.tabsContentItemActive]: t.key === internalActiveTab,\n                })}\n              >\n                {t.children}\n              </div>\n            );\n          })}\n        </div>\n      ) : (\n        <div className={styles.tabsContent}>\n          <div className={classnames(styles.tabsContentItem, styles.tabsContentItemActive)}>\n            {internalTabs.find((t) => t.key === internalActiveTab)?.children}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/UploadDriver/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n}\n"
  },
  {
    "path": "chat2db-client/src/components/UploadDriver/index.tsx",
    "content": "import React, { memo, useEffect, useState, useRef } from 'react';\nimport i18n from '@/i18n'\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Button, message, Upload, Form, Input } from 'antd';\nimport type { UploadProps } from 'antd';\nimport connectionService from '@/service/connection';\nimport { DatabaseTypeCode } from '@/constants'\n\ninterface IProps {\n  className?: string;\n  databaseType: DatabaseTypeCode;\n  formChange: Function;\n  jdbcDriverClass: string | undefined;\n}\n\nexport default memo<IProps>(function UploadDriver(props) {\n  const { className, databaseType = DatabaseTypeCode.MYSQL, formChange, jdbcDriverClass } = props;\n  const [formData, setFormData] = useState<any>({\n    dbType: databaseType,\n    jdbcDriverClass: jdbcDriverClass,\n    jdbcDriver: []\n  });\n\n  const uploadProps: UploadProps = {\n    name: 'multipartFiles',\n    action: `${window._BaseURL}/api/jdbc/driver/upload`,\n    multiple: true,\n    onChange(info) {\n      if (info.file.percent === 100 && info.file?.response?.data?.[0]) {\n        setFormData({\n          ...formData,\n          jdbcDriver: [...(formData.jdbcDriver), info.file?.response?.data?.[0]]\n        })\n      }\n    },\n    accept: \"application/java-archive\"\n  };\n\n  useEffect(() => {\n    formChange(formData)\n  }, [formData])\n\n  function onChange(e: any) {\n    setFormData({\n      ...formData,\n      jdbcDriverClass: e.target.value\n    })\n  }\n\n  return <div className={classnames(styles.box, className)}>\n    <div>\n      <Form\n        labelCol={{ span: 3 }}\n        wrapperCol={{ span: 16 }}\n      >\n        <Form.Item label=\"Class\">\n          <Input value={formData.jdbcDriverClass} onChange={onChange} />\n        </Form.Item>\n        <Form.Item label={i18n('connection.title.uploadDriver')}>\n          <Upload {...uploadProps}>\n            <Button>{i18n('connection.button.clickUpload')}</Button>\n          </Upload>\n        </Form.Item>\n      </Form>\n    </div>\n  </div >\n})\n"
  },
  {
    "path": "chat2db-client/src/components/ViewDDL/index.less",
    "content": "@import '../../styles/var.less';\n\n.viewDDL {\n  height: 100%;\n}\n"
  },
  {
    "path": "chat2db-client/src/components/ViewDDL/index.tsx",
    "content": "import React, { memo, useEffect } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport MonacoEditor, { IExportRefFunction } from '@/components/MonacoEditor';\nimport { v4 as uuid } from 'uuid';\nimport sqlServer from '@/service/sql';\n\ninterface IProps {\n  className?: string;\n  data: any;\n}\n\nexport default memo<IProps>((props) => {\n  const { className, data } = props;\n  const [monacoEditorId] = React.useState(uuid());\n  const monacoEditorRef = React.useRef<IExportRefFunction>(null);\n  const [sql,setSql] = React.useState('');\n\n  useEffect(() => {\n    if(data){\n      sqlServer\n      .exportCreateTableSql({\n        ...data,\n      } as any)\n      .then((res) => {\n        setSql(res);\n      });\n    }\n  }, [data]);\n\n  useEffect(() => {\n    monacoEditorRef.current?.setValue(sql || '', 'reset');\n  }, [sql]);\n\n  return (\n    <div className={classnames(styles.viewDDL, className)}>\n      <MonacoEditor\n        id={monacoEditorId}\n        ref={monacoEditorRef}\n        options={{\n          lineNumbers: 'off',\n          readOnly: true,\n          glyphMargin: false,\n          folding: false,\n        }}\n      />\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/components/XXXX_FN/index.less",
    "content": "@import '../../styles/var.less';\n\n.box {\n}\n"
  },
  {
    "path": "chat2db-client/src/components/XXXX_FN/index.tsx",
    "content": "import React, { memo } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\n\ninterface IProps {\n  className?: string;\n}\n\nexport default memo<IProps>((props) => {\n  const { className } = props;\n  return <div className={classnames(styles.box, className)}>demo</div>;\n});\n"
  },
  {
    "path": "chat2db-client/src/constants/IntelliSense/index.ts",
    "content": "import mysql from './mysql';\nimport oracle from './oracle';\nimport postgresql  from './pgsql';\nimport redis from './redis';\nimport sqlserver from './sqlserver'\n\nexport default { mysql, oracle, postgresql, redis, sqlserver };\n"
  },
  {
    "path": "chat2db-client/src/constants/IntelliSense/mysql.ts",
    "content": "import { DatabaseTypeCode } from '../common';\n\nexport default {\n  type: DatabaseTypeCode.MYSQL,\n  keywords: [\n    'ACCESSIBLE',\n    'ADD',\n    'ALL',\n    'ALTER',\n    'ANALYZE',\n    'AND',\n    'AS',\n    'ASC',\n    'ASENSITIVE',\n    'BEFORE',\n    'BETWEEN',\n    'BIGINT',\n    'BINARY',\n    'BLOB',\n    'BOTH',\n    'BY',\n    'CALL',\n    'CASCADE',\n    'CASE',\n    'CHANGE',\n    'CHAR',\n    'CHARACTER',\n    'CHECK',\n    'COLLATE',\n    'COLUMN',\n    'CONDITION',\n    'CONSTRAINT',\n    'CONTINUE',\n    'CONVERT',\n    'CREATE',\n    'CROSS',\n    'CUBE',\n    'CUME_DIST',\n    'CURRENT_DATE',\n    'CURRENT_TIME',\n    'CURRENT_TIMESTAMP',\n    'CURRENT_USER',\n    'CURSOR',\n    'DATABASE',\n    'DATABASES',\n    'DAY_HOUR',\n    'DAY_MICROSECOND',\n    'DAY_MINUTE',\n    'DAY_SECOND',\n    'DEC',\n    'DECIMAL',\n    'DECLARE',\n    'DEFAULT',\n    'DELAYED',\n    'DELETE',\n    'DENSE_RANK',\n    'DESC',\n    'DESCRIBE',\n    'DETERMINISTIC',\n    'DISTINCT',\n    'DISTINCTROW',\n    'DIV',\n    'DOUBLE',\n    'DROP',\n    'DUAL',\n    'EACH',\n    'ELSE',\n    'ELSEIF',\n    'EMPTY',\n    'ENCLOSED',\n    'ESCAPED',\n    'EXCEPT',\n    'EXISTS',\n    'EXIT',\n    'EXPLAIN',\n    'FALSE',\n    'FETCH',\n    'FIRST_VALUE',\n    'FLOAT',\n    'FLOAT4',\n    'FLOAT8',\n    'FOR',\n    'FORCE',\n    'FOREIGN',\n    'FROM',\n    'FULLTEXT',\n    'FUNCTION',\n    'GENERATED',\n    'GET',\n    'GRANT',\n    'GROUP',\n    'GROUPING',\n    'GROUPS',\n    'HAVING',\n    'HIGH_PRIORITY',\n    'HOUR_MICROSECOND',\n    'HOUR_MINUTE',\n    'HOUR_SECOND',\n    'IF',\n    'IGNORE',\n    'IN',\n    'INDEX',\n    'INFILE',\n    'INNER',\n    'INOUT',\n    'INSENSITIVE',\n    'INSERT',\n    'INT',\n    'INT1',\n    'INT2',\n    'INT3',\n    'INT4',\n    'INT8',\n    'INTEGER',\n    'INTERVAL',\n    'INTO',\n    'IO_AFTER_GTIDS',\n    'IO_BEFORE_GTIDS',\n    'IS',\n    'ITERATE',\n    'JOIN',\n    'JSON_TABLE',\n    'KEY',\n    'KEYS',\n    'KILL',\n    'LAG',\n    'LAST_VALUE',\n    'LATERAL',\n    'LEAD',\n    'LEADING',\n    'LEAVE',\n    'LEFT',\n    'LIKE',\n    'LIMIT',\n    'LINEAR',\n    'LINES',\n    'LOAD',\n    'LOCALTIME',\n    'LOCALTIMESTAMP',\n    'LOCK',\n    'LONG',\n    'LONGBLOB',\n    'LONGTEXT',\n    'LOOP',\n    'LOW_PRIORITY',\n    'MASTER_BIND',\n    'MASTER_SSL_VERIFY_SERVER_CERT',\n    'MATCH',\n    'MAXVALUE',\n    'MEDIUMBLOB',\n    'MEDIUMINT',\n    'MEDIUMTEXT',\n    'MIDDLEINT',\n    'MINUTE_MICROSECOND',\n    'MINUTE_SECOND',\n    'MOD',\n    'MODIFIES',\n    'NATURAL',\n    'NOT',\n    'NO_WRITE_TO_BINLOG',\n    'NTH_VALUE',\n    'NTILE',\n    'NULL',\n    'NUMERIC',\n    'OF',\n    'ON',\n    'OPTIMIZE',\n    'OPTIMIZER_COSTS',\n    'OPTION',\n    'OPTIONALLY',\n    'OR',\n    'ORDER',\n    'OUT',\n    'OUTER',\n    'OUTFILE',\n    'OVER',\n    'PARTITION',\n    'PERCENT_RANK',\n    'PRECISION',\n    'PRIMARY',\n    'PROCEDURE',\n    'PURGE',\n    'RANGE',\n    'RANK',\n    'READ',\n    'READS',\n    'READ_WRITE',\n    'REAL',\n    'RECURSIVE',\n    'REFERENCES',\n    'REGEXP',\n    'RELEASE',\n    'RENAME',\n    'REPEAT',\n    'REPLACE',\n    'REQUIRE',\n    'RESIGNAL',\n    'RESTRICT',\n    'RETURN',\n    'REVOKE',\n    'RIGHT',\n    'RLIKE',\n    'ROW',\n    'ROWS',\n    'ROW_NUMBER',\n    'SCHEMA',\n    'SCHEMAS',\n    'SECOND_MICROSECOND',\n    'SELECT',\n    'SENSITIVE',\n    'SEPARATOR',\n    'SET',\n    'SHOW',\n    'SIGNAL',\n    'SMALLINT',\n    'SPATIAL',\n    'SPECIFIC',\n    'SQL',\n    'SQLEXCEPTION',\n    'SQLSTATE',\n    'SQLWARNING',\n    'SQL_BIG_RESULT',\n    'SQL_CALC_FOUND_ROWS',\n    'SQL_SMALL_RESULT',\n    'SQL_NO_CACHE',\n    'SSL',\n    'STARTING',\n    'STORED',\n    'STRAIGHT_JOIN',\n    'SYSTEM',\n    'TABLE',\n    'TERMINATED',\n    'THEN',\n    'TINYBLOB',\n    'TINYINT',\n    'TINYTEXT',\n    'TO',\n    'TRAILING',\n    'TRIGGER',\n    'TRUE',\n    'UNDO',\n    'UNION',\n    'UNIQUE',\n    'UNLOCK',\n    'UNSIGNED',\n    'UPDATE',\n    'USAGE',\n    'USE',\n    'USING',\n    'UTC_DATE',\n    'UTC_TIME',\n    'UTC_TIMESTAMP',\n    'VALUES',\n    'VARBINARY',\n    'VARCHAR',\n    'VARCHARACTER',\n    'VARYING',\n    'VIRTUAL',\n    'WHEN',\n    'WHERE',\n    'WHILE',\n    'WINDOW',\n    'WITH',\n    'WRITE',\n    'XOR',\n    'YEAR_MONTH',\n    'ZEROFILL',\n  ],\n  functions: [\n    'ABS',\n    'ACOS',\n    'ADDDATE',\n    'ADDTIME',\n    'AES_DECRYPT',\n    'AES_ENCRYPT',\n    'ANY_VALUE',\n    'Area',\n    'AsBinary',\n    'AsWKB',\n    'ASCII',\n    'ASIN',\n    'AsText',\n    'AsWKT',\n    'ASYMMETRIC_DECRYPT',\n    'ASYMMETRIC_DERIVE',\n    'ASYMMETRIC_ENCRYPT',\n    'ASYMMETRIC_SIGN',\n    'ASYMMETRIC_VERIFY',\n    'ATAN',\n    'ATAN2',\n    'ATAN',\n    'AVG',\n    'BENCHMARK',\n    'BIN',\n    'BIT_AND',\n    'BIT_COUNT',\n    'BIT_LENGTH',\n    'BIT_OR',\n    'BIT_XOR',\n    'Buffer',\n    'CAST',\n    'CEIL',\n    'CEILING',\n    'Centroid',\n    'CHAR',\n    'CHAR_LENGTH',\n    'CHARACTER_LENGTH',\n    'CHARSET',\n    'COALESCE',\n    'COERCIBILITY',\n    'COLLATION',\n    'COMPRESS',\n    'CONCAT',\n    'CONCAT_WS',\n    'CONNECTION_ID',\n    'Contains',\n    'CONV',\n    'CONVERT',\n    'CONVERT_TZ',\n    'ConvexHull',\n    'COS',\n    'COT',\n    'COUNT',\n    'CRC32',\n    'CREATE_ASYMMETRIC_PRIV_KEY',\n    'CREATE_ASYMMETRIC_PUB_KEY',\n    'CREATE_DH_PARAMETERS',\n    'CREATE_DIGEST',\n    'Crosses',\n    'CUME_DIST',\n    'CURDATE',\n    'CURRENT_DATE',\n    'CURRENT_ROLE',\n    'CURRENT_TIME',\n    'CURRENT_TIMESTAMP',\n    'CURRENT_USER',\n    'CURTIME',\n    'DATABASE',\n    'DATE',\n    'DATE_ADD',\n    'DATE_FORMAT',\n    'DATE_SUB',\n    'DATEDIFF',\n    'DAY',\n    'DAYNAME',\n    'DAYOFMONTH',\n    'DAYOFWEEK',\n    'DAYOFYEAR',\n    'DECODE',\n    'DEFAULT',\n    'DEGREES',\n    'DES_DECRYPT',\n    'DES_ENCRYPT',\n    'DENSE_RANK',\n    'Dimension',\n    'Disjoint',\n    'Distance',\n    'ELT',\n    'ENCODE',\n    'ENCRYPT',\n    'EndPoint',\n    'Envelope',\n    'Equals',\n    'EXP',\n    'EXPORT_SET',\n    'ExteriorRing',\n    'EXTRACT',\n    'ExtractValue',\n    'FIELD',\n    'FIND_IN_SET',\n    'FIRST_VALUE',\n    'FLOOR',\n    'FORMAT',\n    'FORMAT_BYTES',\n    'FORMAT_PICO_TIME',\n    'FOUND_ROWS',\n    'FROM_BASE64',\n    'FROM_DAYS',\n    'FROM_UNIXTIME',\n    'GEN_RANGE',\n    'GEN_RND_EMAIL',\n    'GEN_RND_PAN',\n    'GEN_RND_SSN',\n    'GEN_RND_US_PHONE',\n    'GeomCollection',\n    'GeomCollFromText',\n    'GeometryCollectionFromText',\n    'GeomCollFromWKB',\n    'GeometryCollectionFromWKB',\n    'GeometryCollection',\n    'GeometryN',\n    'GeometryType',\n    'GeomFromText',\n    'GeometryFromText',\n    'GeomFromWKB',\n    'GeometryFromWKB',\n    'GET_FORMAT',\n    'GET_LOCK',\n    'GLength',\n    'GREATEST',\n    'GROUP_CONCAT',\n    'GROUPING',\n    'GTID_SUBSET',\n    'GTID_SUBTRACT',\n    'HEX',\n    'HOUR',\n    'ICU_VERSION',\n    'IF',\n    'IFNULL',\n    'INET_ATON',\n    'INET_NTOA',\n    'INET6_ATON',\n    'INET6_NTOA',\n    'INSERT',\n    'INSTR',\n    'InteriorRingN',\n    'Intersects',\n    'INTERVAL',\n    'IS_FREE_LOCK',\n    'IS_IPV4',\n    'IS_IPV4_COMPAT',\n    'IS_IPV4_MAPPED',\n    'IS_IPV6',\n    'IS_USED_LOCK',\n    'IS_UUID',\n    'IsClosed',\n    'IsEmpty',\n    'ISNULL',\n    'IsSimple',\n    'JSON_APPEND',\n    'JSON_ARRAY',\n    'JSON_ARRAY_APPEND',\n    'JSON_ARRAY_INSERT',\n    'JSON_ARRAYAGG',\n    'JSON_CONTAINS',\n    'JSON_CONTAINS_PATH',\n    'JSON_DEPTH',\n    'JSON_EXTRACT',\n    'JSON_INSERT',\n    'JSON_KEYS',\n    'JSON_LENGTH',\n    'JSON_MERGE',\n    'JSON_MERGE_PATCH',\n    'JSON_MERGE_PRESERVE',\n    'JSON_OBJECT',\n    'JSON_OBJECTAGG',\n    'JSON_OVERLAPS',\n    'JSON_PRETTY',\n    'JSON_QUOTE',\n    'JSON_REMOVE',\n    'JSON_REPLACE',\n    'JSON_SCHEMA_VALID',\n    'JSON_SCHEMA_VALIDATION_REPORT',\n    'JSON_SEARCH',\n    'JSON_SET',\n    'JSON_STORAGE_FREE',\n    'JSON_STORAGE_SIZE',\n    'JSON_TABLE',\n    'JSON_TYPE',\n    'JSON_UNQUOTE',\n    'JSON_VALID',\n    'LAG',\n    'LAST_DAY',\n    'LAST_INSERT_ID',\n    'LAST_VALUE',\n    'LCASE',\n    'LEAD',\n    'LEAST',\n    'LEFT',\n    'LENGTH',\n    'LineFromText',\n    'LineStringFromText',\n    'LineFromWKB',\n    'LineStringFromWKB',\n    'LineString',\n    'LN',\n    'LOAD_FILE',\n    'LOCALTIME',\n    'LOCALTIMESTAMP',\n    'LOCATE',\n    'LOG',\n    'LOG10',\n    'LOG2',\n    'LOWER',\n    'LPAD',\n    'LTRIM',\n    'MAKE_SET',\n    'MAKEDATE',\n    'MAKETIME',\n    'MASK_INNER',\n    'MASK_OUTER',\n    'MASK_PAN',\n    'MASK_PAN_RELAXED',\n    'MASK_SSN',\n    'MASTER_POS_WAIT',\n    'MAX',\n    'MBRContains',\n    'MBRCoveredBy',\n    'MBRCovers',\n    'MBRDisjoint',\n    'MBREqual',\n    'MBREquals',\n    'MBRIntersects',\n    'MBROverlaps',\n    'MBRTouches',\n    'MBRWithin',\n    'MD5',\n    'MEMBER OF',\n    'MICROSECOND',\n    'MID',\n    'MIN',\n    'MINUTE',\n    'MLineFromText',\n    'MultiLineStringFromText',\n    'MLineFromWKB',\n    'MultiLineStringFromWKB',\n    'MOD',\n    'MONTH',\n    'MONTHNAME',\n    'MPointFromText',\n    'MultiPointFromText',\n    'MPointFromWKB',\n    'MultiPointFromWKB',\n    'MPolyFromText',\n    'MultiPolygonFromText',\n    'MPolyFromWKB',\n    'MultiPolygonFromWKB',\n    'MultiLineString',\n    'MultiPoint',\n    'MultiPolygon',\n    'NAME_CONST',\n    'NOT IN',\n    'NOW',\n    'NTH_VALUE',\n    'NTILE',\n    'NULLIF',\n    'NumGeometries',\n    'NumInteriorRings',\n    'NumPoints',\n    'OCT',\n    'OCTET_LENGTH',\n    'OLD_PASSWORD',\n    'ORD',\n    'Overlaps',\n    'PASSWORD',\n    'PERCENT_RANK',\n    'PERIOD_ADD',\n    'PERIOD_DIFF',\n    'PI',\n    'Point',\n    'PointFromText',\n    'PointFromWKB',\n    'PointN',\n    'PolyFromText',\n    'PolygonFromText',\n    'PolyFromWKB',\n    'PolygonFromWKB',\n    'Polygon',\n    'POSITION',\n    'POW',\n    'POWER',\n    'PS_CURRENT_THREAD_ID',\n    'PS_THREAD_ID',\n    'PROCEDURE ANALYSE',\n    'QUARTER',\n    'QUOTE',\n    'RADIANS',\n    'RAND',\n    'RANDOM_BYTES',\n    'RANK',\n    'REGEXP_INSTR',\n    'REGEXP_LIKE',\n    'REGEXP_REPLACE',\n    'REGEXP_REPLACE',\n    'RELEASE_ALL_LOCKS',\n    'RELEASE_LOCK',\n    'REPEAT',\n    'REPLACE',\n    'REVERSE',\n    'RIGHT',\n    'ROLES_GRAPHML',\n    'ROUND',\n    'ROW_COUNT',\n    'ROW_NUMBER',\n    'RPAD',\n    'RTRIM',\n    'SEC_TO_TIME',\n    'SECOND',\n    'SESSION_USER',\n    'SHA1',\n    'SHA',\n    'SHA2',\n    'SIGN',\n    'SIN',\n    'SLEEP',\n    'SOUNDEX',\n    'SOURCE_POS_WAIT',\n    'SPACE',\n    'SQRT',\n    'SRID',\n    'ST_Area',\n    'ST_AsBinary',\n    'ST_AsWKB',\n    'ST_AsGeoJSON',\n    'ST_AsText',\n    'ST_AsWKT',\n    'ST_Buffer',\n    'ST_Buffer_Strategy',\n    'ST_Centroid',\n    'ST_Collect',\n    'ST_Contains',\n    'ST_ConvexHull',\n    'ST_Crosses',\n    'ST_Difference',\n    'ST_Dimension',\n    'ST_Disjoint',\n    'ST_Distance',\n    'ST_Distance_Sphere',\n    'ST_EndPoint',\n    'ST_Envelope',\n    'ST_Equals',\n    'ST_ExteriorRing',\n    'ST_FrechetDistance',\n    'ST_GeoHash',\n    'ST_GeomCollFromText',\n    'ST_GeometryCollectionFromText',\n    'ST_GeomCollFromTxt',\n    'ST_GeomCollFromWKB',\n    'ST_GeometryCollectionFromWKB',\n    'ST_GeometryN',\n    'ST_GeometryType',\n    'ST_GeomFromGeoJSON',\n    'ST_GeomFromText',\n    'ST_GeometryFromText',\n    'ST_GeomFromWKB',\n    'ST_GeometryFromWKB',\n    'ST_HausdorffDistance',\n    'ST_InteriorRingN',\n    'ST_Intersection',\n    'ST_Intersects',\n    'ST_IsClosed',\n    'ST_IsEmpty',\n    'ST_IsSimple',\n    'ST_IsValid',\n    'ST_LatFromGeoHash',\n    'ST_Length',\n    'ST_LineFromText',\n    'ST_LineStringFromText',\n    'ST_LineFromWKB',\n    'ST_LineStringFromWKB',\n    'ST_LineInterpolatePoint',\n    'ST_LineInterpolatePoints',\n    'ST_LongFromGeoHash',\n    'ST_Longitude',\n    'ST_MakeEnvelope',\n    'ST_MLineFromText',\n    'ST_MultiLineStringFromText',\n    'ST_MLineFromWKB',\n    'ST_MultiLineStringFromWKB',\n    'ST_MPointFromText',\n    'ST_MultiPointFromText',\n    'ST_MPointFromWKB',\n    'ST_MultiPointFromWKB',\n    'ST_MPolyFromText',\n    'ST_MultiPolygonFromText',\n    'ST_MPolyFromWKB',\n    'ST_MultiPolygonFromWKB',\n    'ST_NumGeometries',\n    'ST_NumInteriorRing',\n    'ST_NumInteriorRings',\n    'ST_NumPoints',\n    'ST_Overlaps',\n    'ST_PointAtDistance',\n    'ST_PointFromGeoHash',\n    'ST_PointFromText',\n    'ST_PointFromWKB',\n    'ST_PointN',\n    'ST_PolyFromText',\n    'ST_PolygonFromText',\n    'ST_PolyFromWKB',\n    'ST_PolygonFromWKB',\n    'ST_Simplify',\n    'ST_SRID',\n    'ST_StartPoint',\n    'ST_SwapXY',\n    'ST_SymDifference',\n    'ST_Touches',\n    'ST_Transform',\n    'ST_Union',\n    'ST_Validate',\n    'ST_Within',\n    'ST_X',\n    'ST_Y',\n    'StartPoint',\n    'STATEMENT_DIGEST',\n    'STATEMENT_DIGEST_TEXT',\n    'STD',\n    'STDDEV',\n    'STDDEV_POP',\n    'STDDEV_SAMP',\n    'STR_TO_DATE',\n    'STRCMP',\n    'SUBDATE',\n    'SUBSTR',\n    'SUBSTRING',\n    'SUBSTRING_INDEX',\n    'SUBTIME',\n    'SUM',\n    'SYSDATE',\n    'SYSTEM_USER',\n    'TAN',\n    'TIME',\n    'TIME_FORMAT',\n    'TIME_TO_SEC',\n    'TIMEDIFF',\n    'TIMESTAMP',\n    'TIMESTAMPADD',\n    'TIMESTAMPDIFF',\n    'TO_BASE64',\n    'TO_DAYS',\n    'TO_SECONDS',\n    'Touches',\n    'TRIM',\n    'TRUNCATE',\n    'UCASE',\n    'UNCOMPRESS',\n    'UNCOMPRESSED_LENGTH',\n    'UNHEX',\n    'UNIX_TIMESTAMP',\n    'UpdateXML',\n    'UPPER',\n    'USER',\n    'UTC_DATE',\n    'UTC_TIME',\n    'UTC_TIMESTAMP',\n    'UUID',\n    'UUID_SHORT',\n    'UUID_TO_BIN',\n    'VALIDATE_PASSWORD_STRENGTH',\n    'VALUES',\n    'VAR_POP',\n    'VAR_SAMP',\n    'VARIANCE',\n    'VERSION',\n    'WAIT_FOR_EXECUTED_GTID_SET',\n    'WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS',\n    'WEEK',\n    'WEEKDAY',\n    'WEEKOFYEAR',\n    'WEIGHT_STRING',\n    'Within',\n    'X',\n    'Y',\n    'YEAR',\n    'YEARWEEK',\n  ],\n};\n"
  },
  {
    "path": "chat2db-client/src/constants/IntelliSense/oracle.ts",
    "content": "import { DatabaseTypeCode } from '../common';\n\nexport default {\n  type: DatabaseTypeCode.ORACLE,\n  keywords: [\n    'ACCESS',\n    'ADD',\n    'ALL',\n    'ALTER',\n    'AND',\n    'ANY',\n    'AS',\n    'ASC',\n    'AUDIT',\n    'BETWEEN',\n    'BY',\n    'CHAR',\n    'CHECK',\n    'CLUSTER',\n    'COLUMN',\n    'COMMENT',\n    'COMPRESS',\n    'CONNECT',\n    'CREATE',\n    'CURRENT',\n    'DATE',\n    'DECIMAL',\n    'DEFAULT',\n    'DELETE',\n    'DESC',\n    'DISTINCT',\n    'DROP',\n    'ELSE',\n    'EXCLUSIVE',\n    'EXISTS',\n    'FILE',\n    'FLOAT',\n    'FOR',\n    'FROM',\n    'GRANT',\n    'GROUP',\n    'HAVING',\n    'IDENTIFIED',\n    'IMMEDIATE',\n    'IN',\n    'INCREMENT',\n    'INDEX',\n    'INITIAL',\n    'INSERT',\n    'INTEGER',\n    'INTERSECT',\n    'INTO',\n    'IS',\n    'LEVEL',\n    'LIKE',\n    'LOCK',\n    'LONG',\n    'MAXEXTENTS',\n    'MINUS',\n    'MLSLABEL',\n    'MODE',\n    'MODIFY',\n    'NOAUDIT',\n    'NOCOMPRESS',\n    'NOT',\n    'NOWAIT',\n    'NULL',\n    'NUMBER',\n    'OF',\n    'OFFLINE',\n    'ON',\n    'ONLINE',\n    'OPTION',\n    'OR',\n    'ORDER',\n    'PCTFREE',\n    'PRIOR',\n    'PRIVILEGES',\n    'PUBLIC',\n    'RAW',\n    'RENAME',\n    'RESOURCE',\n    'REVOKE',\n    'ROW',\n    'ROWID',\n    'ROWNUM',\n    'ROWS',\n    'SELECT',\n    'SESSION',\n    'SET',\n    'SHARE',\n    'SIZE',\n    'SMALLINT',\n    'START',\n    'SUCCESSFUL',\n    'SYNONYM',\n    'SYSDATE',\n    'TABLE',\n    'THEN',\n    'TO',\n    'TRIGGER',\n    'UID',\n    'UNION',\n    'UNIQUE',\n    'UPDATE',\n    'USER',\n    'VALIDATE',\n    'VALUES',\n    'VARCHAR',\n    'VARCHAR2',\n    'VIEW',\n    'WHENEVER',\n    'WHERE',\n    'WITH',\n  ],\n  functions: [\n    'CONNECT BY',\n    'START WITH',\n    'ORDER SIBLINGS BY',\n    'SIBLINGS',\n    'PRIOR',\n    'NOCYCLE',\n    'LEVEL',\n    'GROUP BY',\n    'GROUPING SETS',\n    'CUBE',\n    'ROLLUP',\n    'PIVOT',\n    'UNPIVOT',\n    'MODEL',\n    'DIMENSION',\n    'MEASURES',\n    'RULES',\n    'ITERATE',\n    'INCREMENT',\n    'PRESENTV',\n    'ABSENTV',\n    'SQLQUERY',\n    'ARRAY',\n    'VARRAY',\n    'ASSOCIATIVE ARRAY',\n    'RECORD',\n    'OBJECT',\n    'TABLE',\n    'VIEW',\n    'MATERIALIZED VIEW',\n    'CONTEXT',\n    'DIRECTORY',\n    'EDITION',\n    'Profile',\n    'ROLE',\n    'SEQUENCE',\n    'SYNONYM',\n    'INDEXTYPE',\n    'LIBRARY',\n    'OPERATOR',\n    'PROCEDURE',\n    'FUNCTION',\n    'PACKAGE',\n    'TYPE',\n    'TRIGGER',\n    'LINK',\n    'VIEW LOG',\n    'CONSTRAINT',\n    'CLUSTER',\n    'COMMENT',\n    'AUDIT',\n    'NOAUDIT',\n    'GRANT',\n    'REVOKE',\n    'FLASHBACK',\n    'PURGE',\n    'MERGE',\n    'ALTER SESSION',\n    'COMMIT',\n    'ROLLBACK',\n    'SAVEPOINT',\n    'SET TRANSACTION',\n    'LOCK',\n    'ROW SHARE',\n    'ROW EXCLUSIVE',\n    'SHARE UPDATE',\n    'SHARE',\n    'EXCLUSIVE',\n    'NOWAIT',\n    'SKIP LOCKED',\n    'PL/SQL',\n    'AUTONOMOUS_TRANSACTION',\n    'PRAGMA',\n    'EXCEPTION',\n    'SERIALLY_REUSABLE',\n  ],\n};\n"
  },
  {
    "path": "chat2db-client/src/constants/IntelliSense/pgsql.ts",
    "content": "import { DatabaseTypeCode } from '../common';\n\nexport default {\n  type: DatabaseTypeCode.POSTGRESQL,\n  keywords: [\n    'ALL',\n    'ANALYSE',\n    'ANALYZE',\n    'AND',\n    'ANY',\n    'ARRAY',\n    'AS',\n    'ASC',\n    'ASYMMETRIC',\n    'AUTHORIZATION',\n    'BINARY',\n    'BOTH',\n    'CASE',\n    'CAST',\n    'CHECK',\n    'COLLATE',\n    'COLLATION',\n    'COLUMN',\n    'CONCURRENTLY',\n    'CONSTRAINT',\n    'CREATE',\n    'CROSS',\n    'CURRENT_CATALOG',\n    'CURRENT_DATE',\n    'CURRENT_ROLE',\n    'CURRENT_SCHEMA',\n    'CURRENT_TIME',\n    'CURRENT_TIMESTAMP',\n    'CURRENT_USER',\n    'DEFAULT',\n    'DEFERRABLE',\n    'DESC',\n    'DISTINCT',\n    'DO',\n    'ELSE',\n    'END',\n    'EXCEPT',\n    'FALSE',\n    'FETCH',\n    'FOR',\n    'FOREIGN',\n    'FREEZE',\n    'FROM',\n    'FULL',\n    'GRANT',\n    'GROUP',\n    'HAVING',\n    'ILIKE',\n    'IN',\n    'INITIALLY',\n    'INNER',\n    'INTERSECT',\n    'INTO',\n    'IS',\n    'ISNULL',\n    'JOIN',\n    'LATERAL',\n    'LEADING',\n    'LEFT',\n    'LIKE',\n    'LIMIT',\n    'LOCALTIME',\n    'LOCALTIMESTAMP',\n    'NATURAL',\n    'NOT',\n    'NOTNULL',\n    'NULL',\n    'OFFSET',\n    'ON',\n    'ONLY',\n    'OR',\n    'ORDER',\n    'OUTER',\n    'OVERLAPS',\n    'PLACING',\n    'PRIMARY',\n    'REFERENCES',\n    'RETURNING',\n    'RIGHT',\n    'SELECT',\n    'SESSION_USER',\n    'SIMILAR',\n    'SOME',\n    'SYMMETRIC',\n    'TABLE',\n    'TABLESAMPLE',\n    'THEN',\n    'TO',\n    'TRAILING',\n    'TRUE',\n    'UNION',\n    'UNIQUE',\n    'USER',\n    'USING',\n    'VARIADIC',\n    'VERBOSE',\n    'WHEN',\n    'WHERE',\n    'WINDOW',\n    'WITH',\n  ],\n  functions: [\n    'abbrev',\n    'abs',\n    'acldefault',\n    'aclexplode',\n    'acos',\n    'acosd',\n    'acosh',\n    'age',\n    'any',\n    'area',\n    'array_agg',\n    'array_append',\n    'array_cat',\n    'array_dims',\n    'array_fill',\n    'array_length',\n    'array_lower',\n    'array_ndims',\n    'array_position',\n    'array_positions',\n    'array_prepend',\n    'array_remove',\n    'array_replace',\n    'array_to_json',\n    'array_to_string',\n    'array_to_tsvector',\n    'array_upper',\n    'ascii',\n    'asin',\n    'asind',\n    'asinh',\n    'atan',\n    'atan2',\n    'atan2d',\n    'atand',\n    'atanh',\n    'avg',\n    'bit',\n    'bit_and',\n    'bit_count',\n    'bit_length',\n    'bit_or',\n    'bit_xor',\n    'bool_and',\n    'bool_or',\n    'bound_box',\n    'box',\n    'brin_desummarize_range',\n    'brin_summarize_new_values',\n    'brin_summarize_range',\n    'broadcast',\n    'btrim',\n    'cardinality',\n    'cbrt',\n    'ceil',\n    'ceiling',\n    'center',\n    'char_length',\n    'character_length',\n    'chr',\n    'circle',\n    'clock_timestamp',\n    'coalesce',\n    'col_description',\n    'concat',\n    'concat_ws',\n    'convert',\n    'convert_from',\n    'convert_to',\n    'corr',\n    'cos',\n    'cosd',\n    'cosh',\n    'cot',\n    'cotd',\n    'count',\n    'covar_pop',\n    'covar_samp',\n    'cume_dist',\n    'current_catalog',\n    'current_database',\n    'current_date',\n    'current_query',\n    'current_role',\n    'current_schema',\n    'current_schemas',\n    'current_setting',\n    'current_time',\n    'current_timestamp',\n    'current_user',\n    'currval',\n    'cursor_to_xml',\n    'cursor_to_xmlschema',\n    'date_bin',\n    'date_part',\n    'date_trunc',\n    'database_to_xml',\n    'database_to_xml_and_xmlschema',\n    'database_to_xmlschema',\n    'decode',\n    'degrees',\n    'dense_rank',\n    'diagonal',\n    'diameter',\n    'div',\n    'encode',\n    'enum_first',\n    'enum_last',\n    'enum_range',\n    'every',\n    'exp',\n    'extract',\n    'factorial',\n    'family',\n    'first_value',\n    'floor',\n    'format',\n    'format_type',\n    'gcd',\n    'gen_random_uuid',\n    'generate_series',\n    'generate_subscripts',\n    'get_bit',\n    'get_byte',\n    'get_current_ts_config',\n    'gin_clean_pending_list',\n    'greatest',\n    'grouping',\n    'has_any_column_privilege',\n    'has_column_privilege',\n    'has_database_privilege',\n    'has_foreign_data_wrapper_privilege',\n    'has_function_privilege',\n    'has_language_privilege',\n    'has_schema_privilege',\n    'has_sequence_privilege',\n    'has_server_privilege',\n    'has_table_privilege',\n    'has_tablespace_privilege',\n    'has_type_privilege',\n    'height',\n    'host',\n    'hostmask',\n    'inet_client_addr',\n    'inet_client_port',\n    'inet_merge',\n    'inet_same_family',\n    'inet_server_addr',\n    'inet_server_port',\n    'initcap',\n    'isclosed',\n    'isempty',\n    'isfinite',\n    'isopen',\n    'json_agg',\n    'json_array_elements',\n    'json_array_elements_text',\n    'json_array_length',\n    'json_build_array',\n    'json_build_object',\n    'json_each',\n    'json_each_text',\n    'json_extract_path',\n    'json_extract_path_text',\n    'json_object',\n    'json_object_agg',\n    'json_object_keys',\n    'json_populate_record',\n    'json_populate_recordset',\n    'json_strip_nulls',\n    'json_to_record',\n    'json_to_recordset',\n    'json_to_tsvector',\n    'json_typeof',\n    'jsonb_agg',\n    'jsonb_array_elements',\n    'jsonb_array_elements_text',\n    'jsonb_array_length',\n    'jsonb_build_array',\n    'jsonb_build_object',\n    'jsonb_each',\n    'jsonb_each_text',\n    'jsonb_extract_path',\n    'jsonb_extract_path_text',\n    'jsonb_insert',\n    'jsonb_object',\n    'jsonb_object_agg',\n    'jsonb_object_keys',\n    'jsonb_path_exists',\n    'jsonb_path_match',\n    'jsonb_path_query',\n    'jsonb_path_query_array',\n    'jsonb_path_exists_tz',\n    'jsonb_path_query_first',\n    'jsonb_path_query_array_tz',\n    'jsonb_path_query_first_tz',\n    'jsonb_path_query_tz',\n    'jsonb_path_match_tz',\n    'jsonb_populate_record',\n    'jsonb_populate_recordset',\n    'jsonb_pretty',\n    'jsonb_set',\n    'jsonb_set_lax',\n    'jsonb_strip_nulls',\n    'jsonb_to_record',\n    'jsonb_to_recordset',\n    'jsonb_to_tsvector',\n    'jsonb_typeof',\n    'justify_days',\n    'justify_hours',\n    'justify_interval',\n    'lag',\n    'last_value',\n    'lastval',\n    'lcm',\n    'lead',\n    'least',\n    'left',\n    'length',\n    'line',\n    'ln',\n    'localtime',\n    'localtimestamp',\n    'log',\n    'log10',\n    'lower',\n    'lower_inc',\n    'lower_inf',\n    'lpad',\n    'lseg',\n    'ltrim',\n    'macaddr8_set7bit',\n    'make_date',\n    'make_interval',\n    'make_time',\n    'make_timestamp',\n    'make_timestamptz',\n    'makeaclitem',\n    'masklen',\n    'max',\n    'md5',\n    'min',\n    'min_scale',\n    'mod',\n    'mode',\n    'multirange',\n    'netmask',\n    'network',\n    'nextval',\n    'normalize',\n    'now',\n    'npoints',\n    'nth_value',\n    'ntile',\n    'nullif',\n    'num_nonnulls',\n    'num_nulls',\n    'numnode',\n    'obj_description',\n    'octet_length',\n    'overlay',\n    'parse_ident',\n    'path',\n    'pclose',\n    'percent_rank',\n    'percentile_cont',\n    'percentile_disc',\n    'pg_advisory_lock',\n    'pg_advisory_lock_shared',\n    'pg_advisory_unlock',\n    'pg_advisory_unlock_all',\n    'pg_advisory_unlock_shared',\n    'pg_advisory_xact_lock',\n    'pg_advisory_xact_lock_shared',\n    'pg_backend_pid',\n    'pg_backup_start_time',\n    'pg_blocking_pids',\n    'pg_cancel_backend',\n    'pg_client_encoding',\n    'pg_collation_actual_version',\n    'pg_collation_is_visible',\n    'pg_column_compression',\n    'pg_column_size',\n    'pg_conf_load_time',\n    'pg_control_checkpoint',\n    'pg_control_init',\n    'pg_control_recovery',\n    'pg_control_system',\n    'pg_conversion_is_visible',\n    'pg_copy_logical_replication_slot',\n    'pg_copy_physical_replication_slot',\n    'pg_create_logical_replication_slot',\n    'pg_create_physical_replication_slot',\n    'pg_create_restore_point',\n    'pg_current_logfile',\n    'pg_current_snapshot',\n    'pg_current_wal_flush_lsn',\n    'pg_current_wal_insert_lsn',\n    'pg_current_wal_lsn',\n    'pg_current_xact_id',\n    'pg_current_xact_id_if_assigned',\n    'pg_current_xlog_flush_location',\n    'pg_current_xlog_insert_location',\n    'pg_current_xlog_location',\n    'pg_database_size',\n    'pg_describe_object',\n    'pg_drop_replication_slot',\n    'pg_event_trigger_ddl_commands',\n    'pg_event_trigger_dropped_objects',\n    'pg_event_trigger_table_rewrite_oid',\n    'pg_event_trigger_table_rewrite_reason',\n    'pg_export_snapshot',\n    'pg_filenode_relation',\n    'pg_function_is_visible',\n    'pg_get_catalog_foreign_keys',\n    'pg_get_constraintdef',\n    'pg_get_expr',\n    'pg_get_function_arguments',\n    'pg_get_function_identity_arguments',\n    'pg_get_function_result',\n    'pg_get_functiondef',\n    'pg_get_indexdef',\n    'pg_get_keywords',\n    'pg_get_object_address',\n    'pg_get_owned_sequence',\n    'pg_get_ruledef',\n    'pg_get_serial_sequence',\n    'pg_get_statisticsobjdef',\n    'pg_get_triggerdef',\n    'pg_get_userbyid',\n    'pg_get_viewdef',\n    'pg_get_wal_replay_pause_state',\n    'pg_has_role',\n    'pg_identify_object',\n    'pg_identify_object_as_address',\n    'pg_import_system_collations',\n    'pg_index_column_has_property',\n    'pg_index_has_property',\n    'pg_indexam_has_property',\n    'pg_indexes_size',\n    'pg_is_in_backup',\n    'pg_is_in_recovery',\n    'pg_is_other_temp_schema',\n    'pg_is_wal_replay_paused',\n    'pg_is_xlog_replay_paused',\n    'pg_jit_available',\n    'pg_last_committed_xact',\n    'pg_last_wal_receive_lsn',\n    'pg_last_wal_replay_lsn',\n    'pg_last_xact_replay_timestamp',\n    'pg_last_xlog_receive_location',\n    'pg_last_xlog_replay_location',\n    'pg_listening_channels',\n    'pg_log_backend_memory_contexts',\n    'pg_logical_emit_message',\n    'pg_logical_slot_get_binary_changes',\n    'pg_logical_slot_get_changes',\n    'pg_logical_slot_peek_binary_changes',\n    'pg_logical_slot_peek_changes',\n    'pg_ls_archive_statusdir',\n    'pg_ls_dir',\n    'pg_ls_logdir',\n    'pg_ls_tmpdir',\n    'pg_ls_waldir',\n    'pg_mcv_list_items',\n    'pg_my_temp_schema',\n    'pg_notification_queue_usage',\n    'pg_opclass_is_visible',\n    'pg_operator_is_visible',\n    'pg_opfamily_is_visible',\n    'pg_options_to_table',\n    'pg_partition_ancestors',\n    'pg_partition_root',\n    'pg_partition_tree',\n    'pg_postmaster_start_time',\n    'pg_promote',\n    'pg_read_binary_file',\n    'pg_read_file',\n    'pg_relation_filenode',\n    'pg_relation_filepath',\n    'pg_relation_size',\n    'pg_reload_conf',\n    'pg_replication_origin_advance',\n    'pg_replication_origin_create',\n    'pg_replication_origin_drop',\n    'pg_replication_origin_oid',\n    'pg_replication_origin_progress',\n    'pg_replication_origin_session_is_setup',\n    'pg_replication_origin_session_progress',\n    'pg_replication_origin_session_reset',\n    'pg_replication_origin_session_setup',\n    'pg_replication_origin_xact_reset',\n    'pg_replication_origin_xact_setup',\n    'pg_replication_slot_advance',\n    'pg_rotate_logfile',\n    'pg_safe_snapshot_blocking_pids',\n    'pg_size_bytes',\n    'pg_size_pretty',\n    'pg_sleep',\n    'pg_sleep_for',\n    'pg_sleep_until',\n    'pg_snapshot_xip',\n    'pg_snapshot_xmax',\n    'pg_snapshot_xmin',\n    'pg_start_backup',\n    'pg_stat_file',\n    'pg_statistics_obj_is_visible',\n    'pg_stop_backup',\n    'pg_switch_wal',\n    'pg_switch_xlog',\n    'pg_table_is_visible',\n    'pg_table_size',\n    'pg_tablespace_databases',\n    'pg_tablespace_location',\n    'pg_tablespace_size',\n    'pg_terminate_backend',\n    'pg_total_relation_size',\n    'pg_trigger_depth',\n    'pg_try_advisory_lock',\n    'pg_try_advisory_lock_shared',\n    'pg_try_advisory_xact_lock',\n    'pg_try_advisory_xact_lock_shared',\n    'pg_ts_config_is_visible',\n    'pg_ts_dict_is_visible',\n    'pg_ts_parser_is_visible',\n    'pg_ts_template_is_visible',\n    'pg_type_is_visible',\n    'pg_typeof',\n    'pg_visible_in_snapshot',\n    'pg_wal_lsn_diff',\n    'pg_wal_replay_pause',\n    'pg_wal_replay_resume',\n    'pg_walfile_name',\n    'pg_walfile_name_offset',\n    'pg_xact_commit_timestamp',\n    'pg_xact_commit_timestamp_origin',\n    'pg_xact_status',\n    'pg_xlog_location_diff',\n    'pg_xlog_replay_pause',\n    'pg_xlog_replay_resume',\n    'pg_xlogfile_name',\n    'pg_xlogfile_name_offset',\n    'phraseto_tsquery',\n    'pi',\n    'plainto_tsquery',\n    'point',\n    'polygon',\n    'popen',\n    'position',\n    'power',\n    'pqserverversion',\n    'query_to_xml',\n    'query_to_xml_and_xmlschema',\n    'query_to_xmlschema',\n    'querytree',\n    'quote_ident',\n    'quote_literal',\n    'quote_nullable',\n    'radians',\n    'radius',\n    'random',\n    'range_agg',\n    'range_intersect_agg',\n    'range_merge',\n    'rank',\n    'regexp_match',\n    'regexp_matches',\n    'regexp_replace',\n    'regexp_split_to_array',\n    'regexp_split_to_table',\n    'regr_avgx',\n    'regr_avgy',\n    'regr_count',\n    'regr_intercept',\n    'regr_r2',\n    'regr_slope',\n    'regr_sxx',\n    'regr_sxy',\n    'regr_syy',\n    'repeat',\n    'replace',\n    'reverse',\n    'right',\n    'round',\n    'row_number',\n    'row_security_active',\n    'row_to_json',\n    'rpad',\n    'rtrim',\n    'scale',\n    'schema_to_xml',\n    'schema_to_xml_and_xmlschema',\n    'schema_to_xmlschema',\n    'session_user',\n    'set_bit',\n    'set_byte',\n    'set_config',\n    'set_masklen',\n    'setseed',\n    'setval',\n    'setweight',\n    'sha224',\n    'sha256',\n    'sha384',\n    'sha512',\n    'shobj_description',\n    'sign',\n    'sin',\n    'sind',\n    'sinh',\n    'slope',\n    'split_part',\n    'sprintf',\n    'sqrt',\n    'starts_with',\n    'statement_timestamp',\n    'stddev',\n    'stddev_pop',\n    'stddev_samp',\n    'string_agg',\n    'string_to_array',\n    'string_to_table',\n    'strip',\n    'strpos',\n    'substr',\n    'substring',\n    'sum',\n    'suppress_redundant_updates_trigger',\n    'table_to_xml',\n    'table_to_xml_and_xmlschema',\n    'table_to_xmlschema',\n    'tan',\n    'tand',\n    'tanh',\n    'text',\n    'timeofday',\n    'timezone',\n    'to_ascii',\n    'to_char',\n    'to_date',\n    'to_hex',\n    'to_json',\n    'to_number',\n    'to_regclass',\n    'to_regcollation',\n    'to_regnamespace',\n    'to_regoper',\n    'to_regoperator',\n    'to_regproc',\n    'to_regprocedure',\n    'to_regrole',\n    'to_regtype',\n    'to_timestamp',\n    'to_tsquery',\n    'to_tsvector',\n    'transaction_timestamp',\n    'translate',\n    'trim',\n    'trim_array',\n    'trim_scale',\n    'trunc',\n    'ts_debug',\n    'ts_delete',\n    'ts_filter',\n    'ts_headline',\n    'ts_lexize',\n    'ts_parse',\n    'ts_rank',\n    'ts_rank_cd',\n    'ts_rewrite',\n    'ts_stat',\n    'ts_token_type',\n    'tsquery_phrase',\n    'tsvector_to_array',\n    'tsvector_update_trigger',\n    'tsvector_update_trigger_column',\n    'txid_current',\n    'txid_current_if_assigned',\n    'txid_current_snapshot',\n    'txid_snapshot_xip',\n    'txid_snapshot_xmax',\n    'txid_snapshot_xmin',\n    'txid_status',\n    'txid_visible_in_snapshot',\n    'unistr',\n    'unnest',\n    'upper',\n    'upper_inc',\n    'upper_inf',\n    'user',\n    'var_pop',\n    'var_samp',\n    'variance',\n    'version',\n    'websearch_to_tsquery',\n    'width',\n    'width_bucket',\n    'xml_is_well_formed',\n    'xml_is_well_formed_content',\n    'xml_is_well_formed_document',\n    'xmlagg',\n    'xmlcomment',\n    'xmlconcat',\n    'xmlelement',\n    'xmlexists',\n    'xmlforest',\n    'xmlparse',\n    'xmlpi',\n    'xmlroot',\n    'xmlserialize',\n    'xpath',\n    'xpath_exists',\n  ],\n};\n"
  },
  {
    "path": "chat2db-client/src/constants/IntelliSense/redis.ts",
    "content": "import { DatabaseTypeCode } from '../common';\n\nexport default {\n  type: DatabaseTypeCode.REDIS,\n  keywords: [\n    'APPEND',\n    'AUTH',\n    'BGREWRITEAOF',\n    'BGSAVE',\n    'BITCOUNT',\n    'BITFIELD',\n    'BITOP',\n    'BITPOS',\n    'BLPOP',\n    'BRPOP',\n    'BRPOPLPUSH',\n    'CLIENT',\n    'KILL',\n    'LIST',\n    'GETNAME',\n    'PAUSE',\n    'REPLY',\n    'SETNAME',\n    'CLUSTER',\n    'ADDSLOTS',\n    'COUNT-FAILURE-REPORTS',\n    'COUNTKEYSINSLOT',\n    'DELSLOTS',\n    'FAILOVER',\n    'FORGET',\n    'GETKEYSINSLOT',\n    'INFO',\n    'KEYSLOT',\n    'MEET',\n    'NODES',\n    'REPLICATE',\n    'RESET',\n    'SAVECONFIG',\n    'SET-CONFIG-EPOCH',\n    'SETSLOT',\n    'SLAVES',\n    'SLOTS',\n    'COMMAND',\n    'COUNT',\n    'GETKEYS',\n    'CONFIG',\n    'GET',\n    'REWRITE',\n    'SET',\n    'RESETSTAT',\n    'DBSIZE',\n    'DEBUG',\n    'OBJECT',\n    'SEGFAULT',\n    'DECR',\n    'DECRBY',\n    'DEL',\n    'DISCARD',\n    'DUMP',\n    'ECHO',\n    'EVAL',\n    'EVALSHA',\n    'EXEC',\n    'EXISTS',\n    'EXPIRE',\n    'EXPIREAT',\n    'FLUSHALL',\n    'FLUSHDB',\n    'GEOADD',\n    'GEOHASH',\n    'GEOPOS',\n    'GEODIST',\n    'GEORADIUS',\n    'GEORADIUSBYMEMBER',\n    'GETBIT',\n    'GETRANGE',\n    'GETSET',\n    'HDEL',\n    'HEXISTS',\n    'HGET',\n    'HGETALL',\n    'HINCRBY',\n    'HINCRBYFLOAT',\n    'HKEYS',\n    'HLEN',\n    'HMGET',\n    'HMSET',\n    'HSET',\n    'HSETNX',\n    'HSTRLEN',\n    'HVALS',\n    'INCR',\n    'INCRBY',\n    'INCRBYFLOAT',\n    'KEYS',\n    'LASTSAVE',\n    'LINDEX',\n    'LINSERT',\n    'LLEN',\n    'LPOP',\n    'LPUSH',\n    'LPUSHX',\n    'LRANGE',\n    'LREM',\n    'LSET',\n    'LTRIM',\n    'MGET',\n    'MIGRATE',\n    'MONITOR',\n    'MOVE',\n    'MSET',\n    'MSETNX',\n    'MULTI',\n    'PERSIST',\n    'PEXPIRE',\n    'PEXPIREAT',\n    'PFADD',\n    'PFCOUNT',\n    'PFMERGE',\n    'PING',\n    'PSETEX',\n    'PSUBSCRIBE',\n    'PUBSUB',\n    'PTTL',\n    'PUBLISH',\n    'PUNSUBSCRIBE',\n    'QUIT',\n    'RANDOMKEY',\n    'READONLY',\n    'READWRITE',\n    'RENAME',\n    'RENAMENX',\n    'RESTORE',\n    'ROLE',\n    'RPOP',\n    'RPOPLPUSH',\n    'RPUSH',\n    'RPUSHX',\n    'SADD',\n    'SAVE',\n    'SCARD',\n    'SCRIPT',\n    'FLUSH',\n    'LOAD',\n    'SDIFF',\n    'SDIFFSTORE',\n    'SELECT',\n    'SETBIT',\n    'SETEX',\n    'SETNX',\n    'SETRANGE',\n    'SHUTDOWN',\n    'SINTER',\n    'SINTERSTORE',\n    'SISMEMBER',\n    'SLAVEOF',\n    'SLOWLOG',\n    'SMEMBERS',\n    'SMOVE',\n    'SORT',\n    'SPOP',\n    'SRANDMEMBER',\n    'SREM',\n    'STRLEN',\n    'SUBSCRIBE',\n    'SUNION',\n    'SUNIONSTORE',\n    'SWAPDB',\n    'SYNC',\n    'TIME',\n    'TOUCH',\n    'TTL',\n    'TYPE',\n    'UNSUBSCRIBE',\n    'UNLINK',\n    'UNWATCH',\n    'WAIT',\n    'WATCH',\n    'ZADD',\n    'ZCARD',\n    'ZCOUNT',\n    'ZINCRBY',\n    'ZINTERSTORE',\n    'ZLEXCOUNT',\n    'ZRANGE',\n    'ZRANGEBYLEX',\n    'ZREVRANGEBYLEX',\n    'ZRANGEBYSCORE',\n    'ZRANK',\n    'ZREM',\n    'ZREMRANGEBYLEX',\n    'ZREMRANGEBYRANK',\n    'ZREMRANGEBYSCORE',\n    'ZREVRANGE',\n    'ZREVRANGEBYSCORE',\n    'ZREVRANK',\n    'ZSCORE',\n    'ZUNIONSTORE',\n    'SCAN',\n    'SSCAN',\n    'HSCAN',\n    'ZSCAN',\n  ],\n  functions: [],\n};\n"
  },
  {
    "path": "chat2db-client/src/constants/IntelliSense/sqlserver.ts",
    "content": "import { DatabaseTypeCode } from '../common';\n\nexport default {\n  type: DatabaseTypeCode.SQLSERVER,\n  keywords: [\n    'ABSOLUTE',\n    'ACTION',\n    'ADD',\n    'AFTER',\n    'ALL',\n    'ALTER',\n    'ALWAYS',\n    'AND',\n    'ANY',\n    'APPLY',\n    'AS',\n    'ASC',\n    'AUTHORIZATION',\n    'BACKUP',\n    'BEGIN',\n    'BETWEEN',\n    'BINARY',\n    'BREAK',\n    'BROWSE',\n    'BULK',\n    'BY',\n    'CASCADE',\n    'CASE',\n    'CAST',\n    'CATCH',\n    'CHECK',\n    'CHECKPOINT',\n    'CLOSE',\n    'CLUSTERED',\n    'COALESCE',\n    'COLLATE',\n    'COLUMN',\n    'COMMIT',\n    'COMPUTE',\n    'CONSTRAINT',\n    'CONTAINS',\n    'CONTAINSTABLE',\n    'CONTINUE',\n    'CONVERT',\n    'CORRESPONDING',\n    'CREATE',\n    'CROSS',\n    'CURRENT',\n    'CURSOR',\n    'CYCLE',\n    'DATA',\n    'DATABASE',\n    'DBCC',\n    'DEALLOCATE',\n    'DECLARE',\n    'DEFAULT',\n    'DELETE',\n    'DENY',\n    'DESC',\n    'DESCRIPTION',\n    'DISABLE',\n    'DISALLOW',\n    'DISCONNECT',\n    'DISTINCT',\n    'DISTRIBUTED',\n    'DOUBLE',\n    'DROP',\n    'DUMP',\n    'ELSE',\n    'ENABLE',\n    'END',\n    'ERRLVL',\n    'ESCAPE',\n    'EXCEPT',\n    'EXEC',\n    'EXECUTE',\n    'EXISTS',\n    'EXIT',\n    'EXTERNAL',\n    'FETCH',\n    'FILE',\n    'FILLFACTOR',\n    'FOLLOWING',\n    'FOR',\n    'FOREIGN',\n    'FROM',\n    'FULL',\n    'FUNCTION',\n    'GOTO',\n    'GRANT',\n    'GROUP',\n    'HAVING',\n    'HOLDLOCK',\n    'IDENTITY',\n    'IF',\n    'IN',\n    'INDEX',\n    'INNER',\n    'INSERT',\n    'INSTEAD',\n    'INTERSECT',\n    'INTO',\n    'IS',\n    'JOIN',\n    'KEY',\n    'KILL',\n    'LAST',\n    'LEFT',\n    'LIKE',\n    'LIMIT',\n    'LINENO',\n    'LOAD',\n    'LOCAL',\n    'MERGE',\n    'NATIONAL',\n    'NOCHECK',\n    'NONCLUSTERED',\n    'NOT',\n    'NULL',\n    'NULLIF',\n    'OF',\n    'OFF',\n    'OFFSETS',\n    'ON',\n    'ONLY',\n    'OPEN',\n    'OPTION',\n    'OR',\n    'ORDER',\n    'OUTER',\n    'OUTPUT',\n    'OVER',\n    'PARTITION',\n    'PERCENT',\n    'PLAN',\n    'PRECEDING',\n    'PRECISION',\n    'PRIMARY',\n    'PRINT',\n    'PROC',\n    'PROCEDURE',\n    'PUBLIC',\n    'RAISERROR',\n    'READ',\n    'READONLY',\n    'READTEXT',\n    'RECONFIGURE',\n    'REFERENCES',\n    'REPLICATION',\n    'RESTORE',\n    'RESTRICT',\n    'RETURN',\n    'REVOKE',\n    'RIGHT',\n    'ROLLBACK',\n    'ROWCOUNT',\n    'ROWGUIDCOL',\n    'RULE',\n    'SAVE',\n    'SCHEMA',\n    'SECURITYAUDIT',\n    'SELECT',\n    'SEMANTICKEYPHRASETABLE',\n    'SEMANTICSIMILARITYDETAILSTABLE',\n    'SEMANTICSIMILARITYTABLE',\n    'SESSION_USER',\n    'SET',\n    'SETUSER',\n    'SHUTDOWN',\n    'SOME',\n    'STATISTICS',\n    'SYSTEM_USER',\n    'TABLE',\n    'TABLESAMPLE',\n    'TEXTSIZE',\n    'THEN',\n    'THROW',\n    'TO',\n    'TOP',\n    'TRAN',\n    'TRANSACTION',\n    'TRIGGER',\n    'TRUNCATE',\n    'TRY',\n    'TSEQUAL',\n    'UNION',\n    'UNIQUE',\n    'UNPIVOT',\n    'UPDATE',\n    'UPDATETEXT',\n    'USE',\n    'USER',\n    'VALUES',\n    'VARYING',\n    'VIEW',\n    'WAITFOR',\n    'WHEN',\n    'WHERE',\n    'WHILE',\n    'WITH',\n    'WITHIN',\n    'WRITETEXT',\n    'XML',\n  ],\n  functions: [\n    'ABS',\n    'ACOS',\n    'APP_NAME',\n    'ASCII',\n    'ASIN',\n    'ATAN',\n    'ATN2',\n    'AVG',\n    'BIGCOUNT',\n    'BINARY_CHECKSUM',\n    'CAST',\n    'CEILING',\n    'CHAR',\n    'CHARINDEX',\n    'CHECKSUM',\n    'CHECKSUM_AGG',\n    'COALESCE',\n    'COL_LENGTH',\n    'COL_NAME',\n    'CONCAT',\n    'CONCAT_WS',\n    'CONNECTIONPROPERTY',\n    'CONTEXT_INFO',\n    'CONVERT',\n    'COS',\n    'COT',\n    'COUNT',\n    'COUNT_BIG',\n    'CRYPT_GEN_RANDOM',\n    'CRYPT_PROPERTY',\n    'CURSOR_STATUS',\n    'DATABASEPROPERTYEX',\n    'DATEADD',\n    'DATEDIFF',\n    'DATENAME',\n    'DATEPART',\n    'DAY',\n    'DB_ID',\n    'DB_NAME',\n    'DEGREES',\n    'DIFFERENCE',\n    'EXP',\n    'FILE_ID',\n    'FILE_NAME',\n    'FILEGROUP_ID',\n    'FILEGROUP_NAME',\n    'FILEGROUPPROPERTY',\n    'FILEPROPERTY',\n    'FLOOR',\n    'FORMAT',\n    'GETANSINULL',\n    'GETDATE',\n    'GETUTCDATE',\n    'HASHBYTES',\n    'HOST_ID',\n    'HOST_NAME',\n    'IDENT_CURRENT',\n    'IDENT_INCR',\n    'IDENT_SEED',\n    'IIF',\n    'INDEX_COL',\n    'INDEXKEY_PROPERTY',\n    'INDEXPROPERTY',\n    'ISDATE',\n    'ISNULL',\n    'ISNUMERIC',\n    'IS_MEMBER',\n    'IS_OBJECTSIGNED',\n    'IS_SRVROLEMEMBER',\n    'ISNULL',\n    'LEFT',\n    'LEN',\n    'LOG',\n    'LOG10',\n    'LOWER',\n    'LTRIM',\n    'MAX',\n    'MIN',\n    'MONTH',\n    'NEWID',\n    'NULLIF',\n    'OBJECT_DEFINITION',\n    'OBJECT_ID',\n    'OBJECT_NAME',\n    'OBJECT_SCHEMA_NAME',\n    'OBJECTPROPERTY',\n    'OBJECTPROPERTYEX',\n    'PARSE',\n    'PATINDEX',\n    'PERCENT_RANK',\n    'PERCENTILE_CONT',\n    'PERCENTILE_DISC',\n    'PI',\n    'POWER',\n    'RADIANS',\n    'RANK',\n    'REPLACE',\n    'REPLICATE',\n    'REVERSE',\n    'RIGHT',\n    'ROUND',\n    'ROW_NUMBER',\n    'RTRIM',\n    'SCOPE_IDENTITY',\n    'SERVERPROPERTY',\n    'SESSIONPROPERTY',\n    'SIGN',\n    'SIN',\n    'SOUNDEX',\n    'SPACE',\n    'SQRT',\n    'SQUARE',\n    'STATS_DATE',\n    'STDEV',\n    'STDEVP',\n    'STUFF',\n    'SUBSTRING',\n    'SUM',\n    'SUSER_ID',\n    'SUSER_NAME',\n    'SUSER_SID',\n    'SUSER_SNAME',\n    'SYSTEM_USER',\n    'TAN',\n    'TEXTPTR',\n    'TEXTVALID',\n    'TRY_CAST',\n    'TRY_CONVERT',\n    'TRY_PARSE',\n    'TYPE_ID',\n    'TYPE_NAME',\n    'TYPEPROPERTY',\n    'UNICODE',\n    'UPPER',\n    'USER_ID',\n    'USER_NAME',\n    'YEAR',\n    'GET_FILESTREAM_TRANSACTION_CONTEXT',\n    'CURRENT_TRANSACTION_ID',\n    'XACT_STATE',\n    'STRING_AGG',\n    'STRING_ESCAPE',\n    'STRING_SPLIT',\n    'FORMATMESSAGE',\n    'XML_SCHEMA_NAMESPACE',\n    'ISJSON',\n    'JSON_VALUE',\n    'JSON_QUERY',\n    'JSON_MODIFY',\n    'JSON_EXISTS',\n    'JSON_TEXTCONTAINS',\n    'CHOOSE',\n    'IIF',\n    'FORMAT',\n    'PARSE',\n    'SEQUENCE_SCHEMA',\n    'SEQUENCE_NAME',\n    'SPATIAL_ST_GEOMETRYTYPE',\n    'SPATIAL_ST_DIMENSION',\n    'SPATIAL_ST_SRID',\n    'SPATIAL_ST_ISVALID',\n    'SPATIAL_ST_ASTEXT',\n    'SPATIAL_ST_ASBINARY',\n    'SPATIAL_ST_AREA',\n    'SPATIAL_ST_LENGTH',\n    'SPATIAL_ST_ISCLOSED',\n    'SPATIAL_ST_NUMPOINTS',\n    'SPATIAL_ST_X',\n    'SPATIAL_ST_Y',\n    'SPATIAL_ST_NUMGEOMETRIES',\n    'SPATIAL_ST_BOUNDARY',\n    'SPATIAL_ST_BUFFER',\n    'SPATIAL_ST_CENTROID',\n    'SPATIAL_ST_CONTAINS',\n    'SPATIAL_ST_CONVEXHULL',\n    'SPATIAL_ST_CROSSES',\n    'SPATIAL_ST_DIFFERENCE',\n    'SPATIAL_ST_DISJOINT',\n    'SPATIAL_ST_DISTANCE',\n    'SPATIAL_ST_ENVELOPE',\n    'SPATIAL_ST_EQUALS',\n    'SPATIAL_ST_INTERSECTION',\n    'SPATIAL_ST_INTERSECTS',\n    'SPATIAL_ST_ISRING',\n    'SPATIAL_ST_ISSIMPLE',\n    'SPATIAL_ST_OVERLAPS',\n    'SPATIAL_ST_SYMDIFFERENCE',\n    'SPATIAL_ST_TOUCHES',\n    'SPATIAL_ST_UNION',\n    'SPATIAL_ST_WITHIN',\n  ],\n};\n"
  },
  {
    "path": "chat2db-client/src/constants/appConfig.ts",
    "content": "export const APP_NAME = 'Chat2DB';\nexport const GITHUB_URL = 'https://github.com/chat2db/Chat2DB/blob/main/CHANGELOG.md'\nexport const WEBSITE_DOC = 'https://doc.sqlgpt.cn/changelog/'\n\n"
  },
  {
    "path": "chat2db-client/src/constants/chat.ts",
    "content": "export const chatError = {\n  CHAT2DB_KEY_INVALID: 'apikey 不在我们的数据库中需要扫码登录',\n  CHAT2DB_KEY_LIMIT: '次数用完了，需要发起推广',\n  CHAT2DB_KEY_EXPIRED: '到过期时间了',\n  CHAT2DB_SERVICE_BUSY: '这个异常就稍后重试就行了',\n  CHAT2DB_AUTH_HEADER_MISSING: '这个是 http 请求 header 没传 Authorization 字段，这个你看要怎么处理',\n  CHAT2DB_AUTH_TOKEN_MISSING:\n    '这个是 http 请求 header 中 Authorization 后面没有以 Bearer 开头，也是传的认证信息有问题，你看要怎么处理',\n  CHAT2DB_SERVICE_ERROR: '这个是出了意料之外的异常要联系管理员',\n  CHAT2DB_BAD_JSON_FORMAT: '这个是传的请求不是 json 格式',\n  CHAT2DB_HTTP_METHOD_INVALID: '这个是传的请求不是 post 请求，目前给 openai 的请求必须是 post',\n};\n\nexport const chatErrorCodeArr = Object.keys(chatError);\n\nexport const chatErrorToLogin = ['CHAT2DB_KEY_INVALID', 'CHAT2DB_AUTH_HEADER_MISSING', 'CHAT2DB_AUTH_TOKEN_MISSING'];\nexport const chatErrorForKey = ['CHAT2DB_KEY_LIMIT', 'CHAT2DB_KEY_EXPIRED'];\n"
  },
  {
    "path": "chat2db-client/src/constants/common.ts",
    "content": "export enum DatabaseTypeCode {\n  MYSQL = 'MYSQL',\n  ORACLE = 'ORACLE',\n  DB2 = 'DB2',\n  MONGODB = 'MONGODB',\n  REDIS = 'REDIS',\n  H2 = 'H2',\n  POSTGRESQL = 'POSTGRESQL',\n  SQLSERVER = 'SQLSERVER',\n  SQLITE = 'SQLITE',\n  MARIADB = 'MARIADB',\n  CLICKHOUSE = 'CLICKHOUSE',\n  DM = 'DM',\n  OCEANBASE = 'OCEANBASE',\n  PRESTO = 'PRESTO',\n  HIVE = 'HIVE',\n  KINGBASE = 'KINGBASE',\n  TIMEPLUS = 'TIMEPLUS',\n}\n\nexport enum ConsoleStatus {\n  DRAFT = 'DRAFT',\n  RELEASE = 'RELEASE',\n}\n\nexport enum OSType {\n  WIN = 'Win',\n  MAC = 'Mac',\n  RESTS = 'rests',\n}\n\nexport enum ConnectionKind {\n  Private = 'PRIVATE',\n  Shared = 'SHARED',\n}\n\n// 通用的增删改查枚举\nexport enum CRUD {\n  CREATE = 'CREATE',\n  READ = 'READ',\n  UPDATE = 'UPDATE',\n  DELETE = 'DELETE',\n  UPDATE_COPY = 'UPDATE_COPY',\n}\n"
  },
  {
    "path": "chat2db-client/src/constants/console.ts",
    "content": "\nexport enum ConsoleOpenedStatus {\n  IS_OPEN = 'y',\n  NOT_OPEN = 'n',\n}\n"
  },
  {
    "path": "chat2db-client/src/constants/database.ts",
    "content": "import mysqlLogo from '@/assets/img/databaseImg/mysql.png';\nimport redisLogo from '@/assets/img/databaseImg/redis.png';\nimport h2Logo from '@/assets/img/databaseImg/h2.png';\nimport moreDBLogo from '@/assets/img/databaseImg/other.png';\nimport { IDatabase } from '@/typings';\nimport { DatabaseTypeCode } from '@/constants';\n\nexport enum ConnectionEnvType {\n  DAILY = 'DAILY',\n  PRODUCT = 'PRODUCT',\n}\n\nexport const databaseMap: {\n  [keys: string]: IDatabase;\n} = {\n  [DatabaseTypeCode.MYSQL]: {\n    name: 'MySQL',\n    img: mysqlLogo,\n    code: DatabaseTypeCode.MYSQL,\n    // port: 3306,\n    icon: '\\uec6d',\n  },\n  [DatabaseTypeCode.H2]: {\n    name: 'H2',\n    img: h2Logo,\n    code: DatabaseTypeCode.H2,\n    // port: 9092,\n    icon: '\\ue61c',\n  },\n  [DatabaseTypeCode.ORACLE]: {\n    name: 'Oracle',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.ORACLE,\n    // port: 1521,\n    icon: '\\uec48',\n  },\n  [DatabaseTypeCode.POSTGRESQL]: {\n    name: 'PostgreSql',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.POSTGRESQL,\n    // port: 5432,\n    icon: '\\uec5d',\n  },\n  [DatabaseTypeCode.SQLSERVER]: {\n    name: 'SQLServer',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.SQLSERVER,\n    // port: 1521,\n    icon: '\\ue664',\n  },\n  [DatabaseTypeCode.SQLITE]: {\n    name: 'SQLite',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.SQLITE,\n    // port: 5432,\n    icon: '\\ue65a',\n  },\n  [DatabaseTypeCode.MARIADB]: {\n    name: 'Mariadb',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.MARIADB,\n    // port: 3306,\n    icon: '\\ue6f5',\n  },\n  [DatabaseTypeCode.CLICKHOUSE]: {\n    name: 'ClickHouse',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.CLICKHOUSE,\n    // port: 8123,\n    icon: '\\ue8f4',\n  },\n  [DatabaseTypeCode.DM]: {\n    name: 'DM',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.DM,\n    // port: 5236,\n    icon: '\\ue655',\n  },\n  [DatabaseTypeCode.PRESTO]: {\n    name: 'Presto',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.PRESTO,\n    //  port: 8080,\n    icon: '\\ue60b',\n  },\n  [DatabaseTypeCode.DB2]: {\n    name: 'DB2',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.DB2,\n    //  port: 50000,\n    icon: '\\ue60a',\n  },\n  [DatabaseTypeCode.OCEANBASE]: {\n    name: 'OceanBase',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.OCEANBASE,\n    // port: 2883,\n    icon: '\\ue982',\n  },\n  [DatabaseTypeCode.HIVE]: {\n    name: 'Hive',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.HIVE,\n    // port: 10000,\n    icon: '\\ue60e',\n  },\n  [DatabaseTypeCode.KINGBASE]: {\n    name: 'KingBase',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.KINGBASE,\n    // port: 54321,\n    icon: '\\ue6a0',\n  },\n  [DatabaseTypeCode.MONGODB]: {\n    name: 'MongoDB',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.MONGODB,\n    // port: 27017,\n    icon: '\\uec21',\n  },\n  [DatabaseTypeCode.TIMEPLUS]: {\n    name: 'Timeplus',\n    img: moreDBLogo,\n    code: DatabaseTypeCode.TIMEPLUS,\n    // port: 8123,\n    icon: '\\ue8f4',\n  },\n  // [DatabaseTypeCode.REDIS]: {\n  //   name: 'Redis',\n  //   img: moreDBLogo,\n  //   code: DatabaseTypeCode.REDIS,\n  //   // port: 6379,\n  //   icon: '\\ue6a2',\n  // },\n};\n\nexport const databaseTypeList = Object.keys(databaseMap).map((keys) => {\n  return databaseMap[keys];\n});\n"
  },
  {
    "path": "chat2db-client/src/constants/editTable.ts",
    "content": "export enum EditColumnOperationType { \n  // 新增\n  Add = 'ADD',\n  // 修改\n  Modify = 'MODIFY',\n  // 删除\n  Delete = 'DELETE',\n}\n\n// nullable\nexport enum NullableType {\n  // 不可为空\n  NotNull = 0,\n  // 可为空\n  Null = 1,\n}\n"
  },
  {
    "path": "chat2db-client/src/constants/environment.ts",
    "content": ""
  },
  {
    "path": "chat2db-client/src/constants/index.ts",
    "content": "export * from './appConfig';\nexport * from './common';\nexport * from './database';\nexport * from './environment';\nexport * from './table';\nexport * from './theme';\nexport * from './tree';\nexport * from './workspace';\nexport * from './editTable';\nexport * from './console';\n\n"
  },
  {
    "path": "chat2db-client/src/constants/table.ts",
    "content": "export enum TableDataType {\n  BOOLEAN = 'BOOLEAN',\n  NUMERIC = 'NUMERIC',\n  STRING = 'STRING',\n  DATETIME = 'DATETIME',\n  // 暂时不适配\n  BINARY = 'BINARY',\n  CONTENT = 'CONTENT',\n  STRUCT = 'STRUCT',\n  DOCUMENT = 'DOCUMENT',\n  ARRAY = 'ARRAY',\n  OBJECT = 'OBJECT',\n  REFERENCE = 'REFERENCE',\n  ROWID = 'ROWID',\n  ANY = 'ANY',\n  UNKNOWN = 'UNKNOWN',\n  CHAT2DB_ROW_NUMBER = 'CHAT2DB_ROW_NUMBER',\n}\n\nexport enum StatusType {\n  SUCCESS = 'success',\n  FAIL = 'fail',\n}\n"
  },
  {
    "path": "chat2db-client/src/constants/theme.ts",
    "content": "export enum ThemeType {\n  Light = 'light',\n  Dark = 'dark',\n  DarkDimmed = 'darkDimmed',\n  FollowOs = 'followOs',\n}\n\nexport enum EditorThemeType {\n  DashboardLightTheme = 'DashboardLightTheme',\n  DashboardBlackTheme = 'DashboardBlackTheme',\n}\n\nexport enum PrimaryColorType {\n  Polar_Green = 'polar-green',\n  Golden_Purple = 'golden-purple',\n  Polar_Blue = 'polar-blue',\n  Silver = 'silver',\n  Red = 'red',\n  Orange = 'orange',\n  Blue2 = 'blue2',\n  Gold = 'gold',\n}\n\nexport enum LangType {\n  EN_US = 'en-us',\n  ZH_CN = 'zh-cn',\n  TR_TR = 'tr-tr',\n  JA_JP = 'ja-jp',\n}\n"
  },
  {
    "path": "chat2db-client/src/constants/tree.ts",
    "content": "export enum TreeNodeType {\n  DATA_SOURCES = 'dataSources',\n  DATA_SOURCE = 'dataSource',\n  DATABASE = 'database',\n  SCHEMAS = 'schemas',\n  TABLES = 'tables',\n  TABLE = 'table',\n  COLUMNS = 'columns',\n  COLUMN = 'column',\n  KEYS = 'keys',\n  KEY = 'key',\n  INDEXES = 'indexes',\n  INDEX = 'index',\n  VIEWS = 'views', // 视图组\n  VIEW = 'view', // 视图\n  VIEWCOLUMN = 'viewColumn',\n  VIEWCOLUMNS = 'viewColumns',\n  FUNCTIONS = 'functions', // 函数组\n  FUNCTION = 'function', // 函数\n  PROCEDURES = 'procedures', // procedure组\n  PROCEDURE = 'procedure', // procedure\n  TRIGGERS = 'triggers',  // trigger组\n  TRIGGER = 'trigger',  // trigger\n  SEQUENCES = 'sequences',\n  SEQUENCE = 'sequence',\n}\n\n// 树右键支持的功能\nexport enum OperationColumn {\n  ShiftOut = 'shiftOut', // 移出数据源\n  Refresh = 'refresh', // 刷新各级菜单\n  CreateTable = 'createTable', //创建表\n  CreateConsole = 'createConsole', // 新建console\n  DeleteTable = 'deleteTable', // 删除表\n  OpenTable = 'openTable', // 打开表\n  ViewDDL = 'viewDDL', // 查看ddl\n  EditSource = 'editSource', // 编辑数据源\n  Pin = 'pin', // 置顶\n  EditTable = 'editTable', // 编辑表\n  EditTableData = 'editTableData', // 编辑表数据\n  CopyName = 'copyName', // 复制名称\n  EditView = 'editView', // 编辑视图\n  OpenView = 'openView', // 打开视图\n  OpenFunction = 'openFunction', // 打开函数\n  OpenProcedure = 'openProcedure', // 打开存储过程\n  OpenTrigger = 'openTrigger', // 打开触发器\n  CreateSchema = 'createSchema', // 新建schema\n  CreateDatabase = 'createDatabase', // 新建database\n  ViewAllTable = 'viewAllTable', // 查看所有的表\n  OpenSequence = 'openSequence', // 打开序列\n  CreateSequence = 'createSequence', // 新建序列\n  EditSequence = 'editSequence', /// 编辑序列\n  DeleteSequence = 'deleteSequence' // 删除序列\n}\n"
  },
  {
    "path": "chat2db-client/src/constants/workspace.ts",
    "content": "export enum CreateTabIntroType {\n  EditorTable = 'editorTable',\n  EditTableData = 'editTableData',\n}\n\n\n// 工作台Tab的类型\nexport enum WorkspaceTabType {\n  CONSOLE = 'console',\n  FUNCTION = 'function',\n  PROCEDURE = 'procedure',\n  VIEW = 'view',\n  TRIGGER = 'trigger',\n  SEQUENCE = 'sequence',\n  EditTable = 'editTable',\n  CreateTable = 'createTable',\n  EditTableData = 'editTableData',\n  ViewAllTable = 'viewAllTable',\n  CreateSequence = 'createSequence',\n  EditSequence = 'editSequence',\n}\n\n// 工作台Tab的类型对应的一些配置\nexport const workspaceTabConfig: {\n  [key in WorkspaceTabType]: {\n    icon: string\n  };\n} = {\n  [WorkspaceTabType.CONSOLE]: {\n    icon: '\\uec83'\n  },\n  [WorkspaceTabType.VIEW]: {\n    icon: '\\ue70c'\n  },\n  [WorkspaceTabType.FUNCTION]: {\n    icon: '\\ue76a'\n  },\n  [WorkspaceTabType.PROCEDURE]: {\n    icon: '\\ue73c'\n  },\n  [WorkspaceTabType.TRIGGER]: {\n    icon: '\\ue64a'\n  },\n  [WorkspaceTabType.SEQUENCE]: {\n    icon: '\\ue63e'\n  },\n  [WorkspaceTabType.EditTable]: {\n    icon: '\\ue6f3'\n  },\n  [WorkspaceTabType.CreateTable]: {\n    icon: '\\ue6b6'\n  },\n  [WorkspaceTabType.EditTableData]: {\n    icon: '\\ue618'\n  },\n  [WorkspaceTabType.ViewAllTable]: {\n    icon: '\\ue611'\n  },\n  [WorkspaceTabType.CreateSequence]: {\n    icon: '\\ue63e'\n  },\n  [WorkspaceTabType.EditSequence]: {\n    icon: '\\ue63e'\n  }\n}\n\n"
  },
  {
    "path": "chat2db-client/src/hooks/getConnection.ts",
    "content": "import connectionService from '@/service/connection';\nimport { setConnectionEnvList, getConnectionList } from '@/pages/main/store/connection';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\n\nconst getConnectionEnvList = () => {\n  connectionService.getEnvList().then((res) => {\n    setConnectionEnvList(res);\n  });\n};\nimport { setCurrentConnectionDetails } from '@/pages/main/workspace/store/common';\n\nconst getConnection = () => {\n  const currentConnectionDetails = useWorkspaceStore.getState().currentConnectionDetails;\n\n\n  getConnectionList().then((res) => {\n    // 如果连接列表为空，则设置当前连接为空\n    if (res.length === 0) {\n      setCurrentConnectionDetails(null);\n      return;\n    }\n\n    // 如果当前连接不存在，则设置当前连接为第一个连接\n    if (!currentConnectionDetails?.id) {\n      setCurrentConnectionDetails(res[0]);\n      return;\n    }\n\n    // 如果存在但是不在列表中，则设置当前连接为第一个连接\n    const currentConnection = res.find((item) => item.id === currentConnectionDetails?.id);\n    if (!currentConnection) {\n      setCurrentConnectionDetails(res[0]);\n    }\n  });\n\n  getConnectionEnvList();\n};\n\nexport default getConnection;\n"
  },
  {
    "path": "chat2db-client/src/hooks/index.ts",
    "content": "export * from './useTheme';\nexport * from './useUpdateEffect';\nexport * from './useEventSource';\n"
  },
  {
    "path": "chat2db-client/src/hooks/useClickAndDoubleClick.ts",
    "content": "import { useState, useEffect, useCallback } from 'react';\n\nconst useClickAndDoubleClick = (singleClickCallback, doubleClickCallback, delay = 250) => {\n  const [clickCount, setClickCount] = useState(0);\n  const [eventData, setEventData] = useState(null);\n\n  const handleClick = useCallback((data) => {\n    setEventData(data);\n    setClickCount((prev) => prev + 1);\n  }, []);\n\n  useEffect(() => {\n    if (clickCount === 1) {\n      const singleClickTimer = setTimeout(() => {\n        singleClickCallback(eventData);\n        setClickCount(0);\n      }, delay);\n      return () => clearTimeout(singleClickTimer);\n    } else if (clickCount === 2) {\n      doubleClickCallback(eventData);\n      setClickCount(0);\n    }\n  }, [clickCount, eventData, singleClickCallback, doubleClickCallback, delay]);\n\n  return handleClick;\n};\n\nexport default useClickAndDoubleClick;\n"
  },
  {
    "path": "chat2db-client/src/hooks/useEventSource.ts",
    "content": "import React, { useEffect, useMemo, useState } from 'react';\nimport { EventSourcePolyfill } from 'event-source-polyfill';\nimport { v4 as uuidv4 } from 'uuid';\n\nfunction useEventSource({ url }) {\n  const uid = useMemo(() => uuidv4(), []);\n  const [messages, setMessage] = useState('');\n\n  useEffect(() => {\n    handleEventSource();\n  }, []);\n\n  const handleEventSource = () => {\n    const eventSource = new EventSourcePolyfill(url, {\n      headers: {\n        uid,\n      },\n    });\n\n    eventSource.onmessage = (event) => {\n      setMessage(event.data);\n      const isEOF = event.data === '[DONE]';\n      if (isEOF) {\n        eventSource.close();\n      }\n    };\n\n    eventSource.onerror = (error) => {\n      console.error('EventSourcePolyfill error:', error);\n    };\n  };\n\n  return messages;\n}\n\nexport default useEventSource;\n"
  },
  {
    "path": "chat2db-client/src/hooks/useFocusData.ts",
    "content": "import { useEffect } from 'react';\nimport { useCommonStore } from '@/store/common';\nimport { tableCopy, copy } from '@/utils'\nimport { setFocusedContent } from '@/store/common/copyFocusedContent';\n\n// 如果用户点击的不是可复制的元素，就清空选中的内容\nfunction useCopyFocusData() {\n  const { focusedContent } = useCommonStore((state) => {\n    return {\n      focusedContent: state.focusedContent\n    }\n  });\n  \n  // 注册快捷键监听cmd+c或ctrl+c复制focusedContent\n  useEffect(() => {\n    const handleCopy = (e: KeyboardEvent) => {\n      if (e.key === 'c' && (e.metaKey || e.ctrlKey)) {\n        if (!focusedContent) return\n        // 如果是数据是数组，就调用tableCopy\n        if (Array.isArray(focusedContent)) {\n          tableCopy(focusedContent as any)\n          return\n        }\n        copy(focusedContent as any);\n      }\n    };\n    document.addEventListener('keydown', handleCopy);\n    return () => {\n      document.removeEventListener('keydown', handleCopy);\n    };\n  }, [focusedContent]);\n\n  useEffect(() => {\n    const handleClick = (event) => {\n      const targetElement = event.target  as Element;\n      if (!targetElement.closest('[data-chat2db-general-can-copy-element]')) {\n        setFocusedContent(null)\n      }\n    };\n    document.addEventListener('click', handleClick);\n    document.addEventListener('contextmenu', handleClick);\n    return () => {\n      document.removeEventListener('click', handleClick);\n      document.removeEventListener('contextmenu', handleClick);\n    };\n  }, [focusedContent]);\n}\n\nexport default useCopyFocusData;\n"
  },
  {
    "path": "chat2db-client/src/hooks/usePollRequestService.ts",
    "content": "import { useState, useEffect, useRef } from 'react';\n\ninterface IProps {\n  /** Maximum number of requests */\n  maxAttempts?: number;\n  /** Request interval ms */\n  interval?: number;\n  /** demand service */\n  loopService: (...rest) => Promise<boolean>;\n}\n\nexport enum ServiceStatus {\n  PENDING = 'PENDING',\n  SUCCESS = 'SUCCESS',\n  FAILURE = 'FAILURE',\n}\n\n/**\n * Polling request back-end service\n */\nconst usePollRequestService = ({ maxAttempts = 200, interval = 200, loopService }: IProps) => {\n  const [serviceStatus, setServiceStatus] = useState<ServiceStatus>(ServiceStatus.PENDING);\n  const [restart, setRestart] = useState(false);\n  const attempts = useRef(0);\n  const startupDate = useRef(new Date().getTime());\n\n  const serviceFn = async () => {\n    // The first request fails. Start the service\n    if (attempts.current === 1 && ServiceStatus.SUCCESS !== serviceStatus) {\n      window.electronApi?.startServerForSpawn();\n    }\n    if (attempts.current >= maxAttempts) {\n      setServiceStatus(ServiceStatus.FAILURE);\n      return;\n    }\n    attempts.current = attempts.current + 1;\n    loopService().then((res) => {\n      if (res) {\n        const now = new Date().getTime();\n        setTimeout(() => {\n          setServiceStatus(ServiceStatus.SUCCESS);\n        }, startupDate.current + 1000 - now);\n      }\n    })\n    .catch(() => {\n      setTimeout(serviceFn, interval);\n    });\n   \n  };\n\n  useEffect(() => {\n    serviceFn();\n  }, [maxAttempts, interval, restart]);\n\n  // Newly added reset function\n  const restartPolling = () => {\n    setServiceStatus(ServiceStatus.PENDING);\n    attempts.current = 0;\n    setRestart(!restart);\n  };\n\n  return { serviceStatus, restartPolling };\n};\n\nexport default usePollRequestService;\n"
  },
  {
    "path": "chat2db-client/src/hooks/useTheme.ts",
    "content": "import { useEffect, useState } from 'react';\nimport { getOsTheme } from '@/utils';\nimport { ITheme } from '@/typings';\nimport { ThemeType, PrimaryColorType } from '@/constants';\nimport { getPrimaryColor, getTheme, setPrimaryColor, setTheme } from '@/utils/localStorage';\nimport { v4 as uuidv4 } from 'uuid';\n\nconst colorSchemeListeners: {\n  [key: string]: (theme: { backgroundColor: ThemeType; primaryColor: PrimaryColorType }) => void;\n} = {};\n\nconst addColorSchemeListener = (\n  callback: (theme: { backgroundColor: ThemeType; primaryColor: PrimaryColorType }) => void,\n) => {\n  const uuid = uuidv4();\n  colorSchemeListeners[uuid] = callback;\n  return uuid;\n};\n\nconst initialTheme = () => {\n  const localStorageTheme = getTheme();\n  const localStoragePrimaryColor = getPrimaryColor();\n\n  // 判断localStorage的theme在不在ThemeType中, 如果存在就用localStorageTheme\n  let backgroundColor = ThemeType.Light;\n  if (Object.values(ThemeType).includes(localStorageTheme)) {\n    backgroundColor = localStorageTheme;\n  }\n\n  let primaryColor = PrimaryColorType.Golden_Purple;\n  if (Object.values(PrimaryColorType).includes(localStoragePrimaryColor)) {\n    primaryColor = localStoragePrimaryColor;\n  }\n\n  if (backgroundColor === ThemeType.FollowOs) {\n    backgroundColor = getOsTheme();\n  }\n  document.documentElement.setAttribute('theme', backgroundColor);\n  document.documentElement.setAttribute('primary-color', primaryColor);\n  return {\n    backgroundColor,\n    primaryColor,\n  };\n};\n\nexport function useTheme<T = ITheme>(): [T, React.Dispatch<React.SetStateAction<ITheme>>] {\n  const [appTheme, setAppTheme] = useState<ITheme>(initialTheme());\n\n  // const isDark = useMemo(() => appTheme.backgroundColor === ThemeType.Dark, [appTheme]);\n\n  useEffect(() => {\n    const uuid = addColorSchemeListener(setAppTheme as any);\n    return () => {\n      delete colorSchemeListeners[uuid];\n    };\n  }, []);\n\n  function handleAppThemeChange(theme: { backgroundColor: ThemeType; primaryColor: PrimaryColorType }) {\n    if (theme.backgroundColor === ThemeType.FollowOs) {\n      theme.backgroundColor =\n        window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches\n          ? ThemeType.DarkDimmed\n          : ThemeType.Light;\n    }\n    Object.keys(colorSchemeListeners)?.forEach((t) => {\n      colorSchemeListeners[t]?.(theme);\n    });\n    document.documentElement.setAttribute('theme', theme.backgroundColor);\n    setTheme(theme.backgroundColor);\n    document.documentElement.setAttribute('primary-color', theme.primaryColor);\n    setPrimaryColor(theme.primaryColor);\n  }\n\n  return [appTheme, handleAppThemeChange] as any;\n}\n"
  },
  {
    "path": "chat2db-client/src/hooks/useUpdateEffect.ts",
    "content": "import { useRef, useEffect } from 'react';\n\n/**\n * 第一次Effect更新不执行 \n * @param fn \n * @param arr \n */\nexport function useUpdateEffect(fn: Function, arr: any[]) {\n  const first = useRef(true);\n  useEffect(() => {\n    if (first.current) {\n      first.current = false;\n    } else {\n      fn();\n    }\n  }, arr);\n}\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/chat.ts",
    "content": "export default {\n  'chat.input.remain': '{1} remaining',\n  'chat.input.tableSelect.placeholder': 'Please choose tables',\n  'chat.input.tableSelect.error.TooManyTable': 'You can only select up to 8 tables',\n  'chat.input.remain.dialog.tips':\n    'Subscribe our official WeChat account, send 推广 to get more chances to experience.',\n  'chat.input.syncTable.tips': 'The automatically synchronize all table structures to the AI context',\n  'chat.input.remain.tooltip': 'The manually selected table will be synchronized to the AI context',\n  'chat.input.syncTable.tempTips': '🎉Update: Automatically synchronize all table structures to the AI context',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/common.ts",
    "content": "export default {\n  'common.text.no': 'no',\n  'common.text.is': 'is',\n  'common.button.affirm': 'Affirm',\n  'common.button.edit': 'Edit',\n  'common.button.modify': 'Modify',\n  'common.button.confirm': 'Confirm',\n  'common.button.cancel': 'Cancel',\n  'common.data.hour': '{1} {hour|hours}',\n  'common.data.minute': '{1} {minute|minutes}',\n  'common.tip.yesterday': '{1} yesterday',\n  'common.tip.tomorrow': '{1} tomorrow',\n  'common.tip.ago': ' ago',\n  'common.tip.later': ' later',\n  'common.tip.now': 'Now',\n  'common.tip.justNow': 'Just now',\n  'common.text.search': 'search',\n  'common.placeholder.select': 'Place Select {1}',\n  'common.text.serviceStarting': 'Service Starting ...',\n  'common.text.serviceFail': 'Service startup failed. Please try refreshing the page...',\n  'common.text.column': 'column',\n  'common.text.row': 'row',\n  'common.text.indexes': 'indexes',\n  'common.button.save': 'Save',\n  'common.button.open': 'Open',\n  'common.button.refresh': 'Refresh',\n  'common.button.execute': 'Run',\n  'common.button.import': 'Import SQL',\n  'common.button.format': 'Format',\n  'common.message.successfulConfig': 'Successful configuration',\n  'common.text.successful': 'successful',\n  'common.text.failure': 'failure',\n  'common.message.modifySuccessfully': 'modify successfully',\n  'common.message.addedSuccessfully': 'successfully added',\n  'common.text.custom': 'custom',\n  'common.button.delete': 'Delete',\n  'common.text.executionResult': 'Result {1}',\n  'common.tips.deleteTable': 'Are you sure delete this Table？',\n  'common.text.tableName': 'Table Name',\n  'common.text.submittedSuccessfully': 'Successfully submitted',\n  'common.text.successfullyDelete': 'Successfully Delete',\n  'common.text.explainSQL': 'Explain SQL',\n  'common.text.optimizeSQL': 'Optimize SQL',\n  'common.text.conversionSQL': 'Conversion SQL',\n  'common.text.table': 'Table',\n  'common.tips.saveSuccessfully': 'Save Successfully',\n  'common.button.copy': 'Copy',\n  'common.button.copyName': 'Copy name',\n  'common.button.copySuccessfully': 'Copy Successfully',\n  'common.button.createConsole': 'Create Console',\n  'common.button.exportWord': 'Export to Word',\n  'common.button.exportExcel': 'Export to Excel',\n  'common.button.exportHtml': 'Export to Html',\n  'common.button.exportMarkdown': 'Export to Markdown',\n  'common.button.exportPdf': 'Export to Pdf',\n  'common.text.successfulExecution': 'Successful Execution',\n  'common.text.result': 'Result',\n  'common.text.timeConsuming': 'Time Consumed',\n  'common.text.searchRow': 'Query Result',\n  'common.text.noData': 'No Data',\n  'common.text.remindMeLater': 'Remind Me Later',\n  'common.text.goToUpdate': 'Go To Update',\n  'common.text.updateReminder': 'Update Reminder',\n  'common.text.detectionLatestVersion': 'The latest version is monitored',\n  'common.text.setting': 'Setting',\n  'common.text.tryToRestart': 'Try To Restart',\n  'common.text.contactUs': 'Contact Us',\n  'common.text.wechatPopularizeAi': 'Follow the wechat public account and send \"AI\" to get free experiences.',\n  'common.text.wechatPopularizeAi2':\n    'Follow the wechat public account and send \"AI\" to get the ApiKey for free, and give away the number of experiences.',\n  'common.text.wechatPopularize': 'You can also send \"promotion\" to get more experiences for free.',\n  'common.text.export': 'Export',\n  'common.notification.detail': 'More details',\n  'common.notification.solution': 'Solution',\n  'common.button.copyError': 'Copy error report',\n  'common.button.copyErrorTips':\n    '（The interface information and detailed parameters will be copied here. If there are sensitive parameters, please parse JSON first and then send them）',\n  'common.tips.formatError': 'Formatting failed, please check whether the sql is correct',\n  'common.text.executeSelectedSQL': 'Execute selected SQL',\n  'common.text.refreshPage': 'Refresh page',\n  'common.text.saveConsole': 'Save console',\n  'common.text.textToSQL': 'Plain text to SQL',\n  'common.text.editorRightClick': 'Editor right click',\n  'common.form.error.required': 'This field is required!',\n  'common.form.error.email': 'The input is not a valid email!',\n  'common.tips.delete.confirm': 'Are you sure to delete it?',\n  'common.tips.updateSuccess': 'Update Successfully',\n  'common.tips.createSuccess': 'Create Successfully',\n  'common.text.action': 'Action',\n  'common.button.add': 'Add',\n  'common.text.errorMessage': 'Error Message',\n  'common.button.cancelRequest': 'Cancel Request',\n  'common.button.executionError': 'Execution Error',\n  'common.text.affectedRows': 'Affected rows: {1}',\n  'common.text.selectFile': 'Select File',\n  'common.text.noTableFoundUp': 'No tables in this database',\n  'common.text.noTableFoundDown': 'Switch databases at the top',\n  'common.title.preview': 'Preview',\n  'common.title.errorMessage': 'Error message',\n  'common.label.comment': 'Comment',\n  'common.label.name': 'Name',\n  'common.title.create': 'Create',\n  'common.title.executiveLogging': 'Runtime logs',\n  'common.text.executionTime': 'Done with {1} ms',\n  'common.button.copyRowAs': 'Copy the row as',\n  'common.button.insertSql': 'Insert SQL',\n  'common.button.updateSql': 'Update SQL',\n  'common.button.tabularSeparatedValues': 'TAB delimited (data)',\n  'common.button.tabularSeparatedValuesFieldName': 'TAB delimited (field name)',\n  'common.button.tabularSeparatedValuesFieldNameAndData': 'Tab-separated (field names and data)',\n  'common.button.cloneRow': 'Clone row',\n  'common.button.deleteRow': 'Delete row',\n  'common.button.setNull': 'Set NULL',\n  'common.button.setDefault': 'Set DEFAULT',\n  'common.button.viewData': 'View/Edit Data',\n  'common.button.close': 'Close',\n  'common.button.closeAll': 'Close all',\n  'common.button.closeOthers': 'Close others',\n  'common.label.tcp': 'TCP',\n  'common.label.LocalFile': 'LocalFile',\n  'common.text.rename': 'Rename',\n  'common.title.info': 'Info',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/connection.ts",
    "content": "export default {\n  'connection.title': 'Connections',\n  'connection.title.connections': 'Connections',\n  'connection.title.createConnection': 'New Connection',\n  'connection.title.editConnection': 'Edit Connection',\n  'connection.title.importConnection': 'Import Connection',\n  'connection.label.name': 'name',\n  'connection.label.host': 'host',\n  'connection.label.authentication': 'authentication',\n  'connection.label.database': 'database',\n  'connection.label.JDBCDrive': 'JDBC Driver',\n  'connection.label.port': 'port',\n  'connection.button.testConnection': 'Test',\n  'connection.label.advancedConfiguration': 'Advanced Configuration',\n  'connection.label.sshConfiguration': 'SSH Configuration',\n  'connection.button.addConnection': 'Add Connection',\n  'connection.button.connect': 'Connect',\n  'connection.button.remove': 'Remove',\n  'connection.message.testConnectResult': 'Test connection is {1}',\n  'connection.message.testSshConnection': 'Test the ssh connection',\n  'connection.tableHeader.name': 'Name',\n  'connection.tableHeader.value': 'Value',\n  'connection.title.uploadDriver': 'Upload',\n  'connection.tips.customUpload': \"Upload driver\",\n  'connection.title.driver': 'Driver',\n  'connection.button.clickUpload': 'Click to Upload',\n  'connection.text.downloadDriver': 'Download Driver',\n  'connection.text.downloadSuccess': 'Download Success',\n  'connection.text.tryAgainDownload': 'Try again download',\n  'connection.text.downloading': 'Downloading...',\n  'connection.label.private': 'Private',\n  'connection.label.shared': 'Shared',\n  'connection.button.createConnection': 'Create connection',\n  'connection.tips.noConnection': 'You have not created any connections yet',\n  'connection.tips.noConnectionTips': 'You do not have permission to view the connection details, but you can connect to the connection directly',\n  'connection.title.importTitle': 'Import file,.ncx(navicat) or.dbp(dbever)',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/dashboard.ts",
    "content": "export default {\n  'dashboard.title': 'Dashboard',\n  'dashboard.edit': 'Edit',\n  'dashboard.modal.editTitle': 'Edit Dashboard',\n  'dashboard.modal.addTitle': 'Add Dashboard',\n  'dashboard.modal.name.placeholder': \"Please enter dashboard's name.\",\n  'dashboard.export2image': 'Export to image',\n  'dashboard.delete': 'Delete',\n  'dashboard.editor.cascader.placeholder': 'Please select a connection pool',\n  'dashboard.editor.execute.noDataSource': 'Please select a data source first',\n  'dashboard.editor.execute.success': 'Successful, Please select Chart Configuration',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/editSequence.ts",
    "content": "export default {\n    'editSequence.button.createSequence': 'New Sequence',\n    'editSequence.button.editSequence': 'Edit Sequence',\n    'editSequence.label.comment': 'Comment',\n    'editSequence.label.relname': 'Sequence Name',\n    'editSequence.label.typname': 'Data Type',\n    'editSequence.label.seqcache': 'Cache',\n    'editSequence.label.rolname': 'Owner',\n    'editSequence.label.seqstart': 'Start Value',\n    'editSequence.label.seqincrement': 'Increment Value',\n    'editSequence.label.seqmax': 'Max Value',\n    'editSequence.label.seqmin': 'Min Value',\n    'editSequence.label.seqcycle': 'Cycle',\n    'editSequence.title.sqlPreview': 'SQL Preview',\n};"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/editTable.ts",
    "content": "export default {\n  'editTable.tab.basicInfo': 'Basic',\n  'editTable.tab.columnInfo': 'Column',\n  'editTable.tab.indexInfo': 'Index',\n  'editTable.label.tableName': 'Table name',\n  'editTable.label.comment': 'Comment',\n  'editTable.button.add': 'Add',\n  'editTable.button.delete': 'Delete',\n  'editTable.button.up': 'Up',\n  'editTable.button.down': 'Down',\n  'editTable.label.indexName': 'Name',\n  'editTable.label.indexType': 'Type',\n  'editTable.label.indexMethod': 'Index method',\n  'editTable.label.includeColumn': 'Include column',\n  'editTable.button.createTable': 'Create Table',\n  'editTable.button.importTable': 'Export Table',\n  'editTable.label.index': 'Index',\n  'editTable.label.columnName': 'Name',\n  'editTable.label.columnSize': 'Size',\n  'editTable.label.columnType': 'Type',\n  'editTable.label.nullable': 'Nullable',\n  'editTable.label.prefixLength': 'Prefix length',\n  'editTable.label.defaultValue': 'Default value',\n  'editTable.label.sparse': 'Sparse',\n  'editTable.label.characterSet': 'Character set',\n  'editTable.label.collation': 'Collation',\n  'editTable.label.decimalPoint': 'Decimal point',\n  'editTable.label.unit': 'Unit',\n  'editTable.label.value': 'Value',\n  'editTable.label.autoIncrement': 'Auto increment',\n  'editTable.label.engine': 'Engine',\n  'editTable.label.incrementValue': 'Increment value',\n  'editTable.label.order': 'Order',\n  'editTable.label.primaryKey': 'Key',\n  'editTable.title.sqlPreview': 'SQL preview',\n  'editTable.button.addColumn': 'Add column',\n  'editTable.button.addIndex': 'Add Index',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/editTableData.ts",
    "content": "export default {\n  'editTableData.tips.addRow': 'Add Row',\n  'editTableData.tips.deleteRow': 'Delete Row',\n  'editTableData.tips.revert': 'Revert',\n  'editTableData.tips.previewPendingChanges': 'Preview Pending Changes',\n  'editTableData.tips.submit': 'Submit',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/index.ts",
    "content": "import common from './common';\nimport connection from './connection';\nimport menu from './menu';\nimport setting from './setting';\nimport workspace from './workspace';\nimport dashboard from './dashboard';\nimport chat from './chat';\nimport team from './team'\nimport login from './login';\nimport editTable from './editTable';\nimport editTableData from './editTableData';\nimport sqlEditor from './sqlEditor'\nimport editSequence from './editSequence';\n\nexport default {\n  lang: 'en',\n  ...common,\n  ...setting,\n  ...connection,\n  ...workspace,\n  ...menu,\n  ...dashboard,\n  ...chat,\n  ...team,\n  ...login,\n  ...editTable,\n  ...editTableData,\n  ...sqlEditor,\n  ...editSequence\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/login.ts",
    "content": "export default {\n  'login.text.logout': 'Logout',\n  'login.text.welcome': 'Welcome to Chat2DB',\n  'login.text.tips': 'The Chat2DB account is only for team collaboration management.',\n  'login.text.tips.title': 'Why need login?',\n  'login.text.setting': 'Setting',\n  'login.form.user': 'UserName',\n  'login.form.user.placeholder': 'Please enter your username',\n  'login.form.password': 'Password',\n  'login.form.password.placeholder': 'Please enter your password',\n  'login.button.login': 'Login',\n  'login.tips.defaultPassword': 'The default user name and password are: chat2db',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/menu.ts",
    "content": "export default {\n  'menu.file' : 'File'\n}"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/setting.ts",
    "content": "export default {\n  'setting.title.setting': 'Setting',\n  'setting.nav.basic': 'Basic',\n  'setting.nav.customAi': 'Custom Ai',\n  'setting.nav.proxy': 'Service Path',\n  'setting.nav.aboutUs': 'About Us',\n  'setting.title.backgroundColor': 'Background Color',\n  'setting.title.themeColor': 'Theme Color',\n  'setting.title.sqlEditorFontSize': 'SQL Editor Font Size',\n  'setting.label.blue': 'Blue',\n  'setting.label.green': 'Green',\n  'setting.label.violet': 'Violet',\n  'setting.text.dark': 'Dark',\n  'setting.text.dark2': 'Dark-2',\n  'setting.text.light': 'Light',\n  'setting.text.followOS': 'FollowOS',\n  'setting.title.language': 'Language',\n  'setting.title.aiSource': 'AI Source',\n  'setting.tab.custom': 'Custom',\n  'setting.tab.aiType.zhipu': 'ZhiPu AI',\n  'setting.tab.aiType.baichuan': 'BaiChuan AI',\n  'setting.tab.aiType.wenxin': 'WenXin AI',\n  'setting.tab.aiType.tongyiqianwen': 'TongYiQianWen AI',\n  'setting.tab.aiType.custom.tips': 'The API format is consistent with the OpenAI API format',\n  'setting.label.serviceAddress': 'Service Address',\n  'setting.button.apply': 'Apply',\n  'setting.text.currentEnv': 'Current Env',\n  'setting.text.currentVersion': 'Current Version',\n  'setting.text.viewingUpdateLogs': 'Viewing Update Logs',\n  'setting.label.isStreamOutput': 'Whether the interface streams output',\n  'setting.label.customAiUrl': 'User-defined interface Url',\n  'setting.placeholder.httpsProxy': 'Not required. Set HTTP proxy {1} when requesting OPENAI interface.',\n  'setting.placeholder.apiKey': 'OpenAI official website to view the APIKEY',\n  'setting.placeholder.chat2dbApiKey': 'Use the APIKEY provided by Chat2DB',\n  'setting.placeholder.customUrl': 'URL of the REST interface of the AI',\n  'setting.placeholder.apiHost': 'This parameter is mandatory. The default value is https://api.openai.com/',\n  'setting.message.urlTestError': 'The interface test failed. Procedure',\n  'setting.placeholder.azureOpenAIKey': 'Get Azure OpenAI key credential from the Azure Portal',\n  'setting.placeholder.azureEndpoint': 'Get Azure OpenAI endpoint from the Azure Portal',\n  'setting.placeholder.azureDeployment': 'Deployment id of the deployed model',\n  'setting.ai.tips': 'Please log in and select AI configuration',\n  'setting.ai.user.hidden': 'Please contact the administrator to set ApiKey in \"Settings ->Custom Ai\"',\n  'setting.button.startDownloading': 'Start Downloading',\n  'setting.button.beDownloading': 'Downloading',\n  'setting.button.redownload': 'Redownload',\n  'setting.button.restart': 'Restart',\n  'setting.text.discoverNewVersion': 'Discover new version {1}',\n  'setting.text.isLatestVersion': 'This is the latest version',\n  'setting.button.changeLog': 'Changelog',\n  'setting.title.updateRule': 'Update rule',\n  'setting.text.autoUpdate': 'The new version will be automatically downloaded and installed',\n  'setting.text.manualUpdate': 'Only alert me when a new version is released',\n  'setting.button.iSee': 'I see',\n  'setting.text.newEditionIsReady':\n    'New version to download completed, restart the software will install the new version',\n  'setting.button.goToUpdate': 'Go to update',\n  'setting.text.UpdatedLatestVersion': 'Updated to the latest version {1}',\n  'setting.title.holdingService': 'Holding Service',\n  'setting.text.holdingService': 'Keep the service when exiting the application to speed up startup',\n  'setting.chat2db.ai.button': 'Please visit Chat2DB Pro for more powerful AI features',\n  'setting.title.goto.chat2db.pro': 'Go to Chat2DB Pro',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/sqlEditor.ts",
    "content": "export default {\n  'sqlEditor.text.keyword': 'Keyword',\n  'sqlEditor.text.function': 'Function',\n  'sqlEditor.text.tableName': 'TableName',\n  'sqlEditor.text.databaseName': 'DatabaseName',\n  'sqlEditor.text.schemaName': 'Schema',\n  'sqlEditor.text.viewName': 'ViewName',\n  'sqlEditor.text.fieldName': 'FieldName',\n\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/team.ts",
    "content": "export default {\n    'team.title': 'Team Management',\n    'team.tab.datasource': 'DataSource Management',\n    'team.tab.user': 'User Management',\n    'team.tab.team': 'Team Management',\n\n\n    'team.action.rightManagement': 'Right Management',\n    'team.action.editDatasource': 'Edit DataSource',\n    'team.action.addDatasource': 'Add DataSource',\n    'team.action.addDatasource.placeholder': 'Search DataSource',\n    'team.action.editUser': 'Edit user',\n    'team.action.addUser': 'Add User',\n    'team.action.addUser.placeholder': 'Search User',\n    'team.action.editTeam': 'Edit Team',\n    'team.action.addTeam': 'Add Team',\n    'team.action.addTeam.placeholder': 'Search Team',\n    'team.action.affiliation.user': 'Affiliation User',\n    'team.action.affiliation.team': 'Affiliation Team',\n    'team.action.affiliation.datasource': 'Affiliation DataSource',\n    'team.action.addUserAndTeam': 'Add User/Team',\n    'team.action.addUserAndTeam.placeholder': 'Search User/Team',\n    'team.input.search.placeholder': 'Please enter keywords to search',\n\n    'team.datasource.rightManagement': 'Right Management',\n    'team.datasource.alias': 'DataSource Name',\n    'team.datasource.url': 'DataSource URL',\n    'team.datasource.code': 'Code',\n    'team.datasource.name': 'Name',\n    'team.datasource.status': 'Status',\n\n    'team.user.name': 'User',\n    'team.user.userName': 'UserName',\n    'team.user.nickName': 'NickName',\n    'team.user.status': 'Status',\n    'team.user.addForm.userName': 'UserName',\n    'team.user.addForm.nickName': 'NickName',\n    'team.user.addForm.email': 'Email',\n    'team.user.addForm.password': 'Password',\n    'team.user.addForm.roleCode': 'Role',\n    'team.user.addForm.roleCode.admin': 'Admin',\n    'team.user.addForm.roleCode.user': 'User',\n    'team.user.addForm.status': 'Status',\n    'team.user.addForm.status.valid': 'Valid',\n    'team.user.addForm.status.invalid': 'Invalid',\n\n\n    'team.team.name': 'Team',\n    'team.team.addForm.code': 'Team Code',\n    'team.team.addForm.name': 'Team Name',\n    'team.team.addForm.status': 'Status',\n    'team.team.addForm.status.valid': 'Valid',\n    'team.team.addForm.status.invalid': 'Invalid',\n    'team.team.addForm.description': 'Description',\n\n\n}"
  },
  {
    "path": "chat2db-client/src/i18n/en-us/workspace.ts",
    "content": "export default {\n  'workspace.title': 'Workspace',\n  'workspace.cascader.placeholder': 'Select Here',\n  'workspace.ai.input.placeholder': 'Enter your plain text statement here',\n  'workspace.title.savedConsole': 'Saved console',\n  'workspace.menu.ViewDDL': 'View DDL',\n  'workspace.menu.deleteTable': 'Delete Table',\n  'workspace.menu.openTable': 'Open Table',\n  'workspace.menu.editTable': 'Edit Table',\n  'workspace.menu.view': 'View',\n  'workspace.menu.pin': 'Pin',\n  'workspace.menu.unPin': 'Unpin',\n  'workspace.menu.editTableData': 'Edit Table Data',\n  'workspace.menu.queryConsole': 'Query console',\n  'workspace.menu.viewAllTable': 'View all table',\n  'workspace.menu.createDatabase': 'Create database',\n  'workspace.menu.createSchema': 'Create schema',\n  'workspace.menu.deleteTablePlaceHolder': 'Please enter the name of the table you want to delete',\n  'workspace.menu.createSequence': 'Create sequence',\n  'workspace.menu.editSequence': 'Edit sequence',\n  'workspace.menu.deleteSequence': 'Delete sequence',\n  'workspace.tips.affirmDeleteTable':\n    'The table name you entered is not the same as the table name you want to delete, please confirm again',\n  'workspace.table.total': 'Total',\n  'workspace.table.total.tip': 'Load total number of rows',\n  'workspace.table.export.all.csv': 'Export results as a CSV',\n  'workspace.table.export.cur.csv': 'Export results on the current page as a CSV',\n  'workspace.table.export.all.insert': 'Export results as INSERT SQL',\n  'workspace.table.export.cur.insert': 'Export results on the current page as INSERT SQL',\n  'workspace.tree.view': 'View',\n  'workspace.tree.trigger': 'Trigger',\n  'workspace.tree.function': 'Function',\n  'workspace.tree.procedure': 'Procedure',\n  'workspace.tree.search.placeholder': 'Search in the expand node',\n  'workspace.tree.delete.tip': 'I understand that this operation is permanently deleted',\n  'workspace.tree.delete.table.tip': 'Are you sure you want to delete the table {1}?',\n  'workspace.tips.noConnection': 'You have not created a connection yet',\n  'workspace.tips.maxConsole': 'You can only open up to 20 consoles',\n  'workspace.tips.openExecutiveLogging': 'Open this executive logging',\n  'workspace.tree.delete.sequence.tip': 'Are you sure you want to delete the sequence {1}?',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/index.tsx",
    "content": "import React, { Fragment } from 'react';\nimport { getLang } from '@/utils/localStorage';\nimport { LangType } from '@/constants';\nimport zhCN from './zh-cn';\nimport enUS from './en-us';\nimport trTR from './tr-tr';\nimport jaJp from './ja-jp';\n\nconst locale = {\n  'en-us': enUS,\n  'zh-cn': zhCN,\n  'tr-tr': trTR,\n  'ja-jp': jaJp,\n};\n\nexport const currentLang: LangType = getLang() || LangType.EN_US;\n\nexport const isEn = currentLang === LangType.EN_US;\n\nexport const isZH = currentLang === LangType.ZH_CN;\n\nexport const isTR = currentLang === LangType.TR_TR;\n\nexport const isJA = currentLang === LangType.JA_JP;\n\nconst langSet: Record<string, string> = locale[currentLang];\n\nfunction i18n(key: keyof typeof zhCN, ...args: any[]) {\n  let result = langSet[key];\n  if (result === undefined) {\n    return `[${key}]`;\n  } else {\n    args.forEach((arg, i) => {\n      result = result.replace(new RegExp(`\\\\{${i + 1}\\\\}`, 'g'), arg);\n    });\n    if (args.length) {\n      result = result.replace(/\\{(.+?)\\|(.+?)\\}/g, (_, singular, plural) => {\n        const n = args[0];\n        return n == 1 ? singular : plural;\n      });\n    }\n    return result;\n  }\n}\n\nfunction i18nElement(key: keyof typeof zhCN, ...args: React.ReactNode[]) {\n  const str = langSet[key];\n  if (str === undefined) {\n    return `[${key}]`;\n  } else {\n    const result: React.ReactNode[] = [];\n    str.split(/(\\{\\d\\})/).forEach((item, i) => {\n      if (/^\\{\\d\\}$/.test(item)) {\n        result.push(\n          <Fragment key={i}>\n            {args[parseInt(item.substring(1, item.length - 1)) - 1]}\n          </Fragment>,\n        );\n      } else {\n        result.push(\n          <Fragment key={i}>\n            {item.replace(/\\{(.+?)\\|(.+?)\\}/g, (_, singular, plural) => {\n              const n = args[0];\n              return n == 1 ? singular : plural;\n            })}\n          </Fragment>,\n        );\n      }\n    });\n    return result;\n  }\n}\n\nexport default i18n;\nexport { i18n, i18nElement };\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/chat.ts",
    "content": "export default {\n  'chat.input.remain': '残り {1} 回',\n  'chat.input.tableSelect.placeholder': 'テーブルを選択してください',\n  'chat.input.tableSelect.error.TooManyTable': '最大で8つのテーブルを選択できます',\n  'chat.input.remain.dialog.tips': '公式アカウントをフォローし、\"プロモーション\"を送信して体験回数を増やす',\n  'chat.input.syncTable.tips': 'すべてのテーブル構造をAIコンテキストに自動同期（Chat2DBAIモデルのみで使用可能、グループ内でグループオーナーに連絡し、Chat2DBAIのホワイトリストに申請）',\n  'chat.input.remain.tooltip': '手動で選択したテーブルの構造はAIコンテキストに同期されます',\n  'chat.input.syncTable.tempTips': '🎉リリース：すべてのテーブル構造をAIコンテキストに自動同期',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/common.ts",
    "content": "export default {\n  'common.text.no': 'いいえ',\n  'common.text.is': 'は',\n  'common.button.affirm': '確認する',\n  'common.button.edit': '編集',\n  'common.button.modify': '修正',\n  'common.button.confirm': '確認',\n  'common.button.cancel': 'キャンセル',\n  'common.data.hour': '{1}時間',\n  'common.data.minute': '{1}分',\n  'common.tip.yesterday': '昨日{1}',\n  'common.tip.tomorrow': '明日{1}',\n  'common.tip.ago': '前',\n  'common.tip.later': '後',\n  'common.tip.now': '今',\n  'common.tip.justNow': 'ちょうど今',\n  'common.text.search': '検索',\n  'common.placeholder.select': '{1}を選択してください',\n  'common.text.serviceStarting': 'サービス開始中...',\n  'common.text.serviceFail': 'サービスの開始に失敗しました',\n  'common.text.column': '列',\n  'common.text.row': '行',\n  'common.text.indexes': 'インデックス',\n  'common.button.save': '保存',\n  'common.button.open': '開く',\n  'common.button.refresh': '更新',\n  'common.button.execute': '実行',\n  \"common.button.import\": 'SQLをインポート',\n  'common.button.format': 'フォーマット',\n  'common.message.successfulConfig': '設定成功',\n  'common.text.successful': '成功',\n  'common.text.failure': '失敗',\n  'common.message.modifySuccessfully': '修正成功',\n  'common.message.addedSuccessfully': '追加成功',\n  'common.text.custom': 'カスタム',\n  'common.button.delete': '削除',\n  'common.text.executionResult': '結果 {1}',\n  'common.tips.deleteTable': 'このテーブルを削除してもよろしいですか？',\n  'common.text.tableName': 'テーブル名',\n  'common.text.submittedSuccessfully': '正常に送信されました',\n  'common.text.successfullyDelete': '削除成功',\n  'common.text.explainSQL': 'SQLを説明する',\n  'common.text.optimizeSQL': 'SQLを最適化する',\n  'common.text.conversionSQL': 'SQLを変換する',\n  'common.text.table': 'テーブル',\n  'common.tips.saveSuccessfully': '保存成功',\n  'common.button.copy': 'コピー',\n  'common.button.copyName': '名前をコピー',\n  'common.button.copySuccessfully': 'コピー成功',\n  'common.button.createConsole': '新しいコンソールを作成',\n  'common.button.exportWord': 'Wordにエクスポート',\n  'common.button.exportExcel': 'Excelにエクスポート',\n  'common.button.exportHtml': 'Htmlにエクスポート',\n  'common.button.exportMarkdown': 'Markdownにエクスポート',\n  'common.button.exportPdf': 'Pdfにエクスポート',\n  'common.text.successfulExecution': '実行成功',\n  'common.text.result': '結果',\n  'common.text.timeConsuming': '時間がかかる',\n  'common.text.searchRow': '検索行数',\n  'common.text.noData': 'データなし',\n  'common.text.remindMeLater': '後でリマインド',\n  'common.text.goToUpdate': '更新に進む',\n  'common.text.updateReminder': '更新リマインダー',\n  'common.text.detectionLatestVersion': '最新バージョンを検出',\n  'common.text.setting': '設定',\n  'common.text.tryToRestart': '再起動を試みる',\n  'common.text.contactUs': 'お問い合わせ',\n  'common.text.wechatPopularizeAi': 'WeChat公式アカウントをフォローし、「AI」を送信して無料体験を取得。',\n  'common.text.wechatPopularizeAi2': 'WeChat公式アカウントをフォローし、「AI」を送信して無料でApiKeyを取得し、体験回数をプレゼント。',\n  'common.text.wechatPopularize': '「プロモーション」を送信して、さらに無料体験を取得。',\n  'common.text.export': 'エクスポート',\n  'common.notification.detail': '詳細を見る',\n  'common.notification.solution': '解決策',\n  'common.button.copyError': 'エラーレポートをコピー',\n  'common.button.copyErrorTips': '（ここではAPI情報と詳細なパラメータがコピーされます。機密情報がある場合は、JSONを解析してから送信してください）',\n  'common.tips.formatError': 'フォーマット失敗、SQLが正しいかどうかを確認してください',\n  'common.text.executeSelectedSQL': '選択したSQLを実行',\n  'common.text.refreshPage': 'ページを更新',\n  'common.text.saveConsole': 'コンソールを保存',\n  'common.text.textToSQL': 'プレーンテキストをSQLに変換',\n  'common.text.editorRightClick': 'エディタの右クリック',\n  'common.form.error.required': '必須項目です！',\n  'common.form.error.email': '正しいメール形式ではありません',\n  'common.tips.delete.confirm': '削除してもよろしいですか？',\n  'common.tips.updateSuccess': '更新成功',\n  'common.tips.createSuccess': '作成成功',\n  'common.text.action': '操作',\n  'common.button.add': '追加',\n  'common.text.errorMessage': 'エラーメッセージ',\n  'common.button.cancelRequest': 'リクエストをキャンセル',\n  'common.button.executionError': '実行エラー',\n  'common.text.affectedRows': '影響を受けた行：{1}',\n  'common.text.selectFile' : 'ファイルを選択',\n  'common.text.noTableFoundUp' : '現在のデータベースにテーブルが見つかりません',\n  'common.text.noTableFoundDown' : 'データベースを切り替えることができます',\n  'common.text.updateNow' : 'すぐに更新',\n  'common.title.preview' : 'プレビュー',\n  'common.title.errorMessage': 'エラーメッセージ',\n  'common.label.comment': 'コメント',\n  'common.label.name': '名前',\n  'common.title.create': '作成',\n  'common.title.executiveLogging': '実行ログ',\n  'common.text.executionTime': '{1}ms 完了',\n  'common.button.copyRowAs': '行をコピー',\n  'common.button.insertSql': 'Insert文',\n  'common.button.updateSql': 'Update文',\n  'common.button.tabularSeparatedValues': 'タブ区切り値',\n  'common.button.tabularSeparatedValuesFieldName': 'タブ区切り値(フィールド名)',\n  'common.button.tabularSeparatedValuesFieldNameAndData': 'タブ区切り値(フィールド名とデータ)',\n  'common.button.cloneRow': '行を複製',\n  'common.button.deleteRow': '行を削除',\n  'common.button.setNull': 'NULLに設定',\n  'common.button.setDefault': 'デフォルトに設定',\n  'common.button.viewData': 'データを見る/修正する',\n  'common.button.close': '閉じる',\n  'common.button.closeAll': 'すべて閉じる',\n  'common.button.closeOthers': '他を閉じる',\n  'common.label.tcp': 'オンライン',\n  'common.label.LocalFile': 'ローカルファイル',\n  'common.text.rename': '名前を変更',\n  'common.title.info': '情報',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/connection.ts",
    "content": "export default {\n  'connection.title': 'データソース',\n  'connection.title.connections': '接続',\n  'connection.title.createConnection': 'データソースを作成',\n  'connection.title.editConnection': 'データソースを編集',\n  'connection.title.importConnection': 'データソースをインポート',\n  'connection.label.name': '名前',\n  'connection.label.host': 'ホスト',\n  'connection.label.authentication': '認証',\n  'connection.label.database': 'データベース',\n  'connection.label.JDBCDrive': 'JDBCドライバ',\n  'connection.label.port': 'ポート',\n  'connection.button.testConnection': '接続をテスト',\n  'connection.label.advancedConfiguration': '高度な設定',\n  'connection.label.sshConfiguration': 'SSH',\n  'connection.button.addConnection': '接続を追加',\n  'connection.button.connect': '接続',\n  'connection.button.remove': '接続を削除',\n  'connection.message.testConnectResult': '接続テスト{1}',\n  'connection.message.testSshConnection': 'SSH接続をテスト',\n  'connection.tableHeader.name': '名前',\n  'connection.tableHeader.value': '値',\n  'connection.title.uploadDriver': 'アップロード',\n  'connection.tips.customUpload': 'ドライバをアップロード',\n  'connection.title.driver': 'ドライバ',\n  'connection.button.clickUpload': 'クリックしてアップロード',\n  'connection.text.downloadDriver': 'ドライバをダウンロード',\n  'connection.text.downloadSuccess': 'ダウンロード成功',\n  'connection.text.tryAgainDownload': '再ダウンロードを試みる',\n  'connection.text.downloading': 'ダウンロード中...',\n  'connection.label.private': 'プライベート',\n  'connection.label.shared': '共有',\n  'connection.button.createConnection': '接続を作成',\n  'connection.tips.noConnection': '現在、接続は作成されていません',\n  'connection.tips.noConnectionTips': 'この接続の詳細を表示する権限がありませんが、直接接続することはできます',\n  'connection.title.importTitle': 'ファイルをインポート, .ncx(navicat) または .dbp(dbever)',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/dashboard.ts",
    "content": "export default {\n  \"dashboard.title\": \"ダッシュボード\",\n  \"dashboard.edit\": \"編集\",\n  \"dashboard.modal.editTitle\": \"ダッシュボードの編集\",\n  \"dashboard.modal.addTitle\": \"ダッシュボードの追加\",\n  \"dashboard.modal.name.placeholder\": \"ダッシュボード名を入力してください\",\n  \"dashboard.delete\": \"削除\",\n  \"dashboard.export2image\": \"画像への出力\",\n  \"dashboard.editor.cascader.placeholder\": \"接続を選択してください\",\n  \"dashboard.editor.execute.noDataSource\": \"まず、データソースを選択してください\",\n  \"dashboard.editor.execute.success\": \"実行成功。チャートの設定を選択してください\"\n}\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/editSequence.ts",
    "content": "export default {\n    'editSequence.button.createSequence': '新規シーケンス',\n    'editSequence.button.editSequence': 'シーケンスの編集',\n    'editSequence.label.comment': 'コメント',\n    'editSequence.label.relname': 'シーケンス名',\n    'editSequence.label.typname': 'データ型',\n    'editSequence.label.seqcache': 'キャッシュ',\n    'editSequence.label.rolname': '所有者',\n    'editSequence.label.seqstart': '開始値',\n    'editSequence.label.seqincrement': '増分値',\n    'editSequence.label.seqmax': '最大値',\n    'editSequence.label.seqmin': '最小値',\n    'editSequence.label.seqcycle': '周期',\n    'editSequence.title.sqlPreview': 'SQLプレビュー',\n};"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/editTable.ts",
    "content": "export default {\n  'editTable.tab.basicInfo': '基本情報',\n  'editTable.tab.columnInfo': '列情報',\n  'editTable.tab.indexInfo': 'インデックス情報',\n  'editTable.label.tableName': 'テーブル名',\n  'editTable.label.comment': 'コメント',\n  'editTable.button.add': '追加',\n  'editTable.button.delete': '削除',\n  'editTable.button.up': '上に移動',\n  'editTable.button.down': '下に移動',\n  'editTable.label.indexName': 'インデックス名',\n  'editTable.label.indexType': 'インデックスタイプ',\n  'editTable.label.indexMethod': 'インデックス方法',\n  'editTable.label.includeColumn': '含まれる列',\n  'editTable.button.createTable': 'テーブルの作成',\n  'editTable.button.importTable': 'テーブルのインポート',\n  'editTable.label.index': '番号',\n  'editTable.label.columnName': '列名',\n  'editTable.label.columnSize': 'サイズ',\n  'editTable.label.columnType': 'タイプ',\n  'editTable.label.nullable': 'NULL可能',\n  'editTable.label.prefixLength': '接頭辞の長さ',\n  'editTable.label.defaultValue': 'デフォルト値',\n  'editTable.label.sparse': 'スパース',\n  'editTable.label.characterSet': '文字セット',\n  'editTable.label.collation': '照合順序',\n  'editTable.label.decimalPoint': '小数点',\n  'editTable.label.unit': '単位',\n  'editTable.label.value': '値',\n  'editTable.label.autoIncrement': '自動増分',\n  'editTable.label.engine': 'エンジン',\n  'editTable.label.incrementValue': '増分値',\n  'editTable.label.order': '順序',\n  'editTable.label.primaryKey': '主キー',\n  'editTable.title.sqlPreview': 'SQLプレビュー',\n  'editTable.button.addColumn': '列を追加',\n  'editTable.button.addIndex': 'インデックスを追加',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/editTableData.ts",
    "content": "export default {\n  'editTableData.tips.addRow': '行を追加',\n  'editTableData.tips.deleteRow': '行を削除',\n  'editTableData.tips.revert': '取り消す',\n  'editTableData.tips.previewPendingChanges':  '保留中の変更をプレビュー',\n  'editTableData.tips.submit': '変更を提出',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/index.ts",
    "content": "import { LangType } from '@/constants'\nimport menu from './menu';\nimport common from './common';\nimport connection from './connection';\nimport setting from './setting';\nimport workspace from './workspace';\nimport dashboard from './dashboard';\nimport chat from './chat';\nimport team from './team'\nimport login from './login';\nimport editTable from './editTable';\nimport editTableData from './editTableData';\nimport sqlEditor from './sqlEditor'\nimport editSequence from './editSequence';\n\nexport default {\n  lang: LangType.ZH_CN,\n  ...connection,\n  ...common,\n  ...setting,\n  ...workspace,\n  ...menu,\n  ...connection,\n  ...dashboard,\n  ...chat,\n  ...team,\n  ...login,\n  ...editTable,\n  ...editTableData,\n  ...sqlEditor,\n  ...editSequence\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/login.ts",
    "content": "export default {\n  'login.text.logout': 'ログアウト',\n  'login.text.welcome': 'Chat2DBをご利用いただき、ありがとうございます',\n  'login.text.tips': 'Chat2DBのアカウントはチームの協力と管理のためにのみ使用されます',\n  'login.text.tips.title': 'なぜログインが必要ですか？',\n  'login.text.setting': '設定',\n  'login.form.user': 'ユーザー名',\n  'login.form.user.placeholder': 'ユーザー名を入力してください',\n  'login.form.password': 'パスワード',\n  'login.form.password.placeholder': 'パスワードを入力してください',\n  'login.button.login': 'ログイン',\n  'login.tips.defaultPassword': 'デフォルトのユーザー名とパスワードは両方とも chat2db です',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/menu.ts",
    "content": "export default {\n  'menu.file': 'File'\n}\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/setting.ts",
    "content": "export default {\n  'setting.title.setting': '設定',\n  'setting.nav.basic': '基本設定',\n  'setting.nav.customAi': 'カスタムAI',\n  'setting.nav.proxy': 'サーバーアドレス',\n  'setting.nav.aboutUs': '私たちについて',\n  'setting.title.backgroundColor': '背景色',\n  'setting.title.themeColor': 'テーマカラー',\n  'setting.title.sqlEditorFontSize': 'SQLエディタのフォントサイズ',\n  'setting.label.blue': 'ブルー',\n  'setting.label.green': 'グリーン',\n  'setting.label.violet': 'バイオレット',\n  'setting.text.dark': 'ダーク',\n  'setting.text.dark2': 'ダーク2',\n  'setting.text.light': 'ライト',\n  'setting.text.followOS': '自動',\n  'setting.title.language': '言語',\n  'setting.title.aiSource': 'AIのソース',\n  'setting.tab.custom': 'カスタム',\n  'setting.tab.aiType.zhipu': '智譜',\n  'setting.tab.aiType.baichuan': '百川',\n  'setting.tab.aiType.wenxin': '文心一言',\n  'setting.tab.aiType.tongyiqianwen': '通義千問',\n  'setting.tab.aiType.custom.tips': 'インターフェース形式はOpenAIのものと互換性があります',\n  'setting.label.serviceAddress': 'サービスアドレス',\n  'setting.button.apply': '適用',\n  'setting.text.currentEnv': '現在の環境',\n  'setting.text.currentVersion': '現在のバージョン',\n  'setting.text.viewingUpdateLogs': '更新ログを見る',\n  'setting.label.isStreamOutput': 'ストリーム出力を行うか',\n  'setting.label.customAiUrl': 'カスタムAIのURL',\n  'setting.placeholder.httpsProxy':\n    '任意項目、OpenAIインターフェースをリクエストする際のHTTPプロキシを設定するためのもの{1}',\n  'setting.placeholder.apiKey':\n    'OpenAIインターフェースを使用する場合は必須です。APIキーはOpenAI公式サイトで確認できます',\n  'setting.placeholder.chat2dbApiKey': 'Chat2DBが提供するAPIキーを使用します',\n  'setting.placeholder.customUrl':\n    'カスタムAIを選択する場合は必須で、カスタムAIのRESTインターフェースのURLを設定します',\n  'setting.placeholder.apiHost': '任意項目、デフォルト値はhttps://api.openai.com/',\n  'setting.message.urlTestError': 'インターフェースのテストに合格しません',\n  'setting.placeholder.azureOpenAIKey': 'AzureポータルからAzure OpenAIキーを取得します',\n  'setting.placeholder.azureEndpoint': 'AzureポータルからAzure OpenAIエンドポイントを取得します',\n  'setting.placeholder.azureDeployment': 'モデルのデプロイIDを指定します',\n  'setting.ai.tips': 'AI設定を選択するにはログインしてください',\n  'setting.ai.user.hidden': '管理者に連絡して、「設定->カスタムAI」でAPIキーを設定してください',\n  'setting.button.startDownloading': 'ダウンロード開始',\n  'setting.button.beDownloading': 'ダウンロード中',\n  'setting.button.redownload': '再ダウンロード',\n  'setting.button.restart': 'すぐに再起動',\n  'setting.text.discoverNewVersion': '新しいバージョン {1} が見つかりました',\n  'setting.text.isLatestVersion': '最新バージョンです',\n  'setting.button.changeLog': '更新ログを見る',\n  'setting.title.updateRule': '更新ルール',\n  'setting.text.autoUpdate': '新しいバージョンを自動でダウンロードしてインストールする',\n  'setting.text.manualUpdate': '新しいバージョンがリリースされたときにのみ通知する',\n  'setting.button.iSee': '了解しました',\n  'setting.text.newEditionIsReady':\n    '新しいバージョンがダウンロードされました。ソフトウェアを再起動すると新しいバージョンがインストールされます',\n  'setting.button.goToUpdate': '更新に進む',\n  'setting.text.UpdatedLatestVersion': '最新バージョン {1} に更新しました',\n  'setting.title.holdingService': 'サービスを維持',\n  'setting.text.holdingService': 'アプリケーションを終了してもサービスを維持し、起動速度を向上させます',\n  'setting.chat2db.ai.button': 'より強力なAI機能を体験するために、Chat2DB Proをご利用ください',\n  'setting.title.goto.chat2db.pro': 'Chat2DB Proへ行く',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/sqlEditor.ts",
    "content": "export default {\n  'sqlEditor.text.keyword': 'キーワード',\n  'sqlEditor.text.function': '関数',\n  'sqlEditor.text.tableName': 'テーブル名',\n  'sqlEditor.text.databaseName': 'データベース名',\n  'sqlEditor.text.schemaName': 'スキーマ名',\n  'sqlEditor.text.viewName': 'ビュー名',\n  'sqlEditor.text.fieldName': 'フィールド名',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/team.ts",
    "content": "export default {\n  'team.title': 'チーム管理',\n  'team.tab.datasource': 'リンク管理',\n  'team.tab.user': 'ユーザー管理',\n  'team.tab.team': 'チーム管理',\n\n  'team.action.rightManagement': '権限管理',\n  'team.action.editDatasource': 'リンクの編集',\n  'team.action.addDatasource': 'リンクの追加',\n  'team.action.addDatasource.placeholder': 'リンクを検索',\n  'team.action.editUser': 'ユーザーの編集',\n  'team.action.addUser': 'ユーザーの追加',\n  'team.action.addUser.placeholder': 'ユーザーを検索',\n  'team.action.editTeam': 'チームの編集',\n  'team.action.addTeam': 'チームの追加',\n  'team.action.addTeam.placeholder': 'チームを検索',\n  'team.action.affiliation.user': '含まれるユーザー',\n  'team.action.affiliation.team': '所属チーム',\n  'team.action.affiliation.datasource': '所属リンク',\n  'team.action.addUserAndTeam': 'ユーザー/チームの追加',\n  'team.action.addUserAndTeam.placeholder': '人員/チームを検索',\n  'team.input.search.placeholder': 'キーワードを入力して検索',\n\n  'team.datasource.rightManagement': '権限管理',\n  'team.datasource.alias': 'リンク名',\n  'team.datasource.url': 'リンクアドレス',\n  'team.datasource.code': 'コード',\n  'team.datasource.name': '名前',\n  'team.datasource.status': 'ステータス',\n\n  'team.user.name': 'ユーザー',\n  'team.user.userName': 'ユーザー名',\n  'team.user.nickName': 'ニックネーム',\n  'team.user.status': 'ステータス',\n\n  'team.user.addForm.userName': 'ユーザー名',\n  'team.user.addForm.nickName': 'ニックネーム',\n  'team.user.addForm.email': 'メールアドレス',\n  'team.user.addForm.password': 'パスワード',\n  'team.user.addForm.roleCode': '権限',\n  'team.user.addForm.roleCode.admin': '管理者',\n  'team.user.addForm.roleCode.user': 'ユーザー',\n  'team.user.addForm.status': 'ステータス',\n  'team.user.addForm.status.valid': '有効',\n  'team.user.addForm.status.invalid': '無効',\n\n  'team.team.name': 'チーム',\n  'team.team.addForm.code': 'コード',\n  'team.team.addForm.name': '名前',\n  'team.team.addForm.status': 'ステータス',\n  'team.team.addForm.status.valid': '有効',\n  'team.team.addForm.status.invalid': '無効',\n  'team.team.addForm.description': '説明',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/ja-jp/workspace.ts",
    "content": "export default {\n  'workspace.title': 'ワークスペース',\n  'workspace.cascader.placeholder': '選択してください',\n  'workspace.ai.input.placeholder': 'ここにプレーンテキストを入力してください',\n  'workspace.title.savedConsole': '保存されたコンソール',\n  'workspace.menu.ViewDDL': 'DDLを見る',\n  'workspace.menu.deleteTable': 'テーブルを削除',\n  'workspace.menu.openTable': 'テーブルを開く',\n  'workspace.menu.editTable': 'テーブルを編集',\n  'workspace.menu.view': '見る',\n  'workspace.menu.pin': 'ピン',\n  'workspace.menu.unPin': 'ピンを外す',\n  'workspace.menu.editTableData': 'テーブルデータを編集',\n  'workspace.menu.queryConsole': '新しいクエリ',\n  'workspace.menu.viewAllTable': 'すべてのテーブルを見る',\n  'workspace.menu.createDatabase': 'データベースを作成',\n  'workspace.menu.createSchema': 'スキーマを作成',\n  'workspace.menu.deleteTablePlaceHolder': '削除するテーブル名を入力してください',\n  'workspace.menu.createSequence': 'シーケンスを作成',\n  'workspace.menu.editSequence': 'シーケンスを編集',\n  'workspace.menu.deleteSequence': 'シーケンスを削除',\n  'workspace.tips.affirmDeleteTable': '入力したテーブル名と削除するテーブル名が一致しません。再確認してください',\n  'workspace.table.total': '合計',\n  'workspace.table.total.tip': '合計行数をロード',\n  'workspace.table.export.all.csv': '結果セットをcsvでエクスポート',\n  'workspace.table.export.cur.csv': '現在のページの結果セットをcsvでエクスポート',\n  'workspace.table.export.all.insert': '結果セットをinsert sqlでエクスポート',\n  'workspace.table.export.cur.insert': '現在のページの結果セットをinsert sqlでエクスポート',\n  'workspace.tree.view': 'ビュー',\n  'workspace.tree.trigger': 'トリガー',\n  'workspace.tree.function': '関数',\n  'workspace.tree.procedure': 'ストアドプロシージャ',\n  'workspace.tree.search.placeholder': '展開されたノードで検索',\n  'workspace.tree.delete.tip': 'この操作は永久的に削除されることを理解しています',\n  'workspace.tree.delete.table.tip': 'テーブル{1}を削除してもよろしいですか？',\n  'workspace.tips.noConnection': 'まだ接続が作成されていません',\n  'workspace.tips.maxConsole': 'コンソールは最大20個まで開くことができます',\n  'workspace.tips.openExecutiveLogging': '実行ログを開く',\n  'workspace.tree.delete.sequence.tip': 'シーケンス{1}を削除してもよろしいですか？',\n  \n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/chat.ts",
    "content": "export default {\n  'chat.input.remain': 'Kalan {1}',\n  'chat.input.tableSelect.placeholder': 'Lütfen tabloları seçin',\n  'chat.input.tableSelect.error.TooManyTable': 'En fazla 8 tablo seçebilirsiniz',\n  'chat.input.remain.dialog.tips':\n    'Resmi WeChat hesabımıza abone olun, daha fazla deneyim şansı için 推广 gönderin.',\n  'chat.input.syncTable.tips': 'Otomatik olarak tüm tablo yapılarını AI bağlamına senkronize eder',\n  'chat.input.remain.tooltip': 'Manuel olarak seçilen tablo, AI bağlamına senkronize edilecektir',\n  'chat.input.syncTable.tempTips': '🎉Güncelleme: Otomatik olarak tüm tablo yapılarını AI bağlamına senkronize etme',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/common.ts",
    "content": "export default {\n  'common.text.no': 'hayır',\n  'common.text.is': 'dir',\n  'common.button.affirm': 'Onayla',\n  'common.button.edit': 'Düzenle',\n  'common.button.modify': 'Değiştir',\n  'common.button.confirm': 'Onayla',\n  'common.button.cancel': 'İptal',\n  'common.data.hour': '{1} {saat|saat}',\n  'common.data.minute': '{1} {dakika|dakika}',\n  'common.tip.yesterday': '{1} dün',\n  'common.tip.tomorrow': '{1} yarın',\n  'common.tip.ago': ' önce',\n  'common.tip.later': ' sonra',\n  'common.tip.now': 'Şimdi',\n  'common.tip.justNow': 'Şu an',\n  'common.text.search': 'arama',\n  'common.placeholder.select': 'Lütfen Seçin {1}',\n  'common.text.serviceStarting': 'Hizmet Başlatılıyor ...',\n  'common.text.serviceFail': 'Hizmet başlatma başarısız oldu. Lütfen sayfayı yeniden yenilemeyi deneyin...',\n  'common.text.column': 'kolon',\n  'common.text.row': 'satır',\n  'common.text.indexes': 'indeksler',\n  'common.button.save': 'Kaydet',\n  'common.button.open': 'Aç',\n  'common.button.refresh': 'Yenile',\n  'common.button.execute': 'Çalıştır',\n  \"common.button.import\": 'SQL İçe Aktar',\n  'common.button.format': 'Biçimlendir',\n  'common.message.successfulConfig': 'Başarılı yapılandırma',\n  'common.text.successful': 'başarılı',\n  'common.text.failure': 'başarısızlık',\n  'common.message.modifySuccessfully': 'başarıyla değiştirildi',\n  'common.message.addedSuccessfully': 'başarıyla eklendi',\n  'common.text.custom': 'özel',\n  'common.button.delete': 'Sil',\n  'common.text.executionResult': 'Sonuç {1}',\n  'common.tips.deleteTable': 'Bu Tabloyu Silmek İstediğinizden Emin Misiniz?',\n  'common.text.tableName': 'Tablo Adı',\n  'common.text.submittedSuccessfully': 'Başarıyla Gönderildi',\n  'common.text.successfullyDelete': 'Başarıyla Silindi',\n  'common.text.explainSQL': 'SQL Açıkla',\n  'common.text.optimizeSQL': 'SQL Optimizasyonu',\n  'common.text.conversionSQL': 'SQL Dönüşümü',\n  'common.text.table': 'Tablo',\n  'common.tips.saveSuccessfully': 'Başarıyla Kaydedildi',\n  'common.button.copy': 'Kopyala',\n  'common.button.copyName': 'Adı Kopyala',\n  'common.button.copySuccessfully': 'Başarıyla Kopyalandı',\n  'common.button.createConsole': 'Konsol Oluştur',\n  'common.button.exportWord': 'Word\\'e Aktar',\n  'common.button.exportExcel': 'Excel\\'e Aktar',\n  'common.button.exportHtml': 'Html\\'e Aktar',\n  'common.button.exportMarkdown': 'Markdown\\'a Aktar',\n  'common.button.exportPdf': 'Pdf\\'e Aktar',\n  'common.text.successfulExecution': 'Başarılı İşlem',\n  'common.text.result': 'Sonuç',\n  'common.text.timeConsuming': 'Zaman Harcama',\n  'common.text.searchRow': 'Satır Ara',\n  'common.text.noData': 'Veri Yok',\n  'common.text.remindMeLater': 'Beni Sonra Hatırlat',\n  'common.text.goToUpdate': 'Güncellemeye Git',\n  'common.text.updateReminder': 'Güncelleme Hatırlatıcısı',\n  'common.text.detectionLatestVersion': 'Son sürüm izleniyor',\n  'common.text.setting': 'Ayar',\n  'common.text.tryToRestart': 'Yeniden Başlatmayı Deneyin',\n  'common.text.contactUs': 'Bize Ulaşın',\n  'common.text.wechatPopularizeAi': 'WeChat resmi hesabımızı takip edin, \"AI\" göndererek ücretsiz deneyimler alın.',\n  'common.text.wechatPopularizeAi2':\n    'WeChat resmi hesabımızı takip edin, \"AI\" göndererek ücretsiz ApiKey alın ve deneyim sayısını hediye edin.',\n  'common.text.wechatPopularize': 'Daha fazla ücretsiz deneyim almak için \"promotion\" gönderebilirsiniz.',\n  'common.text.export': 'Dışa Aktar',\n  'common.notification.detail': 'Daha fazla detay',\n  'common.notification.solution': 'Çözüm',\n  'common.button.copyError': 'Hata raporunu kopyala',\n  'common.button.copyErrorTips':\n    '(Arayüz bilgileri ve ayrıntılı parametreler buraya kopyalanacaktır. Hassas parametreler varsa önce JSON ayrıştırın ve sonra gönderin)',\n  'common.tips.formatError': 'Biçimlendirme başarısız, sql\\'in doğru olup olmadığını kontrol edin',\n  'common.text.executeSelectedSQL': 'Seçilen SQL\\'i Çalıştır',\n  'common.text.refreshPage': 'Sayfayı Yenile',\n  'common.text.saveConsole': 'Konsolu Kaydet',\n  'common.text.textToSQL': 'Düz metinleri SQL\\'e',\n  'common.text.editorRightClick': 'Düzenleyici sağ tıklama',\n  'common.form.error.required': 'Bu alan zorunludur!',\n  'common.form.error.email': 'Girdi geçerli bir e-posta değil!',\n  'common.tips.delete.confirm': 'Emin misiniz silmek için?',\n  'common.tips.updateSuccess': 'Başarıyla Güncellendi',\n  'common.tips.createSuccess': 'Başarıyla Oluşturuldu',\n  'common.text.action': 'Eylem',\n  'common.button.add': 'Ekle',\n  'common.text.errorMessage': 'Hata Mesajı',\n  'common.button.cancelRequest': 'İsteği İptal Et',\n  'common.button.executionError': 'Çalıştırma Hatası',\n  'common.text.affectedRows': 'Etkilenen satırlar: {1}',\n  'common.text.selectFile' : 'Dosya Seç',\n  'common.text.noTableFoundUp' : 'Bu veritabanında tablo bulunmamaktadır',\n  'common.text.noTableFoundDown': 'Üstte veritabanını değiştirin',\n  'common.title.preview': 'Önizleme',\n  'common.title.errorMessage': 'Hata mesajı',\n  'common.label.comment': 'Yorum',\n  'common.label.name': 'Ad',\n  'common.title.create': 'Oluştur',\n  'common.title.executiveLogging': 'Yürütme Günlüğü',\n  'common.text.executionTime': '{1} ms içinde etkilendi',\n  'common.button.copyRowAs': 'Satırı Kopyala',\n  'common.button.insertSql': 'SQL Ekle',\n  'common.button.updateSql': 'SQL Güncelle',\n  'common.button.tabularSeparatedValues': 'TAB sınırlı (veri)',\n  'common.button.tabularSeparatedValuesFieldName': 'TAB sınırlı (alan adı)',\n  'common.button.tabularSeparatedValuesFieldNameAndData': 'TAB ayrılmış (alan adları ve veri)',\n  'common.button.cloneRow': 'Satırı Kopyala',\n  'common.button.deleteRow': 'Satırı Sil',\n  'common.button.setNull': 'NULL Ayarla',\n  'common.button.setDefault': 'DEFAULT Ayarla',\n  'common.button.viewData': 'Veriyi Görüntüle/Düzenle',\n  'common.button.close': 'Kapat',\n  'common.button.closeAll': 'Tümünü Kapat',\n  'common.button.closeOthers': 'Diğerlerini Kapat',\n  'common.label.tcp': 'TCP',\n  'common.label.LocalFile': 'Yerel Dosya',\n  'common.text.rename': 'Yeniden Adlandır',\n  'common.title.info': 'Bilgi',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/connection.ts",
    "content": "export default {\n  'connection.title': 'Bağlantılar',\n  'connection.title.connections': 'Bağlantılar',\n  'connection.title.createConnection': 'Yeni Bağlantı',\n  'connection.title.editConnection': 'Bağlantıyı Düzenle',\n  'connection.title.importConnection': 'Bağlantı İçe Aktar',\n  'connection.label.name': 'ad',\n  'connection.label.host': 'ana bilgisayar',\n  'connection.label.authentication': 'doğrulama',\n  'connection.label.database': 'veritabanı',\n  'connection.label.JDBCDrive': 'JDBC Sürücüsü',\n  'connection.label.port': 'port',\n  'connection.button.testConnection': 'Test Et',\n  'connection.label.advancedConfiguration': 'Gelişmiş Yapılandırma',\n  'connection.label.sshConfiguration': 'SSH Yapılandırma',\n  'connection.button.addConnection': 'Bağlantı Ekle',\n  'connection.button.connect': 'Bağlan',\n  'connection.button.remove': 'Kaldır',\n  'connection.message.testConnectResult': 'Test bağlantısı {1}',\n  'connection.message.testSshConnection': 'SSH bağlantısını test et',\n  'connection.tableHeader.name': 'Adı',\n  'connection.tableHeader.value': 'Değer',\n  'connection.title.uploadDriver': 'Sürücü Yükle',\n  'connection.tips.customUpload': \"Sürücüyü yükle\",\n  'connection.title.driver': 'Sürücü',\n  'connection.button.clickUpload': 'Yükleme için Tıklayın',\n  'connection.text.downloadDriver': 'Sürücü İndir',\n  'connection.text.downloadSuccess': 'İndirme Başarılı',\n  'connection.text.tryAgainDownload': 'Yeniden İndir',\n  'connection.text.downloading': 'İndiriliyor...',\n  'connection.label.private': 'Özel',\n  'connection.label.shared': 'Paylaşılan',\n  'connection.button.createConnection': 'Bağlantı Oluştur',\n  'connection.tips.noConnection': 'Henüz hiç bağlantı oluşturmadınız',\n  'connection.tips.noConnectionTips': 'Bağlantı ayrıntılarını görüntüleme izniniz yok, ancak bağlantıya doğrudan bağlanabilirsiniz',\n  'connection.title.importTitle': 'Dosya İçe Aktar,.ncx(navicat) veya.dbp(dbever)',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/dashboard.ts",
    "content": "export default {\n  'dashboard.title': 'Kontrol Paneli',\n  'dashboard.edit': 'Düzenle',\n  'dashboard.modal.editTitle': 'Kontrol Panelini Düzenle',\n  'dashboard.modal.addTitle': 'Kontrol Paneli Ekle',\n  'dashboard.modal.name.placeholder': \"Lütfen kontrol paneli adını girin.\",\n  'dashboard.export2image': 'Resme Aktar',\n  'dashboard.delete': 'Sil',\n  'dashboard.editor.cascader.placeholder': 'Lütfen bir bağlantı havuzu seçin',\n  'dashboard.editor.execute.noDataSource': 'Lütfen önce bir veri kaynağı seçin',\n  'dashboard.editor.execute.success': 'Başarılı, Lütfen Grafik Yapılandırmasını Seçin',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/editSequence.ts",
    "content": "export default {\n    'editSequence.button.createSequence': 'Sıradan oluştur',\n    'editSequence.button.editSequence': 'Sıralamayı Düzenle',\n    'editSequence.label.comment': 'Yorum',\n    'editSequence.label.relname': 'Sıra Adı',\n    'editSequence.label.typname': 'Veri Türü',\n    'editSequence.label.seqcache': 'Önbelleğe Alma',\n    'editSequence.label.rolname': 'Sahibi',\n    'editSequence.label.seqstart': 'Başlangıç Değeri',\n    'editSequence.label.seqincrement': 'Artış Değeri',\n    'editSequence.label.seqmax': 'En Büyük Değer',\n    'editSequence.label.seqmin': 'En Küçük Değer',\n    'editSequence.label.seqcycle': 'bisiklet',\n    'editSequence.title.sqlPreview': 'SQL Önizleme',\n};"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/editTable.ts",
    "content": "export default {\n  'editTable.tab.basicInfo': 'Temel Bilgi',\n  'editTable.tab.columnInfo': 'Kolon',\n  'editTable.tab.indexInfo': 'İndeks',\n  'editTable.label.tableName': 'Tablo adı',\n  'editTable.label.comment': 'Yorum',\n  'editTable.button.add': 'Ekle',\n  'editTable.button.delete': 'Sil',\n  'editTable.button.up': 'Yukarı',\n  'editTable.button.down': 'Aşağı',\n  'editTable.label.indexName': 'Adı',\n  'editTable.label.indexType': 'Türü',\n  'editTable.label.indexMethod': 'İndeks yöntemi',\n  'editTable.label.includeColumn': 'Kolonu içerir',\n  'editTable.button.createTable': 'Tablo Oluştur',\n  'editTable.button.importTable': 'Tabloyu İçe Aktar',\n  'editTable.label.index': 'İndeks',\n  'editTable.label.columnName': 'Adı',\n  'editTable.label.columnSize': 'Boyutu',\n  'editTable.label.columnType': 'Türü',\n  'editTable.label.nullable': 'Boş bırakılabilir',\n  'editTable.label.prefixLength': 'Önek uzunluğu',\n  'editTable.label.defaultValue': 'Varsayılan değer',\n  'editTable.label.sparse': 'Düzensiz',\n  'editTable.label.characterSet': 'Karakter kümesi',\n  'editTable.label.collation': 'Düzenleme',\n  'editTable.label.decimalPoint': 'Ondalık nokta',\n  'editTable.label.unit': 'Birim',\n  'editTable.label.value': 'Değer',\n  'editTable.label.autoIncrement': 'Otomatik artır',\n  'editTable.label.engine': 'Motor',\n  'editTable.label.incrementValue': 'Artış değeri',\n  'editTable.label.order': 'Sıra',\n  'editTable.label.primaryKey': 'Anahtar',\n  'editTable.title.sqlPreview': 'SQL önizleme',\n  'editTable.button.addColumn': 'Kolon Ekle',\n  'editTable.button.addIndex': 'İndeks Ekle',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/editTableData.ts",
    "content": "export default {\n  'editTableData.tips.addRow': 'Satır Ekle',\n  'editTableData.tips.deleteRow': 'Satırı Sil',\n  'editTableData.tips.revert': 'Geri Al',\n  'editTableData.tips.previewPendingChanges': 'Bekleyen Değişiklikleri Önizle',\n  'editTableData.tips.submit': 'Gönder',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/index.ts",
    "content": "import common from './common';\nimport connection from './connection';\nimport menu from './menu';\nimport setting from './setting';\nimport workspace from './workspace';\nimport dashboard from './dashboard';\nimport chat from './chat';\nimport team from './team'\nimport login from './login';\nimport editTable from './editTable';\nimport editTableData from './editTableData';\nimport sqlEditor from './sqlEditor'\nimport editSequence from './editSequence';\n\nexport default {\n  lang: 'tr',\n  ...common,\n  ...setting,\n  ...connection,\n  ...workspace,\n  ...menu,\n  ...dashboard,\n  ...chat,\n  ...team,\n  ...login,\n  ...editTable,\n  ...editTableData,\n  ...sqlEditor,\n  ...editSequence\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/login.ts",
    "content": "export default {\n  'login.text.logout': 'Çıkış Yap',\n  'login.text.welcome': 'Chat2DB\\'ye Hoş Geldiniz',\n  'login.text.tips': 'Chat2DB hesabı yalnızca takım işbirliği yönetimi içindir.',\n  'login.text.tips.title': 'Neden giriş yapmalı?',\n  'login.text.setting': 'Ayarlar',\n  'login.form.user': 'Kullanıcı Adı',\n  'login.form.user.placeholder': 'Lütfen kullanıcı adınızı girin',\n  'login.form.password': 'Şifre',\n  'login.form.password.placeholder': 'Lütfen şifrenizi girin',\n  'login.button.login': 'Giriş Yap',\n  'login.tips.defaultPassword': 'Varsayılan kullanıcı adı ve şifre: chat2db',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/menu.ts",
    "content": "export default {\n  'menu.file' : 'Dosya'\n}\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/setting.ts",
    "content": "export default {\n  'setting.title.setting': 'Ayarlar',\n  'setting.nav.basic': 'Temel',\n  'setting.nav.customAi': 'Özel AI',\n  'setting.nav.proxy': 'Hizmet Yolu',\n  'setting.nav.aboutUs': 'Hakkımızda',\n  'setting.title.backgroundColor': 'Arka Plan Rengi',\n  'setting.title.themeColor': 'Tema Rengi',\n  'setting.title.sqlEditorFontSize': 'SQL Düzenleyici Yazı Tipi Boyutu',\n  'setting.label.blue': 'Mavi',\n  'setting.label.green': 'Yeşil',\n  'setting.label.violet': 'Menekşe',\n  'setting.text.dark': 'Karanlık',\n  'setting.text.dark2': 'Karanlık-2',\n  'setting.text.light': 'Açık',\n  'setting.text.followOS': 'İşletim Sistemini Takip Et',\n  'setting.title.language': 'Dil',\n  'setting.title.aiSource': 'AI Kaynağı',\n  'setting.tab.custom': 'Özel',\n  'setting.tab.aiType.zhipu': 'ZhiPu AI',\n  'setting.tab.aiType.baichuan': 'BaiChuan AI',\n  'setting.tab.aiType.wenxin': 'WenXin AI',\n  'setting.tab.aiType.tongyiqianwen': 'TongYiQianWen AI',\n  'setting.tab.aiType.custom.tips': 'API formatı OpenAI API formatıyla uyumludur',\n  'setting.label.serviceAddress': 'Hizmet Adresi',\n  'setting.button.apply': 'Uygula',\n  'setting.text.currentEnv': 'Geçerli Ortam',\n  'setting.text.currentVersion': 'Geçerli Sürüm',\n  'setting.text.viewingUpdateLogs': 'Güncelleme Günlüklerini Görüntüleme',\n  'setting.label.isStreamOutput': 'Arayüzün çıktıyı akıtıp akıtmadığı',\n  'setting.label.customAiUrl': \"Kullanıcı tanımlı arayüz URL'si\",\n  'setting.placeholder.httpsProxy': 'Gerekli değil. OPENAI arayüzünü istemek için HTTP proxy {1} ayarlayın.',\n  'setting.placeholder.apiKey': \"APIKEY'i görüntülemek için OpenAI resmi web sitesine gidin\",\n  'setting.placeholder.chat2dbApiKey': \"Chat2DB tarafından sağlanan APIKEY'i kullanın\",\n  'setting.placeholder.customUrl': \"AI REST arayüzünün URL'si\",\n  'setting.placeholder.apiHost': 'Bu parametre zorunludur. Varsayılan değer https://api.openai.com/',\n  'setting.message.urlTestError': 'Arayüz testi başarısız oldu. İşlem',\n  'setting.placeholder.azureOpenAIKey': \"Azure Portal'dan Azure OpenAI anahtar kimlik bilgilerini alın\",\n  'setting.placeholder.azureEndpoint': \"Azure Portal'dan Azure OpenAI uç noktasını alın\",\n  'setting.placeholder.azureDeployment': 'Dağıtılan modelin dağıtım kimliği',\n  'setting.ai.tips': 'Lütfen oturum açın ve AI yapılandırmasını seçin',\n  'setting.ai.user.hidden': '\"Ayarlar -> Özel AI\" içinde ApiKey\\'i ayarlamak için lütfen yönetici ile iletişime geçin',\n  'setting.button.startDownloading': 'İndirmeye Başla',\n  'setting.button.beDownloading': 'İndiriliyor',\n  'setting.button.redownload': 'Yeniden İndir',\n  'setting.button.restart': 'Yeniden Başlat',\n  'setting.text.discoverNewVersion': 'Yeni sürümü keşfedin {1}',\n  'setting.text.isLatestVersion': 'Bu en son sürüm',\n  'setting.button.changeLog': 'Değişiklik Günlüğü',\n  'setting.title.updateRule': 'Güncelleme Kuralı',\n  'setting.text.autoUpdate': 'Yeni sürüm otomatik olarak indirilir ve yükler',\n  'setting.text.manualUpdate': 'Yalnızca yeni bir sürüm yayınlandığında beni uyar',\n  'setting.button.iSee': 'Görüyorum',\n  'setting.text.newEditionIsReady':\n    'Yeni sürüm indirme tamamlandı, yazılımı yeniden başlatarak yeni sürümü kurabilirsiniz',\n  'setting.button.goToUpdate': 'Güncellemeye Git',\n  'setting.text.UpdatedLatestVersion': 'En son sürüme güncellendi {1}',\n  'setting.title.holdingService': 'Hizmeti Tutma',\n  'setting.text.holdingService': 'Uygulamadan çıkarken hizmeti tutarak başlangıcı hızlandırın',\n  'setting.chat2db.ai.button': `Daha güçlü AI özellikleri için lütfen Chat2DB Pro'yu ziyaret edin`,\n  'setting.title.goto.chat2db.pro': `Chat2DB Pro'ya Git`,\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/sqlEditor.ts",
    "content": "export default {\n  'sqlEditor.text.keyword': 'Anahtar Kelime',\n  'sqlEditor.text.function': 'Fonksiyon',\n  'sqlEditor.text.tableName': 'Tablo Adı',\n  'sqlEditor.text.databaseName': 'Veritabanı Adı',\n  'sqlEditor.text.schemaName': 'Şema',\n  'sqlEditor.text.viewName': 'Görünüm Adı',\n  'sqlEditor.text.fieldName': 'Alan Adı',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/team.ts",
    "content": "export default {\n    'team.title': 'Takım Yönetimi',\n    'team.tab.datasource': 'Veri Kaynağı Yönetimi',\n    'team.tab.user': 'Kullanıcı Yönetimi',\n    'team.tab.team': 'Takım Yönetimi',\n  \n    'team.action.rightManagement': 'Yetki Yönetimi',\n    'team.action.editDatasource': 'Veri Kaynağını Düzenle',\n    'team.action.addDatasource': 'Veri Kaynağı Ekle',\n    'team.action.addDatasource.placeholder': 'Veri Kaynağı Ara',\n    'team.action.editUser': 'Kullanıcıyı Düzenle',\n    'team.action.addUser': 'Kullanıcı Ekle',\n    'team.action.addUser.placeholder': 'Kullanıcı Ara',\n    'team.action.editTeam': 'Takımı Düzenle',\n    'team.action.addTeam': 'Takım Ekle',\n    'team.action.addTeam.placeholder': 'Takım Ara',\n    'team.action.affiliation.user': 'Kullanıcıya Bağla',\n    'team.action.affiliation.team': 'Takıma Bağla',\n    'team.action.affiliation.datasource': 'Veri Kaynağına Bağla',\n    'team.action.addUserAndTeam': 'Kullanıcı/Takım Ekle',\n    'team.action.addUserAndTeam.placeholder': 'Kullanıcı/Takım Ara',\n    'team.input.search.placeholder': 'Arama yapmak için anahtar kelimeleri girin',\n  \n    'team.datasource.rightManagement': 'Yetki Yönetimi',\n    'team.datasource.alias': 'Veri Kaynağı Adı',\n    'team.datasource.url': 'Veri Kaynağı URL\\'si',\n    'team.datasource.code': 'Kod',\n    'team.datasource.name': 'Ad',\n    'team.datasource.status': 'Durum',\n  \n    'team.user.name': 'Kullanıcı',\n    'team.user.userName': 'Kullanıcı Adı',\n    'team.user.nickName': 'Takma Ad',\n    'team.user.status': 'Durum',\n    'team.user.addForm.userName': 'Kullanıcı Adı',\n    'team.user.addForm.nickName': 'Takma Ad',\n    'team.user.addForm.email': 'E-posta',\n    'team.user.addForm.password': 'Şifre',\n    'team.user.addForm.roleCode': 'Rol',\n    'team.user.addForm.roleCode.admin': 'Yönetici',\n    'team.user.addForm.roleCode.user': 'Kullanıcı',\n    'team.user.addForm.status': 'Durum',\n    'team.user.addForm.status.valid': 'Geçerli',\n    'team.user.addForm.status.invalid': 'Geçersiz',\n  \n    'team.team.name': 'Takım',\n    'team.team.addForm.code': 'Takım Kodu',\n    'team.team.addForm.name': 'Takım Adı',\n    'team.team.addForm.status': 'Durum',\n    'team.team.addForm.status.valid': 'Geçerli',\n    'team.team.addForm.status.invalid': 'Geçersiz',\n    'team.team.addForm.description': 'Açıklama',\n  };\n  "
  },
  {
    "path": "chat2db-client/src/i18n/tr-tr/workspace.ts",
    "content": "export default {\n  'workspace.title': 'Çalışma Alanı',\n  'workspace.cascader.placeholder': 'Buradan Seçin',\n  'workspace.ai.input.placeholder': 'Düz metin ifadenizi buraya girin',\n  'workspace.title.savedConsole': 'Kaydedilmiş konsol',\n  'workspace.menu.ViewDDL': 'DDL Görüntüle',\n  'workspace.menu.deleteTable': 'Tabloyu Sil',\n  'workspace.menu.openTable': 'Tabloyu Aç',\n  'workspace.menu.editTable': 'Tabloyu Düzenle',\n  'workspace.menu.view': 'Görüntüle',\n  'workspace.menu.pin': 'Sabitle',\n  'workspace.menu.unPin': 'Sabitlemeyi Kaldır',\n  'workspace.menu.editTableData': 'Tablo Verilerini Düzenle',\n  'workspace.menu.queryConsole': 'Sorgu Konsolu',\n  'workspace.menu.viewAllTable': 'Tüm Tabloları Görüntüle',\n  'workspace.menu.createDatabase': 'Veritabanı Oluştur',\n  'workspace.menu.createSchema': 'Şema Oluştur',\n  'workspace.menu.deleteTablePlaceHolder': 'Silmek istediğiniz tablonun adını giriniz',\n  'workspace.menu.createSequence': 'Sıra Oluştur',\n  'workspace.menu.editSequence': 'Sırayı Düzenle',\n  'workspace.menu.deleteSequence': 'Sıradan Sil',\n  'workspace.tips.affirmDeleteTable': 'Girdiğiniz tablo adı, silmek istediğiniz tablo adı ile aynı değil, lütfen tekrar doğrulayın',\n  'workspace.table.total': 'Toplam',\n  'workspace.table.total.tip': 'Toplam satır sayısını yükle',\n  'workspace.table.export.all.csv': 'Sonuç kümesini CSV olarak dışa aktar',\n  'workspace.table.export.cur.csv': 'Geçerli sayfa sonucunu CSV olarak dışa aktar',\n  'workspace.table.export.all.insert': 'Sonuç kümesini INSERT SQL olarak dışa aktar',\n  'workspace.table.export.cur.insert': 'Geçerli sayfa sonucunu INSERT SQL olarak dışa aktar',\n  'workspace.tree.view': 'Görüntüle',\n  'workspace.tree.trigger': 'Tetikleyici',\n  'workspace.tree.function': 'Fonksiyon',\n  'workspace.tree.procedure': 'Prosedür',\n  'workspace.tree.search.placeholder': 'Genişletilmiş düğümde arama yapın',\n  'workspace.tree.delete.tip': 'Bu işlemin kalıcı olarak silindiğini anlıyorum',\n  'workspace.tree.delete.table.tip': '{1} tablosunu silmek istediğinizden emin misiniz?',\n  'workspace.tips.noConnection': 'Henüz bir bağlantı oluşturmadınız',\n  'workspace.tree.delete.sequence.tip': '{1} i silmek istediğinize emin misiniz?'\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/chat.ts",
    "content": "export default {\n  'chat.input.remain': '剩余 {1} 次',\n  'chat.input.tableSelect.placeholder': '请选择表',\n  'chat.input.tableSelect.error.TooManyTable': '最多选择8张表',\n  'chat.input.remain.dialog.tips': '关注公众号，发送\"推广\"获取更多体验次数',\n  'chat.input.syncTable.tips': '自动同步所有表结构给AI上下文（在群内联系群主，申请Chat2DBAI白名单后，仅在Chat2DBAI模型下可用）',\n  'chat.input.remain.tooltip': '手动选中的表的结构将会同步给AI上下文',\n  'chat.input.syncTable.tempTips': '🎉上线：自动同步所有表结构到AI上下文',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/common.ts",
    "content": "export default {\n  'common.text.no': '否',\n  'common.text.is': '是',\n  'common.button.affirm': '确认',\n  'common.button.edit': '编辑',\n  'common.button.modify': '修改',\n  'common.button.confirm': '确认',\n  'common.button.cancel': '取消',\n  'common.data.hour': '{1}小时',\n  'common.data.minute': '{1}分钟',\n  'common.tip.yesterday': '昨天{1}',\n  'common.tip.tomorrow': '明天{1}',\n  'common.tip.ago': '前',\n  'common.tip.later': '后',\n  'common.tip.now': '现在',\n  'common.tip.justNow': '刚刚',\n  'common.text.search': '搜索',\n  'common.placeholder.select': '请选择{1}',\n  'common.text.serviceStarting': '服务启动中...',\n  'common.text.serviceFail': '服务启动失败',\n  'common.text.column': '列',\n  'common.text.row': '行',\n  'common.text.indexes': '索引',\n  'common.button.save': '保存',\n  'common.button.open': '打开',\n  'common.button.refresh': '刷新',\n  'common.button.execute': '执行',\n  \"common.button.import\": '导入SQL',\n  'common.button.format': '格式化',\n  'common.message.successfulConfig': '配置成功',\n  'common.text.successful': '成功',\n  'common.text.failure': '失败',\n  'common.message.modifySuccessfully': '修改成功',\n  'common.message.addedSuccessfully': '添加成功',\n  'common.text.custom': '自定义',\n  'common.button.delete': '删除',\n  'common.text.executionResult': '结果 {1}',\n  'common.tips.deleteTable': '你确定好删除这张表吗？',\n  'common.text.tableName': '表名称',\n  'common.text.submittedSuccessfully': 'Successfully submitted',\n  'common.text.successfullyDelete': '删除成功',\n  'common.text.explainSQL': '解释SQL',\n  'common.text.optimizeSQL': '优化SQL',\n  'common.text.conversionSQL': '转化SQL',\n  'common.text.table': '表',\n  'common.tips.saveSuccessfully': '保存成功',\n  'common.button.copy': '复制',\n  'common.button.copyName': '复制名称',\n  'common.button.copySuccessfully': '复制成功',\n  'common.button.createConsole': '新建控制台',\n  'common.button.exportWord': '导出到Word',\n  'common.button.exportExcel': '导出到Excel',\n  'common.button.exportHtml': '导出到Html',\n  'common.button.exportMarkdown': '导出到Markdown',\n  'common.button.exportPdf': '导出到Pdf',\n  'common.text.successfulExecution': '执行成功',\n  'common.text.result': '结果',\n  'common.text.timeConsuming': '耗时',\n  'common.text.searchRow': '查询行数',\n  'common.text.noData': '暂无数据',\n  'common.text.remindMeLater': '稍后提醒我',\n  'common.text.goToUpdate': '前往更新',\n  'common.text.updateReminder': '更新提醒',\n  'common.text.detectionLatestVersion': '监测到最新版本',\n  'common.text.setting': '设置',\n  'common.text.tryToRestart': '尝试重新启动',\n  'common.text.contactUs': '联系我们',\n  'common.text.wechatPopularizeAi': '关注微信公众号，发送 “AI” 免费获取体验次数。',\n  'common.text.wechatPopularizeAi2': '关注微信公众号，发送 “AI” 可以免费获得ApiKey，并赠送体验次数。',\n  'common.text.wechatPopularize': '发送 “推广” 还可以免费获取更多体验次数。',\n  'common.text.export': '导出',\n  'common.notification.detail': '查看详情',\n  'common.notification.solution': '解决办法',\n  'common.button.copyError': '复制错误报告',\n  'common.button.copyErrorTips': '（这里会复制接口信息以及详细参数，如有敏感参数，请先解析JSON处理后在发送）',\n  'common.tips.formatError': '格式化失败，请看sql是否正确',\n  'common.text.executeSelectedSQL': '执行选中SQL',\n  'common.text.refreshPage': '刷新页面',\n  'common.text.saveConsole': '保存控制台',\n  'common.text.textToSQL': '纯文本转SQL',\n  'common.text.editorRightClick': '编辑器右键',\n  'common.form.error.required': '此项必填!',\n  'common.form.error.email': '不是正确的邮箱格式',\n  'common.tips.delete.confirm': '确认要删除吗？',\n  'common.tips.updateSuccess': '更新成功',\n  'common.tips.createSuccess': '创建成功',\n  'common.text.action': '操作',\n  'common.button.add': '添加',\n  'common.text.errorMessage': '错误信息',\n  'common.button.cancelRequest': '取消请求',\n  'common.button.executionError': '执行错误',\n  'common.text.affectedRows': '受影响行：{1}',\n  'common.text.selectFile' : '选择文件',\n  'common.text.noTableFoundUp' : '当前库没有查询到表',\n  'common.text.noTableFoundDown' : '你可以在顶部切换数据库',\n  'common.text.updateNow' : '立即更新',\n  'common.title.preview' : '预览',\n  'common.title.errorMessage': '错误信息',\n  'common.label.comment': '备注',\n  'common.label.name': '名称',\n  'common.title.create': '创建',\n  'common.title.executiveLogging': '执行记录',\n  'common.text.executionTime': '{1}ms 执行完毕',\n  'common.button.copyRowAs': '复制行为',\n  'common.button.insertSql': 'Insert 语句',\n  'common.button.updateSql': 'Update 语句',\n  'common.button.tabularSeparatedValues': '制表符分隔值',\n  'common.button.tabularSeparatedValuesFieldName': '制表符分隔值(字段名)',\n  'common.button.tabularSeparatedValuesFieldNameAndData': '制表符分隔值(字段名和数据)',\n  'common.button.cloneRow': '克隆行',\n  'common.button.deleteRow': '删除行',\n  'common.button.setNull': '设置为NULL',\n  'common.button.setDefault': '设置为默认值',\n  'common.button.viewData': '查看/修改数据',\n  'common.button.close': '关闭',\n  'common.button.closeAll': '全部关闭',\n  'common.button.closeOthers': '关闭其他',\n  'common.label.tcp': '线上',\n  'common.label.LocalFile': '本地',\n  'common.text.rename': '重命名',\n  'common.title.info': '信息',\n\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/connection.ts",
    "content": "export default {\n  'connection.title': '数据源',\n  'connection.title.connections': '连接',\n  'connection.title.createConnection': '创建数据源',\n  'connection.title.editConnection': '修改数据源',\n  'connection.title.importConnection': '导入数据源',\n  'connection.label.name': '名称',\n  'connection.label.host': '主机',\n  'connection.label.authentication': '身份验证',\n  'connection.label.database': '数据库',\n  'connection.label.JDBCDrive': 'JDBC驱动',\n  'connection.label.port': '端口',\n  'connection.button.testConnection': '测试链接',\n  'connection.label.advancedConfiguration': '高级配置',\n  'connection.label.sshConfiguration': 'SSH',\n  'connection.button.addConnection': '新增连接',\n  'connection.button.connect': '连接',\n  'connection.button.remove': '删除链接',\n  'connection.message.testConnectResult': '测试连接{1}',\n  'connection.message.testSshConnection': '测试ssh连接',\n  'connection.tableHeader.name': '名称',\n  'connection.tableHeader.value': '值',\n  'connection.title.uploadDriver': '上传',\n  'connection.tips.customUpload': '上传驱动',\n  'connection.title.driver': '驱动',\n  'connection.button.clickUpload': '点击上传',\n  'connection.text.downloadDriver': '下载驱动',\n  'connection.text.downloadSuccess': '下载成功',\n  'connection.text.tryAgainDownload': '尝试重新下载',\n  'connection.text.downloading': '下载中...',\n  'connection.label.private': '私有',\n  'connection.label.shared': '共享',\n  'connection.button.createConnection': '创建连接',\n  'connection.tips.noConnection': '您当前还没有创建任何连接',\n  'connection.tips.noConnectionTips': '无权限查看该连接详情，但你可以直接连接该连接',\n  'connection.title.importTitle': '导入文件, .ncx(navicat) 或 .dbp(dbever)',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/dashboard.ts",
    "content": "export default {\n  'dashboard.title': '仪表盘',\n  'dashboard.edit': '编辑',\n  'dashboard.modal.editTitle': '编辑仪表盘',\n  'dashboard.modal.addTitle': '新增仪表盘',\n  'dashboard.modal.name.placeholder': '请输入仪表盘名',\n  'dashboard.delete': '删除',\n  'dashboard.export2image': '导出图片',\n  'dashboard.editor.cascader.placeholder': '请选择连接',\n  'dashboard.editor.execute.noDataSource': '请先选择数据源',\n  'dashboard.editor.execute.success': '执行成功，请选择图表配置',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/editSequence.ts",
    "content": "export default {\n    'editSequence.button.createSequence': '新建序列',\n    'editSequence.button.editSequence': '编辑序列',\n    'editSequence.label.comment': '注释',\n    'editSequence.label.relname': '序列名称',\n    'editSequence.label.typname': '数据类型',\n    'editSequence.label.seqcache': '缓存',\n    'editSequence.label.rolname': '所有者',\n    'editSequence.label.seqstart': '起始值',\n    'editSequence.label.seqincrement': '步长值',\n    'editSequence.label.seqmax': '最大值',\n    'editSequence.label.seqmin': '最小值',\n    'editSequence.label.seqcycle': '周期',\n    'editSequence.title.sqlPreview': 'sql预览'\n};"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/editTable.ts",
    "content": "export default {\n  'editTable.tab.basicInfo': '基本信息',\n  'editTable.tab.columnInfo': '列信息',\n  'editTable.tab.indexInfo': '索引信息',\n  'editTable.label.tableName': '表名',\n  'editTable.label.comment': '注释',\n  'editTable.button.add': '新增',\n  'editTable.button.delete': '删除',\n  'editTable.button.up': '上移',\n  'editTable.button.down': '下移',\n  'editTable.label.indexName': '索引名称',\n  'editTable.label.indexType': '索引类型',\n  'editTable.label.indexMethod': '索引方法',\n  'editTable.label.includeColumn': '包含列',\n  'editTable.button.createTable': '新建表',\n  'editTable.button.importTable': '导出表',\n  'editTable.label.index': '序号',\n  'editTable.label.columnName': '列名',\n  'editTable.label.columnSize': '长度',\n  'editTable.label.columnType': '类型',\n  'editTable.label.nullable': '可空',\n  'editTable.label.prefixLength': '前缀长度',\n  'editTable.label.defaultValue': '默认值',\n  'editTable.label.sparse': '稀疏',\n  'editTable.label.characterSet': '字符集',\n  'editTable.label.collation': '排序规则',\n  'editTable.label.decimalPoint': '小数点',\n  'editTable.label.unit': '单位',\n  'editTable.label.value': '值',\n  'editTable.label.autoIncrement': '是否自增',\n  'editTable.label.engine': '引擎',\n  'editTable.label.incrementValue': '自增值',\n  'editTable.label.order': '排序',\n  'editTable.label.primaryKey': '键',\n  'editTable.title.sqlPreview': 'sql预览',\n  'editTable.button.addColumn': '添加列',\n  'editTable.button.addIndex': '添加索引',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/editTableData.ts",
    "content": "export default {\n  'editTableData.tips.addRow': '添加行',\n  'editTableData.tips.deleteRow': '删除行',\n  'editTableData.tips.revert': '撤销',\n  'editTableData.tips.previewPendingChanges':  '预览待提交的修改',\n  'editTableData.tips.submit': '提交更改',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/index.ts",
    "content": "import { LangType } from '@/constants'\nimport menu from './menu';\nimport common from './common';\nimport connection from './connection';\nimport setting from './setting';\nimport workspace from './workspace';\nimport dashboard from './dashboard';\nimport chat from './chat';\nimport team from './team'\nimport login from './login';\nimport editTable from './editTable';\nimport editTableData from './editTableData';\nimport sqlEditor from './sqlEditor'\nimport editSequence from './editSequence';\n\nexport default {\n  lang: LangType.ZH_CN,\n  ...connection,\n  ...common,\n  ...setting,\n  ...workspace,\n  ...menu,\n  ...connection,\n  ...dashboard,\n  ...chat,\n  ...team,\n  ...login,\n  ...editTable,\n  ...editTableData,\n  ...sqlEditor,\n  ...editSequence\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/login.ts",
    "content": "export default {\n  'login.text.logout': '退出登录',\n  'login.text.welcome': '欢迎使用 Chat2DB',\n  'login.text.tips': 'Chat2DB 账号仅用于团队协作管理',\n  'login.text.tips.title': '为什么需要登录？',\n  'login.text.setting': '设 置',\n  'login.form.user': '用户名',\n  'login.form.user.placeholder': '请输入用户名',\n  'login.form.password': '密码',\n  'login.form.password.placeholder': '请输入密码',\n  'login.button.login': '登 录',\n  'login.tips.defaultPassword': '默认用户名和密码均为: chat2db',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/menu.ts",
    "content": "export default {\n  'menu.file': 'File'\n}\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/setting.ts",
    "content": "export default {\n  'setting.title.setting': '设置',\n  'setting.nav.basic': '基础设置',\n  'setting.nav.customAi': '自定义AI',\n  'setting.nav.proxy': '服务端地址',\n  'setting.nav.aboutUs': '关于我们',\n  'setting.title.backgroundColor': '背景色',\n  'setting.title.themeColor': '主题色',\n  'setting.title.sqlEditorFontSize': 'SQL编辑器字体大小',\n  'setting.label.blue': '蓝蓬釉',\n  'setting.label.green': '极光绿',\n  'setting.label.violet': '酱紫',\n  'setting.text.dark': '暗色',\n  'setting.text.dark2': '暗色2',\n  'setting.text.light': '亮色',\n  'setting.text.followOS': '自动',\n  'setting.title.language': '语言',\n  'setting.title.aiSource': 'AI 来源',\n  'setting.tab.custom': '自定义',\n  'setting.tab.aiType.zhipu': '智谱',\n  'setting.tab.aiType.baichuan': '百川',\n  'setting.tab.aiType.wenxin': '文心一言',\n  'setting.tab.aiType.tongyiqianwen': '通义千问',\n  'setting.tab.aiType.custom.tips': '接口格式与OpenAI接口格式一致',\n  'setting.label.serviceAddress': '服务地址',\n  'setting.button.apply': '应用',\n  'setting.text.currentEnv': '当前环境',\n  'setting.text.currentVersion': '当前版本',\n  'setting.text.viewingUpdateLogs': '查看更新日志',\n  'setting.label.isStreamOutput': '接口是否流式输出',\n  'setting.label.customAiUrl': '自定义接口Url',\n  'setting.placeholder.httpsProxy': '非必填，用于设置请求OPENAI接口时的HTTP代理{1}',\n  'setting.placeholder.apiKey': '使用OpenAi接口时必填，可前往OpenAI官网查看APIKEY',\n  'setting.placeholder.chat2dbApiKey': '使用Chat2DB提供的APIKEY',\n  'setting.placeholder.customUrl': '选择自定义AI时必填，用于设置自定义AI的REST接口URL',\n  'setting.placeholder.apiHost': '非必填，默认值为 https://api.openai.com/',\n  'setting.message.urlTestError': '接口测试不通过',\n  'setting.placeholder.azureOpenAIKey': '从Azure门户获取Azure OpenAI密钥凭证',\n  'setting.placeholder.azureEndpoint': '从Azure门户获取Azure OpenA端口',\n  'setting.placeholder.azureDeployment': '部署模型的部署id',\n  'setting.ai.tips': '请登录后选择AI配置',\n  'setting.ai.user.hidden': '请联系管理员在\"设置->自定义AI\"中设置ApiKey',\n  'setting.button.startDownloading': '开始下载',\n  'setting.button.beDownloading': '下载中',\n  'setting.button.redownload': '重新下载',\n  'setting.button.restart': '立即重启',\n  'setting.text.discoverNewVersion': '发现新版本 {1}',\n  'setting.text.isLatestVersion': '已是最新版本',\n  'setting.button.changeLog': '查看更新日志',\n  'setting.title.updateRule': '更新规则',\n  'setting.text.autoUpdate': '新版自动下载并安装更新',\n  'setting.text.manualUpdate': '仅在新版本发布时提醒我',\n  'setting.button.iSee': '我知道了',\n  'setting.text.newEditionIsReady': '新版本已下载完成, 重启软件将会安装新版本',\n  'setting.button.goToUpdate': '前往更新',\n  'setting.text.UpdatedLatestVersion': '已更新到最新版本 {1}',\n  'setting.title.holdingService': '保持服务',\n  'setting.text.holdingService': '退出应用时保持服务，加快启动速度',\n  'setting.chat2db.ai.button': '请前往 Chat2DB Pro 体验更为强大的AI功能',\n  'setting.title.goto.chat2db.pro': '前往Chat2DB Pro',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/sqlEditor.ts",
    "content": "export default {\n  'sqlEditor.text.keyword': '关键词',\n  'sqlEditor.text.function': '函数',\n  'sqlEditor.text.tableName': '表名',\n  'sqlEditor.text.databaseName': '数据库',\n  'sqlEditor.text.schemaName': 'Schema',\n  'sqlEditor.text.viewName': '视图名',\n  'sqlEditor.text.fieldName': '字段名',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/team.ts",
    "content": "export default {\n  'team.title': '团队管理',\n  'team.tab.datasource': '链接管理',\n  'team.tab.user': '用户管理',\n  'team.tab.team': '团队管理',\n\n  'team.action.rightManagement': '权限管理',\n  'team.action.editDatasource': '编辑链接',\n  'team.action.addDatasource': '添加链接',\n  'team.action.addDatasource.placeholder': '搜索链接',\n  'team.action.editUser': '编辑用户',\n  'team.action.addUser': '添加用户',\n  'team.action.addUser.placeholder': '搜索用户',\n  'team.action.editTeam': '编辑团队',\n  'team.action.addTeam': '添加团队',\n  'team.action.addTeam.placeholder': '搜索团队',\n  'team.action.affiliation.user': '包含用户',\n  'team.action.affiliation.team': '所属团队',\n  'team.action.affiliation.datasource': '归属链接',\n  'team.action.addUserAndTeam': '添加用户/团队',\n  'team.action.addUserAndTeam.placeholder': '搜索人员/团队',\n  'team.input.search.placeholder': '输入关键字进行搜索',\n\n  'team.datasource.rightManagement': '权限管理',\n  'team.datasource.alias': '链接名称',\n  'team.datasource.url': '链接地址',\n  'team.datasource.code': '编码',\n  'team.datasource.name': '名称',\n  'team.datasource.status': '状态',\n\n  'team.user.name': '用户',\n  'team.user.userName': '用户名',\n  'team.user.nickName': '昵称',\n  'team.user.status': '状态',\n  'team.user.addForm.userName': '用户名',\n  'team.user.addForm.nickName': '昵称',\n  'team.user.addForm.email': '邮箱',\n  'team.user.addForm.password': '密码',\n  'team.user.addForm.roleCode': '角色',\n  'team.user.addForm.roleCode.admin': '管理员',\n  'team.user.addForm.roleCode.user': '用户',\n  'team.user.addForm.status': '状态',\n  'team.user.addForm.status.valid': '有效',\n  'team.user.addForm.status.invalid': '无效',\n\n  'team.team.name': '团队',\n  'team.team.addForm.code': '团队编码',\n  'team.team.addForm.name': '团队名',\n  'team.team.addForm.status': '状态',\n  'team.team.addForm.status.valid': '有效',\n  'team.team.addForm.status.invalid': '无效',\n  'team.team.addForm.description': '描述',\n};\n"
  },
  {
    "path": "chat2db-client/src/i18n/zh-cn/workspace.ts",
    "content": "export default {\n  'workspace.title': '工作台',\n  'workspace.cascader.placeholder': '请选择',\n  'workspace.ai.input.placeholder': '在这里输入纯文本语句',\n  'workspace.title.savedConsole': '保存记录',\n  'workspace.menu.ViewDDL': '查看DDL',\n  'workspace.menu.deleteTable': '删除表',\n  'workspace.menu.openTable': '打开表',\n  'workspace.menu.editTable': '修改表',\n  'workspace.menu.view': '查看',\n  'workspace.menu.pin': '置顶',\n  'workspace.menu.unPin': '取消置顶',\n  'workspace.menu.editTableData': '编辑表数据',\n  'workspace.menu.queryConsole': '新建查询',\n  'workspace.menu.viewAllTable': '查看所有表',\n  'workspace.menu.createDatabase': '创建数据库',\n  'workspace.menu.createSchema': '创建Schema',\n  'workspace.menu.deleteTablePlaceHolder': '请输入你要删除的表名',\n  'workspace.menu.createSequence': '创建序列',\n  'workspace.menu.editSequence': '修改序列',\n  'workspace.menu.deleteSequence': '删除序列',\n  'workspace.tips.affirmDeleteTable': '输入的表名与要删除的表名不一致，请再次确认',\n  'workspace.table.total': '总数',\n  'workspace.table.total.tip': '加载总行数',\n  'workspace.table.export.all.csv': '导出结果集 csv',\n  'workspace.table.export.cur.csv': '导出当前页结果集 csv',\n  'workspace.table.export.all.insert': '导出结果集 insert sql',\n  'workspace.table.export.cur.insert': '导出当前页结果集 insert sql',\n  'workspace.tree.view': '视图',\n  'workspace.tree.trigger': '触发器',\n  'workspace.tree.function': '函数',\n  'workspace.tree.procedure': '存储过程',\n  'workspace.tree.search.placeholder': '在展开节点中搜索',\n  'workspace.tree.delete.tip': '我了解该操作是永久性删除',\n  'workspace.tree.delete.table.tip': '确定要删除表{1}吗？',\n  'workspace.tips.noConnection': '你还没有创建连接',\n  'workspace.tips.maxConsole': '最多只能打开20个控制台',\n  'workspace.tips.openExecutiveLogging': '打开执行记录',\n  'workspace.tree.delete.sequence.tip': '确定要删除序列{1}吗？',\n};\n"
  },
  {
    "path": "chat2db-client/src/indexedDB/index.ts",
    "content": "import { tableList } from './table';\n\n// 创建数据库的方法\nexport const createDB = (dbName: string, version: number) => {\n  return new Promise((resolve, reject) => {\n    const request = window.indexedDB.open(dbName, version);\n    request.onerror = (event: any) => {\n      reject(event.target.error);\n    };\n    request.onsuccess = (event: any) => {\n      resolve(event.target.result);\n    };\n    request.onupgradeneeded = (event: any) => {\n      const db = event.target.result; // 数据库对象\n      // 创建存储库\n      tableList.forEach((item: any) => {\n        const { tableDetails } = item;\n        const objectStore = db.createObjectStore(tableDetails.name, tableDetails.primaryKey);\n        tableDetails.column.forEach((i: any) => {\n          if (i.isIndex) {\n            objectStore.createIndex(i.name, i.keyPath, i.options);\n          }\n        });\n      });\n    };\n  });\n};\n\ntype TableType = 'workspaceConsoleDDL';\n\ntype DBType = 'chat2db';\n\n// 添加数据\nexport const addData = (db: DBType, tableName: TableType, data: any) => {\n  return new Promise((resolve, reject) => {\n    const transaction = window._indexedDB[db].transaction(tableName, 'readwrite');\n    const objectStore = transaction.objectStore(tableName);\n    const request = objectStore.add(data);\n    request.onsuccess = () => {\n      resolve(true);\n    };\n    request.onerror = (error) => {\n      reject(error);\n    };\n  });\n};\n\n// 通过索引删除数据\nexport const deleteDataByIndex = (db: DBType, tableName: TableType, indexName, indexValue) => {\n  return new Promise((resolve, reject) => {\n    const transaction = window._indexedDB[db].transaction(tableName, 'readwrite');\n    const objectStore = transaction.objectStore(tableName);\n    const request = objectStore.index(indexName).delete(indexValue);\n    request.onsuccess = () => {\n      resolve(true);\n    };\n    request.onerror = () => {\n      reject(false);\n    };\n  });\n};\n\n// 通过主键删除数据\nexport const deleteData = (db: DBType, tableName: TableType, key: any) => {\n  return new Promise((resolve, reject) => {\n    const transaction = window._indexedDB[db].transaction(tableName, 'readwrite');\n    const objectStore = transaction.objectStore(tableName);\n    const request = objectStore.delete(key);\n    request.onsuccess = () => {\n      resolve(true);\n    };\n    request.onerror = () => {\n      reject(false);\n    };\n  });\n};\n\n// 通过索引查询数据,支持传入多个索引\nexport const getDataByIndex = (db: DBType, tableName: TableType, indexName: string, indexValue: any) => {\n  return new Promise((resolve, reject) => {\n    const transaction = window._indexedDB[db].transaction(tableName, 'readwrite');\n    const objectStore = transaction.objectStore(tableName);\n    const request = objectStore.index(indexName).get(indexValue);\n    request.onsuccess = () => {\n      resolve(request.result);\n    };\n    request.onerror = () => {\n      reject(false);\n    };\n  });\n};\n\n// 通过游标查询数据，支持传入多个条件\nexport const getDataByCursor = (db: DBType, tableName: TableType, condition: {[key in string]: any}\n) => {\n  return new Promise((resolve, reject) => {\n    const transaction = window._indexedDB[db].transaction(tableName, 'readwrite');\n    const objectStore = transaction.objectStore(tableName);\n    const request = objectStore.openCursor();\n    const result: any[] = [];\n    request.onsuccess = (event: any) => {\n      const cursor = event.target.result;\n      if (cursor) {\n        let flag = true;\n        Object.keys(condition).forEach((key) => {\n          if (cursor.value[key] !== condition[key]) {\n            flag = false;\n          }\n        });\n        if (flag) {\n          result.push(cursor.value);\n        }\n        cursor.continue();\n      } else {\n        resolve(result);\n      }\n    };\n    request.onerror = () => {\n      reject(false);\n    };\n  });\n \n};\n\n\n// 修改数据\nexport const updateData = (db: DBType, tableName: TableType, data: any) => {\n  return new Promise((resolve, reject) => {\n    const transaction = window._indexedDB[db].transaction(tableName, 'readwrite');\n    const objectStore = transaction.objectStore(tableName);\n    const request = objectStore.put(data);\n    request.onsuccess = () => {\n      resolve(true);\n    };\n    request.onerror = () => {\n      reject(false);\n    };\n  });\n};\n\n// 关闭数据库\nexport const closeDB = (db: DBType) => {\n  return new Promise((resolve) => {\n    window._indexedDB[db].close();\n    resolve(true);\n  });\n};\n\nexport default {\n  createDB,\n  addData,\n  deleteDataByIndex,\n  deleteData,\n  getDataByIndex,\n  getDataByCursor,\n  updateData,\n  closeDB,\n};\n"
  },
  {
    "path": "chat2db-client/src/indexedDB/table.ts",
    "content": "\nexport interface IWorkspaceConsoleDDL {\n  consoleId: string; // 控制台的id 唯一\n  ddl: string; // 数据源ddl\n  userId?: string; // 用户的唯一id\n}\n\n// 工作区console表\nexport const workspaceConsoleDDL = {\n  name: 'workspaceConsoleDDL',\n  primaryKey: {\n    keyPath: 'consoleId',\n    autoIncrement: true,\n  },\n  column: [\n    {\n      name: 'consoleId',\n      isIndex: true,\n      keyPath: 'consoleId',\n      options: {\n        unique: true,\n      },\n    },\n    {\n      name: 'userId',\n      isIndex: true,\n      keyPath: 'userId',\n      options: {\n        unique: false,\n      },\n    },\n    {\n      name: 'ddl',\n      isIndex: true,\n      keyPath: 'ddl',\n      options: {\n        unique: false,\n      },\n    },\n\n  ],\n}\n\nexport const tableList = [\n  {\n    tableDetails: workspaceConsoleDDL,\n  }\n]\n"
  },
  {
    "path": "chat2db-client/src/layouts/GlobalLayout/index.less",
    "content": "@import '@/styles/global.less';\n@import '@/styles/var.less';\n\n.loadingBox {\n  flex: 1;\n  width: 100vw;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  font-size: 16px;\n  flex-direction: column;\n\n  .contact {\n    line-height: 32px;\n    display: flex;\n    align-items: center;\n\n    .icon {\n      cursor: pointer;\n      font-size: 32px;\n      margin-right: 20px;\n    }\n  }\n}\n\n.app {\n  width: 100vw;\n  height: 100vh;\n  display: flex;\n  flex-direction: column;\n  .appBody {\n    flex: 1;\n    position: relative;\n  }\n}\n\n:global {\n  #root {\n    height: 100%;\n\n    .codicon-symbol-text:before {\n      content: '\\eb11';\n      width: 16px;\n      height: 16px;\n    }\n\n    .codicon-symbol-function:before {\n      content: '\\ebb8';\n      width: 16px;\n      height: 16px;\n      // background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAKZJREFUOE+lkgEOgzAMA83LNl7G9rJtL2O6qkZZlRYGkSpacGzHdNLFmi72a0TwkLQEgRTbI3DzLOk9ctkjWCU9JUE0rBHBoXwi6BWk7tX6p7rgTDEOe1ZxFwkMIjgaPTtPwLc6FkLbeJlNAFaO8/MekZ9sMgICzNL/i6AltivGYb8JtED//zYbcqGJch7lnBEYtHcFyvee1d0LZPaMwFZPOTjUFEFfU0IlESizFzUAAAAASUVORK5CYII=\");\n    }\n\n    .codicon-symbol-folder:before {\n      content: '\\ebb7';\n      width: 16px;\n      height: 16px;\n      // background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAHxJREFUOE/Fk10OgCAMgz9Opp5MPZl4Mk0JS4AEJWK0L+OvZWzF0QkX+SMwA4ot8MAKeBPYgB1YWtjx3ABMJnAANm7UIHBeFWi9OT1XzcBqUYsSuXzCPwLqq0EtEtRaoZxrTb7JatAtkPrg+xqUVr7LQPuZlbs/0xMXBs4Jp5IvERhfiDgAAAAASUVORK5CYII=\");\n    }\n\n    .codicon-symbol-field:before {\n      content: '\\eb17';\n      width: 16px;\n      height: 16px;\n      // background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjk3MTcwMDQyMTI4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjkyNTIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Ik05NzkuMiAwSDQ0LjhhNDQuOCA0NC44IDAgMCAwLTQ0LjggNDQuOHY5MzQuNGE0NC44IDQ0LjggMCAwIDAgNDQuOCA0NC44aDkzNC40YTQ0LjggNDQuOCAwIDAgMCA0NC44LTQ0LjhWNDQuOGE0NC44IDQ0LjggMCAwIDAtNDQuOC00NC44ek05NjAgOTYwSDY0VjY0aDg5NnoiIGZpbGw9IiM1NzU4NjkiIHAtaWQ9IjkyNTMiPjwvcGF0aD48cGF0aCBkPSJNMjU2IDIzOS44MDhoMjI0djU0NC4zODRIMzg0djY0aDI1NnYtNjRINTQ0VjIzOS44MDhINzY4djEzNC43Mmg2NFYxNzUuODA4SDE5MnYxOTguNzJoNjRWMjM5LjgwOHoiIGZpbGw9IiM1NzU4NjkiIHAtaWQ9IjkyNTQiPjwvcGF0aD48L3N2Zz4=');\n    }\n\n    .codicon-symbol-unit:before {\n      content: '\\ea70';\n      width: 16px;\n      height: 16px;\n      // background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjk3MTcwMDQyMTI4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjkyNTIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Ik05NzkuMiAwSDQ0LjhhNDQuOCA0NC44IDAgMCAwLTQ0LjggNDQuOHY5MzQuNGE0NC44IDQ0LjggMCAwIDAgNDQuOCA0NC44aDkzNC40YTQ0LjggNDQuOCAwIDAgMCA0NC44LTQ0LjhWNDQuOGE0NC44IDQ0LjggMCAwIDAtNDQuOC00NC44ek05NjAgOTYwSDY0VjY0aDg5NnoiIGZpbGw9IiM1NzU4NjkiIHAtaWQ9IjkyNTMiPjwvcGF0aD48cGF0aCBkPSJNMjU2IDIzOS44MDhoMjI0djU0NC4zODRIMzg0djY0aDI1NnYtNjRINTQ0VjIzOS44MDhINzY4djEzNC43Mmg2NFYxNzUuODA4SDE5MnYxOTguNzJoNjRWMjM5LjgwOHoiIGZpbGw9IiM1NzU4NjkiIHAtaWQ9IjkyNTQiPjwvcGF0aD48L3N2Zz4=');\n    }\n\n    .codicon-symbol-property:before {\n      content: '\\eace';\n      width: 16px;\n      height: 16px;\n      // background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjk3MTcwMDQyMTI4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjkyNTIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Ik05NzkuMiAwSDQ0LjhhNDQuOCA0NC44IDAgMCAwLTQ0LjggNDQuOHY5MzQuNGE0NC44IDQ0LjggMCAwIDAgNDQuOCA0NC44aDkzNC40YTQ0LjggNDQuOCAwIDAgMCA0NC44LTQ0LjhWNDQuOGE0NC44IDQ0LjggMCAwIDAtNDQuOC00NC44ek05NjAgOTYwSDY0VjY0aDg5NnoiIGZpbGw9IiM1NzU4NjkiIHAtaWQ9IjkyNTMiPjwvcGF0aD48cGF0aCBkPSJNMjU2IDIzOS44MDhoMjI0djU0NC4zODRIMzg0djY0aDI1NnYtNjRINTQ0VjIzOS44MDhINzY4djEzNC43Mmg2NFYxNzUuODA4SDE5MnYxOTguNzJoNjRWMjM5LjgwOHoiIGZpbGw9IiM1NzU4NjkiIHAtaWQ9IjkyNTQiPjwvcGF0aD48L3N2Zz4=');\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/layouts/GlobalLayout/index.tsx",
    "content": "import React, { useEffect, useLayoutEffect, useState } from 'react';\nimport usePollRequestService, { ServiceStatus } from '@/hooks/usePollRequestService';\nimport i18n, { isEn } from '@/i18n';\nimport { Button, ConfigProvider, Spin, Tooltip } from 'antd';\nimport antdEnUS from 'antd/locale/en_US';\nimport antdZhCN from 'antd/locale/zh_CN';\nimport service from '@/service/misc';\nimport useCopyFocusData from '@/hooks/useFocusData';\nimport { useTheme } from '@/hooks/useTheme';\nimport { getAntdThemeConfig } from '@/theme';\nimport { Outlet } from 'umi';\nimport init from '../init/init';\nimport { GithubOutlined, SyncOutlined, WechatOutlined } from '@ant-design/icons';\nimport { ThemeType } from '@/constants';\nimport GlobalComponent from '../init/GlobalComponent';\nimport styles from './index.less';\nimport { useUserStore, queryCurUser } from '@/store/user';\n\nconst GlobalLayout = () => {\n  const [appTheme, setAppTheme] = useTheme();\n  const [antdTheme, setAntdTheme] = useState<any>({});\n  const { curUser } = useUserStore((state) => {\n    return {\n      curUser: state.curUser,\n    };\n  });\n\n  const { serviceStatus, restartPolling } = usePollRequestService({\n    loopService: service.testService,\n  });\n\n  useCopyFocusData();\n\n  useLayoutEffect(() => {\n    setAntdTheme(getAntdThemeConfig(appTheme));\n  }, [appTheme]);\n\n  useLayoutEffect(() => {\n    init();\n    monitorOsTheme();\n  }, []);\n\n  useEffect(() => {\n    if (serviceStatus === ServiceStatus.SUCCESS) {\n      queryCurUser();\n    }\n  }, [serviceStatus]);\n\n  // 监听系统(OS)主题变化\n  const monitorOsTheme = () => {\n    function change(e: any) {\n      if (appTheme.backgroundColor === ThemeType.FollowOs) {\n        setAppTheme({\n          ...appTheme,\n          backgroundColor: e.matches ? ThemeType.Dark : ThemeType.Light,\n        });\n      }\n    }\n    const matchMedia = window.matchMedia('(prefers-color-scheme: dark)');\n    matchMedia.onchange = change;\n  };\n\n  // 等待状态页面\n  if (serviceStatus === ServiceStatus.PENDING || curUser === null) {\n    return (\n      <div className={styles.app}>\n        <Spin className={styles.loadingBox} size=\"large\" />\n      </div>\n    );\n  }\n\n  // 错误状态页面\n  if (serviceStatus === ServiceStatus.FAILURE) {\n    return (\n      <div className={styles.app}>\n        <div className={styles.loadingBox}>\n          <Button type=\"primary\" onClick={restartPolling} style={{ marginBottom: 20 }}>\n            <SyncOutlined />\n            {i18n('common.text.tryToRestart')}\n          </Button>\n          <div className={styles.contact}>\n            {i18n('common.text.contactUs')}：\n            <GithubOutlined className={styles.icon} onClick={() => window.open('https://github.com/chat2db/Chat2DB')} />\n            <Tooltip\n              placement=\"bottom\"\n              title={<img style={{ width: 200, height: 200 }} src=\"https://sqlgpt.cn/_static/img/chat2db_wechat.png\" />}\n            >\n              <WechatOutlined className={styles.icon} />\n            </Tooltip>\n          </div>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <ConfigProvider locale={isEn ? antdEnUS : antdZhCN} theme={antdTheme}>\n      <div className={styles.app}>\n        <div className={styles.appBody}>\n          <Outlet />\n        </div>\n      </div>\n      <GlobalComponent />\n    </ConfigProvider>\n  );\n};\n\nexport default GlobalLayout;\n"
  },
  {
    "path": "chat2db-client/src/layouts/init/GlobalComponent.tsx",
    "content": "import React from 'react';\nimport MyNotification from '@/components/MyNotification';\nimport Modal from '@/components/Modal/BaseModal';\n\nconst GlobalComponent = () => {\n  return <>\n    <MyNotification />\n    <Modal />\n  </>\n}\n\nexport default GlobalComponent;\n"
  },
  {
    "path": "chat2db-client/src/layouts/init/init.ts",
    "content": "import { clearOlderLocalStorage } from '@/utils';\nimport initIndexedDB from './initIndexedDB';\nimport registerElectronApi from './registerElectronApi';\nimport registerMessage from './registerMessage';\nimport registerNotification from './registerNotification';\nimport { getLang, setLang } from '@/utils/localStorage';\nimport { LangType } from '@/constants';\n\nconst init = () => {\n  clearOlderLocalStorage();\n\n  initLang();\n  initIndexedDB();\n  registerElectronApi();\n\n  registerMessage();\n  registerNotification();\n};\n\n// 初始化语言\nconst initLang = () => {\n  const lang = getLang();\n  if (!lang) {\n    setLang(LangType.EN_US);\n    document.documentElement.setAttribute('lang', LangType.EN_US);\n    const date = new Date('2030-12-30 12:30:00').toUTCString();\n    document.cookie = `CHAT2DB.LOCALE=${lang};Expires=${date}`;\n  }\n};\n\nexport default init;\n"
  },
  {
    "path": "chat2db-client/src/layouts/init/initIndexedDB.ts",
    "content": "import indexedDB from '@/indexedDB';\n\n/** 初始化indexedDB */\nconst initIndexedDB = () => {\n  indexedDB.createDB('chat2db', 1).then((db) => {\n    window._indexedDB = {\n      chat2db: db,\n    };\n  });\n};\n\nexport default initIndexedDB;\n"
  },
  {
    "path": "chat2db-client/src/layouts/init/registerElectronApi.ts",
    "content": "// 注册Electron关闭时，关闭服务\nimport { useSettingStore } from '@/store/setting'\nconst registerElectronApi = () => {\n  window.electronApi?.registerAppMenu({\n    version: __APP_VERSION__,\n  });\n  window.electronApi?.setBaseURL?.(window._BaseURL);\n  window.electronApi?.setForceQuitCode?.(useSettingStore.getState().holdingService);\n};\n\nexport default registerElectronApi;\n"
  },
  {
    "path": "chat2db-client/src/layouts/init/registerMessage.ts",
    "content": "import { message } from 'antd';\n\nexport default () => {\n  message.config({\n    maxCount: 1,\n    duration: 3,\n  });\n};\n"
  },
  {
    "path": "chat2db-client/src/layouts/init/registerNotification.ts",
    "content": "import { notification } from 'antd';\n\nexport default () => {\n  notification.config({\n    placement: 'BottomRight',\n    maxCount: 2,\n    duration: null,\n  });\n};\n"
  },
  {
    "path": "chat2db-client/src/main/analysis.js",
    "content": "const os = require('os');\nconst { readVersion } = require('./utils');\nconst Analytics4 = require('./ga4');\n\nfunction registerAnalytics() {\n  const analytics = new Analytics4('G-V8M4E5SF61', 'LShbzC_vRka5Sw5AWco7Tw');\n  const customParams = {\n    platform: 'DESKTOP',\n    version: readVersion(),\n    os: os.platform(),\n  };\n  analytics.setParams(customParams).event('first_enter');\n}\n\nmodule.exports = registerAnalytics;\n"
  },
  {
    "path": "chat2db-client/src/main/constants.js",
    "content": "const DEV_WEB_URL = 'http://localhost:8000/';\n\n/** jar包名 */\nconst JAVA_APP_NAME = 'chat2db-server-start.jar';\nconst JAVA_PATH = 'jre/bin/java';\n\nmodule.exports = {\n  DEV_WEB_URL,\n\n  JAVA_APP_NAME,\n  JAVA_PATH,\n};\n"
  },
  {
    "path": "chat2db-client/src/main/ga4.js",
    "content": "const { net } = require('electron');\nconst { v4: uuidv4 } = require('uuid');\nconst { machineIdSync } = require('node-machine-id');\nconst log = require('electron-log');\n\nclass Analytics4 {\n  constructor(trackingID, secretKey, clientID = machineIdSync(), sessionID = uuidv4()) {\n    this.trackingID = trackingID;\n    this.secretKey = secretKey;\n    this.clientID = clientID;\n    this.sessionID = sessionID;\n    this.customParams = {};\n    this.userProperties = null;\n    this.baseURL = 'https://google-analytics.com/mp';\n    this.collectURL = '/collect';\n  }\n\n  set(key, value) {\n    if (value !== null) {\n      this.customParams[key] = value;\n    } else {\n      delete this.customParams[key];\n    }\n    return this;\n  }\n\n  setParams(params) {\n    if (typeof params === 'object' && Object.keys(params).length > 0) {\n      Object.assign(this.customParams, params);\n    } else {\n      this.customParams = {};\n    }\n    return this;\n  }\n\n  setUserProperties(upValue) {\n    if (typeof upValue === 'object' && Object.keys(upValue).length > 0) {\n      this.userProperties = upValue;\n    } else {\n      this.userProperties = null;\n    }\n    return this;\n  }\n\n  event(eventName) {\n    const payload = {\n      client_id: this.clientID,\n      events: [\n        {\n          name: eventName,\n          params: {\n            session_id: this.sessionID,\n            ...this.customParams,\n          },\n        },\n      ],\n    };\n\n    if (this.userProperties) {\n      Object.assign(payload, { user_properties: this.userProperties });\n    }\n\n    const url = `${this.baseURL}${this.collectURL}?measurement_id=${this.trackingID}&api_secret=${this.secretKey}`;\n    const request = net.request({\n      method: 'POST',\n      url,\n    });\n\n    request.on('response', (response) => {\n      let responseData = '';\n      response.on('data', (chunk) => {\n        responseData += chunk;\n      });\n\n      response.on('end', () => {\n        if (response.statusCode >= 200 && response.statusCode < 300) {\n          log.info('success', responseData);\n        } else {\n          log.error('response error', response.statusCode);\n        }\n      });\n    });\n\n    request.on('error', (error) => {\n      log.error('Error posting data:', error);\n    });\n\n    request.write(JSON.stringify(payload));\n\n    request.end();\n  }\n}\n\nmodule.exports = Analytics4;\n"
  },
  {
    "path": "chat2db-client/src/main/i18n/en/index.js",
    "content": "const locale = {\n  'menu.file': 'File',\n  'menu.edit': 'Edit',\n};\n\nmodule.exports = locale;\n"
  },
  {
    "path": "chat2db-client/src/main/i18n/index.js",
    "content": "const zhCN = require('./zh-cn');\nconst en = require('./en');\n\n// TODO: 需要获取渲染进程的语言环境\nconst isZH = false;\n\nconst locale = isZH ? zhCN : en;\n\nconst i18n = (key) => locale[key] || key;\n\nmodule.exports = i18n;\n"
  },
  {
    "path": "chat2db-client/src/main/i18n/zh-cn/index.js",
    "content": "const locale = {\n  'menu.file': '文件',\n  'menu.edit': '编辑',\n};\n\nmodule.exports = locale;\n"
  },
  {
    "path": "chat2db-client/src/main/index.js",
    "content": "const { app, BrowserWindow, shell, net, ipcMain, globalShortcut } = require('electron');\nconst path = require('path');\nconst registerAppMenu = require('./menu');\nconst registerAnalysis = require('./analysis');\nconst store = require('./store');\nconst { loadMainResource, isMac } = require('./utils');\n\nlet mainWindow = null;\n\nlet baseUrl = null;\nlet _forceQuitCode = false;\n\n/**\n * Initial window options\n */\n\nfunction createWindow() {\n  const { width, height, x, y } = store.get('windowBounds', { width: 1440, height: 800 });\n\n  const options = {\n    x,\n    y,\n    height,\n    width,\n    minWidth: 1080,\n    minHeight: 720,\n    show: false,\n    webPreferences: {\n      webSecurity: false,\n      spellcheck: false, // 禁用拼写检查器\n      nodeIntegration: true,\n      contextIsolation: true,\n      preload: path.join(__dirname, 'preload.js'),\n    },\n  };\n\n  mainWindow = new BrowserWindow(options);\n  mainWindow.show();\n\n  // 加载应用-----\n  loadMainResource(mainWindow);\n\n  mainWindow.webContents.setWindowOpenHandler(({ url }) => {\n    shell.openExternal(url);\n    return { action: 'deny' };\n  });\n\n  mainWindow.on('resize', () => {\n    store.set('windowBounds', mainWindow.getBounds());\n  });\n\n  mainWindow.on('move', () => {\n    store.set('windowBounds', mainWindow.getBounds());\n  });\n\n  // 注册快捷键Ctrl+Shift+I打开开发者工具\n  globalShortcut.register('CommandOrControl+Shift+I', () => {\n    mainWindow.webContents.openDevTools()\n  })\n}\n\n// const menu = Menu.buildFromTemplate(menuBar);\n// Menu.setApplicationMenu(menu);\n\napp.commandLine.appendSwitch('--disable-gpu-sandbox');\n\napp.on('ready', () => {\n  createWindow();\n  registerAppMenu(mainWindow);\n  registerAnalysis();\n});\n\napp.on('activate', () => {\n  if (!mainWindow) {\n    createWindow();\n  } else {\n    if (mainWindow.isMinimized()) {\n      mainWindow.restore();\n    }\n    if (mainWindow.isVisible()) {\n      mainWindow.focus();\n    } else {\n      mainWindow.show();\n    }\n  }\n});\n\napp.on('window-all-closed', (e) => {\n  mainWindow = null\n  if (isMac) return;\n  app.quit();\n});\n\napp.on('before-quit', () => {\n  if (baseUrl) {\n    try {\n      const request = net.request({\n        headers: {\n          'Content-Type': 'application/json',\n        },\n        method: 'POST',\n        url: `${baseUrl}/api/system/stop?forceQuit=${_forceQuitCode}`,\n      });\n      request.end();\n    } catch (error) {}\n  }\n});\n\nipcMain.handle('get-product-name', () => {\n  const exePath = app.getPath('exe');\n  const { name } = path.parse(exePath);\n  return name;\n});\n\n// 重启app\nipcMain.on('quit-app', () => {\n  app.relaunch();\n  app.quit();\n});\n\n// 放大或还原窗口\nipcMain.on('set-maximize', () => {\n  if (mainWindow.isMaximized()) {\n    mainWindow.unmaximize();\n  } else {\n    mainWindow.maximize();\n  }\n});\n\nipcMain.on('register-app-menu', (event, orgs) => {\n  registerAppMenu(mainWindow, orgs);\n});\n\nipcMain.on('set-base-url', (event, _baseUrl) => {\n  baseUrl = _baseUrl;\n});\n\nipcMain.on('set-force-quit-code', (event, _forceQuitCode) => {\n  forceQuitCode = _forceQuitCode;\n});\n\nipcMain.on('close-window', () => {\n  mainWindow.close();\n});\n\n// 最小化窗口\nipcMain.on('minimize-window', () => {\n  mainWindow.minimize();\n});\n\n// 获取当前窗口是否是最大化\nipcMain.on('is-maximized', () => {\n  return mainWindow.isMaximized();\n});\n"
  },
  {
    "path": "chat2db-client/src/main/main.js",
    "content": "/*! For license information please see main.js.LICENSE.txt */\n(()=>{var e={6084:(e,t,r)=>{const n=r(2037),{readVersion:s}=r(4288),o=r(7504);e.exports=function(){const e=new o(\"G-V8M4E5SF61\",\"LShbzC_vRka5Sw5AWco7Tw\"),t={platform:\"DESKTOP\",version:s(),os:n.platform()};e.setParams(t).event(\"first_enter\")}},6381:e=>{e.exports={DEV_WEB_URL:\"http://localhost:8000/\",JAVA_APP_NAME:\"chat2db-server-start.jar\",JAVA_PATH:\"jre/bin/java\"}},7504:(e,t,r)=>{const{net:n}=r(2298),{v4:s}=r(2600),{machineIdSync:o}=r(1572),i=r(7377);e.exports=class{constructor(e,t,r=o(),n=s()){this.trackingID=e,this.secretKey=t,this.clientID=r,this.sessionID=n,this.customParams={},this.userProperties=null,this.baseURL=\"https://google-analytics.com/mp\",this.collectURL=\"/collect\"}set(e,t){return null!==t?this.customParams[e]=t:delete this.customParams[e],this}setParams(e){return\"object\"==typeof e&&Object.keys(e).length>0?Object.assign(this.customParams,e):this.customParams={},this}setUserProperties(e){return\"object\"==typeof e&&Object.keys(e).length>0?this.userProperties=e:this.userProperties=null,this}event(e){const t={client_id:this.clientID,events:[{name:e,params:{session_id:this.sessionID,...this.customParams}}]};this.userProperties&&Object.assign(t,{user_properties:this.userProperties});const r=`${this.baseURL}${this.collectURL}?measurement_id=${this.trackingID}&api_secret=${this.secretKey}`,s=n.request({method:\"POST\",url:r});s.on(\"response\",(e=>{let t=\"\";e.on(\"data\",(e=>{t+=e})),e.on(\"end\",(()=>{e.statusCode>=200&&e.statusCode<300?i.info(\"success\",t):i.error(\"response error\",e.statusCode)}))})),s.on(\"error\",(e=>{i.error(\"Error posting data:\",e)})),s.write(JSON.stringify(t)),s.end()}}},6010:(e,t,r)=>{const{app:n,BrowserWindow:s,shell:o,net:i,ipcMain:a,globalShortcut:c}=r(2298),l=r(1017),u=r(5316),d=r(6084),f=r(4234),{loadMainResource:h,isMac:p}=r(4288);let m=null,y=null;function g(){const{width:e,height:t,x:r,y:n}=f.get(\"windowBounds\",{width:1440,height:800}),i={x:r,y:n,height:t,width:e,minWidth:1080,minHeight:720,show:!1,webPreferences:{webSecurity:!1,spellcheck:!1,nodeIntegration:!0,contextIsolation:!0,preload:l.join(__dirname,\"preload.js\")}};m=new s(i),m.show(),h(m),m.webContents.setWindowOpenHandler((({url:e})=>(o.openExternal(e),{action:\"deny\"}))),m.on(\"resize\",(()=>{f.set(\"windowBounds\",m.getBounds())})),m.on(\"move\",(()=>{f.set(\"windowBounds\",m.getBounds())})),c.register(\"CommandOrControl+Shift+I\",(()=>{m.webContents.openDevTools()}))}n.commandLine.appendSwitch(\"--disable-gpu-sandbox\"),n.on(\"ready\",(()=>{g(),u(m),d()})),n.on(\"activate\",(()=>{m?(m.isMinimized()&&m.restore(),m.isVisible()?m.focus():m.show()):g()})),n.on(\"window-all-closed\",(e=>{m=null,p||n.quit()})),n.on(\"before-quit\",(()=>{if(y)try{i.request({headers:{\"Content-Type\":\"application/json\"},method:\"POST\",url:`${y}/api/system/stop?forceQuit=false`}).end()}catch(e){}})),a.handle(\"get-product-name\",(()=>{const e=n.getPath(\"exe\"),{name:t}=l.parse(e);return t})),a.on(\"quit-app\",(()=>{n.relaunch(),n.quit()})),a.on(\"set-maximize\",(()=>{m.isMaximized()?m.unmaximize():m.maximize()})),a.on(\"register-app-menu\",((e,t)=>{u(m,t)})),a.on(\"set-base-url\",((e,t)=>{y=t})),a.on(\"set-force-quit-code\",((e,t)=>{forceQuitCode=t})),a.on(\"close-window\",(()=>{m.close()})),a.on(\"minimize-window\",(()=>{m.minimize()})),a.on(\"is-maximized\",(()=>m.isMaximized()))},5316:(e,t,r)=>{const{shell:n,app:s,dialog:o,BrowserWindow:i,Menu:a}=r(2298),c=r(2037),l=r(1017),{isMac:u}=r(4288);e.exports=(e,t)=>{if(!u)return void a.setApplicationMenu(null);const r=[{label:\"Chat2DB\",submenu:[{label:\"关于Chat2DB\",click(){o.showMessageBox({title:\"关于Chat2DB\",message:`关于Chat2DB v${t?.version||s.getVersion()}`,detail:\"一个集成AI能力的智能数据库客户端和智能BI报表工具。\",icon:\"./logo/icon.png\"})}},{type:\"separator\"},{label:\"重新启动\",click(){s.relaunch(),s.quit()}},{label:\"退出\",accelerator:\"darwin\"===process.platform?\"Cmd+Q\":\"Alt+F4\",click(){s.quit()}}]},{label:\"编辑\",submenu:[{label:\"撤销\",role:\"undo\"},{label:\"重做\",role:\"redo\"},{type:\"separator\"},{label:\"剪切\",role:\"cut\"},{label:\"复制\",role:\"copy\"},{label:\"粘贴\",role:\"paste\"},{label:\"全选\",role:\"selectAll\"}]},{label:\"视图\",submenu:[{type:\"separator\"},{label:\"放大\",accelerator:\"CmdOrCtrl+=\",role:\"zoomIn\"},{label:\"缩小\",accelerator:\"CmdOrCtrl+-\",role:\"zoomOut\"},{label:\"重置\",accelerator:\"CmdOrCtrl+0\",role:\"resetZoom\"},{type:\"separator\"},{label:\"全屏\",role:\"togglefullscreen\"}]},{label:\"窗口\",role:\"window\",submenu:[{label:\"最小化\",role:\"minimize\",accelerator:\"Command+W\"},{label:\"关闭\",role:\"close\"}]},{label:\"帮助\",submenu:[{label:\"打开日志\",accelerator:\"darwin\"===process.platform?\"Cmd+Shift+T\":\"Ctrl+Shift+T\",click(){const e=l.join(c.homedir(),\".chat2db/logs/application.log\");n.openPath(e).then((e=>console.log(\"err:\",e)))}},{label:\"打开控制台\",accelerator:\"darwin\"===process.platform?\"Cmd+Shift+I\":\"Ctrl+Shift+I\",click(){const e=i.getFocusedWindow();e&&e.toggleDevTools()}},{label:\"访问官网\",click(){n.openExternal(\"https://www.sqlgpt.cn/zh\")}},{label:\"查看文档\",click(){n.openExternal(\"https://doc.sqlgpt.cn/zh/\")}},{label:\"查看更新日志\",click(){n.openExternal(\"https://doc.sqlgpt.cn/zh/changelog/\")}}]}];a.setApplicationMenu(a.buildFromTemplate(r))}},6540:(e,t)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.NOOP=t.LIMIT_FILES_DESCRIPTORS=t.LIMIT_BASENAME_LENGTH=t.IS_USER_ROOT=t.IS_POSIX=t.DEFAULT_TIMEOUT_SYNC=t.DEFAULT_TIMEOUT_ASYNC=t.DEFAULT_WRITE_OPTIONS=t.DEFAULT_READ_OPTIONS=t.DEFAULT_FOLDER_MODE=t.DEFAULT_FILE_MODE=t.DEFAULT_ENCODING=void 0,t.DEFAULT_ENCODING=\"utf8\",t.DEFAULT_FILE_MODE=438,t.DEFAULT_FOLDER_MODE=511,t.DEFAULT_READ_OPTIONS={},t.DEFAULT_WRITE_OPTIONS={},t.DEFAULT_TIMEOUT_ASYNC=5e3,t.DEFAULT_TIMEOUT_SYNC=100;const r=!!process.getuid;t.IS_POSIX=r;const n=!!process.getuid&&!process.getuid();t.IS_USER_ROOT=n,t.LIMIT_BASENAME_LENGTH=128,t.LIMIT_FILES_DESCRIPTORS=1e4,t.NOOP=()=>{}},2582:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.writeFileSync=t.writeFile=t.readFileSync=t.readFile=void 0;const n=r(1017),s=r(6540),o=r(1788),i=r(4033),a=r(1729),c=r(2046);t.readFile=function e(t,r=s.DEFAULT_READ_OPTIONS){var n;if(i.default.isString(r))return e(t,{encoding:r});const a=Date.now()+(null!==(n=r.timeout)&&void 0!==n?n:s.DEFAULT_TIMEOUT_ASYNC);return o.default.readFileRetry(a)(t,r)},t.readFileSync=function e(t,r=s.DEFAULT_READ_OPTIONS){var n;if(i.default.isString(r))return e(t,{encoding:r});const a=Date.now()+(null!==(n=r.timeout)&&void 0!==n?n:s.DEFAULT_TIMEOUT_SYNC);return o.default.readFileSyncRetry(a)(t,r)};const l=(e,t,r,n)=>{if(i.default.isFunction(r))return l(e,t,s.DEFAULT_WRITE_OPTIONS,r);const o=u(e,t,r);return n&&o.then(n,n),o};t.writeFile=l;const u=async(e,t,r=s.DEFAULT_WRITE_OPTIONS)=>{var l;if(i.default.isString(r))return u(e,t,{encoding:r});const d=Date.now()+(null!==(l=r.timeout)&&void 0!==l?l:s.DEFAULT_TIMEOUT_ASYNC);let f=null,h=null,p=null,m=null,y=null;try{r.schedule&&(f=await r.schedule(e)),h=await a.default.schedule(e),e=await o.default.realpathAttempt(e)||e,[m,p]=c.default.get(e,r.tmpCreate||c.default.create,!(!1===r.tmpPurge));const l=s.IS_POSIX&&i.default.isUndefined(r.chown),u=i.default.isUndefined(r.mode);if(l||u){const t=await o.default.statAttempt(e);t&&(r={...r},l&&(r.chown={uid:t.uid,gid:t.gid}),u&&(r.mode=t.mode))}const g=n.dirname(e);await o.default.mkdirAttempt(g,{mode:s.DEFAULT_FOLDER_MODE,recursive:!0}),y=await o.default.openRetry(d)(m,\"w\",r.mode||s.DEFAULT_FILE_MODE),r.tmpCreated&&r.tmpCreated(m),i.default.isString(t)?await o.default.writeRetry(d)(y,t,0,r.encoding||s.DEFAULT_ENCODING):i.default.isUndefined(t)||await o.default.writeRetry(d)(y,t,0,t.length,0),!1!==r.fsync&&(!1!==r.fsyncWait?await o.default.fsyncRetry(d)(y):o.default.fsyncAttempt(y)),await o.default.closeRetry(d)(y),y=null,r.chown&&await o.default.chownAttempt(m,r.chown.uid,r.chown.gid),r.mode&&await o.default.chmodAttempt(m,r.mode);try{await o.default.renameRetry(d)(m,e)}catch(t){if(\"ENAMETOOLONG\"!==t.code)throw t;await o.default.renameRetry(d)(m,c.default.truncate(e))}p(),m=null}finally{y&&await o.default.closeAttempt(y),m&&c.default.purge(m),f&&f(),h&&h()}},d=(e,t,r=s.DEFAULT_WRITE_OPTIONS)=>{var a;if(i.default.isString(r))return d(e,t,{encoding:r});const l=Date.now()+(null!==(a=r.timeout)&&void 0!==a?a:s.DEFAULT_TIMEOUT_SYNC);let u=null,f=null,h=null;try{e=o.default.realpathSyncAttempt(e)||e,[f,u]=c.default.get(e,r.tmpCreate||c.default.create,!(!1===r.tmpPurge));const a=s.IS_POSIX&&i.default.isUndefined(r.chown),d=i.default.isUndefined(r.mode);if(a||d){const t=o.default.statSyncAttempt(e);t&&(r={...r},a&&(r.chown={uid:t.uid,gid:t.gid}),d&&(r.mode=t.mode))}const p=n.dirname(e);o.default.mkdirSyncAttempt(p,{mode:s.DEFAULT_FOLDER_MODE,recursive:!0}),h=o.default.openSyncRetry(l)(f,\"w\",r.mode||s.DEFAULT_FILE_MODE),r.tmpCreated&&r.tmpCreated(f),i.default.isString(t)?o.default.writeSyncRetry(l)(h,t,0,r.encoding||s.DEFAULT_ENCODING):i.default.isUndefined(t)||o.default.writeSyncRetry(l)(h,t,0,t.length,0),!1!==r.fsync&&(!1!==r.fsyncWait?o.default.fsyncSyncRetry(l)(h):o.default.fsyncAttempt(h)),o.default.closeSyncRetry(l)(h),h=null,r.chown&&o.default.chownSyncAttempt(f,r.chown.uid,r.chown.gid),r.mode&&o.default.chmodSyncAttempt(f,r.mode);try{o.default.renameSyncRetry(l)(f,e)}catch(t){if(\"ENAMETOOLONG\"!==t.code)throw t;o.default.renameSyncRetry(l)(f,c.default.truncate(e))}u(),f=null}finally{h&&o.default.closeSyncAttempt(h),f&&c.default.purge(f)}};t.writeFileSync=d},7121:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.attemptifySync=t.attemptifyAsync=void 0;const n=r(6540);t.attemptifyAsync=(e,t=n.NOOP)=>function(){return e.apply(void 0,arguments).catch(t)},t.attemptifySync=(e,t=n.NOOP)=>function(){try{return e.apply(void 0,arguments)}catch(e){return t(e)}}},1788:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(7147),s=r(3837),o=r(7121),i=r(1197),a=r(3823),c={chmodAttempt:o.attemptifyAsync(s.promisify(n.chmod),i.default.onChangeError),chownAttempt:o.attemptifyAsync(s.promisify(n.chown),i.default.onChangeError),closeAttempt:o.attemptifyAsync(s.promisify(n.close)),fsyncAttempt:o.attemptifyAsync(s.promisify(n.fsync)),mkdirAttempt:o.attemptifyAsync(s.promisify(n.mkdir)),realpathAttempt:o.attemptifyAsync(s.promisify(n.realpath)),statAttempt:o.attemptifyAsync(s.promisify(n.stat)),unlinkAttempt:o.attemptifyAsync(s.promisify(n.unlink)),closeRetry:a.retryifyAsync(s.promisify(n.close),i.default.isRetriableError),fsyncRetry:a.retryifyAsync(s.promisify(n.fsync),i.default.isRetriableError),openRetry:a.retryifyAsync(s.promisify(n.open),i.default.isRetriableError),readFileRetry:a.retryifyAsync(s.promisify(n.readFile),i.default.isRetriableError),renameRetry:a.retryifyAsync(s.promisify(n.rename),i.default.isRetriableError),statRetry:a.retryifyAsync(s.promisify(n.stat),i.default.isRetriableError),writeRetry:a.retryifyAsync(s.promisify(n.write),i.default.isRetriableError),chmodSyncAttempt:o.attemptifySync(n.chmodSync,i.default.onChangeError),chownSyncAttempt:o.attemptifySync(n.chownSync,i.default.onChangeError),closeSyncAttempt:o.attemptifySync(n.closeSync),mkdirSyncAttempt:o.attemptifySync(n.mkdirSync),realpathSyncAttempt:o.attemptifySync(n.realpathSync),statSyncAttempt:o.attemptifySync(n.statSync),unlinkSyncAttempt:o.attemptifySync(n.unlinkSync),closeSyncRetry:a.retryifySync(n.closeSync,i.default.isRetriableError),fsyncSyncRetry:a.retryifySync(n.fsyncSync,i.default.isRetriableError),openSyncRetry:a.retryifySync(n.openSync,i.default.isRetriableError),readFileSyncRetry:a.retryifySync(n.readFileSync,i.default.isRetriableError),renameSyncRetry:a.retryifySync(n.renameSync,i.default.isRetriableError),statSyncRetry:a.retryifySync(n.statSync,i.default.isRetriableError),writeSyncRetry:a.retryifySync(n.writeSync,i.default.isRetriableError)};t.default=c},1197:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(6540),s={isChangeErrorOk:e=>{const{code:t}=e;return\"ENOSYS\"===t||!(n.IS_USER_ROOT||\"EINVAL\"!==t&&\"EPERM\"!==t)},isRetriableError:e=>{const{code:t}=e;return\"EMFILE\"===t||\"ENFILE\"===t||\"EAGAIN\"===t||\"EBUSY\"===t||\"EACCESS\"===t||\"EACCS\"===t||\"EPERM\"===t},onChangeError:e=>{if(!s.isChangeErrorOk(e))throw e}};t.default=s},4033:(e,t)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default={isFunction:e=>\"function\"==typeof e,isString:e=>\"string\"==typeof e,isUndefined:e=>void 0===e}},3823:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.retryifySync=t.retryifyAsync=void 0;const n=r(2068);t.retryifyAsync=(e,t)=>function(r){return function s(){return n.default.schedule().then((n=>e.apply(void 0,arguments).then((e=>(n(),e)),(e=>{if(n(),Date.now()>=r)throw e;if(t(e)){const e=Math.round(100+400*Math.random());return new Promise((t=>setTimeout(t,e))).then((()=>s.apply(void 0,arguments)))}throw e}))))}},t.retryifySync=(e,t)=>function(r){return function n(){try{return e.apply(void 0,arguments)}catch(e){if(Date.now()>r)throw e;if(t(e))return n.apply(void 0,arguments);throw e}}}},2068:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n={interval:25,intervalId:void 0,limit:r(6540).LIMIT_FILES_DESCRIPTORS,queueActive:new Set,queueWaiting:new Set,init:()=>{n.intervalId||(n.intervalId=setInterval(n.tick,n.interval))},reset:()=>{n.intervalId&&(clearInterval(n.intervalId),delete n.intervalId)},add:e=>{n.queueWaiting.add(e),n.queueActive.size<n.limit/2?n.tick():n.init()},remove:e=>{n.queueWaiting.delete(e),n.queueActive.delete(e)},schedule:()=>new Promise((e=>{const t=()=>n.remove(r),r=()=>e(t);n.add(r)})),tick:()=>{if(!(n.queueActive.size>=n.limit)){if(!n.queueWaiting.size)return n.reset();for(const e of n.queueWaiting){if(n.queueActive.size>=n.limit)break;n.queueWaiting.delete(e),n.queueActive.add(e),e()}}}};t.default=n},1729:(e,t)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const r={},n={next:e=>{const t=r[e];if(!t)return;t.shift();const s=t[0];s?s((()=>n.next(e))):delete r[e]},schedule:e=>new Promise((t=>{let s=r[e];s||(s=r[e]=[]),s.push(t),s.length>1||t((()=>n.next(e)))}))};t.default=n},2046:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(1017),s=r(6540),o=r(1788),i={store:{},create:e=>{const t=`000000${Math.floor(16777215*Math.random()).toString(16)}`.slice(-6);return`${e}.tmp-${Date.now().toString().slice(-10)}${t}`},get:(e,t,r=!0)=>{const n=i.truncate(t(e));return n in i.store?i.get(e,t,r):(i.store[n]=r,[n,()=>delete i.store[n]])},purge:e=>{i.store[e]&&(delete i.store[e],o.default.unlinkAttempt(e))},purgeSync:e=>{i.store[e]&&(delete i.store[e],o.default.unlinkSyncAttempt(e))},purgeSyncAll:()=>{for(const e in i.store)i.purgeSync(e)},truncate:e=>{const t=n.basename(e);if(t.length<=s.LIMIT_BASENAME_LENGTH)return e;const r=/^(\\.?)(.*?)((?:\\.[^.]+)?(?:\\.tmp-\\d{10}[a-f0-9]{6})?)$/.exec(t);if(!r)return e;const o=t.length-s.LIMIT_BASENAME_LENGTH;return`${e.slice(0,-t.length)}${r[1]}${r[2].slice(0,-o)}${r[3]}`}};process.on(\"exit\",i.purgeSyncAll),t.default=i},9658:function(e,t,r){\"use strict\";e=r.nmd(e);var n,s,o,i,a,c,l=this&&this.__classPrivateFieldSet||function(e,t,r,n,s){if(\"m\"===n)throw new TypeError(\"Private method is not writable\");if(\"a\"===n&&!s)throw new TypeError(\"Private accessor was defined without a setter\");if(\"function\"==typeof t?e!==t||!s:!t.has(e))throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");return\"a\"===n?s.call(e,r):s?s.value=r:t.set(e,r),r},u=this&&this.__classPrivateFieldGet||function(e,t,r,n){if(\"a\"===r&&!n)throw new TypeError(\"Private accessor was defined without a getter\");if(\"function\"==typeof t?e!==t||!n:!t.has(e))throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");return\"m\"===r?n:\"a\"===r?n.call(e):n?n.value:t.get(e)};Object.defineProperty(t,\"__esModule\",{value:!0});const d=r(3837),f=r(7147),h=r(1017),p=r(6113),m=r(9491),y=r(2361),g=r(3517),v=r(8866),w=r(1766),E=r(2582),$=r(5133),_=r(6838),b=r(7319),S=r(1249),O=r(7678),P=\"aes-256-cbc\",x=()=>Object.create(null);let N=\"\";try{delete r.c[__filename],N=h.dirname(null!==(s=null===(n=e.parent)||void 0===n?void 0:n.filename)&&void 0!==s?s:\".\")}catch(e){}const I=\"__internal__\",j=`${I}.migrations.version`;class R{constructor(e={}){var t;o.set(this,void 0),i.set(this,void 0),a.set(this,void 0),c.set(this,{}),this._deserialize=e=>JSON.parse(e),this._serialize=e=>JSON.stringify(e,void 0,\"\\t\");const r={configName:\"config\",fileExtension:\"json\",projectSuffix:\"nodejs\",clearInvalidConfig:!1,accessPropertiesByDotNotation:!0,configFileMode:438,...e},n=O((()=>{const e=v.sync({cwd:N}),t=e&&JSON.parse(f.readFileSync(e,\"utf8\"));return null!=t?t:{}}));if(!r.cwd){if(r.projectName||(r.projectName=n().name),!r.projectName)throw new Error(\"Project name could not be inferred. Please specify the `projectName` option.\");r.cwd=w(r.projectName,{suffix:r.projectSuffix}).config}if(l(this,a,r,\"f\"),r.schema){if(\"object\"!=typeof r.schema)throw new TypeError(\"The `schema` option must be an object.\");const e=new $.default({allErrors:!0,useDefaults:!0});(0,_.default)(e);const t={type:\"object\",properties:r.schema};l(this,o,e.compile(t),\"f\");for(const[e,t]of Object.entries(r.schema))(null==t?void 0:t.default)&&(u(this,c,\"f\")[e]=t.default)}r.defaults&&l(this,c,{...u(this,c,\"f\"),...r.defaults},\"f\"),r.serialize&&(this._serialize=r.serialize),r.deserialize&&(this._deserialize=r.deserialize),this.events=new y.EventEmitter,l(this,i,r.encryptionKey,\"f\");const s=r.fileExtension?`.${r.fileExtension}`:\"\";this.path=h.resolve(r.cwd,`${null!==(t=r.configName)&&void 0!==t?t:\"config\"}${s}`);const d=this.store,p=Object.assign(x(),r.defaults,d);this._validate(p);try{m.deepEqual(d,p)}catch(e){this.store=p}if(r.watch&&this._watch(),r.migrations){if(r.projectVersion||(r.projectVersion=n().version),!r.projectVersion)throw new Error(\"Project version could not be inferred. Please specify the `projectVersion` option.\");this._migrate(r.migrations,r.projectVersion,r.beforeEachMigration)}}get(e,t){if(u(this,a,\"f\").accessPropertiesByDotNotation)return this._get(e,t);const{store:r}=this;return e in r?r[e]:t}set(e,t){if(\"string\"!=typeof e&&\"object\"!=typeof e)throw new TypeError(\"Expected `key` to be of type `string` or `object`, got \"+typeof e);if(\"object\"!=typeof e&&void 0===t)throw new TypeError(\"Use `delete()` to clear values\");if(this._containsReservedKey(e))throw new TypeError(`Please don't use the ${I} key, as it's used to manage this module internal operations.`);const{store:r}=this,n=(e,t)=>{((e,t)=>{const r=typeof t;if(new Set([\"undefined\",\"symbol\",\"function\"]).has(r))throw new TypeError(`Setting a value of type \\`${r}\\` for key \\`${e}\\` is not allowed as it's not supported by JSON`)})(e,t),u(this,a,\"f\").accessPropertiesByDotNotation?g.set(r,e,t):r[e]=t};if(\"object\"==typeof e){const t=e;for(const[e,r]of Object.entries(t))n(e,r)}else n(e,t);this.store=r}has(e){return u(this,a,\"f\").accessPropertiesByDotNotation?g.has(this.store,e):e in this.store}reset(...e){for(const t of e)null!=u(this,c,\"f\")[t]&&this.set(t,u(this,c,\"f\")[t])}delete(e){const{store:t}=this;u(this,a,\"f\").accessPropertiesByDotNotation?g.delete(t,e):delete t[e],this.store=t}clear(){this.store=x();for(const e of Object.keys(u(this,c,\"f\")))this.reset(e)}onDidChange(e,t){if(\"string\"!=typeof e)throw new TypeError(\"Expected `key` to be of type `string`, got \"+typeof e);if(\"function\"!=typeof t)throw new TypeError(\"Expected `callback` to be of type `function`, got \"+typeof t);return this._handleChange((()=>this.get(e)),t)}onDidAnyChange(e){if(\"function\"!=typeof e)throw new TypeError(\"Expected `callback` to be of type `function`, got \"+typeof e);return this._handleChange((()=>this.store),e)}get size(){return Object.keys(this.store).length}get store(){try{const e=f.readFileSync(this.path,u(this,i,\"f\")?null:\"utf8\"),t=this._encryptData(e),r=this._deserialize(t);return this._validate(r),Object.assign(x(),r)}catch(e){if(\"ENOENT\"===(null==e?void 0:e.code))return this._ensureDirectory(),x();if(u(this,a,\"f\").clearInvalidConfig&&\"SyntaxError\"===e.name)return x();throw e}}set store(e){this._ensureDirectory(),this._validate(e),this._write(e),this.events.emit(\"change\")}*[(o=new WeakMap,i=new WeakMap,a=new WeakMap,c=new WeakMap,Symbol.iterator)](){for(const[e,t]of Object.entries(this.store))yield[e,t]}_encryptData(e){if(!u(this,i,\"f\"))return e.toString();try{if(u(this,i,\"f\"))try{if(\":\"===e.slice(16,17).toString()){const t=e.slice(0,16),r=p.pbkdf2Sync(u(this,i,\"f\"),t.toString(),1e4,32,\"sha512\"),n=p.createDecipheriv(P,r,t);e=Buffer.concat([n.update(Buffer.from(e.slice(17))),n.final()]).toString(\"utf8\")}else{const t=p.createDecipher(P,u(this,i,\"f\"));e=Buffer.concat([t.update(Buffer.from(e)),t.final()]).toString(\"utf8\")}}catch(e){}}catch(e){}return e.toString()}_handleChange(e,t){let r=e();const n=()=>{const n=r,s=e();(0,d.isDeepStrictEqual)(s,n)||(r=s,t.call(this,s,n))};return this.events.on(\"change\",n),()=>this.events.removeListener(\"change\",n)}_validate(e){if(!u(this,o,\"f\"))return;if(u(this,o,\"f\").call(this,e)||!u(this,o,\"f\").errors)return;const t=u(this,o,\"f\").errors.map((({instancePath:e,message:t=\"\"})=>`\\`${e.slice(1)}\\` ${t}`));throw new Error(\"Config schema violation: \"+t.join(\"; \"))}_ensureDirectory(){f.mkdirSync(h.dirname(this.path),{recursive:!0})}_write(e){let t=this._serialize(e);if(u(this,i,\"f\")){const e=p.randomBytes(16),r=p.pbkdf2Sync(u(this,i,\"f\"),e.toString(),1e4,32,\"sha512\"),n=p.createCipheriv(P,r,e);t=Buffer.concat([e,Buffer.from(\":\"),n.update(Buffer.from(t)),n.final()])}if(process.env.SNAP)f.writeFileSync(this.path,t,{mode:u(this,a,\"f\").configFileMode});else try{E.writeFileSync(this.path,t,{mode:u(this,a,\"f\").configFileMode})}catch(e){if(\"EXDEV\"===(null==e?void 0:e.code))return void f.writeFileSync(this.path,t,{mode:u(this,a,\"f\").configFileMode});throw e}}_watch(){this._ensureDirectory(),f.existsSync(this.path)||this._write(x()),\"win32\"===process.platform?f.watch(this.path,{persistent:!1},b((()=>{this.events.emit(\"change\")}),{wait:100})):f.watchFile(this.path,{persistent:!1},b((()=>{this.events.emit(\"change\")}),{wait:5e3}))}_migrate(e,t,r){let n=this._get(j,\"0.0.0\");const s=Object.keys(e).filter((e=>this._shouldPerformMigration(e,n,t)));let o={...this.store};for(const i of s)try{r&&r(this,{fromVersion:n,toVersion:i,finalVersion:t,versions:s}),(0,e[i])(this),this._set(j,i),n=i,o={...this.store}}catch(e){throw this.store=o,new Error(`Something went wrong during the migration! Changes applied to the store until this failed migration will be restored. ${e}`)}!this._isVersionInRangeFormat(n)&&S.eq(n,t)||this._set(j,t)}_containsReservedKey(e){return\"object\"==typeof e&&Object.keys(e)[0]===I||\"string\"==typeof e&&!!u(this,a,\"f\").accessPropertiesByDotNotation&&!!e.startsWith(`${I}.`)}_isVersionInRangeFormat(e){return null===S.clean(e)}_shouldPerformMigration(e,t,r){return this._isVersionInRangeFormat(e)?(\"0.0.0\"===t||!S.satisfies(t,e))&&S.satisfies(r,e):!S.lte(e,t)&&!S.gt(e,r)}_get(e,t){return g.get(this.store,e,t)}_set(e,t){const{store:r}=this;g.set(r,e,t),this.store=r}}t.default=R,e.exports=R,e.exports.default=R},5583:(e,t)=>{\"use strict\";function r(e,t){return{validate:e,compare:t}}Object.defineProperty(t,\"__esModule\",{value:!0}),t.formatNames=t.fastFormats=t.fullFormats=void 0,t.fullFormats={date:r(o,i),time:r(c,l),\"date-time\":r((function(e){const t=e.split(u);return 2===t.length&&o(t[0])&&c(t[1],!0)}),d),duration:/^P(?!$)((\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+S)?)?|(\\d+W)?)$/,uri:function(e){return f.test(e)&&h.test(e)},\"uri-reference\":/^(?:[a-z][a-z0-9+\\-.]*:)?(?:\\/?\\/(?:(?:[a-z0-9\\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\\.[a-z0-9\\-._~!$&'()*+,;=:]+)\\]|(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)|(?:[a-z0-9\\-._~!$&'\"()*+,;=]|%[0-9a-f]{2})*)(?::\\d*)?(?:\\/(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})*)*|\\/(?:(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})+(?:\\/(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})+(?:\\/(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\\?(?:[a-z0-9\\-._~!$&'\"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\\-._~!$&'\"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,\"uri-template\":/^(?:(?:[^\\x00-\\x20\"'<>%\\\\^`{|}]|%[0-9a-f]{2})|\\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\\*)?)*\\})*$/i,url:/^(?:https?|ftp):\\/\\/(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)(?:\\.(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)*(?:\\.(?:[a-z\\u{00a1}-\\u{ffff}]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$/iu,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:/^(?=.{1,253}\\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\\.?$/i,ipv4:/^(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$/,ipv6:/^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))$/i,regex:function(e){if(v.test(e))return!1;try{return new RegExp(e),!0}catch(e){return!1}},uuid:/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,\"json-pointer\":/^(?:\\/(?:[^~/]|~0|~1)*)*$/,\"json-pointer-uri-fragment\":/^#(?:\\/(?:[a-z0-9_\\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,\"relative-json-pointer\":/^(?:0|[1-9][0-9]*)(?:#|(?:\\/(?:[^~/]|~0|~1)*)*)$/,byte:function(e){return p.lastIndex=0,p.test(e)},int32:{type:\"number\",validate:function(e){return Number.isInteger(e)&&e<=y&&e>=m}},int64:{type:\"number\",validate:function(e){return Number.isInteger(e)}},float:{type:\"number\",validate:g},double:{type:\"number\",validate:g},password:!0,binary:!0},t.fastFormats={...t.fullFormats,date:r(/^\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d$/,i),time:r(/^(?:[0-2]\\d:[0-5]\\d:[0-5]\\d|23:59:60)(?:\\.\\d+)?(?:z|[+-]\\d\\d(?::?\\d\\d)?)?$/i,l),\"date-time\":r(/^\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d[t\\s](?:[0-2]\\d:[0-5]\\d:[0-5]\\d|23:59:60)(?:\\.\\d+)?(?:z|[+-]\\d\\d(?::?\\d\\d)?)$/i,d),uri:/^(?:[a-z][a-z0-9+\\-.]*:)(?:\\/?\\/)?[^\\s]*$/i,\"uri-reference\":/^(?:(?:[a-z][a-z0-9+\\-.]*:)?\\/?\\/)?(?:[^\\\\\\s#][^\\s#]*)?(?:#[^\\\\\\s]*)?$/i,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i},t.formatNames=Object.keys(t.fullFormats);const n=/^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)$/,s=[0,31,28,31,30,31,30,31,31,30,31,30,31];function o(e){const t=n.exec(e);if(!t)return!1;const r=+t[1],o=+t[2],i=+t[3];return o>=1&&o<=12&&i>=1&&i<=(2===o&&function(e){return e%4==0&&(e%100!=0||e%400==0)}(r)?29:s[o])}function i(e,t){if(e&&t)return e>t?1:e<t?-1:0}const a=/^(\\d\\d):(\\d\\d):(\\d\\d)(\\.\\d+)?(z|[+-]\\d\\d(?::?\\d\\d)?)?$/i;function c(e,t){const r=a.exec(e);if(!r)return!1;const n=+r[1],s=+r[2],o=+r[3],i=r[5];return(n<=23&&s<=59&&o<=59||23===n&&59===s&&60===o)&&(!t||\"\"!==i)}function l(e,t){if(!e||!t)return;const r=a.exec(e),n=a.exec(t);return r&&n?(e=r[1]+r[2]+r[3]+(r[4]||\"\"))>(t=n[1]+n[2]+n[3]+(n[4]||\"\"))?1:e<t?-1:0:void 0}const u=/t|\\s/i;function d(e,t){if(!e||!t)return;const[r,n]=e.split(u),[s,o]=t.split(u),a=i(r,s);return void 0!==a?a||l(n,o):void 0}const f=/\\/|:/,h=/^(?:[a-z][a-z0-9+\\-.]*:)(?:\\/?\\/(?:(?:[a-z0-9\\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\\.[a-z0-9\\-._~!$&'()*+,;=:]+)\\]|(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)|(?:[a-z0-9\\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\\d*)?(?:\\/(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\\/(?:(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\\/(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\\/(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\\?(?:[a-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,p=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm,m=-(2**31),y=2**31-1;function g(){return!0}const v=/[^\\\\]\\\\Z/},6838:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5583),s=r(1984),o=r(5899),i=new o.Name(\"fullFormats\"),a=new o.Name(\"fastFormats\"),c=(e,t={keywords:!0})=>{if(Array.isArray(t))return l(e,t,n.fullFormats,i),e;const[r,o]=\"fast\"===t.mode?[n.fastFormats,a]:[n.fullFormats,i];return l(e,t.formats||n.formatNames,r,o),t.keywords&&s.default(e),e};function l(e,t,r,n){var s,i;null!==(s=(i=e.opts.code).formats)&&void 0!==s||(i.formats=o._`require(\"ajv-formats/dist/formats\").${n}`);for(const n of t)e.addFormat(n,r[n])}c.get=(e,t=\"full\")=>{const r=(\"fast\"===t?n.fastFormats:n.fullFormats)[e];if(!r)throw new Error(`Unknown format \"${e}\"`);return r},e.exports=t=c,Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=c},1984:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.formatLimitDefinition=void 0;const n=r(5133),s=r(5899),o=s.operators,i={formatMaximum:{okStr:\"<=\",ok:o.LTE,fail:o.GT},formatMinimum:{okStr:\">=\",ok:o.GTE,fail:o.LT},formatExclusiveMaximum:{okStr:\"<\",ok:o.LT,fail:o.GTE},formatExclusiveMinimum:{okStr:\">\",ok:o.GT,fail:o.LTE}},a={message:({keyword:e,schemaCode:t})=>s.str`should be ${i[e].okStr} ${t}`,params:({keyword:e,schemaCode:t})=>s._`{comparison: ${i[e].okStr}, limit: ${t}}`};t.formatLimitDefinition={keyword:Object.keys(i),type:\"string\",schemaType:\"string\",$data:!0,error:a,code(e){const{gen:t,data:r,schemaCode:o,keyword:a,it:c}=e,{opts:l,self:u}=c;if(!l.validateFormats)return;const d=new n.KeywordCxt(c,u.RULES.all.format.definition,\"format\");function f(e){return s._`${e}.compare(${r}, ${o}) ${i[a].fail} 0`}d.$data?function(){const r=t.scopeValue(\"formats\",{ref:u.formats,code:l.code.formats}),n=t.const(\"fmt\",s._`${r}[${d.schemaCode}]`);e.fail$data(s.or(s._`typeof ${n} != \"object\"`,s._`${n} instanceof RegExp`,s._`typeof ${n}.compare != \"function\"`,f(n)))}():function(){const r=d.schema,n=u.formats[r];if(!n||!0===n)return;if(\"object\"!=typeof n||n instanceof RegExp||\"function\"!=typeof n.compare)throw new Error(`\"${a}\": format \"${r}\" does not define \"compare\" function`);const o=t.scopeValue(\"formats\",{key:r,ref:n,code:l.code.formats?s._`${l.code.formats}${s.getProperty(r)}`:void 0});e.fail$data(f(o))}()},dependencies:[\"format\"]},t.default=e=>(e.addKeyword(t.formatLimitDefinition),e)},5133:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.MissingRefError=t.ValidationError=t.CodeGen=t.Name=t.nil=t.stringify=t.str=t._=t.KeywordCxt=void 0;const n=r(3579),s=r(6568),o=r(8402),i=r(115),a=[\"/properties\"],c=\"http://json-schema.org/draft-07/schema\";class l extends n.default{_addVocabularies(){super._addVocabularies(),s.default.forEach((e=>this.addVocabulary(e))),this.opts.discriminator&&this.addKeyword(o.default)}_addDefaultMetaSchema(){if(super._addDefaultMetaSchema(),!this.opts.meta)return;const e=this.opts.$data?this.$dataMetaSchema(i,a):i;this.addMetaSchema(e,c,!1),this.refs[\"http://json-schema.org/schema\"]=c}defaultMeta(){return this.opts.defaultMeta=super.defaultMeta()||(this.getSchema(c)?c:void 0)}}e.exports=t=l,Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=l;var u=r(5032);Object.defineProperty(t,\"KeywordCxt\",{enumerable:!0,get:function(){return u.KeywordCxt}});var d=r(5899);Object.defineProperty(t,\"_\",{enumerable:!0,get:function(){return d._}}),Object.defineProperty(t,\"str\",{enumerable:!0,get:function(){return d.str}}),Object.defineProperty(t,\"stringify\",{enumerable:!0,get:function(){return d.stringify}}),Object.defineProperty(t,\"nil\",{enumerable:!0,get:function(){return d.nil}}),Object.defineProperty(t,\"Name\",{enumerable:!0,get:function(){return d.Name}}),Object.defineProperty(t,\"CodeGen\",{enumerable:!0,get:function(){return d.CodeGen}});var f=r(7173);Object.defineProperty(t,\"ValidationError\",{enumerable:!0,get:function(){return f.default}});var h=r(433);Object.defineProperty(t,\"MissingRefError\",{enumerable:!0,get:function(){return h.default}})},6796:(e,t)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.regexpCode=t.getEsmExportName=t.getProperty=t.safeStringify=t.stringify=t.strConcat=t.addCodeArg=t.str=t._=t.nil=t._Code=t.Name=t.IDENTIFIER=t._CodeOrName=void 0;class r{}t._CodeOrName=r,t.IDENTIFIER=/^[a-z$_][a-z$_0-9]*$/i;class n extends r{constructor(e){if(super(),!t.IDENTIFIER.test(e))throw new Error(\"CodeGen: name must be a valid identifier\");this.str=e}toString(){return this.str}emptyStr(){return!1}get names(){return{[this.str]:1}}}t.Name=n;class s extends r{constructor(e){super(),this._items=\"string\"==typeof e?[e]:e}toString(){return this.str}emptyStr(){if(this._items.length>1)return!1;const e=this._items[0];return\"\"===e||'\"\"'===e}get str(){var e;return null!==(e=this._str)&&void 0!==e?e:this._str=this._items.reduce(((e,t)=>`${e}${t}`),\"\")}get names(){var e;return null!==(e=this._names)&&void 0!==e?e:this._names=this._items.reduce(((e,t)=>(t instanceof n&&(e[t.str]=(e[t.str]||0)+1),e)),{})}}function o(e,...t){const r=[e[0]];let n=0;for(;n<t.length;)c(r,t[n]),r.push(e[++n]);return new s(r)}t._Code=s,t.nil=new s(\"\"),t._=o;const i=new s(\"+\");function a(e,...t){const r=[u(e[0])];let n=0;for(;n<t.length;)r.push(i),c(r,t[n]),r.push(i,u(e[++n]));return function(e){let t=1;for(;t<e.length-1;){if(e[t]===i){const r=l(e[t-1],e[t+1]);if(void 0!==r){e.splice(t-1,3,r);continue}e[t++]=\"+\"}t++}}(r),new s(r)}function c(e,t){var r;t instanceof s?e.push(...t._items):t instanceof n?e.push(t):e.push(\"number\"==typeof(r=t)||\"boolean\"==typeof r||null===r?r:u(Array.isArray(r)?r.join(\",\"):r))}function l(e,t){if('\"\"'===t)return e;if('\"\"'===e)return t;if(\"string\"==typeof e){if(t instanceof n||'\"'!==e[e.length-1])return;return\"string\"!=typeof t?`${e.slice(0,-1)}${t}\"`:'\"'===t[0]?e.slice(0,-1)+t.slice(1):void 0}return\"string\"!=typeof t||'\"'!==t[0]||e instanceof n?void 0:`\"${e}${t.slice(1)}`}function u(e){return JSON.stringify(e).replace(/\\u2028/g,\"\\\\u2028\").replace(/\\u2029/g,\"\\\\u2029\")}t.str=a,t.addCodeArg=c,t.strConcat=function(e,t){return t.emptyStr()?e:e.emptyStr()?t:a`${e}${t}`},t.stringify=function(e){return new s(u(e))},t.safeStringify=u,t.getProperty=function(e){return\"string\"==typeof e&&t.IDENTIFIER.test(e)?new s(`.${e}`):o`[${e}]`},t.getEsmExportName=function(e){if(\"string\"==typeof e&&t.IDENTIFIER.test(e))return new s(`${e}`);throw new Error(`CodeGen: invalid export name: ${e}, use explicit $id name mapping`)},t.regexpCode=function(e){return new s(e.toString())}},5899:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.or=t.and=t.not=t.CodeGen=t.operators=t.varKinds=t.ValueScopeName=t.ValueScope=t.Scope=t.Name=t.regexpCode=t.stringify=t.getProperty=t.nil=t.strConcat=t.str=t._=void 0;const n=r(6796),s=r(2321);var o=r(6796);Object.defineProperty(t,\"_\",{enumerable:!0,get:function(){return o._}}),Object.defineProperty(t,\"str\",{enumerable:!0,get:function(){return o.str}}),Object.defineProperty(t,\"strConcat\",{enumerable:!0,get:function(){return o.strConcat}}),Object.defineProperty(t,\"nil\",{enumerable:!0,get:function(){return o.nil}}),Object.defineProperty(t,\"getProperty\",{enumerable:!0,get:function(){return o.getProperty}}),Object.defineProperty(t,\"stringify\",{enumerable:!0,get:function(){return o.stringify}}),Object.defineProperty(t,\"regexpCode\",{enumerable:!0,get:function(){return o.regexpCode}}),Object.defineProperty(t,\"Name\",{enumerable:!0,get:function(){return o.Name}});var i=r(2321);Object.defineProperty(t,\"Scope\",{enumerable:!0,get:function(){return i.Scope}}),Object.defineProperty(t,\"ValueScope\",{enumerable:!0,get:function(){return i.ValueScope}}),Object.defineProperty(t,\"ValueScopeName\",{enumerable:!0,get:function(){return i.ValueScopeName}}),Object.defineProperty(t,\"varKinds\",{enumerable:!0,get:function(){return i.varKinds}}),t.operators={GT:new n._Code(\">\"),GTE:new n._Code(\">=\"),LT:new n._Code(\"<\"),LTE:new n._Code(\"<=\"),EQ:new n._Code(\"===\"),NEQ:new n._Code(\"!==\"),NOT:new n._Code(\"!\"),OR:new n._Code(\"||\"),AND:new n._Code(\"&&\"),ADD:new n._Code(\"+\")};class a{optimizeNodes(){return this}optimizeNames(e,t){return this}}class c extends a{constructor(e,t,r){super(),this.varKind=e,this.name=t,this.rhs=r}render({es5:e,_n:t}){const r=e?s.varKinds.var:this.varKind,n=void 0===this.rhs?\"\":` = ${this.rhs}`;return`${r} ${this.name}${n};`+t}optimizeNames(e,t){if(e[this.name.str])return this.rhs&&(this.rhs=R(this.rhs,e,t)),this}get names(){return this.rhs instanceof n._CodeOrName?this.rhs.names:{}}}class l extends a{constructor(e,t,r){super(),this.lhs=e,this.rhs=t,this.sideEffects=r}render({_n:e}){return`${this.lhs} = ${this.rhs};`+e}optimizeNames(e,t){if(!(this.lhs instanceof n.Name)||e[this.lhs.str]||this.sideEffects)return this.rhs=R(this.rhs,e,t),this}get names(){return j(this.lhs instanceof n.Name?{}:{...this.lhs.names},this.rhs)}}class u extends l{constructor(e,t,r,n){super(e,r,n),this.op=t}render({_n:e}){return`${this.lhs} ${this.op}= ${this.rhs};`+e}}class d extends a{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`${this.label}:`+e}}class f extends a{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`break${this.label?` ${this.label}`:\"\"};`+e}}class h extends a{constructor(e){super(),this.error=e}render({_n:e}){return`throw ${this.error};`+e}get names(){return this.error.names}}class p extends a{constructor(e){super(),this.code=e}render({_n:e}){return`${this.code};`+e}optimizeNodes(){return`${this.code}`?this:void 0}optimizeNames(e,t){return this.code=R(this.code,e,t),this}get names(){return this.code instanceof n._CodeOrName?this.code.names:{}}}class m extends a{constructor(e=[]){super(),this.nodes=e}render(e){return this.nodes.reduce(((t,r)=>t+r.render(e)),\"\")}optimizeNodes(){const{nodes:e}=this;let t=e.length;for(;t--;){const r=e[t].optimizeNodes();Array.isArray(r)?e.splice(t,1,...r):r?e[t]=r:e.splice(t,1)}return e.length>0?this:void 0}optimizeNames(e,t){const{nodes:r}=this;let n=r.length;for(;n--;){const s=r[n];s.optimizeNames(e,t)||(A(e,s.names),r.splice(n,1))}return r.length>0?this:void 0}get names(){return this.nodes.reduce(((e,t)=>I(e,t.names)),{})}}class y extends m{render(e){return\"{\"+e._n+super.render(e)+\"}\"+e._n}}class g extends m{}class v extends y{}v.kind=\"else\";class w extends y{constructor(e,t){super(t),this.condition=e}render(e){let t=`if(${this.condition})`+super.render(e);return this.else&&(t+=\"else \"+this.else.render(e)),t}optimizeNodes(){super.optimizeNodes();const e=this.condition;if(!0===e)return this.nodes;let t=this.else;if(t){const e=t.optimizeNodes();t=this.else=Array.isArray(e)?new v(e):e}return t?!1===e?t instanceof w?t:t.nodes:this.nodes.length?this:new w(T(e),t instanceof w?[t]:t.nodes):!1!==e&&this.nodes.length?this:void 0}optimizeNames(e,t){var r;if(this.else=null===(r=this.else)||void 0===r?void 0:r.optimizeNames(e,t),super.optimizeNames(e,t)||this.else)return this.condition=R(this.condition,e,t),this}get names(){const e=super.names;return j(e,this.condition),this.else&&I(e,this.else.names),e}}w.kind=\"if\";class E extends y{}E.kind=\"for\";class $ extends E{constructor(e){super(),this.iteration=e}render(e){return`for(${this.iteration})`+super.render(e)}optimizeNames(e,t){if(super.optimizeNames(e,t))return this.iteration=R(this.iteration,e,t),this}get names(){return I(super.names,this.iteration.names)}}class _ extends E{constructor(e,t,r,n){super(),this.varKind=e,this.name=t,this.from=r,this.to=n}render(e){const t=e.es5?s.varKinds.var:this.varKind,{name:r,from:n,to:o}=this;return`for(${t} ${r}=${n}; ${r}<${o}; ${r}++)`+super.render(e)}get names(){const e=j(super.names,this.from);return j(e,this.to)}}class b extends E{constructor(e,t,r,n){super(),this.loop=e,this.varKind=t,this.name=r,this.iterable=n}render(e){return`for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})`+super.render(e)}optimizeNames(e,t){if(super.optimizeNames(e,t))return this.iterable=R(this.iterable,e,t),this}get names(){return I(super.names,this.iterable.names)}}class S extends y{constructor(e,t,r){super(),this.name=e,this.args=t,this.async=r}render(e){return`${this.async?\"async \":\"\"}function ${this.name}(${this.args})`+super.render(e)}}S.kind=\"func\";class O extends m{render(e){return\"return \"+super.render(e)}}O.kind=\"return\";class P extends y{render(e){let t=\"try\"+super.render(e);return this.catch&&(t+=this.catch.render(e)),this.finally&&(t+=this.finally.render(e)),t}optimizeNodes(){var e,t;return super.optimizeNodes(),null===(e=this.catch)||void 0===e||e.optimizeNodes(),null===(t=this.finally)||void 0===t||t.optimizeNodes(),this}optimizeNames(e,t){var r,n;return super.optimizeNames(e,t),null===(r=this.catch)||void 0===r||r.optimizeNames(e,t),null===(n=this.finally)||void 0===n||n.optimizeNames(e,t),this}get names(){const e=super.names;return this.catch&&I(e,this.catch.names),this.finally&&I(e,this.finally.names),e}}class x extends y{constructor(e){super(),this.error=e}render(e){return`catch(${this.error})`+super.render(e)}}x.kind=\"catch\";class N extends y{render(e){return\"finally\"+super.render(e)}}function I(e,t){for(const r in t)e[r]=(e[r]||0)+(t[r]||0);return e}function j(e,t){return t instanceof n._CodeOrName?I(e,t.names):e}function R(e,t,r){return e instanceof n.Name?o(e):(s=e)instanceof n._Code&&s._items.some((e=>e instanceof n.Name&&1===t[e.str]&&void 0!==r[e.str]))?new n._Code(e._items.reduce(((e,t)=>(t instanceof n.Name&&(t=o(t)),t instanceof n._Code?e.push(...t._items):e.push(t),e)),[])):e;var s;function o(e){const n=r[e.str];return void 0===n||1!==t[e.str]?e:(delete t[e.str],n)}}function A(e,t){for(const r in t)e[r]=(e[r]||0)-(t[r]||0)}function T(e){return\"boolean\"==typeof e||\"number\"==typeof e||null===e?!e:n._`!${L(e)}`}N.kind=\"finally\",t.CodeGen=class{constructor(e,t={}){this._values={},this._blockStarts=[],this._constants={},this.opts={...t,_n:t.lines?\"\\n\":\"\"},this._extScope=e,this._scope=new s.Scope({parent:e}),this._nodes=[new g]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){return this._extScope.name(e)}scopeValue(e,t){const r=this._extScope.value(e,t);return(this._values[r.prefix]||(this._values[r.prefix]=new Set)).add(r),r}getScopeValue(e,t){return this._extScope.getValue(e,t)}scopeRefs(e){return this._extScope.scopeRefs(e,this._values)}scopeCode(){return this._extScope.scopeCode(this._values)}_def(e,t,r,n){const s=this._scope.toName(t);return void 0!==r&&n&&(this._constants[s.str]=r),this._leafNode(new c(e,s,r)),s}const(e,t,r){return this._def(s.varKinds.const,e,t,r)}let(e,t,r){return this._def(s.varKinds.let,e,t,r)}var(e,t,r){return this._def(s.varKinds.var,e,t,r)}assign(e,t,r){return this._leafNode(new l(e,t,r))}add(e,r){return this._leafNode(new u(e,t.operators.ADD,r))}code(e){return\"function\"==typeof e?e():e!==n.nil&&this._leafNode(new p(e)),this}object(...e){const t=[\"{\"];for(const[r,s]of e)t.length>1&&t.push(\",\"),t.push(r),(r!==s||this.opts.es5)&&(t.push(\":\"),(0,n.addCodeArg)(t,s));return t.push(\"}\"),new n._Code(t)}if(e,t,r){if(this._blockNode(new w(e)),t&&r)this.code(t).else().code(r).endIf();else if(t)this.code(t).endIf();else if(r)throw new Error('CodeGen: \"else\" body without \"then\" body');return this}elseIf(e){return this._elseNode(new w(e))}else(){return this._elseNode(new v)}endIf(){return this._endBlockNode(w,v)}_for(e,t){return this._blockNode(e),t&&this.code(t).endFor(),this}for(e,t){return this._for(new $(e),t)}forRange(e,t,r,n,o=(this.opts.es5?s.varKinds.var:s.varKinds.let)){const i=this._scope.toName(e);return this._for(new _(o,i,t,r),(()=>n(i)))}forOf(e,t,r,o=s.varKinds.const){const i=this._scope.toName(e);if(this.opts.es5){const e=t instanceof n.Name?t:this.var(\"_arr\",t);return this.forRange(\"_i\",0,n._`${e}.length`,(t=>{this.var(i,n._`${e}[${t}]`),r(i)}))}return this._for(new b(\"of\",o,i,t),(()=>r(i)))}forIn(e,t,r,o=(this.opts.es5?s.varKinds.var:s.varKinds.const)){if(this.opts.ownProperties)return this.forOf(e,n._`Object.keys(${t})`,r);const i=this._scope.toName(e);return this._for(new b(\"in\",o,i,t),(()=>r(i)))}endFor(){return this._endBlockNode(E)}label(e){return this._leafNode(new d(e))}break(e){return this._leafNode(new f(e))}return(e){const t=new O;if(this._blockNode(t),this.code(e),1!==t.nodes.length)throw new Error('CodeGen: \"return\" should have one node');return this._endBlockNode(O)}try(e,t,r){if(!t&&!r)throw new Error('CodeGen: \"try\" without \"catch\" and \"finally\"');const n=new P;if(this._blockNode(n),this.code(e),t){const e=this.name(\"e\");this._currNode=n.catch=new x(e),t(e)}return r&&(this._currNode=n.finally=new N,this.code(r)),this._endBlockNode(x,N)}throw(e){return this._leafNode(new h(e))}block(e,t){return this._blockStarts.push(this._nodes.length),e&&this.code(e).endBlock(t),this}endBlock(e){const t=this._blockStarts.pop();if(void 0===t)throw new Error(\"CodeGen: not in self-balancing block\");const r=this._nodes.length-t;if(r<0||void 0!==e&&r!==e)throw new Error(`CodeGen: wrong number of nodes: ${r} vs ${e} expected`);return this._nodes.length=t,this}func(e,t=n.nil,r,s){return this._blockNode(new S(e,t,r)),s&&this.code(s).endFunc(),this}endFunc(){return this._endBlockNode(S)}optimize(e=1){for(;e-- >0;)this._root.optimizeNodes(),this._root.optimizeNames(this._root.names,this._constants)}_leafNode(e){return this._currNode.nodes.push(e),this}_blockNode(e){this._currNode.nodes.push(e),this._nodes.push(e)}_endBlockNode(e,t){const r=this._currNode;if(r instanceof e||t&&r instanceof t)return this._nodes.pop(),this;throw new Error(`CodeGen: not in block \"${t?`${e.kind}/${t.kind}`:e.kind}\"`)}_elseNode(e){const t=this._currNode;if(!(t instanceof w))throw new Error('CodeGen: \"else\" without \"if\"');return this._currNode=t.else=e,this}get _root(){return this._nodes[0]}get _currNode(){const e=this._nodes;return e[e.length-1]}set _currNode(e){const t=this._nodes;t[t.length-1]=e}},t.not=T;const C=k(t.operators.AND);t.and=function(...e){return e.reduce(C)};const D=k(t.operators.OR);function k(e){return(t,r)=>t===n.nil?r:r===n.nil?t:n._`${L(t)} ${e} ${L(r)}`}function L(e){return e instanceof n.Name?e:n._`(${e})`}t.or=function(...e){return e.reduce(D)}},2321:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.ValueScope=t.ValueScopeName=t.Scope=t.varKinds=t.UsedValueState=void 0;const n=r(6796);class s extends Error{constructor(e){super(`CodeGen: \"code\" for ${e} not defined`),this.value=e.value}}var o;!function(e){e[e.Started=0]=\"Started\",e[e.Completed=1]=\"Completed\"}(o=t.UsedValueState||(t.UsedValueState={})),t.varKinds={const:new n.Name(\"const\"),let:new n.Name(\"let\"),var:new n.Name(\"var\")};class i{constructor({prefixes:e,parent:t}={}){this._names={},this._prefixes=e,this._parent=t}toName(e){return e instanceof n.Name?e:this.name(e)}name(e){return new n.Name(this._newName(e))}_newName(e){return`${e}${(this._names[e]||this._nameGroup(e)).index++}`}_nameGroup(e){var t,r;if((null===(r=null===(t=this._parent)||void 0===t?void 0:t._prefixes)||void 0===r?void 0:r.has(e))||this._prefixes&&!this._prefixes.has(e))throw new Error(`CodeGen: prefix \"${e}\" is not allowed in this scope`);return this._names[e]={prefix:e,index:0}}}t.Scope=i;class a extends n.Name{constructor(e,t){super(t),this.prefix=e}setValue(e,{property:t,itemIndex:r}){this.value=e,this.scopePath=n._`.${new n.Name(t)}[${r}]`}}t.ValueScopeName=a;const c=n._`\\n`;t.ValueScope=class extends i{constructor(e){super(e),this._values={},this._scope=e.scope,this.opts={...e,_n:e.lines?c:n.nil}}get(){return this._scope}name(e){return new a(e,this._newName(e))}value(e,t){var r;if(void 0===t.ref)throw new Error(\"CodeGen: ref must be passed in value\");const n=this.toName(e),{prefix:s}=n,o=null!==(r=t.key)&&void 0!==r?r:t.ref;let i=this._values[s];if(i){const e=i.get(o);if(e)return e}else i=this._values[s]=new Map;i.set(o,n);const a=this._scope[s]||(this._scope[s]=[]),c=a.length;return a[c]=t.ref,n.setValue(t,{property:s,itemIndex:c}),n}getValue(e,t){const r=this._values[e];if(r)return r.get(t)}scopeRefs(e,t=this._values){return this._reduceValues(t,(t=>{if(void 0===t.scopePath)throw new Error(`CodeGen: name \"${t}\" has no value`);return n._`${e}${t.scopePath}`}))}scopeCode(e=this._values,t,r){return this._reduceValues(e,(e=>{if(void 0===e.value)throw new Error(`CodeGen: name \"${e}\" has no value`);return e.value.code}),t,r)}_reduceValues(e,r,i={},a){let c=n.nil;for(const l in e){const u=e[l];if(!u)continue;const d=i[l]=i[l]||new Map;u.forEach((e=>{if(d.has(e))return;d.set(e,o.Started);let i=r(e);if(i){const r=this.opts.es5?t.varKinds.var:t.varKinds.const;c=n._`${c}${r} ${e} = ${i};${this.opts._n}`}else{if(!(i=null==a?void 0:a(e)))throw new s(e);c=n._`${c}${i}${this.opts._n}`}d.set(e,o.Completed)}))}return c}}},4677:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.extendErrors=t.resetErrorsCount=t.reportExtraError=t.reportError=t.keyword$DataError=t.keywordError=void 0;const n=r(5899),s=r(320),o=r(6934);function i(e,t){const r=e.const(\"err\",t);e.if(n._`${o.default.vErrors} === null`,(()=>e.assign(o.default.vErrors,n._`[${r}]`)),n._`${o.default.vErrors}.push(${r})`),e.code(n._`${o.default.errors}++`)}function a(e,t){const{gen:r,validateName:s,schemaEnv:o}=e;o.$async?r.throw(n._`new ${e.ValidationError}(${t})`):(r.assign(n._`${s}.errors`,t),r.return(!1))}t.keywordError={message:({keyword:e})=>n.str`must pass \"${e}\" keyword validation`},t.keyword$DataError={message:({keyword:e,schemaType:t})=>t?n.str`\"${e}\" keyword must be ${t} ($data)`:n.str`\"${e}\" keyword is invalid ($data)`},t.reportError=function(e,r=t.keywordError,s,o){const{it:c}=e,{gen:u,compositeRule:d,allErrors:f}=c,h=l(e,r,s);(null!=o?o:d||f)?i(u,h):a(c,n._`[${h}]`)},t.reportExtraError=function(e,r=t.keywordError,n){const{it:s}=e,{gen:c,compositeRule:u,allErrors:d}=s;i(c,l(e,r,n)),u||d||a(s,o.default.vErrors)},t.resetErrorsCount=function(e,t){e.assign(o.default.errors,t),e.if(n._`${o.default.vErrors} !== null`,(()=>e.if(t,(()=>e.assign(n._`${o.default.vErrors}.length`,t)),(()=>e.assign(o.default.vErrors,null)))))},t.extendErrors=function({gen:e,keyword:t,schemaValue:r,data:s,errsCount:i,it:a}){if(void 0===i)throw new Error(\"ajv implementation error\");const c=e.name(\"err\");e.forRange(\"i\",i,o.default.errors,(i=>{e.const(c,n._`${o.default.vErrors}[${i}]`),e.if(n._`${c}.instancePath === undefined`,(()=>e.assign(n._`${c}.instancePath`,(0,n.strConcat)(o.default.instancePath,a.errorPath)))),e.assign(n._`${c}.schemaPath`,n.str`${a.errSchemaPath}/${t}`),a.opts.verbose&&(e.assign(n._`${c}.schema`,r),e.assign(n._`${c}.data`,s))}))};const c={keyword:new n.Name(\"keyword\"),schemaPath:new n.Name(\"schemaPath\"),params:new n.Name(\"params\"),propertyName:new n.Name(\"propertyName\"),message:new n.Name(\"message\"),schema:new n.Name(\"schema\"),parentSchema:new n.Name(\"parentSchema\")};function l(e,t,r){const{createErrors:s}=e.it;return!1===s?n._`{}`:function(e,t,r={}){const{gen:s,it:i}=e,a=[u(i,r),d(e,r)];return function(e,{params:t,message:r},s){const{keyword:i,data:a,schemaValue:l,it:u}=e,{opts:d,propertyName:f,topSchemaRef:h,schemaPath:p}=u;s.push([c.keyword,i],[c.params,\"function\"==typeof t?t(e):t||n._`{}`]),d.messages&&s.push([c.message,\"function\"==typeof r?r(e):r]),d.verbose&&s.push([c.schema,l],[c.parentSchema,n._`${h}${p}`],[o.default.data,a]),f&&s.push([c.propertyName,f])}(e,t,a),s.object(...a)}(e,t,r)}function u({errorPath:e},{instancePath:t}){const r=t?n.str`${e}${(0,s.getErrorPath)(t,s.Type.Str)}`:e;return[o.default.instancePath,(0,n.strConcat)(o.default.instancePath,r)]}function d({keyword:e,it:{errSchemaPath:t}},{schemaPath:r,parentSchema:o}){let i=o?t:n.str`${t}/${e}`;return r&&(i=n.str`${i}${(0,s.getErrorPath)(r,s.Type.Str)}`),[c.schemaPath,i]}},7760:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.resolveSchema=t.getCompilingSchema=t.resolveRef=t.compileSchema=t.SchemaEnv=void 0;const n=r(5899),s=r(7173),o=r(6934),i=r(6885),a=r(320),c=r(5032);class l{constructor(e){var t;let r;this.refs={},this.dynamicAnchors={},\"object\"==typeof e.schema&&(r=e.schema),this.schema=e.schema,this.schemaId=e.schemaId,this.root=e.root||this,this.baseId=null!==(t=e.baseId)&&void 0!==t?t:(0,i.normalizeId)(null==r?void 0:r[e.schemaId||\"$id\"]),this.schemaPath=e.schemaPath,this.localRefs=e.localRefs,this.meta=e.meta,this.$async=null==r?void 0:r.$async,this.refs={}}}function u(e){const t=f.call(this,e);if(t)return t;const r=(0,i.getFullPath)(this.opts.uriResolver,e.root.baseId),{es5:a,lines:l}=this.opts.code,{ownProperties:u}=this.opts,d=new n.CodeGen(this.scope,{es5:a,lines:l,ownProperties:u});let h;e.$async&&(h=d.scopeValue(\"Error\",{ref:s.default,code:n._`require(\"ajv/dist/runtime/validation_error\").default`}));const p=d.scopeName(\"validate\");e.validateName=p;const m={gen:d,allErrors:this.opts.allErrors,data:o.default.data,parentData:o.default.parentData,parentDataProperty:o.default.parentDataProperty,dataNames:[o.default.data],dataPathArr:[n.nil],dataLevel:0,dataTypes:[],definedProperties:new Set,topSchemaRef:d.scopeValue(\"schema\",!0===this.opts.code.source?{ref:e.schema,code:(0,n.stringify)(e.schema)}:{ref:e.schema}),validateName:p,ValidationError:h,schema:e.schema,schemaEnv:e,rootId:r,baseId:e.baseId||r,schemaPath:n.nil,errSchemaPath:e.schemaPath||(this.opts.jtd?\"\":\"#\"),errorPath:n._`\"\"`,opts:this.opts,self:this};let y;try{this._compilations.add(e),(0,c.validateFunctionCode)(m),d.optimize(this.opts.code.optimize);const t=d.toString();y=`${d.scopeRefs(o.default.scope)}return ${t}`,this.opts.code.process&&(y=this.opts.code.process(y,e));const r=new Function(`${o.default.self}`,`${o.default.scope}`,y)(this,this.scope.get());if(this.scope.value(p,{ref:r}),r.errors=null,r.schema=e.schema,r.schemaEnv=e,e.$async&&(r.$async=!0),!0===this.opts.code.source&&(r.source={validateName:p,validateCode:t,scopeValues:d._values}),this.opts.unevaluated){const{props:e,items:t}=m;r.evaluated={props:e instanceof n.Name?void 0:e,items:t instanceof n.Name?void 0:t,dynamicProps:e instanceof n.Name,dynamicItems:t instanceof n.Name},r.source&&(r.source.evaluated=(0,n.stringify)(r.evaluated))}return e.validate=r,e}catch(t){throw delete e.validate,delete e.validateName,y&&this.logger.error(\"Error compiling schema, function code:\",y),t}finally{this._compilations.delete(e)}}function d(e){return(0,i.inlineRef)(e.schema,this.opts.inlineRefs)?e.schema:e.validate?e:u.call(this,e)}function f(e){for(const n of this._compilations)if(r=e,(t=n).schema===r.schema&&t.root===r.root&&t.baseId===r.baseId)return n;var t,r}function h(e,t){let r;for(;\"string\"==typeof(r=this.refs[t]);)t=r;return r||this.schemas[t]||p.call(this,e,t)}function p(e,t){const r=this.opts.uriResolver.parse(t),n=(0,i._getFullPath)(this.opts.uriResolver,r);let s=(0,i.getFullPath)(this.opts.uriResolver,e.baseId,void 0);if(Object.keys(e.schema).length>0&&n===s)return y.call(this,r,e);const o=(0,i.normalizeId)(n),a=this.refs[o]||this.schemas[o];if(\"string\"==typeof a){const t=p.call(this,e,a);if(\"object\"!=typeof(null==t?void 0:t.schema))return;return y.call(this,r,t)}if(\"object\"==typeof(null==a?void 0:a.schema)){if(a.validate||u.call(this,a),o===(0,i.normalizeId)(t)){const{schema:t}=a,{schemaId:r}=this.opts,n=t[r];return n&&(s=(0,i.resolveUrl)(this.opts.uriResolver,s,n)),new l({schema:t,schemaId:r,root:e,baseId:s})}return y.call(this,r,a)}}t.SchemaEnv=l,t.compileSchema=u,t.resolveRef=function(e,t,r){var n;r=(0,i.resolveUrl)(this.opts.uriResolver,t,r);const s=e.refs[r];if(s)return s;let o=h.call(this,e,r);if(void 0===o){const s=null===(n=e.localRefs)||void 0===n?void 0:n[r],{schemaId:i}=this.opts;s&&(o=new l({schema:s,schemaId:i,root:e,baseId:t}))}return void 0!==o?e.refs[r]=d.call(this,o):void 0},t.getCompilingSchema=f,t.resolveSchema=p;const m=new Set([\"properties\",\"patternProperties\",\"enum\",\"dependencies\",\"definitions\"]);function y(e,{baseId:t,schema:r,root:n}){var s;if(\"/\"!==(null===(s=e.fragment)||void 0===s?void 0:s[0]))return;for(const n of e.fragment.slice(1).split(\"/\")){if(\"boolean\"==typeof r)return;const e=r[(0,a.unescapeFragment)(n)];if(void 0===e)return;const s=\"object\"==typeof(r=e)&&r[this.opts.schemaId];!m.has(n)&&s&&(t=(0,i.resolveUrl)(this.opts.uriResolver,t,s))}let o;if(\"boolean\"!=typeof r&&r.$ref&&!(0,a.schemaHasRulesButRef)(r,this.RULES)){const e=(0,i.resolveUrl)(this.opts.uriResolver,t,r.$ref);o=p.call(this,n,e)}const{schemaId:c}=this.opts;return o=o||new l({schema:r,schemaId:c,root:n,baseId:t}),o.schema!==o.root.schema?o:void 0}},6934:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s={data:new n.Name(\"data\"),valCxt:new n.Name(\"valCxt\"),instancePath:new n.Name(\"instancePath\"),parentData:new n.Name(\"parentData\"),parentDataProperty:new n.Name(\"parentDataProperty\"),rootData:new n.Name(\"rootData\"),dynamicAnchors:new n.Name(\"dynamicAnchors\"),vErrors:new n.Name(\"vErrors\"),errors:new n.Name(\"errors\"),this:new n.Name(\"this\"),self:new n.Name(\"self\"),scope:new n.Name(\"scope\"),json:new n.Name(\"json\"),jsonPos:new n.Name(\"jsonPos\"),jsonLen:new n.Name(\"jsonLen\"),jsonPart:new n.Name(\"jsonPart\")};t.default=s},433:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(6885);class s extends Error{constructor(e,t,r,s){super(s||`can't resolve reference ${r} from id ${t}`),this.missingRef=(0,n.resolveUrl)(e,t,r),this.missingSchema=(0,n.normalizeId)((0,n.getFullPath)(e,this.missingRef))}}t.default=s},6885:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.getSchemaRefs=t.resolveUrl=t.normalizeId=t._getFullPath=t.getFullPath=t.inlineRef=void 0;const n=r(320),s=r(4063),o=r(5528),i=new Set([\"type\",\"format\",\"pattern\",\"maxLength\",\"minLength\",\"maxProperties\",\"minProperties\",\"maxItems\",\"minItems\",\"maximum\",\"minimum\",\"uniqueItems\",\"multipleOf\",\"required\",\"enum\",\"const\"]);t.inlineRef=function(e,t=!0){return\"boolean\"==typeof e||(!0===t?!c(e):!!t&&l(e)<=t)};const a=new Set([\"$ref\",\"$recursiveRef\",\"$recursiveAnchor\",\"$dynamicRef\",\"$dynamicAnchor\"]);function c(e){for(const t in e){if(a.has(t))return!0;const r=e[t];if(Array.isArray(r)&&r.some(c))return!0;if(\"object\"==typeof r&&c(r))return!0}return!1}function l(e){let t=0;for(const r in e){if(\"$ref\"===r)return 1/0;if(t++,!i.has(r)&&(\"object\"==typeof e[r]&&(0,n.eachItem)(e[r],(e=>t+=l(e))),t===1/0))return 1/0}return t}function u(e,t=\"\",r){!1!==r&&(t=h(t));const n=e.parse(t);return d(e,n)}function d(e,t){return e.serialize(t).split(\"#\")[0]+\"#\"}t.getFullPath=u,t._getFullPath=d;const f=/#\\/?$/;function h(e){return e?e.replace(f,\"\"):\"\"}t.normalizeId=h,t.resolveUrl=function(e,t,r){return r=h(r),e.resolve(t,r)};const p=/^[a-z_][-a-z0-9._]*$/i;t.getSchemaRefs=function(e,t){if(\"boolean\"==typeof e)return{};const{schemaId:r,uriResolver:n}=this.opts,i=h(e[r]||t),a={\"\":i},c=u(n,i,!1),l={},d=new Set;return o(e,{allKeys:!0},((e,t,n,s)=>{if(void 0===s)return;const o=c+t;let i=a[s];function u(t){const r=this.opts.uriResolver.resolve;if(t=h(i?r(i,t):t),d.has(t))throw m(t);d.add(t);let n=this.refs[t];return\"string\"==typeof n&&(n=this.refs[n]),\"object\"==typeof n?f(e,n.schema,t):t!==h(o)&&(\"#\"===t[0]?(f(e,l[t],t),l[t]=e):this.refs[t]=o),t}function y(e){if(\"string\"==typeof e){if(!p.test(e))throw new Error(`invalid anchor \"${e}\"`);u.call(this,`#${e}`)}}\"string\"==typeof e[r]&&(i=u.call(this,e[r])),y.call(this,e.$anchor),y.call(this,e.$dynamicAnchor),a[t]=i})),l;function f(e,t,r){if(void 0!==t&&!s(e,t))throw m(r)}function m(e){return new Error(`reference \"${e}\" resolves to more than one schema`)}}},6777:(e,t)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.getRules=t.isJSONType=void 0;const r=new Set([\"string\",\"number\",\"integer\",\"boolean\",\"null\",\"object\",\"array\"]);t.isJSONType=function(e){return\"string\"==typeof e&&r.has(e)},t.getRules=function(){const e={number:{type:\"number\",rules:[]},string:{type:\"string\",rules:[]},array:{type:\"array\",rules:[]},object:{type:\"object\",rules:[]}};return{types:{...e,integer:!0,boolean:!0,null:!0},rules:[{rules:[]},e.number,e.string,e.array,e.object],post:{rules:[]},all:{},keywords:{}}}},320:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.checkStrictMode=t.getErrorPath=t.Type=t.useFunc=t.setEvaluated=t.evaluatedPropsToName=t.mergeEvaluated=t.eachItem=t.unescapeJsonPointer=t.escapeJsonPointer=t.escapeFragment=t.unescapeFragment=t.schemaRefOrVal=t.schemaHasRulesButRef=t.schemaHasRules=t.checkUnknownRules=t.alwaysValidSchema=t.toHash=void 0;const n=r(5899),s=r(6796);function o(e,t=e.schema){const{opts:r,self:n}=e;if(!r.strictSchema)return;if(\"boolean\"==typeof t)return;const s=n.RULES.keywords;for(const r in t)s[r]||p(e,`unknown keyword: \"${r}\"`)}function i(e,t){if(\"boolean\"==typeof e)return!e;for(const r in e)if(t[r])return!0;return!1}function a(e){return\"number\"==typeof e?`${e}`:e.replace(/~/g,\"~0\").replace(/\\//g,\"~1\")}function c(e){return e.replace(/~1/g,\"/\").replace(/~0/g,\"~\")}function l({mergeNames:e,mergeToName:t,mergeValues:r,resultToName:s}){return(o,i,a,c)=>{const l=void 0===a?i:a instanceof n.Name?(i instanceof n.Name?e(o,i,a):t(o,i,a),a):i instanceof n.Name?(t(o,a,i),i):r(i,a);return c!==n.Name||l instanceof n.Name?l:s(o,l)}}function u(e,t){if(!0===t)return e.var(\"props\",!0);const r=e.var(\"props\",n._`{}`);return void 0!==t&&d(e,r,t),r}function d(e,t,r){Object.keys(r).forEach((r=>e.assign(n._`${t}${(0,n.getProperty)(r)}`,!0)))}t.toHash=function(e){const t={};for(const r of e)t[r]=!0;return t},t.alwaysValidSchema=function(e,t){return\"boolean\"==typeof t?t:0===Object.keys(t).length||(o(e,t),!i(t,e.self.RULES.all))},t.checkUnknownRules=o,t.schemaHasRules=i,t.schemaHasRulesButRef=function(e,t){if(\"boolean\"==typeof e)return!e;for(const r in e)if(\"$ref\"!==r&&t.all[r])return!0;return!1},t.schemaRefOrVal=function({topSchemaRef:e,schemaPath:t},r,s,o){if(!o){if(\"number\"==typeof r||\"boolean\"==typeof r)return r;if(\"string\"==typeof r)return n._`${r}`}return n._`${e}${t}${(0,n.getProperty)(s)}`},t.unescapeFragment=function(e){return c(decodeURIComponent(e))},t.escapeFragment=function(e){return encodeURIComponent(a(e))},t.escapeJsonPointer=a,t.unescapeJsonPointer=c,t.eachItem=function(e,t){if(Array.isArray(e))for(const r of e)t(r);else t(e)},t.mergeEvaluated={props:l({mergeNames:(e,t,r)=>e.if(n._`${r} !== true && ${t} !== undefined`,(()=>{e.if(n._`${t} === true`,(()=>e.assign(r,!0)),(()=>e.assign(r,n._`${r} || {}`).code(n._`Object.assign(${r}, ${t})`)))})),mergeToName:(e,t,r)=>e.if(n._`${r} !== true`,(()=>{!0===t?e.assign(r,!0):(e.assign(r,n._`${r} || {}`),d(e,r,t))})),mergeValues:(e,t)=>!0===e||{...e,...t},resultToName:u}),items:l({mergeNames:(e,t,r)=>e.if(n._`${r} !== true && ${t} !== undefined`,(()=>e.assign(r,n._`${t} === true ? true : ${r} > ${t} ? ${r} : ${t}`))),mergeToName:(e,t,r)=>e.if(n._`${r} !== true`,(()=>e.assign(r,!0===t||n._`${r} > ${t} ? ${r} : ${t}`))),mergeValues:(e,t)=>!0===e||Math.max(e,t),resultToName:(e,t)=>e.var(\"items\",t)})},t.evaluatedPropsToName=u,t.setEvaluated=d;const f={};var h;function p(e,t,r=e.opts.strictSchema){if(r){if(t=`strict mode: ${t}`,!0===r)throw new Error(t);e.self.logger.warn(t)}}t.useFunc=function(e,t){return e.scopeValue(\"func\",{ref:t,code:f[t.code]||(f[t.code]=new s._Code(t.code))})},function(e){e[e.Num=0]=\"Num\",e[e.Str=1]=\"Str\"}(h=t.Type||(t.Type={})),t.getErrorPath=function(e,t,r){if(e instanceof n.Name){const s=t===h.Num;return r?s?n._`\"[\" + ${e} + \"]\"`:n._`\"['\" + ${e} + \"']\"`:s?n._`\"/\" + ${e}`:n._`\"/\" + ${e}.replace(/~/g, \"~0\").replace(/\\\\//g, \"~1\")`}return r?(0,n.getProperty)(e).toString():\"/\"+a(e)},t.checkStrictMode=p},8149:(e,t)=>{\"use strict\";function r(e,t){return t.rules.some((t=>n(e,t)))}function n(e,t){var r;return void 0!==e[t.keyword]||(null===(r=t.definition.implements)||void 0===r?void 0:r.some((t=>void 0!==e[t])))}Object.defineProperty(t,\"__esModule\",{value:!0}),t.shouldUseRule=t.shouldUseGroup=t.schemaHasRulesForType=void 0,t.schemaHasRulesForType=function({schema:e,self:t},n){const s=t.RULES.types[n];return s&&!0!==s&&r(e,s)},t.shouldUseGroup=r,t.shouldUseRule=n},473:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.boolOrEmptySchema=t.topBoolOrEmptySchema=void 0;const n=r(4677),s=r(5899),o=r(6934),i={message:\"boolean schema is false\"};function a(e,t){const{gen:r,data:s}=e,o={gen:r,keyword:\"false schema\",data:s,schema:!1,schemaCode:!1,schemaValue:!1,params:{},it:e};(0,n.reportError)(o,i,void 0,t)}t.topBoolOrEmptySchema=function(e){const{gen:t,schema:r,validateName:n}=e;!1===r?a(e,!1):\"object\"==typeof r&&!0===r.$async?t.return(o.default.data):(t.assign(s._`${n}.errors`,null),t.return(!0))},t.boolOrEmptySchema=function(e,t){const{gen:r,schema:n}=e;!1===n?(r.var(t,!1),a(e)):r.var(t,!0)}},2292:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.reportTypeError=t.checkDataTypes=t.checkDataType=t.coerceAndCheckDataType=t.getJSONTypes=t.getSchemaTypes=t.DataType=void 0;const n=r(6777),s=r(8149),o=r(4677),i=r(5899),a=r(320);var c;function l(e){const t=Array.isArray(e)?e:e?[e]:[];if(t.every(n.isJSONType))return t;throw new Error(\"type must be JSONType or JSONType[]: \"+t.join(\",\"))}!function(e){e[e.Correct=0]=\"Correct\",e[e.Wrong=1]=\"Wrong\"}(c=t.DataType||(t.DataType={})),t.getSchemaTypes=function(e){const t=l(e.type);if(t.includes(\"null\")){if(!1===e.nullable)throw new Error(\"type: null contradicts nullable: false\")}else{if(!t.length&&void 0!==e.nullable)throw new Error('\"nullable\" cannot be used without \"type\"');!0===e.nullable&&t.push(\"null\")}return t},t.getJSONTypes=l,t.coerceAndCheckDataType=function(e,t){const{gen:r,data:n,opts:o}=e,a=function(e,t){return t?e.filter((e=>u.has(e)||\"array\"===t&&\"array\"===e)):[]}(t,o.coerceTypes),l=t.length>0&&!(0===a.length&&1===t.length&&(0,s.schemaHasRulesForType)(e,t[0]));if(l){const s=f(t,n,o.strictNumbers,c.Wrong);r.if(s,(()=>{a.length?function(e,t,r){const{gen:n,data:s,opts:o}=e,a=n.let(\"dataType\",i._`typeof ${s}`),c=n.let(\"coerced\",i._`undefined`);\"array\"===o.coerceTypes&&n.if(i._`${a} == 'object' && Array.isArray(${s}) && ${s}.length == 1`,(()=>n.assign(s,i._`${s}[0]`).assign(a,i._`typeof ${s}`).if(f(t,s,o.strictNumbers),(()=>n.assign(c,s))))),n.if(i._`${c} !== undefined`);for(const e of r)(u.has(e)||\"array\"===e&&\"array\"===o.coerceTypes)&&l(e);function l(e){switch(e){case\"string\":return void n.elseIf(i._`${a} == \"number\" || ${a} == \"boolean\"`).assign(c,i._`\"\" + ${s}`).elseIf(i._`${s} === null`).assign(c,i._`\"\"`);case\"number\":return void n.elseIf(i._`${a} == \"boolean\" || ${s} === null\n              || (${a} == \"string\" && ${s} && ${s} == +${s})`).assign(c,i._`+${s}`);case\"integer\":return void n.elseIf(i._`${a} === \"boolean\" || ${s} === null\n              || (${a} === \"string\" && ${s} && ${s} == +${s} && !(${s} % 1))`).assign(c,i._`+${s}`);case\"boolean\":return void n.elseIf(i._`${s} === \"false\" || ${s} === 0 || ${s} === null`).assign(c,!1).elseIf(i._`${s} === \"true\" || ${s} === 1`).assign(c,!0);case\"null\":return n.elseIf(i._`${s} === \"\" || ${s} === 0 || ${s} === false`),void n.assign(c,null);case\"array\":n.elseIf(i._`${a} === \"string\" || ${a} === \"number\"\n              || ${a} === \"boolean\" || ${s} === null`).assign(c,i._`[${s}]`)}}n.else(),p(e),n.endIf(),n.if(i._`${c} !== undefined`,(()=>{n.assign(s,c),function({gen:e,parentData:t,parentDataProperty:r},n){e.if(i._`${t} !== undefined`,(()=>e.assign(i._`${t}[${r}]`,n)))}(e,c)}))}(e,t,a):p(e)}))}return l};const u=new Set([\"string\",\"number\",\"integer\",\"boolean\",\"null\"]);function d(e,t,r,n=c.Correct){const s=n===c.Correct?i.operators.EQ:i.operators.NEQ;let o;switch(e){case\"null\":return i._`${t} ${s} null`;case\"array\":o=i._`Array.isArray(${t})`;break;case\"object\":o=i._`${t} && typeof ${t} == \"object\" && !Array.isArray(${t})`;break;case\"integer\":o=a(i._`!(${t} % 1) && !isNaN(${t})`);break;case\"number\":o=a();break;default:return i._`typeof ${t} ${s} ${e}`}return n===c.Correct?o:(0,i.not)(o);function a(e=i.nil){return(0,i.and)(i._`typeof ${t} == \"number\"`,e,r?i._`isFinite(${t})`:i.nil)}}function f(e,t,r,n){if(1===e.length)return d(e[0],t,r,n);let s;const o=(0,a.toHash)(e);if(o.array&&o.object){const e=i._`typeof ${t} != \"object\"`;s=o.null?e:i._`!${t} || ${e}`,delete o.null,delete o.array,delete o.object}else s=i.nil;o.number&&delete o.integer;for(const e in o)s=(0,i.and)(s,d(e,t,r,n));return s}t.checkDataType=d,t.checkDataTypes=f;const h={message:({schema:e})=>`must be ${e}`,params:({schema:e,schemaValue:t})=>\"string\"==typeof e?i._`{type: ${e}}`:i._`{type: ${t}}`};function p(e){const t=function(e){const{gen:t,data:r,schema:n}=e,s=(0,a.schemaRefOrVal)(e,n,\"type\");return{gen:t,keyword:\"type\",data:r,schema:n.type,schemaCode:s,schemaValue:s,parentSchema:n,params:{},it:e}}(e);(0,o.reportError)(t,h)}t.reportTypeError=p},162:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.assignDefaults=void 0;const n=r(5899),s=r(320);function o(e,t,r){const{gen:o,compositeRule:i,data:a,opts:c}=e;if(void 0===r)return;const l=n._`${a}${(0,n.getProperty)(t)}`;if(i)return void(0,s.checkStrictMode)(e,`default is ignored for: ${l}`);let u=n._`${l} === undefined`;\"empty\"===c.useDefaults&&(u=n._`${u} || ${l} === null || ${l} === \"\"`),o.if(u,n._`${l} = ${(0,n.stringify)(r)}`)}t.assignDefaults=function(e,t){const{properties:r,items:n}=e.schema;if(\"object\"===t&&r)for(const t in r)o(e,t,r[t].default);else\"array\"===t&&Array.isArray(n)&&n.forEach(((t,r)=>o(e,r,t.default)))}},5032:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.getData=t.KeywordCxt=t.validateFunctionCode=void 0;const n=r(473),s=r(2292),o=r(8149),i=r(2292),a=r(162),c=r(9653),l=r(9282),u=r(5899),d=r(6934),f=r(6885),h=r(320),p=r(4677);function m({gen:e,validateName:t,schema:r,schemaEnv:n,opts:s},o){s.code.es5?e.func(t,u._`${d.default.data}, ${d.default.valCxt}`,n.$async,(()=>{e.code(u._`\"use strict\"; ${y(r,s)}`),function(e,t){e.if(d.default.valCxt,(()=>{e.var(d.default.instancePath,u._`${d.default.valCxt}.${d.default.instancePath}`),e.var(d.default.parentData,u._`${d.default.valCxt}.${d.default.parentData}`),e.var(d.default.parentDataProperty,u._`${d.default.valCxt}.${d.default.parentDataProperty}`),e.var(d.default.rootData,u._`${d.default.valCxt}.${d.default.rootData}`),t.dynamicRef&&e.var(d.default.dynamicAnchors,u._`${d.default.valCxt}.${d.default.dynamicAnchors}`)}),(()=>{e.var(d.default.instancePath,u._`\"\"`),e.var(d.default.parentData,u._`undefined`),e.var(d.default.parentDataProperty,u._`undefined`),e.var(d.default.rootData,d.default.data),t.dynamicRef&&e.var(d.default.dynamicAnchors,u._`{}`)}))}(e,s),e.code(o)})):e.func(t,u._`${d.default.data}, ${function(e){return u._`{${d.default.instancePath}=\"\", ${d.default.parentData}, ${d.default.parentDataProperty}, ${d.default.rootData}=${d.default.data}${e.dynamicRef?u._`, ${d.default.dynamicAnchors}={}`:u.nil}}={}`}(s)}`,n.$async,(()=>e.code(y(r,s)).code(o)))}function y(e,t){const r=\"object\"==typeof e&&e[t.schemaId];return r&&(t.code.source||t.code.process)?u._`/*# sourceURL=${r} */`:u.nil}function g({schema:e,self:t}){if(\"boolean\"==typeof e)return!e;for(const r in e)if(t.RULES.all[r])return!0;return!1}function v(e){return\"boolean\"!=typeof e.schema}function w(e){(0,h.checkUnknownRules)(e),function(e){const{schema:t,errSchemaPath:r,opts:n,self:s}=e;t.$ref&&n.ignoreKeywordsWithRef&&(0,h.schemaHasRulesButRef)(t,s.RULES)&&s.logger.warn(`$ref: keywords ignored in schema at path \"${r}\"`)}(e)}function E(e,t){if(e.opts.jtd)return _(e,[],!1,t);const r=(0,s.getSchemaTypes)(e.schema);_(e,r,!(0,s.coerceAndCheckDataType)(e,r),t)}function $({gen:e,schemaEnv:t,schema:r,errSchemaPath:n,opts:s}){const o=r.$comment;if(!0===s.$comment)e.code(u._`${d.default.self}.logger.log(${o})`);else if(\"function\"==typeof s.$comment){const r=u.str`${n}/$comment`,s=e.scopeValue(\"root\",{ref:t.root});e.code(u._`${d.default.self}.opts.$comment(${o}, ${r}, ${s}.schema)`)}}function _(e,t,r,n){const{gen:s,schema:a,data:c,allErrors:l,opts:f,self:p}=e,{RULES:m}=p;function y(h){(0,o.shouldUseGroup)(a,h)&&(h.type?(s.if((0,i.checkDataType)(h.type,c,f.strictNumbers)),b(e,h),1===t.length&&t[0]===h.type&&r&&(s.else(),(0,i.reportTypeError)(e)),s.endIf()):b(e,h),l||s.if(u._`${d.default.errors} === ${n||0}`))}!a.$ref||!f.ignoreKeywordsWithRef&&(0,h.schemaHasRulesButRef)(a,m)?(f.jtd||function(e,t){!e.schemaEnv.meta&&e.opts.strictTypes&&(function(e,t){t.length&&(e.dataTypes.length?(t.forEach((t=>{S(e.dataTypes,t)||O(e,`type \"${t}\" not allowed by context \"${e.dataTypes.join(\",\")}\"`)})),function(e,t){const r=[];for(const n of e.dataTypes)S(t,n)?r.push(n):t.includes(\"integer\")&&\"number\"===n&&r.push(\"integer\");e.dataTypes=r}(e,t)):e.dataTypes=t)}(e,t),e.opts.allowUnionTypes||function(e,t){t.length>1&&(2!==t.length||!t.includes(\"null\"))&&O(e,\"use allowUnionTypes to allow union type keyword\")}(e,t),function(e,t){const r=e.self.RULES.all;for(const n in r){const s=r[n];if(\"object\"==typeof s&&(0,o.shouldUseRule)(e.schema,s)){const{type:r}=s.definition;r.length&&!r.some((e=>{return n=e,(r=t).includes(n)||\"number\"===n&&r.includes(\"integer\");var r,n}))&&O(e,`missing type \"${r.join(\",\")}\" for keyword \"${n}\"`)}}}(e,e.dataTypes))}(e,t),s.block((()=>{for(const e of m.rules)y(e);y(m.post)}))):s.block((()=>x(e,\"$ref\",m.all.$ref.definition)))}function b(e,t){const{gen:r,schema:n,opts:{useDefaults:s}}=e;s&&(0,a.assignDefaults)(e,t.type),r.block((()=>{for(const r of t.rules)(0,o.shouldUseRule)(n,r)&&x(e,r.keyword,r.definition,t.type)}))}function S(e,t){return e.includes(t)||\"integer\"===t&&e.includes(\"number\")}function O(e,t){t+=` at \"${e.schemaEnv.baseId+e.errSchemaPath}\" (strictTypes)`,(0,h.checkStrictMode)(e,t,e.opts.strictTypes)}t.validateFunctionCode=function(e){v(e)&&(w(e),g(e))?function(e){const{schema:t,opts:r,gen:n}=e;m(e,(()=>{r.$comment&&t.$comment&&$(e),function(e){const{schema:t,opts:r}=e;void 0!==t.default&&r.useDefaults&&r.strictSchema&&(0,h.checkStrictMode)(e,\"default is ignored in the schema root\")}(e),n.let(d.default.vErrors,null),n.let(d.default.errors,0),r.unevaluated&&function(e){const{gen:t,validateName:r}=e;e.evaluated=t.const(\"evaluated\",u._`${r}.evaluated`),t.if(u._`${e.evaluated}.dynamicProps`,(()=>t.assign(u._`${e.evaluated}.props`,u._`undefined`))),t.if(u._`${e.evaluated}.dynamicItems`,(()=>t.assign(u._`${e.evaluated}.items`,u._`undefined`)))}(e),E(e),function(e){const{gen:t,schemaEnv:r,validateName:n,ValidationError:s,opts:o}=e;r.$async?t.if(u._`${d.default.errors} === 0`,(()=>t.return(d.default.data)),(()=>t.throw(u._`new ${s}(${d.default.vErrors})`))):(t.assign(u._`${n}.errors`,d.default.vErrors),o.unevaluated&&function({gen:e,evaluated:t,props:r,items:n}){r instanceof u.Name&&e.assign(u._`${t}.props`,r),n instanceof u.Name&&e.assign(u._`${t}.items`,n)}(e),t.return(u._`${d.default.errors} === 0`))}(e)}))}(e):m(e,(()=>(0,n.topBoolOrEmptySchema)(e)))};class P{constructor(e,t,r){if((0,c.validateKeywordUsage)(e,t,r),this.gen=e.gen,this.allErrors=e.allErrors,this.keyword=r,this.data=e.data,this.schema=e.schema[r],this.$data=t.$data&&e.opts.$data&&this.schema&&this.schema.$data,this.schemaValue=(0,h.schemaRefOrVal)(e,this.schema,r,this.$data),this.schemaType=t.schemaType,this.parentSchema=e.schema,this.params={},this.it=e,this.def=t,this.$data)this.schemaCode=e.gen.const(\"vSchema\",j(this.$data,e));else if(this.schemaCode=this.schemaValue,!(0,c.validSchemaType)(this.schema,t.schemaType,t.allowUndefined))throw new Error(`${r} value must be ${JSON.stringify(t.schemaType)}`);(\"code\"in t?t.trackErrors:!1!==t.errors)&&(this.errsCount=e.gen.const(\"_errs\",d.default.errors))}result(e,t,r){this.failResult((0,u.not)(e),t,r)}failResult(e,t,r){this.gen.if(e),r?r():this.error(),t?(this.gen.else(),t(),this.allErrors&&this.gen.endIf()):this.allErrors?this.gen.endIf():this.gen.else()}pass(e,t){this.failResult((0,u.not)(e),void 0,t)}fail(e){if(void 0===e)return this.error(),void(this.allErrors||this.gen.if(!1));this.gen.if(e),this.error(),this.allErrors?this.gen.endIf():this.gen.else()}fail$data(e){if(!this.$data)return this.fail(e);const{schemaCode:t}=this;this.fail(u._`${t} !== undefined && (${(0,u.or)(this.invalid$data(),e)})`)}error(e,t,r){if(t)return this.setParams(t),this._error(e,r),void this.setParams({});this._error(e,r)}_error(e,t){(e?p.reportExtraError:p.reportError)(this,this.def.error,t)}$dataError(){(0,p.reportError)(this,this.def.$dataError||p.keyword$DataError)}reset(){if(void 0===this.errsCount)throw new Error('add \"trackErrors\" to keyword definition');(0,p.resetErrorsCount)(this.gen,this.errsCount)}ok(e){this.allErrors||this.gen.if(e)}setParams(e,t){t?Object.assign(this.params,e):this.params=e}block$data(e,t,r=u.nil){this.gen.block((()=>{this.check$data(e,r),t()}))}check$data(e=u.nil,t=u.nil){if(!this.$data)return;const{gen:r,schemaCode:n,schemaType:s,def:o}=this;r.if((0,u.or)(u._`${n} === undefined`,t)),e!==u.nil&&r.assign(e,!0),(s.length||o.validateSchema)&&(r.elseIf(this.invalid$data()),this.$dataError(),e!==u.nil&&r.assign(e,!1)),r.else()}invalid$data(){const{gen:e,schemaCode:t,schemaType:r,def:n,it:s}=this;return(0,u.or)(function(){if(r.length){if(!(t instanceof u.Name))throw new Error(\"ajv implementation error\");const e=Array.isArray(r)?r:[r];return u._`${(0,i.checkDataTypes)(e,t,s.opts.strictNumbers,i.DataType.Wrong)}`}return u.nil}(),function(){if(n.validateSchema){const r=e.scopeValue(\"validate$data\",{ref:n.validateSchema});return u._`!${r}(${t})`}return u.nil}())}subschema(e,t){const r=(0,l.getSubschema)(this.it,e);(0,l.extendSubschemaData)(r,this.it,e),(0,l.extendSubschemaMode)(r,e);const s={...this.it,...r,items:void 0,props:void 0};return function(e,t){v(e)&&(w(e),g(e))?function(e,t){const{schema:r,gen:n,opts:s}=e;s.$comment&&r.$comment&&$(e),function(e){const t=e.schema[e.opts.schemaId];t&&(e.baseId=(0,f.resolveUrl)(e.opts.uriResolver,e.baseId,t))}(e),function(e){if(e.schema.$async&&!e.schemaEnv.$async)throw new Error(\"async schema in sync schema\")}(e);const o=n.const(\"_errs\",d.default.errors);E(e,o),n.var(t,u._`${o} === ${d.default.errors}`)}(e,t):(0,n.boolOrEmptySchema)(e,t)}(s,t),s}mergeEvaluated(e,t){const{it:r,gen:n}=this;r.opts.unevaluated&&(!0!==r.props&&void 0!==e.props&&(r.props=h.mergeEvaluated.props(n,e.props,r.props,t)),!0!==r.items&&void 0!==e.items&&(r.items=h.mergeEvaluated.items(n,e.items,r.items,t)))}mergeValidEvaluated(e,t){const{it:r,gen:n}=this;if(r.opts.unevaluated&&(!0!==r.props||!0!==r.items))return n.if(t,(()=>this.mergeEvaluated(e,u.Name))),!0}}function x(e,t,r,n){const s=new P(e,r,t);\"code\"in r?r.code(s,n):s.$data&&r.validate?(0,c.funcKeywordCode)(s,r):\"macro\"in r?(0,c.macroKeywordCode)(s,r):(r.compile||r.validate)&&(0,c.funcKeywordCode)(s,r)}t.KeywordCxt=P;const N=/^\\/(?:[^~]|~0|~1)*$/,I=/^([0-9]+)(#|\\/(?:[^~]|~0|~1)*)?$/;function j(e,{dataLevel:t,dataNames:r,dataPathArr:n}){let s,o;if(\"\"===e)return d.default.rootData;if(\"/\"===e[0]){if(!N.test(e))throw new Error(`Invalid JSON-pointer: ${e}`);s=e,o=d.default.rootData}else{const i=I.exec(e);if(!i)throw new Error(`Invalid JSON-pointer: ${e}`);const a=+i[1];if(s=i[2],\"#\"===s){if(a>=t)throw new Error(c(\"property/index\",a));return n[t-a]}if(a>t)throw new Error(c(\"data\",a));if(o=r[t-a],!s)return o}let i=o;const a=s.split(\"/\");for(const e of a)e&&(o=u._`${o}${(0,u.getProperty)((0,h.unescapeJsonPointer)(e))}`,i=u._`${i} && ${o}`);return i;function c(e,r){return`Cannot access ${e} ${r} levels up, current level is ${t}`}}t.getData=j},9653:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.validateKeywordUsage=t.validSchemaType=t.funcKeywordCode=t.macroKeywordCode=void 0;const n=r(5899),s=r(6934),o=r(7470),i=r(4677);function a(e){const{gen:t,data:r,it:s}=e;t.if(s.parentData,(()=>t.assign(r,n._`${s.parentData}[${s.parentDataProperty}]`)))}function c(e,t,r){if(void 0===r)throw new Error(`keyword \"${t}\" failed to compile`);return e.scopeValue(\"keyword\",\"function\"==typeof r?{ref:r}:{ref:r,code:(0,n.stringify)(r)})}t.macroKeywordCode=function(e,t){const{gen:r,keyword:s,schema:o,parentSchema:i,it:a}=e,l=t.macro.call(a.self,o,i,a),u=c(r,s,l);!1!==a.opts.validateSchema&&a.self.validateSchema(l,!0);const d=r.name(\"valid\");e.subschema({schema:l,schemaPath:n.nil,errSchemaPath:`${a.errSchemaPath}/${s}`,topSchemaRef:u,compositeRule:!0},d),e.pass(d,(()=>e.error(!0)))},t.funcKeywordCode=function(e,t){var r;const{gen:l,keyword:u,schema:d,parentSchema:f,$data:h,it:p}=e;!function({schemaEnv:e},t){if(t.async&&!e.$async)throw new Error(\"async keyword in sync schema\")}(p,t);const m=!h&&t.compile?t.compile.call(p.self,d,f,p):t.validate,y=c(l,u,m),g=l.let(\"valid\");function v(r=(t.async?n._`await `:n.nil)){const i=p.opts.passContext?s.default.this:s.default.self,a=!(\"compile\"in t&&!h||!1===t.schema);l.assign(g,n._`${r}${(0,o.callValidateCode)(e,y,i,a)}`,t.modifying)}function w(e){var r;l.if((0,n.not)(null!==(r=t.valid)&&void 0!==r?r:g),e)}e.block$data(g,(function(){if(!1===t.errors)v(),t.modifying&&a(e),w((()=>e.error()));else{const r=t.async?function(){const e=l.let(\"ruleErrs\",null);return l.try((()=>v(n._`await `)),(t=>l.assign(g,!1).if(n._`${t} instanceof ${p.ValidationError}`,(()=>l.assign(e,n._`${t}.errors`)),(()=>l.throw(t))))),e}():function(){const e=n._`${y}.errors`;return l.assign(e,null),v(n.nil),e}();t.modifying&&a(e),w((()=>function(e,t){const{gen:r}=e;r.if(n._`Array.isArray(${t})`,(()=>{r.assign(s.default.vErrors,n._`${s.default.vErrors} === null ? ${t} : ${s.default.vErrors}.concat(${t})`).assign(s.default.errors,n._`${s.default.vErrors}.length`),(0,i.extendErrors)(e)}),(()=>e.error()))}(e,r)))}})),e.ok(null!==(r=t.valid)&&void 0!==r?r:g)},t.validSchemaType=function(e,t,r=!1){return!t.length||t.some((t=>\"array\"===t?Array.isArray(e):\"object\"===t?e&&\"object\"==typeof e&&!Array.isArray(e):typeof e==t||r&&void 0===e))},t.validateKeywordUsage=function({schema:e,opts:t,self:r,errSchemaPath:n},s,o){if(Array.isArray(s.keyword)?!s.keyword.includes(o):s.keyword!==o)throw new Error(\"ajv implementation error\");const i=s.dependencies;if(null==i?void 0:i.some((t=>!Object.prototype.hasOwnProperty.call(e,t))))throw new Error(`parent schema must have dependencies of ${o}: ${i.join(\",\")}`);if(s.validateSchema&&!s.validateSchema(e[o])){const e=`keyword \"${o}\" value is invalid at path \"${n}\": `+r.errorsText(s.validateSchema.errors);if(\"log\"!==t.validateSchema)throw new Error(e);r.logger.error(e)}}},9282:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.extendSubschemaMode=t.extendSubschemaData=t.getSubschema=void 0;const n=r(5899),s=r(320);t.getSubschema=function(e,{keyword:t,schemaProp:r,schema:o,schemaPath:i,errSchemaPath:a,topSchemaRef:c}){if(void 0!==t&&void 0!==o)throw new Error('both \"keyword\" and \"schema\" passed, only one allowed');if(void 0!==t){const o=e.schema[t];return void 0===r?{schema:o,schemaPath:n._`${e.schemaPath}${(0,n.getProperty)(t)}`,errSchemaPath:`${e.errSchemaPath}/${t}`}:{schema:o[r],schemaPath:n._`${e.schemaPath}${(0,n.getProperty)(t)}${(0,n.getProperty)(r)}`,errSchemaPath:`${e.errSchemaPath}/${t}/${(0,s.escapeFragment)(r)}`}}if(void 0!==o){if(void 0===i||void 0===a||void 0===c)throw new Error('\"schemaPath\", \"errSchemaPath\" and \"topSchemaRef\" are required with \"schema\"');return{schema:o,schemaPath:i,topSchemaRef:c,errSchemaPath:a}}throw new Error('either \"keyword\" or \"schema\" must be passed')},t.extendSubschemaData=function(e,t,{dataProp:r,dataPropType:o,data:i,dataTypes:a,propertyName:c}){if(void 0!==i&&void 0!==r)throw new Error('both \"data\" and \"dataProp\" passed, only one allowed');const{gen:l}=t;if(void 0!==r){const{errorPath:i,dataPathArr:a,opts:c}=t;u(l.let(\"data\",n._`${t.data}${(0,n.getProperty)(r)}`,!0)),e.errorPath=n.str`${i}${(0,s.getErrorPath)(r,o,c.jsPropertySyntax)}`,e.parentDataProperty=n._`${r}`,e.dataPathArr=[...a,e.parentDataProperty]}function u(r){e.data=r,e.dataLevel=t.dataLevel+1,e.dataTypes=[],t.definedProperties=new Set,e.parentData=t.data,e.dataNames=[...t.dataNames,r]}void 0!==i&&(u(i instanceof n.Name?i:l.let(\"data\",i,!0)),void 0!==c&&(e.propertyName=c)),a&&(e.dataTypes=a)},t.extendSubschemaMode=function(e,{jtdDiscriminator:t,jtdMetadata:r,compositeRule:n,createErrors:s,allErrors:o}){void 0!==n&&(e.compositeRule=n),void 0!==s&&(e.createErrors=s),void 0!==o&&(e.allErrors=o),e.jtdDiscriminator=t,e.jtdMetadata=r}},3579:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.CodeGen=t.Name=t.nil=t.stringify=t.str=t._=t.KeywordCxt=void 0;var n=r(5032);Object.defineProperty(t,\"KeywordCxt\",{enumerable:!0,get:function(){return n.KeywordCxt}});var s=r(5899);Object.defineProperty(t,\"_\",{enumerable:!0,get:function(){return s._}}),Object.defineProperty(t,\"str\",{enumerable:!0,get:function(){return s.str}}),Object.defineProperty(t,\"stringify\",{enumerable:!0,get:function(){return s.stringify}}),Object.defineProperty(t,\"nil\",{enumerable:!0,get:function(){return s.nil}}),Object.defineProperty(t,\"Name\",{enumerable:!0,get:function(){return s.Name}}),Object.defineProperty(t,\"CodeGen\",{enumerable:!0,get:function(){return s.CodeGen}});const o=r(7173),i=r(433),a=r(6777),c=r(7760),l=r(5899),u=r(6885),d=r(2292),f=r(320),h=r(9756),p=r(2972),m=(e,t)=>new RegExp(e,t);m.code=\"new RegExp\";const y=[\"removeAdditional\",\"useDefaults\",\"coerceTypes\"],g=new Set([\"validate\",\"serialize\",\"parse\",\"wrapper\",\"root\",\"schema\",\"keyword\",\"pattern\",\"formats\",\"validate$data\",\"func\",\"obj\",\"Error\"]),v={errorDataPath:\"\",format:\"`validateFormats: false` can be used instead.\",nullable:'\"nullable\" keyword is supported by default.',jsonPointers:\"Deprecated jsPropertySyntax can be used instead.\",extendRefs:\"Deprecated ignoreKeywordsWithRef can be used instead.\",missingRefs:\"Pass empty schema with $id that should be ignored to ajv.addSchema.\",processCode:\"Use option `code: {process: (code, schemaEnv: object) => string}`\",sourceCode:\"Use option `code: {source: true}`\",strictDefaults:\"It is default now, see option `strict`.\",strictKeywords:\"It is default now, see option `strict`.\",uniqueItems:'\"uniqueItems\" keyword is always validated.',unknownFormats:\"Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).\",cache:\"Map is used as cache, schema object as key.\",serialize:\"Map is used as cache, schema object as key.\",ajvErrors:\"It is default now.\"},w={ignoreKeywordsWithRef:\"\",jsPropertySyntax:\"\",unicode:'\"minLength\"/\"maxLength\" account for unicode characters by default.'};function E(e){var t,r,n,s,o,i,a,c,l,u,d,f,h,y,g,v,w,E,$,_,b,S,O,P,x;const N=e.strict,I=null===(t=e.code)||void 0===t?void 0:t.optimize,j=!0===I||void 0===I?1:I||0,R=null!==(n=null===(r=e.code)||void 0===r?void 0:r.regExp)&&void 0!==n?n:m,A=null!==(s=e.uriResolver)&&void 0!==s?s:p.default;return{strictSchema:null===(i=null!==(o=e.strictSchema)&&void 0!==o?o:N)||void 0===i||i,strictNumbers:null===(c=null!==(a=e.strictNumbers)&&void 0!==a?a:N)||void 0===c||c,strictTypes:null!==(u=null!==(l=e.strictTypes)&&void 0!==l?l:N)&&void 0!==u?u:\"log\",strictTuples:null!==(f=null!==(d=e.strictTuples)&&void 0!==d?d:N)&&void 0!==f?f:\"log\",strictRequired:null!==(y=null!==(h=e.strictRequired)&&void 0!==h?h:N)&&void 0!==y&&y,code:e.code?{...e.code,optimize:j,regExp:R}:{optimize:j,regExp:R},loopRequired:null!==(g=e.loopRequired)&&void 0!==g?g:200,loopEnum:null!==(v=e.loopEnum)&&void 0!==v?v:200,meta:null===(w=e.meta)||void 0===w||w,messages:null===(E=e.messages)||void 0===E||E,inlineRefs:null===($=e.inlineRefs)||void 0===$||$,schemaId:null!==(_=e.schemaId)&&void 0!==_?_:\"$id\",addUsedSchema:null===(b=e.addUsedSchema)||void 0===b||b,validateSchema:null===(S=e.validateSchema)||void 0===S||S,validateFormats:null===(O=e.validateFormats)||void 0===O||O,unicodeRegExp:null===(P=e.unicodeRegExp)||void 0===P||P,int32range:null===(x=e.int32range)||void 0===x||x,uriResolver:A}}class ${constructor(e={}){this.schemas={},this.refs={},this.formats={},this._compilations=new Set,this._loading={},this._cache=new Map,e=this.opts={...e,...E(e)};const{es5:t,lines:r}=this.opts.code;this.scope=new l.ValueScope({scope:{},prefixes:g,es5:t,lines:r}),this.logger=function(e){if(!1===e)return N;if(void 0===e)return console;if(e.log&&e.warn&&e.error)return e;throw new Error(\"logger must implement log, warn and error methods\")}(e.logger);const n=e.validateFormats;e.validateFormats=!1,this.RULES=(0,a.getRules)(),_.call(this,v,e,\"NOT SUPPORTED\"),_.call(this,w,e,\"DEPRECATED\",\"warn\"),this._metaOpts=x.call(this),e.formats&&O.call(this),this._addVocabularies(),this._addDefaultMetaSchema(),e.keywords&&P.call(this,e.keywords),\"object\"==typeof e.meta&&this.addMetaSchema(e.meta),S.call(this),e.validateFormats=n}_addVocabularies(){this.addKeyword(\"$async\")}_addDefaultMetaSchema(){const{$data:e,meta:t,schemaId:r}=this.opts;let n=h;\"id\"===r&&(n={...h},n.id=n.$id,delete n.$id),t&&e&&this.addMetaSchema(n,n[r],!1)}defaultMeta(){const{meta:e,schemaId:t}=this.opts;return this.opts.defaultMeta=\"object\"==typeof e?e[t]||e:void 0}validate(e,t){let r;if(\"string\"==typeof e){if(r=this.getSchema(e),!r)throw new Error(`no schema with key or ref \"${e}\"`)}else r=this.compile(e);const n=r(t);return\"$async\"in r||(this.errors=r.errors),n}compile(e,t){const r=this._addSchema(e,t);return r.validate||this._compileSchemaEnv(r)}compileAsync(e,t){if(\"function\"!=typeof this.opts.loadSchema)throw new Error(\"options.loadSchema should be a function\");const{loadSchema:r}=this.opts;return n.call(this,e,t);async function n(e,t){await s.call(this,e.$schema);const r=this._addSchema(e,t);return r.validate||o.call(this,r)}async function s(e){e&&!this.getSchema(e)&&await n.call(this,{$ref:e},!0)}async function o(e){try{return this._compileSchemaEnv(e)}catch(t){if(!(t instanceof i.default))throw t;return a.call(this,t),await c.call(this,t.missingSchema),o.call(this,e)}}function a({missingSchema:e,missingRef:t}){if(this.refs[e])throw new Error(`AnySchema ${e} is loaded but ${t} cannot be resolved`)}async function c(e){const r=await l.call(this,e);this.refs[e]||await s.call(this,r.$schema),this.refs[e]||this.addSchema(r,e,t)}async function l(e){const t=this._loading[e];if(t)return t;try{return await(this._loading[e]=r(e))}finally{delete this._loading[e]}}}addSchema(e,t,r,n=this.opts.validateSchema){if(Array.isArray(e)){for(const t of e)this.addSchema(t,void 0,r,n);return this}let s;if(\"object\"==typeof e){const{schemaId:t}=this.opts;if(s=e[t],void 0!==s&&\"string\"!=typeof s)throw new Error(`schema ${t} must be string`)}return t=(0,u.normalizeId)(t||s),this._checkUnique(t),this.schemas[t]=this._addSchema(e,r,t,n,!0),this}addMetaSchema(e,t,r=this.opts.validateSchema){return this.addSchema(e,t,!0,r),this}validateSchema(e,t){if(\"boolean\"==typeof e)return!0;let r;if(r=e.$schema,void 0!==r&&\"string\"!=typeof r)throw new Error(\"$schema must be a string\");if(r=r||this.opts.defaultMeta||this.defaultMeta(),!r)return this.logger.warn(\"meta-schema not available\"),this.errors=null,!0;const n=this.validate(r,e);if(!n&&t){const e=\"schema is invalid: \"+this.errorsText();if(\"log\"!==this.opts.validateSchema)throw new Error(e);this.logger.error(e)}return n}getSchema(e){let t;for(;\"string\"==typeof(t=b.call(this,e));)e=t;if(void 0===t){const{schemaId:r}=this.opts,n=new c.SchemaEnv({schema:{},schemaId:r});if(t=c.resolveSchema.call(this,n,e),!t)return;this.refs[e]=t}return t.validate||this._compileSchemaEnv(t)}removeSchema(e){if(e instanceof RegExp)return this._removeAllSchemas(this.schemas,e),this._removeAllSchemas(this.refs,e),this;switch(typeof e){case\"undefined\":return this._removeAllSchemas(this.schemas),this._removeAllSchemas(this.refs),this._cache.clear(),this;case\"string\":{const t=b.call(this,e);return\"object\"==typeof t&&this._cache.delete(t.schema),delete this.schemas[e],delete this.refs[e],this}case\"object\":{const t=e;this._cache.delete(t);let r=e[this.opts.schemaId];return r&&(r=(0,u.normalizeId)(r),delete this.schemas[r],delete this.refs[r]),this}default:throw new Error(\"ajv.removeSchema: invalid parameter\")}}addVocabulary(e){for(const t of e)this.addKeyword(t);return this}addKeyword(e,t){let r;if(\"string\"==typeof e)r=e,\"object\"==typeof t&&(this.logger.warn(\"these parameters are deprecated, see docs for addKeyword\"),t.keyword=r);else{if(\"object\"!=typeof e||void 0!==t)throw new Error(\"invalid addKeywords parameters\");if(r=(t=e).keyword,Array.isArray(r)&&!r.length)throw new Error(\"addKeywords: keyword must be string or non-empty array\")}if(j.call(this,r,t),!t)return(0,f.eachItem)(r,(e=>R.call(this,e))),this;T.call(this,t);const n={...t,type:(0,d.getJSONTypes)(t.type),schemaType:(0,d.getJSONTypes)(t.schemaType)};return(0,f.eachItem)(r,0===n.type.length?e=>R.call(this,e,n):e=>n.type.forEach((t=>R.call(this,e,n,t)))),this}getKeyword(e){const t=this.RULES.all[e];return\"object\"==typeof t?t.definition:!!t}removeKeyword(e){const{RULES:t}=this;delete t.keywords[e],delete t.all[e];for(const r of t.rules){const t=r.rules.findIndex((t=>t.keyword===e));t>=0&&r.rules.splice(t,1)}return this}addFormat(e,t){return\"string\"==typeof t&&(t=new RegExp(t)),this.formats[e]=t,this}errorsText(e=this.errors,{separator:t=\", \",dataVar:r=\"data\"}={}){return e&&0!==e.length?e.map((e=>`${r}${e.instancePath} ${e.message}`)).reduce(((e,r)=>e+t+r)):\"No errors\"}$dataMetaSchema(e,t){const r=this.RULES.all;e=JSON.parse(JSON.stringify(e));for(const n of t){const t=n.split(\"/\").slice(1);let s=e;for(const e of t)s=s[e];for(const e in r){const t=r[e];if(\"object\"!=typeof t)continue;const{$data:n}=t.definition,o=s[e];n&&o&&(s[e]=D(o))}}return e}_removeAllSchemas(e,t){for(const r in e){const n=e[r];t&&!t.test(r)||(\"string\"==typeof n?delete e[r]:n&&!n.meta&&(this._cache.delete(n.schema),delete e[r]))}}_addSchema(e,t,r,n=this.opts.validateSchema,s=this.opts.addUsedSchema){let o;const{schemaId:i}=this.opts;if(\"object\"==typeof e)o=e[i];else{if(this.opts.jtd)throw new Error(\"schema must be object\");if(\"boolean\"!=typeof e)throw new Error(\"schema must be object or boolean\")}let a=this._cache.get(e);if(void 0!==a)return a;r=(0,u.normalizeId)(o||r);const l=u.getSchemaRefs.call(this,e,r);return a=new c.SchemaEnv({schema:e,schemaId:i,meta:t,baseId:r,localRefs:l}),this._cache.set(a.schema,a),s&&!r.startsWith(\"#\")&&(r&&this._checkUnique(r),this.refs[r]=a),n&&this.validateSchema(e,!0),a}_checkUnique(e){if(this.schemas[e]||this.refs[e])throw new Error(`schema with key or id \"${e}\" already exists`)}_compileSchemaEnv(e){if(e.meta?this._compileMetaSchema(e):c.compileSchema.call(this,e),!e.validate)throw new Error(\"ajv implementation error\");return e.validate}_compileMetaSchema(e){const t=this.opts;this.opts=this._metaOpts;try{c.compileSchema.call(this,e)}finally{this.opts=t}}}function _(e,t,r,n=\"error\"){for(const s in e){const o=s;o in t&&this.logger[n](`${r}: option ${s}. ${e[o]}`)}}function b(e){return e=(0,u.normalizeId)(e),this.schemas[e]||this.refs[e]}function S(){const e=this.opts.schemas;if(e)if(Array.isArray(e))this.addSchema(e);else for(const t in e)this.addSchema(e[t],t)}function O(){for(const e in this.opts.formats){const t=this.opts.formats[e];t&&this.addFormat(e,t)}}function P(e){if(Array.isArray(e))this.addVocabulary(e);else{this.logger.warn(\"keywords option as map is deprecated, pass array\");for(const t in e){const r=e[t];r.keyword||(r.keyword=t),this.addKeyword(r)}}}function x(){const e={...this.opts};for(const t of y)delete e[t];return e}t.default=$,$.ValidationError=o.default,$.MissingRefError=i.default;const N={log(){},warn(){},error(){}},I=/^[a-z_$][a-z0-9_$:-]*$/i;function j(e,t){const{RULES:r}=this;if((0,f.eachItem)(e,(e=>{if(r.keywords[e])throw new Error(`Keyword ${e} is already defined`);if(!I.test(e))throw new Error(`Keyword ${e} has invalid name`)})),t&&t.$data&&!(\"code\"in t)&&!(\"validate\"in t))throw new Error('$data keyword must have \"code\" or \"validate\" function')}function R(e,t,r){var n;const s=null==t?void 0:t.post;if(r&&s)throw new Error('keyword with \"post\" flag cannot have \"type\"');const{RULES:o}=this;let i=s?o.post:o.rules.find((({type:e})=>e===r));if(i||(i={type:r,rules:[]},o.rules.push(i)),o.keywords[e]=!0,!t)return;const a={keyword:e,definition:{...t,type:(0,d.getJSONTypes)(t.type),schemaType:(0,d.getJSONTypes)(t.schemaType)}};t.before?A.call(this,i,a,t.before):i.rules.push(a),o.all[e]=a,null===(n=t.implements)||void 0===n||n.forEach((e=>this.addKeyword(e)))}function A(e,t,r){const n=e.rules.findIndex((e=>e.keyword===r));n>=0?e.rules.splice(n,0,t):(e.rules.push(t),this.logger.warn(`rule ${r} is not defined`))}function T(e){let{metaSchema:t}=e;void 0!==t&&(e.$data&&this.opts.$data&&(t=D(t)),e.validateSchema=this.compile(t,!0))}const C={$ref:\"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#\"};function D(e){return{anyOf:[e,C]}}},283:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(4063);n.code='require(\"ajv/dist/runtime/equal\").default',t.default=n},6913:(e,t)=>{\"use strict\";function r(e){const t=e.length;let r,n=0,s=0;for(;s<t;)n++,r=e.charCodeAt(s++),r>=55296&&r<=56319&&s<t&&(r=e.charCodeAt(s),56320==(64512&r)&&s++);return n}Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=r,r.code='require(\"ajv/dist/runtime/ucs2length\").default'},2972:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(540);n.code='require(\"ajv/dist/runtime/uri\").default',t.default=n},7173:(e,t)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});class r extends Error{constructor(e){super(\"validation failed\"),this.errors=e,this.ajv=this.validation=!0}}t.default=r},558:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.validateAdditionalItems=void 0;const n=r(5899),s=r(320),o={keyword:\"additionalItems\",type:\"array\",schemaType:[\"boolean\",\"object\"],before:\"uniqueItems\",error:{message:({params:{len:e}})=>n.str`must NOT have more than ${e} items`,params:({params:{len:e}})=>n._`{limit: ${e}}`},code(e){const{parentSchema:t,it:r}=e,{items:n}=t;Array.isArray(n)?i(e,n):(0,s.checkStrictMode)(r,'\"additionalItems\" is ignored when \"items\" is not an array of schemas')}};function i(e,t){const{gen:r,schema:o,data:i,keyword:a,it:c}=e;c.items=!0;const l=r.const(\"len\",n._`${i}.length`);if(!1===o)e.setParams({len:t.length}),e.pass(n._`${l} <= ${t.length}`);else if(\"object\"==typeof o&&!(0,s.alwaysValidSchema)(c,o)){const o=r.var(\"valid\",n._`${l} <= ${t.length}`);r.if((0,n.not)(o),(()=>function(o){r.forRange(\"i\",t.length,l,(t=>{e.subschema({keyword:a,dataProp:t,dataPropType:s.Type.Num},o),c.allErrors||r.if((0,n.not)(o),(()=>r.break()))}))}(o))),e.ok(o)}}t.validateAdditionalItems=i,t.default=o},2038:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(7470),s=r(5899),o=r(6934),i=r(320),a={keyword:\"additionalProperties\",type:[\"object\"],schemaType:[\"boolean\",\"object\"],allowUndefined:!0,trackErrors:!0,error:{message:\"must NOT have additional properties\",params:({params:e})=>s._`{additionalProperty: ${e.additionalProperty}}`},code(e){const{gen:t,schema:r,parentSchema:a,data:c,errsCount:l,it:u}=e;if(!l)throw new Error(\"ajv implementation error\");const{allErrors:d,opts:f}=u;if(u.props=!0,\"all\"!==f.removeAdditional&&(0,i.alwaysValidSchema)(u,r))return;const h=(0,n.allSchemaProperties)(a.properties),p=(0,n.allSchemaProperties)(a.patternProperties);function m(e){t.code(s._`delete ${c}[${e}]`)}function y(n){if(\"all\"===f.removeAdditional||f.removeAdditional&&!1===r)m(n);else{if(!1===r)return e.setParams({additionalProperty:n}),e.error(),void(d||t.break());if(\"object\"==typeof r&&!(0,i.alwaysValidSchema)(u,r)){const r=t.name(\"valid\");\"failing\"===f.removeAdditional?(g(n,r,!1),t.if((0,s.not)(r),(()=>{e.reset(),m(n)}))):(g(n,r),d||t.if((0,s.not)(r),(()=>t.break())))}}}function g(t,r,n){const s={keyword:\"additionalProperties\",dataProp:t,dataPropType:i.Type.Str};!1===n&&Object.assign(s,{compositeRule:!0,createErrors:!1,allErrors:!1}),e.subschema(s,r)}t.forIn(\"key\",c,(r=>{h.length||p.length?t.if(function(r){let o;if(h.length>8){const e=(0,i.schemaRefOrVal)(u,a.properties,\"properties\");o=(0,n.isOwnProperty)(t,e,r)}else o=h.length?(0,s.or)(...h.map((e=>s._`${r} === ${e}`))):s.nil;return p.length&&(o=(0,s.or)(o,...p.map((t=>s._`${(0,n.usePattern)(e,t)}.test(${r})`)))),(0,s.not)(o)}(r),(()=>y(r))):y(r)})),e.ok(s._`${l} === ${o.default.errors}`)}};t.default=a},6386:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(320),s={keyword:\"allOf\",schemaType:\"array\",code(e){const{gen:t,schema:r,it:s}=e;if(!Array.isArray(r))throw new Error(\"ajv implementation error\");const o=t.name(\"valid\");r.forEach(((t,r)=>{if((0,n.alwaysValidSchema)(s,t))return;const i=e.subschema({keyword:\"allOf\",schemaProp:r},o);e.ok(o),e.mergeEvaluated(i)}))}};t.default=s},3235:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n={keyword:\"anyOf\",schemaType:\"array\",trackErrors:!0,code:r(7470).validateUnion,error:{message:\"must match a schema in anyOf\"}};t.default=n},6065:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(320),o={keyword:\"contains\",type:\"array\",schemaType:[\"object\",\"boolean\"],before:\"uniqueItems\",trackErrors:!0,error:{message:({params:{min:e,max:t}})=>void 0===t?n.str`must contain at least ${e} valid item(s)`:n.str`must contain at least ${e} and no more than ${t} valid item(s)`,params:({params:{min:e,max:t}})=>void 0===t?n._`{minContains: ${e}}`:n._`{minContains: ${e}, maxContains: ${t}}`},code(e){const{gen:t,schema:r,parentSchema:o,data:i,it:a}=e;let c,l;const{minContains:u,maxContains:d}=o;a.opts.next?(c=void 0===u?1:u,l=d):c=1;const f=t.const(\"len\",n._`${i}.length`);if(e.setParams({min:c,max:l}),void 0===l&&0===c)return void(0,s.checkStrictMode)(a,'\"minContains\" == 0 without \"maxContains\": \"contains\" keyword ignored');if(void 0!==l&&c>l)return(0,s.checkStrictMode)(a,'\"minContains\" > \"maxContains\" is always invalid'),void e.fail();if((0,s.alwaysValidSchema)(a,r)){let t=n._`${f} >= ${c}`;return void 0!==l&&(t=n._`${t} && ${f} <= ${l}`),void e.pass(t)}a.items=!0;const h=t.name(\"valid\");function p(){const e=t.name(\"_valid\"),r=t.let(\"count\",0);m(e,(()=>t.if(e,(()=>function(e){t.code(n._`${e}++`),void 0===l?t.if(n._`${e} >= ${c}`,(()=>t.assign(h,!0).break())):(t.if(n._`${e} > ${l}`,(()=>t.assign(h,!1).break())),1===c?t.assign(h,!0):t.if(n._`${e} >= ${c}`,(()=>t.assign(h,!0))))}(r)))))}function m(r,n){t.forRange(\"i\",0,f,(t=>{e.subschema({keyword:\"contains\",dataProp:t,dataPropType:s.Type.Num,compositeRule:!0},r),n()}))}void 0===l&&1===c?m(h,(()=>t.if(h,(()=>t.break())))):0===c?(t.let(h,!0),void 0!==l&&t.if(n._`${i}.length > 0`,p)):(t.let(h,!1),p()),e.result(h,(()=>e.reset()))}};t.default=o},4839:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.validateSchemaDeps=t.validatePropertyDeps=t.error=void 0;const n=r(5899),s=r(320),o=r(7470);t.error={message:({params:{property:e,depsCount:t,deps:r}})=>{const s=1===t?\"property\":\"properties\";return n.str`must have ${s} ${r} when property ${e} is present`},params:({params:{property:e,depsCount:t,deps:r,missingProperty:s}})=>n._`{property: ${e},\n    missingProperty: ${s},\n    depsCount: ${t},\n    deps: ${r}}`};const i={keyword:\"dependencies\",type:\"object\",schemaType:\"object\",error:t.error,code(e){const[t,r]=function({schema:e}){const t={},r={};for(const n in e)\"__proto__\"!==n&&((Array.isArray(e[n])?t:r)[n]=e[n]);return[t,r]}(e);a(e,t),c(e,r)}};function a(e,t=e.schema){const{gen:r,data:s,it:i}=e;if(0===Object.keys(t).length)return;const a=r.let(\"missing\");for(const c in t){const l=t[c];if(0===l.length)continue;const u=(0,o.propertyInData)(r,s,c,i.opts.ownProperties);e.setParams({property:c,depsCount:l.length,deps:l.join(\", \")}),i.allErrors?r.if(u,(()=>{for(const t of l)(0,o.checkReportMissingProp)(e,t)})):(r.if(n._`${u} && (${(0,o.checkMissingProp)(e,l,a)})`),(0,o.reportMissingProp)(e,a),r.else())}}function c(e,t=e.schema){const{gen:r,data:n,keyword:i,it:a}=e,c=r.name(\"valid\");for(const l in t)(0,s.alwaysValidSchema)(a,t[l])||(r.if((0,o.propertyInData)(r,n,l,a.opts.ownProperties),(()=>{const t=e.subschema({keyword:i,schemaProp:l},c);e.mergeValidEvaluated(t,c)}),(()=>r.var(c,!0))),e.ok(c))}t.validatePropertyDeps=a,t.validateSchemaDeps=c,t.default=i},6100:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(320),o={keyword:\"if\",schemaType:[\"object\",\"boolean\"],trackErrors:!0,error:{message:({params:e})=>n.str`must match \"${e.ifClause}\" schema`,params:({params:e})=>n._`{failingKeyword: ${e.ifClause}}`},code(e){const{gen:t,parentSchema:r,it:o}=e;void 0===r.then&&void 0===r.else&&(0,s.checkStrictMode)(o,'\"if\" without \"then\" and \"else\" is ignored');const a=i(o,\"then\"),c=i(o,\"else\");if(!a&&!c)return;const l=t.let(\"valid\",!0),u=t.name(\"_valid\");if(function(){const t=e.subschema({keyword:\"if\",compositeRule:!0,createErrors:!1,allErrors:!1},u);e.mergeEvaluated(t)}(),e.reset(),a&&c){const r=t.let(\"ifClause\");e.setParams({ifClause:r}),t.if(u,d(\"then\",r),d(\"else\",r))}else a?t.if(u,d(\"then\")):t.if((0,n.not)(u),d(\"else\"));function d(r,s){return()=>{const o=e.subschema({keyword:r},u);t.assign(l,u),e.mergeValidEvaluated(o,l),s?t.assign(s,n._`${r}`):e.setParams({ifClause:r})}}e.pass(l,(()=>e.error(!0)))}};function i(e,t){const r=e.schema[t];return void 0!==r&&!(0,s.alwaysValidSchema)(e,r)}t.default=o},222:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(558),s=r(4531),o=r(4820),i=r(3785),a=r(6065),c=r(4839),l=r(7620),u=r(2038),d=r(3966),f=r(5975),h=r(2624),p=r(3235),m=r(7706),y=r(6386),g=r(6100),v=r(8957);t.default=function(e=!1){const t=[h.default,p.default,m.default,y.default,g.default,v.default,l.default,u.default,c.default,d.default,f.default];return e?t.push(s.default,i.default):t.push(n.default,o.default),t.push(a.default),t}},4820:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.validateTuple=void 0;const n=r(5899),s=r(320),o=r(7470),i={keyword:\"items\",type:\"array\",schemaType:[\"object\",\"array\",\"boolean\"],before:\"uniqueItems\",code(e){const{schema:t,it:r}=e;if(Array.isArray(t))return a(e,\"additionalItems\",t);r.items=!0,(0,s.alwaysValidSchema)(r,t)||e.ok((0,o.validateArray)(e))}};function a(e,t,r=e.schema){const{gen:o,parentSchema:i,data:a,keyword:c,it:l}=e;!function(e){const{opts:n,errSchemaPath:o}=l,i=r.length,a=i===e.minItems&&(i===e.maxItems||!1===e[t]);if(n.strictTuples&&!a){const e=`\"${c}\" is ${i}-tuple, but minItems or maxItems/${t} are not specified or different at path \"${o}\"`;(0,s.checkStrictMode)(l,e,n.strictTuples)}}(i),l.opts.unevaluated&&r.length&&!0!==l.items&&(l.items=s.mergeEvaluated.items(o,r.length,l.items));const u=o.name(\"valid\"),d=o.const(\"len\",n._`${a}.length`);r.forEach(((t,r)=>{(0,s.alwaysValidSchema)(l,t)||(o.if(n._`${d} > ${r}`,(()=>e.subschema({keyword:c,schemaProp:r,dataProp:r},u))),e.ok(u))}))}t.validateTuple=a,t.default=i},3785:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(320),o=r(7470),i=r(558),a={keyword:\"items\",type:\"array\",schemaType:[\"object\",\"boolean\"],before:\"uniqueItems\",error:{message:({params:{len:e}})=>n.str`must NOT have more than ${e} items`,params:({params:{len:e}})=>n._`{limit: ${e}}`},code(e){const{schema:t,parentSchema:r,it:n}=e,{prefixItems:a}=r;n.items=!0,(0,s.alwaysValidSchema)(n,t)||(a?(0,i.validateAdditionalItems)(e,a):e.ok((0,o.validateArray)(e)))}};t.default=a},2624:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(320),s={keyword:\"not\",schemaType:[\"object\",\"boolean\"],trackErrors:!0,code(e){const{gen:t,schema:r,it:s}=e;if((0,n.alwaysValidSchema)(s,r))return void e.fail();const o=t.name(\"valid\");e.subschema({keyword:\"not\",compositeRule:!0,createErrors:!1,allErrors:!1},o),e.failResult(o,(()=>e.reset()),(()=>e.error()))},error:{message:\"must NOT be valid\"}};t.default=s},7706:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(320),o={keyword:\"oneOf\",schemaType:\"array\",trackErrors:!0,error:{message:\"must match exactly one schema in oneOf\",params:({params:e})=>n._`{passingSchemas: ${e.passing}}`},code(e){const{gen:t,schema:r,parentSchema:o,it:i}=e;if(!Array.isArray(r))throw new Error(\"ajv implementation error\");if(i.opts.discriminator&&o.discriminator)return;const a=r,c=t.let(\"valid\",!1),l=t.let(\"passing\",null),u=t.name(\"_valid\");e.setParams({passing:l}),t.block((function(){a.forEach(((r,o)=>{let a;(0,s.alwaysValidSchema)(i,r)?t.var(u,!0):a=e.subschema({keyword:\"oneOf\",schemaProp:o,compositeRule:!0},u),o>0&&t.if(n._`${u} && ${c}`).assign(c,!1).assign(l,n._`[${l}, ${o}]`).else(),t.if(u,(()=>{t.assign(c,!0),t.assign(l,o),a&&e.mergeEvaluated(a,n.Name)}))}))})),e.result(c,(()=>e.reset()),(()=>e.error(!0)))}};t.default=o},5975:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(7470),s=r(5899),o=r(320),i=r(320),a={keyword:\"patternProperties\",type:\"object\",schemaType:\"object\",code(e){const{gen:t,schema:r,data:a,parentSchema:c,it:l}=e,{opts:u}=l,d=(0,n.allSchemaProperties)(r),f=d.filter((e=>(0,o.alwaysValidSchema)(l,r[e])));if(0===d.length||f.length===d.length&&(!l.opts.unevaluated||!0===l.props))return;const h=u.strictSchema&&!u.allowMatchingProperties&&c.properties,p=t.name(\"valid\");!0===l.props||l.props instanceof s.Name||(l.props=(0,i.evaluatedPropsToName)(t,l.props));const{props:m}=l;function y(e){for(const t in h)new RegExp(e).test(t)&&(0,o.checkStrictMode)(l,`property ${t} matches pattern ${e} (use allowMatchingProperties)`)}function g(r){t.forIn(\"key\",a,(o=>{t.if(s._`${(0,n.usePattern)(e,r)}.test(${o})`,(()=>{const n=f.includes(r);n||e.subschema({keyword:\"patternProperties\",schemaProp:r,dataProp:o,dataPropType:i.Type.Str},p),l.opts.unevaluated&&!0!==m?t.assign(s._`${m}[${o}]`,!0):n||l.allErrors||t.if((0,s.not)(p),(()=>t.break()))}))}))}!function(){for(const e of d)h&&y(e),l.allErrors?g(e):(t.var(p,!0),g(e),t.if(p))}()}};t.default=a},4531:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(4820),s={keyword:\"prefixItems\",type:\"array\",schemaType:[\"array\"],before:\"uniqueItems\",code:e=>(0,n.validateTuple)(e,\"items\")};t.default=s},3966:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5032),s=r(7470),o=r(320),i=r(2038),a={keyword:\"properties\",type:\"object\",schemaType:\"object\",code(e){const{gen:t,schema:r,parentSchema:a,data:c,it:l}=e;\"all\"===l.opts.removeAdditional&&void 0===a.additionalProperties&&i.default.code(new n.KeywordCxt(l,i.default,\"additionalProperties\"));const u=(0,s.allSchemaProperties)(r);for(const e of u)l.definedProperties.add(e);l.opts.unevaluated&&u.length&&!0!==l.props&&(l.props=o.mergeEvaluated.props(t,(0,o.toHash)(u),l.props));const d=u.filter((e=>!(0,o.alwaysValidSchema)(l,r[e])));if(0===d.length)return;const f=t.name(\"valid\");for(const r of d)h(r)?p(r):(t.if((0,s.propertyInData)(t,c,r,l.opts.ownProperties)),p(r),l.allErrors||t.else().var(f,!0),t.endIf()),e.it.definedProperties.add(r),e.ok(f);function h(e){return l.opts.useDefaults&&!l.compositeRule&&void 0!==r[e].default}function p(t){e.subschema({keyword:\"properties\",schemaProp:t,dataProp:t},f)}}};t.default=a},7620:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(320),o={keyword:\"propertyNames\",type:\"object\",schemaType:[\"object\",\"boolean\"],error:{message:\"property name must be valid\",params:({params:e})=>n._`{propertyName: ${e.propertyName}}`},code(e){const{gen:t,schema:r,data:o,it:i}=e;if((0,s.alwaysValidSchema)(i,r))return;const a=t.name(\"valid\");t.forIn(\"key\",o,(r=>{e.setParams({propertyName:r}),e.subschema({keyword:\"propertyNames\",data:r,dataTypes:[\"string\"],propertyName:r,compositeRule:!0},a),t.if((0,n.not)(a),(()=>{e.error(!0),i.allErrors||t.break()}))})),e.ok(a)}};t.default=o},8957:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(320),s={keyword:[\"then\",\"else\"],schemaType:[\"object\",\"boolean\"],code({keyword:e,parentSchema:t,it:r}){void 0===t.if&&(0,n.checkStrictMode)(r,`\"${e}\" without \"if\" is ignored`)}};t.default=s},7470:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.validateUnion=t.validateArray=t.usePattern=t.callValidateCode=t.schemaProperties=t.allSchemaProperties=t.noPropertyInData=t.propertyInData=t.isOwnProperty=t.hasPropFunc=t.reportMissingProp=t.checkMissingProp=t.checkReportMissingProp=void 0;const n=r(5899),s=r(320),o=r(6934),i=r(320);function a(e){return e.scopeValue(\"func\",{ref:Object.prototype.hasOwnProperty,code:n._`Object.prototype.hasOwnProperty`})}function c(e,t,r){return n._`${a(e)}.call(${t}, ${r})`}function l(e,t,r,s){const o=n._`${t}${(0,n.getProperty)(r)} === undefined`;return s?(0,n.or)(o,(0,n.not)(c(e,t,r))):o}function u(e){return e?Object.keys(e).filter((e=>\"__proto__\"!==e)):[]}t.checkReportMissingProp=function(e,t){const{gen:r,data:s,it:o}=e;r.if(l(r,s,t,o.opts.ownProperties),(()=>{e.setParams({missingProperty:n._`${t}`},!0),e.error()}))},t.checkMissingProp=function({gen:e,data:t,it:{opts:r}},s,o){return(0,n.or)(...s.map((s=>(0,n.and)(l(e,t,s,r.ownProperties),n._`${o} = ${s}`))))},t.reportMissingProp=function(e,t){e.setParams({missingProperty:t},!0),e.error()},t.hasPropFunc=a,t.isOwnProperty=c,t.propertyInData=function(e,t,r,s){const o=n._`${t}${(0,n.getProperty)(r)} !== undefined`;return s?n._`${o} && ${c(e,t,r)}`:o},t.noPropertyInData=l,t.allSchemaProperties=u,t.schemaProperties=function(e,t){return u(t).filter((r=>!(0,s.alwaysValidSchema)(e,t[r])))},t.callValidateCode=function({schemaCode:e,data:t,it:{gen:r,topSchemaRef:s,schemaPath:i,errorPath:a},it:c},l,u,d){const f=d?n._`${e}, ${t}, ${s}${i}`:t,h=[[o.default.instancePath,(0,n.strConcat)(o.default.instancePath,a)],[o.default.parentData,c.parentData],[o.default.parentDataProperty,c.parentDataProperty],[o.default.rootData,o.default.rootData]];c.opts.dynamicRef&&h.push([o.default.dynamicAnchors,o.default.dynamicAnchors]);const p=n._`${f}, ${r.object(...h)}`;return u!==n.nil?n._`${l}.call(${u}, ${p})`:n._`${l}(${p})`};const d=n._`new RegExp`;t.usePattern=function({gen:e,it:{opts:t}},r){const s=t.unicodeRegExp?\"u\":\"\",{regExp:o}=t.code,a=o(r,s);return e.scopeValue(\"pattern\",{key:a.toString(),ref:a,code:n._`${\"new RegExp\"===o.code?d:(0,i.useFunc)(e,o)}(${r}, ${s})`})},t.validateArray=function(e){const{gen:t,data:r,keyword:o,it:i}=e,a=t.name(\"valid\");if(i.allErrors){const e=t.let(\"valid\",!0);return c((()=>t.assign(e,!1))),e}return t.var(a,!0),c((()=>t.break())),a;function c(i){const c=t.const(\"len\",n._`${r}.length`);t.forRange(\"i\",0,c,(r=>{e.subschema({keyword:o,dataProp:r,dataPropType:s.Type.Num},a),t.if((0,n.not)(a),i)}))}},t.validateUnion=function(e){const{gen:t,schema:r,keyword:o,it:i}=e;if(!Array.isArray(r))throw new Error(\"ajv implementation error\");if(r.some((e=>(0,s.alwaysValidSchema)(i,e)))&&!i.opts.unevaluated)return;const a=t.let(\"valid\",!1),c=t.name(\"_valid\");t.block((()=>r.forEach(((r,s)=>{const i=e.subschema({keyword:o,schemaProp:s,compositeRule:!0},c);t.assign(a,n._`${a} || ${c}`),e.mergeValidEvaluated(i,c)||t.if((0,n.not)(a))})))),e.result(a,(()=>e.reset()),(()=>e.error(!0)))}},194:(e,t)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const r={keyword:\"id\",code(){throw new Error('NOT SUPPORTED: keyword \"id\", use \"$id\" for schema ID')}};t.default=r},4901:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(194),s=r(3898),o=[\"$schema\",\"$id\",\"$defs\",\"$vocabulary\",{keyword:\"$comment\"},\"definitions\",n.default,s.default];t.default=o},3898:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.callRef=t.getValidate=void 0;const n=r(433),s=r(7470),o=r(5899),i=r(6934),a=r(7760),c=r(320),l={keyword:\"$ref\",schemaType:\"string\",code(e){const{gen:t,schema:r,it:s}=e,{baseId:i,schemaEnv:c,validateName:l,opts:f,self:h}=s,{root:p}=c;if((\"#\"===r||\"#/\"===r)&&i===p.baseId)return function(){if(c===p)return d(e,l,c,c.$async);const r=t.scopeValue(\"root\",{ref:p});return d(e,o._`${r}.validate`,p,p.$async)}();const m=a.resolveRef.call(h,p,i,r);if(void 0===m)throw new n.default(s.opts.uriResolver,i,r);return m instanceof a.SchemaEnv?function(t){const r=u(e,t);d(e,r,t,t.$async)}(m):function(n){const s=t.scopeValue(\"schema\",!0===f.code.source?{ref:n,code:(0,o.stringify)(n)}:{ref:n}),i=t.name(\"valid\"),a=e.subschema({schema:n,dataTypes:[],schemaPath:o.nil,topSchemaRef:s,errSchemaPath:r},i);e.mergeEvaluated(a),e.ok(i)}(m)}};function u(e,t){const{gen:r}=e;return t.validate?r.scopeValue(\"validate\",{ref:t.validate}):o._`${r.scopeValue(\"wrapper\",{ref:t})}.validate`}function d(e,t,r,n){const{gen:a,it:l}=e,{allErrors:u,schemaEnv:d,opts:f}=l,h=f.passContext?i.default.this:o.nil;function p(e){const t=o._`${e}.errors`;a.assign(i.default.vErrors,o._`${i.default.vErrors} === null ? ${t} : ${i.default.vErrors}.concat(${t})`),a.assign(i.default.errors,o._`${i.default.vErrors}.length`)}function m(e){var t;if(!l.opts.unevaluated)return;const n=null===(t=null==r?void 0:r.validate)||void 0===t?void 0:t.evaluated;if(!0!==l.props)if(n&&!n.dynamicProps)void 0!==n.props&&(l.props=c.mergeEvaluated.props(a,n.props,l.props));else{const t=a.var(\"props\",o._`${e}.evaluated.props`);l.props=c.mergeEvaluated.props(a,t,l.props,o.Name)}if(!0!==l.items)if(n&&!n.dynamicItems)void 0!==n.items&&(l.items=c.mergeEvaluated.items(a,n.items,l.items));else{const t=a.var(\"items\",o._`${e}.evaluated.items`);l.items=c.mergeEvaluated.items(a,t,l.items,o.Name)}}n?function(){if(!d.$async)throw new Error(\"async schema referenced by sync schema\");const r=a.let(\"valid\");a.try((()=>{a.code(o._`await ${(0,s.callValidateCode)(e,t,h)}`),m(t),u||a.assign(r,!0)}),(e=>{a.if(o._`!(${e} instanceof ${l.ValidationError})`,(()=>a.throw(e))),p(e),u||a.assign(r,!1)})),e.ok(r)}():e.result((0,s.callValidateCode)(e,t,h),(()=>m(t)),(()=>p(t)))}t.getValidate=u,t.callRef=d,t.default=l},8402:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(2872),o=r(7760),i=r(320),a={keyword:\"discriminator\",type:\"object\",schemaType:\"object\",error:{message:({params:{discrError:e,tagName:t}})=>e===s.DiscrError.Tag?`tag \"${t}\" must be string`:`value of tag \"${t}\" must be in oneOf`,params:({params:{discrError:e,tag:t,tagName:r}})=>n._`{error: ${e}, tag: ${r}, tagValue: ${t}}`},code(e){const{gen:t,data:r,schema:a,parentSchema:c,it:l}=e,{oneOf:u}=c;if(!l.opts.discriminator)throw new Error(\"discriminator: requires discriminator option\");const d=a.propertyName;if(\"string\"!=typeof d)throw new Error(\"discriminator: requires propertyName\");if(a.mapping)throw new Error(\"discriminator: mapping is not supported\");if(!u)throw new Error(\"discriminator: requires oneOf keyword\");const f=t.let(\"valid\",!1),h=t.const(\"tag\",n._`${r}${(0,n.getProperty)(d)}`);function p(r){const s=t.name(\"valid\"),o=e.subschema({keyword:\"oneOf\",schemaProp:r},s);return e.mergeEvaluated(o,n.Name),s}t.if(n._`typeof ${h} == \"string\"`,(()=>function(){const r=function(){var e;const t={},r=s(c);let n=!0;for(let t=0;t<u.length;t++){let c=u[t];(null==c?void 0:c.$ref)&&!(0,i.schemaHasRulesButRef)(c,l.self.RULES)&&(c=o.resolveRef.call(l.self,l.schemaEnv.root,l.baseId,null==c?void 0:c.$ref),c instanceof o.SchemaEnv&&(c=c.schema));const f=null===(e=null==c?void 0:c.properties)||void 0===e?void 0:e[d];if(\"object\"!=typeof f)throw new Error(`discriminator: oneOf subschemas (or referenced schemas) must have \"properties/${d}\"`);n=n&&(r||s(c)),a(f,t)}if(!n)throw new Error(`discriminator: \"${d}\" must be required`);return t;function s({required:e}){return Array.isArray(e)&&e.includes(d)}function a(e,t){if(e.const)f(e.const,t);else{if(!e.enum)throw new Error(`discriminator: \"properties/${d}\" must have \"const\" or \"enum\"`);for(const r of e.enum)f(r,t)}}function f(e,r){if(\"string\"!=typeof e||e in t)throw new Error(`discriminator: \"${d}\" values must be unique strings`);t[e]=r}}();t.if(!1);for(const e in r)t.elseIf(n._`${h} === ${e}`),t.assign(f,p(r[e]));t.else(),e.error(!1,{discrError:s.DiscrError.Mapping,tag:h,tagName:d}),t.endIf()}()),(()=>e.error(!1,{discrError:s.DiscrError.Tag,tag:h,tagName:d}))),e.ok(f)}};t.default=a},2872:(e,t)=>{\"use strict\";var r;Object.defineProperty(t,\"__esModule\",{value:!0}),t.DiscrError=void 0,(r=t.DiscrError||(t.DiscrError={})).Tag=\"tag\",r.Mapping=\"mapping\"},6568:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(4901),s=r(6823),o=r(222),i=r(4805),a=r(4110),c=[n.default,s.default,(0,o.default)(),i.default,a.metadataVocabulary,a.contentVocabulary];t.default=c},2970:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s={keyword:\"format\",type:[\"number\",\"string\"],schemaType:\"string\",$data:!0,error:{message:({schemaCode:e})=>n.str`must match format \"${e}\"`,params:({schemaCode:e})=>n._`{format: ${e}}`},code(e,t){const{gen:r,data:s,$data:o,schema:i,schemaCode:a,it:c}=e,{opts:l,errSchemaPath:u,schemaEnv:d,self:f}=c;l.validateFormats&&(o?function(){const o=r.scopeValue(\"formats\",{ref:f.formats,code:l.code.formats}),i=r.const(\"fDef\",n._`${o}[${a}]`),c=r.let(\"fType\"),u=r.let(\"format\");r.if(n._`typeof ${i} == \"object\" && !(${i} instanceof RegExp)`,(()=>r.assign(c,n._`${i}.type || \"string\"`).assign(u,n._`${i}.validate`)),(()=>r.assign(c,n._`\"string\"`).assign(u,i))),e.fail$data((0,n.or)(!1===l.strictSchema?n.nil:n._`${a} && !${u}`,function(){const e=d.$async?n._`(${i}.async ? await ${u}(${s}) : ${u}(${s}))`:n._`${u}(${s})`,r=n._`(typeof ${u} == \"function\" ? ${e} : ${u}.test(${s}))`;return n._`${u} && ${u} !== true && ${c} === ${t} && !${r}`}()))}():function(){const o=f.formats[i];if(!o)return void function(){if(!1!==l.strictSchema)throw new Error(e());function e(){return`unknown format \"${i}\" ignored in schema at path \"${u}\"`}f.logger.warn(e())}();if(!0===o)return;const[a,c,h]=function(e){const t=e instanceof RegExp?(0,n.regexpCode)(e):l.code.formats?n._`${l.code.formats}${(0,n.getProperty)(i)}`:void 0,s=r.scopeValue(\"formats\",{key:i,ref:e,code:t});return\"object\"!=typeof e||e instanceof RegExp?[\"string\",e,s]:[e.type||\"string\",e.validate,n._`${s}.validate`]}(o);a===t&&e.pass(function(){if(\"object\"==typeof o&&!(o instanceof RegExp)&&o.async){if(!d.$async)throw new Error(\"async format in sync schema\");return n._`await ${h}(${s})`}return\"function\"==typeof c?n._`${h}(${s})`:n._`${h}.test(${s})`}())}())}};t.default=s},4805:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=[r(2970).default];t.default=n},4110:(e,t)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.contentVocabulary=t.metadataVocabulary=void 0,t.metadataVocabulary=[\"title\",\"description\",\"default\",\"deprecated\",\"readOnly\",\"writeOnly\",\"examples\"],t.contentVocabulary=[\"contentMediaType\",\"contentEncoding\",\"contentSchema\"]},1475:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(320),o=r(283),i={keyword:\"const\",$data:!0,error:{message:\"must be equal to constant\",params:({schemaCode:e})=>n._`{allowedValue: ${e}}`},code(e){const{gen:t,data:r,$data:i,schemaCode:a,schema:c}=e;i||c&&\"object\"==typeof c?e.fail$data(n._`!${(0,s.useFunc)(t,o.default)}(${r}, ${a})`):e.fail(n._`${c} !== ${r}`)}};t.default=i},975:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(320),o=r(283),i={keyword:\"enum\",schemaType:\"array\",$data:!0,error:{message:\"must be equal to one of the allowed values\",params:({schemaCode:e})=>n._`{allowedValues: ${e}}`},code(e){const{gen:t,data:r,$data:i,schema:a,schemaCode:c,it:l}=e;if(!i&&0===a.length)throw new Error(\"enum must have non-empty array\");const u=a.length>=l.opts.loopEnum;let d;const f=()=>null!=d?d:d=(0,s.useFunc)(t,o.default);let h;if(u||i)h=t.let(\"valid\"),e.block$data(h,(function(){t.assign(h,!1),t.forOf(\"v\",c,(e=>t.if(n._`${f()}(${r}, ${e})`,(()=>t.assign(h,!0).break()))))}));else{if(!Array.isArray(a))throw new Error(\"ajv implementation error\");const e=t.const(\"vSchema\",c);h=(0,n.or)(...a.map(((t,s)=>function(e,t){const s=a[t];return\"object\"==typeof s&&null!==s?n._`${f()}(${r}, ${e}[${t}])`:n._`${r} === ${s}`}(e,s))))}e.pass(h)}};t.default=i},6823:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(8276),s=r(7597),o=r(7398),i=r(3486),a=r(1448),c=r(8813),l=r(7511),u=r(9075),d=r(1475),f=r(975),h=[n.default,s.default,o.default,i.default,a.default,c.default,l.default,u.default,{keyword:\"type\",schemaType:[\"string\",\"array\"]},{keyword:\"nullable\",schemaType:\"boolean\"},d.default,f.default];t.default=h},7511:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s={keyword:[\"maxItems\",\"minItems\"],type:\"array\",schemaType:\"number\",$data:!0,error:{message({keyword:e,schemaCode:t}){const r=\"maxItems\"===e?\"more\":\"fewer\";return n.str`must NOT have ${r} than ${t} items`},params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){const{keyword:t,data:r,schemaCode:s}=e,o=\"maxItems\"===t?n.operators.GT:n.operators.LT;e.fail$data(n._`${r}.length ${o} ${s}`)}};t.default=s},7398:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=r(320),o=r(6913),i={keyword:[\"maxLength\",\"minLength\"],type:\"string\",schemaType:\"number\",$data:!0,error:{message({keyword:e,schemaCode:t}){const r=\"maxLength\"===e?\"more\":\"fewer\";return n.str`must NOT have ${r} than ${t} characters`},params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){const{keyword:t,data:r,schemaCode:i,it:a}=e,c=\"maxLength\"===t?n.operators.GT:n.operators.LT,l=!1===a.opts.unicode?n._`${r}.length`:n._`${(0,s.useFunc)(e.gen,o.default)}(${r})`;e.fail$data(n._`${l} ${c} ${i}`)}};t.default=i},8276:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s=n.operators,o={maximum:{okStr:\"<=\",ok:s.LTE,fail:s.GT},minimum:{okStr:\">=\",ok:s.GTE,fail:s.LT},exclusiveMaximum:{okStr:\"<\",ok:s.LT,fail:s.GTE},exclusiveMinimum:{okStr:\">\",ok:s.GT,fail:s.LTE}},i={message:({keyword:e,schemaCode:t})=>n.str`must be ${o[e].okStr} ${t}`,params:({keyword:e,schemaCode:t})=>n._`{comparison: ${o[e].okStr}, limit: ${t}}`},a={keyword:Object.keys(o),type:\"number\",schemaType:\"number\",$data:!0,error:i,code(e){const{keyword:t,data:r,schemaCode:s}=e;e.fail$data(n._`${r} ${o[t].fail} ${s} || isNaN(${r})`)}};t.default=a},1448:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s={keyword:[\"maxProperties\",\"minProperties\"],type:\"object\",schemaType:\"number\",$data:!0,error:{message({keyword:e,schemaCode:t}){const r=\"maxProperties\"===e?\"more\":\"fewer\";return n.str`must NOT have ${r} than ${t} properties`},params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){const{keyword:t,data:r,schemaCode:s}=e,o=\"maxProperties\"===t?n.operators.GT:n.operators.LT;e.fail$data(n._`Object.keys(${r}).length ${o} ${s}`)}};t.default=s},7597:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(5899),s={keyword:\"multipleOf\",type:\"number\",schemaType:\"number\",$data:!0,error:{message:({schemaCode:e})=>n.str`must be multiple of ${e}`,params:({schemaCode:e})=>n._`{multipleOf: ${e}}`},code(e){const{gen:t,data:r,schemaCode:s,it:o}=e,i=o.opts.multipleOfPrecision,a=t.let(\"res\"),c=i?n._`Math.abs(Math.round(${a}) - ${a}) > 1e-${i}`:n._`${a} !== parseInt(${a})`;e.fail$data(n._`(${s} === 0 || (${a} = ${r}/${s}, ${c}))`)}};t.default=s},3486:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(7470),s=r(5899),o={keyword:\"pattern\",type:\"string\",schemaType:\"string\",$data:!0,error:{message:({schemaCode:e})=>s.str`must match pattern \"${e}\"`,params:({schemaCode:e})=>s._`{pattern: ${e}}`},code(e){const{data:t,$data:r,schema:o,schemaCode:i,it:a}=e,c=a.opts.unicodeRegExp?\"u\":\"\",l=r?s._`(new RegExp(${i}, ${c}))`:(0,n.usePattern)(e,o);e.fail$data(s._`!${l}.test(${t})`)}};t.default=o},8813:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(7470),s=r(5899),o=r(320),i={keyword:\"required\",type:\"object\",schemaType:\"array\",$data:!0,error:{message:({params:{missingProperty:e}})=>s.str`must have required property '${e}'`,params:({params:{missingProperty:e}})=>s._`{missingProperty: ${e}}`},code(e){const{gen:t,schema:r,schemaCode:i,data:a,$data:c,it:l}=e,{opts:u}=l;if(!c&&0===r.length)return;const d=r.length>=u.loopRequired;if(l.allErrors?function(){if(d||c)e.block$data(s.nil,f);else for(const t of r)(0,n.checkReportMissingProp)(e,t)}():function(){const o=t.let(\"missing\");if(d||c){const r=t.let(\"valid\",!0);e.block$data(r,(()=>function(r,o){e.setParams({missingProperty:r}),t.forOf(r,i,(()=>{t.assign(o,(0,n.propertyInData)(t,a,r,u.ownProperties)),t.if((0,s.not)(o),(()=>{e.error(),t.break()}))}),s.nil)}(o,r))),e.ok(r)}else t.if((0,n.checkMissingProp)(e,r,o)),(0,n.reportMissingProp)(e,o),t.else()}(),u.strictRequired){const t=e.parentSchema.properties,{definedProperties:n}=e.it;for(const e of r)if(void 0===(null==t?void 0:t[e])&&!n.has(e)){const t=`required property \"${e}\" is not defined at \"${l.schemaEnv.baseId+l.errSchemaPath}\" (strictRequired)`;(0,o.checkStrictMode)(l,t,l.opts.strictRequired)}}function f(){t.forOf(\"prop\",i,(r=>{e.setParams({missingProperty:r}),t.if((0,n.noPropertyInData)(t,a,r,u.ownProperties),(()=>e.error()))}))}}};t.default=i},9075:(e,t,r)=>{\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0});const n=r(2292),s=r(5899),o=r(320),i=r(283),a={keyword:\"uniqueItems\",type:\"array\",schemaType:\"boolean\",$data:!0,error:{message:({params:{i:e,j:t}})=>s.str`must NOT have duplicate items (items ## ${t} and ${e} are identical)`,params:({params:{i:e,j:t}})=>s._`{i: ${e}, j: ${t}}`},code(e){const{gen:t,data:r,$data:a,schema:c,parentSchema:l,schemaCode:u,it:d}=e;if(!a&&!c)return;const f=t.let(\"valid\"),h=l.items?(0,n.getSchemaTypes)(l.items):[];function p(o,i){const a=t.name(\"item\"),c=(0,n.checkDataTypes)(h,a,d.opts.strictNumbers,n.DataType.Wrong),l=t.const(\"indices\",s._`{}`);t.for(s._`;${o}--;`,(()=>{t.let(a,s._`${r}[${o}]`),t.if(c,s._`continue`),h.length>1&&t.if(s._`typeof ${a} == \"string\"`,s._`${a} += \"_\"`),t.if(s._`typeof ${l}[${a}] == \"number\"`,(()=>{t.assign(i,s._`${l}[${a}]`),e.error(),t.assign(f,!1).break()})).code(s._`${l}[${a}] = ${o}`)}))}function m(n,a){const c=(0,o.useFunc)(t,i.default),l=t.name(\"outer\");t.label(l).for(s._`;${n}--;`,(()=>t.for(s._`${a} = ${n}; ${a}--;`,(()=>t.if(s._`${c}(${r}[${n}], ${r}[${a}])`,(()=>{e.error(),t.assign(f,!1).break(l)}))))))}e.block$data(f,(function(){const n=t.let(\"i\",s._`${r}.length`),o=t.let(\"j\");e.setParams({i:n,j:o}),t.assign(f,!0),t.if(s._`${n} > 1`,(()=>(h.length>0&&!h.some((e=>\"object\"===e||\"array\"===e))?p:m)(n,o)))}),s._`${u} === false`),e.ok(f)}};t.default=a},5528:e=>{\"use strict\";var t=e.exports=function(e,t,n){\"function\"==typeof t&&(n=t,t={}),r(t,\"function\"==typeof(n=t.cb||n)?n:n.pre||function(){},n.post||function(){},e,\"\",e)};function r(e,n,s,o,i,a,c,l,u,d){if(o&&\"object\"==typeof o&&!Array.isArray(o)){for(var f in n(o,i,a,c,l,u,d),o){var h=o[f];if(Array.isArray(h)){if(f in t.arrayKeywords)for(var p=0;p<h.length;p++)r(e,n,s,h[p],i+\"/\"+f+\"/\"+p,a,i,f,o,p)}else if(f in t.propsKeywords){if(h&&\"object\"==typeof h)for(var m in h)r(e,n,s,h[m],i+\"/\"+f+\"/\"+m.replace(/~/g,\"~0\").replace(/\\//g,\"~1\"),a,i,f,o,m)}else(f in t.keywords||e.allKeys&&!(f in t.skipKeywords))&&r(e,n,s,h,i+\"/\"+f,a,i,f,o)}s(o,i,a,c,l,u,d)}}t.keywords={additionalItems:!0,items:!0,contains:!0,additionalProperties:!0,propertyNames:!0,not:!0,if:!0,then:!0,else:!0},t.arrayKeywords={items:!0,allOf:!0,anyOf:!0,oneOf:!0},t.propsKeywords={$defs:!0,definitions:!0,properties:!0,patternProperties:!0,dependencies:!0},t.skipKeywords={default:!0,enum:!0,const:!0,required:!0,maximum:!0,minimum:!0,exclusiveMaximum:!0,exclusiveMinimum:!0,multipleOf:!0,maxLength:!0,minLength:!0,pattern:!0,format:!0,maxItems:!0,minItems:!0,uniqueItems:!0,maxProperties:!0,minProperties:!0}},7319:(e,t,r)=>{\"use strict\";const n=r(6022);e.exports=(e,t={})=>{if(\"function\"!=typeof e)throw new TypeError(`Expected the first argument to be a function, got \\`${typeof e}\\``);const{wait:r=0,before:s=!1,after:o=!0}=t;if(!s&&!o)throw new Error(\"Both `before` and `after` are false, function wouldn't be called.\");let i,a;const c=function(...t){const n=this,c=s&&!i;return clearTimeout(i),i=setTimeout((()=>{i=void 0,o&&(a=e.apply(n,t))}),r),c&&(a=e.apply(n,t)),a};return n(c,e),c.cancel=()=>{i&&(clearTimeout(i),i=void 0)},c}},6022:e=>{\"use strict\";const t=(e,t,n,s)=>{if(\"length\"===n||\"prototype\"===n)return;if(\"arguments\"===n||\"caller\"===n)return;const o=Object.getOwnPropertyDescriptor(e,n),i=Object.getOwnPropertyDescriptor(t,n);!r(o,i)&&s||Object.defineProperty(e,n,i)},r=function(e,t){return void 0===e||e.configurable||e.writable===t.writable&&e.enumerable===t.enumerable&&e.configurable===t.configurable&&(e.writable||e.value===t.value)},n=(e,t)=>`/* Wrapped ${e}*/\\n${t}`,s=Object.getOwnPropertyDescriptor(Function.prototype,\"toString\"),o=Object.getOwnPropertyDescriptor(Function.prototype.toString,\"name\");e.exports=(e,r,{ignoreNonConfigurable:i=!1}={})=>{const{name:a}=e;for(const n of Reflect.ownKeys(r))t(e,r,n,i);return((e,t)=>{const r=Object.getPrototypeOf(t);r!==Object.getPrototypeOf(e)&&Object.setPrototypeOf(e,r)})(e,r),((e,t,r)=>{const i=\"\"===r?\"\":`with ${r.trim()}() `,a=n.bind(null,i,t.toString());Object.defineProperty(a,\"name\",o),Object.defineProperty(e,\"toString\",{...s,value:a})})(e,r,a),e}},3517:(e,t,r)=>{\"use strict\";const n=r(4290),s=new Set([\"__proto__\",\"prototype\",\"constructor\"]);function o(e){const t=e.split(\".\"),r=[];for(let e=0;e<t.length;e++){let n=t[e];for(;\"\\\\\"===n[n.length-1]&&void 0!==t[e+1];)n=n.slice(0,-1)+\".\",n+=t[++e];r.push(n)}return r.some((e=>s.has(e)))?[]:r}e.exports={get(e,t,r){if(!n(e)||\"string\"!=typeof t)return void 0===r?e:r;const s=o(t);if(0!==s.length){for(let t=0;t<s.length;t++)if(null==(e=e[s[t]])){if(t!==s.length-1)return r;break}return void 0===e?r:e}},set(e,t,r){if(!n(e)||\"string\"!=typeof t)return e;const s=e,i=o(t);for(let t=0;t<i.length;t++){const s=i[t];n(e[s])||(e[s]={}),t===i.length-1&&(e[s]=r),e=e[s]}return s},delete(e,t){if(!n(e)||\"string\"!=typeof t)return!1;const r=o(t);for(let t=0;t<r.length;t++){const s=r[t];if(t===r.length-1)return delete e[s],!0;if(e=e[s],!n(e))return!1}},has(e,t){if(!n(e)||\"string\"!=typeof t)return!1;const r=o(t);if(0===r.length)return!1;for(let t=0;t<r.length;t++){if(!n(e))return!1;if(!(r[t]in e))return!1;e=e[r[t]]}return!0}}},2666:(e,t,r)=>{\"use strict\";const n=r(1787);class s{static instances={};errorHandler=null;eventLogger=null;functions={};hooks=[];isDev=!1;levels=null;logId=null;scope=null;transports={};variables={};constructor({allowUnknownLevel:e=!1,errorHandler:t,eventLogger:r,initializeFn:o,isDev:i=!1,levels:a=[\"error\",\"warn\",\"info\",\"verbose\",\"debug\",\"silly\"],logId:c,transportFactories:l={},variables:u}={}){this.addLevel=this.addLevel.bind(this),this.create=this.create.bind(this),this.logData=this.logData.bind(this),this.processMessage=this.processMessage.bind(this),this.allowUnknownLevel=e,this.initializeFn=o,this.isDev=i,this.levels=a,this.logId=c,this.transportFactories=l,this.variables=u||{},this.scope=n(this),this.addLevel(\"log\",!1);for(const e of this.levels)this.addLevel(e,!1);this.errorHandler=t,t?.setOptions({logFn:this.error}),this.eventLogger=r,r?.setOptions({logger:this});for(const[e,t]of Object.entries(l))this.transports[e]=t(this);s.instances[c]=this}static getInstance({logId:e}){return this.instances[e]||this.instances.default}addLevel(e,t=this.levels.length){!1!==t&&this.levels.splice(t,0,e),this[e]=(...t)=>this.logData(t,{level:e}),this.functions[e]=this[e]}catchErrors(e){return this.processMessage({data:[\"log.catchErrors is deprecated. Use log.errorHandler instead\"],level:\"warn\"},{transports:[\"console\"]}),this.errorHandler.startCatching(e)}create(e){return\"string\"==typeof e&&(e={logId:e}),new s({...e,errorHandler:this.errorHandler,initializeFn:this.initializeFn,isDev:this.isDev,transportFactories:this.transportFactories,variables:{...this.variables}})}compareLevels(e,t,r=this.levels){const n=r.indexOf(e),s=r.indexOf(t);return-1===s||-1===n||s<=n}initialize(e={}){this.initializeFn({logger:this,...e})}logData(e,t={}){this.processMessage({data:e,...t})}processMessage(e,{transports:t=this.transports}={}){if(\"errorHandler\"===e.cmd)return void this.errorHandler.handle(e.error,{errorName:e.errorName,processType:\"renderer\",showDialog:Boolean(e.showDialog)});let r=e.level;this.allowUnknownLevel||(r=this.levels.includes(e.level)?e.level:\"info\");const n={date:new Date,...e,level:r,variables:{...this.variables,...e.variables}};for(const[r,s]of this.transportEntries(t))if(\"function\"==typeof s&&!1!==s.level&&this.compareLevels(s.level,e.level))try{const e=this.hooks.reduce(((e,t)=>e?t(e,s,r):e),n);e&&s({...e,data:[...e.data]})}catch(e){this.processInternalErrorFn(e)}}processInternalErrorFn(e){}transportEntries(e=this.transports){return(Array.isArray(e)?e:Object.entries(e)).map((e=>{switch(typeof e){case\"string\":return this.transports[e]?[e,this.transports[e]]:null;case\"function\":return[e.name,e];default:return Array.isArray(e)?e:null}})).filter(Boolean)}}e.exports=s},1787:e=>{\"use strict\";e.exports=function(e){return Object.defineProperties(t,{defaultLabel:{value:\"\",writable:!0},labelPadding:{value:!0,writable:!0},maxLabelLength:{value:0,writable:!0},labelLength:{get(){switch(typeof t.labelPadding){case\"boolean\":return t.labelPadding?t.maxLabelLength:0;case\"number\":return t.labelPadding;default:return 0}}}});function t(r){t.maxLabelLength=Math.max(t.maxLabelLength,r.length);const n={};for(const t of[...e.levels,\"log\"])n[t]=(...n)=>e.logData(n,{level:t,scope:r});return n}}},7377:(e,t,r)=>{\"use strict\";\"undefined\"==typeof process||\"renderer\"===process.type||\"worker\"===process.type?(r(9654),e.exports=r(9618)):e.exports=r(7631)},2641:(e,t,r)=>{\"use strict\";const n=r(2674);e.exports=class{isActive=!1;logFn=null;onError=null;showDialog=!0;constructor({logFn:e=null,onError:t=null,showDialog:r=!0}={}){this.createIssue=this.createIssue.bind(this),this.handleError=this.handleError.bind(this),this.handleRejection=this.handleRejection.bind(this),this.setOptions({logFn:e,onError:t,showDialog:r}),this.startCatching=this.startCatching.bind(this),this.stopCatching=this.stopCatching.bind(this)}handle(e,{logFn:t=this.logFn,onError:r=this.onError,processType:s=\"browser\",showDialog:o=this.showDialog,errorName:i=\"\"}={}){e=function(e){if(e instanceof Error)return e;if(e&&\"object\"==typeof e){if(e.message)return Object.assign(new Error(e.message),e);try{return new Error(JSON.stringify(e))}catch(t){return new Error(`Couldn't normalize error ${String(e)}: ${t}`)}}return new Error(`Can't normalize error ${String(e)}`)}(e);try{if(\"function\"==typeof r){const t=n.getVersions();if(!1===r({createIssue:this.createIssue,error:e,errorName:i,processType:s,versions:t}))return}i?t(i,e):t(e),o&&!i.includes(\"rejection\")&&n.showErrorBox(`A JavaScript error occurred in the ${s} process`,e.stack)}catch{console.error(e)}}setOptions({logFn:e,onError:t,showDialog:r}){\"function\"==typeof e&&(this.logFn=e),\"function\"==typeof t&&(this.onError=t),\"boolean\"==typeof r&&(this.showDialog=r)}startCatching({onError:e,showDialog:t}={}){this.isActive||(this.isActive=!0,this.setOptions({onError:e,showDialog:t}),process.on(\"uncaughtException\",this.handleError),process.on(\"unhandledRejection\",this.handleRejection))}stopCatching(){this.isActive=!1,process.removeListener(\"uncaughtException\",this.handleError),process.removeListener(\"unhandledRejection\",this.handleRejection)}createIssue(e,t){n.openUrl(`${e}?${new URLSearchParams(t).toString()}`)}handleError(e){this.handle(e,{errorName:\"Unhandled\"})}handleRejection(e){const t=e instanceof Error?e:new Error(JSON.stringify(e));this.handle(t,{errorName:\"Unhandled rejection\"})}}},6900:(e,t,r)=>{\"use strict\";const n=r(2674);e.exports=class{disposers=[];format=\"{eventSource}#{eventName}:\";formatters={app:{\"certificate-error\":({args:e})=>this.arrayToObject(e.slice(1,4),[\"url\",\"error\",\"certificate\"]),\"child-process-gone\":({args:e})=>1===e.length?e[0]:e,\"render-process-gone\":({args:[e,t]})=>t&&\"object\"==typeof t?{...t,...this.getWebContentsDetails(e)}:[]},webContents:{\"console-message\":({args:[e,t,r,n]})=>{if(!(e<3))return{message:t,source:`${n}:${r}`}},\"did-fail-load\":({args:e})=>this.arrayToObject(e,[\"errorCode\",\"errorDescription\",\"validatedURL\",\"isMainFrame\",\"frameProcessId\",\"frameRoutingId\"]),\"did-fail-provisional-load\":({args:e})=>this.arrayToObject(e,[\"errorCode\",\"errorDescription\",\"validatedURL\",\"isMainFrame\",\"frameProcessId\",\"frameRoutingId\"]),\"plugin-crashed\":({args:e})=>this.arrayToObject(e,[\"name\",\"version\"]),\"preload-error\":({args:e})=>this.arrayToObject(e,[\"preloadPath\",\"error\"])}};events={app:{\"certificate-error\":!0,\"child-process-gone\":!0,\"render-process-gone\":!0},webContents:{\"did-fail-load\":!0,\"did-fail-provisional-load\":!0,\"plugin-crashed\":!0,\"preload-error\":!0,unresponsive:!0}};level=\"error\";scope=\"\";constructor(e={}){this.setOptions(e)}setOptions({events:e,level:t,logger:r,format:n,formatters:s,scope:o}){\"object\"==typeof e&&(this.events=e),\"string\"==typeof t&&(this.level=t),\"object\"==typeof r&&(this.logger=r),\"string\"!=typeof n&&\"function\"!=typeof n||(this.format=n),\"object\"==typeof s&&(this.formatters=s),\"string\"==typeof o&&(this.scope=o)}startLogging(e={}){this.setOptions(e),this.disposeListeners();for(const e of this.getEventNames(this.events.app))this.disposers.push(n.onAppEvent(e,((...t)=>{this.handleEvent({eventSource:\"app\",eventName:e,handlerArgs:t})})));for(const e of this.getEventNames(this.events.webContents))this.disposers.push(n.onEveryWebContentsEvent(e,((...t)=>{this.handleEvent({eventSource:\"webContents\",eventName:e,handlerArgs:t})})))}stopLogging(){this.disposeListeners()}arrayToObject(e,t){const r={};return t.forEach(((t,n)=>{r[t]=e[n]})),e.length>t.length&&(r.unknownArgs=e.slice(t.length)),r}disposeListeners(){this.disposers.forEach((e=>e())),this.disposers=[]}formatEventLog({eventName:e,eventSource:t,handlerArgs:r}){const[n,...s]=r;if(\"function\"==typeof this.format)return this.format({args:s,event:n,eventName:e,eventSource:t});const o=this.formatters[t]?.[e];let i=s;if(\"function\"==typeof o&&(i=o({args:s,event:n,eventName:e,eventSource:t})),!i)return;const a={};return Array.isArray(i)?a.args=i:\"object\"==typeof i&&Object.assign(a,i),\"webContents\"===t&&Object.assign(a,this.getWebContentsDetails(n?.sender)),[this.format.replace(\"{eventSource}\",\"app\"===t?\"App\":\"WebContents\").replace(\"{eventName}\",e),a]}getEventNames(e){return e&&\"object\"==typeof e?Object.entries(e).filter((([e,t])=>t)).map((([e])=>e)):[]}getWebContentsDetails(e){if(!e?.loadURL)return{};try{return{webContents:{id:e.id,url:e.getURL()}}}catch{return{}}}handleEvent({eventName:e,eventSource:t,handlerArgs:r}){const n=this.formatEventLog({eventName:e,eventSource:t,handlerArgs:r});if(n){const e=this.scope?this.logger.scope(this.scope):this.logger;e?.[this.level]?.(...n)}}}},2674:(e,t,r)=>{\"use strict\";const n=r(2037),s=r(1017);let o;try{o=r(2298)}catch{o=null}function i(){return c(\"app\")}function a(){const e=i();return e?\"name\"in e?e.name:e.getName():null}function c(e){return o?.[e]||null}function l(){return\"browser\"===process.type&&o?.ipcMain?o.ipcMain:\"renderer\"===process.type&&o?.ipcRenderer?o.ipcRenderer:null}function u(){const e=i();return e?\"version\"in e?e.version:e.getVersion():null}function d(){let e=n.type().replace(\"_\",\" \"),t=n.release();return\"Darwin\"===e&&(e=\"macOS\",t=function(){const e=Number(n.release().split(\".\")[0]);return e<=19?\"10.\"+(e-4):e-9}()),`${e} ${t}`}function f(e){const t=i();if(!t)return null;try{return t.getPath(e)}catch(e){return null}}e.exports={getAppUserDataPath:()=>f(\"userData\"),getName:a,getPath:f,getVersion:u,getVersions:()=>({app:`${a()} ${u()}`,electron:`Electron ${process.versions.electron}`,os:d()}),isDev(){const e=i();return void 0!==e?.isPackaged?!e.isPackaged:\"string\"==typeof process.execPath?s.basename(process.execPath).toLowerCase().startsWith(\"electron\"):\"1\"===process.env.ELECTRON_IS_DEV},isElectron:()=>Boolean(process.versions.electron),onAppEvent:(e,t)=>(o?.app?.on(e,t),()=>{o?.app?.off(e,t)}),onAppReady(e){o?.app?.isReady()?e():o?.app?.once?o?.app?.once(\"ready\",e):e()},onEveryWebContentsEvent(e,t){return o?.webContents?.getAllWebContents().forEach((r=>{r.on(e,t)})),o?.app?.on(\"web-contents-created\",r),()=>{o?.webContents?.getAllWebContents().forEach((r=>{r.off(e,t)})),o?.app?.off(\"web-contents-created\",r)};function r(r,n){n.on(e,t)}},onIpc(e,t){l()?.on(e,t)},onIpcInvoke(e,t){l()?.handle?.(e,t)},openUrl(e,t=console.error){c(\"shell\")?.openExternal(e).catch(t)},setPreloadFileForSessions({filePath:e,includeFutureSession:t=!0,getSessions:r=(()=>[o?.session?.defaultSession])}){for(const e of r().filter(Boolean))n(e);function n(t){t.setPreloads([...t.getPreloads(),e])}t&&o?.app?.on(\"session-created\",(e=>{n(e)}))},sendIpc(e,t){\"browser\"===process.type?function(e,t){o?.BrowserWindow?.getAllWindows().forEach((r=>{!1===r.webContents?.isDestroyed()&&r.webContents.send(e,t)}))}(e,t):\"renderer\"===process.type&&function(e,t){l()?.send(e,t)}(e,t)},showErrorBox(e,t){const r=c(\"dialog\");r&&r.showErrorBox(e,t)}}},7631:(e,t,r)=>{\"use strict\";const n=r(2674),{initialize:s}=r(5471),o=r(429),i=r(1688),a=r(7988),c=r(2666),l=r(2641),u=r(6900),d=new c({errorHandler:new l,eventLogger:new u,initializeFn:s,isDev:n.isDev(),logId:\"default\",transportFactories:{console:o,file:i,remote:a},variables:{processType:\"main\"}});function f(e){c.getInstance(e)?.processMessage(e)}d.processInternalErrorFn=e=>{d.transports.console.writeFn({message:{data:[\"Unhandled electron-log error\",e],level:\"error\"}})},e.exports=d,e.exports.Logger=c,e.exports.default=e.exports,n.onIpc(\"__ELECTRON_LOG__\",((e,t)=>{t.scope&&c.getInstance(t).scope(t.scope);const r=new Date(t.date);f({...t,date:r.getTime()?r:new Date})})),n.onIpcInvoke(\"__ELECTRON_LOG__\",((e,{cmd:t=\"\",logId:r})=>\"getOptions\"===t?{levels:c.getInstance({logId:r}).levels,logId:r}:(f({data:[`Unknown cmd '${t}'`],level:\"error\"}),{})))},5471:(e,t,r)=>{\"use strict\";const n=r(7147),s=r(2037),o=r(1017),i=r(2674),a=r(9654);e.exports={initialize({getSessions:e,includeFutureSession:t,logger:r,preload:c=!0,spyRendererConsole:l=!1}){i.onAppReady((()=>{try{c&&function({getSessions:e,includeFutureSession:t,preloadOption:r}){let c=\"string\"==typeof r?r:o.resolve(__dirname,\"../renderer/electron-log-preload.js\");if(!n.existsSync(c)){c=o.join(i.getAppUserDataPath()||s.tmpdir(),\"electron-log-preload.js\");const e=`\\n      try {\\n        (${a.toString()})(require('electron'));\\n      } catch(e) {\\n        console.error(e);\\n      }\\n    `;n.writeFileSync(c,e,\"utf8\")}i.setPreloadFileForSessions({filePath:c,includeFutureSession:t,getSessions:e})}({getSessions:e,includeFutureSession:t,preloadOption:c}),l&&function({logger:e}){const t=[\"verbose\",\"info\",\"warning\",\"error\"];i.onEveryWebContentsEvent(\"console-message\",((r,n,s)=>{e.processMessage({data:[s],level:t[n],variables:{processType:\"renderer\"}})}))}({logger:r})}catch(e){r.warn(e)}}))}}},1888:(e,t,r)=>{\"use strict\";const{transform:n}=r(6829);function s(e){const t=Math.abs(e);return`${e>=0?\"-\":\"+\"}${Math.floor(t/60).toString().padStart(2,\"0\")}:${(t%60).toString().padStart(2,\"0\")}`}function o({data:e,logger:t,message:r}){const{defaultLabel:n,labelLength:s}=t?.scope||{},o=e[0];let i,a=r.scope;return a||(a=n),i=\"\"===a?s>0?\"\".padEnd(s+3):\"\":\"string\"==typeof a?` (${a})`.padEnd(s+3):\"\",e[0]=o.replace(\"{scope}\",i),e}function i({data:e,message:t}){let r=e[0];if(\"string\"!=typeof r)return e;r=r.replace(\"{level}]\",`${t.level}]`.padEnd(6,\" \"));const n=t.date||new Date;return e[0]=r.replace(/\\{(\\w+)}/g,((e,r)=>{switch(r){case\"level\":return t.level||\"info\";case\"logId\":return t.logId;case\"y\":return n.getFullYear().toString(10);case\"m\":return(n.getMonth()+1).toString(10).padStart(2,\"0\");case\"d\":return n.getDate().toString(10).padStart(2,\"0\");case\"h\":return n.getHours().toString(10).padStart(2,\"0\");case\"i\":return n.getMinutes().toString(10).padStart(2,\"0\");case\"s\":return n.getSeconds().toString(10).padStart(2,\"0\");case\"ms\":return n.getMilliseconds().toString(10).padStart(3,\"0\");case\"z\":return s(n.getTimezoneOffset());case\"iso\":return n.toISOString();default:return t.variables?.[r]||e}})).trim(),e}function a({data:e}){const t=e[0];if(\"string\"!=typeof t)return e;if(t.lastIndexOf(\"{text}\")===t.length-6)return e[0]=t.replace(/\\s?{text}/,\"\"),\"\"===e[0]&&e.shift(),e;const r=t.split(\"{text}\");let n=[];return\"\"!==r[0]&&n.push(r[0]),n=n.concat(e.slice(1)),\"\"!==r[1]&&n.push(r[1]),n}e.exports={concatFirstStringElements:function({data:e}){return\"string\"!=typeof e[0]||\"string\"!=typeof e[1]||e[0].match(/%[1cdfiOos]/)?e:[`${e[0]} ${e[1]}`,...e.slice(2)]},formatScope:o,formatText:a,formatVariables:i,timeZoneFromOffset:s,format({message:e,logger:t,transport:r,data:s=e?.data}){switch(typeof r.format){case\"string\":return n({message:e,logger:t,transforms:[i,o,a],transport:r,initialData:[r.format,...s]});case\"function\":return r.format({data:s,level:e?.level||\"info\",logger:t,message:e,transport:r});default:return s}}}},2189:(e,t,r)=>{\"use strict\";const n=r(3837);function s(e={}){const t=new WeakSet;return function(r,n){if(\"object\"==typeof n&&null!==n){if(t.has(n))return;t.add(n)}return o(0,n,e)}}function o(e,t,r={}){const n=!1!==r?.serializeMapAndSet;return t instanceof Error?t.stack:t?\"function\"==typeof t?`[function] ${t.toString()}`:n&&t instanceof Map&&Object.fromEntries?Object.fromEntries(t):n&&t instanceof Set&&Array.from?Array.from(t):t:t}e.exports={serialize:o,maxDepth({data:t,transport:r,depth:n=r?.depth??6}){if(!t)return t;if(n<1)return Array.isArray(t)?\"[array]\":\"object\"==typeof t&&t?\"[object]\":t;if(Array.isArray(t))return t.map((t=>e.exports.maxDepth({data:t,depth:n-1})));if(\"object\"!=typeof t)return t;if(t&&\"function\"==typeof t.toISOString)return t;if(null===t)return null;if(t instanceof Error)return t;const s={};for(const r in t)Object.prototype.hasOwnProperty.call(t,r)&&(s[r]=e.exports.maxDepth({data:t[r],depth:n-1}));return s},toJSON:({data:e})=>JSON.parse(JSON.stringify(e,s())),toString({data:e,transport:t}){const r=t?.inspectOptions||{},o=e.map((e=>{if(void 0!==e)try{const t=JSON.stringify(e,s(),\"  \");return void 0===t?void 0:JSON.parse(t)}catch(t){return e}}));return n.formatWithOptions(r,...o)}}},2163:e=>{\"use strict\";e.exports={transformStyles:s,applyAnsiStyles:({data:e})=>s(e,r,n),removeStyles:({data:e})=>s(e,(()=>\"\"))};const t={unset:\"\u001b[0m\",black:\"\u001b[30m\",red:\"\u001b[31m\",green:\"\u001b[32m\",yellow:\"\u001b[33m\",blue:\"\u001b[34m\",magenta:\"\u001b[35m\",cyan:\"\u001b[36m\",white:\"\u001b[37m\"};function r(e){const r=e.replace(/color:\\s*(\\w+).*/,\"$1\").toLowerCase();return t[r]||\"\"}function n(e){return e+t.unset}function s(e,t,r){const n={};return e.reduce(((e,s,o,i)=>{if(n[o])return e;if(\"string\"==typeof s){let e=o,a=!1;s=s.replace(/%[1cdfiOos]/g,(r=>{if(e+=1,\"%c\"!==r)return r;const o=i[e];return\"string\"==typeof o?(n[e]=!0,a=!0,t(o,s)):r})),a&&r&&(s=r(s))}return e.push(s),e}),[])}},6829:e=>{\"use strict\";e.exports={transform:function({logger:e,message:t,transport:r,initialData:n=t?.data||[],transforms:s=r?.transforms}){return s.reduce(((n,s)=>\"function\"==typeof s?s({data:n,logger:e,message:t,transport:r}):n),n)}}},429:(e,t,r)=>{\"use strict\";const{concatFirstStringElements:n,format:s}=r(1888),{maxDepth:o,toJSON:i}=r(2189),{applyAnsiStyles:a,removeStyles:c}=r(2163),{transform:l}=r(6829),u={error:console.error,warn:console.warn,info:console.info,verbose:console.info,debug:console.debug,silly:console.debug,log:console.log};e.exports=f;const d=`%c{h}:{i}:{s}.{ms}{scope}%c ${\"win32\"===process.platform?\">\":\"›\"} {text}`;function f(e){return Object.assign((function t(r){const n=l({logger:e,message:r,transport:t});t.writeFn({message:{...r,data:n}})}),{format:d,level:\"silly\",transforms:[h,s,p,n,o,i],useStyles:process.env.FORCE_STYLES,writeFn({message:e}){(u[e.level]||u.info)(...e.data)}})}function h({data:e,message:t,transport:r}){return r.format!==d?e:[`color:${m(t.level)}`,\"color:unset\",...e]}function p(e){const{message:t,transport:r}=e;return(function(e,t){if(\"boolean\"==typeof e)return e;const r=\"error\"===t||\"warn\"===t?process.stderr:process.stdout;return r&&r.isTTY}(r.useStyles,t.level)?a:c)(e)}function m(e){const t={error:\"red\",warn:\"yellow\",info:\"cyan\",default:\"unset\"};return t[e]||t.default}Object.assign(f,{DEFAULT_FORMAT:d})},9441:(e,t,r)=>{\"use strict\";const n=r(2361),s=r(7147),o=r(2037);e.exports=class extends n{asyncWriteQueue=[];bytesWritten=0;hasActiveAsyncWriting=!1;path=null;initialSize=void 0;writeOptions=null;writeAsync=!1;constructor({path:e,writeOptions:t={encoding:\"utf8\",flag:\"a\",mode:438},writeAsync:r=!1}){super(),this.path=e,this.writeOptions=t,this.writeAsync=r}get size(){return this.getSize()}clear(){try{return s.writeFileSync(this.path,\"\",{mode:this.writeOptions.mode,flag:\"w\"}),this.reset(),!0}catch(e){return\"ENOENT\"===e.code||(this.emit(\"error\",e,this),!1)}}crop(e){try{const t=function(e,t){const r=Buffer.alloc(t),n=s.statSync(e),o=Math.min(n.size,t),i=Math.max(0,n.size-t),a=s.openSync(e,\"r\"),c=s.readSync(a,r,0,o,i);return s.closeSync(a),r.toString(\"utf8\",0,c)}(this.path,e||4096);this.clear(),this.writeLine(`[log cropped]${o.EOL}${t}`)}catch(e){this.emit(\"error\",new Error(`Couldn't crop file ${this.path}. ${e.message}`),this)}}getSize(){if(void 0===this.initialSize)try{const e=s.statSync(this.path);this.initialSize=e.size}catch(e){this.initialSize=0}return this.initialSize+this.bytesWritten}increaseBytesWrittenCounter(e){this.bytesWritten+=Buffer.byteLength(e,this.writeOptions.encoding)}isNull(){return!1}nextAsyncWrite(){const e=this;if(this.hasActiveAsyncWriting||0===this.asyncWriteQueue.length)return;const t=this.asyncWriteQueue.join(\"\");this.asyncWriteQueue=[],this.hasActiveAsyncWriting=!0,s.writeFile(this.path,t,this.writeOptions,(r=>{e.hasActiveAsyncWriting=!1,r?e.emit(\"error\",new Error(`Couldn't write to ${e.path}. ${r.message}`),this):e.increaseBytesWrittenCounter(t),e.nextAsyncWrite()}))}reset(){this.initialSize=void 0,this.bytesWritten=0}toString(){return this.path}writeLine(e){if(e+=o.EOL,this.writeAsync)return this.asyncWriteQueue.push(e),void this.nextAsyncWrite();try{s.writeFileSync(this.path,e,this.writeOptions),this.increaseBytesWrittenCounter(e)}catch(e){this.emit(\"error\",new Error(`Couldn't write to ${this.path}. ${e.message}`),this)}}}},8126:(e,t,r)=>{\"use strict\";const n=r(2361),s=r(7147),o=r(1017),i=r(9441),a=r(8392);e.exports=class extends n{store={};constructor(){super(),this.emitError=this.emitError.bind(this)}provide({filePath:e,writeOptions:t,writeAsync:r=!1}){let n;try{if(e=o.resolve(e),this.store[e])return this.store[e];n=this.createFile({filePath:e,writeOptions:t,writeAsync:r})}catch(t){n=new a({path:e}),this.emitError(t,n)}return n.on(\"error\",this.emitError),this.store[e]=n,n}createFile({filePath:e,writeOptions:t,writeAsync:r}){return this.testFileWriting(e),new i({path:e,writeOptions:t,writeAsync:r})}emitError(e,t){this.emit(\"error\",e,t)}testFileWriting(e){s.mkdirSync(o.dirname(e),{recursive:!0}),s.writeFileSync(e,\"\",{flag:\"a\"})}}},8392:(e,t,r)=>{\"use strict\";const n=r(9441);e.exports=class extends n{clear(){}crop(){}getSize(){return 0}isNull(){return!0}writeLine(){}}},1688:(e,t,r)=>{\"use strict\";const n=r(7147),s=r(1017),o=r(2037),i=r(8126),a=r(1230),{transform:c}=r(6829),{removeStyles:l}=r(2163),{format:u}=r(1888),{toString:d}=r(2189);e.exports=function(e,t=f){let r;return t.listenerCount(\"error\")<1&&t.on(\"error\",((e,t)=>{p(`Can't write to ${t}`,e)})),Object.assign(i,{fileName:h(e.variables.processType),format:\"[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}]{scope} {text}\",getFile:m,inspectOptions:{depth:5},level:\"silly\",maxSize:1048576,readAllLogs:function({fileFilter:e=(e=>e.endsWith(\".log\"))}={}){const t=s.dirname(i.resolvePathFn(r));return n.readdirSync(t).map((e=>s.join(t,e))).filter(e).map((e=>{try{return{path:e,lines:n.readFileSync(e,\"utf8\").split(o.EOL)}}catch{return null}})).filter(Boolean)},sync:!0,transforms:[l,u,d],writeOptions:{flag:\"a\",mode:438,encoding:\"utf8\"},archiveLogFn(e){const t=e.toString(),r=s.parse(t);try{n.renameSync(t,s.join(r.dir,`${r.name}.old${r.ext}`))}catch(t){p(\"Could not rotate log\",t);const r=Math.round(i.maxSize/4);e.crop(Math.min(r,262144))}},resolvePathFn:e=>s.join(e.libraryDefaultDir,e.fileName)});function i(t){const r=m(t);i.maxSize>0&&r.size>i.maxSize&&(i.archiveLogFn(r),r.reset());const n=c({logger:e,message:t,transport:i});r.writeLine(n)}function p(t,r=null,n=\"error\"){const s=[`electron-log.transports.file: ${t}`];r&&s.push(r),e.transports.console({data:s,date:new Date,level:n})}function m(e){r||(r=Object.create(Object.prototype,{...Object.getOwnPropertyDescriptors(a.getPathVariables(process.platform)),fileName:{get:()=>i.fileName,enumerable:!0}}),\"function\"==typeof i.archiveLog&&(i.archiveLogFn=i.archiveLog,p(\"archiveLog is deprecated. Use archiveLogFn instead\")),\"function\"==typeof i.resolvePath&&(i.resolvePathFn=i.resolvePath,p(\"resolvePath is deprecated. Use resolvePathFn instead\")));const n=i.resolvePathFn(r,e);return t.provide({filePath:n,writeAsync:!i.sync,writeOptions:i.writeOptions})}};const f=new i;function h(e=process.type){switch(e){case\"renderer\":return\"renderer.log\";case\"worker\":return\"worker.log\";default:return\"main.log\"}}},2628:(e,t,r)=>{\"use strict\";const n=r(7147),s=r(1017);function o(...e){if(!e[0])return null;try{const t=function(e,t){let r=t;for(;;){const t=s.parse(r),o=t.root,i=t.dir;if(n.existsSync(s.join(r,e)))return s.resolve(s.join(r,e));if(r===o)return null;r=i}}(\"package.json\",s.join(...e));if(!t)return null;const r=JSON.parse(n.readFileSync(t,\"utf8\")),o=r.productName||r.name;if(!o||\"electron\"===o.toLowerCase())return null;if(r.productName||r.name)return{name:o,version:r.version}}catch(e){return null}}e.exports={readPackageJson:function(){return o(r.c[r.s]&&r.c[r.s].filename)||o(function(){const e=process.argv.filter((e=>0===e.indexOf(\"--user-data-dir=\")));return 0===e.length||\"string\"!=typeof e[0]?null:e[0].replace(\"--user-data-dir=\",\"\")}())||o(process.resourcesPath,\"app.asar\")||o(process.resourcesPath,\"app\")||o(process.cwd())||{name:null,version:null}},tryReadJsonAt:o}},1230:(e,t,r)=>{\"use strict\";const n=r(2037),s=r(1017),o=r(2674),i=r(2628);function a(e){const t=o.getPath(\"appData\");if(t)return t;const r=c();switch(e){case\"darwin\":return s.join(r,\"Library/Application Support\");case\"win32\":return process.env.APPDATA||s.join(r,\"AppData/Roaming\");default:return process.env.XDG_CONFIG_HOME||s.join(r,\".config\")}}function c(){return n.homedir?n.homedir():process.env.HOME}function l(e,t){return\"darwin\"===e?s.join(c(),\"Library/Logs\",t):s.join(f(e,t),\"logs\")}function u(e){return\"darwin\"===e?s.join(c(),\"Library/Logs\",\"{appName}\"):s.join(a(e),\"{appName}\",\"logs\")}function d(){let e=o.getName()||\"\",t=o.getVersion();if(\"electron\"===e.toLowerCase()&&(e=\"\",t=\"\"),e&&t)return{name:e,version:t};const r=i.readPackageJson();return e||(e=r.name),t||(t=r.version),e||(e=\"Electron\"),{name:e,version:t}}function f(e,t){return o.getName()!==t?s.join(a(e),t):o.getPath(\"userData\")||s.join(a(e),t)}e.exports={getAppData:a,getLibraryDefaultDir:l,getLibraryTemplate:u,getNameAndVersion:d,getPathVariables:function(e){const t=d(),r=t.name,s=t.version;return{appData:a(e),appName:r,appVersion:s,get electronDefaultDir(){return o.getPath(\"logs\")},home:c(),libraryDefaultDir:l(e,r),libraryTemplate:u(e),temp:o.getPath(\"temp\")||n.tmpdir(),userData:f(e,r)}},getUserData:f}},7988:(e,t,r)=>{\"use strict\";const n=r(3685),s=r(5687),{transform:o}=r(6829),{removeStyles:i}=r(2163),{toJSON:a,maxDepth:c}=r(2189);e.exports=function(e){return Object.assign(t,{client:{name:\"electron-application\"},depth:6,level:!1,requestOptions:{},transforms:[i,a,c],makeBodyFn:({message:e})=>JSON.stringify({client:t.client,data:e.data,date:e.date.getTime(),level:e.level,scope:e.scope,variables:e.variables}),processErrorFn({error:r}){e.processMessage({data:[`electron-log: can't POST ${t.url}`,r],level:\"warn\"},{transports:[\"console\",\"file\"]})},sendRequestFn({serverUrl:e,requestOptions:t,body:r}){const o=(e.startsWith(\"https:\")?s:n).request(e,{method:\"POST\",...t,headers:{\"Content-Type\":\"application/json\",\"Content-Length\":r.length,...t.headers}});return o.write(r),o.end(),o}});function t(r){if(!t.url)return;const n=t.makeBodyFn({logger:e,message:{...r,data:o({logger:e,message:r,transport:t})},transport:t}),s=t.sendRequestFn({serverUrl:t.url,requestOptions:t.requestOptions,body:Buffer.from(n,\"utf8\")});s.on(\"error\",(n=>t.processErrorFn({error:n,logger:e,message:r,request:s,transport:t})))}}},9654:(e,t,r)=>{\"use strict\";let n={};try{n=r(2298)}catch(e){}function s({contextBridge:e,ipcRenderer:t}){if(!t)return;t.on(\"__ELECTRON_LOG_IPC__\",((e,t)=>{window.postMessage({cmd:\"message\",...t})})),t.invoke(\"__ELECTRON_LOG__\",{cmd:\"getOptions\"}).catch((e=>console.error(new Error(`electron-log isn't initialized in the main process. Please call log.initialize() before. ${e.message}`))));const r={sendToMain(e){try{t.send(\"__ELECTRON_LOG__\",e)}catch(r){console.error(\"electronLog.sendToMain \",r,\"data:\",e),t.send(\"__ELECTRON_LOG__\",{cmd:\"errorHandler\",error:{message:r?.message,stack:r?.stack},errorName:\"sendToMain\"})}},log(...e){r.sendToMain({data:e,level:\"info\"})}};for(const e of[\"error\",\"warn\",\"info\",\"verbose\",\"debug\",\"silly\"])r[e]=(...t)=>r.sendToMain({data:t,level:e});if(e&&process.contextIsolated)try{e.exposeInMainWorld(\"__electronLog\",r)}catch{}\"object\"==typeof window?window.__electronLog=r:__electronLog=r}n.ipcRenderer&&s(n),e.exports=s},9618:(e,t,r)=>{\"use strict\";const n=r(2666),s=r(3893),o=r(6448),i=r(8671);e.exports=function(){const e=new n({allowUnknownLevel:!0,errorHandler:new s,initializeFn:()=>{},logId:\"default\",transportFactories:{console:o,ipc:i},variables:{processType:\"renderer\"}});return e.errorHandler.setOptions({logFn({error:t,errorName:r,showDialog:n}){e.transports.console({data:[r,t].filter(Boolean),level:\"error\"}),e.transports.ipc({cmd:\"errorHandler\",error:{cause:t?.cause,code:t?.code,name:t?.name,message:t?.message,stack:t?.stack},errorName:r,logId:e.logId,showDialog:n})}}),\"object\"==typeof window&&window.addEventListener(\"message\",(e=>{const{cmd:t,logId:r,...s}=e.data||{},o=n.getInstance({logId:r});\"message\"===t&&o.processMessage(s,{transports:[\"console\"]})})),new Proxy(e,{get:(t,r)=>void 0!==t[r]?t[r]:(...t)=>e.logData(t,{level:r})})}(),e.exports.Logger=n,e.exports.default=e.exports},3893:e=>{\"use strict\";const t=console.error;e.exports=class{logFn=null;onError=null;showDialog=!1;preventDefault=!0;constructor({logFn:e=null}={}){this.handleError=this.handleError.bind(this),this.handleRejection=this.handleRejection.bind(this),this.startCatching=this.startCatching.bind(this),this.logFn=e}handle(e,{logFn:r=this.logFn,errorName:n=\"\",onError:s=this.onError,showDialog:o=this.showDialog}={}){try{!1!==s?.({error:e,errorName:n,processType:\"renderer\"})&&r({error:e,errorName:n,showDialog:o})}catch{t(e)}}setOptions({logFn:e,onError:t,preventDefault:r,showDialog:n}){\"function\"==typeof e&&(this.logFn=e),\"function\"==typeof t&&(this.onError=t),\"boolean\"==typeof r&&(this.preventDefault=r),\"boolean\"==typeof n&&(this.showDialog=n)}startCatching({onError:e,showDialog:t}={}){this.isActive||(this.isActive=!0,this.setOptions({onError:e,showDialog:t}),window.addEventListener(\"error\",(e=>{this.preventDefault&&e.preventDefault?.(),this.handleError(e.error||e)})),window.addEventListener(\"unhandledrejection\",(e=>{this.preventDefault&&e.preventDefault?.(),this.handleRejection(e.reason||e)})))}handleError(e){this.handle(e,{errorName:\"Unhandled\"})}handleRejection(e){const t=e instanceof Error?e:new Error(JSON.stringify(e));this.handle(t,{errorName:\"Unhandled rejection\"})}}},6448:e=>{\"use strict\";e.exports=function(e){return Object.assign(r,{format:\"{h}:{i}:{s}.{ms}{scope} › {text}\",formatDataFn:({data:t=[],date:n=new Date,format:s=r.format,logId:o=e.logId,scope:i=e.scopeName,...a})=>\"function\"==typeof s?s({...a,data:t,date:n,logId:o,scope:i}):(\"string\"!=typeof s||(t.unshift(s),\"string\"==typeof t[1]&&t[1].match(/%[1cdfiOos]/)&&(t=[`${t[0]} ${t[1]}`,...t.slice(2)]),t[0]=t[0].replace(/\\{(\\w+)}/g,((e,t)=>{switch(t){case\"level\":return a.level;case\"logId\":return o;case\"scope\":return i?` (${i})`:\"\";case\"text\":return\"\";case\"y\":return n.getFullYear().toString(10);case\"m\":return(n.getMonth()+1).toString(10).padStart(2,\"0\");case\"d\":return n.getDate().toString(10).padStart(2,\"0\");case\"h\":return n.getHours().toString(10).padStart(2,\"0\");case\"i\":return n.getMinutes().toString(10).padStart(2,\"0\");case\"s\":return n.getSeconds().toString(10).padStart(2,\"0\");case\"ms\":return n.getMilliseconds().toString(10).padStart(3,\"0\");case\"iso\":return n.toISOString();default:return a.variables?.[t]||e}})).trim()),t),writeFn({message:{level:e,data:r}}){const n=t[e]||t.info;setTimeout((()=>n(...r)))}});function r(e){r.writeFn({message:{...e,data:r.formatDataFn(e)}})}};const t={error:console.error,warn:console.warn,info:console.info,verbose:console.info,debug:console.debug,silly:console.debug,log:console.log}},8671:e=>{\"use strict\";e.exports=function(e){return Object.assign(r,{depth:5,serializeFn:(e,{depth:n=5,seen:s=new WeakSet}={})=>n<1?`[${typeof e}]`:s.has(e)?e:[\"function\",\"symbol\"].includes(typeof e)?e.toString():Object(e)!==e?e:t.has(e.constructor)?`[${e.constructor.name}]`:Array.isArray(e)?e.map((e=>r.serializeFn(e,{depth:n-1,seen:s}))):e instanceof Error?e.stack:e instanceof Map?new Map(Array.from(e).map((([e,t])=>[r.serializeFn(e,{depth:n-1,seen:s}),r.serializeFn(t,{depth:n-1,seen:s})]))):e instanceof Set?new Set(Array.from(e).map((e=>r.serializeFn(e,{depth:n-1,seen:s})))):(s.add(e),Object.fromEntries(Object.entries(e).map((([e,t])=>[e,r.serializeFn(t,{depth:n-1,seen:s})]))))});function r(t){if(window.__electronLog)try{__electronLog.sendToMain(r.serializeFn(t,{depth:r.depth}))}catch(r){e.transports.console({data:[\"electronLog.transports.ipc\",r,\"data:\",t.data],level:\"error\"})}else e.processMessage({data:[\"electron-log: logger isn't initialized in the main process\"],level:\"error\"},{transports:[\"console\"]})}};const t=new Set([Promise,WeakMap,WeakSet])},6143:(e,t,r)=>{\"use strict\";const n=r(1017),{app:s,ipcMain:o,ipcRenderer:i,shell:a}=r(2298),c=r(9658);let l=!1;const u=()=>{if(!o||!s)throw new Error(\"Electron Store: You need to call `.initRenderer()` from the main process.\");const e={defaultCwd:s.getPath(\"userData\"),appVersion:s.getVersion()};return l||(o.on(\"electron-store-get-data\",(t=>{t.returnValue=e})),l=!0),e};e.exports=class extends c{constructor(e){let t,r;if(i){const e=i.sendSync(\"electron-store-get-data\");if(!e)throw new Error(\"Electron Store: You need to call `.initRenderer()` from the main process.\");({defaultCwd:t,appVersion:r}=e)}else o&&s&&({defaultCwd:t,appVersion:r}=u());(e={name:\"config\",...e}).projectVersion||(e.projectVersion=r),e.cwd?e.cwd=n.isAbsolute(e.cwd)?e.cwd:n.join(t,e.cwd):e.cwd=t,e.configName=e.name,delete e.name,super(e)}static initRenderer(){u()}openInEditor(){a.openPath(this.path)}}},1766:(e,t,r)=>{\"use strict\";const n=r(1017),s=r(2037),o=s.homedir(),i=s.tmpdir(),{env:a}=process,c=(e,t)=>{if(\"string\"!=typeof e)throw new TypeError(\"Expected string, got \"+typeof e);return(t=Object.assign({suffix:\"nodejs\"},t)).suffix&&(e+=`-${t.suffix}`),\"darwin\"===process.platform?(e=>{const t=n.join(o,\"Library\");return{data:n.join(t,\"Application Support\",e),config:n.join(t,\"Preferences\",e),cache:n.join(t,\"Caches\",e),log:n.join(t,\"Logs\",e),temp:n.join(i,e)}})(e):\"win32\"===process.platform?(e=>{const t=a.APPDATA||n.join(o,\"AppData\",\"Roaming\"),r=a.LOCALAPPDATA||n.join(o,\"AppData\",\"Local\");return{data:n.join(r,e,\"Data\"),config:n.join(t,e,\"Config\"),cache:n.join(r,e,\"Cache\"),log:n.join(r,e,\"Log\"),temp:n.join(i,e)}})(e):(e=>{const t=n.basename(o);return{data:n.join(a.XDG_DATA_HOME||n.join(o,\".local\",\"share\"),e),config:n.join(a.XDG_CONFIG_HOME||n.join(o,\".config\"),e),cache:n.join(a.XDG_CACHE_HOME||n.join(o,\".cache\"),e),log:n.join(a.XDG_STATE_HOME||n.join(o,\".local\",\"state\"),e),temp:n.join(i,t,e)}})(e)};e.exports=c,e.exports.default=c},4063:e=>{\"use strict\";e.exports=function e(t,r){if(t===r)return!0;if(t&&r&&\"object\"==typeof t&&\"object\"==typeof r){if(t.constructor!==r.constructor)return!1;var n,s,o;if(Array.isArray(t)){if((n=t.length)!=r.length)return!1;for(s=n;0!=s--;)if(!e(t[s],r[s]))return!1;return!0}if(t.constructor===RegExp)return t.source===r.source&&t.flags===r.flags;if(t.valueOf!==Object.prototype.valueOf)return t.valueOf()===r.valueOf();if(t.toString!==Object.prototype.toString)return t.toString()===r.toString();if((n=(o=Object.keys(t)).length)!==Object.keys(r).length)return!1;for(s=n;0!=s--;)if(!Object.prototype.hasOwnProperty.call(r,o[s]))return!1;for(s=n;0!=s--;){var i=o[s];if(!e(t[i],r[i]))return!1}return!0}return t!=t&&r!=r}},9516:(e,t,r)=>{\"use strict\";const n=r(1017),s=r(6401);e.exports=(e,t={})=>{const r=n.resolve(t.cwd||\"\"),{root:o}=n.parse(r),i=[].concat(e);return new Promise((e=>{!function t(r){s(i,{cwd:r}).then((s=>{s?e(n.join(r,s)):r===o?e(null):t(n.dirname(r))}))}(r)}))},e.exports.sync=(e,t={})=>{let r=n.resolve(t.cwd||\"\");const{root:o}=n.parse(r),i=[].concat(e);for(;;){const e=s.sync(i,{cwd:r});if(e)return n.join(r,e);if(r===o)return null;r=n.dirname(r)}}},4290:e=>{\"use strict\";e.exports=e=>{const t=typeof e;return null!==e&&(\"object\"===t||\"function\"===t)}},6401:(e,t,r)=>{\"use strict\";const n=r(1017),s=r(6789),o=r(1885);e.exports=(e,t)=>(t=Object.assign({cwd:process.cwd()},t),o(e,(e=>s(n.resolve(t.cwd,e))),t)),e.exports.sync=(e,t)=>{t=Object.assign({cwd:process.cwd()},t);for(const r of e)if(s.sync(n.resolve(t.cwd,r)))return r}},6789:(e,t,r)=>{\"use strict\";const n=r(7147);e.exports=e=>new Promise((t=>{n.access(e,(e=>{t(!e)}))})),e.exports.sync=e=>{try{return n.accessSync(e),!0}catch(e){return!1}}},9593:(e,t,r)=>{\"use strict\";const n=r(4411),s=Symbol(\"max\"),o=Symbol(\"length\"),i=Symbol(\"lengthCalculator\"),a=Symbol(\"allowStale\"),c=Symbol(\"maxAge\"),l=Symbol(\"dispose\"),u=Symbol(\"noDisposeOnSet\"),d=Symbol(\"lruList\"),f=Symbol(\"cache\"),h=Symbol(\"updateAgeOnGet\"),p=()=>1,m=(e,t,r)=>{const n=e[f].get(t);if(n){const t=n.value;if(y(e,t)){if(v(e,n),!e[a])return}else r&&(e[h]&&(n.value.now=Date.now()),e[d].unshiftNode(n));return t.value}},y=(e,t)=>{if(!t||!t.maxAge&&!e[c])return!1;const r=Date.now()-t.now;return t.maxAge?r>t.maxAge:e[c]&&r>e[c]},g=e=>{if(e[o]>e[s])for(let t=e[d].tail;e[o]>e[s]&&null!==t;){const r=t.prev;v(e,t),t=r}},v=(e,t)=>{if(t){const r=t.value;e[l]&&e[l](r.key,r.value),e[o]-=r.length,e[f].delete(r.key),e[d].removeNode(t)}};class w{constructor(e,t,r,n,s){this.key=e,this.value=t,this.length=r,this.now=n,this.maxAge=s||0}}const E=(e,t,r,n)=>{let s=r.value;y(e,s)&&(v(e,r),e[a]||(s=void 0)),s&&t.call(n,s.value,s.key,e)};e.exports=class{constructor(e){if(\"number\"==typeof e&&(e={max:e}),e||(e={}),e.max&&(\"number\"!=typeof e.max||e.max<0))throw new TypeError(\"max must be a non-negative number\");this[s]=e.max||1/0;const t=e.length||p;if(this[i]=\"function\"!=typeof t?p:t,this[a]=e.stale||!1,e.maxAge&&\"number\"!=typeof e.maxAge)throw new TypeError(\"maxAge must be a number\");this[c]=e.maxAge||0,this[l]=e.dispose,this[u]=e.noDisposeOnSet||!1,this[h]=e.updateAgeOnGet||!1,this.reset()}set max(e){if(\"number\"!=typeof e||e<0)throw new TypeError(\"max must be a non-negative number\");this[s]=e||1/0,g(this)}get max(){return this[s]}set allowStale(e){this[a]=!!e}get allowStale(){return this[a]}set maxAge(e){if(\"number\"!=typeof e)throw new TypeError(\"maxAge must be a non-negative number\");this[c]=e,g(this)}get maxAge(){return this[c]}set lengthCalculator(e){\"function\"!=typeof e&&(e=p),e!==this[i]&&(this[i]=e,this[o]=0,this[d].forEach((e=>{e.length=this[i](e.value,e.key),this[o]+=e.length}))),g(this)}get lengthCalculator(){return this[i]}get length(){return this[o]}get itemCount(){return this[d].length}rforEach(e,t){t=t||this;for(let r=this[d].tail;null!==r;){const n=r.prev;E(this,e,r,t),r=n}}forEach(e,t){t=t||this;for(let r=this[d].head;null!==r;){const n=r.next;E(this,e,r,t),r=n}}keys(){return this[d].toArray().map((e=>e.key))}values(){return this[d].toArray().map((e=>e.value))}reset(){this[l]&&this[d]&&this[d].length&&this[d].forEach((e=>this[l](e.key,e.value))),this[f]=new Map,this[d]=new n,this[o]=0}dump(){return this[d].map((e=>!y(this,e)&&{k:e.key,v:e.value,e:e.now+(e.maxAge||0)})).toArray().filter((e=>e))}dumpLru(){return this[d]}set(e,t,r){if((r=r||this[c])&&\"number\"!=typeof r)throw new TypeError(\"maxAge must be a number\");const n=r?Date.now():0,a=this[i](t,e);if(this[f].has(e)){if(a>this[s])return v(this,this[f].get(e)),!1;const i=this[f].get(e).value;return this[l]&&(this[u]||this[l](e,i.value)),i.now=n,i.maxAge=r,i.value=t,this[o]+=a-i.length,i.length=a,this.get(e),g(this),!0}const h=new w(e,t,a,n,r);return h.length>this[s]?(this[l]&&this[l](e,t),!1):(this[o]+=h.length,this[d].unshift(h),this[f].set(e,this[d].head),g(this),!0)}has(e){if(!this[f].has(e))return!1;const t=this[f].get(e).value;return!y(this,t)}get(e){return m(this,e,!0)}peek(e){return m(this,e,!1)}pop(){const e=this[d].tail;return e?(v(this,e),e.value):null}del(e){v(this,this[f].get(e))}load(e){this.reset();const t=Date.now();for(let r=e.length-1;r>=0;r--){const n=e[r],s=n.e||0;if(0===s)this.set(n.k,n.v);else{const e=s-t;e>0&&this.set(n.k,n.v,e)}}}prune(){this[f].forEach(((e,t)=>m(this,t,!1)))}}},4341:e=>{\"use strict\";const t=(e,t)=>{for(const r of Reflect.ownKeys(t))Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r));return e};e.exports=t,e.exports.default=t},1572:function(e,t,r){e.exports=(r(2081),r(6113),function(e){function t(n){if(r[n])return r[n].exports;var s=r[n]={exports:{},id:n,loaded:!1};return e[n].call(s.exports,s,s.exports,t),s.loaded=!0,s.exports}var r={};return t.m=e,t.c=r,t.p=\"\",t(0)}([function(e,t,r){e.exports=r(34)},function(e,t,r){var n=r(29)(\"wks\"),s=r(33),o=r(2).Symbol,i=\"function\"==typeof o;(e.exports=function(e){return n[e]||(n[e]=i&&o[e]||(i?o:s)(\"Symbol.\"+e))}).store=n},function(e,t){var r=e.exports=\"undefined\"!=typeof window&&window.Math==Math?window:\"undefined\"!=typeof self&&self.Math==Math?self:Function(\"return this\")();\"number\"==typeof __g&&(__g=r)},function(e,t,r){var n=r(9);e.exports=function(e){if(!n(e))throw TypeError(e+\" is not an object!\");return e}},function(e,t,r){e.exports=!r(24)((function(){return 7!=Object.defineProperty({},\"a\",{get:function(){return 7}}).a}))},function(e,t,r){var n=r(12),s=r(17);e.exports=r(4)?function(e,t,r){return n.f(e,t,s(1,r))}:function(e,t,r){return e[t]=r,e}},function(e,t){var r=e.exports={version:\"2.4.0\"};\"number\"==typeof __e&&(__e=r)},function(e,t,r){var n=r(14);e.exports=function(e,t,r){if(n(e),void 0===t)return e;switch(r){case 1:return function(r){return e.call(t,r)};case 2:return function(r,n){return e.call(t,r,n)};case 3:return function(r,n,s){return e.call(t,r,n,s)}}return function(){return e.apply(t,arguments)}}},function(e,t){var r={}.hasOwnProperty;e.exports=function(e,t){return r.call(e,t)}},function(e,t){e.exports=function(e){return\"object\"==typeof e?null!==e:\"function\"==typeof e}},function(e,t){e.exports={}},function(e,t){var r={}.toString;e.exports=function(e){return r.call(e).slice(8,-1)}},function(e,t,r){var n=r(3),s=r(26),o=r(32),i=Object.defineProperty;t.f=r(4)?Object.defineProperty:function(e,t,r){if(n(e),t=o(t,!0),n(r),s)try{return i(e,t,r)}catch(e){}if(\"get\"in r||\"set\"in r)throw TypeError(\"Accessors not supported!\");return\"value\"in r&&(e[t]=r.value),e}},function(e,t,r){var n=r(42),s=r(15);e.exports=function(e){return n(s(e))}},function(e,t){e.exports=function(e){if(\"function\"!=typeof e)throw TypeError(e+\" is not a function!\");return e}},function(e,t){e.exports=function(e){if(null==e)throw TypeError(\"Can't call method on  \"+e);return e}},function(e,t,r){var n=r(9),s=r(2).document,o=n(s)&&n(s.createElement);e.exports=function(e){return o?s.createElement(e):{}}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,r){var n=r(12).f,s=r(8),o=r(1)(\"toStringTag\");e.exports=function(e,t,r){e&&!s(e=r?e:e.prototype,o)&&n(e,o,{configurable:!0,value:t})}},function(e,t,r){var n=r(29)(\"keys\"),s=r(33);e.exports=function(e){return n[e]||(n[e]=s(e))}},function(e,t){var r=Math.ceil,n=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?n:r)(e)}},function(e,t,r){var n=r(11),s=r(1)(\"toStringTag\"),o=\"Arguments\"==n(function(){return arguments}());e.exports=function(e){var t,r,i;return void 0===e?\"Undefined\":null===e?\"Null\":\"string\"==typeof(r=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),s))?r:o?n(t):\"Object\"==(i=n(t))&&\"function\"==typeof t.callee?\"Arguments\":i}},function(e,t){e.exports=\"constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf\".split(\",\")},function(e,t,r){var n=r(2),s=r(6),o=r(7),i=r(5),a=\"prototype\",c=function(e,t,r){var l,u,d,f=e&c.F,h=e&c.G,p=e&c.S,m=e&c.P,y=e&c.B,g=e&c.W,v=h?s:s[t]||(s[t]={}),w=v[a],E=h?n:p?n[t]:(n[t]||{})[a];for(l in h&&(r=t),r)(u=!f&&E&&void 0!==E[l])&&l in v||(d=u?E[l]:r[l],v[l]=h&&\"function\"!=typeof E[l]?r[l]:y&&u?o(d,n):g&&E[l]==d?function(e){var t=function(t,r,n){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,r)}return new e(t,r,n)}return e.apply(this,arguments)};return t[a]=e[a],t}(d):m&&\"function\"==typeof d?o(Function.call,d):d,m&&((v.virtual||(v.virtual={}))[l]=d,e&c.R&&w&&!w[l]&&i(w,l,d)))};c.F=1,c.G=2,c.S=4,c.P=8,c.B=16,c.W=32,c.U=64,c.R=128,e.exports=c},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t,r){e.exports=r(2).document&&document.documentElement},function(e,t,r){e.exports=!r(4)&&!r(24)((function(){return 7!=Object.defineProperty(r(16)(\"div\"),\"a\",{get:function(){return 7}}).a}))},function(e,t,r){\"use strict\";var n=r(28),s=r(23),o=r(57),i=r(5),a=r(8),c=r(10),l=r(45),u=r(18),d=r(52),f=r(1)(\"iterator\"),h=!([].keys&&\"next\"in[].keys()),p=\"keys\",m=\"values\",y=function(){return this};e.exports=function(e,t,r,g,v,w,E){l(r,t,g);var $,_,b,S=function(e){if(!h&&e in N)return N[e];switch(e){case p:case m:return function(){return new r(this,e)}}return function(){return new r(this,e)}},O=t+\" Iterator\",P=v==m,x=!1,N=e.prototype,I=N[f]||N[\"@@iterator\"]||v&&N[v],j=I||S(v),R=v?P?S(\"entries\"):j:void 0,A=\"Array\"==t&&N.entries||I;if(A&&(b=d(A.call(new e)))!==Object.prototype&&(u(b,O,!0),n||a(b,f)||i(b,f,y)),P&&I&&I.name!==m&&(x=!0,j=function(){return I.call(this)}),n&&!E||!h&&!x&&N[f]||i(N,f,j),c[t]=j,c[O]=y,v)if($={values:P?j:S(m),keys:w?j:S(p),entries:R},E)for(_ in $)_ in N||o(N,_,$[_]);else s(s.P+s.F*(h||x),t,$);return $}},function(e,t){e.exports=!0},function(e,t,r){var n=r(2),s=\"__core-js_shared__\",o=n[s]||(n[s]={});e.exports=function(e){return o[e]||(o[e]={})}},function(e,t,r){var n,s,o,i=r(7),a=r(41),c=r(25),l=r(16),u=r(2),d=u.process,f=u.setImmediate,h=u.clearImmediate,p=u.MessageChannel,m=0,y={},g=\"onreadystatechange\",v=function(){var e=+this;if(y.hasOwnProperty(e)){var t=y[e];delete y[e],t()}},w=function(e){v.call(e.data)};f&&h||(f=function(e){for(var t=[],r=1;arguments.length>r;)t.push(arguments[r++]);return y[++m]=function(){a(\"function\"==typeof e?e:Function(e),t)},n(m),m},h=function(e){delete y[e]},\"process\"==r(11)(d)?n=function(e){d.nextTick(i(v,e,1))}:p?(o=(s=new p).port2,s.port1.onmessage=w,n=i(o.postMessage,o,1)):u.addEventListener&&\"function\"==typeof postMessage&&!u.importScripts?(n=function(e){u.postMessage(e+\"\",\"*\")},u.addEventListener(\"message\",w,!1)):n=g in l(\"script\")?function(e){c.appendChild(l(\"script\"))[g]=function(){c.removeChild(this),v.call(e)}}:function(e){setTimeout(i(v,e,1),0)}),e.exports={set:f,clear:h}},function(e,t,r){var n=r(20),s=Math.min;e.exports=function(e){return e>0?s(n(e),9007199254740991):0}},function(e,t,r){var n=r(9);e.exports=function(e,t){if(!n(e))return e;var r,s;if(t&&\"function\"==typeof(r=e.toString)&&!n(s=r.call(e)))return s;if(\"function\"==typeof(r=e.valueOf)&&!n(s=r.call(e)))return s;if(!t&&\"function\"==typeof(r=e.toString)&&!n(s=r.call(e)))return s;throw TypeError(\"Can't convert object to primitive value\")}},function(e,t){var r=0,n=Math.random();e.exports=function(e){return\"Symbol(\".concat(void 0===e?\"\":e,\")_\",(++r+n).toString(36))}},function(e,t,r){\"use strict\";function n(e){return(0,a.createHash)(\"sha256\").update(e).digest(\"hex\")}function s(e){switch(c){case\"darwin\":return e.split(\"IOPlatformUUID\")[1].split(\"\\n\")[0].replace(/\\=|\\s+|\\\"/gi,\"\").toLowerCase();case\"win32\":return e.toString().split(\"REG_SZ\")[1].replace(/\\r+|\\n+|\\s+/gi,\"\").toLowerCase();case\"linux\":case\"freebsd\":return e.toString().replace(/\\r+|\\n+|\\s+/gi,\"\").toLowerCase();default:throw new Error(\"Unsupported platform: \"+process.platform)}}Object.defineProperty(t,\"__esModule\",{value:!0});var o=function(e){return e&&e.__esModule?e:{default:e}}(r(35));t.machineIdSync=function(e){var t=s((0,i.execSync)(l[c]).toString());return e?t:n(t)},t.machineId=function(e){return new o.default((function(t,r){return(0,i.exec)(l[c],{},(function(o,i,a){if(o)return r(new Error(\"Error while obtaining machine id: \"+o.stack));var c=s(i.toString());return t(e?c:n(c))}))}))};var i=r(70),a=r(71),c=process.platform,l={darwin:\"ioreg -rd1 -c IOPlatformExpertDevice\",win32:{native:\"%windir%\\\\System32\",mixed:\"%windir%\\\\sysnative\\\\cmd.exe /c %windir%\\\\System32\"}[\"win32\"!==process.platform?\"\":\"ia32\"===process.arch&&process.env.hasOwnProperty(\"PROCESSOR_ARCHITEW6432\")?\"mixed\":\"native\"]+\"\\\\REG.exe QUERY HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\Microsoft\\\\Cryptography /v MachineGuid\",linux:\"( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :\",freebsd:\"kenv -q smbios.system.uuid || sysctl -n kern.hostuuid\"}},function(e,t,r){e.exports={default:r(36),__esModule:!0}},function(e,t,r){r(66),r(68),r(69),r(67),e.exports=r(6).Promise},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t,r,n){if(!(e instanceof t)||void 0!==n&&n in e)throw TypeError(r+\": incorrect invocation!\");return e}},function(e,t,r){var n=r(13),s=r(31),o=r(62);e.exports=function(e){return function(t,r,i){var a,c=n(t),l=s(c.length),u=o(i,l);if(e&&r!=r){for(;l>u;)if((a=c[u++])!=a)return!0}else for(;l>u;u++)if((e||u in c)&&c[u]===r)return e||u||0;return!e&&-1}}},function(e,t,r){var n=r(7),s=r(44),o=r(43),i=r(3),a=r(31),c=r(64),l={},u={};t=e.exports=function(e,t,r,d,f){var h,p,m,y,g=f?function(){return e}:c(e),v=n(r,d,t?2:1),w=0;if(\"function\"!=typeof g)throw TypeError(e+\" is not iterable!\");if(o(g)){for(h=a(e.length);h>w;w++)if((y=t?v(i(p=e[w])[0],p[1]):v(e[w]))===l||y===u)return y}else for(m=g.call(e);!(p=m.next()).done;)if((y=s(m,v,p.value,t))===l||y===u)return y},t.BREAK=l,t.RETURN=u},function(e,t){e.exports=function(e,t,r){var n=void 0===r;switch(t.length){case 0:return n?e():e.call(r);case 1:return n?e(t[0]):e.call(r,t[0]);case 2:return n?e(t[0],t[1]):e.call(r,t[0],t[1]);case 3:return n?e(t[0],t[1],t[2]):e.call(r,t[0],t[1],t[2]);case 4:return n?e(t[0],t[1],t[2],t[3]):e.call(r,t[0],t[1],t[2],t[3])}return e.apply(r,t)}},function(e,t,r){var n=r(11);e.exports=Object(\"z\").propertyIsEnumerable(0)?Object:function(e){return\"String\"==n(e)?e.split(\"\"):Object(e)}},function(e,t,r){var n=r(10),s=r(1)(\"iterator\"),o=Array.prototype;e.exports=function(e){return void 0!==e&&(n.Array===e||o[s]===e)}},function(e,t,r){var n=r(3);e.exports=function(e,t,r,s){try{return s?t(n(r)[0],r[1]):t(r)}catch(t){var o=e.return;throw void 0!==o&&n(o.call(e)),t}}},function(e,t,r){\"use strict\";var n=r(49),s=r(17),o=r(18),i={};r(5)(i,r(1)(\"iterator\"),(function(){return this})),e.exports=function(e,t,r){e.prototype=n(i,{next:s(1,r)}),o(e,t+\" Iterator\")}},function(e,t,r){var n=r(1)(\"iterator\"),s=!1;try{var o=[7][n]();o.return=function(){s=!0},Array.from(o,(function(){throw 2}))}catch(e){}e.exports=function(e,t){if(!t&&!s)return!1;var r=!1;try{var o=[7],i=o[n]();i.next=function(){return{done:r=!0}},o[n]=function(){return i},e(o)}catch(e){}return r}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,r){var n=r(2),s=r(30).set,o=n.MutationObserver||n.WebKitMutationObserver,i=n.process,a=n.Promise,c=\"process\"==r(11)(i);e.exports=function(){var e,t,r,l=function(){var n,s;for(c&&(n=i.domain)&&n.exit();e;){s=e.fn,e=e.next;try{s()}catch(n){throw e?r():t=void 0,n}}t=void 0,n&&n.enter()};if(c)r=function(){i.nextTick(l)};else if(o){var u=!0,d=document.createTextNode(\"\");new o(l).observe(d,{characterData:!0}),r=function(){d.data=u=!u}}else if(a&&a.resolve){var f=a.resolve();r=function(){f.then(l)}}else r=function(){s.call(n,l)};return function(n){var s={fn:n,next:void 0};t&&(t.next=s),e||(e=s,r()),t=s}}},function(e,t,r){var n=r(3),s=r(50),o=r(22),i=r(19)(\"IE_PROTO\"),a=function(){},c=\"prototype\",l=function(){var e,t=r(16)(\"iframe\"),n=o.length;for(t.style.display=\"none\",r(25).appendChild(t),t.src=\"javascript:\",(e=t.contentWindow.document).open(),e.write(\"<script>document.F=Object<\\/script>\"),e.close(),l=e.F;n--;)delete l[c][o[n]];return l()};e.exports=Object.create||function(e,t){var r;return null!==e?(a[c]=n(e),r=new a,a[c]=null,r[i]=e):r=l(),void 0===t?r:s(r,t)}},function(e,t,r){var n=r(12),s=r(3),o=r(54);e.exports=r(4)?Object.defineProperties:function(e,t){s(e);for(var r,i=o(t),a=i.length,c=0;a>c;)n.f(e,r=i[c++],t[r]);return e}},function(e,t,r){var n=r(55),s=r(17),o=r(13),i=r(32),a=r(8),c=r(26),l=Object.getOwnPropertyDescriptor;t.f=r(4)?l:function(e,t){if(e=o(e),t=i(t,!0),c)try{return l(e,t)}catch(e){}if(a(e,t))return s(!n.f.call(e,t),e[t])}},function(e,t,r){var n=r(8),s=r(63),o=r(19)(\"IE_PROTO\"),i=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=s(e),n(e,o)?e[o]:\"function\"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?i:null}},function(e,t,r){var n=r(8),s=r(13),o=r(39)(!1),i=r(19)(\"IE_PROTO\");e.exports=function(e,t){var r,a=s(e),c=0,l=[];for(r in a)r!=i&&n(a,r)&&l.push(r);for(;t.length>c;)n(a,r=t[c++])&&(~o(l,r)||l.push(r));return l}},function(e,t,r){var n=r(53),s=r(22);e.exports=Object.keys||function(e){return n(e,s)}},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,r){var n=r(5);e.exports=function(e,t,r){for(var s in t)r&&e[s]?e[s]=t[s]:n(e,s,t[s]);return e}},function(e,t,r){e.exports=r(5)},function(e,t,r){var n=r(9),s=r(3),o=function(e,t){if(s(e),!n(t)&&null!==t)throw TypeError(t+\": can't set as prototype!\")};e.exports={set:Object.setPrototypeOf||(\"__proto__\"in{}?function(e,t,n){try{(n=r(7)(Function.call,r(51).f(Object.prototype,\"__proto__\").set,2))(e,[]),t=!(e instanceof Array)}catch(e){t=!0}return function(e,r){return o(e,r),t?e.__proto__=r:n(e,r),e}}({},!1):void 0),check:o}},function(e,t,r){\"use strict\";var n=r(2),s=r(6),o=r(12),i=r(4),a=r(1)(\"species\");e.exports=function(e){var t=\"function\"==typeof s[e]?s[e]:n[e];i&&t&&!t[a]&&o.f(t,a,{configurable:!0,get:function(){return this}})}},function(e,t,r){var n=r(3),s=r(14),o=r(1)(\"species\");e.exports=function(e,t){var r,i=n(e).constructor;return void 0===i||null==(r=n(i)[o])?t:s(r)}},function(e,t,r){var n=r(20),s=r(15);e.exports=function(e){return function(t,r){var o,i,a=String(s(t)),c=n(r),l=a.length;return c<0||c>=l?e?\"\":void 0:(o=a.charCodeAt(c))<55296||o>56319||c+1===l||(i=a.charCodeAt(c+1))<56320||i>57343?e?a.charAt(c):o:e?a.slice(c,c+2):i-56320+(o-55296<<10)+65536}}},function(e,t,r){var n=r(20),s=Math.max,o=Math.min;e.exports=function(e,t){return(e=n(e))<0?s(e+t,0):o(e,t)}},function(e,t,r){var n=r(15);e.exports=function(e){return Object(n(e))}},function(e,t,r){var n=r(21),s=r(1)(\"iterator\"),o=r(10);e.exports=r(6).getIteratorMethod=function(e){if(null!=e)return e[s]||e[\"@@iterator\"]||o[n(e)]}},function(e,t,r){\"use strict\";var n=r(37),s=r(47),o=r(10),i=r(13);e.exports=r(27)(Array,\"Array\",(function(e,t){this._t=i(e),this._i=0,this._k=t}),(function(){var e=this._t,t=this._k,r=this._i++;return!e||r>=e.length?(this._t=void 0,s(1)):s(0,\"keys\"==t?r:\"values\"==t?e[r]:[r,e[r]])}),\"values\"),o.Arguments=o.Array,n(\"keys\"),n(\"values\"),n(\"entries\")},function(e,t){},function(e,t,r){\"use strict\";var n,s,o,i=r(28),a=r(2),c=r(7),l=r(21),u=r(23),d=r(9),f=(r(3),r(14)),h=r(38),p=r(40),m=(r(58).set,r(60)),y=r(30).set,g=r(48)(),v=\"Promise\",w=a.TypeError,E=a.process,$=a[v],_=\"process\"==l(E=a.process),b=function(){},S=!!function(){try{var e=$.resolve(1),t=(e.constructor={})[r(1)(\"species\")]=function(e){e(b,b)};return(_||\"function\"==typeof PromiseRejectionEvent)&&e.then(b)instanceof t}catch(e){}}(),O=function(e,t){return e===t||e===$&&t===o},P=function(e){var t;return!(!d(e)||\"function\"!=typeof(t=e.then))&&t},x=function(e){return O($,e)?new N(e):new s(e)},N=s=function(e){var t,r;this.promise=new e((function(e,n){if(void 0!==t||void 0!==r)throw w(\"Bad Promise constructor\");t=e,r=n})),this.resolve=f(t),this.reject=f(r)},I=function(e){try{e()}catch(e){return{error:e}}},j=function(e,t){if(!e._n){e._n=!0;var r=e._c;g((function(){for(var n=e._v,s=1==e._s,o=0,i=function(t){var r,o,i=s?t.ok:t.fail,a=t.resolve,c=t.reject,l=t.domain;try{i?(s||(2==e._h&&T(e),e._h=1),!0===i?r=n:(l&&l.enter(),r=i(n),l&&l.exit()),r===t.promise?c(w(\"Promise-chain cycle\")):(o=P(r))?o.call(r,a,c):a(r)):c(n)}catch(e){c(e)}};r.length>o;)i(r[o++]);e._c=[],e._n=!1,t&&!e._h&&R(e)}))}},R=function(e){y.call(a,(function(){var t,r,n,s=e._v;if(A(e)&&(t=I((function(){_?E.emit(\"unhandledRejection\",s,e):(r=a.onunhandledrejection)?r({promise:e,reason:s}):(n=a.console)&&n.error&&n.error(\"Unhandled promise rejection\",s)})),e._h=_||A(e)?2:1),e._a=void 0,t)throw t.error}))},A=function(e){if(1==e._h)return!1;for(var t,r=e._a||e._c,n=0;r.length>n;)if((t=r[n++]).fail||!A(t.promise))return!1;return!0},T=function(e){y.call(a,(function(){var t;_?E.emit(\"rejectionHandled\",e):(t=a.onrejectionhandled)&&t({promise:e,reason:e._v})}))},C=function(e){var t=this;t._d||(t._d=!0,(t=t._w||t)._v=e,t._s=2,t._a||(t._a=t._c.slice()),j(t,!0))},D=function(e){var t,r=this;if(!r._d){r._d=!0,r=r._w||r;try{if(r===e)throw w(\"Promise can't be resolved itself\");(t=P(e))?g((function(){var n={_w:r,_d:!1};try{t.call(e,c(D,n,1),c(C,n,1))}catch(e){C.call(n,e)}})):(r._v=e,r._s=1,j(r,!1))}catch(e){C.call({_w:r,_d:!1},e)}}};S||($=function(e){h(this,$,v,\"_h\"),f(e),n.call(this);try{e(c(D,this,1),c(C,this,1))}catch(e){C.call(this,e)}},(n=function(e){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1}).prototype=r(56)($.prototype,{then:function(e,t){var r=x(m(this,$));return r.ok=\"function\"!=typeof e||e,r.fail=\"function\"==typeof t&&t,r.domain=_?E.domain:void 0,this._c.push(r),this._a&&this._a.push(r),this._s&&j(this,!1),r.promise},catch:function(e){return this.then(void 0,e)}}),N=function(){var e=new n;this.promise=e,this.resolve=c(D,e,1),this.reject=c(C,e,1)}),u(u.G+u.W+u.F*!S,{Promise:$}),r(18)($,v),r(59)(v),o=r(6)[v],u(u.S+u.F*!S,v,{reject:function(e){var t=x(this);return(0,t.reject)(e),t.promise}}),u(u.S+u.F*(i||!S),v,{resolve:function(e){if(e instanceof $&&O(e.constructor,this))return e;var t=x(this);return(0,t.resolve)(e),t.promise}}),u(u.S+u.F*!(S&&r(46)((function(e){$.all(e).catch(b)}))),v,{all:function(e){var t=this,r=x(t),n=r.resolve,s=r.reject,o=I((function(){var r=[],o=0,i=1;p(e,!1,(function(e){var a=o++,c=!1;r.push(void 0),i++,t.resolve(e).then((function(e){c||(c=!0,r[a]=e,--i||n(r))}),s)})),--i||n(r)}));return o&&s(o.error),r.promise},race:function(e){var t=this,r=x(t),n=r.reject,s=I((function(){p(e,!1,(function(e){t.resolve(e).then(r.resolve,n)}))}));return s&&n(s.error),r.promise}})},function(e,t,r){\"use strict\";var n=r(61)(!0);r(27)(String,\"String\",(function(e){this._t=String(e),this._i=0}),(function(){var e,t=this._t,r=this._i;return r>=t.length?{value:void 0,done:!0}:(e=n(t,r),this._i+=e.length,{value:e,done:!1})}))},function(e,t,r){r(65);for(var n=r(2),s=r(5),o=r(10),i=r(1)(\"toStringTag\"),a=[\"NodeList\",\"DOMTokenList\",\"MediaList\",\"StyleSheetList\",\"CSSRuleList\"],c=0;c<5;c++){var l=a[c],u=n[l],d=u&&u.prototype;d&&!d[i]&&s(d,i,l),o[l]=o.Array}},function(e,t){e.exports=r(2081)},function(e,t){e.exports=r(6113)}]))},7678:(e,t,r)=>{\"use strict\";const n=r(4341),s=new WeakMap,o=(e,t={})=>{if(\"function\"!=typeof e)throw new TypeError(\"Expected a function\");let r,o=0;const i=e.displayName||e.name||\"<anonymous>\",a=function(...n){if(s.set(a,++o),1===o)r=e.apply(this,n),e=null;else if(!0===t.throw)throw new Error(`Function \\`${i}\\` can only be called once`);return r};return n(a,e),s.set(a,o),a};e.exports=o,e.exports.default=o,e.exports.callCount=e=>{if(!s.has(e))throw new Error(`The given function \\`${e.name}\\` is not wrapped by the \\`onetime\\` package`);return s.get(e)}},406:(e,t,r)=>{\"use strict\";const n=r(9161),s=e=>{if(!Number.isInteger(e)&&e!==1/0||!(e>0))return Promise.reject(new TypeError(\"Expected `concurrency` to be a number from 1 and up\"));const t=[];let r=0;const s=()=>{r--,t.length>0&&t.shift()()},o=(e,t,...o)=>{r++;const i=n(e,...o);t(i),i.then(s,s)},i=(n,...s)=>new Promise((i=>((n,s,...i)=>{r<e?o(n,s,...i):t.push(o.bind(null,n,s,...i))})(n,i,...s)));return Object.defineProperties(i,{activeCount:{get:()=>r},pendingCount:{get:()=>t.length},clearQueue:{value:()=>{t.length=0}}}),i};e.exports=s,e.exports.default=s},1885:(e,t,r)=>{\"use strict\";const n=r(406);class s extends Error{constructor(e){super(),this.value=e}}const o=(e,t)=>Promise.resolve(e).then(t),i=e=>Promise.all(e).then((e=>!0===e[1]&&Promise.reject(new s(e[0]))));e.exports=(e,t,r)=>{r=Object.assign({concurrency:1/0,preserveOrder:!0},r);const a=n(r.concurrency),c=[...e].map((e=>[e,a(o,e,t)])),l=n(r.preserveOrder?1:1/0);return Promise.all(c.map((e=>l(i,e)))).then((()=>{})).catch((e=>e instanceof s?e.value:Promise.reject(e)))}},9161:e=>{\"use strict\";const t=(e,...t)=>new Promise((r=>{r(e(...t))}));e.exports=t,e.exports.default=t},8866:(e,t,r)=>{\"use strict\";const n=r(9516);e.exports=async({cwd:e}={})=>n(\"package.json\",{cwd:e}),e.exports.sync=({cwd:e}={})=>n.sync(\"package.json\",{cwd:e})},2257:(e,t,r)=>{const n=Symbol(\"SemVer ANY\");class s{static get ANY(){return n}constructor(e,t){if(t=o(t),e instanceof s){if(e.loose===!!t.loose)return e;e=e.value}e=e.trim().split(/\\s+/).join(\" \"),l(\"comparator\",e,t),this.options=t,this.loose=!!t.loose,this.parse(e),this.semver===n?this.value=\"\":this.value=this.operator+this.semver.version,l(\"comp\",this)}parse(e){const t=this.options.loose?i[a.COMPARATORLOOSE]:i[a.COMPARATOR],r=e.match(t);if(!r)throw new TypeError(`Invalid comparator: ${e}`);this.operator=void 0!==r[1]?r[1]:\"\",\"=\"===this.operator&&(this.operator=\"\"),r[2]?this.semver=new u(r[2],this.options.loose):this.semver=n}toString(){return this.value}test(e){if(l(\"Comparator.test\",e,this.options.loose),this.semver===n||e===n)return!0;if(\"string\"==typeof e)try{e=new u(e,this.options)}catch(e){return!1}return c(e,this.operator,this.semver,this.options)}intersects(e,t){if(!(e instanceof s))throw new TypeError(\"a Comparator is required\");return\"\"===this.operator?\"\"===this.value||new d(e.value,t).test(this.value):\"\"===e.operator?\"\"===e.value||new d(this.value,t).test(e.semver):!((t=o(t)).includePrerelease&&(\"<0.0.0-0\"===this.value||\"<0.0.0-0\"===e.value)||!t.includePrerelease&&(this.value.startsWith(\"<0.0.0\")||e.value.startsWith(\"<0.0.0\"))||(!this.operator.startsWith(\">\")||!e.operator.startsWith(\">\"))&&(!this.operator.startsWith(\"<\")||!e.operator.startsWith(\"<\"))&&(this.semver.version!==e.semver.version||!this.operator.includes(\"=\")||!e.operator.includes(\"=\"))&&!(c(this.semver,\"<\",e.semver,t)&&this.operator.startsWith(\">\")&&e.operator.startsWith(\"<\"))&&!(c(this.semver,\">\",e.semver,t)&&this.operator.startsWith(\"<\")&&e.operator.startsWith(\">\")))}}e.exports=s;const o=r(2893),{safeRe:i,t:a}=r(5765),c=r(7539),l=r(4225),u=r(6376),d=r(6902)},6902:(e,t,r)=>{class n{constructor(e,t){if(t=o(t),e instanceof n)return e.loose===!!t.loose&&e.includePrerelease===!!t.includePrerelease?e:new n(e.raw,t);if(e instanceof i)return this.raw=e.value,this.set=[[e]],this.format(),this;if(this.options=t,this.loose=!!t.loose,this.includePrerelease=!!t.includePrerelease,this.raw=e.trim().split(/\\s+/).join(\" \"),this.set=this.raw.split(\"||\").map((e=>this.parseRange(e.trim()))).filter((e=>e.length)),!this.set.length)throw new TypeError(`Invalid SemVer Range: ${this.raw}`);if(this.set.length>1){const e=this.set[0];if(this.set=this.set.filter((e=>!y(e[0]))),0===this.set.length)this.set=[e];else if(this.set.length>1)for(const e of this.set)if(1===e.length&&g(e[0])){this.set=[e];break}}this.format()}format(){return this.range=this.set.map((e=>e.join(\" \").trim())).join(\"||\").trim(),this.range}toString(){return this.range}parseRange(e){const t=((this.options.includePrerelease&&p)|(this.options.loose&&m))+\":\"+e,r=s.get(t);if(r)return r;const n=this.options.loose,o=n?l[u.HYPHENRANGELOOSE]:l[u.HYPHENRANGE];e=e.replace(o,I(this.options.includePrerelease)),a(\"hyphen replace\",e),e=e.replace(l[u.COMPARATORTRIM],d),a(\"comparator trim\",e),e=e.replace(l[u.TILDETRIM],f),a(\"tilde trim\",e),e=e.replace(l[u.CARETTRIM],h),a(\"caret trim\",e);let c=e.split(\" \").map((e=>w(e,this.options))).join(\" \").split(/\\s+/).map((e=>N(e,this.options)));n&&(c=c.filter((e=>(a(\"loose invalid filter\",e,this.options),!!e.match(l[u.COMPARATORLOOSE]))))),a(\"range list\",c);const g=new Map,v=c.map((e=>new i(e,this.options)));for(const e of v){if(y(e))return[e];g.set(e.value,e)}g.size>1&&g.has(\"\")&&g.delete(\"\");const E=[...g.values()];return s.set(t,E),E}intersects(e,t){if(!(e instanceof n))throw new TypeError(\"a Range is required\");return this.set.some((r=>v(r,t)&&e.set.some((e=>v(e,t)&&r.every((r=>e.every((e=>r.intersects(e,t)))))))))}test(e){if(!e)return!1;if(\"string\"==typeof e)try{e=new c(e,this.options)}catch(e){return!1}for(let t=0;t<this.set.length;t++)if(j(this.set[t],e,this.options))return!0;return!1}}e.exports=n;const s=new(r(9593))({max:1e3}),o=r(2893),i=r(2257),a=r(4225),c=r(6376),{safeRe:l,t:u,comparatorTrimReplace:d,tildeTrimReplace:f,caretTrimReplace:h}=r(5765),{FLAG_INCLUDE_PRERELEASE:p,FLAG_LOOSE:m}=r(3295),y=e=>\"<0.0.0-0\"===e.value,g=e=>\"\"===e.value,v=(e,t)=>{let r=!0;const n=e.slice();let s=n.pop();for(;r&&n.length;)r=n.every((e=>s.intersects(e,t))),s=n.pop();return r},w=(e,t)=>(a(\"comp\",e,t),e=b(e,t),a(\"caret\",e),e=$(e,t),a(\"tildes\",e),e=O(e,t),a(\"xrange\",e),e=x(e,t),a(\"stars\",e),e),E=e=>!e||\"x\"===e.toLowerCase()||\"*\"===e,$=(e,t)=>e.trim().split(/\\s+/).map((e=>_(e,t))).join(\" \"),_=(e,t)=>{const r=t.loose?l[u.TILDELOOSE]:l[u.TILDE];return e.replace(r,((t,r,n,s,o)=>{let i;return a(\"tilde\",e,t,r,n,s,o),E(r)?i=\"\":E(n)?i=`>=${r}.0.0 <${+r+1}.0.0-0`:E(s)?i=`>=${r}.${n}.0 <${r}.${+n+1}.0-0`:o?(a(\"replaceTilde pr\",o),i=`>=${r}.${n}.${s}-${o} <${r}.${+n+1}.0-0`):i=`>=${r}.${n}.${s} <${r}.${+n+1}.0-0`,a(\"tilde return\",i),i}))},b=(e,t)=>e.trim().split(/\\s+/).map((e=>S(e,t))).join(\" \"),S=(e,t)=>{a(\"caret\",e,t);const r=t.loose?l[u.CARETLOOSE]:l[u.CARET],n=t.includePrerelease?\"-0\":\"\";return e.replace(r,((t,r,s,o,i)=>{let c;return a(\"caret\",e,t,r,s,o,i),E(r)?c=\"\":E(s)?c=`>=${r}.0.0${n} <${+r+1}.0.0-0`:E(o)?c=\"0\"===r?`>=${r}.${s}.0${n} <${r}.${+s+1}.0-0`:`>=${r}.${s}.0${n} <${+r+1}.0.0-0`:i?(a(\"replaceCaret pr\",i),c=\"0\"===r?\"0\"===s?`>=${r}.${s}.${o}-${i} <${r}.${s}.${+o+1}-0`:`>=${r}.${s}.${o}-${i} <${r}.${+s+1}.0-0`:`>=${r}.${s}.${o}-${i} <${+r+1}.0.0-0`):(a(\"no pr\"),c=\"0\"===r?\"0\"===s?`>=${r}.${s}.${o}${n} <${r}.${s}.${+o+1}-0`:`>=${r}.${s}.${o}${n} <${r}.${+s+1}.0-0`:`>=${r}.${s}.${o} <${+r+1}.0.0-0`),a(\"caret return\",c),c}))},O=(e,t)=>(a(\"replaceXRanges\",e,t),e.split(/\\s+/).map((e=>P(e,t))).join(\" \")),P=(e,t)=>{e=e.trim();const r=t.loose?l[u.XRANGELOOSE]:l[u.XRANGE];return e.replace(r,((r,n,s,o,i,c)=>{a(\"xRange\",e,r,n,s,o,i,c);const l=E(s),u=l||E(o),d=u||E(i),f=d;return\"=\"===n&&f&&(n=\"\"),c=t.includePrerelease?\"-0\":\"\",l?r=\">\"===n||\"<\"===n?\"<0.0.0-0\":\"*\":n&&f?(u&&(o=0),i=0,\">\"===n?(n=\">=\",u?(s=+s+1,o=0,i=0):(o=+o+1,i=0)):\"<=\"===n&&(n=\"<\",u?s=+s+1:o=+o+1),\"<\"===n&&(c=\"-0\"),r=`${n+s}.${o}.${i}${c}`):u?r=`>=${s}.0.0${c} <${+s+1}.0.0-0`:d&&(r=`>=${s}.${o}.0${c} <${s}.${+o+1}.0-0`),a(\"xRange return\",r),r}))},x=(e,t)=>(a(\"replaceStars\",e,t),e.trim().replace(l[u.STAR],\"\")),N=(e,t)=>(a(\"replaceGTE0\",e,t),e.trim().replace(l[t.includePrerelease?u.GTE0PRE:u.GTE0],\"\")),I=e=>(t,r,n,s,o,i,a,c,l,u,d,f,h)=>`${r=E(n)?\"\":E(s)?`>=${n}.0.0${e?\"-0\":\"\"}`:E(o)?`>=${n}.${s}.0${e?\"-0\":\"\"}`:i?`>=${r}`:`>=${r}${e?\"-0\":\"\"}`} ${c=E(l)?\"\":E(u)?`<${+l+1}.0.0-0`:E(d)?`<${l}.${+u+1}.0-0`:f?`<=${l}.${u}.${d}-${f}`:e?`<${l}.${u}.${+d+1}-0`:`<=${c}`}`.trim(),j=(e,t,r)=>{for(let r=0;r<e.length;r++)if(!e[r].test(t))return!1;if(t.prerelease.length&&!r.includePrerelease){for(let r=0;r<e.length;r++)if(a(e[r].semver),e[r].semver!==i.ANY&&e[r].semver.prerelease.length>0){const n=e[r].semver;if(n.major===t.major&&n.minor===t.minor&&n.patch===t.patch)return!0}return!1}return!0}},6376:(e,t,r)=>{const n=r(4225),{MAX_LENGTH:s,MAX_SAFE_INTEGER:o}=r(3295),{safeRe:i,t:a}=r(5765),c=r(2893),{compareIdentifiers:l}=r(6742);class u{constructor(e,t){if(t=c(t),e instanceof u){if(e.loose===!!t.loose&&e.includePrerelease===!!t.includePrerelease)return e;e=e.version}else if(\"string\"!=typeof e)throw new TypeError(`Invalid version. Must be a string. Got type \"${typeof e}\".`);if(e.length>s)throw new TypeError(`version is longer than ${s} characters`);n(\"SemVer\",e,t),this.options=t,this.loose=!!t.loose,this.includePrerelease=!!t.includePrerelease;const r=e.trim().match(t.loose?i[a.LOOSE]:i[a.FULL]);if(!r)throw new TypeError(`Invalid Version: ${e}`);if(this.raw=e,this.major=+r[1],this.minor=+r[2],this.patch=+r[3],this.major>o||this.major<0)throw new TypeError(\"Invalid major version\");if(this.minor>o||this.minor<0)throw new TypeError(\"Invalid minor version\");if(this.patch>o||this.patch<0)throw new TypeError(\"Invalid patch version\");r[4]?this.prerelease=r[4].split(\".\").map((e=>{if(/^[0-9]+$/.test(e)){const t=+e;if(t>=0&&t<o)return t}return e})):this.prerelease=[],this.build=r[5]?r[5].split(\".\"):[],this.format()}format(){return this.version=`${this.major}.${this.minor}.${this.patch}`,this.prerelease.length&&(this.version+=`-${this.prerelease.join(\".\")}`),this.version}toString(){return this.version}compare(e){if(n(\"SemVer.compare\",this.version,this.options,e),!(e instanceof u)){if(\"string\"==typeof e&&e===this.version)return 0;e=new u(e,this.options)}return e.version===this.version?0:this.compareMain(e)||this.comparePre(e)}compareMain(e){return e instanceof u||(e=new u(e,this.options)),l(this.major,e.major)||l(this.minor,e.minor)||l(this.patch,e.patch)}comparePre(e){if(e instanceof u||(e=new u(e,this.options)),this.prerelease.length&&!e.prerelease.length)return-1;if(!this.prerelease.length&&e.prerelease.length)return 1;if(!this.prerelease.length&&!e.prerelease.length)return 0;let t=0;do{const r=this.prerelease[t],s=e.prerelease[t];if(n(\"prerelease compare\",t,r,s),void 0===r&&void 0===s)return 0;if(void 0===s)return 1;if(void 0===r)return-1;if(r!==s)return l(r,s)}while(++t)}compareBuild(e){e instanceof u||(e=new u(e,this.options));let t=0;do{const r=this.build[t],s=e.build[t];if(n(\"prerelease compare\",t,r,s),void 0===r&&void 0===s)return 0;if(void 0===s)return 1;if(void 0===r)return-1;if(r!==s)return l(r,s)}while(++t)}inc(e,t,r){switch(e){case\"premajor\":this.prerelease.length=0,this.patch=0,this.minor=0,this.major++,this.inc(\"pre\",t,r);break;case\"preminor\":this.prerelease.length=0,this.patch=0,this.minor++,this.inc(\"pre\",t,r);break;case\"prepatch\":this.prerelease.length=0,this.inc(\"patch\",t,r),this.inc(\"pre\",t,r);break;case\"prerelease\":0===this.prerelease.length&&this.inc(\"patch\",t,r),this.inc(\"pre\",t,r);break;case\"major\":0===this.minor&&0===this.patch&&0!==this.prerelease.length||this.major++,this.minor=0,this.patch=0,this.prerelease=[];break;case\"minor\":0===this.patch&&0!==this.prerelease.length||this.minor++,this.patch=0,this.prerelease=[];break;case\"patch\":0===this.prerelease.length&&this.patch++,this.prerelease=[];break;case\"pre\":{const e=Number(r)?1:0;if(!t&&!1===r)throw new Error(\"invalid increment argument: identifier is empty\");if(0===this.prerelease.length)this.prerelease=[e];else{let n=this.prerelease.length;for(;--n>=0;)\"number\"==typeof this.prerelease[n]&&(this.prerelease[n]++,n=-2);if(-1===n){if(t===this.prerelease.join(\".\")&&!1===r)throw new Error(\"invalid increment argument: identifier already exists\");this.prerelease.push(e)}}if(t){let n=[t,e];!1===r&&(n=[t]),0===l(this.prerelease[0],t)?isNaN(this.prerelease[1])&&(this.prerelease=n):this.prerelease=n}break}default:throw new Error(`invalid increment argument: ${e}`)}return this.raw=this.format(),this.build.length&&(this.raw+=`+${this.build.join(\".\")}`),this}}e.exports=u},3507:(e,t,r)=>{const n=r(3959);e.exports=(e,t)=>{const r=n(e.trim().replace(/^[=v]+/,\"\"),t);return r?r.version:null}},7539:(e,t,r)=>{const n=r(8718),s=r(1194),o=r(1312),i=r(5903),a=r(1544),c=r(2056);e.exports=(e,t,r,l)=>{switch(t){case\"===\":return\"object\"==typeof e&&(e=e.version),\"object\"==typeof r&&(r=r.version),e===r;case\"!==\":return\"object\"==typeof e&&(e=e.version),\"object\"==typeof r&&(r=r.version),e!==r;case\"\":case\"=\":case\"==\":return n(e,r,l);case\"!=\":return s(e,r,l);case\">\":return o(e,r,l);case\">=\":return i(e,r,l);case\"<\":return a(e,r,l);case\"<=\":return c(e,r,l);default:throw new TypeError(`Invalid operator: ${t}`)}}},9038:(e,t,r)=>{const n=r(6376),s=r(3959),{safeRe:o,t:i}=r(5765);e.exports=(e,t)=>{if(e instanceof n)return e;if(\"number\"==typeof e&&(e=String(e)),\"string\"!=typeof e)return null;let r=null;if((t=t||{}).rtl){let t;for(;(t=o[i.COERCERTL].exec(e))&&(!r||r.index+r[0].length!==e.length);)r&&t.index+t[0].length===r.index+r[0].length||(r=t),o[i.COERCERTL].lastIndex=t.index+t[1].length+t[2].length;o[i.COERCERTL].lastIndex=-1}else r=e.match(o[i.COERCE]);return null===r?null:s(`${r[2]}.${r[3]||\"0\"}.${r[4]||\"0\"}`,t)}},8880:(e,t,r)=>{const n=r(6376);e.exports=(e,t,r)=>{const s=new n(e,r),o=new n(t,r);return s.compare(o)||s.compareBuild(o)}},7880:(e,t,r)=>{const n=r(6269);e.exports=(e,t)=>n(e,t,!0)},6269:(e,t,r)=>{const n=r(6376);e.exports=(e,t,r)=>new n(e,r).compare(new n(t,r))},2378:(e,t,r)=>{const n=r(3959);e.exports=(e,t)=>{const r=n(e,null,!0),s=n(t,null,!0),o=r.compare(s);if(0===o)return null;const i=o>0,a=i?r:s,c=i?s:r,l=!!a.prerelease.length;if(c.prerelease.length&&!l)return c.patch||c.minor?a.patch?\"patch\":a.minor?\"minor\":\"major\":\"major\";const u=l?\"pre\":\"\";return r.major!==s.major?u+\"major\":r.minor!==s.minor?u+\"minor\":r.patch!==s.patch?u+\"patch\":\"prerelease\"}},8718:(e,t,r)=>{const n=r(6269);e.exports=(e,t,r)=>0===n(e,t,r)},1312:(e,t,r)=>{const n=r(6269);e.exports=(e,t,r)=>n(e,t,r)>0},5903:(e,t,r)=>{const n=r(6269);e.exports=(e,t,r)=>n(e,t,r)>=0},253:(e,t,r)=>{const n=r(6376);e.exports=(e,t,r,s,o)=>{\"string\"==typeof r&&(o=s,s=r,r=void 0);try{return new n(e instanceof n?e.version:e,r).inc(t,s,o).version}catch(e){return null}}},1544:(e,t,r)=>{const n=r(6269);e.exports=(e,t,r)=>n(e,t,r)<0},2056:(e,t,r)=>{const n=r(6269);e.exports=(e,t,r)=>n(e,t,r)<=0},8679:(e,t,r)=>{const n=r(6376);e.exports=(e,t)=>new n(e,t).major},7789:(e,t,r)=>{const n=r(6376);e.exports=(e,t)=>new n(e,t).minor},1194:(e,t,r)=>{const n=r(6269);e.exports=(e,t,r)=>0!==n(e,t,r)},3959:(e,t,r)=>{const n=r(6376);e.exports=(e,t,r=!1)=>{if(e instanceof n)return e;try{return new n(e,t)}catch(e){if(!r)return null;throw e}}},2358:(e,t,r)=>{const n=r(6376);e.exports=(e,t)=>new n(e,t).patch},7559:(e,t,r)=>{const n=r(3959);e.exports=(e,t)=>{const r=n(e,t);return r&&r.prerelease.length?r.prerelease:null}},9795:(e,t,r)=>{const n=r(6269);e.exports=(e,t,r)=>n(t,e,r)},3657:(e,t,r)=>{const n=r(8880);e.exports=(e,t)=>e.sort(((e,r)=>n(r,e,t)))},5712:(e,t,r)=>{const n=r(6902);e.exports=(e,t,r)=>{try{t=new n(t,r)}catch(e){return!1}return t.test(e)}},1100:(e,t,r)=>{const n=r(8880);e.exports=(e,t)=>e.sort(((e,r)=>n(e,r,t)))},6397:(e,t,r)=>{const n=r(3959);e.exports=(e,t)=>{const r=n(e,t);return r?r.version:null}},1249:(e,t,r)=>{const n=r(5765),s=r(3295),o=r(6376),i=r(6742),a=r(3959),c=r(6397),l=r(3507),u=r(253),d=r(2378),f=r(8679),h=r(7789),p=r(2358),m=r(7559),y=r(6269),g=r(9795),v=r(7880),w=r(8880),E=r(1100),$=r(3657),_=r(1312),b=r(1544),S=r(8718),O=r(1194),P=r(5903),x=r(2056),N=r(7539),I=r(9038),j=r(2257),R=r(6902),A=r(5712),T=r(1042),C=r(5775),D=r(1657),k=r(8829),L=r(9042),M=r(6826),F=r(7606),U=r(32),z=r(2937),V=r(7908),G=r(799);e.exports={parse:a,valid:c,clean:l,inc:u,diff:d,major:f,minor:h,patch:p,prerelease:m,compare:y,rcompare:g,compareLoose:v,compareBuild:w,sort:E,rsort:$,gt:_,lt:b,eq:S,neq:O,gte:P,lte:x,cmp:N,coerce:I,Comparator:j,Range:R,satisfies:A,toComparators:T,maxSatisfying:C,minSatisfying:D,minVersion:k,validRange:L,outside:M,gtr:F,ltr:U,intersects:z,simplifyRange:V,subset:G,SemVer:o,re:n.re,src:n.src,tokens:n.t,SEMVER_SPEC_VERSION:s.SEMVER_SPEC_VERSION,RELEASE_TYPES:s.RELEASE_TYPES,compareIdentifiers:i.compareIdentifiers,rcompareIdentifiers:i.rcompareIdentifiers}},3295:e=>{const t=Number.MAX_SAFE_INTEGER||9007199254740991;e.exports={MAX_LENGTH:256,MAX_SAFE_COMPONENT_LENGTH:16,MAX_SAFE_BUILD_LENGTH:250,MAX_SAFE_INTEGER:t,RELEASE_TYPES:[\"major\",\"premajor\",\"minor\",\"preminor\",\"patch\",\"prepatch\",\"prerelease\"],SEMVER_SPEC_VERSION:\"2.0.0\",FLAG_INCLUDE_PRERELEASE:1,FLAG_LOOSE:2}},4225:e=>{const t=\"object\"==typeof process&&process.env&&process.env.NODE_DEBUG&&/\\bsemver\\b/i.test(process.env.NODE_DEBUG)?(...e)=>console.error(\"SEMVER\",...e):()=>{};e.exports=t},6742:e=>{const t=/^[0-9]+$/,r=(e,r)=>{const n=t.test(e),s=t.test(r);return n&&s&&(e=+e,r=+r),e===r?0:n&&!s?-1:s&&!n?1:e<r?-1:1};e.exports={compareIdentifiers:r,rcompareIdentifiers:(e,t)=>r(t,e)}},2893:e=>{const t=Object.freeze({loose:!0}),r=Object.freeze({});e.exports=e=>e?\"object\"!=typeof e?t:e:r},5765:(e,t,r)=>{const{MAX_SAFE_COMPONENT_LENGTH:n,MAX_SAFE_BUILD_LENGTH:s,MAX_LENGTH:o}=r(3295),i=r(4225),a=(t=e.exports={}).re=[],c=t.safeRe=[],l=t.src=[],u=t.t={};let d=0;const f=\"[a-zA-Z0-9-]\",h=[[\"\\\\s\",1],[\"\\\\d\",o],[f,s]],p=(e,t,r)=>{const n=(e=>{for(const[t,r]of h)e=e.split(`${t}*`).join(`${t}{0,${r}}`).split(`${t}+`).join(`${t}{1,${r}}`);return e})(t),s=d++;i(e,s,t),u[e]=s,l[s]=t,a[s]=new RegExp(t,r?\"g\":void 0),c[s]=new RegExp(n,r?\"g\":void 0)};p(\"NUMERICIDENTIFIER\",\"0|[1-9]\\\\d*\"),p(\"NUMERICIDENTIFIERLOOSE\",\"\\\\d+\"),p(\"NONNUMERICIDENTIFIER\",`\\\\d*[a-zA-Z-]${f}*`),p(\"MAINVERSION\",`(${l[u.NUMERICIDENTIFIER]})\\\\.(${l[u.NUMERICIDENTIFIER]})\\\\.(${l[u.NUMERICIDENTIFIER]})`),p(\"MAINVERSIONLOOSE\",`(${l[u.NUMERICIDENTIFIERLOOSE]})\\\\.(${l[u.NUMERICIDENTIFIERLOOSE]})\\\\.(${l[u.NUMERICIDENTIFIERLOOSE]})`),p(\"PRERELEASEIDENTIFIER\",`(?:${l[u.NUMERICIDENTIFIER]}|${l[u.NONNUMERICIDENTIFIER]})`),p(\"PRERELEASEIDENTIFIERLOOSE\",`(?:${l[u.NUMERICIDENTIFIERLOOSE]}|${l[u.NONNUMERICIDENTIFIER]})`),p(\"PRERELEASE\",`(?:-(${l[u.PRERELEASEIDENTIFIER]}(?:\\\\.${l[u.PRERELEASEIDENTIFIER]})*))`),p(\"PRERELEASELOOSE\",`(?:-?(${l[u.PRERELEASEIDENTIFIERLOOSE]}(?:\\\\.${l[u.PRERELEASEIDENTIFIERLOOSE]})*))`),p(\"BUILDIDENTIFIER\",`${f}+`),p(\"BUILD\",`(?:\\\\+(${l[u.BUILDIDENTIFIER]}(?:\\\\.${l[u.BUILDIDENTIFIER]})*))`),p(\"FULLPLAIN\",`v?${l[u.MAINVERSION]}${l[u.PRERELEASE]}?${l[u.BUILD]}?`),p(\"FULL\",`^${l[u.FULLPLAIN]}$`),p(\"LOOSEPLAIN\",`[v=\\\\s]*${l[u.MAINVERSIONLOOSE]}${l[u.PRERELEASELOOSE]}?${l[u.BUILD]}?`),p(\"LOOSE\",`^${l[u.LOOSEPLAIN]}$`),p(\"GTLT\",\"((?:<|>)?=?)\"),p(\"XRANGEIDENTIFIERLOOSE\",`${l[u.NUMERICIDENTIFIERLOOSE]}|x|X|\\\\*`),p(\"XRANGEIDENTIFIER\",`${l[u.NUMERICIDENTIFIER]}|x|X|\\\\*`),p(\"XRANGEPLAIN\",`[v=\\\\s]*(${l[u.XRANGEIDENTIFIER]})(?:\\\\.(${l[u.XRANGEIDENTIFIER]})(?:\\\\.(${l[u.XRANGEIDENTIFIER]})(?:${l[u.PRERELEASE]})?${l[u.BUILD]}?)?)?`),p(\"XRANGEPLAINLOOSE\",`[v=\\\\s]*(${l[u.XRANGEIDENTIFIERLOOSE]})(?:\\\\.(${l[u.XRANGEIDENTIFIERLOOSE]})(?:\\\\.(${l[u.XRANGEIDENTIFIERLOOSE]})(?:${l[u.PRERELEASELOOSE]})?${l[u.BUILD]}?)?)?`),p(\"XRANGE\",`^${l[u.GTLT]}\\\\s*${l[u.XRANGEPLAIN]}$`),p(\"XRANGELOOSE\",`^${l[u.GTLT]}\\\\s*${l[u.XRANGEPLAINLOOSE]}$`),p(\"COERCE\",`(^|[^\\\\d])(\\\\d{1,${n}})(?:\\\\.(\\\\d{1,${n}}))?(?:\\\\.(\\\\d{1,${n}}))?(?:$|[^\\\\d])`),p(\"COERCERTL\",l[u.COERCE],!0),p(\"LONETILDE\",\"(?:~>?)\"),p(\"TILDETRIM\",`(\\\\s*)${l[u.LONETILDE]}\\\\s+`,!0),t.tildeTrimReplace=\"$1~\",p(\"TILDE\",`^${l[u.LONETILDE]}${l[u.XRANGEPLAIN]}$`),p(\"TILDELOOSE\",`^${l[u.LONETILDE]}${l[u.XRANGEPLAINLOOSE]}$`),p(\"LONECARET\",\"(?:\\\\^)\"),p(\"CARETTRIM\",`(\\\\s*)${l[u.LONECARET]}\\\\s+`,!0),t.caretTrimReplace=\"$1^\",p(\"CARET\",`^${l[u.LONECARET]}${l[u.XRANGEPLAIN]}$`),p(\"CARETLOOSE\",`^${l[u.LONECARET]}${l[u.XRANGEPLAINLOOSE]}$`),p(\"COMPARATORLOOSE\",`^${l[u.GTLT]}\\\\s*(${l[u.LOOSEPLAIN]})$|^$`),p(\"COMPARATOR\",`^${l[u.GTLT]}\\\\s*(${l[u.FULLPLAIN]})$|^$`),p(\"COMPARATORTRIM\",`(\\\\s*)${l[u.GTLT]}\\\\s*(${l[u.LOOSEPLAIN]}|${l[u.XRANGEPLAIN]})`,!0),t.comparatorTrimReplace=\"$1$2$3\",p(\"HYPHENRANGE\",`^\\\\s*(${l[u.XRANGEPLAIN]})\\\\s+-\\\\s+(${l[u.XRANGEPLAIN]})\\\\s*$`),p(\"HYPHENRANGELOOSE\",`^\\\\s*(${l[u.XRANGEPLAINLOOSE]})\\\\s+-\\\\s+(${l[u.XRANGEPLAINLOOSE]})\\\\s*$`),p(\"STAR\",\"(<|>)?=?\\\\s*\\\\*\"),p(\"GTE0\",\"^\\\\s*>=\\\\s*0\\\\.0\\\\.0\\\\s*$\"),p(\"GTE0PRE\",\"^\\\\s*>=\\\\s*0\\\\.0\\\\.0-0\\\\s*$\")},7606:(e,t,r)=>{const n=r(6826);e.exports=(e,t,r)=>n(e,t,\">\",r)},2937:(e,t,r)=>{const n=r(6902);e.exports=(e,t,r)=>(e=new n(e,r),t=new n(t,r),e.intersects(t,r))},32:(e,t,r)=>{const n=r(6826);e.exports=(e,t,r)=>n(e,t,\"<\",r)},5775:(e,t,r)=>{const n=r(6376),s=r(6902);e.exports=(e,t,r)=>{let o=null,i=null,a=null;try{a=new s(t,r)}catch(e){return null}return e.forEach((e=>{a.test(e)&&(o&&-1!==i.compare(e)||(o=e,i=new n(o,r)))})),o}},1657:(e,t,r)=>{const n=r(6376),s=r(6902);e.exports=(e,t,r)=>{let o=null,i=null,a=null;try{a=new s(t,r)}catch(e){return null}return e.forEach((e=>{a.test(e)&&(o&&1!==i.compare(e)||(o=e,i=new n(o,r)))})),o}},8829:(e,t,r)=>{const n=r(6376),s=r(6902),o=r(1312);e.exports=(e,t)=>{e=new s(e,t);let r=new n(\"0.0.0\");if(e.test(r))return r;if(r=new n(\"0.0.0-0\"),e.test(r))return r;r=null;for(let t=0;t<e.set.length;++t){const s=e.set[t];let i=null;s.forEach((e=>{const t=new n(e.semver.version);switch(e.operator){case\">\":0===t.prerelease.length?t.patch++:t.prerelease.push(0),t.raw=t.format();case\"\":case\">=\":i&&!o(t,i)||(i=t);break;case\"<\":case\"<=\":break;default:throw new Error(`Unexpected operation: ${e.operator}`)}})),!i||r&&!o(r,i)||(r=i)}return r&&e.test(r)?r:null}},6826:(e,t,r)=>{const n=r(6376),s=r(2257),{ANY:o}=s,i=r(6902),a=r(5712),c=r(1312),l=r(1544),u=r(2056),d=r(5903);e.exports=(e,t,r,f)=>{let h,p,m,y,g;switch(e=new n(e,f),t=new i(t,f),r){case\">\":h=c,p=u,m=l,y=\">\",g=\">=\";break;case\"<\":h=l,p=d,m=c,y=\"<\",g=\"<=\";break;default:throw new TypeError('Must provide a hilo val of \"<\" or \">\"')}if(a(e,t,f))return!1;for(let r=0;r<t.set.length;++r){const n=t.set[r];let i=null,a=null;if(n.forEach((e=>{e.semver===o&&(e=new s(\">=0.0.0\")),i=i||e,a=a||e,h(e.semver,i.semver,f)?i=e:m(e.semver,a.semver,f)&&(a=e)})),i.operator===y||i.operator===g)return!1;if((!a.operator||a.operator===y)&&p(e,a.semver))return!1;if(a.operator===g&&m(e,a.semver))return!1}return!0}},7908:(e,t,r)=>{const n=r(5712),s=r(6269);e.exports=(e,t,r)=>{const o=[];let i=null,a=null;const c=e.sort(((e,t)=>s(e,t,r)));for(const e of c)n(e,t,r)?(a=e,i||(i=e)):(a&&o.push([i,a]),a=null,i=null);i&&o.push([i,null]);const l=[];for(const[e,t]of o)e===t?l.push(e):t||e!==c[0]?t?e===c[0]?l.push(`<=${t}`):l.push(`${e} - ${t}`):l.push(`>=${e}`):l.push(\"*\");const u=l.join(\" || \"),d=\"string\"==typeof t.raw?t.raw:String(t);return u.length<d.length?u:t}},799:(e,t,r)=>{const n=r(6902),s=r(2257),{ANY:o}=s,i=r(5712),a=r(6269),c=[new s(\">=0.0.0-0\")],l=[new s(\">=0.0.0\")],u=(e,t,r)=>{if(e===t)return!0;if(1===e.length&&e[0].semver===o){if(1===t.length&&t[0].semver===o)return!0;e=r.includePrerelease?c:l}if(1===t.length&&t[0].semver===o){if(r.includePrerelease)return!0;t=l}const n=new Set;let s,u,h,p,m,y,g;for(const t of e)\">\"===t.operator||\">=\"===t.operator?s=d(s,t,r):\"<\"===t.operator||\"<=\"===t.operator?u=f(u,t,r):n.add(t.semver);if(n.size>1)return null;if(s&&u){if(h=a(s.semver,u.semver,r),h>0)return null;if(0===h&&(\">=\"!==s.operator||\"<=\"!==u.operator))return null}for(const e of n){if(s&&!i(e,String(s),r))return null;if(u&&!i(e,String(u),r))return null;for(const n of t)if(!i(e,String(n),r))return!1;return!0}let v=!(!u||r.includePrerelease||!u.semver.prerelease.length)&&u.semver,w=!(!s||r.includePrerelease||!s.semver.prerelease.length)&&s.semver;v&&1===v.prerelease.length&&\"<\"===u.operator&&0===v.prerelease[0]&&(v=!1);for(const e of t){if(g=g||\">\"===e.operator||\">=\"===e.operator,y=y||\"<\"===e.operator||\"<=\"===e.operator,s)if(w&&e.semver.prerelease&&e.semver.prerelease.length&&e.semver.major===w.major&&e.semver.minor===w.minor&&e.semver.patch===w.patch&&(w=!1),\">\"===e.operator||\">=\"===e.operator){if(p=d(s,e,r),p===e&&p!==s)return!1}else if(\">=\"===s.operator&&!i(s.semver,String(e),r))return!1;if(u)if(v&&e.semver.prerelease&&e.semver.prerelease.length&&e.semver.major===v.major&&e.semver.minor===v.minor&&e.semver.patch===v.patch&&(v=!1),\"<\"===e.operator||\"<=\"===e.operator){if(m=f(u,e,r),m===e&&m!==u)return!1}else if(\"<=\"===u.operator&&!i(u.semver,String(e),r))return!1;if(!e.operator&&(u||s)&&0!==h)return!1}return!(s&&y&&!u&&0!==h||u&&g&&!s&&0!==h||w||v)},d=(e,t,r)=>{if(!e)return t;const n=a(e.semver,t.semver,r);return n>0?e:n<0||\">\"===t.operator&&\">=\"===e.operator?t:e},f=(e,t,r)=>{if(!e)return t;const n=a(e.semver,t.semver,r);return n<0?e:n>0||\"<\"===t.operator&&\"<=\"===e.operator?t:e};e.exports=(e,t,r={})=>{if(e===t)return!0;e=new n(e,r),t=new n(t,r);let s=!1;e:for(const n of e.set){for(const e of t.set){const t=u(n,e,r);if(s=s||null!==t,t)continue e}if(s)return!1}return!0}},1042:(e,t,r)=>{const n=r(6902);e.exports=(e,t)=>new n(e,t).set.map((e=>e.map((e=>e.value)).join(\" \").trim().split(\" \")))},9042:(e,t,r)=>{const n=r(6902);e.exports=(e,t)=>{try{return new n(e,t).range||\"*\"}catch(e){return null}}},540:function(e,t){!function(e){\"use strict\";function t(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];if(t.length>1){t[0]=t[0].slice(0,-1);for(var n=t.length-1,s=1;s<n;++s)t[s]=t[s].slice(1,-1);return t[n]=t[n].slice(1),t.join(\"\")}return t[0]}function r(e){return\"(?:\"+e+\")\"}function n(e){return void 0===e?\"undefined\":null===e?\"null\":Object.prototype.toString.call(e).split(\" \").pop().split(\"]\").shift().toLowerCase()}function s(e){return e.toUpperCase()}function o(e){var n=\"[A-Za-z]\",s=\"[0-9]\",o=t(s,\"[A-Fa-f]\"),i=r(r(\"%[EFef]\"+o+\"%\"+o+o+\"%\"+o+o)+\"|\"+r(\"%[89A-Fa-f]\"+o+\"%\"+o+o)+\"|\"+r(\"%\"+o+o)),a=\"[\\\\!\\\\$\\\\&\\\\'\\\\(\\\\)\\\\*\\\\+\\\\,\\\\;\\\\=]\",c=t(\"[\\\\:\\\\/\\\\?\\\\#\\\\[\\\\]\\\\@]\",a),l=e?\"[\\\\uE000-\\\\uF8FF]\":\"[]\",u=t(n,s,\"[\\\\-\\\\.\\\\_\\\\~]\",e?\"[\\\\xA0-\\\\u200D\\\\u2010-\\\\u2029\\\\u202F-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFEF]\":\"[]\"),d=r(n+t(n,s,\"[\\\\+\\\\-\\\\.]\")+\"*\"),f=r(r(i+\"|\"+t(u,a,\"[\\\\:]\"))+\"*\"),h=(r(r(\"25[0-5]\")+\"|\"+r(\"2[0-4]\"+s)+\"|\"+r(\"1\"+s+s)+\"|\"+r(\"[1-9]\"+s)+\"|\"+s),r(r(\"25[0-5]\")+\"|\"+r(\"2[0-4]\"+s)+\"|\"+r(\"1\"+s+s)+\"|\"+r(\"0?[1-9]\"+s)+\"|0?0?\"+s)),p=r(h+\"\\\\.\"+h+\"\\\\.\"+h+\"\\\\.\"+h),m=r(o+\"{1,4}\"),y=r(r(m+\"\\\\:\"+m)+\"|\"+p),g=r(r(m+\"\\\\:\")+\"{6}\"+y),v=r(\"\\\\:\\\\:\"+r(m+\"\\\\:\")+\"{5}\"+y),w=r(r(m)+\"?\\\\:\\\\:\"+r(m+\"\\\\:\")+\"{4}\"+y),E=r(r(r(m+\"\\\\:\")+\"{0,1}\"+m)+\"?\\\\:\\\\:\"+r(m+\"\\\\:\")+\"{3}\"+y),$=r(r(r(m+\"\\\\:\")+\"{0,2}\"+m)+\"?\\\\:\\\\:\"+r(m+\"\\\\:\")+\"{2}\"+y),_=r(r(r(m+\"\\\\:\")+\"{0,3}\"+m)+\"?\\\\:\\\\:\"+m+\"\\\\:\"+y),b=r(r(r(m+\"\\\\:\")+\"{0,4}\"+m)+\"?\\\\:\\\\:\"+y),S=r(r(r(m+\"\\\\:\")+\"{0,5}\"+m)+\"?\\\\:\\\\:\"+m),O=r(r(r(m+\"\\\\:\")+\"{0,6}\"+m)+\"?\\\\:\\\\:\"),P=r([g,v,w,E,$,_,b,S,O].join(\"|\")),x=r(r(u+\"|\"+i)+\"+\"),N=(r(P+\"\\\\%25\"+x),r(P+r(\"\\\\%25|\\\\%(?!\"+o+\"{2})\")+x)),I=r(\"[vV]\"+o+\"+\\\\.\"+t(u,a,\"[\\\\:]\")+\"+\"),j=r(\"\\\\[\"+r(N+\"|\"+P+\"|\"+I)+\"\\\\]\"),R=r(r(i+\"|\"+t(u,a))+\"*\"),A=r(j+\"|\"+p+\"(?!\"+R+\")|\"+R),T=r(s+\"*\"),C=r(r(f+\"@\")+\"?\"+A+r(\"\\\\:\"+T)+\"?\"),D=r(i+\"|\"+t(u,a,\"[\\\\:\\\\@]\")),k=r(D+\"*\"),L=r(D+\"+\"),M=r(r(i+\"|\"+t(u,a,\"[\\\\@]\"))+\"+\"),F=r(r(\"\\\\/\"+k)+\"*\"),U=r(\"\\\\/\"+r(L+F)+\"?\"),z=r(M+F),V=r(L+F),G=\"(?!\"+D+\")\",q=(r(F+\"|\"+U+\"|\"+z+\"|\"+V+\"|\"+G),r(r(D+\"|\"+t(\"[\\\\/\\\\?]\",l))+\"*\")),W=r(r(D+\"|[\\\\/\\\\?]\")+\"*\"),B=r(r(\"\\\\/\\\\/\"+C+F)+\"|\"+U+\"|\"+V+\"|\"+G),H=r(d+\"\\\\:\"+B+r(\"\\\\?\"+q)+\"?\"+r(\"\\\\#\"+W)+\"?\"),K=r(r(\"\\\\/\\\\/\"+C+F)+\"|\"+U+\"|\"+z+\"|\"+G),J=r(K+r(\"\\\\?\"+q)+\"?\"+r(\"\\\\#\"+W)+\"?\");return r(H+\"|\"+J),r(d+\"\\\\:\"+B+r(\"\\\\?\"+q)+\"?\"),r(r(\"\\\\/\\\\/(\"+r(\"(\"+f+\")@\")+\"?(\"+A+\")\"+r(\"\\\\:(\"+T+\")\")+\"?)\")+\"?(\"+F+\"|\"+U+\"|\"+V+\"|\"+G+\")\"),r(\"\\\\?(\"+q+\")\"),r(\"\\\\#(\"+W+\")\"),r(r(\"\\\\/\\\\/(\"+r(\"(\"+f+\")@\")+\"?(\"+A+\")\"+r(\"\\\\:(\"+T+\")\")+\"?)\")+\"?(\"+F+\"|\"+U+\"|\"+z+\"|\"+G+\")\"),r(\"\\\\?(\"+q+\")\"),r(\"\\\\#(\"+W+\")\"),r(r(\"\\\\/\\\\/(\"+r(\"(\"+f+\")@\")+\"?(\"+A+\")\"+r(\"\\\\:(\"+T+\")\")+\"?)\")+\"?(\"+F+\"|\"+U+\"|\"+V+\"|\"+G+\")\"),r(\"\\\\?(\"+q+\")\"),r(\"\\\\#(\"+W+\")\"),r(\"(\"+f+\")@\"),r(\"\\\\:(\"+T+\")\"),{NOT_SCHEME:new RegExp(t(\"[^]\",n,s,\"[\\\\+\\\\-\\\\.]\"),\"g\"),NOT_USERINFO:new RegExp(t(\"[^\\\\%\\\\:]\",u,a),\"g\"),NOT_HOST:new RegExp(t(\"[^\\\\%\\\\[\\\\]\\\\:]\",u,a),\"g\"),NOT_PATH:new RegExp(t(\"[^\\\\%\\\\/\\\\:\\\\@]\",u,a),\"g\"),NOT_PATH_NOSCHEME:new RegExp(t(\"[^\\\\%\\\\/\\\\@]\",u,a),\"g\"),NOT_QUERY:new RegExp(t(\"[^\\\\%]\",u,a,\"[\\\\:\\\\@\\\\/\\\\?]\",l),\"g\"),NOT_FRAGMENT:new RegExp(t(\"[^\\\\%]\",u,a,\"[\\\\:\\\\@\\\\/\\\\?]\"),\"g\"),ESCAPE:new RegExp(t(\"[^]\",u,a),\"g\"),UNRESERVED:new RegExp(u,\"g\"),OTHER_CHARS:new RegExp(t(\"[^\\\\%]\",u,c),\"g\"),PCT_ENCODED:new RegExp(i,\"g\"),IPV4ADDRESS:new RegExp(\"^(\"+p+\")$\"),IPV6ADDRESS:new RegExp(\"^\\\\[?(\"+P+\")\"+r(r(\"\\\\%25|\\\\%(?!\"+o+\"{2})\")+\"(\"+x+\")\")+\"?\\\\]?$\")}}var i=o(!1),a=o(!0),c=function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return function(e,t){var r=[],n=!0,s=!1,o=void 0;try{for(var i,a=e[Symbol.iterator]();!(n=(i=a.next()).done)&&(r.push(i.value),!t||r.length!==t);n=!0);}catch(e){s=!0,o=e}finally{try{!n&&a.return&&a.return()}finally{if(s)throw o}}return r}(e,t);throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")},l=2147483647,u=36,d=/^xn--/,f=/[^\\0-\\x7E]/,h=/[\\x2E\\u3002\\uFF0E\\uFF61]/g,p={overflow:\"Overflow: input needs wider integers to process\",\"not-basic\":\"Illegal input >= 0x80 (not a basic code point)\",\"invalid-input\":\"Invalid input\"},m=Math.floor,y=String.fromCharCode;function g(e){throw new RangeError(p[e])}function v(e,t){var r=e.split(\"@\"),n=\"\";return r.length>1&&(n=r[0]+\"@\",e=r[1]),n+function(e,t){for(var r=[],n=e.length;n--;)r[n]=t(e[n]);return r}((e=e.replace(h,\".\")).split(\".\"),t).join(\".\")}function w(e){for(var t=[],r=0,n=e.length;r<n;){var s=e.charCodeAt(r++);if(s>=55296&&s<=56319&&r<n){var o=e.charCodeAt(r++);56320==(64512&o)?t.push(((1023&s)<<10)+(1023&o)+65536):(t.push(s),r--)}else t.push(s)}return t}var E=function(e,t){return e+22+75*(e<26)-((0!=t)<<5)},$=function(e,t,r){var n=0;for(e=r?m(e/700):e>>1,e+=m(e/t);e>455;n+=u)e=m(e/35);return m(n+36*e/(e+38))},_=function(e){var t,r=[],n=e.length,s=0,o=128,i=72,a=e.lastIndexOf(\"-\");a<0&&(a=0);for(var c=0;c<a;++c)e.charCodeAt(c)>=128&&g(\"not-basic\"),r.push(e.charCodeAt(c));for(var d=a>0?a+1:0;d<n;){for(var f=s,h=1,p=u;;p+=u){d>=n&&g(\"invalid-input\");var y=(t=e.charCodeAt(d++))-48<10?t-22:t-65<26?t-65:t-97<26?t-97:u;(y>=u||y>m((l-s)/h))&&g(\"overflow\"),s+=y*h;var v=p<=i?1:p>=i+26?26:p-i;if(y<v)break;var w=u-v;h>m(l/w)&&g(\"overflow\"),h*=w}var E=r.length+1;i=$(s-f,E,0==f),m(s/E)>l-o&&g(\"overflow\"),o+=m(s/E),s%=E,r.splice(s++,0,o)}return String.fromCodePoint.apply(String,r)},b=function(e){var t=[],r=(e=w(e)).length,n=128,s=0,o=72,i=!0,a=!1,c=void 0;try{for(var d,f=e[Symbol.iterator]();!(i=(d=f.next()).done);i=!0){var h=d.value;h<128&&t.push(y(h))}}catch(e){a=!0,c=e}finally{try{!i&&f.return&&f.return()}finally{if(a)throw c}}var p=t.length,v=p;for(p&&t.push(\"-\");v<r;){var _=l,b=!0,S=!1,O=void 0;try{for(var P,x=e[Symbol.iterator]();!(b=(P=x.next()).done);b=!0){var N=P.value;N>=n&&N<_&&(_=N)}}catch(e){S=!0,O=e}finally{try{!b&&x.return&&x.return()}finally{if(S)throw O}}var I=v+1;_-n>m((l-s)/I)&&g(\"overflow\"),s+=(_-n)*I,n=_;var j=!0,R=!1,A=void 0;try{for(var T,C=e[Symbol.iterator]();!(j=(T=C.next()).done);j=!0){var D=T.value;if(D<n&&++s>l&&g(\"overflow\"),D==n){for(var k=s,L=u;;L+=u){var M=L<=o?1:L>=o+26?26:L-o;if(k<M)break;var F=k-M,U=u-M;t.push(y(E(M+F%U,0))),k=m(F/U)}t.push(y(E(k,0))),o=$(s,I,v==p),s=0,++v}}}catch(e){R=!0,A=e}finally{try{!j&&C.return&&C.return()}finally{if(R)throw A}}++s,++n}return t.join(\"\")},S={version:\"2.1.0\",ucs2:{decode:w,encode:function(e){return String.fromCodePoint.apply(String,function(e){if(Array.isArray(e)){for(var t=0,r=Array(e.length);t<e.length;t++)r[t]=e[t];return r}return Array.from(e)}(e))}},decode:_,encode:b,toASCII:function(e){return v(e,(function(e){return f.test(e)?\"xn--\"+b(e):e}))},toUnicode:function(e){return v(e,(function(e){return d.test(e)?_(e.slice(4).toLowerCase()):e}))}},O={};function P(e){var t=e.charCodeAt(0);return t<16?\"%0\"+t.toString(16).toUpperCase():t<128?\"%\"+t.toString(16).toUpperCase():t<2048?\"%\"+(t>>6|192).toString(16).toUpperCase()+\"%\"+(63&t|128).toString(16).toUpperCase():\"%\"+(t>>12|224).toString(16).toUpperCase()+\"%\"+(t>>6&63|128).toString(16).toUpperCase()+\"%\"+(63&t|128).toString(16).toUpperCase()}function x(e){for(var t=\"\",r=0,n=e.length;r<n;){var s=parseInt(e.substr(r+1,2),16);if(s<128)t+=String.fromCharCode(s),r+=3;else if(s>=194&&s<224){if(n-r>=6){var o=parseInt(e.substr(r+4,2),16);t+=String.fromCharCode((31&s)<<6|63&o)}else t+=e.substr(r,6);r+=6}else if(s>=224){if(n-r>=9){var i=parseInt(e.substr(r+4,2),16),a=parseInt(e.substr(r+7,2),16);t+=String.fromCharCode((15&s)<<12|(63&i)<<6|63&a)}else t+=e.substr(r,9);r+=9}else t+=e.substr(r,3),r+=3}return t}function N(e,t){function r(e){var r=x(e);return r.match(t.UNRESERVED)?r:e}return e.scheme&&(e.scheme=String(e.scheme).replace(t.PCT_ENCODED,r).toLowerCase().replace(t.NOT_SCHEME,\"\")),void 0!==e.userinfo&&(e.userinfo=String(e.userinfo).replace(t.PCT_ENCODED,r).replace(t.NOT_USERINFO,P).replace(t.PCT_ENCODED,s)),void 0!==e.host&&(e.host=String(e.host).replace(t.PCT_ENCODED,r).toLowerCase().replace(t.NOT_HOST,P).replace(t.PCT_ENCODED,s)),void 0!==e.path&&(e.path=String(e.path).replace(t.PCT_ENCODED,r).replace(e.scheme?t.NOT_PATH:t.NOT_PATH_NOSCHEME,P).replace(t.PCT_ENCODED,s)),void 0!==e.query&&(e.query=String(e.query).replace(t.PCT_ENCODED,r).replace(t.NOT_QUERY,P).replace(t.PCT_ENCODED,s)),void 0!==e.fragment&&(e.fragment=String(e.fragment).replace(t.PCT_ENCODED,r).replace(t.NOT_FRAGMENT,P).replace(t.PCT_ENCODED,s)),e}function I(e){return e.replace(/^0*(.*)/,\"$1\")||\"0\"}function j(e,t){var r=e.match(t.IPV4ADDRESS)||[],n=c(r,2)[1];return n?n.split(\".\").map(I).join(\".\"):e}function R(e,t){var r=e.match(t.IPV6ADDRESS)||[],n=c(r,3),s=n[1],o=n[2];if(s){for(var i=s.toLowerCase().split(\"::\").reverse(),a=c(i,2),l=a[0],u=a[1],d=u?u.split(\":\").map(I):[],f=l.split(\":\").map(I),h=t.IPV4ADDRESS.test(f[f.length-1]),p=h?7:8,m=f.length-p,y=Array(p),g=0;g<p;++g)y[g]=d[g]||f[m+g]||\"\";h&&(y[p-1]=j(y[p-1],t));var v=y.reduce((function(e,t,r){if(!t||\"0\"===t){var n=e[e.length-1];n&&n.index+n.length===r?n.length++:e.push({index:r,length:1})}return e}),[]).sort((function(e,t){return t.length-e.length}))[0],w=void 0;if(v&&v.length>1){var E=y.slice(0,v.index),$=y.slice(v.index+v.length);w=E.join(\":\")+\"::\"+$.join(\":\")}else w=y.join(\":\");return o&&(w+=\"%\"+o),w}return e}var A=/^(?:([^:\\/?#]+):)?(?:\\/\\/((?:([^\\/?#@]*)@)?(\\[[^\\/?#\\]]+\\]|[^\\/?#:]*)(?:\\:(\\d*))?))?([^?#]*)(?:\\?([^#]*))?(?:#((?:.|\\n|\\r)*))?/i,T=void 0===\"\".match(/(){0}/)[1];function C(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r={},n=!1!==t.iri?a:i;\"suffix\"===t.reference&&(e=(t.scheme?t.scheme+\":\":\"\")+\"//\"+e);var s=e.match(A);if(s){T?(r.scheme=s[1],r.userinfo=s[3],r.host=s[4],r.port=parseInt(s[5],10),r.path=s[6]||\"\",r.query=s[7],r.fragment=s[8],isNaN(r.port)&&(r.port=s[5])):(r.scheme=s[1]||void 0,r.userinfo=-1!==e.indexOf(\"@\")?s[3]:void 0,r.host=-1!==e.indexOf(\"//\")?s[4]:void 0,r.port=parseInt(s[5],10),r.path=s[6]||\"\",r.query=-1!==e.indexOf(\"?\")?s[7]:void 0,r.fragment=-1!==e.indexOf(\"#\")?s[8]:void 0,isNaN(r.port)&&(r.port=e.match(/\\/\\/(?:.|\\n)*\\:(?:\\/|\\?|\\#|$)/)?s[4]:void 0)),r.host&&(r.host=R(j(r.host,n),n)),void 0!==r.scheme||void 0!==r.userinfo||void 0!==r.host||void 0!==r.port||r.path||void 0!==r.query?void 0===r.scheme?r.reference=\"relative\":void 0===r.fragment?r.reference=\"absolute\":r.reference=\"uri\":r.reference=\"same-document\",t.reference&&\"suffix\"!==t.reference&&t.reference!==r.reference&&(r.error=r.error||\"URI is not a \"+t.reference+\" reference.\");var o=O[(t.scheme||r.scheme||\"\").toLowerCase()];if(t.unicodeSupport||o&&o.unicodeSupport)N(r,n);else{if(r.host&&(t.domainHost||o&&o.domainHost))try{r.host=S.toASCII(r.host.replace(n.PCT_ENCODED,x).toLowerCase())}catch(e){r.error=r.error||\"Host's domain name can not be converted to ASCII via punycode: \"+e}N(r,i)}o&&o.parse&&o.parse(r,t)}else r.error=r.error||\"URI can not be parsed.\";return r}var D=/^\\.\\.?\\//,k=/^\\/\\.(\\/|$)/,L=/^\\/\\.\\.(\\/|$)/,M=/^\\/?(?:.|\\n)*?(?=\\/|$)/;function F(e){for(var t=[];e.length;)if(e.match(D))e=e.replace(D,\"\");else if(e.match(k))e=e.replace(k,\"/\");else if(e.match(L))e=e.replace(L,\"/\"),t.pop();else if(\".\"===e||\"..\"===e)e=\"\";else{var r=e.match(M);if(!r)throw new Error(\"Unexpected dot segment condition\");var n=r[0];e=e.slice(n.length),t.push(n)}return t.join(\"\")}function U(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.iri?a:i,n=[],s=O[(t.scheme||e.scheme||\"\").toLowerCase()];if(s&&s.serialize&&s.serialize(e,t),e.host)if(r.IPV6ADDRESS.test(e.host));else if(t.domainHost||s&&s.domainHost)try{e.host=t.iri?S.toUnicode(e.host):S.toASCII(e.host.replace(r.PCT_ENCODED,x).toLowerCase())}catch(r){e.error=e.error||\"Host's domain name can not be converted to \"+(t.iri?\"Unicode\":\"ASCII\")+\" via punycode: \"+r}N(e,r),\"suffix\"!==t.reference&&e.scheme&&(n.push(e.scheme),n.push(\":\"));var o=function(e,t){var r=!1!==t.iri?a:i,n=[];return void 0!==e.userinfo&&(n.push(e.userinfo),n.push(\"@\")),void 0!==e.host&&n.push(R(j(String(e.host),r),r).replace(r.IPV6ADDRESS,(function(e,t,r){return\"[\"+t+(r?\"%25\"+r:\"\")+\"]\"}))),\"number\"!=typeof e.port&&\"string\"!=typeof e.port||(n.push(\":\"),n.push(String(e.port))),n.length?n.join(\"\"):void 0}(e,t);if(void 0!==o&&(\"suffix\"!==t.reference&&n.push(\"//\"),n.push(o),e.path&&\"/\"!==e.path.charAt(0)&&n.push(\"/\")),void 0!==e.path){var c=e.path;t.absolutePath||s&&s.absolutePath||(c=F(c)),void 0===o&&(c=c.replace(/^\\/\\//,\"/%2F\")),n.push(c)}return void 0!==e.query&&(n.push(\"?\"),n.push(e.query)),void 0!==e.fragment&&(n.push(\"#\"),n.push(e.fragment)),n.join(\"\")}function z(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n={};return arguments[3]||(e=C(U(e,r),r),t=C(U(t,r),r)),!(r=r||{}).tolerant&&t.scheme?(n.scheme=t.scheme,n.userinfo=t.userinfo,n.host=t.host,n.port=t.port,n.path=F(t.path||\"\"),n.query=t.query):(void 0!==t.userinfo||void 0!==t.host||void 0!==t.port?(n.userinfo=t.userinfo,n.host=t.host,n.port=t.port,n.path=F(t.path||\"\"),n.query=t.query):(t.path?(\"/\"===t.path.charAt(0)?n.path=F(t.path):(void 0===e.userinfo&&void 0===e.host&&void 0===e.port||e.path?e.path?n.path=e.path.slice(0,e.path.lastIndexOf(\"/\")+1)+t.path:n.path=t.path:n.path=\"/\"+t.path,n.path=F(n.path)),n.query=t.query):(n.path=e.path,void 0!==t.query?n.query=t.query:n.query=e.query),n.userinfo=e.userinfo,n.host=e.host,n.port=e.port),n.scheme=e.scheme),n.fragment=t.fragment,n}function V(e,t){return e&&e.toString().replace(t&&t.iri?a.PCT_ENCODED:i.PCT_ENCODED,x)}var G={scheme:\"http\",domainHost:!0,parse:function(e,t){return e.host||(e.error=e.error||\"HTTP URIs must have a host.\"),e},serialize:function(e,t){var r=\"https\"===String(e.scheme).toLowerCase();return e.port!==(r?443:80)&&\"\"!==e.port||(e.port=void 0),e.path||(e.path=\"/\"),e}},q={scheme:\"https\",domainHost:G.domainHost,parse:G.parse,serialize:G.serialize};function W(e){return\"boolean\"==typeof e.secure?e.secure:\"wss\"===String(e.scheme).toLowerCase()}var B={scheme:\"ws\",domainHost:!0,parse:function(e,t){var r=e;return r.secure=W(r),r.resourceName=(r.path||\"/\")+(r.query?\"?\"+r.query:\"\"),r.path=void 0,r.query=void 0,r},serialize:function(e,t){if(e.port!==(W(e)?443:80)&&\"\"!==e.port||(e.port=void 0),\"boolean\"==typeof e.secure&&(e.scheme=e.secure?\"wss\":\"ws\",e.secure=void 0),e.resourceName){var r=e.resourceName.split(\"?\"),n=c(r,2),s=n[0],o=n[1];e.path=s&&\"/\"!==s?s:void 0,e.query=o,e.resourceName=void 0}return e.fragment=void 0,e}},H={scheme:\"wss\",domainHost:B.domainHost,parse:B.parse,serialize:B.serialize},K={},J=\"[A-Za-z0-9\\\\-\\\\.\\\\_\\\\~\\\\xA0-\\\\u200D\\\\u2010-\\\\u2029\\\\u202F-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFEF]\",X=\"[0-9A-Fa-f]\",Y=r(r(\"%[EFef]\"+X+\"%\"+X+X+\"%\"+X+X)+\"|\"+r(\"%[89A-Fa-f]\"+X+\"%\"+X+X)+\"|\"+r(\"%\"+X+X)),Q=t(\"[\\\\!\\\\$\\\\%\\\\'\\\\(\\\\)\\\\*\\\\+\\\\,\\\\-\\\\.0-9\\\\<\\\\>A-Z\\\\x5E-\\\\x7E]\",'[\\\\\"\\\\\\\\]'),Z=new RegExp(J,\"g\"),ee=new RegExp(Y,\"g\"),te=new RegExp(t(\"[^]\",\"[A-Za-z0-9\\\\!\\\\$\\\\%\\\\'\\\\*\\\\+\\\\-\\\\^\\\\_\\\\`\\\\{\\\\|\\\\}\\\\~]\",\"[\\\\.]\",'[\\\\\"]',Q),\"g\"),re=new RegExp(t(\"[^]\",J,\"[\\\\!\\\\$\\\\'\\\\(\\\\)\\\\*\\\\+\\\\,\\\\;\\\\:\\\\@]\"),\"g\"),ne=re;function se(e){var t=x(e);return t.match(Z)?t:e}var oe={scheme:\"mailto\",parse:function(e,t){var r=e,n=r.to=r.path?r.path.split(\",\"):[];if(r.path=void 0,r.query){for(var s=!1,o={},i=r.query.split(\"&\"),a=0,c=i.length;a<c;++a){var l=i[a].split(\"=\");switch(l[0]){case\"to\":for(var u=l[1].split(\",\"),d=0,f=u.length;d<f;++d)n.push(u[d]);break;case\"subject\":r.subject=V(l[1],t);break;case\"body\":r.body=V(l[1],t);break;default:s=!0,o[V(l[0],t)]=V(l[1],t)}}s&&(r.headers=o)}r.query=void 0;for(var h=0,p=n.length;h<p;++h){var m=n[h].split(\"@\");if(m[0]=V(m[0]),t.unicodeSupport)m[1]=V(m[1],t).toLowerCase();else try{m[1]=S.toASCII(V(m[1],t).toLowerCase())}catch(e){r.error=r.error||\"Email address's domain name can not be converted to ASCII via punycode: \"+e}n[h]=m.join(\"@\")}return r},serialize:function(e,t){var r,n=e,o=null!=(r=e.to)?r instanceof Array?r:\"number\"!=typeof r.length||r.split||r.setInterval||r.call?[r]:Array.prototype.slice.call(r):[];if(o){for(var i=0,a=o.length;i<a;++i){var c=String(o[i]),l=c.lastIndexOf(\"@\"),u=c.slice(0,l).replace(ee,se).replace(ee,s).replace(te,P),d=c.slice(l+1);try{d=t.iri?S.toUnicode(d):S.toASCII(V(d,t).toLowerCase())}catch(e){n.error=n.error||\"Email address's domain name can not be converted to \"+(t.iri?\"Unicode\":\"ASCII\")+\" via punycode: \"+e}o[i]=u+\"@\"+d}n.path=o.join(\",\")}var f=e.headers=e.headers||{};e.subject&&(f.subject=e.subject),e.body&&(f.body=e.body);var h=[];for(var p in f)f[p]!==K[p]&&h.push(p.replace(ee,se).replace(ee,s).replace(re,P)+\"=\"+f[p].replace(ee,se).replace(ee,s).replace(ne,P));return h.length&&(n.query=h.join(\"&\")),n}},ie=/^([^\\:]+)\\:(.*)/,ae={scheme:\"urn\",parse:function(e,t){var r=e.path&&e.path.match(ie),n=e;if(r){var s=t.scheme||n.scheme||\"urn\",o=r[1].toLowerCase(),i=r[2],a=s+\":\"+(t.nid||o),c=O[a];n.nid=o,n.nss=i,n.path=void 0,c&&(n=c.parse(n,t))}else n.error=n.error||\"URN can not be parsed.\";return n},serialize:function(e,t){var r=t.scheme||e.scheme||\"urn\",n=e.nid,s=r+\":\"+(t.nid||n),o=O[s];o&&(e=o.serialize(e,t));var i=e,a=e.nss;return i.path=(n||t.nid)+\":\"+a,i}},ce=/^[0-9A-Fa-f]{8}(?:\\-[0-9A-Fa-f]{4}){3}\\-[0-9A-Fa-f]{12}$/,le={scheme:\"urn:uuid\",parse:function(e,t){var r=e;return r.uuid=r.nss,r.nss=void 0,t.tolerant||r.uuid&&r.uuid.match(ce)||(r.error=r.error||\"UUID is not valid.\"),r},serialize:function(e,t){var r=e;return r.nss=(e.uuid||\"\").toLowerCase(),r}};O[G.scheme]=G,O[q.scheme]=q,O[B.scheme]=B,O[H.scheme]=H,O[oe.scheme]=oe,O[ae.scheme]=ae,O[le.scheme]=le,e.SCHEMES=O,e.pctEncChar=P,e.pctDecChars=x,e.parse=C,e.removeDotSegments=F,e.serialize=U,e.resolveComponents=z,e.resolve=function(e,t,r){var n=function(e,t){var r=e;if(t)for(var n in t)r[n]=t[n];return r}({scheme:\"null\"},r);return U(z(C(e,n),C(t,n),n,!0),n)},e.normalize=function(e,t){return\"string\"==typeof e?e=U(C(e,t),t):\"object\"===n(e)&&(e=C(U(e,t),t)),e},e.equal=function(e,t,r){return\"string\"==typeof e?e=U(C(e,r),r):\"object\"===n(e)&&(e=U(e,r)),\"string\"==typeof t?t=U(C(t,r),r):\"object\"===n(t)&&(t=U(t,r)),e===t},e.escapeComponent=function(e,t){return e&&e.toString().replace(t&&t.iri?a.ESCAPE:i.ESCAPE,P)},e.unescapeComponent=V,Object.defineProperty(e,\"__esModule\",{value:!0})}(t)},2600:(e,t,r)=>{\"use strict\";r.r(t),r.d(t,{NIL:()=>S,parse:()=>v,stringify:()=>f,v1:()=>g,v3:()=>E,v4:()=>_,v5:()=>b,validate:()=>l,version:()=>O});var n=r(6113),s=r.n(n);const o=new Uint8Array(256);let i=o.length;function a(){return i>o.length-16&&(s().randomFillSync(o),i=0),o.slice(i,i+=16)}const c=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,l=function(e){return\"string\"==typeof e&&c.test(e)},u=[];for(let e=0;e<256;++e)u.push((e+256).toString(16).slice(1));function d(e,t=0){return u[e[t+0]]+u[e[t+1]]+u[e[t+2]]+u[e[t+3]]+\"-\"+u[e[t+4]]+u[e[t+5]]+\"-\"+u[e[t+6]]+u[e[t+7]]+\"-\"+u[e[t+8]]+u[e[t+9]]+\"-\"+u[e[t+10]]+u[e[t+11]]+u[e[t+12]]+u[e[t+13]]+u[e[t+14]]+u[e[t+15]]}const f=function(e,t=0){const r=d(e,t);if(!l(r))throw TypeError(\"Stringified UUID is invalid\");return r};let h,p,m=0,y=0;const g=function(e,t,r){let n=t&&r||0;const s=t||new Array(16);let o=(e=e||{}).node||h,i=void 0!==e.clockseq?e.clockseq:p;if(null==o||null==i){const t=e.random||(e.rng||a)();null==o&&(o=h=[1|t[0],t[1],t[2],t[3],t[4],t[5]]),null==i&&(i=p=16383&(t[6]<<8|t[7]))}let c=void 0!==e.msecs?e.msecs:Date.now(),l=void 0!==e.nsecs?e.nsecs:y+1;const u=c-m+(l-y)/1e4;if(u<0&&void 0===e.clockseq&&(i=i+1&16383),(u<0||c>m)&&void 0===e.nsecs&&(l=0),l>=1e4)throw new Error(\"uuid.v1(): Can't create more than 10M uuids/sec\");m=c,y=l,p=i,c+=122192928e5;const f=(1e4*(268435455&c)+l)%4294967296;s[n++]=f>>>24&255,s[n++]=f>>>16&255,s[n++]=f>>>8&255,s[n++]=255&f;const g=c/4294967296*1e4&268435455;s[n++]=g>>>8&255,s[n++]=255&g,s[n++]=g>>>24&15|16,s[n++]=g>>>16&255,s[n++]=i>>>8|128,s[n++]=255&i;for(let e=0;e<6;++e)s[n+e]=o[e];return t||d(s)},v=function(e){if(!l(e))throw TypeError(\"Invalid UUID\");let t;const r=new Uint8Array(16);return r[0]=(t=parseInt(e.slice(0,8),16))>>>24,r[1]=t>>>16&255,r[2]=t>>>8&255,r[3]=255&t,r[4]=(t=parseInt(e.slice(9,13),16))>>>8,r[5]=255&t,r[6]=(t=parseInt(e.slice(14,18),16))>>>8,r[7]=255&t,r[8]=(t=parseInt(e.slice(19,23),16))>>>8,r[9]=255&t,r[10]=(t=parseInt(e.slice(24,36),16))/1099511627776&255,r[11]=t/4294967296&255,r[12]=t>>>24&255,r[13]=t>>>16&255,r[14]=t>>>8&255,r[15]=255&t,r};function w(e,t,r){function n(e,n,s,o){var i;if(\"string\"==typeof e&&(e=function(e){e=unescape(encodeURIComponent(e));const t=[];for(let r=0;r<e.length;++r)t.push(e.charCodeAt(r));return t}(e)),\"string\"==typeof n&&(n=v(n)),16!==(null===(i=n)||void 0===i?void 0:i.length))throw TypeError(\"Namespace must be array-like (16 iterable integer values, 0-255)\");let a=new Uint8Array(16+e.length);if(a.set(n),a.set(e,n.length),a=r(a),a[6]=15&a[6]|t,a[8]=63&a[8]|128,s){o=o||0;for(let e=0;e<16;++e)s[o+e]=a[e];return s}return d(a)}try{n.name=e}catch(e){}return n.DNS=\"6ba7b810-9dad-11d1-80b4-00c04fd430c8\",n.URL=\"6ba7b811-9dad-11d1-80b4-00c04fd430c8\",n}const E=w(\"v3\",48,(function(e){return Array.isArray(e)?e=Buffer.from(e):\"string\"==typeof e&&(e=Buffer.from(e,\"utf8\")),s().createHash(\"md5\").update(e).digest()})),$={randomUUID:s().randomUUID},_=function(e,t,r){if($.randomUUID&&!t&&!e)return $.randomUUID();const n=(e=e||{}).random||(e.rng||a)();if(n[6]=15&n[6]|64,n[8]=63&n[8]|128,t){r=r||0;for(let e=0;e<16;++e)t[r+e]=n[e];return t}return d(n)},b=w(\"v5\",80,(function(e){return Array.isArray(e)?e=Buffer.from(e):\"string\"==typeof e&&(e=Buffer.from(e,\"utf8\")),s().createHash(\"sha1\").update(e).digest()})),S=\"00000000-0000-0000-0000-000000000000\",O=function(e){if(!l(e))throw TypeError(\"Invalid UUID\");return parseInt(e.slice(14,15),16)}},9602:e=>{\"use strict\";e.exports=function(e){e.prototype[Symbol.iterator]=function*(){for(let e=this.head;e;e=e.next)yield e.value}}},4411:(e,t,r)=>{\"use strict\";function n(e){var t=this;if(t instanceof n||(t=new n),t.tail=null,t.head=null,t.length=0,e&&\"function\"==typeof e.forEach)e.forEach((function(e){t.push(e)}));else if(arguments.length>0)for(var r=0,s=arguments.length;r<s;r++)t.push(arguments[r]);return t}function s(e,t,r){var n=t===e.head?new a(r,null,t,e):new a(r,t,t.next,e);return null===n.next&&(e.tail=n),null===n.prev&&(e.head=n),e.length++,n}function o(e,t){e.tail=new a(t,e.tail,null,e),e.head||(e.head=e.tail),e.length++}function i(e,t){e.head=new a(t,null,e.head,e),e.tail||(e.tail=e.head),e.length++}function a(e,t,r,n){if(!(this instanceof a))return new a(e,t,r,n);this.list=n,this.value=e,t?(t.next=this,this.prev=t):this.prev=null,r?(r.prev=this,this.next=r):this.next=null}e.exports=n,n.Node=a,n.create=n,n.prototype.removeNode=function(e){if(e.list!==this)throw new Error(\"removing node which does not belong to this list\");var t=e.next,r=e.prev;return t&&(t.prev=r),r&&(r.next=t),e===this.head&&(this.head=t),e===this.tail&&(this.tail=r),e.list.length--,e.next=null,e.prev=null,e.list=null,t},n.prototype.unshiftNode=function(e){if(e!==this.head){e.list&&e.list.removeNode(e);var t=this.head;e.list=this,e.next=t,t&&(t.prev=e),this.head=e,this.tail||(this.tail=e),this.length++}},n.prototype.pushNode=function(e){if(e!==this.tail){e.list&&e.list.removeNode(e);var t=this.tail;e.list=this,e.prev=t,t&&(t.next=e),this.tail=e,this.head||(this.head=e),this.length++}},n.prototype.push=function(){for(var e=0,t=arguments.length;e<t;e++)o(this,arguments[e]);return this.length},n.prototype.unshift=function(){for(var e=0,t=arguments.length;e<t;e++)i(this,arguments[e]);return this.length},n.prototype.pop=function(){if(this.tail){var e=this.tail.value;return this.tail=this.tail.prev,this.tail?this.tail.next=null:this.head=null,this.length--,e}},n.prototype.shift=function(){if(this.head){var e=this.head.value;return this.head=this.head.next,this.head?this.head.prev=null:this.tail=null,this.length--,e}},n.prototype.forEach=function(e,t){t=t||this;for(var r=this.head,n=0;null!==r;n++)e.call(t,r.value,n,this),r=r.next},n.prototype.forEachReverse=function(e,t){t=t||this;for(var r=this.tail,n=this.length-1;null!==r;n--)e.call(t,r.value,n,this),r=r.prev},n.prototype.get=function(e){for(var t=0,r=this.head;null!==r&&t<e;t++)r=r.next;if(t===e&&null!==r)return r.value},n.prototype.getReverse=function(e){for(var t=0,r=this.tail;null!==r&&t<e;t++)r=r.prev;if(t===e&&null!==r)return r.value},n.prototype.map=function(e,t){t=t||this;for(var r=new n,s=this.head;null!==s;)r.push(e.call(t,s.value,this)),s=s.next;return r},n.prototype.mapReverse=function(e,t){t=t||this;for(var r=new n,s=this.tail;null!==s;)r.push(e.call(t,s.value,this)),s=s.prev;return r},n.prototype.reduce=function(e,t){var r,n=this.head;if(arguments.length>1)r=t;else{if(!this.head)throw new TypeError(\"Reduce of empty list with no initial value\");n=this.head.next,r=this.head.value}for(var s=0;null!==n;s++)r=e(r,n.value,s),n=n.next;return r},n.prototype.reduceReverse=function(e,t){var r,n=this.tail;if(arguments.length>1)r=t;else{if(!this.tail)throw new TypeError(\"Reduce of empty list with no initial value\");n=this.tail.prev,r=this.tail.value}for(var s=this.length-1;null!==n;s--)r=e(r,n.value,s),n=n.prev;return r},n.prototype.toArray=function(){for(var e=new Array(this.length),t=0,r=this.head;null!==r;t++)e[t]=r.value,r=r.next;return e},n.prototype.toArrayReverse=function(){for(var e=new Array(this.length),t=0,r=this.tail;null!==r;t++)e[t]=r.value,r=r.prev;return e},n.prototype.slice=function(e,t){(t=t||this.length)<0&&(t+=this.length),(e=e||0)<0&&(e+=this.length);var r=new n;if(t<e||t<0)return r;e<0&&(e=0),t>this.length&&(t=this.length);for(var s=0,o=this.head;null!==o&&s<e;s++)o=o.next;for(;null!==o&&s<t;s++,o=o.next)r.push(o.value);return r},n.prototype.sliceReverse=function(e,t){(t=t||this.length)<0&&(t+=this.length),(e=e||0)<0&&(e+=this.length);var r=new n;if(t<e||t<0)return r;e<0&&(e=0),t>this.length&&(t=this.length);for(var s=this.length,o=this.tail;null!==o&&s>t;s--)o=o.prev;for(;null!==o&&s>e;s--,o=o.prev)r.push(o.value);return r},n.prototype.splice=function(e,t,...r){e>this.length&&(e=this.length-1),e<0&&(e=this.length+e);for(var n=0,o=this.head;null!==o&&n<e;n++)o=o.next;var i=[];for(n=0;o&&n<t;n++)i.push(o.value),o=this.removeNode(o);for(null===o&&(o=this.tail),o!==this.head&&o!==this.tail&&(o=o.prev),n=0;n<r.length;n++)o=s(this,o,r[n]);return i},n.prototype.reverse=function(){for(var e=this.head,t=this.tail,r=e;null!==r;r=r.prev){var n=r.prev;r.prev=r.next,r.next=n}return this.head=t,this.tail=e,this};try{r(9602)(n)}catch(e){}},4234:(e,t,r)=>{const n=new(r(6143));e.exports=n},4288:(e,t,r)=>{const{DEV_WEB_URL:n}=r(6381),s=r(1017),o=r(7310),i=r(7147),a=\"linux\"==process.platform,c=\"win32\"==process.platform,l=\"darwin\"==process.platform;function u(){let e=\"\";return e=i.readFileSync(s.join(__dirname,\"../..\",\"./versions/version\")),e.toString().trim()}e.exports={loadMainResource:function(e){e.loadURL(o.format({pathname:s.join(__dirname,\"../..\",`./versions/${u()}`,\"./dist/index.html\"),protocol:\"file:\",slashes:!0}))},readVersion:u,isLinux:a,isWin:c,isMac:l,isProd:!0}},9491:e=>{\"use strict\";e.exports=require(\"assert\")},2081:e=>{\"use strict\";e.exports=require(\"child_process\")},6113:e=>{\"use strict\";e.exports=require(\"crypto\")},2298:e=>{\"use strict\";e.exports=require(\"electron\")},2361:e=>{\"use strict\";e.exports=require(\"events\")},7147:e=>{\"use strict\";e.exports=require(\"fs\")},3685:e=>{\"use strict\";e.exports=require(\"http\")},5687:e=>{\"use strict\";e.exports=require(\"https\")},2037:e=>{\"use strict\";e.exports=require(\"os\")},1017:e=>{\"use strict\";e.exports=require(\"path\")},7310:e=>{\"use strict\";e.exports=require(\"url\")},3837:e=>{\"use strict\";e.exports=require(\"util\")},9756:e=>{\"use strict\";e.exports=JSON.parse('{\"$id\":\"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#\",\"description\":\"Meta-schema for $data reference (JSON AnySchema extension proposal)\",\"type\":\"object\",\"required\":[\"$data\"],\"properties\":{\"$data\":{\"type\":\"string\",\"anyOf\":[{\"format\":\"relative-json-pointer\"},{\"format\":\"json-pointer\"}]}},\"additionalProperties\":false}')},115:e=>{\"use strict\";e.exports=JSON.parse('{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"$id\":\"http://json-schema.org/draft-07/schema#\",\"title\":\"Core schema meta-schema\",\"definitions\":{\"schemaArray\":{\"type\":\"array\",\"minItems\":1,\"items\":{\"$ref\":\"#\"}},\"nonNegativeInteger\":{\"type\":\"integer\",\"minimum\":0},\"nonNegativeIntegerDefault0\":{\"allOf\":[{\"$ref\":\"#/definitions/nonNegativeInteger\"},{\"default\":0}]},\"simpleTypes\":{\"enum\":[\"array\",\"boolean\",\"integer\",\"null\",\"number\",\"object\",\"string\"]},\"stringArray\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},\"uniqueItems\":true,\"default\":[]}},\"type\":[\"object\",\"boolean\"],\"properties\":{\"$id\":{\"type\":\"string\",\"format\":\"uri-reference\"},\"$schema\":{\"type\":\"string\",\"format\":\"uri\"},\"$ref\":{\"type\":\"string\",\"format\":\"uri-reference\"},\"$comment\":{\"type\":\"string\"},\"title\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"},\"default\":true,\"readOnly\":{\"type\":\"boolean\",\"default\":false},\"examples\":{\"type\":\"array\",\"items\":true},\"multipleOf\":{\"type\":\"number\",\"exclusiveMinimum\":0},\"maximum\":{\"type\":\"number\"},\"exclusiveMaximum\":{\"type\":\"number\"},\"minimum\":{\"type\":\"number\"},\"exclusiveMinimum\":{\"type\":\"number\"},\"maxLength\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},\"minLength\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},\"pattern\":{\"type\":\"string\",\"format\":\"regex\"},\"additionalItems\":{\"$ref\":\"#\"},\"items\":{\"anyOf\":[{\"$ref\":\"#\"},{\"$ref\":\"#/definitions/schemaArray\"}],\"default\":true},\"maxItems\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},\"minItems\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},\"uniqueItems\":{\"type\":\"boolean\",\"default\":false},\"contains\":{\"$ref\":\"#\"},\"maxProperties\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},\"minProperties\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},\"required\":{\"$ref\":\"#/definitions/stringArray\"},\"additionalProperties\":{\"$ref\":\"#\"},\"definitions\":{\"type\":\"object\",\"additionalProperties\":{\"$ref\":\"#\"},\"default\":{}},\"properties\":{\"type\":\"object\",\"additionalProperties\":{\"$ref\":\"#\"},\"default\":{}},\"patternProperties\":{\"type\":\"object\",\"additionalProperties\":{\"$ref\":\"#\"},\"propertyNames\":{\"format\":\"regex\"},\"default\":{}},\"dependencies\":{\"type\":\"object\",\"additionalProperties\":{\"anyOf\":[{\"$ref\":\"#\"},{\"$ref\":\"#/definitions/stringArray\"}]}},\"propertyNames\":{\"$ref\":\"#\"},\"const\":true,\"enum\":{\"type\":\"array\",\"items\":true,\"minItems\":1,\"uniqueItems\":true},\"type\":{\"anyOf\":[{\"$ref\":\"#/definitions/simpleTypes\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/simpleTypes\"},\"minItems\":1,\"uniqueItems\":true}]},\"format\":{\"type\":\"string\"},\"contentMediaType\":{\"type\":\"string\"},\"contentEncoding\":{\"type\":\"string\"},\"if\":{\"$ref\":\"#\"},\"then\":{\"$ref\":\"#\"},\"else\":{\"$ref\":\"#\"},\"allOf\":{\"$ref\":\"#/definitions/schemaArray\"},\"anyOf\":{\"$ref\":\"#/definitions/schemaArray\"},\"oneOf\":{\"$ref\":\"#/definitions/schemaArray\"},\"not\":{\"$ref\":\"#\"}},\"default\":true}')}},t={};function r(n){var s=t[n];if(void 0!==s)return s.exports;var o=t[n]={id:n,loaded:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.loaded=!0,o.exports}r.c=t,r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),r(r.s=6010)})();"
  },
  {
    "path": "chat2db-client/src/main/main.js.LICENSE.txt",
    "content": "/** @license URI.js v4.4.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */\n"
  },
  {
    "path": "chat2db-client/src/main/menu.js",
    "content": "const { shell, app, dialog, BrowserWindow, Menu } = require('electron');\nconst os = require('os');\nconst path = require('path');\nconst { isMac } = require('./utils');\n\nconst registerAppMenu = (mainWindow, orgs) => {\n  if (!isMac) {\n    Menu.setApplicationMenu(null);\n    return;\n  }\n  const menuBar = [\n    {\n      label: 'Chat2DB',\n      submenu: [\n        {\n          label: '关于Chat2DB',\n          click() {\n            dialog.showMessageBox({\n              title: '关于Chat2DB',\n              message: `关于Chat2DB v${orgs?.version || app.getVersion()}`,\n              detail:\n                // An intelligent database client and smart BI reporting tool with integrated AI capabilities.\n                '一个集成AI能力的智能数据库客户端和智能BI报表工具。',\n              icon: './logo/icon.png',\n            });\n          },\n        },\n        { type: 'separator' },\n        {\n          label: '重新启动',\n          click() {\n            // 退出程序\n            app.relaunch();\n            app.quit();\n          },\n        },\n        {\n          label: '退出',\n          accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Alt+F4',\n          click() {\n            // 退出程序\n            app.quit();\n          },\n        },\n      ],\n    },\n    {\n      label: '编辑',\n      submenu: [\n        { label: '撤销', role: 'undo' },\n        { label: '重做', role: 'redo' },\n        { type: 'separator' },\n        { label: '剪切', role: 'cut' },\n        { label: '复制', role: 'copy' },\n        { label: '粘贴', role: 'paste' },\n        { label: '全选', role: 'selectAll' },\n      ],\n    },\n    {\n      // label: i18n('menu.edit'),\n      label: '视图',\n      submenu: [\n        // {\n        //   label: '刷新',\n        //   accelerator: 'CmdOrCtrl+Shift+R',\n        //   click() {\n        //     const focusedWindow = BrowserWindow.getFocusedWindow();\n        //     if (focusedWindow) {\n        //       focusedWindow.reload();\n        //     }\n        //   },\n        // },\n        { type: 'separator' },\n        {\n          label: '放大',\n          accelerator: 'CmdOrCtrl+=',\n          role: 'zoomIn',\n        },\n        {\n          label: '缩小',\n          accelerator: 'CmdOrCtrl+-',\n          role: 'zoomOut',\n        },\n        {\n          label: '重置',\n          accelerator: 'CmdOrCtrl+0',\n          role: 'resetZoom',\n        },\n        { type: 'separator' },\n        { label: '全屏', role: 'togglefullscreen' },\n      ],\n    },\n    {\n      label: '窗口',\n      role: 'window',\n      submenu: [\n        { label: '最小化', role: 'minimize', accelerator: 'Command+W' },\n        { label: '关闭', role: 'close' },\n      ],\n    },\n    {\n      label: '帮助',\n      submenu: [\n        {\n          label: '打开日志',\n          accelerator: process.platform === 'darwin' ? 'Cmd+Shift+T' : 'Ctrl+Shift+T',\n          click() {\n            const fileName = '.chat2db/logs/application.log';\n            const url = path.join(os.homedir(), fileName);\n            shell.openPath(url).then((str) => console.log('err:', str));\n          },\n        },\n        {\n          label: '打开控制台',\n          accelerator: process.platform === 'darwin' ? 'Cmd+Shift+I' : 'Ctrl+Shift+I',\n          click() {\n            const focusedWindow = BrowserWindow.getFocusedWindow();\n            focusedWindow && focusedWindow.toggleDevTools();\n          },\n        },\n        {\n          label: '访问官网',\n          click() {\n            const url = 'https://www.sqlgpt.cn/zh';\n            shell.openExternal(url);\n          },\n        },\n        {\n          label: '查看文档',\n          click() {\n            const url = 'https://doc.sqlgpt.cn/zh/';\n            shell.openExternal(url);\n          },\n        },\n        {\n          label: '查看更新日志',\n          click() {\n            const url = 'https://doc.sqlgpt.cn/zh/changelog/';\n            shell.openExternal(url);\n          },\n        },\n      ],\n    },\n  ];\n  Menu.setApplicationMenu(Menu.buildFromTemplate(menuBar));\n};\n\nmodule.exports = registerAppMenu;\n"
  },
  {
    "path": "chat2db-client/src/main/package.json",
    "content": "{\n  \"name\": \"main\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"build:dev\": \"cross-env NODE_ENV=development webpack\",\n    \"build\": \"webpack\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"electron-log\": \"^5.0.3\",\n    \"electron-store\": \"^8.1.0\",\n    \"node-machine-id\": \"^1.1.12\",\n    \"uuid\": \"^9.0.1\"\n  },\n  \"devDependencies\": {\n    \"webpack\": \"^5.89.0\",\n    \"webpack-cli\": \"^5.1.4\"\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/main/preload.js",
    "content": "const { contextBridge, ipcRenderer } = require('electron');\nconst { spawn } = require('child_process');\nconst { JAVA_APP_NAME, JAVA_PATH } = require('./constants');\nconst path = require('path');\nconst { readVersion, isLinux, isWin, isMac } = require('./utils');\n\ncontextBridge.exposeInMainWorld('electronApi', {\n  startServerForSpawn: async () => {\n    const appVersion = readVersion();\n    const javaPath = path.join(__dirname, '../..', `./versions/${appVersion}`, `./static/${JAVA_APP_NAME}`);\n    const libPath = path.join(__dirname, '../..', `./versions/${appVersion}`, './static/lib');\n\n    const productName = await ipcRenderer.invoke('get-product-name');\n\n    const isTest = productName.match(/test$/i) !== null;\n\n    console.log('productName:', productName, isTest);\n\n    const child = spawn(path.join(__dirname, '../..', `./static/${JAVA_PATH}`), [\n      '-noverify',\n      `-Dspring.profiles.active=${isTest ? 'test' : 'release'}`,\n      '-Dserver.address=127.0.0.1',\n      '-Dchat2db.mode=DESKTOP',\n      `-Dproject.path=${javaPath}`,\n      `-Dloader.path=${libPath}`,\n      `-Dclient.version=${appVersion}`,\n      '-Xmx1024M',\n      '-jar',\n      javaPath,\n    ]);\n\n    child.stdout.on('data', (buffer) => {\n      console.log(buffer.toString('utf8'));\n      const data = buffer.toString('utf8');\n      if (data.toString().indexOf('Started Application') !== -1) {\n        console.log('load success');\n      }\n    });\n    child.stderr.on('data', (data) => {\n      console.error(`stderr: ${data}`);\n    });\n    child.on('close', (code) => {\n      console.log(`child process exited with code ${code}`);\n    });\n  },\n  quitApp: () => {\n    ipcRenderer.send('quit-app');\n  },\n  setBaseURL: (baseUrl) => {\n    ipcRenderer.send('set-base-url', baseUrl);\n  },\n  setForceQuitCode: (code) => {\n    ipcRenderer.send('set-force-quit-code', !code);\n  },\n  registerAppMenu: (menuProps) => {\n    ipcRenderer.send('register-app-menu', menuProps);\n  },\n  setMaximize: () => {\n    ipcRenderer.send('set-maximize');\n  },\n  // 获取当前窗口是否是最大化\n  isMaximized: () => {\n    ipcRenderer.send('is-maximized');\n  },\n  closeWindow: () => {\n    ipcRenderer.send('close-window');\n  },\n  // 最小化窗口\n  minimizeWindow: () => {\n    ipcRenderer.send('minimize-window');\n  },\n  // 获取环境是mac还是windows还是linux\n  getPlatform: () => {\n    return {\n      isLinux,\n      isWin,\n      isMac,\n    };\n  },\n});\n"
  },
  {
    "path": "chat2db-client/src/main/store.js",
    "content": "const Store = require('electron-store');\n\nconst store = new Store();\n\nmodule.exports = store;\n"
  },
  {
    "path": "chat2db-client/src/main/utils.js",
    "content": "const { DEV_WEB_URL } = require('./constants');\nconst path = require('path');\nconst url = require('url');\nconst fs = require('fs');\n\nconst isLinux = process.platform == 'linux';\nconst isWin = process.platform == 'win32';\nconst isMac = process.platform == 'darwin';\nconst isProd = process.env.NODE_ENV == 'production';\n\n/**\n * 加载主进程前端资源\n * @param {*} mainWindow\n */\nfunction loadMainResource(mainWindow) {\n  if (process.env.NODE_ENV === 'development') {\n    mainWindow.loadURL(DEV_WEB_URL);\n    mainWindow.webContents.openDevTools();\n    // 监听应用程序根路径下的所有文件，当文件发生修改时，自动刷新应用程序\n    // require('electron-reload')(path.join(__dirname, '..'));\n  } else {\n    mainWindow.loadURL(\n      url.format({\n        pathname: path.join(__dirname, '../..', `./versions/${readVersion()}`, `./dist/index.html`),\n        protocol: 'file:',\n        slashes: true,\n      }),\n    );\n  }\n}\n\n/**\n * 应该读取哪个版本的资源\n * @param {*}\n */\nfunction readVersion() {\n  let version = '';\n  if (process.env.NODE_ENV !== 'development') {\n    version = fs.readFileSync(path.join(__dirname, '../..', './versions/version'));\n  }\n  return version.toString().trim();\n}\n\nmodule.exports = {\n  loadMainResource,\n  readVersion,\n  isLinux,\n  isWin,\n  isMac,\n  isProd,\n};\n"
  },
  {
    "path": "chat2db-client/src/main/webpack.config.js",
    "content": "const path = require('path');\n\nmodule.exports = {\n  entry: './index.js',\n  output: {\n    filename: 'main.js',\n    path: path.resolve(__dirname),\n  },\n  target: 'electron-main',\n};\n"
  },
  {
    "path": "chat2db-client/src/pages/demo/index.less",
    "content": ".introduce{\n  height: 18px;\n}"
  },
  {
    "path": "chat2db-client/src/pages/demo/index.tsx",
    "content": "import React, { useEffect } from 'react';\nimport sqlService from '@/service/sql';\n\nfunction Test() {\n  const sql = \"INSERT INTO `big_data_table` (`name1`,`name2`,`name3`,`name4`,`name5`,`name6`,`name7`,`name8`,`name9`,`name10`,`name11`,`name21`,`name31`,`name41`,`name51`,`name61`,`name71`,`name81`,`name91`,`name12`,`name22`,`name32`,`name42`,`name52`,`name62`,`name72`,`name82`,`name92`,`name13`,`name223`,`name323`,`name423`,`name523`,`name623`,`name723`,`name823`,`name923`) VALUES ('牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛','牛牛牛牛牛牛牛牛牛牛牛牛');\";\n  const sqls:any = [];\n  for (let i = 30; i < 1030; i++) {\n    sqls.push(sql)\n  }\n  const a = () =>{\n    const executeSQLParams = {\n      sql: sqls.join(''),\n      dataSourceId:2,\n      databaseName: \"e-commerc\",\n    };\n    // 获取当前SQL的查询结果\n    return sqlService.executeSql(executeSQLParams).finally(()=>{\n      a()\n    });\n  }\n  useEffect(() => {\n    a()\n  },[])\n  return 11111;\n}\n\nexport default Test;\n"
  },
  {
    "path": "chat2db-client/src/pages/document.ejs",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\" />\n  <link rel=\"icon\" type=\"image/ico\" sizes=\"32x32\" href=\"../assets/logo/logo.png\">\n  <title>Chat2DB</title>\n  <meta name=\"description\"\n    content=\"Chat2DB 是面向开发人员的免费多平台数据库工具。多种数据库一个工具。它用于查询、创建和管理数据库，数据库可以在本地、服务器或云端。支持 MySQL、PostgreSQL、Microsoft SQL Server、Oracle、H2等，未来我们会不断完善其他非关系型数据的支持，如Redis。\">\n  <meta name=\"keywords\" content=\"数据库，chatgpt，chat，DB，database，后端，程序员，数据库\">\n  <meta property=\"og:title\" content=\"Chat2DB\" />\n  <meta property=\"og:description\"\n    content=\"Chat2DB 是面向开发人员的免费多平台数据库工具。多种数据库一个工具。它用于查询、创建和管理数据库，数据库可以在本地、服务器或云端。支持 MySQL、PostgreSQL、Microsoft SQL Server、Oracle、H2等，未来我们会不断完善其他非关系型数据的支持，如Redis。\" />\n  <meta name=\"viewport\"\n    content=\"width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no\" />\n  <script src=\"./static/front/umi.js\"></script>\n</head>\n\n<body>\n  <div id=\"root\"></div>\n  <script>\n    console.log('run startServer: window.myAPI', window.myAPI);\n    if (window.myAPI) {\n      window.myAPI.startServerForSpawn();\n    }\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "chat2db-client/src/pages/login/index.less",
    "content": ".loginPage {\n  width: 100vw;\n  height: 100vh;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: url('../../assets/img/login-bg.svg');\n  background-size: cover;\n  background-position: center center;\n  background-repeat: no-repeat;\n}\n\n.logo {\n  position: fixed;\n  top: 32px;\n  left: 32px;\n  // app-region: 'no-drag';\n  display: flex;\n  align-items: center;\n\n  .logoImage {\n    width: 36px;\n    height: 36px;\n    margin-right: 12px;\n  }\n\n  .logoText {\n    color: var(--color-text);\n    font-weight: bold;\n    font-size: 20px;\n  }\n}\n\n.loginPlane {\n  width: 360px;\n  border: 1px solid var(--color-border);\n  box-shadow: rgb(22 14 45 / 2%) 0px 0px 40px, rgb(22 14 45 / 6%) 0px 0px 104px;\n  border-radius: 8px;\n  padding: 48px;\n  color: var(--color-text);\n  background-color: var(--color-bg-container);\n  margin-bottom: 48px;\n  .loginWelcome {\n    font-size: 24px;\n    text-align: center;\n  }\n  .whyLogin {\n    margin: 8px auto 0px;\n    text-align: center;\n    font-size: 12px;\n    line-height: 24px;\n    opacity: 0.4;\n    cursor: pointer;\n    max-width: fit-content;\n\n    &:hover {\n      opacity: 0.6;\n      font-size: 14px;\n      text-decoration: underline;\n    }\n  }\n}\n\n.defaultPasswordTips{\n  opacity: 0.3;\n  text-align: center;\n}\n\n.loginForm {\n  margin-top: 36px;\n  .loginFormSubmit {\n    margin-top: 24px;\n    width: 100%;\n  }\n}\n\n.setting {\n  position: fixed;\n  bottom: 32px;\n  left: 32px;\n  height: auto;\n  width: auto;\n\n  .settingBtn {\n    padding: 0px 12px;\n    font-size: 14px;\n    color: var(--color-text);\n    opacity: 0.8;\n    &:hover {\n      opacity: 1;\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/login/index.tsx",
    "content": "import React, { useEffect } from 'react';\nimport { Button, Form, Input, Tooltip } from 'antd';\nimport { userLogin } from '@/service/user';\nimport LogoImg from '@/assets/logo/logo.png';\nimport styles from './index.less';\nimport Setting from '@/blocks/Setting';\nimport Iconfont from '@/components/Iconfont';\nimport i18n from '@/i18n';\n// import { useNavigate } from 'react-router-dom';\nimport { logoutClearSomeLocalStorage, navigate } from '@/utils';\nimport { queryCurUser } from '@/store/user';\n\ninterface IFormData {\n  userName: string;\n  password: string;\n}\n\nconst Login: React.FC = () => {\n  useEffect(() => {\n    logoutClearSomeLocalStorage();\n  }, []);\n\n  const handleLogin = async (formData: IFormData) => {\n    const token = await userLogin(formData);\n    const res = await queryCurUser();\n    if (token && res) {\n      navigate('/');\n    }\n  };\n\n  return (\n    <div className={styles.loginPage}>\n      <div className={styles.logo}>\n        <img className={styles.logoImage} src={LogoImg} />\n        <div className={styles.logoText}>Chat2DB</div>\n      </div>\n      <div className={styles.loginPlane}>\n        <div className={styles.loginWelcome}>{i18n('login.text.welcome')}</div>\n        <Tooltip\n          placement=\"right\"\n          color={window._AppThemePack?.colorBgBase}\n          title={\n            <div style={{ color: window._AppThemePack?.colorText, opacity: 0.8, padding: '8px 4px' }}>\n              {i18n('login.text.tips')}\n            </div>\n          }\n        >\n          <div className={styles.whyLogin}>{i18n('login.text.tips.title')}</div>\n        </Tooltip>\n\n        <Form className={styles.loginForm} size=\"large\" onFinish={handleLogin}>\n          <Form.Item\n            className={styles.loginFormItem}\n            name=\"userName\"\n            rules={[{ required: true, message: i18n('login.form.user.placeholder') }]}\n          >\n            <Input autoComplete=\"off\" placeholder={i18n('login.form.user')} />\n          </Form.Item>\n          <Form.Item name=\"password\" rules={[{ required: true, message: i18n('login.form.password.placeholder') }]}>\n            <Input.Password placeholder={i18n('login.form.password')} />\n          </Form.Item>\n          <div className={styles.defaultPasswordTips}>{i18n('login.tips.defaultPassword')}</div>\n          <Button type=\"primary\" htmlType=\"submit\" className={styles.loginFormSubmit}>\n            {i18n('login.button.login')}\n          </Button>\n        </Form>\n      </div>\n\n      <Setting\n        className={styles.setting}\n        noLogin\n        render={\n          <Button\n            type=\"text\"\n            icon={<Iconfont style={{ fontSize: '14px' }} code=\"&#xe630;\" />}\n            className={styles.settingBtn}\n          >\n            {i18n('login.text.setting')}\n          </Button>\n        }\n      />\n    </div>\n  );\n};\n\nexport default Login;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/connection/index.less",
    "content": "@import '../../../styles/var.less';\n\n.box {\n  display: flex;\n  background-color: var(--color-bg);\n  width: 100%;\n  height: 100%;\n}\n\n.layoutLeft {\n  flex-shrink: 0;\n  display: flex;\n  flex-direction: column;\n  width: 220px;\n  overflow: hidden;\n  background-color: var(--color-bg-subtle);\n  border-right: 1px solid var(--color-border-secondary);\n  border-top: 0px;\n  border-bottom: 0px;\n}\n\n.pageTitle {\n  font-size: 20px;\n  line-height: 24px;\n  font-weight: 500;\n  margin: 20px 0px 10px;\n  padding-left: 20px;\n}\n\n.menuBox {\n  flex: 1;\n  overflow-y: auto;\n  padding: 0px 8px;\n\n  .menuItem {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    cursor: pointer;\n    padding: 8px;\n    margin-bottom: 4px;\n    height: 20px;\n    border-radius: 8px;\n    user-select: none;\n\n    .menuItemsTitle {\n      flex: 1;\n      width: 0;\n      display: flex;\n      align-items: center;\n\n      .name {\n        .f-single-line();\n      }\n\n      .envTag {\n        flex-shrink: 0;\n        width: 8px;\n        height: 8px;\n        border-radius: 50%;\n        background-color: var(--color-primary);\n        margin-right: 8px;\n      }\n\n      .databaseTypeIcon {\n        margin-right: 6px;\n      }\n    }\n\n    .moreButton {\n      flex-shrink: 0;\n      display: none;\n      transform: rotate(90deg);\n    }\n\n    &:hover {\n      background-color: var(--color-hover-bg);\n\n      .moreButton {\n        display: block;\n      }\n    }\n  }\n\n  .menuItemIcon {\n    color: var(--color-primary) !important;\n  }\n\n\n  .menuItemActive {\n    color: var(--color-primary);\n    // background-color: var(--color-primary-bg);\n    background-color: var(--color-hover-bg);\n  }\n\n  :global {\n    .ant-menu-inline {\n      border-inline-end: none !important;\n    }\n\n    .ant-menu-item {\n      padding-left: 14px !important;\n    }\n  }\n}\n\n.addConnection {\n  margin: 0 20px 10px;\n}\n\n.layoutRight {\n  flex: 1;\n  overflow: auto;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  position: relative;\n}\n\n.dataBaseList {\n  // display: flex;\n  // justify-content: space-between;\n  // flex-wrap: wrap;\n  // max-width: 800px;\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 10px;\n}\n@media (max-width: 1000px) {\n  .dataBaseList {\n    grid-template-columns: repeat(2, 1fr);\n  }\n}\n\n.databaseItem {\n  min-width: 220px;\n  border-radius: 4px;\n  height: 50px;\n  margin: 10px 20px;\n  padding: 0px 16px;\n  border-radius: 8px;\n  overflow: hidden;\n  box-sizing: border-box;\n  border: 1px solid var(--color-border);\n\n  .databaseItemMain {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    height: 50px;\n    border-radius: 8px;\n  }\n\n  .databaseItemLeft {\n    display: flex;\n    align-items: center;\n  }\n\n  .databaseItemRight {\n    display: none;\n\n    i {\n      font-size: 16px;\n    }\n  }\n\n  &:hover {\n    background-color: var(--color-bg-medium);\n    color: var(--color-primary);\n    border: 1px solid var(--color-primary);\n    cursor: pointer;\n\n    .databaseItemRight {\n      display: block;\n\n      i {\n        color: var(--color-primary);\n      }\n    }\n  }\n\n  .logoBox {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    height: 28px;\n    width: 28px;\n    border-radius: 8px;\n    margin-right: 16px;\n\n    i {\n      font-size: 16px;\n    }\n  }\n}\n\n.databaseItemSpacer {\n  flex-grow: 1;\n  width: 210px;\n  margin: 0px 20px;\n  padding: 0px 16px;\n  box-sizing: border-box;\n}\n\n.createConnections {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  z-index: -1;\n  background-color: var(--color-bg);\n  overflow-y: auto;\n  transform: scale(0.2);\n  transition: 0.1s ease-in-out;\n}\n\n.showCreateConnections {\n  z-index: 1;\n  transform: scale(1);\n  transition: transform 0.3s ease-in-out;\n}\n\n.envLabel {\n  display: flex;\n  justify-content: space-between;\n  line-height: 30px;\n  padding: 4px;\n  font-size: 14px;\n}\n\n.envRefreshBox {\n  &:hover {\n    cursor: pointer;\n    color: var(--color-primary);\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/connection/index.tsx",
    "content": "import React, { useRef, useState, Fragment, useEffect } from 'react';\nimport { Button, Dropdown } from 'antd';\nimport classnames from 'classnames';\nimport i18n from '@/i18n';\n// import RefreshLoadingButton from '@/components/RefreshLoadingButton';\n\n// ----- services -----\nimport connectionService from '@/service/connection';\n\n// ----- constants/typings -----\nimport { databaseMap } from '@/constants';\nimport { IConnectionDetails, IConnectionListItem } from '@/typings';\n\n// ----- components -----\nimport CreateConnection from '@/blocks/CreateConnection';\nimport Iconfont from '@/components/Iconfont';\nimport LoadingContent from '@/components/Loading/LoadingContent';\nimport MenuLabel from '@/components/MenuLabel';\n\n// ----- hooks -----\nimport useClickAndDoubleClick from '@/hooks/useClickAndDoubleClick';\n\n// ----- store -----\nimport { useConnectionStore, getConnectionList } from '@/pages/main/store/connection';\nimport { setMainPageActiveTab } from '@/pages/main/store/main';\nimport { setCurrentConnectionDetails } from '@/pages/main/workspace/store/common';\nimport { getOpenConsoleList } from '@/pages/main/workspace/store/console';\n\nimport styles from './index.less';\n\nconst ConnectionsPage = () => {\n  const { connectionList } = useConnectionStore((state) => {\n    return {\n      connectionList: state.connectionList,\n    };\n  });\n  const volatileRef = useRef<any>();\n  const [connectionActiveId, setConnectionActiveId] = useState<IConnectionListItem['id'] | null>(null);\n  const [connectionDetail, setConnectionDetail] = useState<IConnectionDetails | null | undefined>(null);\n\n  // 处理列表单击事件\n  const handleMenuItemSingleClick = (t: IConnectionListItem) => {\n    if (connectionActiveId !== t.id) {\n      setConnectionActiveId(t.id);\n    }\n  };\n\n  // 处理列表双击事件\n  const handleMenuItemDoubleClick = (t: IConnectionListItem) => {\n    setCurrentConnectionDetails(t);\n    setMainPageActiveTab('workspace');\n  };\n\n  // 处理列表单击和双击事件\n  const handleClickConnectionMenu = useClickAndDoubleClick(handleMenuItemSingleClick, handleMenuItemDoubleClick);\n\n  // 切换连接的详情\n  useEffect(() => {\n    if (!connectionActiveId) {\n      return;\n    }\n    setConnectionDetail(undefined);\n    connectionService\n      .getDetails({ id: connectionActiveId })\n      .then((res) => {\n        setConnectionDetail(res);\n      })\n      .catch(() => {\n        setConnectionActiveId(null);\n      });\n  }, [connectionActiveId]);\n\n  //\n  const createDropdownItems = (t) => {\n    const handelDelete = (e) => {\n      // 禁止冒泡到menuItem\n      e.domEvent?.stopPropagation?.();\n      connectionService.remove({ id: t.id }).then(() => {\n        getConnectionList().then(() => {\n          // 连接删除后需要更新下 consoleList\n          getOpenConsoleList();\n        });\n        if (connectionActiveId === t.id) {\n          setConnectionActiveId(null);\n          setConnectionDetail(null);\n        }\n      });\n    };\n\n    const enterWorkSpace = (e) => {\n      e.domEvent?.stopPropagation?.();\n      handleMenuItemDoubleClick(t);\n    };\n\n    const copyConnection = (e) => {\n      e.domEvent?.stopPropagation?.();\n      connectionService.clone({ id: t.id }).then((res) => {\n        getConnectionList();\n        setConnectionActiveId(res);\n      });\n    }\n\n    return [\n      {\n        key: 'enterWorkSpace',\n        label: <MenuLabel icon=\"&#xec57;\" label={i18n('connection.button.connect')} />,\n        onClick: enterWorkSpace,\n      },\n      {\n        key: 'copyConnection',\n        label: <MenuLabel icon=\"&#xec7a;\" label={i18n('common.button.copy')} />,\n        onClick: copyConnection,\n      },\n      {\n        key: 'delete',\n        label: <MenuLabel icon=\"&#xe6a7;\" label={i18n('connection.button.remove')} />,\n        onClick: handelDelete,\n      },\n    ];\n  };\n\n  const renderConnectionMenuList = () => {\n    return connectionList?.map((t) => {\n      return (\n        <Dropdown\n          key={t.id}\n          trigger={['contextMenu']}\n          menu={{\n            items: createDropdownItems(t),\n          }}\n        >\n          <div\n            className={classnames(styles.menuItem, {\n              [styles.menuItemActive]: connectionActiveId === t.id,\n            })}\n            onClick={() => {\n              handleClickConnectionMenu(t);\n            }}\n          >\n            <div className={classnames(styles.menuItemsTitle)}>\n              <span className={styles.envTag} style={{ background: t.environment.color.toLocaleLowerCase() }} />\n              <span className={styles.databaseTypeIcon}>\n                {<Iconfont className={styles.menuItemIcon} code={databaseMap[t.type]?.icon} />}\n              </span>\n              <span className={styles.name}>{t.alias}</span>\n              {/* <Tag color={t.environment.color.toLocaleLowerCase()}>\n              {t.environment.shortName}\n            </Tag> */}\n            </div>\n          </div>\n        </Dropdown>\n      );\n    });\n  };\n\n  const onSubmit = (data) => {\n    return connectionService\n      .save({\n        ...data,\n      })\n      .then((res) => {\n        getConnectionList();\n        setConnectionActiveId(res);\n      });\n  };\n\n  return (\n    <>\n      <div className={styles.box}>\n        <div ref={volatileRef} className={styles.layoutLeft}>\n          <div className={styles.pageTitle}>{i18n('connection.title.connections')}</div>\n          <div className={styles.menuBox}>{renderConnectionMenuList()}</div>\n          {connectionActiveId && (\n            <Button\n              type=\"primary\"\n              className={styles.addConnection}\n              onClick={() => {\n                setConnectionActiveId(null);\n                setConnectionDetail(null);\n              }}\n            >\n              {i18n('connection.button.addConnection')}\n            </Button>\n          )}\n        </div>\n        <LoadingContent\n          className={styles.layoutRight}\n          isLoading={connectionDetail === undefined && !!connectionActiveId}\n        >\n          <CreateConnection connectionDetail={connectionDetail} onSubmit={onSubmit} />\n        </LoadingContent>\n      </div>\n    </>\n  );\n};\n\nexport default ConnectionsPage;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/chart/bar/index.less",
    "content": ""
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/chart/bar/index.tsx",
    "content": "import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react';\nimport * as charts from 'echarts';\nimport ReactEcharts from 'echarts-for-react';\nimport './index.less';\ntype EChartsOption = charts.EChartsOption;\n\ninterface IProps {\n  data?: {\n    xAxis: string[];\n    yAxis: any[];\n  };\n}\n\nconst BarChart = (props: IProps, ref) => {\n  const barRef = useRef<any>(null);\n\n  const option: EChartsOption = useMemo(\n    () => ({\n      xAxis: {\n        type: 'category',\n        data: props?.data?.xAxis ?? [],\n      },\n      yAxis: {\n        type: 'value',\n      },\n      series: [\n        {\n          data: props?.data?.yAxis ?? [],\n          type: 'bar',\n        },\n      ],\n    }),\n    [props.data],\n  );\n\n  useImperativeHandle(ref, () => ({\n    getEchartsInstance: () => barRef.current.getEchartsInstance(),\n  }));\n  return <ReactEcharts  ref={barRef} option={option} opts={{ renderer: 'svg' }} />;\n};\n\nexport default forwardRef(BarChart);\n"
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/chart/line/index.less",
    "content": ""
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/chart/line/index.tsx",
    "content": "import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react';\nimport * as charts from 'echarts';\nimport ReactEcharts from 'echarts-for-react';\nimport './index.less';\ntype EChartsOption = charts.EChartsOption;\n\ninterface IProps {\n  data: {\n    xAxis: string[];\n    yAxis: any[];\n  };\n}\n\nconst LineChart = (props: IProps, ref) => {\n  const lineRef = useRef<any>(null);\n  const option: EChartsOption = useMemo(\n    () => ({\n      xAxis: {\n        type: 'category',\n        data: props?.data?.xAxis ?? [],\n      },\n      yAxis: {\n        type: 'value',\n      },\n      series: [\n        {\n          data: props?.data?.yAxis ?? [],\n          type: 'line',\n        },\n      ],\n      tooltip: {\n        trigger: 'axis',\n      },\n    }),\n    [props.data],\n  );\n\n  useImperativeHandle(ref, () => ({\n    getEchartsInstance: () => lineRef.current.getEchartsInstance(),\n  }));\n\n  return <ReactEcharts ref={lineRef} option={option} opts={{ renderer: 'svg' }} />;\n};\n\nexport default forwardRef(LineChart);\n"
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/chart/pie/index.less",
    "content": ""
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/chart/pie/index.tsx",
    "content": "import React, { ForwardedRef, forwardRef, useImperativeHandle, useMemo, useRef } from 'react';\nimport * as charts from 'echarts';\nimport ReactEcharts from 'echarts-for-react';\nimport './index.less';\ntype EChartsOption = charts.EChartsOption;\n\ninterface IProps {\n  data: Array<{ value: number; name: string }>;\n}\n\nconst PieChart = (props: IProps, ref: ForwardedRef<{ getEchartsInstance: Function }>) => {\n  const pieRef = useRef<any>(null);\n\n  const option: EChartsOption = useMemo(\n    () => ({\n      tooltip: {\n        trigger: 'item',\n      },\n      legend: {\n        orient: 'horizontal',\n        align: 'auto',\n        type: 'scroll', //分页类型\n      },\n\n      series: [\n        {\n          type: 'pie',\n          radius: ['40%', '70%'],\n          data: props.data,\n          // label: {\n          //   show: false,\n          //   position: 'center'\n          // },\n          emphasis: {\n            label: {\n              show: true,\n              fontSize: 16,\n              fontWeight: 'bold',\n            },\n          },\n        },\n      ],\n    }),\n    [props.data],\n  );\n\n  useImperativeHandle(ref, () => ({\n    getEchartsInstance: () => pieRef.current.getEchartsInstance(),\n  }));\n\n  return <ReactEcharts ref={pieRef} option={option} opts={{ renderer: 'svg' }} />;\n};\n\nexport default forwardRef(PieChart);\n"
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/chart-item/index.less",
    "content": ".container {\n  flex: 1;\n  position: relative;\n  border: 1px solid var(--color-border);\n  border-radius: var(--border-radius-l-g);\n  padding: 16px;\n  background-color: var(--color-gray-200);\n  margin-bottom: 20px;\n}\n\n.titleBar {\n  display: flex;\n  justify-content: space-between;\n  line-height: 20px;\n  font-size: 14px;\n}\n\n.edit {\n  cursor: pointer;\n}\n\n.left_overlay_add {\n  position: absolute;\n  top: 0;\n  left: -8px;\n  width: 16px;\n  height: 100%;\n  cursor: pointer;\n}\n\n.right_overlay_add {\n  position: absolute;\n  top: 0;\n  right: -8px;\n  width: 16px;\n  height: 100%;\n  cursor: pointer;\n}\n\n.top_overlay_add {\n  position: absolute;\n  left: 0;\n  top: -8px;\n  height: 16px;\n  width: 100%;\n  cursor: pointer;\n}\n\n.bottom_overlay_add {\n  position: absolute;\n  left: 0;\n  bottom: -8px;\n  height: 16px;\n  width: 100%;\n  cursor: pointer;\n}\n\n.left_overlay_add:hover .add_chart_icon,\n.right_overlay_add:hover .add_chart_icon,\n.top_overlay_add:hover .add_chart_icon,\n.bottom_overlay_add:hover .add_chart_icon {\n  opacity: 1;\n}\n\n.add_chart_icon {\n  opacity: 0;\n  width: 16px;\n  height: 16px;\n  background-color: var(--color-primary-400);\n  position: absolute;\n  left: 0;\n  top: 50%;\n  transform: translateY(-50%);\n  transition: opacity 0.3s;\n  border-radius: 999px;\n}\n\n.add_chart_icon_y {\n  left: 50%;\n  transform: translate(-50%, -50%);\n}\n\n.add_chart_plus_icon {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n  width: 16px;\n  height: 16px;\n  filter: var(--filter-color-gray-100);\n}\n\n.emptyChartBlock {\n  width: 100%;\n  padding: 20px 0;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n}\n\n.emptyDataImage {\n  font-size: 56px;\n  color: #666666;\n  margin-bottom: 16px;\n}\n\n.emptyDataText {\n  font-size: 12px;\n  color: var(--color-gray-300);\n  line-height: 14px;\n  margin-bottom: 40px;\n}\n\n.editBlock {\n  border-radius: 4px;\n  // background-color: var(--color-bg-subtle);\n  max-height: 1000px;\n}\n\n.editorBlock {\n  display: flex;\n  // flex-direction: column;\n  flex-wrap: wrap;\n}\n\n.editor {\n  flex: 2;\n  min-width: 320px;\n  min-height: 320px;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n  border: 1px solid var(--color-border-secondary);\n  overflow: hidden;\n}\n\n.dataSourceSelect {\n  position: absolute;\n  bottom: 8px;\n  right: 24px;\n}\n\n.chartParamsForm {\n  border: 1px solid var(--color-border);\n  border-left: 1px solid var(--color-border);\n  color: var(--color-text);\n  flex: 1;\n  min-width: 120px;\n  display: flex;\n  flex-direction: column;\n  justify-content: start;\n  align-items: start;\n  padding: 24px;\n}\n\n.chartParamsFormTitle {\n  font-size: 14px;\n  font-weight: bold;\n  line-height: 14px;\n  margin-bottom: 24px;\n}\n\n.editorOptionBlock {\n  display: flex;\n  justify-content: flex-end;\n  padding: 20px 0px 0px 0px;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/chart-item/index.tsx",
    "content": "import { IChartItem, IChartType, IConnectionDetails } from '@/typings';\nimport React, { useEffect, useRef, useState, useMemo } from 'react';\nimport styles from './index.less';\nimport addImage from '@/assets/img/add.svg';\nimport cs from 'classnames';\nimport Line from '../chart/line';\nimport Pie from '../chart/pie';\nimport Bar from '../chart/bar';\nimport { MoreOutlined } from '@ant-design/icons';\nimport { Button, Dropdown, Form, message, Select, Spin } from 'antd';\nimport { deleteChart, getChartById, updateChart } from '@/service/dashboard';\nimport ConsoleEditor from '@/components/ConsoleEditor';\nimport Iconfont from '@/components/Iconfont';\nimport sqlService, { IExecuteSqlParams } from '@/service/sql';\nimport { Option } from '@/typings/common';\nimport { handleDatabaseAndSchema } from '@/utils/database';\nimport i18n from '@/i18n';\nimport { isValid } from '@/utils/check';\n\nconst handleSQLResult2ChartData = (data) => {\n  const { headerList, dataList } = data;\n  const mockData = headerList?.reduce((acc, cur, index) => {\n    acc[cur.name] = {\n      ...cur,\n      data: dataList?.map((i: any) => i[index]),\n    };\n    return acc;\n  }, {});\n  return mockData;\n};\n\nfunction countArrayElements<T>(arr: T[]): { name: T; value: number }[] {\n  const counts = new Map<T, number>();\n  // 统计每个元素出现的次数\n  arr.forEach((item) => {\n    if (counts.has(item)) {\n      counts.set(item, counts.get(item)! + 1);\n    } else {\n      counts.set(item, 1);\n    }\n  });\n  // 转换为数组形式\n  const result: { name: T; value: number }[] = [];\n  for (const [key, value] of counts.entries()) {\n    result.push({ name: key, value });\n  }\n  return result;\n}\n\ninterface IChartItemProps {\n  id: number;\n  isEditing?: boolean;\n  canAddRowItem: boolean;\n  connectionList: any[];\n  remainingUse: any;\n  onDelete?: (id: number) => void;\n  addChartTop?: () => void;\n  addChartBottom?: () => void;\n  addChartLeft?: () => void;\n  addChartRight?: () => void;\n}\n\nfunction ChartItem(props: IChartItemProps) {\n  const { connectionList, id } = props;\n  const [cascaderOption, setCascaderOption] = useState<Option[]>([]);\n  const [curConnection, setCurConnection] = useState<IConnectionDetails>();\n  const [chartData, setChartData] = useState<IChartItem>({});\n  const [chartMetaData, setChartMetaData] = useState<any>();\n  const [, setCascaderValue] = useState<(string | number)[]>([]);\n  const [isEditing, setIsEditing] = useState<boolean>(props.isEditing ?? false);\n  const [isLoading, setIsLoading] = useState(false);\n  const [initDDL, setInitDDL] = useState('');\n  const [form] = Form.useForm(); // 创建一个表单实例\n  const chartRef = useRef<any>();\n\n  useEffect(() => {\n    if (id !== undefined) {\n      queryChartData();\n    }\n  }, [id]);\n\n  useEffect(() => {\n    if (connectionList && connectionList.length > 0) {\n      setCurConnection(connectionList[0]);\n      setCascaderOption(\n        (connectionList || []).map((c) => ({\n          value: c.id,\n          label: c.alias,\n          isLeaf: false,\n        })),\n      );\n    }\n  }, [connectionList]);\n\n  useEffect(() => {\n    if (!curConnection) {\n      return;\n    }\n    setChartData({\n      ...chartData,\n      dataSourceId: curConnection.id,\n      type: curConnection.type,\n    });\n\n    queryDatabaseAndSchemaList(curConnection.id);\n  }, [curConnection]);\n\n  useEffect(() => {\n    handleChartConfigChange();\n  }, [chartData.sqlData]);\n\n  const queryDatabaseAndSchemaList = async (dataSourceId: number) => {\n    const res = await sqlService.getDatabaseSchemaList({ dataSourceId });\n    const dataSource = (cascaderOption || []).find((c) => c.value === dataSourceId);\n    if (!dataSource) return;\n\n    dataSource.children = handleDatabaseAndSchema(res);\n    setCascaderOption([...cascaderOption]);\n  };\n\n  const handleExecuteSQL = async (sql: string, _chartData: IChartItem) => {\n    const { dataSourceId, databaseName, schemaName } = _chartData;\n    if (!isValid(dataSourceId)) {\n      message.success(i18n('dashboard.editor.execute.noDataSource'));\n      return;\n    }\n    setIsLoading(true);\n    try {\n      const executeSQLParams: IExecuteSqlParams = {\n        sql,\n        dataSourceId,\n        databaseName,\n        schemaName\n      };\n      // 获取当前SQL的查询结果\n      const sqlResult = await sqlService.executeSql(executeSQLParams);\n\n      let sqlData;\n      if (sqlResult && sqlResult[0]) {\n        sqlData = handleSQLResult2ChartData(sqlResult[0]);\n      }\n      setChartData({\n        ..._chartData,\n        ddl: sql,\n        sqlData,\n      });\n      message.success(i18n('dashboard.editor.execute.success'));\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  /** 根据id请求Chart数据 */\n  const queryChartData = async () => {\n    setIsLoading(true);\n\n    const res = await getChartById({ id: props.id });\n    if (!res.dataSourceId) {\n      res.connectable = undefined;\n    }\n    setChartData(res);\n\n    // 设置级联value\n    const cascaderKey = ['dataSourceId', 'databaseName', 'schemaName'];\n    const _cascaderValue = cascaderKey.map((k: string) => res[k]).filter((i) => !!i);\n    setCascaderValue(_cascaderValue);\n\n    // 设置Chart参数，eg ChartType、xAxis、yAxis\n    const formValue = JSON.parse(res.schema || '{}');\n    form.setFieldsValue(formValue);\n\n    if (res.ddl && res.connectable) {\n      setInitDDL(res.ddl);\n      handleExecuteSQL(res.ddl, res);\n      // let p: IExecuteSqlParams = {\n      //   sql: res?.ddl ?? '',\n      //   dataSourceId,\n      //   databaseName,\n      // };\n      // sqlService.executeSql(p).then((result) => {\n      //   let sqlData;\n      //   if (result && result[0]) {\n      //     sqlData = handleSQLResult2ChartData(result[0]);\n      //   }\n\n      //   setChartData({\n      //     ...res,\n      //     ...chartData,\n      //     sqlData,\n      //   });\n      // });\n    }\n    setIsLoading(false);\n  };\n\n  const onExport2Image = () => {\n    const echartInstance = chartRef.current.getEchartsInstance();\n    const img = new Image();\n    img.src = echartInstance.getDataURL({\n      type: 'png',\n      devicePixelRatio: 4,\n      backgroundColor: '#FFF',\n    });\n    img.onload = function () {\n      const canvas = document.createElement('canvas');\n      canvas.width = img.width;\n      canvas.height = img.height;\n      const ctx = canvas.getContext('2d');\n      ctx?.drawImage(img, 0, 0);\n      const dataURL = canvas.toDataURL('image/png');\n\n      const a = document.createElement('a');\n      const event = new MouseEvent('click');\n      a.download = 'image.png';\n      a.href = dataURL;\n      a.dispatchEvent(event);\n    };\n  };\n\n  const onDeleteChart = () => {\n    deleteChart({ id: props.id });\n    props.onDelete && props.onDelete(props.id);\n  };\n\n  const handleSaveChart = async () => {\n    const params: IChartItem = {\n      id: props.id,\n      ...chartData,\n      schema: JSON.stringify(form.getFieldsValue(true)),\n    };\n    await updateChart(params);\n    setIsEditing(false);\n    message.success(i18n('common.tips.saveSuccessfully'));\n  };\n\n  const handleChartConfigChange = () => {\n    const { sqlData = {} } = chartData || {};\n    const { chartType, xAxis, yAxis } = form.getFieldsValue(true);\n    // let xAxisOptions: Array<{ label: string; value: string }> = [];\n    // let yAxisOptions: Array<{ label: string; value: string }> = [];\n\n    if (chartType === IChartType.Pie) {\n      const dimension = sqlData[xAxis];\n      const { data = [] } = dimension || {};\n      const finallyData = countArrayElements(data);\n      setChartMetaData(finallyData);\n    } else if (chartType === IChartType.Line) {\n      const dimensionX = sqlData[xAxis]?.data;\n      const dimensionY = sqlData[yAxis]?.data;\n      setChartMetaData({\n        xAxis: dimensionX,\n        yAxis: dimensionY,\n      });\n    } else if (chartType === IChartType.Column) {\n      const dimensionX = sqlData[xAxis]?.data;\n      const dimensionY = sqlData[yAxis]?.data;\n      setChartMetaData({\n        xAxis: dimensionX,\n        yAxis: dimensionY,\n      });\n    }\n  };\n\n  const renderPlusIcon = () => {\n    return (\n      <>\n        {props.canAddRowItem && (\n          <div onClick={props.addChartLeft} className={styles.left_overlay_add}>\n            <div className={styles.add_chart_icon}>\n              <img className={styles.add_chart_plus_icon} src={addImage} alt=\"Add chart\" />\n            </div>\n          </div>\n        )}\n        {props.canAddRowItem && (\n          <div onClick={props.addChartRight} className={styles.right_overlay_add}>\n            <div className={styles.add_chart_icon}>\n              <img className={styles.add_chart_plus_icon} src={addImage} alt=\"Add chart\" />\n            </div>\n          </div>\n        )}\n        <div onClick={props.addChartTop} className={styles.top_overlay_add}>\n          <div className={cs(styles.add_chart_icon, styles.add_chart_icon_y)}>\n            <img className={styles.add_chart_plus_icon} src={addImage} alt=\"Add chart\" />\n          </div>\n        </div>\n        <div onClick={props.addChartBottom} className={styles.bottom_overlay_add}>\n          <div className={cs(styles.add_chart_icon, styles.add_chart_icon_y)}>\n            <img className={styles.add_chart_plus_icon} src={addImage} alt=\"Add chart\" />\n          </div>\n        </div>\n      </>\n    );\n  };\n\n  const renderChart = () => {\n    // const { chartType } = chartData || {};\n    // const { chartType } = JSON.parse(schema) || {};\n    const { chartType } = form.getFieldsValue(true);\n    switch (chartType) {\n      case IChartType.Pie:\n        return <Pie ref={chartRef} data={chartMetaData} />;\n      case IChartType.Line:\n        return <Line ref={chartRef} data={chartMetaData} />;\n      case IChartType.Column:\n        return <Bar ref={chartRef} data={chartMetaData} />;\n      default:\n        return (\n          <div style={{ height: '120px', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>\n            <Iconfont code=\"&#xe638;\" className={styles.emptyDataImage} />\n          </div>\n        );\n    }\n  };\n\n  const renderEmptyBlock = () => {\n    return (\n      <div className={styles.emptyChartBlock}>\n        <Iconfont code=\"&#xe638;\" className={styles.emptyDataImage} />\n\n        <div className={styles.emptyDataText}>No data selected</div>\n        <Button\n          type=\"primary\"\n          onClick={() => {\n            setIsEditing(true);\n          }}\n        >\n          Add Data\n        </Button>\n      </div>\n    );\n  };\n\n  const initDDLMemo = useMemo(() => {\n    return {\n      text: initDDL,\n      range: 'front',\n    };\n  }, [initDDL]);\n\n  const setBoundInfo = (boundInfo) => {\n    setChartData({\n      ...chartData,\n      ...boundInfo,\n    });\n  };\n\n  const renderEditorBlock = () => {\n    const { sqlData = {} } = chartData || {};\n    const options = Object.keys(sqlData).map((i) => ({ label: i, value: i }));\n\n    return (\n      <div className={styles.editBlock}>\n        <div className={styles.editorBlock}>\n          <div className={styles.editor}>\n            <ConsoleEditor\n              defaultValue={initDDLMemo.text}\n              boundInfo={chartData as any}\n              setBoundInfo={setBoundInfo}\n              hasAiChat={true}\n              hasAi2Lang={false}\n              hasSaveBtn={false}\n              value={chartData?.ddl}\n              onExecuteSQL={(sql: string) => handleExecuteSQL(sql, chartData)}\n              editorOptions={{\n                lineNumbers: 'off',\n              }}\n              isActive={true}\n            />\n            {/* <Cascader\n              options={cascaderOption}\n              value={cascaderValue}\n              loadData={loadData}\n              onChange={(value, selectedOptions) => {\n                const p: any = {\n                  dataSourceId: '',\n                };\n                //包含了dataSourceId、databaseName、schemaName\n                (selectedOptions || []).forEach((o: any) => {\n                  if (o.type) {\n                    p[`${o.type}Name`] = o.value;\n                  } else {\n                    p.dataSourceId = o.value;\n                  }\n                });\n                setCascaderValue(value);\n                setChartData({\n                  ...chartData,\n                  ...p,\n                });\n              }}\n              className={styles.dataSourceSelect}\n              placeholder={i18n('dashboard.editor.cascader.placeholder')}\n              // style={{ width: '100%' }}\n            /> */}\n          </div>\n          <div className={styles.chartParamsForm}>\n            <div className={styles.chartParamsFormTitle}>Charts:</div>\n            <Form\n              form={form}\n              // labelCol={{ span: 24 }}\n              // wrapperCol={{ span: 24 }}\n              layout=\"vertical\"\n              autoComplete=\"off\"\n              onValuesChange={handleChartConfigChange}\n              style={{ width: '100%' }}\n            >\n              <Form.Item label={'Chart Type'} name={'chartType'}>\n                <Select\n                  options={[\n                    { label: 'Line', value: 'Line' },\n                    { label: 'Pie', value: 'Pie' },\n                    { label: 'Column', value: 'Column' },\n                  ]}\n                />\n              </Form.Item>\n              <Form.Item label={'xAxis'} name={'xAxis'}>\n                <Select options={options} />\n              </Form.Item>\n              <Form.Item label={'yAxis'} name={'yAxis'} hidden={form.getFieldValue('chartType') === IChartType.Pie}>\n                <Select options={options} />\n              </Form.Item>\n            </Form>\n          </div>\n        </div>\n        <div className={styles.editorOptionBlock}>\n          <Button type=\"primary\" style={{ marginRight: '8px' }} onClick={handleSaveChart}>\n            {i18n('common.button.confirm')}\n          </Button>\n          <Button\n            onClick={() => {\n              setIsEditing(false);\n            }}\n          >\n            {i18n('common.button.cancel')}\n          </Button>\n        </div>\n      </div>\n    );\n  };\n\n  return (\n    <Spin spinning={isLoading}>\n      <div className={styles.container}>\n        {renderPlusIcon()}\n        <div className={styles.titleBar}>\n          <div className={styles.title}>{chartData?.name}</div>\n          <div>\n            <Dropdown\n              menu={{\n                items: [\n                  {\n                    key: 'Edit',\n                    label: i18n('dashboard.edit'),\n                    onClick: () => {\n                      setIsEditing(true);\n                    },\n                  },\n                  {\n                    key: 'Export',\n                    label: i18n('dashboard.export2image'),\n                    onClick: onExport2Image,\n                  },\n                  {\n                    key: 'delete',\n                    label: i18n('dashboard.delete'),\n                    onClick: onDeleteChart,\n                  },\n                ],\n              }}\n              placement=\"bottomLeft\"\n            >\n              <MoreOutlined className={styles.edit} />\n            </Dropdown>\n          </div>\n        </div>\n        {chartData?.sqlData || isEditing ? renderChart() : renderEmptyBlock()}\n        {isEditing && renderEditorBlock()}\n      </div>\n    </Spin>\n  );\n}\n\nexport default ChartItem;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/index.less",
    "content": "@import '../../../styles/var.less';\n\n.box {\n  display: flex;\n  background-color: var(--color-bg);\n  height: 100%;\n  width: 100%;\n}\n.dragBox {\n  width: 220px;\n  height: 100%;\n  overflow: hidden;\n  border-right: 1px solid var(--color-border-secondary);\n  border-top: 0px;\n  border-bottom: 0px;\n}\n\n.boxLeft {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  background-color: var(--color-bg-subtle);\n  padding: 10px 8px 0px;\n  box-sizing: border-box;\n  min-width: 200px;\n}\n\n.createDashboardBtn {\n  width: 100%;\n  // margin: 0 10px 20px 10px;\n  margin-bottom: 20px;\n  padding: 0 12px;\n}\n\n.boxLeftTitle {\n  font-size: 20px;\n  line-height: 24px;\n  font-weight: 500;\n  margin-bottom: 20px;\n  padding-left: 20px;\n  padding-right: 8px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.plusIcon {\n  font-size: 18px;\n  padding: 2px;\n  cursor: pointer;\n\n  &:hover {\n    background-color: var(--color-hover-bg);\n  }\n}\n\n.boxLeftItem {\n  font-size: 14px;\n  padding-left: 24px;\n  padding-right: 8px;\n  line-height: 36px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  border-radius: var(--border-radius-l-g);\n  margin-bottom: 4px;\n\n  .itemTitle {\n    font-weight: 400;\n    &:hover {\n      color: var(--color-primary);\n    }\n  }\n\n  .moreButton {\n    flex-shrink: 0;\n    display: none;\n    transform: rotate(90deg);\n  }\n\n  &:hover {\n    cursor: pointer;\n    background-color: var(--color-hover-bg);\n\n    .moreButton {\n      display: block;\n    }\n  }\n}\n\n.activeItem {\n  color: var(--color-primary);\n  background-color: var(--color-hover-bg);\n}\n\n.boxRight {\n  flex: 1;\n  padding: 24px 40px;\n  overflow-y: auto;\n  padding-bottom: 60px;\n}\n\n.boxRightTitle {\n  display: flex;\n  align-items: center;\n  font-weight: 500;\n  font-size: 20px;\n  line-height: 24px;\n  margin-bottom: 24px;\n}\n\n.boxRightContent {\n}\n\n.boxRightContentRow {\n  display: flex;\n}\n\n.boxRightContentColumn {\n  flex: 1;\n  margin-right: 20px;\n\n  &:last-child {\n    margin-right: 0px;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/index.tsx",
    "content": "import React, { useEffect, useState, useRef } from 'react';\nimport { Dropdown, Form, Input, Modal, message } from 'antd';\nimport cs from 'classnames';\nimport { IChartItem, IDashboardItem } from '@/typings';\nimport DraggableContainer from '@/components/DraggableContainer';\nimport Iconfont from '@/components/Iconfont';\nimport ChartItem from './chart-item';\n// import { ReactSortable, Store } from 'react-sortablejs';\nimport {\n  createChart,\n  createDashboard,\n  deleteDashboard,\n  getDashboardById,\n  getDashboardList,\n  updateDashboard,\n} from '@/service/dashboard';\nimport i18n from '@/i18n';\nimport styles from './index.less';\n// import { IConnectionModelState } from '@/models/connection';\n// import { IWorkspaceModelState } from '@/models/workspace';\n// import { IAIState } from '@/models/ai';\nimport { useConnectionStore } from '../store/connection';\nimport { useSettingStore } from '@/store/setting';\n\ninterface IProps {\n  className?: string;\n}\n\nexport const initChartItem: IChartItem = {\n  name: '',\n  description: '',\n  // chatType: IChartType.Line,\n  schema: '{\"chatType\":\"\",\"xAxis\":\"\",\"yAxis\":\"\"}',\n};\n\nfunction Chart(props: IProps) {\n  const { className } = props;\n  const [dashboardList, setDashboardList] = useState<IDashboardItem[]>([]);\n  const [curDashboard, setCurDashboard] = useState<IDashboardItem>();\n  const [openAddDashboard, setOpenAddDashboard] = useState(false);\n  const connectionList = useConnectionStore((state) => state.connectionList);\n  const remainingUse = useSettingStore((state) => state.remainingUse);\n\n  const [form] = Form.useForm(); // 创建一个表单实例\n  const [messageApi] = message.useMessage();\n  const draggableRef = useRef<any>();\n\n  useEffect(() => {\n    // 获取列表数据\n    queryDashboardList();\n  }, []);\n\n  useEffect(() => {\n    const { chartIds } = curDashboard || {};\n    if (!curDashboard) {\n      return;\n    }\n    if (!chartIds || !chartIds.length) {\n      initCreateChart(curDashboard);\n    }\n  }, [curDashboard]);\n\n  const queryDashboardList = async () => {\n    const res = await getDashboardList({});\n    const { data } = res;\n    if (Array.isArray(data) && data.length > 0) {\n      setDashboardList(data);\n      const _curDashboard = await getDashboardById({ id: data[0].id });\n      setCurDashboard(_curDashboard);\n    }\n  };\n\n  const initCreateChart = async (dashboard?: IDashboardItem) => {\n    if (!dashboard) return;\n\n    const chartId = await createChart({});\n    const newDashboard = {\n      ...dashboard,\n      schema: JSON.stringify([[chartId]]),\n      chartIds: [chartId],\n    };\n    updateDashboard(newDashboard);\n    setCurDashboard(newDashboard);\n  };\n\n  const onClickDashboardItem = async (dashboard: IDashboardItem) => {\n    const { id } = dashboard;\n    if (curDashboard?.id === id) {\n      return;\n    }\n    const res = await getDashboardById({ id });\n    setCurDashboard(res);\n  };\n\n  const renderLeft = () =>\n    (dashboardList || []).map((i, index) => (\n      <div\n        key={index}\n        className={cs({ [styles.boxLeftItem]: true, [styles.activeItem]: curDashboard?.id === i.id })}\n        onClick={() => onClickDashboardItem(i)}\n      >\n        <div className={styles.itemTitle}>\n          <Iconfont code=\"&#xe60d;\" style={{ marginRight: '8px' }} />\n          {i.name}\n        </div>\n        <Dropdown\n          menu={{\n            items: [\n              {\n                key: 'Edit',\n                label: i18n('dashboard.edit'),\n                onClick: () => {\n                  const { id, name, description } = i;\n                  setOpenAddDashboard(true);\n                  form.setFieldsValue({\n                    id,\n                    name,\n                    description,\n                  });\n                },\n              },\n              {\n                key: 'Delete',\n                label: i18n('dashboard.delete'),\n                onClick: async () => {\n                  const { id } = i;\n                  await deleteDashboard({ id });\n                  messageApi.open({\n                    type: 'success',\n                    content: 'delete dashboard success.',\n                  });\n                  queryDashboardList();\n                },\n              },\n            ],\n          }}\n        >\n          <div className={styles.moreButton}>\n            <Iconfont code=\"&#xe601;\" />\n          </div>\n        </Dropdown>\n      </div>\n    ));\n\n  const onAddChart = async (type: 'top' | 'bottom' | 'left' | 'right', rowIndex: number, colIndex: number) => {\n    const { id, schema, chartIds = [] } = curDashboard || {};\n\n    const chartList: number[][] = JSON.parse(schema || '') || [[]];\n    const chartId = await createChart({});\n    switch (type) {\n      case 'top':\n        chartList.splice(rowIndex, 0, [chartId]);\n        break;\n      case 'bottom':\n        chartList.splice(rowIndex + 1, 0, [chartId]);\n        break;\n      case 'left':\n        chartList[rowIndex].splice(colIndex, 0, chartId);\n        break;\n      case 'right':\n        chartList[rowIndex].splice(colIndex + 1, 0, chartId);\n        break;\n      default:\n        break;\n    }\n\n    const newDashboard = {\n      ...curDashboard,\n      id: id!,\n      schema: JSON.stringify(chartList),\n      chartIds: [...chartIds, chartId],\n    };\n    await updateDashboard(newDashboard);\n    setCurDashboard(newDashboard);\n  };\n\n  const onDeleteChart = async (chartId: number, rowIndex: number, colIndex: number) => {\n    const { id, schema, chartIds } = curDashboard || {};\n\n    const chartList: number[][] = JSON.parse(schema || '') || [[]];\n    if (chartList[rowIndex].length === 1) {\n      chartList.splice(rowIndex, 1);\n    } else {\n      chartList[rowIndex].splice(colIndex, 1);\n    }\n\n    const newDashboard = {\n      id: id!,\n      ...curDashboard,\n      schema: JSON.stringify(chartList),\n      chartIds: chartIds?.filter((_id) => _id !== chartId),\n    };\n    await updateDashboard(newDashboard);\n    setCurDashboard(newDashboard);\n  };\n\n  const renderContent = () => {\n    const { schema, name } = curDashboard || {};\n    if (!schema) return;\n\n    const chartList = JSON.parse(schema);\n\n    return (\n      <>\n        <div className={styles.boxRightTitle}>\n          <Iconfont code=\"&#xe60d;\" />\n          <div style={{ marginLeft: '8px' }}>{name}</div>\n        </div>\n\n        <div className={styles.BoxRightContent}>\n          {chartList.map((rowData: number[], rowIndex: number) => (\n            <div key={rowIndex} className={styles.boxRightContentRow}>\n              {rowData.map((chartId: number, colIndex: number) => (\n                <div\n                  key={colIndex}\n                  className={styles.boxRightContentColumn}\n                  style={{ width: `${100 / rowData.length}%` }}\n                >\n                  <ChartItem\n                    id={chartId}\n                    key={chartId}\n                    canAddRowItem={rowData.length < 3}\n                    addChartTop={() => onAddChart('top', rowIndex, colIndex)}\n                    addChartBottom={() => onAddChart('bottom', rowIndex, colIndex)}\n                    addChartLeft={() => onAddChart('left', rowIndex, colIndex)}\n                    addChartRight={() => onAddChart('right', rowIndex, colIndex)}\n                    onDelete={(id: number) => onDeleteChart(id, rowIndex, colIndex)}\n                    connectionList={connectionList || []}\n                    remainingUse={remainingUse}\n                  />\n                </div>\n              ))}\n            </div>\n          ))}\n        </div>\n      </>\n    );\n  };\n\n  return (\n    <>\n      <DraggableContainer className={cs(styles.box, className)}>\n        <div ref={draggableRef} className={styles.dragBox}>\n          <div className={styles.boxLeft}>\n            <div className={styles.boxLeftTitle}>\n              <div>{i18n('dashboard.title')}</div>\n              <Iconfont code=\"&#xe631;\" className={styles.plusIcon} onClick={() => setOpenAddDashboard(true)} />\n            </div>\n            {renderLeft()}\n          </div>\n        </div>\n        <div className={styles.boxRight}>{renderContent()}</div>\n      </DraggableContainer>\n\n      <Modal\n        title={form.getFieldValue('id') ? i18n('dashboard.modal.editTitle') : i18n('dashboard.modal.addTitle')}\n        open={openAddDashboard}\n        onOk={async () => {\n          try {\n            await form.validateFields();\n            const formValue = form.getFieldsValue(true);\n            const { id } = formValue;\n\n            if (id) {\n              await updateDashboard(formValue);\n            } else {\n              await createDashboard(formValue);\n            }\n            queryDashboardList();\n            setOpenAddDashboard(false);\n            form.resetFields();\n          } catch (errorInfo) {\n            form.resetFields();\n          }\n        }}\n        onCancel={() => {\n          setOpenAddDashboard(false);\n          form.resetFields();\n        }}\n        okText={i18n('common.button.confirm')}\n        cancelText={i18n('common.button.cancel')}\n      >\n        <Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} form={form} autoComplete={'off'}>\n          <Form.Item\n            label={'name'}\n            name={'name'}\n            rules={[{ required: true, message: i18n('dashboard.modal.name.placeholder') }]}\n          >\n            <Input />\n          </Form.Item>\n          <Form.Item label={'description'} name={'description'}>\n            <Input.TextArea />\n          </Form.Item>\n        </Form>\n      </Modal>\n    </>\n  );\n}\n\nexport default Chart;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/left-block/index.less",
    "content": ""
  },
  {
    "path": "chat2db-client/src/pages/main/dashboard/left-block/index.tsx",
    "content": ""
  },
  {
    "path": "chat2db-client/src/pages/main/functions/getConnection.ts",
    "content": "import connectionService from '@/service/connection';\nimport { setConnectionEnvList } from '@/pages/main/store/connection';\n\nconst getConnectionEnvList = () => {\n  connectionService.getEnvList().then((res) => {\n    setConnectionEnvList(res);\n  });\n};\n\nexport default getConnectionEnvList;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/index.less",
    "content": ".page {\n  display: flex;\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  bottom: 0;\n}\n\n.layoutLeft {\n  position: relative;\n  flex-shrink: 0;\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n  align-items: center;\n  width: 52px;\n  padding-top: 10px;\n  background-color: var(--color-bg-subtle);\n  border-right: 1px solid var(--color-border-secondary);\n  user-select: none;\n  overflow: hidden;\n}\n\n.dargBox {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  -webkit-app-region: drag; //set electron this region Can drag\n  z-index: -10;\n  pointer-events: none;\n}\n\n.brandLogo {\n  flex-shrink: 0;\n  overflow: hidden;\n  margin-bottom: 20px;\n  margin-top: 10px;\n  cursor: pointer;\n}\n\n.navList {\n  flex: 1;\n  width: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n\n  li {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    align-items: center;\n    margin-bottom: 16px;\n    width: 40px;\n    height: 40px;\n    border-radius: 8px;\n    font-size: 12px;\n    color: var(--color-text);\n    cursor: pointer;\n\n    .icon {\n      color: var(--custom-color-icon);\n    }\n\n    &:last-of-type {\n      margin-bottom: 0px;\n    }\n\n    &:hover {\n      .icon {\n        color: var(--color-primary);\n      }\n      background-color: var(--color-hover-bg);\n    }\n  }\n\n  .activeNav {\n    .icon {\n      color: var(--color-primary);\n    }\n    background-color: var(--color-hover-bg);\n  }\n}\n\n.footer {\n  flex-shrink: 0;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  margin-bottom: 20px;\n  .userBox {\n    width: 32px;\n    height: 32px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    margin-bottom: 6px;\n\n    .questionIcon {\n      font-size: 20px;\n      cursor: pointer;\n      transform: translateX(1px);\n      color: var(--custom-color-icon);\n    }\n    &:hover .questionIcon {\n      color: var(--color-primary);\n    }\n  }\n  .rocketIcon{\n    font-size: 20px;\n    cursor: pointer;\n    color: var(--custom-color-icon);\n    cursor: pointer;\n    margin-bottom: 12px;\n    &:hover {\n      color: var(--color-primary);\n    }\n  }\n}\n\n.userDropdown {\n  display: flex;\n  align-items: center;\n  i {\n    margin-right: 6px;\n  }\n}\n\n.layoutRight {\n  flex: 1;\n  overflow: hidden;\n}\n\n.main {\n  position: relative;\n  min-height: 100vh;\n}\n\n.componentBox {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/index.tsx",
    "content": "import React, { useEffect, useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Button, Dropdown, Tooltip } from 'antd';\nimport classnames from 'classnames';\n\nimport Iconfont from '@/components/Iconfont';\nimport BrandLogo from '@/components/BrandLogo';\n\nimport i18n from '@/i18n';\nimport { userLogout } from '@/service/user';\nimport { INavItem } from '@/typings/main';\nimport { IRole } from '@/typings/user';\n\n// ----- hooks -----\nimport getConnectionEnvList from './functions/getConnection';\n\n// ----- store -----\nimport { useMainStore, setMainPageActiveTab } from '@/pages/main/store/main';\nimport { getConnectionList } from '@/pages/main/store/connection';\nimport { useUserStore, setCurUser } from '@/store/user';\nimport { setAppTitleBarRightComponent } from '@/store/common/appTitleBarConfig';\n\n// ----- component -----\nimport CustomLayout from '@/components/CustomLayout';\n\n// ----- block -----\nimport Workspace from './workspace';\nimport Dashboard from './dashboard';\nimport Connection from './connection';\nimport Team from './team';\nimport Setting from '@/blocks/Setting';\n\nimport styles from './index.less';\nimport { useUpdateEffect } from '@/hooks';\nimport { getLinkBasedOnTimezone } from '@/utils/timezone';\nimport { RocketIcon } from 'lucide-react';\n\nconst initNavConfig: INavItem[] = [\n  {\n    key: 'workspace',\n    icon: '\\ue616',\n    iconFontSize: 16,\n    isLoad: false,\n    component: <Workspace />,\n    name: i18n('workspace.title'),\n  },\n  {\n    key: 'dashboard',\n    icon: '\\ue629',\n    iconFontSize: 24,\n    isLoad: false,\n    component: <Dashboard />,\n    name: i18n('dashboard.title'),\n  },\n  {\n    key: 'connections',\n    icon: '\\ue622',\n    iconFontSize: 20,\n    isLoad: false,\n    component: <Connection />,\n    name: i18n('connection.title'),\n  },\n  {\n    key: 'github',\n    icon: '\\ue885',\n    iconFontSize: 26,\n    isLoad: false,\n    openBrowser: 'https://github.com/chat2db/Chat2DB/',\n    name: 'Github',\n  },\n];\n\nfunction MainPage() {\n  const navigate = useNavigate();\n  const { userInfo } = useUserStore((state) => {\n    return {\n      userInfo: state.curUser,\n    };\n  });\n  const [navConfig, setNavConfig] = useState<INavItem[]>(initNavConfig);\n  const mainPageActiveTab = useMainStore((state) => state.mainPageActiveTab);\n  const [activeNavKey, setActiveNavKey] = useState<string>(\n    __ENV__ === 'desktop' ? mainPageActiveTab : window.location.pathname.split('/')[1] || mainPageActiveTab,\n  );\n\n  const isMac = useMemo(() => {\n    return window.electronApi?.getPlatform().isMac;\n  }, []);\n\n  // 当页面在workspace时，显示自定义布局\n  useEffect(() => {\n    if (mainPageActiveTab === 'workspace') {\n      setAppTitleBarRightComponent(<CustomLayout />);\n    } else {\n      setAppTitleBarRightComponent(false);\n    }\n    return () => {\n      setAppTitleBarRightComponent(false);\n    };\n  }, [mainPageActiveTab]);\n\n  useEffect(() => {\n    handleInitPage();\n    getConnectionList();\n    getConnectionEnvList();\n  }, []);\n\n  useUpdateEffect(() => {\n    switchingNav(mainPageActiveTab);\n  }, [mainPageActiveTab]);\n\n  // 切换tab\n  useEffect(() => {\n    // 获取当前地址栏的tab\n    const activeIndex = navConfig.findIndex((t) => `${t.key}` === activeNavKey);\n    if (activeIndex > -1) {\n      navConfig[activeIndex].isLoad = true;\n      setNavConfig([...navConfig]);\n      if (__ENV__ !== 'desktop') {\n        const href = window.location.origin + '/' + activeNavKey;\n        window.history.pushState({}, '', href);\n      }\n    }\n  }, [activeNavKey]);\n\n  const handleInitPage = async () => {\n    const cloneNavConfig = [...navConfig];\n    if (userInfo) {\n      const hasTeamIcon = cloneNavConfig.find((i) => i.key === 'team');\n      if (userInfo.admin && !hasTeamIcon) {\n        cloneNavConfig.splice(3, 0, {\n          key: 'team',\n          icon: '\\ue64b',\n          iconFontSize: 24,\n          isLoad: activeNavKey === 'team', // 如果当前是team，直接加载\n          component: <Team />,\n          name: i18n('team.title'),\n        });\n      }\n      if (!userInfo.admin && hasTeamIcon) {\n        cloneNavConfig.splice(3, 1);\n      }\n    }\n    setNavConfig([...cloneNavConfig]);\n  };\n\n  const switchingNav = (key: string) => {\n    if (key === 'github') {\n      window.open('https://github.com/chat2db/Chat2DB/', '_blank');\n    } else {\n      setActiveNavKey(key);\n      setMainPageActiveTab(key);\n    }\n  };\n\n  const handleLogout = () => {\n    userLogout().then(() => {\n      setCurUser(undefined);\n      navigate('/login');\n    });\n  };\n\n  const renderUser = () => {\n    return (\n      <Dropdown\n        menu={{\n          items: [\n            {\n              key: '1',\n              label: (\n                <div className={styles.userDropdown} onClick={handleLogout}>\n                  <Iconfont code=\"&#xe6b2;\" />\n                  {i18n('login.text.logout')}\n                </div>\n              ),\n            },\n          ],\n        }}\n        placement=\"bottomRight\"\n        trigger={['click']}\n      >\n        <div className={styles.userBox}>\n          <Iconfont code=\"&#xe64c;\" className={styles.questionIcon} />\n        </div>\n      </Dropdown>\n    );\n  };\n\n  return (\n    <div className={styles.page}>\n      <div className={styles.layoutLeft}>\n        {isMac === void 0 && <BrandLogo size={38} className={styles.brandLogo} />}\n        <ul className={styles.navList}>\n          {navConfig.map((item) => {\n            return (\n              <Tooltip key={item.key} placement=\"right\" title={item.name}>\n                <li\n                  className={classnames({\n                    [styles.activeNav]: item.key == activeNavKey,\n                  })}\n                  onClick={() => switchingNav(item.key)}\n                >\n                  <Iconfont size={item.iconFontSize} className={styles.icon} code={item.icon} />\n                </li>\n              </Tooltip>\n            );\n          })}\n        </ul>\n        <div className={styles.footer}>\n          <Tooltip placement=\"right\" title={i18n('setting.title.goto.chat2db.pro')}>\n            <RocketIcon\n              className={styles.rocketIcon} \n              onClick={() => {\n                const link = getLinkBasedOnTimezone();\n                window.open(link, '_blank');\n              }}\n            />\n          </Tooltip>\n          {/* <Tooltip placement=\"right\" title=\"个人中心\">\n            {userInfo?.roleCode !== IRole.DESKTOP ? renderUser() : null}\n          </Tooltip> */}\n          <Setting className={styles.setIcon} />\n        </div>\n      </div>\n      <div className={styles.layoutRight}>\n        {navConfig.map((item) => {\n          return (\n            <div key={item.key} className={styles.componentBox} hidden={activeNavKey !== item.key}>\n              {item.isLoad ? item.component : null}\n            </div>\n          );\n        })}\n      </div>\n    </div>\n  );\n}\n\nexport default MainPage;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/store/connection/index.ts",
    "content": "import { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\nimport { StoreApi } from 'zustand';\n\nimport { IConnectionListItem, IConnectionEnv } from '@/typings/connection';\nimport connectionService from '@/service/connection';\n\nimport { setCurrentConnectionDetails } from '@/pages/main/workspace/store/common';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\n\nexport interface IConnectionStore {\n  connectionList: IConnectionListItem[] | null;\n  connectionEnvList: IConnectionEnv[] | null;\n}\n\nexport const initConnectionStore = {\n  connectionList: null,\n  connectionEnvList: null,\n};\n\nexport const useConnectionStore: UseBoundStoreWithEqualityFn<StoreApi<IConnectionStore>> = createWithEqualityFn(\n  devtools(() => initConnectionStore),\n  shallow,\n);\n\nexport const setConnectionList = (connectionList: IConnectionListItem[]) => {\n  return useConnectionStore.setState({ connectionList });\n};\n\nexport const setConnectionEnvList = (connectionEnvList: IConnectionEnv[]) => {\n  return useConnectionStore.setState({ connectionEnvList });\n};\n\nexport const getConnectionList: () => Promise<IConnectionListItem[]> = () => {\n  return new Promise((resolve, reject) => {\n    const currentConnectionDetails = useWorkspaceStore.getState().currentConnectionDetails;\n    connectionService\n      .getList({\n        pageNo: 1,\n        pageSize: 1000,\n        refresh: true,\n      })\n      .then((res) => {\n        const connectionList = res?.data || [];\n        useConnectionStore.setState({ connectionList });\n        resolve(connectionList);\n\n        // 如果连接列表为空，则设置当前连接为空\n        if (connectionList.length === 0) {\n          setCurrentConnectionDetails(null);\n          return;\n        }\n\n        // 如果当前连接不存在，则设置当前连接为第一个连接\n        if (!currentConnectionDetails?.id) {\n          setCurrentConnectionDetails(connectionList[0]);\n          return;\n        }\n\n        // 如果存在但是不在列表中，则设置当前连接为第一个连接\n        const currentConnection = connectionList.find((item) => item.id === currentConnectionDetails?.id);\n        if (!currentConnection) {\n          setCurrentConnectionDetails(connectionList[0]);\n        }\n      })\n      .catch(() => {\n        useConnectionStore.setState({ connectionList: [] });\n        reject([]);\n      });\n  });\n};\n"
  },
  {
    "path": "chat2db-client/src/pages/main/store/main/index.ts",
    "content": "import { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools, persist } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\nimport { StoreApi } from 'zustand';\n\nexport interface IMainStore {\n  mainPageActiveTab: string;\n}\n\nconst initMainStore = {\n  mainPageActiveTab: 'connections',\n};\n\nexport const useMainStore: UseBoundStoreWithEqualityFn<StoreApi<IMainStore>> = createWithEqualityFn(\n  devtools(\n    persist(() => initMainStore, {\n      name: 'main-page-store',\n      getStorage: () => localStorage,\n      // 工作区的状态只保存 layout布局信息\n      partialize: (state: IMainStore) => ({\n        mainPageActiveTab: state.mainPageActiveTab,\n      }),\n    }),\n  ),\n  shallow,\n);\n\nexport const setMainPageActiveTab = (mainPageActiveTab: string) => {\n  return useMainStore.setState({\n    mainPageActiveTab,\n  });\n};\n"
  },
  {
    "path": "chat2db-client/src/pages/main/store/monaco/index.ts",
    "content": "import { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\nimport { StoreApi } from 'zustand';\n\n// 表信息\nexport interface tableItem {\n  columnList: {\n    columnName?: string;\n    columnType?: string;\n  }[];\n}\n\n// schema信息\nexport interface schemaItem {\n  tableList?: tableItem[];\n}\n\n// 数据库信息\nexport type databaseItem = {\n  schemaList?: schemaItem[];\n} | {\n  databaseName: string;\n  tableList: tableItem[];\n}\n\n// monaco store\nexport interface IMonacoStore {\n  registerProvider: {\n    // 数据源id\n    [key: number]: databaseItem[]\n  }\n}\n\nconst initMonacoStore = {\n  registerProvider: {}\n}\n\nexport const useMonacoStore: UseBoundStoreWithEqualityFn<StoreApi<IMonacoStore>> = createWithEqualityFn(\n  devtools(() => (initMonacoStore)),\n  shallow\n);\n\nexport const setRegisterProvider = (id: number, data: databaseItem[]) => {\n  useMonacoStore.getState().registerProvider[id] = data;\n}\n\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/datasource-management/index.less",
    "content": ".tableTop {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin: 16px 0;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/datasource-management/index.tsx",
    "content": "import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { Button, Input, Table, Popconfirm, message, Drawer } from 'antd';\nimport { SearchOutlined, PlusOutlined } from '@ant-design/icons';\nimport ConnectionServer from '@/service/connection';\nimport { createDataSource, deleteDataSource, getDataSourceList, updateDataSource } from '@/service/team';\nimport { IConnectionDetails } from '@/typings';\nimport { AffiliationType, IDataSourceVO } from '@/typings/team';\nimport i18n from '@/i18n';\nimport { isValid } from '@/utils/check';\nimport CreateConnection from '@/blocks/CreateConnection';\nimport UniversalDrawer from '../universal-drawer';\nimport { isNumber } from 'lodash';\nimport styles from './index.less';\n\nfunction DataSourceManagement() {\n  const [dataSource, setDataSource] = useState<IDataSourceVO[]>([]);\n  const [pagination, setPagination] = useState({\n    searchKey: '',\n    current: 1,\n    pageSize: 10,\n    total: 0,\n    showSizeChanger: true,\n    showQuickJumper: true,\n    // pageSizeOptions: ['10', '20', '30', '40'],\n  });\n  const [showCreateConnection, setShowCreateConnection] = useState(false);\n  const connectionInfo = useRef<IConnectionDetails | null>(null);\n\n  const [drawerInfo, setDrawerInfo] = useState<{ open: boolean; type: AffiliationType; id?: number }>({\n    open: false,\n    type: AffiliationType['DATASOURCE_USER/TEAM'],\n  });\n\n  const columns = useMemo(\n    () => [\n      {\n        title: i18n('team.datasource.alias'),\n        dataIndex: 'alias',\n        key: 'alias',\n      },\n      {\n        title: i18n('team.datasource.url'),\n        dataIndex: 'url',\n        key: 'url',\n      },\n      {\n        title: i18n('common.text.action'),\n        key: 'action',\n        width: 300,\n        render: (_: any, record: IDataSourceVO) => (\n          <>\n            <Button\n              type=\"link\"\n              onClick={() => {\n                handleEdit(record);\n              }}\n            >\n              {i18n('common.button.edit')}\n            </Button>\n            <Button\n              type=\"link\"\n              onClick={() => {\n                setDrawerInfo({\n                  ...drawerInfo,\n                  open: true,\n                  id: record.id,\n                });\n              }}\n            >\n              {i18n('team.action.rightManagement')}\n            </Button>\n            <Popconfirm\n              title={i18n('common.tips.delete.confirm')}\n              onConfirm={() => handleDelete(record.id)}\n              okText={i18n('common.button.affirm')}\n              cancelText={i18n('common.button.cancel')}\n            >\n              <a href=\"#\" onClick={(e) => e.preventDefault()}>\n                {i18n('common.button.delete')}\n              </a>\n            </Popconfirm>\n          </>\n        ),\n      },\n    ],\n    [],\n  );\n\n  useEffect(() => {\n    queryDataSourceList();\n  }, [pagination.current, pagination.pageSize, pagination.searchKey]);\n\n  const queryDataSourceList = async () => {\n    const { searchKey, current: pageNo, pageSize } = pagination;\n    const res = await getDataSourceList({ searchKey, pageNo, pageSize });\n    if (res) {\n      setDataSource(res?.data ?? []);\n      setPagination({\n        ...pagination,\n        total: res?.total ?? 0,\n      } as any);\n    }\n  };\n\n  const handleSearch = (searchKey: string) => {\n    setPagination({\n      ...pagination,\n      searchKey,\n    });\n  };\n\n  const handleTableChange = (p: any) => {\n    setPagination({\n      ...pagination,\n      ...p,\n    });\n  };\n\n  const handleAddDataSource = () => {\n    connectionInfo.current = null;\n    setShowCreateConnection(true);\n  };\n\n  const handleEdit = async (record: IDataSourceVO) => {\n    const { id } = record;\n    if (!id) {\n      return;\n    }\n\n    const detail = await ConnectionServer.getDetails({ id });\n    connectionInfo.current = detail;\n    setShowCreateConnection(true);\n  };\n\n  const handleDelete = async (id?: number) => {\n    if (isNumber(id)) {\n      await deleteDataSource({ id });\n      message.success(i18n('common.text.successfullyDelete'));\n      queryDataSourceList();\n    }\n  };\n\n  const handleConfirmConnection = (data: IConnectionDetails) => {\n    if (JSON.stringify(connectionInfo.current) === '{}') {\n      return new Promise((resolve) => {\n        resolve(true);\n      });\n    }\n    connectionInfo.current = data;\n\n    const isUpdate = isValid(connectionInfo?.current?.id);\n    const requestApi = isUpdate ? updateDataSource : createDataSource;\n    return requestApi({ ...connectionInfo.current }).then(() => {\n      message.success(isUpdate ? i18n('common.tips.updateSuccess') : i18n('common.tips.createSuccess'));\n      setShowCreateConnection(false);\n      queryDataSourceList();\n    });\n  };\n\n  return (\n    <div>\n      <div className={styles.tableTop}>\n        <Input.Search\n          style={{ width: '320px' }}\n          placeholder={i18n('team.input.search.placeholder')}\n          onSearch={handleSearch}\n          enterButton={<SearchOutlined />}\n        />\n        <Button type=\"primary\" icon={<PlusOutlined />} onClick={handleAddDataSource}>\n          {i18n('team.action.addDatasource')}\n        </Button>\n      </div>\n      <Table\n        style={{\n          maxHeight: '82vh',\n          overflow: 'auto',\n        }}\n        sticky\n        rowKey={'id'}\n        dataSource={dataSource}\n        columns={columns}\n        pagination={pagination}\n        onChange={handleTableChange}\n      />\n\n      <Drawer\n        title={connectionInfo?.current?.id ? i18n('team.action.editDatasource') : i18n('team.action.addDatasource')}\n        width={1000}\n        open={showCreateConnection}\n        onClose={() => setShowCreateConnection(false)}\n      >\n        <CreateConnection connectionDetail={connectionInfo.current} onSubmit={handleConfirmConnection} />\n      </Drawer>\n\n      <UniversalDrawer\n        {...drawerInfo}\n        byId={drawerInfo.id}\n        onClose={() => {\n          setDrawerInfo({\n            ...drawerInfo,\n            open: false,\n          });\n        }}\n      />\n    </div>\n  );\n}\n\nexport default DataSourceManagement;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/index.less",
    "content": ".teamWrapper {\n  height: 100vh;\n  padding: 14px 16px;\n  box-sizing: border-box;\n}\n\n.teamTabsBox{\n  height: 100%;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/index.tsx",
    "content": "import React, { useMemo, useState } from 'react';\nimport { ApiOutlined, UserOutlined, TeamOutlined } from '@ant-design/icons';\nimport { Tabs } from 'antd';\nimport DataSourceManagement from './datasource-management';\nimport UserManagement from './user-management';\nimport TeamManagement from './team-management';\nimport i18n from '@/i18n';\nimport styles from './index.less';\n\nconst Team = () => {\n  const [activeKey, setActiveKey] = useState<string>('0');\n  const tabList = useMemo(\n    () => [\n      {\n        label: i18n('team.tab.datasource'),\n        icon: <ApiOutlined />,\n        children: <DataSourceManagement />,\n      },\n      {\n        label: i18n('team.tab.user'),\n        icon: <UserOutlined />,\n        children: <UserManagement />,\n      },\n      {\n        label: i18n('team.tab.team'),\n        icon: <TeamOutlined />,\n        children: <TeamManagement />,\n      },\n    ],\n    [],\n  );\n\n  return (\n    <div className={styles.teamWrapper}>\n      <Tabs\n        className={styles.teamTabsBox}\n        activeKey={activeKey}\n        onChange={(_activeKey) => setActiveKey(_activeKey)}\n        items={tabList.map((tab, index) => {\n          return {\n            key: String(index),\n            label: (\n              <span>\n                {tab.icon}\n                {tab.label}\n              </span>\n            ),\n            children: tab.children,\n          };\n        })}\n      />\n    </div>\n  );\n};\n\nexport default Team;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/team-management/index.less",
    "content": ".tableTop {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin: 16px 0;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/team-management/index.tsx",
    "content": "import React, { useEffect, useMemo, useState } from 'react';\nimport { createTeam, deleteTeam, getTeamManagementList, updateTeam } from '@/service/team';\nimport { Button, Form, Input, Modal, Popconfirm, Radio, Table, Tag, message } from 'antd';\nimport { SearchOutlined, PlusOutlined } from '@ant-design/icons';\nimport UniversalDrawer from '../universal-drawer';\nimport { AffiliationType, ITeamVO, StatusType } from '@/typings/team';\nimport i18n from '@/i18n';\nimport styles from './index.less';\n\nconst formItemLayout = {\n  labelCol: { span: 6 },\n  wrapperCol: { span: 16 },\n  colon: false,\n};\n\nconst requireRule = { required: true, message: i18n('common.form.error.required') };\n\nfunction TeamManagement() {\n  const [form] = Form.useForm();\n  const [loadding, setLoading] = useState(false);\n  const [dataSource, setDataSource] = useState<ITeamVO[]>([]);\n  const [pagination, setPagination] = useState({\n    searchKey: '',\n    current: 1,\n    pageSize: 10,\n    total: 0,\n    showSizeChanger: true,\n    showQuickJumper: true,\n    // pageSizeOptions: ['10', '20', '30', '40'],\n  });\n  const [isModalVisible, setIsModalVisible] = useState(false);\n  const [drawerInfo, setDrawerInfo] = useState<{ open: boolean; type?: AffiliationType; teamId?: number }>({\n    open: false,\n  });\n  const columns = useMemo(\n    () => [\n      {\n        title: i18n('team.team.addForm.code'),\n        dataIndex: 'code',\n        key: 'code',\n      },\n      {\n        title: i18n('team.team.addForm.name'),\n        dataIndex: 'name',\n        key: 'name',\n      },\n      {\n        title: i18n('team.team.addForm.status'),\n        dataIndex: 'status',\n        key: 'status',\n        render: (status: StatusType) => <Tag color={status === StatusType.VALID ? 'green' : 'red'}>{status}</Tag>,\n      },\n      {\n        title: i18n('common.text.action'),\n        key: 'action',\n        width: 260,\n        render: (_: any, record: ITeamVO) => (\n          <>\n            <Button type=\"link\" onClick={() => handleEdit(record)}>\n              {i18n('common.button.edit')}\n            </Button>\n            <Button\n              type=\"link\"\n              onClick={() => {\n                setDrawerInfo({\n                  ...drawerInfo,\n                  open: true,\n                  teamId: record.id,\n                  type: AffiliationType.TEAM_USER,\n                });\n              }}\n            >\n              {i18n('team.action.affiliation.user')}\n            </Button>\n            <Button\n              type=\"link\"\n              onClick={() => {\n                setDrawerInfo({\n                  ...drawerInfo,\n                  open: true,\n                  teamId: record.id,\n                  type: AffiliationType.TEAM_DATASOURCE,\n                });\n              }}\n            >\n              {i18n('team.action.affiliation.datasource')}\n            </Button>\n            <Popconfirm\n              title={i18n('common.tips.delete.confirm')}\n              onConfirm={() => handleDelete(record.id)}\n              okText={i18n('common.button.affirm')}\n              cancelText={i18n('common.button.cancel')}\n            >\n              <a href=\"#\" onClick={(e) => e.preventDefault()}>\n                {i18n('common.button.delete')}\n              </a>\n            </Popconfirm>\n          </>\n        ),\n      },\n    ],\n    [],\n  );\n\n  useEffect(() => {\n    queryTeamList();\n  }, [pagination.current, pagination.pageSize, pagination.searchKey]);\n\n  const queryTeamList = async () => {\n    setLoading(true);\n    try {\n      const { searchKey, current: pageNo, pageSize } = pagination;\n      let res = await getTeamManagementList({ searchKey, pageNo, pageSize });\n      if (res) {\n        setDataSource(res?.data ?? []);\n        setPagination({\n          ...pagination,\n          total: res?.total ?? 0,\n        } as any);\n      }\n    } catch (error) {\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  const handleTableChange = (p: any) => {\n    setPagination({\n      ...pagination,\n      ...p,\n    });\n  };\n\n  const handleSearch = (searchKey: string) => {\n    setPagination({\n      ...pagination,\n      searchKey,\n    });\n  };\n\n  const handleCreateOrUpdateTeam = async (teamInfo: ITeamVO) => {\n    const requestApi = teamInfo.id ? updateTeam : createTeam;\n    let res = await requestApi(teamInfo);\n    if (res) {\n      queryTeamList();\n    }\n  };\n\n  const handleEdit = (record: ITeamVO) => {\n    form.setFieldsValue(record);\n    setIsModalVisible(true);\n  };\n\n  const handleDelete = async (id?: number) => {\n    if (id !== undefined) {\n      await deleteTeam({ id });\n      message.success(i18n('common.text.successfullyDelete'));\n      queryTeamList();\n    }\n  };\n\n  return (\n    <div>\n      <div className={styles.tableTop}>\n        <Input.Search\n          style={{ width: '320px' }}\n          placeholder={i18n('team.input.search.placeholder')}\n          onSearch={handleSearch}\n          enterButton={<SearchOutlined />}\n        />\n        <Button type=\"primary\" icon={<PlusOutlined />} onClick={() => setIsModalVisible(true)}>\n          {i18n('team.action.addTeam')}\n        </Button>\n      </div>\n      <Table\n        style={{\n          maxHeight: '82vh',\n          overflow: 'auto',\n        }}\n        sticky\n        rowKey={'id'}\n        loading={loadding}\n        dataSource={dataSource}\n        columns={columns}\n        pagination={pagination}\n        onChange={handleTableChange}\n      />\n\n      <Modal\n        title={form.getFieldValue('id') !== undefined ? i18n('team.action.editTeam') : i18n('team.action.addTeam')}\n        open={isModalVisible}\n        onOk={() => {\n          form\n            .validateFields()\n            .then((values) => {\n              const formValues = form.getFieldsValue(true);\n              handleCreateOrUpdateTeam(formValues);\n              setIsModalVisible(false);\n              form.resetFields();\n            })\n            .catch((errorInfo) => {\n              form.scrollToField(errorInfo.errorFields[0].name);\n              form.setFields(errorInfo.errorFields);\n            });\n        }}\n        onCancel={() => {\n          form.resetFields();\n          setIsModalVisible(false);\n        }}\n      >\n        <Form\n          {...formItemLayout}\n          form={form}\n          autoComplete={'off'}\n          initialValues={{\n            status: StatusType.VALID,\n          }}\n        >\n          <Form.Item label={i18n('team.team.addForm.code')} name=\"code\" rules={[requireRule]}>\n            <Input />\n          </Form.Item>\n          <Form.Item label={i18n('team.team.addForm.name')} name=\"name\">\n            <Input />\n          </Form.Item>\n          <Form.Item label={i18n('team.team.addForm.status')} name=\"status\" rules={[requireRule]}>\n            <Radio.Group>\n              <Radio value={StatusType.VALID}>{i18n('team.team.addForm.status.valid')}</Radio>\n              <Radio value={StatusType.INVALID}>{i18n('team.team.addForm.status.invalid')}</Radio>\n            </Radio.Group>\n          </Form.Item>\n          <Form.Item label={i18n('team.team.addForm.description')} name=\"description\">\n            <Input.TextArea />\n          </Form.Item>\n        </Form>\n      </Modal>\n\n      <UniversalDrawer\n        {...drawerInfo}\n        byId={drawerInfo.teamId}\n        onClose={() => {\n          setDrawerInfo({\n            ...drawerInfo,\n            open: false,\n          });\n        }}\n      />\n    </div>\n  );\n}\n\nexport default TeamManagement;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/universal-add-modal/index.less",
    "content": ""
  },
  {
    "path": "chat2db-client/src/pages/main/team/universal-add-modal/index.tsx",
    "content": "import {\n  getCommonDataSourceList,\n  getCommonTeamList,\n  getCommonUserAndTeamList,\n  getCommonUserList,\n} from '@/service/team';\nimport { IDataSourceVO, ITeamAndUserVO, ITeamVO, IUserVO, ManagementType, SearchType } from '@/typings/team';\nimport { Modal, Select, Spin } from 'antd';\nimport debounce from 'lodash/debounce';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport styles from './index.less';\nimport i18n from '@/i18n';\n\ninterface IProps {\n  open: boolean;\n  type?: SearchType;\n  onConfirm: (values: Object) => void;\n  onClose: () => void;\n}\n\ninterface ValueType {\n  id?: number;\n  key: number;\n  label: React.ReactNode;\n  value: number;\n}\n\nconst addAuthMap = {\n  [SearchType['USER/TEAM']]: {\n    title: i18n('team.action.addUserAndTeam'),\n    loadRequest: getCommonUserAndTeamList,\n    searchLabel: (data: ITeamAndUserVO) => data.name,\n    searchValue: (data: ITeamAndUserVO) => JSON.stringify({ id: data.id, type: data.type }),\n    searchListKey: 'accessObjectList',\n    placeholder: i18n('team.action.addUserAndTeam.placeholder'),\n  },\n  [SearchType.TEAM]: {\n    title: i18n('team.action.addTeam'),\n    loadRequest: getCommonTeamList,\n    searchLabel: (data: ITeamVO) => data.name,\n    searchValue: (data: ITeamVO) => data.id,\n    searchListKey: 'teamIdList',\n    placeholder: i18n('team.action.addTeam.placeholder'),\n  },\n  [SearchType.USER]: {\n    title: i18n('team.action.addUser'),\n    loadRequest: getCommonUserList,\n    searchLabel: (data: IUserVO) => data.userName,\n    searchValue: (data: IUserVO) => data.id,\n    searchListKey: 'userIdList',\n    placeholder: i18n('team.action.addUser.placeholder'),\n  },\n  [SearchType.DATASOURCE]: {\n    title: i18n('team.action.addDatasource'),\n    loadRequest: getCommonDataSourceList,\n    searchLabel: (data: IDataSourceVO) => data.alias,\n    searchValue: (data: IDataSourceVO) => data.id,\n    searchListKey: 'dataSourceIdList',\n    placeholder: i18n('team.action.addDatasource.placeholder'),\n  },\n};\n\nfunction UniversalAddModal(props: IProps) {\n  const { open, type } = props;\n\n  const [fetching, setFetching] = useState(false);\n  const [options, setOptions] = useState<ValueType[]>([]);\n  const [selectedValues, setSelectedValues] = useState([]);\n\n  const authData = useMemo(() => {\n    if (type) {\n      return addAuthMap[type];\n    }\n  }, [type]);\n\n  useEffect(() => {\n    loadOptions('');\n  }, []);\n\n  const loadOptions = (value: string) => {\n    setOptions([]);\n    setFetching(true);\n\n    authData?.loadRequest({ searchKey: value }).then((res) => {\n      const newOptions = (res || []).map((i) => ({\n        ...i,\n        label: authData.searchLabel(i),\n        value: authData.searchValue(i),\n        key: i.id,\n      }));\n\n      setOptions(newOptions);\n\n      setFetching(false);\n    });\n  };\n\n  const handleOk = () => {\n    if (!props.onConfirm || !authData) {\n      return;\n    }\n\n    const realValue = {\n      [authData.searchListKey]:\n        type !== SearchType['USER/TEAM'] ? selectedValues : selectedValues.map((i) => JSON.parse(i)),\n    };\n\n    props.onConfirm(realValue);\n    props.onClose && props.onClose();\n    setSelectedValues([]);\n    setOptions([]);\n  };\n\n  return (\n    <Modal\n      open={open}\n      onOk={handleOk}\n      onCancel={() => {\n        props.onClose && props.onClose();\n      }}\n      title={authData?.title}\n    >\n      <Select\n        size=\"large\"\n        mode=\"multiple\"\n        style={{ width: '100%' }}\n        onSearch={debounce(loadOptions, 300)}\n        placeholder={authData?.placeholder}\n        filterOption={false}\n        notFoundContent={fetching ? <Spin style={{ margin: '16px 0' }} size=\"small\" /> : null}\n        options={options}\n        value={selectedValues}\n        onChange={(values) => {\n          setSelectedValues(values);\n        }}\n      />\n    </Modal>\n  );\n}\n\nexport default UniversalAddModal;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/universal-drawer/index.less",
    "content": ".tableTop {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin: 16px 0;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/universal-drawer/index.tsx",
    "content": "import { Button, Drawer, Input, message, Popconfirm, Table, Tag } from 'antd';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { SearchOutlined, PlusOutlined } from '@ant-design/icons';\nimport {\n  AffiliationType,\n  IDataSourceAccessVO,\n  IDataSourceVO,\n  ITeamVO,\n  ITeamWithDataSourceVO,\n  ITeamWithUserVO,\n  IUserVO,\n  IUserWithDataSourceVO,\n  IUserWithTeamVO,\n  ManagementType,\n  SearchType,\n} from '@/typings/team';\nimport {\n  deleteDataSourceFromTeam,\n  deleteDataSourceFromUser,\n  deleteTeamListFromUser,\n  deleteUserFromTeam,\n  deleteUserOrTeamFromDataSource,\n  getDataSourceListFromTeam,\n  getDataSourceListFromUser,\n  getTeamListFromUser,\n  getUserAndTeamListFromDataSource,\n  getUserListFromTeam,\n  updateDataSourceListFromTeam,\n  updateDataSourceListFromUser,\n  updateTeamListFromUser,\n  updateUserAndTeamListFromDataSource,\n  updateUserListFromTeam,\n} from '@/service/team';\n\nimport UniversalAddModal from '../universal-add-modal';\nimport styles from './index.less';\nimport { ColumnsType } from 'antd/es/table';\nimport i18n from '@/i18n';\nimport { isNumber } from 'lodash';\n\ninterface IProps {\n  type?: AffiliationType;\n  open: boolean;\n  onClose: () => void;\n  byId?: number;\n}\n\ninterface IAffiliationDetail {\n  type: AffiliationType;\n  searchType: SearchType;\n  title: string;\n  byIdKey: string;\n  columns: ColumnsType<any>;\n  queryListApi: (params: any) => Promise<any>;\n  updateListApi: (params: any) => Promise<any>;\n  deleteApi: (params: { id: number }) => Promise<any>;\n}\n\nfunction UniversalDrawer(props: IProps) {\n  const { type, open } = props;\n  const [dataSource, setDataSource] = useState<Array<IUserVO | ITeamVO | IDataSourceVO>>([]);\n  const [modalInfo, setModalInfo] = useState<{ open: boolean; type?: SearchType }>({\n    open: false,\n  });\n  const [searchInput, setSearchInput] = useState('');\n\n  const [pagination, setPagination] = useState({\n    searchKey: '',\n    current: 1,\n    pageSize: 10,\n    total: 0,\n    showSizeChanger: true,\n    showQuickJumper: true,\n  });\n  const [total, setTotal] = useState(0);\n\n  const managementMap: Record<AffiliationType, IAffiliationDetail> = useMemo(\n    () => ({\n      [AffiliationType.USER_TEAM]: {\n        type: AffiliationType.USER_TEAM,\n        searchType: SearchType.TEAM,\n        title: i18n('team.team.name'),\n        byIdKey: 'userId',\n        queryListApi: getTeamListFromUser,\n        updateListApi: updateTeamListFromUser,\n        deleteApi: deleteTeamListFromUser,\n        columns: [\n          {\n            title: i18n('team.team.addForm.code'),\n            dataIndex: ['team', 'code'],\n            key: 'team.code',\n          },\n          {\n            title: i18n('team.team.addForm.name'),\n            dataIndex: ['team', 'name'],\n            key: 'team.name',\n          },\n          {\n            title: i18n('common.text.action'),\n            key: 'action',\n            width: 100,\n            render: (_: any, record: IUserWithTeamVO) => (\n              <Popconfirm\n                title={i18n('common.tips.delete.confirm')}\n                okText={i18n('common.button.affirm')}\n                cancelText={i18n('common.button.cancel')}\n                onConfirm={async () => {\n                  if (record.id !== undefined) {\n                    await deleteTeamListFromUser({ id: record.id });\n                    message.success(i18n('common.text.successfullyDelete'));\n                    queryTableList();\n                  }\n                }}\n              >\n                <a href=\"#\" onClick={(e) => e.preventDefault()}>\n                  {i18n('common.button.delete')}\n                </a>\n              </Popconfirm>\n            ),\n          },\n        ],\n      },\n      [AffiliationType.USER_DATASOURCE]: {\n        type: AffiliationType.USER_DATASOURCE,\n        searchType: SearchType.DATASOURCE,\n        title: i18n('team.datasource.rightManagement'),\n        byIdKey: 'userId',\n        queryListApi: getDataSourceListFromUser,\n        updateListApi: updateDataSourceListFromUser,\n        deleteApi: deleteDataSourceFromUser,\n        columns: [\n          {\n            title: i18n('team.datasource.alias'),\n            dataIndex: ['dataSource', 'alias'],\n            key: 'dataSource.alias',\n          },\n          {\n            title: i18n('team.datasource.url'),\n            dataIndex: ['dataSource', 'url'],\n            key: 'dataSource.url',\n          },\n          {\n            title: i18n('common.text.action'),\n            key: 'action',\n            width: 100,\n            render: (_: any, record: IUserWithDataSourceVO) => (\n              <Popconfirm\n                title={i18n('common.tips.delete.confirm')}\n                okText={i18n('common.button.affirm')}\n                cancelText={i18n('common.button.cancel')}\n                onConfirm={async () => {\n                  if (record.id !== undefined) {\n                    await deleteDataSourceFromUser({ id: record.id });\n                    message.success(i18n('common.text.successfullyDelete'));\n                    queryTableList();\n                  }\n                }}\n              >\n                <a href=\"#\" onClick={(e) => e.preventDefault()}>\n                  {i18n('common.button.delete')}\n                </a>\n              </Popconfirm>\n            ),\n          },\n        ],\n      },\n      [AffiliationType.TEAM_USER]: {\n        type: AffiliationType.TEAM_USER,\n        searchType: SearchType.USER,\n        title: i18n('team.user.name'),\n        byIdKey: 'teamId',\n        queryListApi: getUserListFromTeam,\n        updateListApi: updateUserListFromTeam,\n        deleteApi: deleteUserFromTeam,\n        columns: [\n          {\n            title: i18n('team.user.addForm.userName'),\n            dataIndex: ['user', 'userName'],\n            key: 'user.userName',\n          },\n          {\n            title: i18n('team.user.addForm.nickName'),\n            dataIndex: ['user', 'nickName'],\n            key: 'user.nickName',\n          },\n          {\n            title: i18n('common.text.action'),\n            key: 'action',\n            width: 100,\n            render: (_: any, record: ITeamWithUserVO) => (\n              <Popconfirm\n                title={i18n('common.tips.delete.confirm')}\n                okText={i18n('common.button.affirm')}\n                cancelText={i18n('common.button.cancel')}\n                onConfirm={async () => {\n                  if (record.id !== undefined) {\n                    await deleteUserFromTeam({ id: record.id });\n                    message.success(i18n('common.text.successfullyDelete'));\n                    queryTableList();\n                  }\n                }}\n              >\n                <a href=\"#\" onClick={(e) => e.preventDefault()}>\n                  {i18n('common.button.delete')}\n                </a>\n              </Popconfirm>\n            ),\n          },\n        ],\n      },\n      [AffiliationType.TEAM_DATASOURCE]: {\n        type: AffiliationType.TEAM_DATASOURCE,\n        searchType: SearchType.DATASOURCE,\n        title: i18n('team.action.affiliation.datasource'),\n        byIdKey: 'teamId',\n        queryListApi: getDataSourceListFromTeam,\n        updateListApi: updateDataSourceListFromTeam,\n        deleteApi: deleteDataSourceFromTeam,\n        columns: [\n          {\n            title: i18n('team.datasource.alias'),\n            dataIndex: ['dataSource', 'alias'],\n            key: 'dataSource.alias',\n          },\n          {\n            title: i18n('team.datasource.url'),\n            dataIndex: ['dataSource', 'url'],\n            key: 'dataSource.url',\n          },\n          {\n            title: i18n('common.text.action'),\n            key: 'action',\n            width: 100,\n            render: (_: any, record: ITeamWithDataSourceVO) => (\n              <Popconfirm\n                title={i18n('common.tips.delete.confirm')}\n                okText={i18n('common.button.affirm')}\n                cancelText={i18n('common.button.cancel')}\n                onConfirm={async () => {\n                  if (record.id !== undefined) {\n                    await deleteDataSourceFromUser({ id: record.id });\n                    message.success(i18n('common.text.successfullyDelete'));\n                    queryTableList();\n                  }\n                }}\n              >\n                <a href=\"#\" onClick={(e) => e.preventDefault()}>\n                  {i18n('common.button.delete')}\n                </a>\n              </Popconfirm>\n            ),\n          },\n        ],\n      },\n      [AffiliationType['DATASOURCE_USER/TEAM']]: {\n        type: AffiliationType['DATASOURCE_USER/TEAM'],\n        searchType: SearchType['USER/TEAM'],\n        title: i18n('team.datasource.rightManagement'),\n        byIdKey: 'dataSourceId',\n        queryListApi: getUserAndTeamListFromDataSource,\n        updateListApi: updateUserAndTeamListFromDataSource,\n        deleteApi: deleteUserOrTeamFromDataSource,\n        columns: [\n          {\n            title: i18n('team.datasource.code'),\n            dataIndex: ['accessObject', 'code'],\n            key: 'accessObject.code',\n          },\n          {\n            title: i18n('team.datasource.name'),\n            dataIndex: ['accessObject', 'name'],\n            key: 'accessObject.name',\n          },\n          {\n            title: i18n('team.datasource.status'),\n            dataIndex: ['accessObject', 'type'],\n            key: 'accessObject.type',\n            render: (status: ManagementType) => (\n              <Tag color={status === ManagementType.TEAM ? 'blue' : 'lime'}>{status}</Tag>\n            ),\n          },\n          {\n            title: i18n('common.text.action'),\n            key: 'action',\n            width: 100,\n            render: (_: any, record: IDataSourceAccessVO) => (\n              <Popconfirm\n                title={i18n('common.tips.delete.confirm')}\n                okText={i18n('common.button.affirm')}\n                cancelText={i18n('common.button.cancel')}\n                onConfirm={async () => {\n                  if (record.id !== undefined) {\n                    await deleteUserOrTeamFromDataSource({ id: record.id });\n                    message.success(i18n('common.text.successfullyDelete'));\n                    queryTableList();\n                  }\n                }}\n              >\n                <a href=\"#\" onClick={(e) => e.preventDefault()}>\n                  {i18n('common.button.delete')}\n                </a>\n              </Popconfirm>\n            ),\n          },\n        ],\n      },\n    }),\n    [props.byId, type],\n  );\n\n  const managementDataByType = type ? managementMap[type] : null;\n\n  useEffect(() => {\n    if (!open) {\n      return;\n    }\n    setSearchInput('');\n    setPagination({\n      searchKey: '',\n      current: 1,\n      pageSize: 10,\n      total: 0,\n      showSizeChanger: true,\n      showQuickJumper: true,\n    });\n    setTotal(0);\n    setModalInfo({\n      open: false,\n      type: managementDataByType?.searchType,\n    });\n  }, [props.byId, type, open]);\n\n  useEffect(() => {\n    queryTableList();\n  }, [pagination]);\n\n  const queryTableList = async (searchKey?: string) => {\n    const { current: pageNo, pageSize } = pagination;\n    const requestApi = managementDataByType?.queryListApi;\n    if (!requestApi || !isNumber(props.byId)) {\n      return;\n    }\n    const res = await requestApi({\n      searchKey: searchKey || pagination.searchKey,\n      pageNo,\n      pageSize,\n      [managementDataByType?.byIdKey]: props.byId,\n    });\n    if (res) {\n      setDataSource(res?.data ?? []);\n      setTotal(res?.total ?? 0);\n    }\n  };\n\n  const handleSearch = (searchKey: string) => {\n    setPagination({\n      ...pagination,\n      searchKey,\n    });\n  };\n\n  const handleTableChange = (p: any) => {\n    setPagination({\n      ...pagination,\n      ...p,\n    });\n  };\n\n  if (!managementDataByType) {\n    return;\n  }\n\n  return (\n    <Drawer open={open} width={720} title={managementDataByType?.title} onClose={props.onClose}>\n      <div className={styles.tableTop}>\n        <Input.Search\n          style={{ width: '200px' }}\n          placeholder={i18n('team.input.search.placeholder')}\n          value={searchInput}\n          onChange={(v) => setSearchInput(v.target.value)}\n          onSearch={handleSearch}\n          enterButton={<SearchOutlined />}\n        />\n        <Button\n          type=\"primary\"\n          icon={<PlusOutlined />}\n          onClick={() => {\n            setModalInfo({\n              ...modalInfo,\n              open: true,\n              type: managementDataByType.searchType,\n            });\n          }}\n        >\n          {i18n('common.button.add')}\n        </Button>\n      </div>\n      <Table\n        rowKey={'id'}\n        pagination={{\n          ...pagination,\n          total,\n        }}\n        columns={managementDataByType?.columns}\n        dataSource={dataSource}\n        onChange={handleTableChange}\n      />\n\n      <UniversalAddModal\n        {...modalInfo}\n        onConfirm={(values) => {\n          managementDataByType.updateListApi({ [managementDataByType.byIdKey]: props.byId, ...values }).then((res) => {\n            message.success(i18n('common.tips.updateSuccess'));\n            queryTableList();\n          });\n        }}\n        onClose={() => {\n          setModalInfo({\n            ...modalInfo,\n            open: false,\n          });\n        }}\n      />\n    </Drawer>\n  );\n}\n\nexport default UniversalDrawer;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/user-management/index.less",
    "content": ".tableTop {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin: 16px 0;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/team/user-management/index.tsx",
    "content": "import { createUser, deleteUser, getUserManagementList, updateUser } from '@/service/team';\nimport { Button, Form, Input, Modal, Popconfirm, Radio, Table, Tag, message } from 'antd';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { SearchOutlined, PlusOutlined } from '@ant-design/icons';\nimport styles from './index.less';\nimport { AffiliationType, IUserVO, RoleType, StatusType } from '@/typings/team';\nimport i18n from '@/i18n';\nimport UniversalDrawer from '../universal-drawer';\n\nconst formItemLayout = {\n  labelCol: { span: 6 },\n  wrapperCol: { span: 16 },\n  colon: false,\n};\n\nconst requireRule = { required: true, message: i18n('common.form.error.required') };\n\nfunction UserManagement() {\n  const [form] = Form.useForm();\n  const [dataSource, setDataSource] = useState<IUserVO[]>([]);\n  const [pagination, setPagination] = useState({\n    searchKey: '',\n    current: 1,\n    pageSize: 10,\n    total: 0,\n    showSizeChanger: true,\n    showQuickJumper: true,\n    pageSizeOptions: ['10', '20', '30', '40'],\n  });\n  const [isModalVisible, setIsModalVisible] = useState(false);\n  const [drawerInfo, setDrawerInfo] = useState<{ open: boolean; type?: AffiliationType; id?: number }>({\n    open: false,\n  });\n\n  const columns = useMemo(\n    () => [\n      {\n        title: i18n('team.user.userName'),\n        dataIndex: 'userName',\n        key: 'userName',\n      },\n      {\n        title: i18n('team.user.nickName'),\n        dataIndex: 'nickName',\n        key: 'nickName',\n      },\n      {\n        title: i18n('team.user.status'),\n        dataIndex: 'status',\n        key: 'status',\n        render: (status: StatusType) => <Tag color={status === StatusType.VALID ? 'green' : 'red'}>{status}</Tag>,\n      },\n      {\n        title: i18n('common.text.action'),\n        key: 'action',\n        width: 260,\n        render: (_: any, record: IUserVO) => (\n          <>\n            <Button\n              type=\"link\"\n              onClick={() => {\n                handleEdit(record);\n              }}\n            >\n              {i18n('common.button.edit')}\n            </Button>\n            <Button\n              type=\"link\"\n              onClick={() => {\n                setDrawerInfo({\n                  ...drawerInfo,\n                  open: true,\n                  type: AffiliationType.USER_TEAM,\n                  id: record.id,\n                });\n              }}\n            >\n              {i18n('team.action.affiliation.team')}\n            </Button>\n            <Button\n              type=\"link\"\n              onClick={() => {\n                setDrawerInfo({\n                  ...drawerInfo,\n                  open: true,\n                  type: AffiliationType.USER_DATASOURCE,\n                  id: record.id,\n                });\n              }}\n            >\n              {i18n('team.action.affiliation.datasource')}\n            </Button>\n            <Popconfirm\n              title={i18n('common.tips.delete.confirm')}\n              onConfirm={() => handleDelete(record.id)}\n              okText={i18n('common.button.affirm')}\n              cancelText={i18n('common.button.cancel')}\n            >\n              <a href=\"#\" onClick={(e) => e.preventDefault()}>\n                {i18n('common.button.delete')}\n              </a>\n            </Popconfirm>\n          </>\n        ),\n      },\n    ],\n    [],\n  );\n\n  useEffect(() => {\n    queryUserList();\n  }, [pagination.current, pagination.pageSize, pagination.searchKey]);\n\n  const queryUserList = async () => {\n    const { searchKey, current: pageNo, pageSize } = pagination;\n    let res = await getUserManagementList({ searchKey, pageNo, pageSize });\n    if (res) {\n      setDataSource(res?.data ?? []);\n      setPagination({\n        ...pagination,\n        total: res?.total ?? 0,\n      } as any);\n    }\n  };\n\n  const handleTableChange = (p: any) => {\n    setPagination({\n      ...pagination,\n      ...p,\n    });\n  };\n\n  const handleSearch = (searchKey: string) => {\n    setPagination({\n      ...pagination,\n      searchKey,\n    });\n  };\n\n  const handleCreateOrUpdateUser = async (userInfo: IUserVO) => {\n    const requestApi = userInfo?.id ? updateUser : createUser;\n    let res = await requestApi(userInfo);\n    if (res) {\n      queryUserList();\n    }\n  };\n\n  const handleEdit = (record: IUserVO) => {\n    form.setFieldsValue(record);\n    setIsModalVisible(true);\n  };\n\n  const handleDelete = async (id: number) => {\n    await deleteUser({ id });\n    message.success(i18n('common.text.successfullyDelete'));\n    queryUserList();\n  };\n\n  const isEditing = useMemo(() => {\n    return form.getFieldValue('id') !== undefined;\n  }, [form.getFieldValue('id')]);\n\n  return (\n    <div>\n      <div className={styles.tableTop}>\n        <Input.Search\n          maxLength={50}\n          style={{ width: '320px' }}\n          placeholder={i18n('team.input.search.placeholder')}\n          onSearch={handleSearch}\n          enterButton={<SearchOutlined />}\n        />\n        <Button\n          type=\"primary\"\n          icon={<PlusOutlined />}\n          onClick={() => {\n            form.resetFields();\n            setIsModalVisible(true);\n          }}\n        >\n          {i18n('team.action.addUser')}\n        </Button>\n      </div>\n      <Table\n        style={{\n          maxHeight: '82vh',\n          overflow: 'auto',\n        }}\n        sticky\n        rowKey={'id'}\n        dataSource={dataSource}\n        columns={columns}\n        pagination={pagination}\n        onChange={handleTableChange}\n      />\n\n      <Modal\n        title={isEditing ? i18n('team.action.editUser') : i18n('team.action.addUser')}\n        open={isModalVisible}\n        onOk={() => {\n          form\n            .validateFields()\n            .then((values) => {\n              const formValues = form.getFieldsValue(true);\n              handleCreateOrUpdateUser(formValues);\n              setIsModalVisible(false);\n              form.resetFields();\n            })\n            .catch((errorInfo) => {\n              form.scrollToField(errorInfo.errorFields[0].name);\n              form.setFields(errorInfo.errorFields);\n            })\n            .finally(() => {\n              form.resetFields();\n            });\n        }}\n        onCancel={() => {\n          form.resetFields();\n          setIsModalVisible(false);\n        }}\n      >\n        <Form\n          {...formItemLayout}\n          form={form}\n          autoComplete={'off'}\n          initialValues={{\n            roleCode: RoleType.USER,\n            status: StatusType.VALID,\n          }}\n        >\n          <Form.Item label={i18n('team.user.addForm.userName')} name=\"userName\" rules={[requireRule]}>\n            <Input maxLength={50} showCount autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={i18n('team.user.addForm.nickName')} name=\"nickName\" rules={[requireRule]}>\n            <Input maxLength={100} showCount />\n          </Form.Item>\n          <Form.Item\n            label={i18n('team.user.addForm.email')}\n            name=\"email\"\n            rules={[\n              requireRule,\n              {\n                type: 'email',\n                message: i18n('common.form.error.email'),\n              },\n            ]}\n          >\n            <Input autoComplete=\"off\" />\n          </Form.Item>\n          <Form.Item label={i18n('team.user.addForm.password')} name=\"password\" rules={[requireRule]}>\n            <Input.Password maxLength={30} placeholder={isEditing ? '******' : ''} autoComplete=\"fake-password\" />\n          </Form.Item>\n          <Form.Item label={i18n('team.user.addForm.roleCode')} name=\"roleCode\" rules={[requireRule]}>\n            <Radio.Group>\n              <Radio value={RoleType.ADMIN}>{i18n('team.user.addForm.roleCode.admin')}</Radio>\n              <Radio value={RoleType.USER}>{i18n('team.user.addForm.roleCode.user')}</Radio>\n            </Radio.Group>\n          </Form.Item>\n          <Form.Item label={i18n('team.user.addForm.status')} name=\"status\" rules={[requireRule]}>\n            <Radio.Group>\n              <Radio value={StatusType.VALID}>{i18n('team.user.addForm.status.valid')}</Radio>\n              <Radio value={StatusType.INVALID}>{i18n('team.user.addForm.status.invalid')}</Radio>\n            </Radio.Group>\n          </Form.Item>\n        </Form>\n      </Modal>\n\n      <UniversalDrawer\n        {...drawerInfo}\n        byId={drawerInfo.id}\n        onClose={() => {\n          setDrawerInfo({\n            ...drawerInfo,\n            open: false,\n          });\n        }}\n      />\n    </div>\n  );\n}\n\nexport default UserManagement;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/OperationLine/index.less",
    "content": ".operationLine{\n  flex-shrink: 0;\n  border-top: 1px solid var(--color-border-secondary);\n  height: 30px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 0px 4px;\n  margin: 0px -2px;\n  .operationLineLeft{\n    display: flex;\n    align-items: center;\n    >div{\n      margin: 0px 2px;\n    }\n  }\n}\n\n.searchBox{\n  flex-shrink: 0;\n  padding: 4px;\n  border-top: 1px solid var(--color-border-secondary);\n}"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/OperationLine/index.tsx",
    "content": "import React, { memo, useMemo, useState } from 'react';\nimport i18n from '@/i18n';\nimport styles from './index.less';\nimport { Input } from 'antd';\n\n// ----- constants -----\nimport { DatabaseTypeCode } from '@/constants';\n\n// ----- components -----\nimport Iconfont from '@/components/Iconfont';\n\n// ----- store -----\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\n\ninterface IProps {\n  searchValue: string;\n  setSearchValue: (value: string) => void;\n  getTreeData: (refresh?: boolean) => void;\n}\n\n// 不支持创建数据库的数据库类型\nconst notSupportCreateDatabaseType = [DatabaseTypeCode.H2];\n\n// 不支持创建schema的数据库类型\nconst notSupportCreateSchemaType = [DatabaseTypeCode.ORACLE];\n\nconst OperationLine = (props: IProps) => {\n  const [searchIng, setSearchIng] = useState<boolean>(false);\n  const { searchValue, setSearchValue, getTreeData } = props;\n  const { currentConnectionDetails, openCreateDatabaseModal } = useWorkspaceStore((state) => {\n    return {\n      currentConnectionDetails: state.currentConnectionDetails,\n      openCreateDatabaseModal: state.openCreateDatabaseModal,\n    };\n  });\n\n  const handelOpenCreateDatabaseModal = () => {\n    const type = currentConnectionDetails?.supportDatabase ? 'database' : 'schema';\n\n    openCreateDatabaseModal?.({\n      type,\n      relyOnParams: {\n        databaseType: currentConnectionDetails!.type!,\n        dataSourceId: currentConnectionDetails!.id!,\n      },\n      executedCallback: () => {\n        getTreeData(true);\n      },\n    });\n  };\n\n  const showCreate = useMemo(() => {\n    if (currentConnectionDetails?.supportDatabase) {\n      return !notSupportCreateDatabaseType.includes(currentConnectionDetails!.type!);\n    }\n    if (currentConnectionDetails?.supportSchema) {\n      return !notSupportCreateSchemaType.includes(currentConnectionDetails!.type!);\n    }\n  }, [currentConnectionDetails]);\n\n  return (\n    <>\n      <div className={styles.operationLine}>\n        <div className={styles.operationLineLeft}>\n          {showCreate && (\n            <Iconfont onClick={handelOpenCreateDatabaseModal} code=\"&#xeb78;\" box boxSize={20} size={15} />\n          )}\n          <Iconfont\n            onClick={() => {\n              getTreeData(true);\n            }}\n            code=\"&#xe668;\"\n            box\n            boxSize={20}\n            size={14}\n          />\n          {/* {searchIng ? (\n            <Iconfont\n              onClick={() => {\n                setSearchIng(false);\n                setSearchValue('');\n              }}\n              box\n              boxSize={20}\n              code=\"&#xe634;\"\n            />\n          ) : (\n            <Iconfont\n              onClick={() => {\n                setSearchIng(true);\n              }}\n              code=\"&#xe888;\"\n              box\n              boxSize={20}\n              size={14}\n            />\n          )} */}\n        </div>\n        {/* <div>1</div> */}\n      </div>\n      {/* {searchIng && ( */}\n      <div className={styles.searchBox}>\n        <Input\n          size=\"small\"\n          prefix={<Iconfont code=\"&#xe888;\" />}\n          value={searchValue}\n          onChange={(e) => setSearchValue(e.target.value)}\n          allowClear\n          placeholder={i18n('workspace.tree.search.placeholder')}\n        />\n      </div>\n      {/* )} */}\n    </>\n  );\n};\n\nexport default memo(OperationLine);\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/SQLExecute/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.sqlExecute {\n  height: 100%;\n}\n\n.boxRightCenter {\n  height: 100%;\n}\n\n.boxRightConsole {\n  height: 40vh;\n  overflow: hidden;\n}\n\n.boxRightResult {\n  border-top: 1px solid var(--color-border);\n  height: 0px;\n  flex: 1;\n  position: relative;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/SQLExecute/index.tsx",
    "content": "import React, { memo, useEffect, useRef, useState } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport DraggableContainer from '@/components/DraggableContainer';\nimport ConsoleEditor, { IConsoleRef } from '@/components/ConsoleEditor';\nimport SearchResult, { ISearchResultRef } from '@/components/SearchResult';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport { IBoundInfo } from '@/typings';\n\ninterface IProps {\n  boundInfo: IBoundInfo;\n  initDDL: string;\n  // 异步加载sql\n  loadSQL: () => Promise<string>;\n}\n\nconst SQLExecute = memo<IProps>((props) => {\n  const { boundInfo: _boundInfo, initDDL, loadSQL } = props;\n  const draggableRef = useRef<any>();\n  const searchResultRef = useRef<ISearchResultRef>(null);\n  const consoleRef = useRef<IConsoleRef>(null);\n  const [boundInfo, setBoundInfo] = useState<IBoundInfo>(_boundInfo);\n  const activeConsoleId = useWorkspaceStore((state) => state.activeConsoleId);\n\n  useEffect(() => {\n    if (loadSQL) {\n      loadSQL().then((sql) => {\n        consoleRef.current?.editorRef?.setValue(sql, 'cover');\n      });\n    }\n  }, []);\n\n  return (\n    <div className={classnames(styles.sqlExecute)}>\n      <DraggableContainer layout=\"column\" className={styles.boxRightCenter}>\n        <div ref={draggableRef} className={styles.boxRightConsole}>\n          <ConsoleEditor\n            ref={consoleRef}\n            source=\"workspace\"\n            defaultValue={initDDL}\n            boundInfo={boundInfo}\n            setBoundInfo={setBoundInfo}\n            hasAiChat={true}\n            hasAi2Lang={true}\n            isActive={activeConsoleId === boundInfo.consoleId}\n            onExecuteSQL={(sql) => {\n              searchResultRef.current?.handleExecuteSQL(sql);\n            }}\n          />\n        </div>\n        <div className={styles.boxRightResult}>\n          <SearchResult\n            isActive={activeConsoleId === boundInfo.consoleId}\n            ref={searchResultRef}\n            executeSqlParams={boundInfo}\n          />\n        </div>\n      </DraggableContainer>\n    </div>\n  );\n});\n\nexport default SQLExecute;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/SaveList/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.saveModule {\n  flex-shrink: 0;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n.leftModuleTitle {\n  flex-shrink: 0;\n  margin-bottom: 4px;\n  padding: 0px 10px;\n  height: 32px;\n  display: flex;\n  align-items: center;\n  border-bottom: 1px solid var(--color-border);\n  .leftModuleTitleText {\n    width: 100%;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    height: 26px;\n    .modelName {\n      font-weight: bold;\n      line-height: 100%;\n    }\n    .iconBox {\n      display: flex;\n      align-items: center;\n      height: 100%;\n    }\n    .refreshIcon {\n      margin-right: 10px;\n    }\n    .refreshIcon,\n    .searchIcon {\n      cursor: pointer;\n      height: 100%;\n      display: flex;\n      align-items: center;\n      &:hover {\n        color: var(--color-primary);\n      }\n    }\n  }\n  .leftModuleTitleSearch {\n    height: 26px;\n  }\n}\n\n.loadingContent {\n  height: auto;\n  padding-bottom: 4px;\n}\n\n.saveBoxList {\n  flex: 1;\n  height: 0px;\n  padding: 0px 4px;\n  overflow-y: hidden;\n  &:hover {\n    overflow-y: auto;\n  }\n}\n\n.leftModuleTitleShadow {\n  // 地步加一点模糊\n  box-shadow: 0px 1px 2px 0px var(--color-border);\n}\n\n.saveItem {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 0px 6px;\n  height: 26px;\n  line-height: 26px;\n  border-radius: 4px;\n  user-select: none;\n  cursor: pointer;\n  .saveItemText {\n    width: 0px;\n    flex: 1;\n    .f-single-line();\n    display: flex;\n    align-items: center;\n    .iconBox {\n      width: 20px;\n      flex-shrink: 0;\n    }\n    .itemName {\n      flex: 1;\n      .f-single-line();\n    }\n  }\n\n  &:hover {\n    background-color: var(--color-hover-bg);\n    color: var(--color-primary);\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/SaveList/index.tsx",
    "content": "import React, { useState, useEffect, useRef } from 'react';\nimport i18n from '@/i18n';\nimport { Input, Dropdown, Modal } from 'antd';\nimport Iconfont from '@/components/Iconfont';\nimport LoadingContent from '@/components/Loading/LoadingContent';\nimport historyServer from '@/service/history';\nimport { ConsoleOpenedStatus, workspaceTabConfig } from '@/constants';\nimport { IConsole, ITreeNode } from '@/typings';\nimport styles from './index.less';\nimport { approximateList } from '@/utils';\nimport { addWorkspaceTab, getSavedConsoleList } from '@/pages/main/workspace/store/console';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport MenuLabel from '@/components/MenuLabel';\n\nconst SaveList = () => {\n  const [searching, setSearching] = useState<boolean>(false);\n  const inputRef = useRef<any>();\n  const [searchedList, setSearchedList] = useState<ITreeNode[] | undefined>();\n  const leftModuleTitleRef = useRef<any>(null);\n  const saveBoxListRef = useRef<any>(null);\n  const consoleList = useWorkspaceStore((state) => state.savedConsoleList);\n  const [editData, setEditData] = useState<any>(null);\n\n  useEffect(() => {\n    getSavedConsoleList();\n  }, []);\n\n  useEffect(() => {\n    if (searching) {\n      inputRef.current!.focus({\n        cursor: 'start',\n      });\n    }\n  }, [searching]);\n\n  function openSearch() {\n    setSearching(true);\n  }\n\n  function onBlur() {\n    if (!inputRef.current.input.value) {\n      setSearching(false);\n      setSearchedList(undefined);\n    }\n  }\n\n  function onChange(value: string) {\n    if (consoleList) {\n      setSearchedList(approximateList(consoleList as any, value));\n    }\n  }\n\n  function openConsole(item: IConsole) {\n    const params: any = {\n      id: item.id,\n      tabOpened: ConsoleOpenedStatus.IS_OPEN,\n    };\n    historyServer.updateSavedConsole(params).then(() => {\n      addWorkspaceTab({\n        id: item.id,\n        type: item.operationType,\n        title: item.name,\n        uniqueData: {\n          dataSourceId: item.dataSourceId,\n          dataSourceName: item.dataSourceName,\n          databaseType: item.type,\n          databaseName: item.databaseName,\n          schemaName: item.schemaName,\n          status: item.status,\n          ddl: item.ddl,\n          connectable: item.connectable,\n        },\n      });\n    });\n  }\n\n  function deleteSaved(data: IConsole) {\n    const params: any = {\n      id: data.id,\n    };\n    historyServer.deleteSavedConsole(params).then(() => {\n      getSavedConsoleList();\n    });\n  }\n\n  const editSaved = (data: IConsole) => {\n    setEditData(data);\n  };\n\n  return (\n    <>\n      <div className={styles.saveModule}>\n        <div ref={leftModuleTitleRef} className={styles.leftModuleTitle}>\n          {searching ? (\n            <div className={styles.leftModuleTitleSearch}>\n              <Input\n                ref={inputRef}\n                size=\"small\"\n                placeholder={i18n('common.text.search')}\n                prefix={<Iconfont code=\"&#xe600;\" />}\n                onBlur={onBlur}\n                onChange={(e) => onChange(e.target.value)}\n                allowClear\n              />\n            </div>\n          ) : (\n            <div className={styles.leftModuleTitleText}>\n              <div className={styles.modelName}>{i18n('workspace.title.savedConsole')}</div>\n              <div className={styles.iconBox}>\n                {/* <div className={styles.refreshIcon} onClick={() => refreshTableList()}>\n                  <Iconfont code=\"&#xec08;\" />\n                </div> */}\n                <div className={styles.searchIcon} onClick={() => openSearch()}>\n                  <Iconfont code=\"&#xe600;\" />\n                </div>\n              </div>\n            </div>\n          )}\n        </div>\n        <div ref={saveBoxListRef} className={styles.saveBoxList}>\n          <LoadingContent className={styles.loadingContent} data={consoleList} handleEmpty>\n            {(searchedList || consoleList)?.map((t) => {\n              return (\n                <Dropdown\n                  key={t.id}\n                  trigger={['contextMenu']}\n                  menu={{\n                    items: [\n                      {\n                        key: 'open',\n                        label: <MenuLabel icon=\"&#xec83;\" label={i18n('common.button.open')} />,\n                        onClick: () => {\n                          openConsole(t);\n                        },\n                      },\n                      {\n                        key: 'edit',\n                        label: <MenuLabel icon=\"&#xe602;\" label={i18n('common.text.rename')} />,\n                        onClick: () => {\n                          editSaved(t);\n                        },\n                      },\n                      {\n                        key: 'delete',\n                        label: <MenuLabel icon=\"&#xe6a7;\" label={i18n('common.button.delete')} />,\n                        onClick: () => {\n                          deleteSaved(t);\n                        },\n                      },\n                    ],\n                  }}\n                >\n                  <div\n                    onDoubleClick={() => {\n                      openConsole(t);\n                    }}\n                    className={styles.saveItem}\n                  >\n                    <div className={styles.saveItemText}>\n                      <div className={styles.iconBox}>\n                        <Iconfont code={workspaceTabConfig[t.operationType]?.icon} />\n                      </div>\n                      <div className={styles.itemName} dangerouslySetInnerHTML={{ __html: t.name }} />\n                    </div>\n                  </div>\n                </Dropdown>\n              );\n            })}\n          </LoadingContent>\n        </div>\n      </div>\n      <Modal\n        title={i18n('common.text.rename')}\n        open={!!editData}\n        onOk={() => {\n          const params: any = {\n            id: editData.id,\n            name: editData.name,\n          };\n          historyServer.updateSavedConsole(params).then(() => {\n      \n            getSavedConsoleList();\n            setEditData(null);\n          });\n        }}\n        onCancel={() => setEditData(null)}\n      >\n        <Input\n          value={editData?.name}\n          onChange={(e) => {\n            setEditData({\n              ...editData,\n              name: e.target.value,\n            });\n          }}\n        />\n      </Modal>\n    </>\n  );\n};\n\nexport default SaveList;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/TableList/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.treeContainer{\n  flex: 1;\n  height: 0px;\n  display: flex;\n  flex-direction: column;\n}\n\n.treeBox{\n  flex: 1;\n  height: 0px;\n}\n\n.leftModuleTitleShadow{\n  border-bottom: 1px solid var(--color-border-secondary);\n}\n\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/TableList/index.tsx",
    "content": "import React, { memo, useEffect, useState } from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\n\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\n\n// ----- components -----\nimport OperationLine from '../OperationLine';\n\nimport Tree from '@/blocks/Tree';\nimport { treeConfig } from '@/blocks/Tree/treeConfig';\nimport { ITreeNode } from '@/typings';\nimport { TreeNodeType } from '@/constants';\n\ninterface IProps {\n  className?: string;\n}\n\nexport default memo<IProps>((props) => {\n  const { className } = props;\n  const [treeData, setTreeData] = useState<ITreeNode[] | null>(null);\n\n  const [searchValue, setSearchValue] = useState<string>('');\n\n  const currentConnectionDetails = useWorkspaceStore((state) => state.currentConnectionDetails);\n\n  const getTreeData = (refresh = false) => {\n    if (!currentConnectionDetails?.id) {\n      setTreeData([]);\n      return;\n    }\n    const treeNodeType = currentConnectionDetails.supportDatabase ? TreeNodeType.DATA_SOURCE : TreeNodeType.DATABASE;\n    setTreeData(null);\n    treeConfig[treeNodeType]\n      .getChildren?.({\n        dataSourceId: currentConnectionDetails.id,\n        dataSourceName: currentConnectionDetails.alias,\n        refresh: refresh,\n        extraParams: {\n          dataSourceId: currentConnectionDetails.id,\n          dataSourceName: currentConnectionDetails.alias,\n          databaseType: currentConnectionDetails.type,\n        },\n      })\n      .then((res) => {\n        setTreeData(res);\n      })\n      .catch(() => {\n        setTreeData([]);\n      });\n  };\n\n  useEffect(() => {\n    getTreeData();\n  }, [currentConnectionDetails]);\n\n  return (\n    <div className={classnames(styles.treeContainer, className)}>\n      <OperationLine getTreeData={getTreeData} searchValue={searchValue} setSearchValue={setSearchValue} />\n      <Tree className={styles.treeBox} searchValue={searchValue} treeData={treeData} />\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/ViewAllTable/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.allTable {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n.contentCenter{\n  flex: 1;\n  display: flex;\n  width: 100%;\n  .tableBox{\n    flex: 1;\n  }\n  .viewDDLBox{\n    border-left: 1px solid var(--color-border);\n    flex-shrink: 0;\n    width: 200px;\n    display: flex;\n    flex-direction: column;\n  }\n  .viewDDLHeader{\n    height: 25px;\n    border-bottom: 1px solid var(--color-border);\n    box-sizing: border-box;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-weight: bold;\n    // background-color: var(--color-bg-subtle);\n  }\n  .viewDDL{\n    flex: 1;\n  }\n}\n\n.headerBox {\n  height: 30px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 0px 4px;\n  border-bottom: 1px solid var(--color-border);\n  .headerBoxLeft {\n    display: flex;\n    align-items: center;\n  }\n}\n\n.pagingBox {\n  height: 40px;\n  padding: 4px 4px;\n  box-sizing: border-box;\n  display: flex;\n  align-items: center;\n  justify-content: flex-end;\n  border-top: 1px solid var(--color-border);\n}\n\n.tableCell {\n  padding: 2px 6px;\n  height: 24px;\n  box-sizing: border-box;\n  cursor: pointer;\n}\n\n.activeTableCell {\n  background-color: var(--color-primary-hover);\n  color: var(--color-bg-base);\n}\n\n:global {\n  .ant-table-wrapper .ant-table-cell {\n    padding: 0px;\n  }\n  .ant-table-wrapper .ant-table-thead > tr > th {\n    padding: 2px 6px;\n    height: 24px;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/ViewAllTable/index.tsx",
    "content": "import React, { memo, useEffect } from 'react';\nimport i18n from '@/i18n';\nimport styles from './index.less';\nimport classnames from 'classnames';\nimport { Table, Dropdown, Input, Pagination } from 'antd';\nimport { DatabaseTypeCode, TreeNodeType, OperationColumn, WorkspaceTabType } from '@/constants';\nimport sqlServer from '@/service/sql';\nimport type { ColumnsType } from 'antd/es/table';\nimport { IPageParams } from '@/typings';\nimport { v4 as uuid } from 'uuid';\n\n// ----- components -----\nimport Iconfont from '@/components/Iconfont';\nimport { getRightClickMenu } from '@/blocks/Tree/hooks/useGetRightClickMenu';\nimport MenuLabel from '@/components/MenuLabel';\nimport { setCurrentWorkspaceGlobalExtend } from '@/pages/main/workspace/store/common';\n\n// ----- store -----\nimport { addWorkspaceTab } from '@/pages/main/workspace/store/console';\n\nconst { Search } = Input;\n\ninterface IProps {\n  className?: string;\n  uniqueData: {\n    dataSourceId: string;\n    dataSourceName: string;\n    databaseType: DatabaseTypeCode;\n    databaseName?: string;\n    schemaName?: string;\n  };\n}\n\nexport default memo<IProps>((props) => {\n  const { className, uniqueData } = props;\n  const [tableData, setTableData] = React.useState<any[] | null>(null);\n  const [tableLoading, setTableLoading] = React.useState<boolean>(false);\n  const tableBoxRef = React.useRef<HTMLDivElement>(null);\n  const [allTableWidth, setAllTableWidth] = React.useState(0);\n  const [allTableHeight, setAllTableHeight] = React.useState(0);\n  // 选中表\n  const [activeId, setActiveId] = React.useState<string>('');\n  const [tableDataTotal, setTableDataTotal] = React.useState(0);\n  const [currentPageNo, setCurrentPageNo] = React.useState(1);\n  const [openDropdown, setOpenDropdown] = React.useState<boolean | undefined>(undefined);\n  const [dropdownItems, setDropdownItems] = React.useState<any[]>([]);\n\n  useEffect(() => {\n    getTable({\n      pageNo: 1,\n      pageSize: 1000,\n    });\n  }, []);\n\n  useEffect(() => {\n    if (openDropdown === false) {\n      setOpenDropdown(undefined);\n    }\n  }, [openDropdown]);\n\n  const getTable = (params: IPageParams) => {\n    setCurrentPageNo(params.pageNo);\n    setTableLoading(true);\n    sqlServer\n      .getTableList({\n        ...props.uniqueData,\n        ...(params || {}),\n      } as any)\n      .then((res) => {\n        setTableDataTotal(res.total);\n        const data = res.data.map((t) => {\n          const key = uuid();\n          return {\n            uuid: key,\n            name: t.name,\n            treeNodeType: TreeNodeType.TABLE,\n            key: t.name,\n            pinned: t.pinned,\n            comment: t.comment,\n            extraParams: {\n              ...uniqueData,\n              tableName: t.name,\n            },\n          };\n        });\n        setTableData(data);\n      })\n      .finally(() => {\n        setTableLoading(false);\n      });\n  };\n\n  const paginationChange = (pageNo: number) => {\n    getTable({\n      pageNo,\n      pageSize: 1000,\n    });\n  };\n\n  const createTable = () => {\n    addWorkspaceTab({\n      id: uuid(),\n      title: i18n('editTable.button.createTable'),\n      type: WorkspaceTabType.CreateTable,\n      uniqueData: {\n        ...props.uniqueData,\n      },\n    });\n  };\n\n  const getDropdownsItems = (record) => {\n    const rightClickMenu = getRightClickMenu({\n      treeNodeData: record,\n      loadData: () => {},\n    });\n    const dropdownsItems: any = rightClickMenu.map((item) => {\n      return {\n        key: item.key,\n        type: item.type,\n        onClick: () => {\n          setOpenDropdown(false);\n          item.onClick(record);\n        },\n        label: <MenuLabel icon={item.labelProps.icon} label={item.labelProps.label} />,\n      };\n    });\n\n    const excludeList = [\n      OperationColumn.OpenTable,\n      OperationColumn.CreateConsole,\n      // OperationColumn.Pin,\n      OperationColumn.ViewDDL,\n      OperationColumn.EditTable,\n      OperationColumn.CopyName,\n    ];\n\n    return dropdownsItems.filter((item) => excludeList.includes(item.type));\n  };\n\n  const renderCell = (text, record) => {\n    return (\n      <div className={classnames(styles.tableCell, { [styles.activeTableCell]: activeId === record.key })}>{text}</div>\n    );\n  };\n\n  const columns: ColumnsType<any> = [\n    {\n      title: 'Table name',\n      dataIndex: 'name',\n      key: 'name',\n      render: renderCell,\n    },\n    {\n      title: 'Comment',\n      dataIndex: 'comment',\n      key: 'comment',\n      render: renderCell,\n    },\n  ];\n\n  // 监听allTable的高度的变化\n  useEffect(() => {\n    const resizeObserver = new ResizeObserver((entries) => {\n      const { width, height } = entries[0].contentRect;\n      setAllTableWidth(width);\n      setAllTableHeight(height);\n    });\n    resizeObserver.observe(tableBoxRef.current!);\n  }, []);\n\n  // 监听allTable的宽度的变化\n  useEffect(() => {\n    const resizeObserver = new ResizeObserver((entries) => {\n      const { width, height } = entries[0].contentRect;\n      setAllTableWidth(width);\n      setAllTableHeight(height);\n    });\n    resizeObserver.observe(tableBoxRef.current!);\n  }, []);\n\n  // useEffect(() => {\n  //   const record = tableData?.find((t) => t.key === activeId);\n  //   if (record) {\n  //     sqlServer\n  //       .exportCreateTableSql({\n  //         ...uniqueData,\n  //         tableName: record.name,\n  //       } as any)\n  //       .then((res) => {\n  //         setViewDDLSql(res);\n  //       });\n  //   }\n  // }, [activeId]);\n\n  const onSearch = (value: string) => {\n    getTable({\n      pageNo: 1,\n      pageSize: 1000,\n      searchKey: value,\n    });\n  };\n\n  return (\n    // <ConfigProvider\n    //   theme={{\n    //     token: {\n    //       motion: false,\n    //     },\n    //   }}\n    // >\n    <div className={classnames(styles.allTable, className)}>\n      <div className={styles.headerBox}>\n        <div className={styles.headerBoxLeft}>\n          <Iconfont code=\"&#xe726;\" box boxSize={24} onClick={createTable} />\n          <Iconfont\n            onClick={() => {\n              getTable({\n                pageNo: 1,\n                pageSize: 1000,\n                refresh: true,\n              });\n            }}\n            code=\"&#xe668;\"\n            box\n            boxSize={24}\n          />\n        </div>\n        <div className={styles.headerBoxRight}>\n          <Search size=\"small\" placeholder={i18n('common.text.search')} onSearch={onSearch} style={{ width: 150 }} />\n        </div>\n      </div>\n      <div className={styles.contentCenter}>\n        <Dropdown\n          open={openDropdown}\n          menu={{\n            items: dropdownItems,\n          }}\n          trigger={['contextMenu']}\n          onOpenChange={(_open) => {\n            setOpenDropdown(_open);\n          }}\n        >\n          <div ref={tableBoxRef} className={styles.tableBox}>\n            <Table\n              loading={tableLoading}\n              onRow={(row) => {\n                return {\n                  onClick: () => {\n                    setActiveId(row.key);\n                    setCurrentWorkspaceGlobalExtend({\n                      code: 'viewDDL',\n                      uniqueData: {\n                        ...uniqueData,\n                        tableName: row.name,\n                      }\n                    });\n                  },\n                  onContextMenu: (event) => {\n                    event.preventDefault();\n                    setActiveId(row.key);\n                    setOpenDropdown(true);\n                    setDropdownItems(getDropdownsItems(tableData?.find((t) => t.key === row.key)));\n                  },\n                };\n              }}\n              virtual\n              scroll={{ x: allTableWidth - 10, y: allTableHeight - 25 }}\n              columns={columns}\n              pagination={false}\n              dataSource={tableData || []}\n            />\n          </div>\n        </Dropdown>\n      </div>\n      {/* {tableDataTotal > 1000 && (\n      )} */}\n      <div className={styles.pagingBox}>\n        <Pagination\n          onChange={paginationChange}\n          showSizeChanger={false}\n          current={currentPageNo}\n          pageSize={1000}\n          total={tableDataTotal}\n        />\n      </div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/GlobalExtendComponents/index.less",
    "content": ".viewDDLBox {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  .viewDDLHeader {\n    flex-shrink: 0;\n    line-height: 32px;\n    padding: 0px 10px;\n    border-bottom: 1px solid var(--color-border);\n    font-weight: bold;\n  }\n  .viewDDL {\n    flex: 1;\n  }\n}\n\n.noInformation{\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/GlobalExtendComponents/index.tsx",
    "content": "import React from 'react';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport {  GlobalComponents } from '../config';\nimport ViewDDL from '@/components/ViewDDL';\nimport styles from './index.less';\n\nconst GlobalExtendComponents = () => {\n  const { currentWorkspaceGlobalExtend } = useWorkspaceStore((state) => {\n    return {\n      currentWorkspaceGlobalExtend: state.currentWorkspaceGlobalExtend,\n    };\n  });\n\n  switch (currentWorkspaceGlobalExtend?.code) {\n    case GlobalComponents.view_ddl:\n      return  <div className={styles.viewDDLBox}>\n      <div className={styles.viewDDLHeader}>{`${currentWorkspaceGlobalExtend.uniqueData.tableName}-DDL`}</div>\n      <ViewDDL data={currentWorkspaceGlobalExtend.uniqueData} />;\n    </div>\n    default:\n      return <div className={styles.noInformation}>No information</div>;\n  }\n};\n\nexport default GlobalExtendComponents;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/WorkspaceExtendBody/index.less",
    "content": ".WorkspaceExtendBody{\n  // flex: 1;\n}"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/WorkspaceExtendBody/index.tsx",
    "content": "import React, { useMemo } from 'react';\nimport {extendConfig} from '../config';\nimport {useWorkspaceStore} from '@/pages/main/workspace/store';\n\n\nexport default () => {\n  const {currentWorkspaceExtend} = useWorkspaceStore((state) => {\n    return {\n      currentWorkspaceExtend: state.currentWorkspaceExtend,\n    }\n  });\n  const Component = useMemo(() => {\n    return extendConfig.find((item) => item.code === currentWorkspaceExtend)?.components \n  }, [currentWorkspaceExtend]);\n\n  return Component ? <Component /> : false\n};\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/WorkspaceExtendNav/index.less",
    "content": "@import '../../../../../../styles/var.less';\n\n// .workspaceExtend {\n//   display: none;\n//   background-color: var(--color-bg-subtle);\n//   .workspaceExtendMain {\n//     flex: 1;\n//     width: 0px;\n//     border-right: 1px solid var(--color-border);\n//   }\n// }\n\n.workspaceExtendNav {\n  flex-shrink: 0;\n  width: 38px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  padding: 5px 0px;\n}\n\n.rightBarFront {\n  margin: 2px 0px;\n}\n\n.workspaceExtendActive {\n  display: flex;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/WorkspaceExtendNav/index.tsx",
    "content": "import React from 'react';\nimport styles from './index.less';\nimport classnames from 'classnames';\n// import i18n from '@/i18n';\nimport { Popover } from 'antd';\nimport Iconfont from '@/components/Iconfont';\nimport {extendConfig} from '../config';\n\nimport { setCurrentWorkspaceExtend } from '@/pages/main/workspace/store/common';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\n\ninterface IToolbar {\n  code: string;\n  title: string;\n  icon: string;\n  components: any;\n}\n\ninterface IProps {\n  className?: any;\n}\n\nexport default (props:IProps) => {\n  const { className } = props;\n  const { currentWorkspaceExtend } = useWorkspaceStore((state) => {\n    return {\n      currentWorkspaceExtend: state.currentWorkspaceExtend,\n    };\n  });\n\n  const changeExtend = (item: IToolbar) => {\n    if (currentWorkspaceExtend === item.code) {\n      setCurrentWorkspaceExtend(null);\n      return;\n    }\n    setCurrentWorkspaceExtend(item.code);\n  };\n\n  return (\n    <div className={classnames(className,styles.workspaceExtendNav)}>\n      {extendConfig.map((item, index) => {\n        return (\n          <Popover mouseEnterDelay={0.8} key={index} placement=\"left\" content={item.title}>\n            <div className={styles.rightBarFront} onClick={changeExtend.bind(null, item)}>\n              <Iconfont code={item.icon} box size={18} active={currentWorkspaceExtend === item.code} />\n            </div>\n          </Popover>\n        );\n      })}\n    </div>\n  );\n\n};\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/config.tsx",
    "content": "import i18n from '@/i18n';\nimport Output from '@/components/Output';\nimport GlobalExtendComponents from './GlobalExtendComponents';\nimport SaveList from '../SaveList';\nimport ViewDDL from '@/components/ViewDDL';\n\ninterface IToolbar {\n  code: string;\n  title: string;\n  icon: string;\n  components: any;\n}\n\nexport enum GlobalComponents {\n  view_ddl = 'viewDDL',\n  executive_log = 'executiveLog',\n  save_list = 'saveList'\n}\n\nexport const globalComponents: {\n  [key in GlobalComponents]: any;\n} = {\n  [GlobalComponents.view_ddl]: ViewDDL,\n  [GlobalComponents.executive_log]: Output,\n  [GlobalComponents.save_list]: SaveList\n}\n\nexport const extendConfig: IToolbar[] = [\n  {\n    code: 'info',\n    title: i18n('common.title.info'),\n    icon: '\\ue8e8',\n    components: GlobalExtendComponents,\n  },\n  {\n    code: 'executiveLog',\n    title: i18n('common.title.executiveLogging'),\n    icon: '\\ue8ad',\n    components: globalComponents.executiveLog,\n  },\n  {\n    code: 'saveList',\n    title: i18n('workspace.title.savedConsole'),\n    icon: '\\ue619',\n    components: globalComponents.saveList,\n  },\n];\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.workspaceExtend {\n  display: none;\n  background-color: var(--color-bg-subtle);\n  .workspaceExtendBar {\n    flex-shrink: 0;\n    width: 38px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    padding: 5px 0px;\n  }\n  .workspaceExtendMain {\n    flex: 1;\n    width: 0px;\n    border-right: 1px solid var(--color-border);\n  }\n}\n\n.rightBarFront {\n  margin: 2px 0px;\n}\n\n.workspaceExtendActive {\n  display: flex;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceExtend/index.tsx",
    "content": "import React, { useState } from 'react';\nimport styles from './index.less';\n// import classnames from 'classnames';\nimport { Popover } from 'antd';\nimport Iconfont from '@/components/Iconfont';\nimport Output from '@/components/Output';\nimport SaveList from '../SaveList';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport i18n from '@/i18n';\n\ninterface IToolbar {\n  code: string;\n  title: string;\n  icon: string;\n  components: any;\n}\n\nexport const useWorkspaceExtend = () => {\n  const [activeExtend, setActiveExtend] = useState<IToolbar | null>(null);\n  const { panelRight } = useWorkspaceStore((state) => state.layout);\n\n  const toolbarConfig: IToolbar[] = [\n    // {\n    //   code: 'ai',\n    //   title: 'AI',\n    //   icon: '\\ue8ad',\n    //   components: <div>ai</div>,\n    // },\n    {\n      code: 'executiveLog',\n      title: i18n('common.title.executiveLogging'),\n      icon: '\\ue8ad',\n      components: <Output />,\n    },\n    {\n      code: 'saveList',\n      title: i18n('workspace.title.savedConsole'),\n      icon: '\\ue619',\n      components: <SaveList />,\n    },\n  ];\n\n  const changeExtend = (item: IToolbar) => {\n    if (activeExtend?.code === item.code) {\n      setActiveExtend(null);\n      return;\n    }\n    setActiveExtend(item);\n  };\n\n  // return (\n  //   <div\n  //     className={classnames(styles.workspaceExtend, className, {\n  //       [styles.workspaceExtendActive]: panelRight,\n  //     })}\n  //   >\n  //   </div>\n  // );\n\n  const extendBody = (\n    <>{activeExtend && <div className={styles.workspaceExtendMain}>{activeExtend?.components}</div>}</>\n  );\n  const extendNav = (\n    <div className={styles.workspaceExtendBar}>\n      {toolbarConfig.map((item, index) => {\n        return (\n          <Popover mouseEnterDelay={0.8} key={index} placement=\"left\" content={item.title}>\n            <div className={styles.rightBarFront} onClick={changeExtend.bind(null, item)}>\n              <Iconfont code={item.icon} box size={18} active={activeExtend?.code === item.code} />\n            </div>\n          </Popover>\n        );\n      })}\n    </div>\n  );\n\n  return [extendBody, extendNav];\n};\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceLeft/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.workspaceLeft {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  background-color: var(--color-bg-subtle);\n  box-sizing: border-box;\n  min-width: 200px;\n}\n\n.noConnectionList {\n  height: 100%;\n  margin-top: 30vh;\n  text-align: center;\n  font-size: 14px;\n  .noConnectionListIcon {\n    font-size: 60px;\n    color: var(--color-primary);\n  }\n  .noConnectionListTips {\n    margin: 10px 0px;\n  }\n  .create {\n    color: var(--color-primary);\n    text-decoration: underline;\n    cursor: pointer;\n    margin-right: 4px;\n    &:hover {\n      color: var(--color-primary-hover);\n    }\n  }\n}\n\n.createButtonBox {\n  padding: 10px 0px 10px;\n}\n\n.divider {\n  margin: 0px;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceLeft/index.tsx",
    "content": "import React, { memo } from 'react';\nimport i18n from '@/i18n';\nimport classnames from 'classnames';\nimport styles from './index.less';\nimport TableList from '../TableList';\nimport WorkspaceLeftHeader from '../WorkspaceLeftHeader';\nimport CreateDatabase from '@/components/CreateDatabase';\nimport Iconfont from '@/components/Iconfont';\nimport { useConnectionStore } from '@/pages/main/store/connection';\nimport { setMainPageActiveTab } from '@/pages/main/store/main';\n\nconst WorkspaceLeft = memo(() => {\n  const { connectionList } = useConnectionStore((state) => {\n    return {\n      connectionList: state.connectionList,\n    };\n  });\n\n  const jumpPage = () => {\n    setMainPageActiveTab('connections');\n  };\n\n  return (\n    <>\n      <div className={classnames(styles.workspaceLeft)}>\n        {connectionList?.length ? (\n          <>\n            <WorkspaceLeftHeader />\n            <TableList />\n          </>\n        ) : (\n          <div className={styles.noConnectionList}>\n            <Iconfont className={styles.noConnectionListIcon} code=\"&#xe638;\" />\n            <div className={styles.noConnectionListTips}>{i18n('workspace.tips.noConnection')}</div>\n            <div>\n              <span className={styles.create} onClick={jumpPage}>\n                {i18n('common.title.create')}\n              </span>\n              {i18n('connection.title.connections')}\n            </div>\n          </div>\n        )}\n      </div>\n      <CreateDatabase />\n    </>\n  );\n});\n\nexport default WorkspaceLeft;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceLeftHeader/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.splitViewLeft {\n}\n\n.selectConnection {\n  padding: 6px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  cursor: pointer;\n  box-sizing: border-box;\n  height: 32px;\n  .menuLabel {\n    flex: 1;\n    width: 0px;\n  }\n  .dropDownArrow {\n    transform: rotate(90deg);\n    color: var(--color-text-secondary);\n  }\n  &:hover {\n    background: var(--color-hover-bg);\n  }\n}\n\n.dropdownOverlay {\n  max-width: 500px;\n  :global {\n    .ant-dropdown-menu-item {\n      padding: 4px 6px !important;\n    }\n  }\n}\n\n.menuLabel {\n  display: flex;\n  align-items: center;\n  .menuLabelIconBox {\n    width: 22px;\n    display: flex;\n    align-items: center;\n  }\n  .menuLabelIcon {\n    color: var(--color-primary);\n  }\n  .menuLabelTag {\n    margin-right: 10px;\n  }\n  .menuLabelTitle {\n    flex: 1;\n    width: 0px;\n    .f-single-line();\n  }\n  .envTag {\n    flex-shrink: 0;\n    width: 8px;\n    height: 8px;\n    border-radius: 50%;\n    background-color: var(--color-primary);\n    margin-right: 8px;\n  }\n}\n\n.databaseTypeIcon {\n  margin-right: 10px;\n  font-weight: 400;\n  color: var(--color-primary);\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceLeftHeader/index.tsx",
    "content": "import React, { memo, useMemo } from 'react';\nimport { Dropdown } from 'antd';\nimport classnames from 'classnames';\nimport styles from './index.less';\n\n// ---- store ----\nimport { useConnectionStore } from '@/pages/main/store/connection';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport { setCurrentConnectionDetails } from '@/pages/main/workspace/store/common';\n\n// ----- components -----\nimport Iconfont from '@/components/Iconfont';\n\n// ----- constants/typings -----\nimport { databaseMap } from '@/constants';\n\nimport { IConnectionListItem } from '@/typings/connection';\n\nexport default memo(() => {\n  const { connectionList } = useConnectionStore((state) => {\n    return {\n      connectionList: state.connectionList,\n    };\n  });\n\n  const { currentConnectionDetails } = useWorkspaceStore((state) => {\n    return {\n      currentConnectionDetails: state.currentConnectionDetails,\n    };\n  });\n\n  const renderConnectionLabel = (item: IConnectionListItem) => {\n    return (\n      <div className={classnames(styles.menuLabel)}>\n        {/* <Tag className={styles.menuLabelTag} color={item.environment.color.toLocaleLowerCase()}>\n          {item.environment.shortName}\n        </Tag> */}\n        <span className={styles.envTag} style={{ background: item.environment.color.toLocaleLowerCase() }} />\n        <div className={styles.menuLabelIconBox}>\n          <Iconfont className={classnames(styles.menuLabelIcon)} code={databaseMap[item.type]?.icon} />\n        </div>\n        <div className={styles.menuLabelTitle}>{item.alias}</div>\n      </div>\n    );\n  };\n\n  const connectionItems = useMemo(() => {\n    return (\n      connectionList?.map((item) => {\n        return {\n          key: item.id,\n          label: renderConnectionLabel(item),\n          onClick: () => {\n            setCurrentConnectionDetails(item);\n          },\n        };\n      }) || []\n    );\n  }, [connectionList, currentConnectionDetails]);\n\n  return (\n    <Dropdown menu={{ items: connectionItems }} trigger={['click']} overlayClassName={styles.dropdownOverlay}>\n      <div className={styles.selectConnection}>\n        {currentConnectionDetails && renderConnectionLabel(currentConnectionDetails)}\n        <div className={styles.dropDownArrow}>\n          <Iconfont code=\"&#xe641;\" />\n        </div>\n      </div>\n    </Dropdown>\n  );\n});\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceRight/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.workspaceRight {\n  position: relative;\n  display: flex;\n  flex: 1;\n  width: 0px;\n}\n\n.workspaceExtendNav {\n  border-left: 1px solid var(--color-border);\n}\n\n.draggableContainer {\n  flex: 1;\n  width: 0px;\n  overflow: hidden;\n}\n\n.workspaceExtendBody {\n  border-left: 1px solid var(--color-border);\n  overflow: hidden;\n}\n\n.workspaceExtend {\n  height: 100%;\n  flex-shrink: 0;\n  border-left: 1px solid var(--color-border);\n  display: flex;\n}\n\n.consoleTabsContainer {\n  height: 50%;\n}\n\n.searchResultContainer {\n  border-top: 1px solid var(--color-border-secondary);\n  flex: 1;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceRight/index.tsx",
    "content": "import React, { memo, useRef, useCallback } from 'react';\nimport styles from './index.less';\n// import classnames from 'classnames';\nimport WorkspaceExtendBody from '../WorkspaceExtend/WorkspaceExtendBody';\nimport WorkspaceExtendNav from '../WorkspaceExtend/WorkspaceExtendNav';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport { setPanelRightWidth } from '@/pages/main/workspace/store/config';\n\n// ----- components -----\nimport WorkspaceTabs from '../WorkspaceTabs';\nimport DraggableContainer from '@/components/DraggableContainer';\n\nconst WorkspaceRight = memo(() => {\n  const draggableRef = useRef<any>();\n\n  const { currentWorkspaceExtend, panelRight, panelRightWidth } = useWorkspaceStore((state) => {\n    return {\n      currentWorkspaceExtend: state.currentWorkspaceExtend,\n      panelRight: state.layout.panelRight,\n      panelRightWidth: state.layout.panelRightWidth,\n    };\n  });\n\n  const draggableContainerResize = useCallback((data: number) => {\n    setPanelRightWidth(data);\n  }, []);\n\n  return (\n    <div className={styles.workspaceRight}>\n      <DraggableContainer\n        onResize={draggableContainerResize}\n        showLine={!!currentWorkspaceExtend}\n        min={200}\n        className={styles.draggableContainer}\n      >\n        <WorkspaceTabs />\n        <div\n          ref={draggableRef}\n          className={styles.workspaceExtendBody}\n          style={{\n            display: currentWorkspaceExtend && panelRight ? 'block' : 'none',\n            width: `${panelRightWidth || 0}px`,\n          }}\n        >\n          <WorkspaceExtendBody />\n        </div>\n      </DraggableContainer>\n      {panelRight && <WorkspaceExtendNav className={styles.workspaceExtendNav} />}\n    </div>\n  );\n});\n\nexport default WorkspaceRight;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceTabs/index.less",
    "content": "@import '../../../../../styles/var.less';\n\n.tabBox {\n  height: 100%;\n  flex: 1;\n  width: 0px;\n}\n\n.ears {\n  flex: 1;\n  width: 0px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  overflow: hidden;\n  button {\n    display: flex;\n    align-items: center;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/components/WorkspaceTabs/index.tsx",
    "content": "import React, { memo, useEffect, useMemo, Fragment } from 'react';\nimport styles from './index.less';\nimport i18n from '@/i18n';\nimport { Button } from 'antd';\n\n// ----- constants -----\nimport { WorkspaceTabType, workspaceTabConfig } from '@/constants';\nimport { IWorkspaceTab } from '@/typings';\n\n// ----- components -----\nimport Tabs, { ITabItem } from '@/components/Tabs';\nimport SearchResult from '@/components/SearchResult';\nimport DatabaseTableEditor from '@/blocks/DatabaseTableEditor';\nimport SequenceEditor from '@/blocks/SequenceEditor';\nimport SQLExecute from '../SQLExecute';\nimport ViewAllTable from '../ViewAllTable';\nimport Iconfont from '@/components/Iconfont';\nimport ShortcutKey from '@/components/ShortcutKey';\n\n// ---- store -----\nimport {\n  getOpenConsoleList,\n  setActiveConsoleId,\n  setWorkspaceTabList,\n  createConsole,\n} from '@/pages/main/workspace/store/console';\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport { useTreeStore } from '@/blocks/Tree/treeStore';\n\n// ----- services -----\nimport historyService from '@/service/history';\n\nimport indexedDB from '@/indexedDB';\n\nconst WorkspaceTabs = memo(() => {\n  const { activeConsoleId, consoleList, workspaceTabList } = useWorkspaceStore((state) => {\n    return {\n      consoleList: state.consoleList,\n      activeConsoleId: state.activeConsoleId,\n      workspaceTabList: state.workspaceTabList,\n    };\n  });\n\n  const currentConnectionDetails = useWorkspaceStore((state) => state.currentConnectionDetails);\n\n  // 获取console\n  useEffect(() => {\n    getOpenConsoleList();\n  }, []);\n\n  // consoleList 先转换为通用的 workspaceTabList\n  useEffect(() => {\n    const _workspaceTabItems =\n      consoleList?.map((item) => {\n        return {\n          id: item.id,\n          type: item.operationType,\n          title: item.name,\n          uniqueData: {\n            dataSourceId: item.dataSourceId,\n            dataSourceName: item.dataSourceName,\n            databaseType: item.type,\n            databaseName: item.databaseName,\n            schemaName: item.schemaName,\n            status: item.status,\n            ddl: item.ddl,\n            connectable: item.connectable,\n          },\n        };\n      }) || [];\n    setWorkspaceTabList(_workspaceTabItems);\n  }, [consoleList]);\n\n  // 关闭tab\n  const closeWindowTab = (key: number) => {\n    const p: any = {\n      id: key,\n      tabOpened: 'n',\n    };\n\n    historyService.updateSavedConsole(p).then(() => {\n      indexedDB.deleteData('chat2db', 'workspaceConsoleDDL', key);\n    });\n  };\n\n  const createNewConsole = () => {\n    const { databaseName, schemaName } = useTreeStore.getState().focusTreeNode || {};\n    if (currentConnectionDetails) {\n      createConsole({\n        dataSourceId: currentConnectionDetails.id,\n        dataSourceName: currentConnectionDetails.alias,\n        databaseType: currentConnectionDetails.type,\n        databaseName,\n        schemaName\n      });\n    }\n  };\n\n  // 删除 新增tab\n  const handelTabsEdit = (action: 'add' | 'remove', data: ITabItem[]) => {\n    if (action === 'remove') {\n      setWorkspaceTabList(\n        workspaceTabList?.filter((t) => {\n          return data.findIndex((item) => item.key === t.id) === -1;\n        }) || [],\n      );\n      data.forEach((item) => {\n        const editData = workspaceTabList?.find((t) => t.id === item.key);\n        if (\n          editData?.type === WorkspaceTabType.CONSOLE ||\n          editData?.type === WorkspaceTabType.FUNCTION ||\n          editData?.type === WorkspaceTabType.PROCEDURE ||\n          editData?.type === WorkspaceTabType.TRIGGER ||\n          editData?.type === WorkspaceTabType.VIEW ||\n          editData?.type === WorkspaceTabType.SEQUENCE ||\n          // table 和 !editData?.type 为了兼容老数据\n          editData?.type === ('table' as any) ||\n          !editData?.type\n        ) {\n          closeWindowTab(item.key as number);\n        }\n      });\n    }\n    if (action === 'add') {\n      createNewConsole();\n    }\n  };\n\n  // 切换tab\n  const onTabChange = (key: string | null) => {\n    setActiveConsoleId(key);\n  };\n\n  // 编辑名称\n  const editableNameOnBlur = (t: ITabItem) => {\n    const _params: any = {\n      id: t.key,\n      name: t.label,\n    };\n    historyService.updateSavedConsole(_params);\n\n    const _workspaceTabList: any =\n      workspaceTabList?.map((item) => {\n        if (item.id === t.key) {\n          return {\n            ...item,\n            title: t.label,\n          };\n        }\n        return item;\n      }) || [];\n    setWorkspaceTabList(_workspaceTabList);\n  };\n\n  // 修改tab详情\n  const changeTabDetails = (data: IWorkspaceTab) => {\n    const list =\n      workspaceTabList?.map((t) => {\n        if (t.id === data.id) {\n          return data;\n        }\n        return t;\n      }) || [];\n    setWorkspaceTabList(list);\n  };\n\n  // 渲染sql执行器\n  const renderSQLExecute = (item: IWorkspaceTab) => {\n    const { uniqueData } = item;\n    return (\n      <SQLExecute\n        boundInfo={{\n          dataSourceId: uniqueData.dataSourceId,\n          dataSourceName: uniqueData.dataSourceName,\n          databaseName: uniqueData?.databaseName,\n          databaseType: uniqueData.databaseType,\n          schemaName: uniqueData?.schemaName,\n          consoleId: item.id as number,\n          status: uniqueData.status,\n          connectable: uniqueData.connectable,\n          supportDatabase: uniqueData.supportDatabase,\n          supportSchema: uniqueData.supportSchema,\n        }}\n        initDDL={uniqueData.ddl}\n        loadSQL={uniqueData.loadSQL}\n      />\n    );\n  };\n  // 渲染序列编辑器\n  const renderSequenceEditor = (item: IWorkspaceTab) => {\n    const { uniqueData } = item;\n    return (\n      <SequenceEditor\n        tabDetails={item}\n        changeTabDetails={changeTabDetails}\n        dataSourceId={uniqueData.dataSourceId}\n        databaseName={uniqueData.databaseName!}\n        databaseType={uniqueData?.databaseType}\n        schemaName={uniqueData?.schemaName}\n        tableName={uniqueData.tableName}\n        submitCallback={uniqueData.submitCallback}\n      />\n    );\n  };\n  // 渲染表格编辑器\n  const renderTableEditor = (item: IWorkspaceTab) => {\n    const { uniqueData } = item;\n    return (\n      <DatabaseTableEditor\n        tabDetails={item}\n        changeTabDetails={changeTabDetails}\n        dataSourceId={uniqueData.dataSourceId}\n        databaseName={uniqueData.databaseName!}\n        databaseType={uniqueData?.databaseType}\n        schemaName={uniqueData?.schemaName}\n        tableName={uniqueData.tableName}\n        submitCallback={uniqueData.submitCallback}\n      />\n    );\n  };\n\n  // 渲染搜索结果\n  const renderSearchResult = (item: IWorkspaceTab) => {\n    const { uniqueData } = item;\n    return (\n      <SearchResult\n        isActive={activeConsoleId === item.id}\n        sql={uniqueData.sql}\n        executeSqlParams={uniqueData}\n        viewTable\n        concealTabHeader\n      />\n    );\n  };\n\n  // 渲染所有表\n  const renderViewAllTable = (item: IWorkspaceTab) => {\n    const { uniqueData } = item;\n    return <ViewAllTable uniqueData={uniqueData} />;\n  };\n\n  // 根据不同的tab类型渲染不同的内容\n  const workspaceTabConnectionMap = (item: IWorkspaceTab) => {\n    switch (item.type) {\n      case 'table' as any: // 为了兼容老数据\n      case null as any: // 为了兼容老数据\n      case WorkspaceTabType.CONSOLE:\n      case WorkspaceTabType.FUNCTION:\n      case WorkspaceTabType.SEQUENCE:\n      case WorkspaceTabType.PROCEDURE:\n      case WorkspaceTabType.TRIGGER:\n      case WorkspaceTabType.VIEW:\n        return renderSQLExecute(item);\n      case WorkspaceTabType.EditTable:\n      case WorkspaceTabType.CreateTable:\n        return renderTableEditor(item);\n      case WorkspaceTabType.EditTableData:\n        return renderSearchResult(item);\n      case WorkspaceTabType.ViewAllTable:\n        return renderViewAllTable(item);\n      case WorkspaceTabType.CreateSequence:\n      case WorkspaceTabType.EditSequence:\n        return renderSequenceEditor(item);\n      default:\n        return <div>Unknown</div>;\n    }\n  };\n\n  // tab列表\n  const workspaceTabItems = useMemo(() => {\n    return workspaceTabList?.map((item) => {\n      return {\n        prefixIcon: workspaceTabConfig[item.type]?.icon,\n        label: item.title,\n        key: item.id,\n        editableName: item.type === WorkspaceTabType.CONSOLE,\n        children: <Fragment key={item.id}>{workspaceTabConnectionMap(item)}</Fragment>,\n      };\n    });\n  }, [workspaceTabList, activeConsoleId]);\n\n  function renderCreateConsoleButton() {\n    return (\n      <div className={styles.createButtonBox}>\n        <Button\n          className={styles.createButton}\n          type=\"primary\"\n          onClick={() => {\n            createNewConsole();\n          }}\n        >\n          <Iconfont code=\"&#xe63a;\" />\n          {i18n('common.button.createConsole')}\n        </Button>\n      </div>\n    );\n  }\n\n  return workspaceTabItems?.length ? (\n    <Tabs\n      className={styles.tabBox}\n      onChange={onTabChange as any}\n      onEdit={handelTabsEdit as any}\n      activeKey={activeConsoleId}\n      editableNameOnBlur={editableNameOnBlur}\n      items={workspaceTabItems}\n    />\n  ) : (\n    <div className={styles.ears}>\n      <ShortcutKey slot={renderCreateConsoleButton} />\n    </div>\n  );\n});\n\nexport default WorkspaceTabs;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/functions/shortcutKeyCreateConsole.ts",
    "content": "import { createConsole} from '../store/console'\nimport { useWorkspaceStore } from '../store';\n\nexport const handelCreateConsole = () => { \n  const params = useWorkspaceStore.getState().currentConnectionDetails;\n  if (params) {\n    createConsole({\n      dataSourceId: params.id,\n      dataSourceName: params.alias,\n      databaseType: params.type,\n    });\n  }\n}\n\nconst shortcutKeyCreateConsole = () => {\n  // 注册快捷键监听cmd+shift+l或ctrl+shift+l新建一个console\n  const handleKeyDown = (e: KeyboardEvent) => {\n    if ((e.metaKey && e.shiftKey && e.code === 'KeyL') || (e.ctrlKey && e.shiftKey && e.key === 'KeyL')) {\n      handelCreateConsole()\n    }\n  };\n  window.addEventListener('keydown', handleKeyDown);\n  return () => {\n    window.removeEventListener('keydown', handleKeyDown);\n  };\n}\n\nexport default shortcutKeyCreateConsole;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/index.less",
    "content": "@import '../../../styles/var.less';\n\n.workspace {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n.workspaceMain {\n  height: 100%;\n}\n\n.loadingContent {\n  flex: 1;\n  height: 0px !important;\n}\n\n.boxLeft {\n  width: var(--panel-left-width);\n  height: 100%;\n  overflow: hidden;\n  border-right: 1px solid var(--color-border-secondary);\n  border-top: 0px;\n  border-bottom: 0px;\n}\n\n.boxRight {\n  position: relative;\n  z-index: 1; //为了覆盖左侧 Cascader\n  flex: 1;\n  width: 0px;\n  background-color: var(--color-bg-base);\n}\n\n.box_right_center {\n  height: 100%;\n}\n\n.box_right_console {\n  height: 300px;\n  overflow: hidden;\n}\n\n.box_right_result {\n  height: 0px;\n  flex: 1;\n}\n\n.select_database_box {\n  display: flex;\n  align-items: center;\n  width: 100%;\n  .current_database {\n    flex: 1;\n    width: 0px;\n    display: flex;\n    align-items: center;\n    cursor: pointer;\n    i {\n      flex-shrink: 0;\n      margin-left: 10px;\n    }\n    .name {\n      max-width: 90%;\n      .f-single-line();\n    }\n  }\n\n  .other_operations {\n    flex-shrink: 0;\n  }\n\n  .icon_box {\n    width: 30px;\n    height: 30px;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n  }\n}\n\n.cascade_popup {\n  :global {\n    .ant-cascader-menu-item-content {\n      font-weight: 400 !important;\n    }\n  }\n}\n\n.hiddenPanelLeft {\n  display: none;\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/index.tsx",
    "content": "import React, { memo, useCallback, useEffect, useRef } from 'react';\nimport classnames from 'classnames';\n\nimport { useWorkspaceStore } from '@/pages/main/workspace/store';\nimport { setPanelLeftWidth } from '@/pages/main/workspace/store/config';\n\nimport DraggableContainer from '@/components/DraggableContainer';\nimport WorkspaceLeft from './components/WorkspaceLeft';\nimport WorkspaceRight from './components/WorkspaceRight';\n\nimport useMonacoTheme from '@/components/MonacoEditor/useMonacoTheme';\nimport shortcutKeyCreateConsole from './functions/shortcutKeyCreateConsole';\n\nimport styles from './index.less';\n\nconst workspacePage = memo(() => {\n  const draggableRef = useRef<any>();\n  const { panelLeft, panelLeftWidth } = useWorkspaceStore((state) => {\n    return {\n      panelLeft: state.layout.panelLeft,\n      panelLeftWidth: state.layout.panelLeftWidth,\n    };\n  });\n\n  // 编辑器的主题\n  useMonacoTheme();\n\n  // 快捷键\n  useEffect(() => {\n    shortcutKeyCreateConsole();\n  }, []);\n\n  const draggableContainerResize = useCallback((data: number) => {\n    setPanelLeftWidth(data);\n  }, []);\n\n  return (\n    <div className={styles.workspace}>\n      <DraggableContainer className={styles.workspaceMain} onResize={draggableContainerResize}>\n        <div\n          ref={draggableRef}\n          style={{ '--panel-left-width': `${panelLeftWidth}px` } as any}\n          className={classnames({ [styles.hiddenPanelLeft]: !panelLeft }, styles.boxLeft)}\n        >\n          <WorkspaceLeft />\n        </div>\n        <WorkspaceRight />\n      </DraggableContainer>\n    </div>\n  );\n});\n\nexport default workspacePage;\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/store/common.ts",
    "content": "import { IConnectionListItem } from '@/typings/connection';\nimport { useWorkspaceStore } from './index';\n\nexport interface ICommonStore {\n  currentConnectionDetails: IConnectionListItem | null;\n  currentWorkspaceExtend: string | null;\n  currentWorkspaceGlobalExtend: {\n    code: string,\n    uniqueData: any,\n  } | null;\n}\n\nexport const initCommonStore: ICommonStore = {\n  currentConnectionDetails: null,\n  currentWorkspaceExtend: null,\n  currentWorkspaceGlobalExtend: null,\n}\n\nexport const setCurrentConnectionDetails = (connectionDetails: ICommonStore['currentConnectionDetails']) => {\n  return useWorkspaceStore.setState({ currentConnectionDetails: connectionDetails });\n}\n\nexport const setCurrentWorkspaceExtend = (workspaceExtend: ICommonStore['currentWorkspaceExtend']) => {\n  return useWorkspaceStore.setState({ currentWorkspaceExtend: workspaceExtend });\n}\n\nexport const setCurrentWorkspaceGlobalExtend = (workspaceGlobalExtend: ICommonStore['currentWorkspaceGlobalExtend']) => {\n  return useWorkspaceStore.setState({ currentWorkspaceGlobalExtend: workspaceGlobalExtend });\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/store/config.ts",
    "content": "import {useWorkspaceStore} from './index'\nexport interface IConfigStore {\n  layout: {\n    panelLeft: boolean;\n    panelLeftWidth: number;\n    panelRight: boolean;\n    panelRightWidth: number;\n  };\n}\n\nexport const initConfigStore: IConfigStore = {\n  layout: {\n    panelLeft: true,\n    panelRight: true,\n    panelLeftWidth: 220,\n    panelRightWidth: 300,\n  },\n}\n\nexport const togglePanelRight = () => {\n  return useWorkspaceStore.setState((state) => ({\n    layout: {\n      ...state.layout,\n      panelRight: !state.layout.panelRight,\n    },\n  }))\n}\n\nexport const togglePanelLeft = () => {\n  return useWorkspaceStore.setState((state) => ({\n    layout: {\n      ...state.layout,\n      panelLeft: !state.layout.panelLeft,\n    },\n  }))\n}\n\nexport const setPanelLeftWidth = (width: number) => { \n  return useWorkspaceStore.setState((state) => ({\n    layout: {\n      ...state.layout,\n      panelLeftWidth: width,\n    },\n  }))\n}\n\nexport const setPanelRightWidth = (width: number) => { \n  return useWorkspaceStore.setState((state) => ({\n    layout: {\n      ...state.layout,\n      panelRightWidth: width,\n    },\n  }))\n}\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/store/console.ts",
    "content": "import { useWorkspaceStore } from './index';\nimport { IConsole, ICreateConsoleParams } from '@/typings';\nimport { IWorkspaceTab } from '@/typings/workspace';\nimport historyService from '@/service/history';\nimport { ConsoleStatus, WorkspaceTabType } from '@/constants';\nimport { message } from 'antd';\nimport i18n from '@/i18n';\n\nexport interface IConsoleStore {\n  consoleList: IConsole[] | null;\n  savedConsoleList: IConsole[] | null;\n  activeConsoleId: string | number | null;\n  workspaceTabList: IWorkspaceTab[] | null;\n  createConsoleLoading: boolean\n}\n\nexport const initConsoleStore = {\n  consoleList: null,\n  savedConsoleList: null,\n  activeConsoleId: null,\n  workspaceTabList: null,\n  createConsoleLoading: false,\n};\n\nexport const getOpenConsoleList = () => {\n  historyService\n    .getConsoleList({\n      tabOpened: 'y',\n      pageNo: 1,\n      pageSize: 20,\n    })\n    .then((res) => {\n      useWorkspaceStore.setState({ consoleList: res?.data });\n    });\n};\n\nexport const getSavedConsoleList = () => { \n  historyService\n    .getConsoleList({\n      pageNo: 1,\n      pageSize: 100,\n      status: ConsoleStatus.RELEASE,\n    })\n    .then((res) => {\n      useWorkspaceStore.setState({ savedConsoleList: res?.data });\n    });\n}\n\nexport const setActiveConsoleId = (id: IConsoleStore['activeConsoleId']) => {\n  useWorkspaceStore.setState({ activeConsoleId: id });\n};\n\nexport const setWorkspaceTabList = (items: IConsoleStore['workspaceTabList']) => {\n  useWorkspaceStore.setState({ workspaceTabList: items });\n};\n\nexport const createConsole = (params: ICreateConsoleParams) => {\n  \n  const workspaceTabList = useWorkspaceStore.getState().workspaceTabList;\n  const currentConnectionDetails = useWorkspaceStore.getState().currentConnectionDetails;\n  const newConsole = {\n    ...params,\n    name: params.name || `untitled-${params.databaseName || params.schemaName} (${params.dataSourceName})`,\n    ddl: params.ddl || '',\n    status: ConsoleStatus.DRAFT,\n    operationType: params.operationType || WorkspaceTabType.CONSOLE,\n    type: params.databaseType,\n    supportDatabase: currentConnectionDetails?.supportDatabase,\n    supportSchema: currentConnectionDetails?.supportSchema,\n  };\n  return new Promise((resolve) => {\n    if ((workspaceTabList?.length || 0) >= 20) {\n      message.warning(i18n('workspace.tips.maxConsole'));\n      return;\n    }\n\n    useWorkspaceStore.setState({ createConsoleLoading: true });\n    historyService.createConsole(newConsole).then((res) => {\n      const newList = [\n        ...(workspaceTabList || []),\n        {\n          id: res,\n          title: newConsole.name,\n          type: newConsole.operationType,\n          uniqueData: newConsole,\n        },\n      ];\n\n      setWorkspaceTabList(newList);\n      setActiveConsoleId(res);\n      resolve(res);\n    })\n      .finally(() => { \n      useWorkspaceStore.setState({ createConsoleLoading: false });\n    });\n  });\n};\n\nexport const addWorkspaceTab = (params: IWorkspaceTab) => {\n  const workspaceTabList = useWorkspaceStore.getState().workspaceTabList;\n  if (workspaceTabList?.findIndex((item) => item?.id === params?.id) !== -1) {\n    setActiveConsoleId(params.id);\n    return;\n  }\n\n  const newList = [...(workspaceTabList || []), params];\n\n  setWorkspaceTabList(newList);\n  setActiveConsoleId(params.id);\n};\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/store/index.ts",
    "content": "import { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools, persist } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\nimport { StoreApi } from 'zustand';\n\nimport { initConfigStore, IConfigStore } from './config';\nimport { initConsoleStore, IConsoleStore } from './console';\nimport { initCommonStore, ICommonStore } from './common';\nimport { initModalStore, IModalStore } from './modal';\n\nexport type IStore = IConfigStore & IConsoleStore & ICommonStore & IModalStore;\n\nexport const useWorkspaceStore: UseBoundStoreWithEqualityFn<StoreApi<IStore>> = createWithEqualityFn(\n  devtools(\n    persist(\n      () => ({\n        ...initConsoleStore,\n        ...initConfigStore,\n        ...initCommonStore,\n        ...initModalStore,\n      }),\n      // persist config\n      {\n        name: 'workspace-store',\n        getStorage: () => localStorage,\n        // 工作区的状态只保存 layout布局信息\n        partialize: (state: IStore) => ({\n          layout: state.layout,\n          currentConnectionDetails: state.currentConnectionDetails,\n        }),\n      },\n    ),\n    {\n      name: 'workspaceStore',\n    },\n  ),\n  shallow,\n);\n"
  },
  {
    "path": "chat2db-client/src/pages/main/workspace/store/modal.ts",
    "content": "import { useWorkspaceStore } from './index';\nimport { DatabaseTypeCode } from '@/constants';\nimport { CreateType } from '@/components/CreateDatabase';\nexport interface IModalStore {\n  openCreateDatabaseModal: ((params: {\n    type: CreateType;\n    relyOnParams: {\n      databaseType: DatabaseTypeCode;\n      dataSourceId: number;\n      databaseName?: string;\n    };\n    executedCallback?: (status: true) => void;\n  }) => void) | null;\n}\n\nexport const initModalStore: IModalStore = {\n  openCreateDatabaseModal: null,\n};\n\nexport const setOpenCreateDatabaseModal = (fn: any) => {\n  useWorkspaceStore.setState({ openCreateDatabaseModal: fn });\n};\n"
  },
  {
    "path": "chat2db-client/src/pages/test/index.less",
    "content": ".container{\n  width: 100vw;\n  height: 100vh;\n}"
  },
  {
    "path": "chat2db-client/src/pages/test/index.tsx",
    "content": "import MonacoEditor from '@/components/MonacoEditor';\nimport { Button } from 'antd';\nimport React, { useState } from 'react';\nimport styles from './index.less';\n\nfunction Test() {\n  const [value, setValue] = useState('select * from ');\n\n  return (\n    <>\n      <Button onClick={() => setValue('select * from table1')}>Change1</Button>\n      <Button onClick={() => setValue('select * from table2')}>Change2</Button>\n\n      <MonacoEditor\n        id={'0'}\n        className={styles.container}\n        language={'sql'}\n        // value={value}\n        onChange={(v, e) => {\n          setValue(v);\n        }}\n      />\n    </>\n  );\n}\n\nexport default Test;\n"
  },
  {
    "path": "chat2db-client/src/service/ai.ts",
    "content": "import createRequest from './base';\nimport { IInviteQrCode, ILoginAndQrCode, IRemainingUse } from '@/typings/ai';\n\nconst getRemainingUse = createRequest<void, IRemainingUse>('/api/ai/config/remaininguses', {\n  errorLevel: false,\n});\n\nconst getLoginQrCode = createRequest<{ token?: string }, ILoginAndQrCode>('/api/ai/config/getLoginQrCode');\n\nconst getLoginStatus = createRequest<{ token?: string }, ILoginAndQrCode>('/api/ai/config/getLoginStatus', {\n  errorLevel: false,\n});\n\nconst getInviteQrCode = createRequest<void, IInviteQrCode>('/api/ai/config/getInviteQrCode');\n\nexport default { getRemainingUse, getLoginQrCode, getLoginStatus, getInviteQrCode };\n"
  },
  {
    "path": "chat2db-client/src/service/base.ts",
    "content": "import { extend, ResponseError, type RequestOptionsInit } from 'umi-request';\nimport { message } from 'antd';\nimport { navigate } from '@/utils';\n\nexport type IErrorLevel = 'toast' | 'prompt' | 'critical' | false;\nexport interface IOptions {\n  method?: 'get' | 'post' | 'put' | 'delete';\n  mock?: boolean;\n  errorLevel?: 'toast' | 'prompt' | 'critical' | false;\n  delayTime?: number | true;\n  outside?: boolean;\n  isFullPath?: boolean;\n  dynamicUrl?: boolean;\n}\n\n// TODO:\nconst codeMessage: { [errorCode: number]: string } = {\n  200: '服务器成功返回请求的数据。',\n  201: '新建或修改数据成功。',\n  202: '一个请求已经进入后台排队（异步任务）。',\n  204: '删除数据成功。',\n  400: '发出的请求有错误，服务器没有进行新建或修改数据的操作。',\n  401: '用户没有权限（令牌、用户名、密码错误）。',\n  403: '用户得到授权，但是访问是被禁止的。',\n  404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',\n  406: '请求的格式不可得。',\n  410: '请求的资源被永久删除，且不会再得到的。',\n  422: '当创建一个对象时，发生一个验证错误。',\n  500: '服务器发生错误，请检查服务器。',\n  502: '网关错误。',\n  503: '服务不可用，服务器暂时过载或维护。',\n  504: '网关超时。',\n};\n\nenum ErrorCode {\n  /** 需要登录 */\n  NEED_LOGGED_IN = 'common.needLoggedIn',\n}\n\nconst noNeedToastErrorCode = [ErrorCode.NEED_LOGGED_IN];\n\n// yapi mock地址\nconst mockUrl = 'https://yapi.com/mock/1000160';\n\n// 桌面端的服务器地址\nconst desktopServiceUrl = `http://127.0.0.1:${__APP_PORT__ || '10824'}`;\n\n// 非桌面端的服务器地址\nconst prodServiceUrl = location.origin;\n\n// 是否自定义了 _BaseURL || 是否为桌面端地址\nconst baseURL =\n  localStorage.getItem('_BaseURL') ||\n  (location.href.indexOf('dist/index.html') > -1 ? desktopServiceUrl : prodServiceUrl);\n\nwindow._BaseURL = baseURL;\n// window._BaseURL = 'http://127.0.0.1:8000';\n\nconst appGatewayParams = localStorage.getItem('app-gateway-params');\n\n// appGateway 的基本信息\nif (appGatewayParams) {\n  window._appGatewayParams = JSON.parse(appGatewayParams);\n} else {\n  window._appGatewayParams = {};\n}\n\nconst outsideUrlPrefix = window._appGatewayParams.baseUrl || 'http://test.sqlgpt.cn/gateway';\n\nconst errorHandler = (error: ResponseError, errorLevel: IErrorLevel) => {\n  const { response } = error;\n  if (!response) return;\n  const errorText = codeMessage[response.status] || response.statusText;\n  const { status } = response;\n  if (errorLevel === 'toast') {\n    // notification.open({\n    //   type: 'error',\n    //   message: status,\n    //   description: errorText,\n    //   placement: 'topRight',\n    // });\n    message.error(`${status}: ${errorText}`);\n  }\n};\n\nconst request = extend({\n  // prefix: '/api',\n  credentials: 'include', // 默认请求是否带上cookie\n  headers: {\n    'Content-Type': 'application/json',\n    Accept: 'application/json',\n  },\n});\n\nrequest.interceptors.request.use((url, options) => {\n  const myOptions: any = {\n    ...options,\n    headers: {\n      ...options.headers,\n    },\n  };\n  if (localStorage.getItem('Chat2db')) {\n    myOptions.headers.Chat2db = localStorage.getItem('Chat2db');\n  }\n  return {\n    options: myOptions,\n  };\n});\n\nrequest.interceptors.response.use(async (response) => {\n  const res = await response.clone().json();\n  if (__ENV__ === 'desktop') {\n    const Chat2db = response.headers.get('Chat2db') || '';\n    if (Chat2db) {\n      localStorage.setItem('Chat2db', Chat2db);\n    }\n  }\n  const { errorCode } = res;\n  if (errorCode === ErrorCode.NEED_LOGGED_IN) {\n    navigate('/login');\n    // const callback = window.location.hash.substr(1).split('?')[0];\n    // window.location.href = '#/login' + (callback === '/login' ? '' : `?callback=${callback}`);\n  }\n\n  return response;\n});\n\nexport default function createRequest<P = void, R = void>(url: string, options?: IOptions) {\n  // 路由跳转\n  const {\n    method = 'get',\n    mock = false,\n    errorLevel = 'toast',\n    delayTime,\n    outside,\n    isFullPath,\n    dynamicUrl,\n  } = options || {};\n\n  return function (params: P, restParams?: RequestOptionsInit) {\n    // 是否需要mock\n    const _baseURL = (mock ? mockUrl : baseURL) || '';\n    // if (url === '/api/rdb/ddl/list') {\n    //   debugger;\n    // }\n    // 在url上按照定义规则拼接params\n    const paramsInUrl: string[] = [];\n\n    const _url = url.replace(/:(.+?)\\b/, (_, name: string) => {\n      const value = params[name];\n      paramsInUrl.push(name);\n      return `${value}`;\n    });\n\n    if (paramsInUrl.length) {\n      paramsInUrl.forEach((name) => {\n        delete params[name];\n      });\n    }\n\n    return new Promise<R>((resolve, reject) => {\n      let dataName = '';\n      switch (method) {\n        case 'get':\n          dataName = 'params';\n          break;\n        case 'delete':\n          dataName = 'params';\n          break;\n        case 'post':\n          dataName = 'data';\n          break;\n        case 'put':\n          dataName = 'data';\n          break;\n        default:\n          dataName = 'params';\n          break;\n      }\n\n      let eventualUrl = outside ? `${outsideUrlPrefix}${_url}` : `${_baseURL}${_url}`;\n      eventualUrl = isFullPath ? url : eventualUrl;\n\n      // 动态的url\n      if (dynamicUrl) {\n        eventualUrl = params as string;\n      }\n\n      request[method](eventualUrl, { [dataName]: params, ...restParams })\n        .then((res) => {\n          if (!res) return;\n          const { success, errorCode, errorMessage, errorDetail, solutionLink, data } = res;\n          if (!success && errorLevel === 'toast' && !noNeedToastErrorCode.includes(errorCode)) {\n            delayTimeFn(() => {\n              window._notificationApi({\n                requestUrl: eventualUrl,\n                requestParams: JSON.stringify(params),\n                errorCode,\n                errorMessage,\n                errorDetail,\n                solutionLink,\n              });\n              // message.error(`${errorCode}: ${errorMessage}`);\n              reject(`${errorCode}: ${errorMessage}`);\n            }, delayTime);\n            return;\n          }\n          // 有些loading效果添加强制延时效果可能会更好看, 可行性待商榷\n          delayTimeFn(() => {\n            resolve(data);\n          }, delayTime);\n        })\n        .catch((error) => {\n          delayTimeFn(() => {\n            errorHandler(error, errorLevel);\n            reject(error);\n          }, delayTime);\n        });\n    });\n  };\n}\n\n// 简单的延时函数\nfunction delayTimeFn(callback: () => void, time: number | true | undefined) {\n  if (time) {\n    const timer = setTimeout(() => {\n      callback();\n      clearInterval(timer);\n    }, time && 500);\n  } else {\n    callback();\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/service/config.ts",
    "content": "import { IAiConfig } from '@/typings';\nimport createRequest from './base';\n\nexport interface ILatestVersion {\n  /**\n   * 桌面\n   */\n  desktop: boolean;\n  /**\n   * 新版本\n   */\n  version: string;\n  /**\n   * 热更新包地址，可用来判断是否热更新\n   */\n  hotUpgradeUrl: null | string;\n  /**\n   * 用户选择的是手动更新还是自动更新\n   */\n  type: 'manual' | 'auto';\n  /**\n   * 是否需要更新\n   */\n  needUpdate?: boolean;\n  /**\n   * 下载地址\n   */\n  downloadLink?: null | string;\n  /**\n   * 更新日志\n   */\n  updateLog?: null | string;\n  /**\n   * 白名单，用于测试\n   */\n  whiteList?: null | string;\n}\n\nconst getSystemConfig = createRequest<{ code: string }, { code: string; content: string }>(\n  '/api/config/system_config/:code',\n  { errorLevel: false },\n);\nconst setSystemConfig = createRequest<{ code: string; content: string }, void>('/api/config/system_config', {\n  errorLevel: 'toast',\n  method: 'post',\n});\n\nconst getAiSystemConfig = createRequest<{ aiSqlSource?: string }, IAiConfig>('/api/config/system_config/ai', {\n  errorLevel: false,\n});\n\nconst setAiSystemConfig = createRequest<IAiConfig, void>('/api/config/system_config/ai', {\n  errorLevel: 'toast',\n  method: 'post',\n});\n\nconst getAiWhiteAccess = createRequest<{ apiKey: string }, boolean>('/api/ai/embedding/white/check', {\n  method: 'get',\n});\n\n// 检测仪更新获取最新的版本信息，如果返回结果为null，说明没有更新\nconst getLatestVersion = createRequest<{ currentVersion: string }, ILatestVersion>('/api/system/get_latest_version', {\n  method: 'get',\n});\n\n// 检测最新的包后端是否下载成功\nconst isUpdateSuccess = createRequest<{ version: string }, boolean>('/api/system/is_update_success', {\n  method: 'get',\n});\n\n// 告诉后端下载最新的包\nconst updateDesktopVersion = createRequest<ILatestVersion, boolean>('/api/system/update_desktop_version', {\n  method: 'post',\n});\n\n// 告诉后端下载最新的包\nconst setAppUpdateType = createRequest<ILatestVersion['type'], boolean>('/api/system/set_update_type', {\n  method: 'post',\n});\n\nexport default {\n  getSystemConfig,\n  setSystemConfig,\n  getAiSystemConfig,\n  setAiSystemConfig,\n  getAiWhiteAccess,\n  getLatestVersion,\n  isUpdateSuccess,\n  updateDesktopVersion,\n  setAppUpdateType\n};\n"
  },
  {
    "path": "chat2db-client/src/service/connection.ts",
    "content": "import { IPageResponse, IConnectionDetails, ICreateConnectionDetails, IConnectionEnv, IPageParams, IConnectionListItem } from '@/typings';\nimport { DatabaseTypeCode } from '@/constants';\nimport createRequest from './base';\n\nexport interface IDriverResponse {\n  driverConfigList: {\n    jdbcDriver: string;\n    jdbcDriverClass: string;\n  }[];\n  defaultDriverConfig: {\n    jdbcDriverClass: string;\n  };\n}\n\ninterface IDriverParams {\n  dbType: DatabaseTypeCode;\n}\n\ninterface IUploadDriver {\n  multipartFiles: any;\n  jdbcDriverClass: string;\n  dbType: string;\n}\n\n/**\n * 查询连接列表\n */\nconst getList = createRequest<IPageParams, IPageResponse<IConnectionListItem>>(\n  '/api/connection/datasource/list',\n  {},\n);\n\nconst getDetails = createRequest<{ id: number }, IConnectionDetails>('/api/connection/datasource/:id', {});\n\nconst save = createRequest<ICreateConnectionDetails, number>('/api/connection/datasource/create', {\n  method: 'post',\n  delayTime: true,\n});\n\nconst close = createRequest<IConnectionDetails, void>('/api/connection/datasource/close', { method: 'post' });\n\nconst test = createRequest<IConnectionDetails, boolean>('/api/connection/datasource/pre_connect', {\n  method: 'post',\n  delayTime: true,\n});\n\nconst testSSH = createRequest<any, boolean>('/api/connection/ssh/pre_connect', {\n  method: 'post',\n  delayTime: true,\n});\n\nconst update = createRequest<IConnectionDetails, void>('/api/connection/datasource/update', { method: 'post' });\n\nconst remove = createRequest<{ id: number }, void>('/api/connection/datasource/:id', { method: 'delete' });\n\nconst clone = createRequest<{ id: number }, number>('/api/connection/datasource/clone', { method: 'post' });\n\nconst getDatabaseList = createRequest<{ dataSourceId: number; refresh?: boolean }, any>('/api/rdb/database/list', {\n  method: 'get',\n});\n\nconst getSchemaList = createRequest<{ dataSourceId: number; databaseName?: string; refresh?: boolean }, any>(\n  '/api/rdb/schema/list',\n  { method: 'get' },\n);\n\nconst getDriverList = createRequest<IDriverParams, IDriverResponse>('/api/jdbc/driver/list', {\n  errorLevel: false,\n  method: 'get',\n});\nconst downloadDriver = createRequest<{ dbType: string }, void>('/api/jdbc/driver/download', {\n  method: 'get',\n});\n\nconst saveDriver = createRequest<IUploadDriver, void>('/api/jdbc/driver/save', { method: 'post' });\n\nconst getEnvList = createRequest<void, IConnectionEnv[]>('/api/common/environment/list_all', { errorLevel: false });\n\n/** 导入Navicat链接 */\n// const importNavicatConnection = createRequest<\n//   {\n//     formData: FormData;\n//   },\n//   void\n// >('/api/converter/ncx/upload', {\n//   method: 'post',\n// });\n\nexport default {\n  getEnvList,\n  getList,\n  getDetails,\n  save,\n  test,\n  update,\n  remove,\n  clone,\n  getDatabaseList,\n  getSchemaList,\n  close,\n  testSSH,\n  getDriverList,\n  downloadDriver,\n  saveDriver,\n  // importNavicatConnection,\n};\n"
  },
  {
    "path": "chat2db-client/src/service/dashboard.ts",
    "content": "import { IChartItem, IDashboardItem, IPageResponse } from '@/typings';\nimport createRequest from './base';\n\n/** 获取报表列表 */\nconst getDashboardList = createRequest<{}, IPageResponse<IDashboardItem>>('/api/dashboard/list', { method: 'get' });\nconst getDashboardById = createRequest<{ id: number }, IDashboardItem>('/api/dashboard/:id', { method: 'get' });\n/** 创建报表 */\nconst createDashboard = createRequest<{ name: string; description: string; schema?: string; chartId?: number[] }, void>(\n  '/api/dashboard/create',\n  { method: 'post' },\n);\n/** 更新报表 */\nconst updateDashboard = createRequest<IDashboardItem, void>('/api/dashboard/update', { method: 'post' });\n/** 删除报表 */\nconst deleteDashboard = createRequest<{ id: number }, string>('/api/dashboard/:id', { method: 'delete' });\n\n/** 根据id 查询图表详情 */\nconst getChartById = createRequest<{ id: number }, IChartItem>('/api/chart/:id', { method: 'get' });\n/** 创建图表 */\nconst createChart = createRequest<IChartItem, number>('/api/chart/create', { method: 'post' });\n/** 更新图表 */\nconst updateChart = createRequest<IChartItem, void>('/api/chart/update', { method: 'post' });\n/** 删除图表 */\nconst deleteChart = createRequest<{ id: number }, string>('/api/chart/:id', { method: 'delete' });\n\nexport {\n  getDashboardList,\n  getDashboardById,\n  createDashboard,\n  updateDashboard,\n  deleteDashboard,\n  getChartById,\n  createChart,\n  updateChart,\n  deleteChart,\n};\n"
  },
  {
    "path": "chat2db-client/src/service/history.ts",
    "content": "import createRequest from \"./base\";\n// import { IPageResponse,IPageParams,IHistoryRecord, IWindowTab, ISavedConsole } from '@/types';\nimport { DatabaseTypeCode, ConsoleStatus } from '@/constants'\nimport { ICreateConsole, IConsole, IPageResponse, IPageParams } from '@/typings';\n\nexport interface IGetSavedListParams extends IPageParams {\n  tabOpened?: 'y' | 'n';\n  status?: ConsoleStatus\n}\nexport interface IGetHistoryListParams extends IPageParams { \n  dataSourceId?: number;\n  databaseName?: string;\n}\nexport interface ISaveBasicInfo {\n  name: string;\n  type: DatabaseTypeCode;\n  ddl: string;\n  dataSourceId: number;\n  databaseName: string;\n}\n\nexport interface IUpdateConsoleParams {\n  id: number;\n}\n\nexport interface IHistoryRecord { \n /**\n * 是否可连接\n */\n connectable?: boolean | null;\n /**\n  * DB名称\n  */\n databaseName?: null | string;\n /**\n  * 数据源id\n  */\n dataSourceId?: number | null;\n /**\n  * 数据源名称\n  */\n dataSourceName?: null | string;\n /**\n  * ddl内容\n  */\n ddl?: null | string;\n /**\n  * 扩展信息\n  */\n extendInfo?: null | string;\n /**\n  * 主键\n  */\n id?: number | null;\n /**\n  * 文件别名\n  */\n name?: null | string;\n /**\n  * 操作行数\n  */\n operationRows?: number | null;\n /**\n  * schema名称\n  */\n schemaName?: null | string;\n /**\n  * 状态\n  */\n status?: null | string;\n /**\n  * ddl语言类型\n  */\n type?: DatabaseTypeCode | null;\n /**\n  * 使用时长\n  */\n useTime?: number | null;\n  /**\n  * 创建时间\n  */\n gmtCreate: string;\n}\n\nconst createConsole = createRequest<ICreateConsole, number>('/api/operation/saved/create', { method: 'post' });\n\n// orderByDesc true 降序\nconst getWindowTab = createRequest<{ id: number, orderByDesc: boolean }, number>('/api/operation/saved/:id', { method: 'get' });\n\nconst updateSavedConsole = createRequest<Partial<IConsole> & {id: number}, number>('/api/operation/saved/update', { method: 'post' });\n\nconst getConsoleList = createRequest<IGetSavedListParams, IPageResponse<IConsole>>('/api/operation/saved/list', {});\n\nconst deleteSavedConsole = createRequest<{ id: number }, string>('/api/operation/saved/:id', { method: 'delete' });\n\nconst createHistory = createRequest<ISaveBasicInfo, void>('/api/operation/log/create', { method: 'post' });\n\nconst getHistoryList = createRequest<IGetHistoryListParams, IPageResponse<IHistoryRecord>>('/api/operation/log/list', {});\n\nexport default {\n  getConsoleList,\n  updateSavedConsole,\n  getHistoryList,\n  createConsole,\n  deleteSavedConsole,\n  createHistory,\n  getWindowTab\n}\n"
  },
  {
    "path": "chat2db-client/src/service/misc.tsx",
    "content": "import createRequest from './base';\nconst testService = createRequest<null, boolean>('/api/system', { errorLevel: false });\nconst systemStop = createRequest<void, void>('/api/system/stop', { errorLevel: false, method: 'post' });\nconst testApiSmooth = createRequest<void, void>('/api/system/get-version-a', { errorLevel: false, method: 'get' });\n\nexport default {\n  testService,\n  systemStop,\n  testApiSmooth,\n};\n"
  },
  {
    "path": "chat2db-client/src/service/outside.ts",
    "content": "import createRequest from './base';\n\nconst dynamicUrl = createRequest<string, void>('', {\n  dynamicUrl: true,\n});\n\nexport default {\n  dynamicUrl,\n};\n"
  },
  {
    "path": "chat2db-client/src/service/sql.ts",
    "content": "import createRequest from './base';\nimport {\n  IPageResponse,\n  IPageParams,\n  IUniversalTableParams,\n  IManageResultData,\n  IRoutines,\n  IDatabaseSupportField,\n  IEditTableInfo,\n  ITable,\n  ISequenceInfo,\n} from '@/typings';\nimport { DatabaseTypeCode } from '@/constants';\nimport { ExportSizeEnum, ExportTypeEnum } from '@/typings/resultTable';\n\nexport interface IGetTableListParams extends IPageParams {\n  dataSourceId: number;\n  databaseName: string;\n  schemaName?: string;\n  databaseType?: DatabaseTypeCode;\n}\n\nexport interface IExecuteSqlParams {\n  sql?: string;\n  consoleId?: number;\n  dataSourceId?: number;\n  databaseName?: string;\n  schemaName?: string | null;\n  tableName?: string;\n  pageNo?: number;\n  pageSize?: number;\n}\n\nexport interface IExecuteSqlResponse {\n  sql: string;\n  description: string;\n  message: string;\n  success: boolean;\n  headerList: any[];\n  dataList: any[];\n}\nexport interface IConnectConsoleParams {\n  consoleId: number;\n  dataSourceId: number;\n  databaseName: string;\n}\n\nconst getTableList = createRequest<IGetTableListParams, IPageResponse<ITable>>('/api/rdb/table/list', { method: 'get' });\n\nconst executeSql = createRequest<IExecuteSqlParams, IManageResultData[]>('/api/rdb/dml/execute', { method: 'post', delayTime: 10 });\n\nconst viewTable = createRequest<IExecuteSqlParams, IManageResultData[]>('/api/rdb/dml/execute_table', { method: 'post', delayTime: 10 });\n\nconst connectConsole = createRequest<IConnectConsoleParams, void>('/api/connection/console/connect', { method: 'get' });\n\n//表操作\nexport interface ITableParams {\n  Name: string;\n  dataSourceId: number;\n  databaseName: string;\n  schemaName?: string;\n}\n\nexport interface IExecuteTableParams {\n  sql: string;\n  consoleId: number;\n  dataSourceId: number;\n  databaseName: string;\n}\n\nexport interface IColumn {\n  name: string;\n  dataType: string;\n  columnType: string; // 列的类型 比如 varchar(100) ,double(10,6)\n  nullable: boolean;\n  primaryKey: boolean;\n  defaultValue: string;\n  autoIncrement: boolean;\n  numericPrecision: number;\n  numericScale: number;\n  characterMaximumLength: number;\n  comment: string;\n}\n\nexport interface ISchemaParams {\n  dataSourceId: number;\n  databaseName: string;\n}\nexport interface ISchemaResponse {\n  name: string;\n}\n\nexport interface MetaSchemaVO {\n  databases?: Database[];\n  schemas?: Schema[];\n}\n\nexport interface Database {\n  name: string;\n  schemas?: Schema[];\n}\n\nexport interface Schema {\n  name: string;\n}\n\nconst deleteTable = createRequest<ITableParams, void>('/api/rdb/ddl/delete', { method: 'post' });\nconst deleteSequence = createRequest<ITableParams, void>('/api/rdb/sequence/delete', { method: 'post' });\nconst createTableExample = createRequest<{ dbType: DatabaseTypeCode }, string>('/api/rdb/ddl/create/example', {\n  method: 'get',\n});\nconst updateTableExample = createRequest<{ dbType: DatabaseTypeCode }, string>('/api/rdb/ddl/update/example', {\n  method: 'get',\n});\nconst exportCreateTableSql = createRequest<ITableParams, string>('/api/rdb/ddl/export', { method: 'get' });\nconst executeTable = createRequest<IExecuteTableParams, string>('/api/rdb/ddl/execute', { method: 'post' });\n\nconst getColumnList = createRequest<ITableParams, IColumn[]>('/api/rdb/ddl/column_list', {\n  method: 'get',\n  delayTime: 200,\n});\nconst getIndexList = createRequest<ITableParams, IColumn[]>('/api/rdb/ddl/index_list', {\n  method: 'get',\n  delayTime: 200,\n});\nconst getSequenceList = createRequest<ITableParams, IColumn[]>('/api/rdb/sequence/list', { \n  method: 'get',\n  delayTime: 200,\n});\nconst getKeyList = createRequest<ITableParams, IColumn[]>('/api/rdb/ddl/key_list', { method: 'get', delayTime: 200 });\nconst getSchemaList = createRequest<ISchemaParams, ISchemaResponse[]>('/api/rdb/ddl/schema_list', {\n  method: 'get',\n  delayTime: 200,\n});\n\nconst getDatabaseSchemaList = createRequest<{ dataSourceId: number }, MetaSchemaVO>(\n  '/api/rdb/ddl/database_schema_list',\n  { method: 'get' },\n);\n\nconst addTablePin = createRequest<IUniversalTableParams, void>('/api/pin/table/add', { method: 'post' });\n\nconst deleteTablePin = createRequest<IUniversalTableParams, void>('/api/pin/table/delete', { method: 'post' });\n\n/** 获取当前执行SQL 所有行 */\nconst getDMLCount = createRequest<IExecuteSqlParams, number>('/api/rdb/dml/count', { method: 'post' });\n\nexport interface IExportParams extends IExecuteSqlParams {\n  originalSql: string;\n  exportType: ExportTypeEnum;\n  exportSize: ExportSizeEnum;\n}\n/**\n * 导出-表格\n */\n// const exportResultTable = createRequest<IExportParams, any>('/api/rdb/dml/export', { method: 'post' });\nconst exportCreateSequenceSql = createRequest<ITableParams, string>('/api/rdb/sequence/export', { method: 'get' });\n\n/** 获取视图列表 */\nconst getViewList = createRequest<IGetTableListParams, IPageResponse<IRoutines>>('/api/rdb/view/list', {\n  method: 'get',\n});\n\n/** 获取函数列表 */\nconst getFunctionList = createRequest<IGetTableListParams, IPageResponse<IRoutines>>('/api/rdb/function/list', {\n  method: 'get',\n});\n\n/** 获取触发器列表 */\nconst getTriggerList = createRequest<IGetTableListParams, IPageResponse<IRoutines>>('/api/rdb/trigger/list', {\n  method: 'get',\n});\n\n/** 获取过程列表 */\nconst getProcedureList = createRequest<IGetTableListParams, IPageResponse<IRoutines>>('/api/rdb/procedure/list', {\n  method: 'get',\n});\n\n/** 获取视图列列表 */\nconst getViewColumnList = createRequest<IGetTableListParams, IPageResponse<IRoutines>>('/api/rdb/view/column_list', {\n  method: 'get',\n});\n\n\n\n/** 获取视图详情 */\nconst getViewDetail = createRequest<\n  {\n    dataSourceId: number;\n    databaseName: string;\n    schemaName?: string;\n    tableName: string;\n  },\n  { ddl: string }\n>('/api/rdb/view/detail', { method: 'get' });\n\n/** 获取触发器详情 */\nconst getTriggerDetail = createRequest<\n  {\n    dataSourceId: number;\n    databaseName: string;\n    schemaName?: string;\n    triggerName: string;\n  },\n  { triggerBody: string }\n>('/api/rdb/trigger/detail', { method: 'get' });\n\n/** 获取函数详情 */\nconst getFunctionDetail = createRequest<\n  {\n    dataSourceId: number;\n    databaseName: string;\n    schemaName?: string;\n    functionName: string;\n  },\n  { functionBody: string }\n>('/api/rdb/function/detail', { method: 'get' });\n\n/** 获取过程详情 */\nconst getProcedureDetail = createRequest<\n  {\n    dataSourceId: number;\n    databaseName: string;\n    schemaName?: string;\n    procedureName: string;\n  },\n  { procedureBody: string }\n>('/api/rdb/procedure/detail', { method: 'get' });\n\n\n\n\n/** 格式化sql */\nconst sqlFormat = createRequest<\n  {\n    sql: string;\n    dbType: DatabaseTypeCode;\n  },\n  string\n>('/api/sql/format', { method: 'get' });\n\n/** 数据库支持的数据类型 */\nconst getDatabaseFieldTypeList = createRequest<\n  {\n    dataSourceId: number;\n    databaseName: string;\n  },\n  IDatabaseSupportField\n>('/api/rdb/table/table_meta', { method: 'get' });\n\n/** 获取表的详情 */\nconst getTableDetails = createRequest<\n  {\n    dataSourceId: number;\n    databaseName: string;\n    schemaName?: string | null;\n    tableName: string;\n    refresh: boolean;\n  },\n  IEditTableInfo\n>('/api/rdb/table/query', { method: 'get' });\n/** 获取库的所有表 */\nconst getAllTableList = createRequest<\n  { dataSourceId: number; databaseName?: string | null; schemaName?: string | null },\n  Array<{ name: string; comment: string }>\n>('/api/rdb/table/table_list', { method: 'get' });\n\n/** 获取表的所有字段 */\nconst getAllFieldByTable = createRequest<\n  { dataSourceId: number; databaseName?: string; schemaName?: string | null; tableName: string },\n  Array<{ name: string; tableName: string }>\n>('/api/rdb/table/column_list', { method: 'get' });\n\nexport interface IModifyTableSqlParams {\n  dataSourceId: number;\n  databaseName: string;\n  schemaName?: string | null;\n  tableName?: string;\n  oldTable?: IEditTableInfo;\n  newTable: IEditTableInfo;\n  refresh: boolean;\n}\n\n/** 获取修改表的sql */\nconst getModifyTableSql = createRequest<IModifyTableSqlParams, { sql: string }[]>('/api/rdb/table/modify/sql', {\n  method: 'post',\n});\n\nexport interface IModifySequenceSqlParams {\n  dataSourceId: number;\n  databaseName: string;\n  schemaName?: string | null;\n  sequenceName?: string;\n  oldSequence?: ISequenceInfo;\n  newSequence: ISequenceInfo;\n  refresh: boolean;\n}\n/** 获取序列的详情 */\nconst getSequenceDetails = createRequest<\n  {\n    dataSourceId: number;\n    databaseName: string;\n    schemaName?: string | null;\n    sequenceName: string;\n    refresh: boolean;\n  },\n  ISequenceInfo\n>('/api/rdb/sequence/query', { method: 'get' });\n/**\n * 获取修改序列的sql\n*/\nconst getModifySequenceSql = createRequest<IModifySequenceSqlParams, { sql: string }[]>('/api/rdb/sequence/modify/sql', {\n  method: 'post',\n});\n\n/** 执行编辑表的sql, 专为编辑表而生 */\nconst executeDDL = createRequest<IExecuteSqlParams, { success: boolean; message: string; originalSql: string }>(\n  '/api/rdb/dml/execute_ddl',\n  { method: 'post' },\n);\n\n// 执行修改表数据的sql\nconst executeUpdateDataSql = createRequest<IExecuteSqlParams, { success: boolean; message: string; sql: string }>(\n  '/api/rdb/dml/execute_update',\n  { method: 'post' },\n);\n\n/** 获取修改表数据的接口 */\nconst getExecuteUpdateSql = createRequest<any, string>('/api/rdb/dml/get_update_sql', { method: 'post' });\n\n/** 创建数据库  */ \nconst getCreateDatabaseSql = createRequest<{\n  dataSourceId: number;\n  databaseName: string;\n}, { sql: string }>('/api/rdb/database/create_database_sql', { method: 'post' });\n\n/** 创建schema  */ \nconst getCreateSchemaSql = createRequest<{\n  dataSourceId: number;\n  databaseName?: string;\n  schemaName?: string;\n}, {sql:string}>('/api/rdb/schema/create_schema_sql', { method: 'post' });\n\n/** 查询数据库用户名列表  */\nconst getDatabaseUserNameList = createRequest<{\n  dataSourceId: number;\n  databaseName: string;\n  schemaName?: string | null;\n  refresh: boolean;\n},{sql:[]}>('/api/rdb/database/database_username_list', { method: 'get' });\nexport default {\n  getCreateSchemaSql,\n  getCreateDatabaseSql,\n  executeUpdateDataSql,\n  executeDDL,\n  getExecuteUpdateSql,\n  getModifyTableSql,\n  getTableDetails,\n  getDatabaseFieldTypeList,\n  sqlFormat,\n  getTriggerDetail,\n  getProcedureDetail,\n  getFunctionDetail,\n  getViewDetail,\n  getViewColumnList,\n  getProcedureList,\n  getTriggerList,\n  getFunctionList,\n  getViewList,\n  getTableList,\n  executeSql,\n  executeTable,\n  connectConsole,\n  deleteTable,\n  createTableExample,\n  updateTableExample,\n  exportCreateTableSql,\n  viewTable,\n  getColumnList,\n  getIndexList,\n  getKeyList,\n  getSchemaList,\n  getDatabaseSchemaList,\n  addTablePin,\n  deleteTablePin,\n  getDMLCount,\n  // exportResultTable\n  getAllTableList,\n  getAllFieldByTable,\n  getSequenceList,\n  exportCreateSequenceSql,\n  getModifySequenceSql,\n  getSequenceDetails,\n  deleteSequence,\n  getDatabaseUserNameList,\n};\n"
  },
  {
    "path": "chat2db-client/src/service/team.ts",
    "content": "import createRequest from './base';\nimport { IConnectionDetails, IPageParams, IPageResponse } from '@/typings';\nimport { IDataSourceAccessObjectVO, IDataSourceVO, ITeamAndUserVO, ITeamVO, ITeamWithDataSourceVO, ITeamWithUserVO, IUserVO, IUserWithDataSourceVO, IUserWithTeamVO, RoleType } from '@/typings/team';\n\n// =============================== DataSource ============================\n/**\n * 链接-获取共享链接列表\n */\nconst getDataSourceList = createRequest<IPageParams, IPageResponse<IDataSourceVO>>('/api/admin/data_source/page', {\n  method: 'get',\n});\n\n/**\n * 链接-创建链接\n */\nconst createDataSource = createRequest<IConnectionDetails | {}, number>('/api/admin/data_source/create', {\n  method: 'post',\n});\n/**\n * 链接-更新链接\n */\nconst updateDataSource = createRequest<IConnectionDetails, number>('/api/admin/data_source/update', {\n  method: 'post',\n});\n/**\n * 链接-删除链接\n */\nconst deleteDataSource = createRequest<{ id: number }, boolean>('/api/admin/data_source/:id', {\n  method: 'delete',\n});\n/**\n * 链接-获取链接包含的团队/用户列表\n */\nconst getUserAndTeamListFromDataSource = createRequest<\n  IPageParams & { dataSourceId: number },\n  IPageResponse<IDataSourceAccessObjectVO>\n>('/api/admin/data_source/access/page', {\n  method: 'get',\n});\n\n/**\n * 链接-添加团队/人员权限到共享链接\n */\nconst updateUserAndTeamListFromDataSource = createRequest<\n  { dataSourceId: number; accessObjectList: Array<{ id: number; type: RoleType }> }, number\n>('/api/admin/data_source/access/batch_create', {\n  method: 'post',\n});\n\n/**\n * 链接-删除团队/人员权限到共享链接\n */\nconst deleteUserOrTeamFromDataSource = createRequest<{ id: number }, boolean>('/api/admin/data_source/access/:id', {\n  method: 'delete',\n});\n\n\n// ====================== User ======================\n\n/** 用户-用户管理列表查询 */\nconst getUserManagementList = createRequest<IPageParams, IPageResponse<IUserVO>>('/api/admin/user/page', {\n  method: 'get',\n});\n\n/** 创建用户 */\nconst createUser = createRequest<IUserVO, number>('/api/admin/user/create', {\n  method: 'post',\n});\n/** 更新用户 */\nconst updateUser = createRequest<IUserVO, number>('/api/admin/user/update', {\n  method: 'post',\n});\n/** 删除用户 */\nconst deleteUser = createRequest<{ id: number }, boolean>('/api/admin/user/:id', {\n  method: 'delete',\n});\n\n/** 用户-用户管理中获取所属团队列表 */\nconst getTeamListFromUser = createRequest<IPageParams & { userId: number }, IPageResponse<IUserWithTeamVO>>(\n  '/api/admin/user/team/page',\n  {\n    method: 'get',\n  },\n);\n/** 用户-用户管理中更新所属团队 */\nconst updateTeamListFromUser = createRequest<{ userId: number; teamIdList: number[] }, number>(\n  '/api/admin/user/team/batch_create',\n  {\n    method: 'post',\n  }\n);\n\n/** 用户-用户管理中删除所属团队 */\nconst deleteTeamListFromUser = createRequest<{ id: number }, boolean>('/api/admin/user/team/:id', {\n  method: 'delete',\n});\n\n/** 用户-用户管理中添加链接 */\nconst getDataSourceListFromUser = createRequest<\n  IPageParams & { userId: number },\n  IPageResponse<IUserWithDataSourceVO>\n>('/api/admin/user/data_source/page', {\n  method: 'get',\n});\n\n/** 用户-用户管理中更新链接 */\nconst updateDataSourceListFromUser = createRequest<\n  { userId: number; dataSourceIdList: number[] }, number>(\n    '/api/admin/user/data_source/batch_create', {\n    method: 'post',\n  });\n\n/** 用户-用户管理中删除链接 */\nconst deleteDataSourceFromUser = createRequest<{ id: number }, boolean>('/api/admin/user/data_source/:id', {\n  method: 'delete',\n});\n\n\n// ======================== 团队 ======================\n\n/** 团队-团队管理列表查询 */\nconst getTeamManagementList = createRequest<IPageParams, IPageResponse<ITeamVO>>('/api/admin/team/page', {\n  method: 'get',\n});\n\n/** 团队-创建团队 */\nconst createTeam = createRequest<ITeamVO, number>('/api/admin/team/create', {\n  method: 'post',\n});\n/** 团队-更新团队 */\nconst updateTeam = createRequest<ITeamVO, number>('/api/admin/team/update', {\n  method: 'post',\n});\n/** 团队-删除团队 */\nconst deleteTeam = createRequest<{ id: number }, boolean>('/api/admin/team/:id', {\n  method: 'delete',\n});\n\n/** 团队-团队管理中获取包含用户列表 */\nconst getUserListFromTeam = createRequest<IPageParams & { teamId: number }, IPageResponse<ITeamWithUserVO>>(\n  '/api/admin/team/user/page',\n  {\n    method: 'get',\n  },\n);\n/** 团队-团队管理中更新包含用户列表 */\nconst updateUserListFromTeam = createRequest<{ teamId: number; userIdList: number[] }, number>(\n  '/api/admin/team/user/batch_create',\n  {\n    method: 'post',\n  },\n);\n/** 团队-团队管理中删除包含用户列表 */\nconst deleteUserFromTeam = createRequest<{ id: number }, boolean>('/api/admin/team/user/:id', {\n  method: 'delete',\n});\n\n\n/** 用户-用户管理中添加归属链接 */\nconst getDataSourceListFromTeam = createRequest<\n  IPageParams & { userId: number },\n  IPageResponse<ITeamWithDataSourceVO>\n>('/api/admin/team/data_source/page', {\n  method: 'get',\n});\n\n/** 用户-用户管理中更新归属链接 */\nconst updateDataSourceListFromTeam = createRequest<\n  { userId: number; dataSourceIdList: number[] }, number\n>('/api/admin/team/data_source/batch_create', {\n  method: 'post',\n});\n\n/** 用户-用户管理中删除所属团队 */\nconst deleteDataSourceFromTeam = createRequest<{ id: number }, boolean>('/api/admin/team/data_source/:id', {\n  method: 'delete',\n});\n\n// ======================= 通用列表 =====================\n/** 通用-获取user列表 */\nconst getCommonUserList = createRequest<{ searchKey: string }, IUserVO[]>('/api/admin/common/user/list', {\n  method: 'get',\n});\n/** 通用-获取team列表 */\nconst getCommonTeamList = createRequest<{ searchKey: string }, ITeamVO[]>('/api/admin/common/team/list', {\n  method: 'get',\n});\n/** 通用-获取DataSource列表 */\nconst getCommonDataSourceList = createRequest<{ searchKey: string }, IDataSourceVO[]>(\n  '/api/admin/common/data_source/list',\n  {\n    method: 'get',\n  },\n);\n/** 通用-获取user和team列表 */\nconst getCommonUserAndTeamList = createRequest<{ searchKey: string }, ITeamAndUserVO[]>(\n  '/api/admin/common/team_user/list',\n  {\n    method: 'get',\n  },\n);\n\nexport {\n  // dataSource\n  getDataSourceList,\n  createDataSource,\n  updateDataSource,\n  deleteDataSource,\n  getUserAndTeamListFromDataSource,\n  updateUserAndTeamListFromDataSource,\n  deleteUserOrTeamFromDataSource,\n  // user\n  getUserManagementList,\n  createUser,\n  updateUser,\n  deleteUser,\n  getTeamListFromUser,\n  updateTeamListFromUser,\n  deleteTeamListFromUser,\n  getDataSourceListFromUser,\n  updateDataSourceListFromUser,\n  deleteDataSourceFromUser,\n  // team\n  getTeamManagementList,\n  createTeam,\n  updateTeam,\n  deleteTeam,\n  getUserListFromTeam,\n  updateUserListFromTeam,\n  deleteUserFromTeam,\n  getDataSourceListFromTeam,\n  updateDataSourceListFromTeam,\n  deleteDataSourceFromTeam,\n  // common\n  getCommonUserList,\n  getCommonTeamList,\n  getCommonDataSourceList,\n  getCommonUserAndTeamList,\n};\n"
  },
  {
    "path": "chat2db-client/src/service/user.ts",
    "content": "import createRequest from './base';\nimport { IPageParams, IPageResponse } from '@/typings';\nimport { IUserVO, IUser } from '@/typings/user';\n\n/** 用户登录接口 */\nconst userLogin = createRequest<{ userName: string; password: string }, boolean>('/api/oauth/login_a', {\n  method: 'post',\n});\n\n/** 用户登出 */\nconst userLogout = createRequest<void, void>('/api/oauth/logout_a', {\n  method: 'post',\n});\n\n/** 获取用户信息 */\nconst getUser = createRequest<void, IUserVO | null>('/api/oauth/user_a', { method: 'get' });\n\n/** 获取用户列表信息 */\nconst getUserList = createRequest<IPageParams, IPageResponse<IUser>>('/api/user/list', {\n  method: 'get',\n});\n\n/** 创建新用户 */\nconst createUser = createRequest<IUser, boolean>('/api/user/create', {\n  method: 'post',\n});\n\n/** 更新用户信息 */\nconst updateUser = createRequest<IUser, boolean>('/api/user/update', {\n  method: 'post',\n});\n\n/** 查询用户 */\nconst queryUserById = createRequest<{ id: number }>('/api/user/:id', {\n  method: 'get',\n});\n\n/** 删除用户 */\nconst deleteUser = createRequest<{ id: number }>('/api/user/:id', {\n  method: 'delete',\n});\n\nexport { createUser, updateUser, queryUserById, deleteUser, getUserList, userLogin, userLogout, getUser };\n"
  },
  {
    "path": "chat2db-client/src/store/common/appTitleBarConfig.ts",
    "content": "import React from 'react';\nimport { useCommonStore } from './index';\nexport interface IAppTitleBarConfig {\n  appTitleBarRightComponent: React.ReactNode | null;\n}\n\nexport const initAppTitleBarConfig = {\n  appTitleBarRightComponent: null,\n};\n\nexport const setAppTitleBarRightComponent: (appTitleBarRightComponent: React.ReactNode | null) => void = (\n  appTitleBarRightComponent,\n) => {\n  return useCommonStore.setState({ appTitleBarRightComponent });\n};\n"
  },
  {
    "path": "chat2db-client/src/store/common/components.ts",
    "content": "import { useCommonStore } from './index';\nimport { IModalData } from '@/components/Modal/BaseModal';\n\nexport interface IComponentsContent {\n  openModal: ((params: IModalData) => void) | null;\n}\n\nexport const initComponentsContent = {\n  openModal: null,\n};\n\nexport const injectOpenModal = (openModal: IComponentsContent['openModal']) => {\n  return useCommonStore.setState({ openModal });\n};\n\nexport const openModal = (modal: IModalData) => {\n  return useCommonStore.getState().openModal?.(modal);\n};\n"
  },
  {
    "path": "chat2db-client/src/store/common/copyFocusedContent.ts",
    "content": "import {useCommonStore} from './index'\nexport interface ICopyFocusedContent {\n  focusedContent: any[][]| any[] | string | null;\n}\n\nexport const initCopyFocusedContent = {\n  focusedContent: null,\n}\n\nexport const setFocusedContent: (content: any[][] | any[] | string | null) => void = (focusedContent) => {\n  return useCommonStore.setState({focusedContent})\n}\n"
  },
  {
    "path": "chat2db-client/src/store/common/index.ts",
    "content": "import { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\nimport { StoreApi } from 'zustand';\n\nimport { initCopyFocusedContent, ICopyFocusedContent } from './copyFocusedContent';\nimport { initComponentsContent, IComponentsContent } from './components';\nimport { initAppTitleBarConfig, IAppTitleBarConfig } from './appTitleBarConfig';\n\nexport type IStore = ICopyFocusedContent & IComponentsContent & IAppTitleBarConfig;\n\nexport const useCommonStore: UseBoundStoreWithEqualityFn<StoreApi<IStore>> = createWithEqualityFn(\n  devtools(\n    () => ({\n      ...initCopyFocusedContent,\n      ...initComponentsContent,\n      ...initAppTitleBarConfig\n    }),\n  ),\n  shallow\n);\n"
  },
  {
    "path": "chat2db-client/src/store/config/index.ts",
    "content": "import { StoreApi } from 'zustand';\nimport { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\n\nexport interface IConfigStore {\n  curRoute: string;\n}\n\nconst initConfigStore: IConfigStore = {\n  curRoute: '/',\n};\n\n/**\n * 配置 store\n */\nexport const useConfigStore: UseBoundStoreWithEqualityFn<StoreApi<IConfigStore>> = createWithEqualityFn(\n  devtools(() => initConfigStore),\n  shallow,\n);\n\n/**\n *\n * @param curRoute 设置当前路由\n */\nexport const setCurRoute = (curRoute: string) => {\n  useConfigStore.setState({ curRoute });\n}\n\n"
  },
  {
    "path": "chat2db-client/src/store/monaco/index.ts",
    "content": "import { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\nimport { StoreApi } from 'zustand';\n\n// 表信息\nexport interface tableItem {\n  columnList: {\n    columnName?: string;\n    columnType?: string;\n  }[];\n}\n\n// schema信息\nexport interface schemaItem {\n  tableList?: tableItem[];\n}\n\n// 数据库信息\nexport type databaseItem = {\n  schemaList?: schemaItem[];\n} | {\n  databaseName: string;\n  tableList: tableItem[];\n}\n\n// monaco store\nexport interface IMonacoStore {\n  registerProvider: {\n    // 数据源id\n    [key: number]: databaseItem[]\n  }\n}\n\nconst initMonacoStore = {\n  registerProvider: {}\n}\n\nexport const useMonacoStore: UseBoundStoreWithEqualityFn<StoreApi<IMonacoStore>> = createWithEqualityFn(\n  devtools(() => (initMonacoStore)),\n  shallow\n);\n\nexport const setRegisterProvider = (id: number, data: databaseItem[]) => {\n  useMonacoStore.getState().registerProvider[id] = data;\n}\n\n"
  },
  {
    "path": "chat2db-client/src/store/setting/index.ts",
    "content": "import { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools, persist } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\nimport { StoreApi } from 'zustand';\n\nimport { message } from 'antd';\nimport i18n from '@/i18n';\n\nimport { IAiConfig } from '@/typings/setting';\nimport { IRemainingUse, AIType } from '@/typings/ai';\nimport configService from '@/service/config';\nimport aiService from '@/service/ai';\n\nexport interface ISettingState {\n  aiConfig: IAiConfig;\n  remainingUse?: IRemainingUse;\n  hasWhite: boolean;\n  holdingService: boolean;\n}\n\nconst initSetting = {\n  remainingUse: undefined,\n  aiConfig: {\n    aiSqlSource: AIType.CHAT2DBAI,\n  },\n  hasWhite: false,\n  holdingService: false,\n}\n\nexport const useSettingStore: UseBoundStoreWithEqualityFn<StoreApi<ISettingState>> = createWithEqualityFn(\n  devtools(\n    persist(\n      () => (initSetting),\n      {\n        name: 'global-setting',\n        getStorage: () => localStorage,\n        // 工作区的状态只保存 layout布局信息\n        partialize: (state: ISettingState) => ({\n          holdingService: state.holdingService,\n        }),\n      },\n    ),\n  ),\n  shallow\n);\n\nexport const setAiConfig = (aiConfig: IAiConfig) => {\n  useSettingStore.setState({ aiConfig });\n}\n\nexport const setRemainUse = (remainingUse?: IRemainingUse) => {\n  useSettingStore.setState({ remainingUse });\n}\n\nexport const setAiWithWhite = (hasWhite: boolean) => {\n  useSettingStore.setState({ hasWhite });\n}\n\nexport const updateAiWithWhite = (apiKey: string) => {\n  configService.getAiWhiteAccess({ apiKey: apiKey ?? '' }).then((res) => {\n    setAiWithWhite(res);\n  });\n}\n\nexport const getAiSystemConfig = () => {\n  configService.getAiSystemConfig({}).then((res) => {\n    setAiConfig(res);\n    if (res?.aiSqlSource === AIType.CHAT2DBAI && res.apiKey) {\n      updateAiWithWhite(res.apiKey);\n    }\n  });\n}\n\nexport const setAiSystemConfig = (aiConfig) => {\n  configService.setAiSystemConfig(aiConfig).then(() => {\n    message.success(i18n('common.text.submittedSuccessfully'));\n    setAiConfig(aiConfig);\n  })\n  if (aiConfig?.aiSqlSource === AIType.CHAT2DBAI) {\n    updateAiWithWhite(aiConfig?.apiKey);\n  } else {\n    setAiWithWhite(false);\n  }\n}\n\nexport const fetchRemainingUse = (apiKey)=>{\n  const currentState = useSettingStore.getState();\n  if (!apiKey || currentState.aiConfig.aiSqlSource !== AIType.CHAT2DBAI) {\n    setRemainUse(undefined);\n    return;\n  }\n  aiService.getRemainingUse().then((res) => {\n    setRemainUse(res);\n  })\n}\n\nexport const setHoldingService = (holdingService: boolean) => {\n  useSettingStore.setState({ holdingService });\n}\n\n\n\n\n"
  },
  {
    "path": "chat2db-client/src/store/user/index.ts",
    "content": "import { StoreApi } from 'zustand';\nimport { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';\nimport { devtools } from 'zustand/middleware';\nimport { shallow } from 'zustand/shallow';\nimport { IUserVO } from '@/typings/user';\nimport { getUser } from '@/service/user';\n\nexport interface IUserStore {\n  curUser?: IUserVO | null;\n}\n\nconst initUserStore: IUserStore = {\n  curUser: null,\n};\n\n/**\n * 用户 store\n */\nexport const useUserStore: UseBoundStoreWithEqualityFn<StoreApi<IUserStore>> = createWithEqualityFn(\n  devtools(() => initUserStore),\n  shallow,\n);\n\n/**\n *\n * @param curUser 设置当前用户\n */\n\nexport const setCurUser = (curUser?: IUserVO) => {\n  useUserStore.setState({ curUser });\n};\n\n/**\n * 获取当前用户\n */\n\nexport const queryCurUser = async () => {\n  // null 表示在padding，返回 void 0(undefined)表示未登录\n  const curUser = await getUser() || void 0;\n  useUserStore.setState({ curUser });\n  // 向cookie中写入当前用户id\n  const date = new Date('2030-12-30 12:30:00').toUTCString();\n  document.cookie = `CHAT2DB.USER_ID=${curUser?.id};Expires=${date}`;\n  return curUser\n};\n"
  },
  {
    "path": "chat2db-client/src/styles/antd.less",
    "content": ":root {\n  :global {\n    // 覆盖antd 的一些样式\n    button {\n      box-shadow: none !important;\n    }\n    \n    // There is some animation when switching the theme color causing a delay in switching background\n    .ant-input,\n    .ant-input-password {\n      transition: all 0.2s, background-color 0s;\n    }\n    .ant-btn {\n      transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1), background-color 0s;\n    }\n\n    .ant-select-single:not(.ant-select-customize-input) .ant-select-selector {\n      transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1), background-color 0s;\n    }\n\n    .ant-modal-header {\n      border-bottom: 0px;\n    }\n\n    // .ant-modal-content {\n    //   background-color: var(--color-bg-elevated) !important;\n\n    //   .ant-modal-confirm-title {\n    //     color: var(--color-text) !important;\n    //   }\n\n    //   .ant-modal-confirm-content {\n    //     color: var(--color-text) !important;\n    //   }\n    // }\n\n    .ant-modal-footer {\n      border-top: 0px;\n      padding: 8px;\n      padding-top: 0px;\n    }\n\n    // .ant-notification-notice {\n    //   background-color: var(--color-bg-elevated) !important;\n    //   padding: 8px 8px 8px 16px !important;\n    //   width: 280px !important;\n\n    //   .ant-notification-notice-icon {\n    //     font-size: 16px !important;\n    //     top: 12px !important;\n    //   }\n\n    //   .ant-notification-notice-message {\n    //     font-size: 14px !important;\n    //     color: var(--color-text) !important;\n    //   }\n\n    //   .ant-notification-notice-close {\n    //     font-size: 12px !important;\n    //     color: var(--color-text) !important;\n    //     width: 16px !important;\n    //     height: 16px !important;\n    //     top: 10px !important;\n    //   }\n    // }\n\n    .ant-dropdown{\n      .ant-dropdown-menu {\n        height: auto;\n        max-height: 50vh;\n        // overflow: hidden  ;\n        // &:hover {\n        // }\n        overflow: auto;\n      }\n    }\n\n    .ant-btn-link {\n      color: var(--color-primary);\n    }\n    \n    .ant-btn-link:not(:disabled):not(.ant-btn-disabled):hover {\n      color: var(--color-primary-hover);\n    }\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/styles/common.less",
    "content": ""
  },
  {
    "path": "chat2db-client/src/styles/global.less",
    "content": "@import '../theme/custom/dark.less';\n@import '../theme/custom/light.less';\n@import './antd.less';\n\n@font-face {\n  font-family: 'HarmonyOS_Sans';\n  src: url('../assets/font/HarmonyOS_Sans_Regular.woff2') format('woff2');\n}\n\nhtml,\nbody {\n  height: 100%;\n  color: var(--color-text);\n  font-size: var(--font-size);\n  background-color: var(--color-bg-base);\n  font-family: 'HarmonyOS_Sans', 'Segoe UI', 'SF Pro Display', -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu,\n    Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, 'HarmonyOS Sans SC', 'PingFang SC', 'Hiragino Sans GB',\n    'Microsoft Yahei UI', 'Microsoft Yahei', 'Source Han Sans CN', sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol',\n    'Apple Color Emoji', 'Twemoji Mozilla', 'Noto Color Emoji', 'Android Emoji';\n}\n\n// 修改账号密码自动回填后input背景变色问题\ninput:-webkit-autofill {\n  -webkit-text-fill-color: var(--color-text) !important; // 填充状态下的字体颜色\n  transition: background-color 0s 9999999999s !important;\n  caret-color: var(--color-text) !important; // 光标的颜色\n}\n\n* {\n  ::-webkit-scrollbar {\n    cursor: pointer;\n    width: 4px;\n    height: 4px;\n    background-color: transparent;\n  }\n\n  ::-webkit-scrollbar-thumb {\n    cursor: pointer;\n    background-color: transparent;\n    border-radius: 2px;\n    // transition: background-color 500ms ${token.motionEaseOut};\n\n    &:hover {\n      background-color: var(--color-text);\n    }\n  }\n\n  ::-webkit-scrollbar-corner {\n    display: none;\n    width: 0;\n    height: 0;\n  }\n\n  &:hover {\n    ::-webkit-scrollbar-thumb {\n      background-color: var(--color-fill);\n    }\n  }\n}\n\nhtml,\nbody,\ndiv,\nspan,\napplet,\nobject,\niframe,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\np,\nblockquote,\npre,\na,\nabbr,\nacronym,\naddress,\nbig,\ncite,\ncode,\ndel,\ndfn,\nem,\nimg,\nins,\nkbd,\nq,\ns,\nsamp,\nsmall,\nstrike,\nstrong,\nsub,\nsup,\ntt,\nvar,\nb,\nu,\ni,\ncenter,\ndl,\ndt,\ndd,\nol,\nul,\nli,\nfieldset,\nform,\nlabel,\nlegend,\ntable,\ncaption,\ntbody,\ntfoot,\nthead,\ntr,\nth,\ntd,\narticle,\naside,\ncanvas,\ndetails,\nembed,\nfigure,\nfigcaption,\nfooter,\nheader,\nhgroup,\nmenu,\nnav,\noutput,\nruby,\nsection,\nsummary,\ntime,\nmark,\naudio,\nvideo {\n  margin: 0;\n  padding: 0;\n  border: 0;\n}\n\nul,\nol,\nli {\n  list-style: none;\n}\n"
  },
  {
    "path": "chat2db-client/src/styles/var.less",
    "content": ".f-single-line() {\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.f-lines(@lines) {\n  word-break: break-word;\n  display: -webkit-box;\n  -webkit-box-orient: vertical;\n  box-orient: vertical;\n  -webkit-line-clamp: @lines;\n  line-clamp: @lines;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.f-icon-button {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  height: 32px;\n  width: 32px;\n  border-radius: 4px;\n  cursor: pointer;\n  color: var(--color-text-45);\n\n  &:hover {\n    background-color: var(--color-bg-hover);\n  }\n\n  i {\n    font-size: 22px;\n  }\n}\n\n.f-fill-absolute {\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  bottom: 0;\n}\n\n.f-button {\n  display: flex !important;\n  align-items: center;\n  span {\n    font-size: 12px !important;\n    font-weight: 400 !important;\n  }\n}\n\n// 文档英文强制换行\n.f-doc-en-break {\n  word-break: break-all;\n}\n\n@keyframes loading-animation {\n  0% {\n    transform: rotate(0deg);\n  }\n  100% {\n    transform: rotate(360deg);\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/theme/abandon/demo/dark.less",
    "content": "html[theme='dark'],\nhtml[primary-color='polar-blue'] {\n  --blue: #1677ff;\n  --purple: #722ed1;\n  --cyan: #13c2c2;\n  --green: #52c41a;\n  --magenta: #eb2f96;\n  --pink: #eb2f96;\n  --red: #f5222d;\n  --orange: #fa8c16;\n  --yellow: #fadb14;\n  --volcano: #fa541c;\n  --geekblue: #2f54eb;\n  --gold: #faad14;\n  --lime: #a0d911;\n  --color-primary: #1668dc;\n  --color-success: #49aa19;\n  --color-warning: #d89614;\n  --color-error: #dc4446;\n  --color-info: #1668dc;\n  --color-text-base: #fff;\n  --color-bg-base: #0a0b0c;\n  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,\n    'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';\n  --font-family-code: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;\n  --font-size: 12;\n  --line-width: 1;\n  --line-type: solid;\n  --motion-unit: 0.1;\n  --motion-base: 0;\n  --motion-ease-out-circ: cubic-bezier(0.08, 0.82, 0.17, 1);\n  --motion-ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86);\n  --motion-ease-out: cubic-bezier(0.215, 0.61, 0.355, 1);\n  --motion-ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1);\n  --motion-ease-out-back: cubic-bezier(0.12, 0.4, 0.29, 1.46);\n  --motion-ease-in-back: cubic-bezier(0.71, -0.46, 0.88, 0.6);\n  --motion-ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);\n  --motion-ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);\n  --border-radius: 4;\n  --size-unit: 4;\n  --size-step: 4;\n  --size-popup-arrow: 16;\n  --control-height: 28;\n  --z-index-base: 0;\n  --z-index-popup-base: 1000;\n  --opacity-image: 1;\n  --wireframe: true;\n  --motion: true;\n  --blue-1: #111a2c;\n  --blue1: #111a2c;\n  --blue-2: #112545;\n  --blue2: #112545;\n  --blue-3: #15325b;\n  --blue3: #15325b;\n  --blue-4: #15417e;\n  --blue4: #15417e;\n  --blue-5: #1554ad;\n  --blue5: #1554ad;\n  --blue-6: #1668dc;\n  --blue6: #1668dc;\n  --blue-7: #3c89e8;\n  --blue7: #3c89e8;\n  --blue-8: #65a9f3;\n  --blue8: #65a9f3;\n  --blue-9: #8dc5f8;\n  --blue9: #8dc5f8;\n  --blue-10: #b7dcfa;\n  --blue10: #b7dcfa;\n  --purple-1: #1a1325;\n  --purple1: #1a1325;\n  --purple-2: #24163a;\n  --purple2: #24163a;\n  --purple-3: #301c4d;\n  --purple3: #301c4d;\n  --purple-4: #3e2069;\n  --purple4: #3e2069;\n  --purple-5: #51258f;\n  --purple5: #51258f;\n  --purple-6: #642ab5;\n  --purple6: #642ab5;\n  --purple-7: #854eca;\n  --purple7: #854eca;\n  --purple-8: #ab7ae0;\n  --purple8: #ab7ae0;\n  --purple-9: #cda8f0;\n  --purple9: #cda8f0;\n  --purple-10: #ebd7fa;\n  --purple10: #ebd7fa;\n  --cyan-1: #112123;\n  --cyan1: #112123;\n  --cyan-2: #113536;\n  --cyan2: #113536;\n  --cyan-3: #144848;\n  --cyan3: #144848;\n  --cyan-4: #146262;\n  --cyan4: #146262;\n  --cyan-5: #138585;\n  --cyan5: #138585;\n  --cyan-6: #13a8a8;\n  --cyan6: #13a8a8;\n  --cyan-7: #33bcb7;\n  --cyan7: #33bcb7;\n  --cyan-8: #58d1c9;\n  --cyan8: #58d1c9;\n  --cyan-9: #84e2d8;\n  --cyan9: #84e2d8;\n  --cyan-10: #b2f1e8;\n  --cyan10: #b2f1e8;\n  --green-1: #162312;\n  --green1: #162312;\n  --green-2: #1d3712;\n  --green2: #1d3712;\n  --green-3: #274916;\n  --green3: #274916;\n  --green-4: #306317;\n  --green4: #306317;\n  --green-5: #3c8618;\n  --green5: #3c8618;\n  --green-6: #49aa19;\n  --green6: #49aa19;\n  --green-7: #6abe39;\n  --green7: #6abe39;\n  --green-8: #8fd460;\n  --green8: #8fd460;\n  --green-9: #b2e58b;\n  --green9: #b2e58b;\n  --green-10: #d5f2bb;\n  --green10: #d5f2bb;\n  --magenta-1: #291321;\n  --magenta1: #291321;\n  --magenta-2: #40162f;\n  --magenta2: #40162f;\n  --magenta-3: #551c3b;\n  --magenta3: #551c3b;\n  --magenta-4: #75204f;\n  --magenta4: #75204f;\n  --magenta-5: #a02669;\n  --magenta5: #a02669;\n  --magenta-6: #cb2b83;\n  --magenta6: #cb2b83;\n  --magenta-7: #e0529c;\n  --magenta7: #e0529c;\n  --magenta-8: #f37fb7;\n  --magenta8: #f37fb7;\n  --magenta-9: #f8a8cc;\n  --magenta9: #f8a8cc;\n  --magenta-10: #fad2e3;\n  --magenta10: #fad2e3;\n  --pink-1: #291321;\n  --pink1: #291321;\n  --pink-2: #40162f;\n  --pink2: #40162f;\n  --pink-3: #551c3b;\n  --pink3: #551c3b;\n  --pink-4: #75204f;\n  --pink4: #75204f;\n  --pink-5: #a02669;\n  --pink5: #a02669;\n  --pink-6: #cb2b83;\n  --pink6: #cb2b83;\n  --pink-7: #e0529c;\n  --pink7: #e0529c;\n  --pink-8: #f37fb7;\n  --pink8: #f37fb7;\n  --pink-9: #f8a8cc;\n  --pink9: #f8a8cc;\n  --pink-10: #fad2e3;\n  --pink10: #fad2e3;\n  --red-1: #2a1215;\n  --red1: #2a1215;\n  --red-2: #431418;\n  --red2: #431418;\n  --red-3: #58181c;\n  --red3: #58181c;\n  --red-4: #791a1f;\n  --red4: #791a1f;\n  --red-5: #a61d24;\n  --red5: #a61d24;\n  --red-6: #d32029;\n  --red6: #d32029;\n  --red-7: #e84749;\n  --red7: #e84749;\n  --red-8: #f37370;\n  --red8: #f37370;\n  --red-9: #f89f9a;\n  --red9: #f89f9a;\n  --red-10: #fac8c3;\n  --red10: #fac8c3;\n  --orange-1: #2b1d11;\n  --orange1: #2b1d11;\n  --orange-2: #442a11;\n  --orange2: #442a11;\n  --orange-3: #593815;\n  --orange3: #593815;\n  --orange-4: #7c4a15;\n  --orange4: #7c4a15;\n  --orange-5: #aa6215;\n  --orange5: #aa6215;\n  --orange-6: #d87a16;\n  --orange6: #d87a16;\n  --orange-7: #e89a3c;\n  --orange7: #e89a3c;\n  --orange-8: #f3b765;\n  --orange8: #f3b765;\n  --orange-9: #f8cf8d;\n  --orange9: #f8cf8d;\n  --orange-10: #fae3b7;\n  --orange10: #fae3b7;\n  --yellow-1: #2b2611;\n  --yellow1: #2b2611;\n  --yellow-2: #443b11;\n  --yellow2: #443b11;\n  --yellow-3: #595014;\n  --yellow3: #595014;\n  --yellow-4: #7c6e14;\n  --yellow4: #7c6e14;\n  --yellow-5: #aa9514;\n  --yellow5: #aa9514;\n  --yellow-6: #d8bd14;\n  --yellow6: #d8bd14;\n  --yellow-7: #e8d639;\n  --yellow7: #e8d639;\n  --yellow-8: #f3ea62;\n  --yellow8: #f3ea62;\n  --yellow-9: #f8f48b;\n  --yellow9: #f8f48b;\n  --yellow-10: #fafab5;\n  --yellow10: #fafab5;\n  --volcano-1: #2b1611;\n  --volcano1: #2b1611;\n  --volcano-2: #441d12;\n  --volcano2: #441d12;\n  --volcano-3: #592716;\n  --volcano3: #592716;\n  --volcano-4: #7c3118;\n  --volcano4: #7c3118;\n  --volcano-5: #aa3e19;\n  --volcano5: #aa3e19;\n  --volcano-6: #d84a1b;\n  --volcano6: #d84a1b;\n  --volcano-7: #e87040;\n  --volcano7: #e87040;\n  --volcano-8: #f3956a;\n  --volcano8: #f3956a;\n  --volcano-9: #f8b692;\n  --volcano9: #f8b692;\n  --volcano-10: #fad4bc;\n  --volcano10: #fad4bc;\n  --geekblue-1: #131629;\n  --geekblue1: #131629;\n  --geekblue-2: #161d40;\n  --geekblue2: #161d40;\n  --geekblue-3: #1c2755;\n  --geekblue3: #1c2755;\n  --geekblue-4: #203175;\n  --geekblue4: #203175;\n  --geekblue-5: #263ea0;\n  --geekblue5: #263ea0;\n  --geekblue-6: #2b4acb;\n  --geekblue6: #2b4acb;\n  --geekblue-7: #5273e0;\n  --geekblue7: #5273e0;\n  --geekblue-8: #7f9ef3;\n  --geekblue8: #7f9ef3;\n  --geekblue-9: #a8c1f8;\n  --geekblue9: #a8c1f8;\n  --geekblue-10: #d2e0fa;\n  --geekblue10: #d2e0fa;\n  --gold-1: #2b2111;\n  --gold1: #2b2111;\n  --gold-2: #443111;\n  --gold2: #443111;\n  --gold-3: #594214;\n  --gold3: #594214;\n  --gold-4: #7c5914;\n  --gold4: #7c5914;\n  --gold-5: #aa7714;\n  --gold5: #aa7714;\n  --gold-6: #d89614;\n  --gold6: #d89614;\n  --gold-7: #e8b339;\n  --gold7: #e8b339;\n  --gold-8: #f3cc62;\n  --gold8: #f3cc62;\n  --gold-9: #f8df8b;\n  --gold9: #f8df8b;\n  --gold-10: #faedb5;\n  --gold10: #faedb5;\n  --lime-1: #1f2611;\n  --lime1: #1f2611;\n  --lime-2: #2e3c10;\n  --lime2: #2e3c10;\n  --lime-3: #3e4f13;\n  --lime3: #3e4f13;\n  --lime-4: #536d13;\n  --lime4: #536d13;\n  --lime-5: #6f9412;\n  --lime5: #6f9412;\n  --lime-6: #8bbb11;\n  --lime6: #8bbb11;\n  --lime-7: #a9d134;\n  --lime7: #a9d134;\n  --lime-8: #c9e75d;\n  --lime8: #c9e75d;\n  --lime-9: #e4f88b;\n  --lime9: #e4f88b;\n  --lime-10: #f0fab5;\n  --lime10: #1c2128;\n  --color-text: rgba(255, 255, 255, 0.85);\n  --color-text-secondary: rgba(255, 255, 255, 0.65);\n  --color-text-tertiary: rgba(255, 255, 255, 0.45);\n  --color-text-quaternary: rgba(255, 255, 255, 0.25);\n  --color-fill: rgba(255, 255, 255, 0.18);\n  --color-fill-secondary: rgba(255, 255, 255, 0.12);\n  --color-fill-tertiary: rgba(255, 255, 255, 0.08);\n  --color-fill-quaternary: rgba(255, 255, 255, 0.04);\n  --color-bg-layout: #0a0b0c;\n  --color-bg-container: #1d1f22;\n  --color-bg-elevated: #262a2d;\n  --color-bg-spotlight: #464d54;\n  --color-border: #464d54;\n  --color-border-secondary: #363b41;\n  --color-primary-bg: #111a2c;\n  --color-primary-bg-hover: #112545;\n  --color-primary-border: #15325b;\n  --color-primary-border-hover: #15417e;\n  --color-primary-hover: #3c89e8;\n  --color-primary-active: #1554ad;\n  --color-primary-text-hover: #3c89e8;\n  --color-primary-text: #1668dc;\n  --color-primary-text-active: #1554ad;\n  --color-success-bg: #162312;\n  --color-success-bg-hover: #1d3712;\n  --color-success-border: #274916;\n  --color-success-border-hover: #306317;\n  --color-success-hover: #306317;\n  --color-success-active: #3c8618;\n  --color-success-text-hover: #6abe39;\n  --color-success-text: #49aa19;\n  --color-success-text-active: #3c8618;\n  --color-error-bg: #2c1618;\n  --color-error-bg-hover: #451d1f;\n  --color-error-border: #5b2526;\n  --color-error-border-hover: #7e2e2f;\n  --color-error-hover: #e86e6b;\n  --color-error-active: #ad393a;\n  --color-error-text-hover: #e86e6b;\n  --color-error-text: #dc4446;\n  --color-error-text-active: #ad393a;\n  --color-warning-bg: #2b2111;\n  --color-warning-bg-hover: #443111;\n  --color-warning-border: #594214;\n  --color-warning-border-hover: #7c5914;\n  --color-warning-hover: #7c5914;\n  --color-warning-active: #aa7714;\n  --color-warning-text-hover: #e8b339;\n  --color-warning-text: #d89614;\n  --color-warning-text-active: #aa7714;\n  --color-info-bg: #111a2c;\n  --color-info-bg-hover: #112545;\n  --color-info-border: #15325b;\n  --color-info-border-hover: #15417e;\n  --color-info-hover: #15417e;\n  --color-info-active: #1554ad;\n  --color-info-text-hover: #3c89e8;\n  --color-info-text: #1668dc;\n  --color-info-text-active: #1554ad;\n  --color-bg-mask: rgba(0, 0, 0, 0.45);\n  --color-white: #fff;\n  --font-size-s-m: 10;\n  --font-size-l-g: 14;\n  --font-size-x-l: 16;\n  --font-size-heading1: 32;\n  --font-size-heading2: 26;\n  --font-size-heading3: 20;\n  --font-size-heading4: 16;\n  --font-size-heading5: 14;\n  --line-height: 1.6666666666666667;\n  --line-height-l-g: 1.5714285714285714;\n  --line-height-s-m: 1.8;\n  --line-height-heading1: 1.25;\n  --line-height-heading2: 1.3076923076923077;\n  --line-height-heading3: 1.4;\n  --line-height-heading4: 1.5;\n  --line-height-heading5: 1.5714285714285714;\n  --size-x-x-l: 48;\n  --size-x-l: 32;\n  --size-l-g: 16;\n  --size-m-d: 16;\n  --size-m-s: 12;\n  --size: 8;\n  --size-s-m: 8;\n  --size-x-s: 4;\n  --size-x-x-s: 4;\n  --control-height-s-m: 21;\n  --control-height-x-s: 14;\n  --control-height-l-g: 35;\n  --motion-duration-fast: 0.1s;\n  --motion-duration-mid: 0.2s;\n  --motion-duration-slow: 0.3s;\n  --line-width-bold: 2;\n  --border-radius-x-s: 1;\n  --border-radius-s-m: 4;\n  --border-radius-l-g: 4;\n  --border-radius-outer: 4;\n  --color-link: #1668dc;\n  --color-link-hover: #15417e;\n  --color-link-active: #1554ad;\n  --color-fill-content: rgba(255, 255, 255, 0.12);\n  --color-fill-content-hover: rgba(255, 255, 255, 0.18);\n  --color-fill-alter: rgba(255, 255, 255, 0.04);\n  --color-bg-container-disabled: rgba(255, 255, 255, 0.08);\n  --color-border-bg: #1d1f22;\n  --color-split: rgba(208, 231, 255, 0.14);\n  --color-text-placeholder: rgba(255, 255, 255, 0.25);\n  --color-text-disabled: rgba(255, 255, 255, 0.25);\n  --color-text-heading: rgba(255, 255, 255, 0.85);\n  --color-text-label: rgba(255, 255, 255, 0.65);\n  --color-text-description: rgba(255, 255, 255, 0.45);\n  --color-text-light-solid: #fff;\n  --color-highlight: #dc4446;\n  --color-bg-text-hover: rgba(255, 255, 255, 0.12);\n  --color-bg-text-active: rgba(255, 255, 255, 0.18);\n  --color-icon: rgba(255, 255, 255, 0.45);\n  --color-icon-hover: rgba(255, 255, 255, 0.85);\n  --color-error-outline: rgba(81, 0, 0, 0.29);\n  --color-warning-outline: rgba(57, 35, 0, 0.5);\n  --font-size-icon: 10;\n  --line-width-focus: 4;\n  --control-outline-width: 2;\n  --control-interactive-size: 14;\n  --control-item-bg-hover: rgba(255, 255, 255, 0.08);\n  --control-item-bg-active: #111a2c;\n  --control-item-bg-active-hover: #112545;\n  --control-item-bg-active-disabled: rgba(255, 255, 255, 0.18);\n  --control-tmp-outline: rgba(255, 255, 255, 0.04);\n  --control-outline: rgba(0, 19, 58, 0.41);\n  --font-weight-strong: 600;\n  --opacity-loading: 0.65;\n  --link-decoration: none;\n  --link-hover-decoration: none;\n  --link-focus-decoration: none;\n  --control-padding-horizontal: 12;\n  --control-padding-horizontal-s-m: 8;\n  --padding-x-x-s: 4;\n  --padding-x-s: 4;\n  --padding-s-m: 8;\n  --padding: 8;\n  --padding-m-d: 16;\n  --padding-l-g: 16;\n  --padding-x-l: 32;\n  --padding-content-horizontal-l-g: 16;\n  --padding-content-vertical-l-g: 12;\n  --padding-content-horizontal: 12;\n  --padding-content-vertical: 8;\n  --padding-content-horizontal-s-m: 8;\n  --padding-content-vertical-s-m: 4;\n  --margin-x-x-s: 4;\n  --margin-x-s: 4;\n  --margin-s-m: 8;\n  --margin: 8;\n  --margin-m-d: 16;\n  --margin-l-g: 16;\n  --margin-x-l: 32;\n  --margin-x-x-l: 48;\n  --box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);\n  --box-shadow-secondary: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12),\n    0 9px 28px 8px rgba(0, 0, 0, 0.05);\n  --box-shadow-tertiary: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02),\n    0 2px 4px 0 rgba(0, 0, 0, 0.02);\n  --screen-x-s: 480;\n  --screen-x-s-min: 480;\n  --screen-x-s-max: 575;\n  --screen-s-m: 576;\n  --screen-s-m-min: 576;\n  --screen-s-m-max: 767;\n  --screen-m-d: 768;\n  --screen-m-d-min: 768;\n  --screen-m-d-max: 991;\n  --screen-l-g: 992;\n  --screen-l-g-min: 992;\n  --screen-l-g-max: 1199;\n  --screen-x-l: 1200;\n  --screen-x-l-min: 1200;\n  --screen-x-l-max: 1599;\n  --screen-x-x-l: 1600;\n  --screen-x-x-l-min: 1600;\n  --box-shadow-popover-arrow: 2px 2px 5px rgba(0, 0, 0, 0.05);\n  --box-shadow-card: 0 1px 2px -2px rgba(0, 0, 0, 0.16), 0 3px 6px 0 rgba(0, 0, 0, 0.12),\n    0 5px 12px 4px rgba(0, 0, 0, 0.09);\n  --box-shadow-drawer-right: -6px 0 16px 0 rgba(0, 0, 0, 0.08), -3px 0 6px -4px rgba(0, 0, 0, 0.12),\n    -9px 0 28px 8px rgba(0, 0, 0, 0.05);\n  --box-shadow-drawer-left: 6px 0 16px 0 rgba(0, 0, 0, 0.08), 3px 0 6px -4px rgba(0, 0, 0, 0.12),\n    9px 0 28px 8px rgba(0, 0, 0, 0.05);\n  --box-shadow-drawer-up: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12),\n    0 9px 28px 8px rgba(0, 0, 0, 0.05);\n  --box-shadow-drawer-down: 0 -6px 16px 0 rgba(0, 0, 0, 0.08), 0 -3px 6px -4px rgba(0, 0, 0, 0.12),\n    0 -9px 28px 8px rgba(0, 0, 0, 0.05);\n  --box-shadow-tabs-overflow-left: inset 10px 0 8px -8px rgba(0, 0, 0, 0.08);\n  --box-shadow-tabs-overflow-right: inset -10px 0 8px -8px rgba(0, 0, 0, 0.08);\n  --box-shadow-tabs-overflow-top: inset 0 10px 8px -8px rgba(0, 0, 0, 0.08);\n  --box-shadow-tabs-overflow-bottom: inset 0 -10px 8px -8px rgba(0, 0, 0, 0.08);\n  --_token-key: nhv2lr;\n  --_hash-id: css-dev-only-do-not-override-aany79;\n}\n"
  },
  {
    "path": "chat2db-client/src/theme/abandon/demo/light.less",
    "content": "{\n  \"blue\": \"#1677ff\",\n  \"purple\": \"#722ED1\",\n  \"cyan\": \"#13C2C2\",\n  \"green\": \"#52C41A\",\n  \"magenta\": \"#EB2F96\",\n  \"pink\": \"#eb2f96\",\n  \"red\": \"#F5222D\",\n  \"orange\": \"#FA8C16\",\n  \"yellow\": \"#FADB14\",\n  \"volcano\": \"#FA541C\",\n  \"geekblue\": \"#2F54EB\",\n  \"gold\": \"#FAAD14\",\n  \"lime\": \"#A0D911\",\n  \"colorPrimary\": \"#1677ff\",\n  \"colorSuccess\": \"#52c41a\",\n  \"colorWarning\": \"#faad14\",\n  \"colorError\": \"#ff4d4f\",\n  \"colorInfo\": \"#1677ff\",\n  \"colorTextBase\": \"#000\",\n  \"colorBgBase\": \"#fff\",\n  \"fontFamily\": \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\\n'Noto Color Emoji'\",\n  \"fontFamilyCode\": \"'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace\",\n  \"fontSize\": null,\n  \"lineWidth\": 1,\n  \"lineType\": \"solid\",\n  \"motionUnit\": 0.1,\n  \"motionBase\": 0,\n  \"motionEaseOutCirc\": \"cubic-bezier(0.08, 0.82, 0.17, 1)\",\n  \"motionEaseInOutCirc\": \"cubic-bezier(0.78, 0.14, 0.15, 0.86)\",\n  \"motionEaseOut\": \"cubic-bezier(0.215, 0.61, 0.355, 1)\",\n  \"motionEaseInOut\": \"cubic-bezier(0.645, 0.045, 0.355, 1)\",\n  \"motionEaseOutBack\": \"cubic-bezier(0.12, 0.4, 0.29, 1.46)\",\n  \"motionEaseInBack\": \"cubic-bezier(0.71, -0.46, 0.88, 0.6)\",\n  \"motionEaseInQuint\": \"cubic-bezier(0.755, 0.05, 0.855, 0.06)\",\n  \"motionEaseOutQuint\": \"cubic-bezier(0.23, 1, 0.32, 1)\",\n  \"borderRadius\": \"4px\",\n  \"sizeUnit\": 4,\n  \"sizeStep\": 4,\n  \"sizePopupArrow\": 16,\n  \"controlHeight\": 28,\n  \"zIndexBase\": 0,\n  \"zIndexPopupBase\": 1000,\n  \"opacityImage\": 1,\n  \"wireframe\": true,\n  \"motion\": true,\n  \"borderRadiusLG\": \"8px\",\n  \"colorHoverBg\": \"#eee\",\n  \"colorBgContainer\": \"#fff\",\n  \"colorBgElevated\": \"#F8F9FA\",\n  \"colorBorder\": \"#d3d3d4\",\n  \"blue-1\": \"#e6f4ff\",\n  \"blue1\": \"#e6f4ff\",\n  \"blue-2\": \"#bae0ff\",\n  \"blue2\": \"#bae0ff\",\n  \"blue-3\": \"#91caff\",\n  \"blue3\": \"#91caff\",\n  \"blue-4\": \"#69b1ff\",\n  \"blue4\": \"#69b1ff\",\n  \"blue-5\": \"#4096ff\",\n  \"blue5\": \"#4096ff\",\n  \"blue-6\": \"#1677ff\",\n  \"blue6\": \"#1677ff\",\n  \"blue-7\": \"#0958d9\",\n  \"blue7\": \"#0958d9\",\n  \"blue-8\": \"#003eb3\",\n  \"blue8\": \"#003eb3\",\n  \"blue-9\": \"#002c8c\",\n  \"blue9\": \"#002c8c\",\n  \"blue-10\": \"#001d66\",\n  \"blue10\": \"#001d66\",\n  \"purple-1\": \"#f9f0ff\",\n  \"purple1\": \"#f9f0ff\",\n  \"purple-2\": \"#efdbff\",\n  \"purple2\": \"#efdbff\",\n  \"purple-3\": \"#d3adf7\",\n  \"purple3\": \"#d3adf7\",\n  \"purple-4\": \"#b37feb\",\n  \"purple4\": \"#b37feb\",\n  \"purple-5\": \"#9254de\",\n  \"purple5\": \"#9254de\",\n  \"purple-6\": \"#722ed1\",\n  \"purple6\": \"#722ed1\",\n  \"purple-7\": \"#531dab\",\n  \"purple7\": \"#531dab\",\n  \"purple-8\": \"#391085\",\n  \"purple8\": \"#391085\",\n  \"purple-9\": \"#22075e\",\n  \"purple9\": \"#22075e\",\n  \"purple-10\": \"#120338\",\n  \"purple10\": \"#120338\",\n  \"cyan-1\": \"#e6fffb\",\n  \"cyan1\": \"#e6fffb\",\n  \"cyan-2\": \"#b5f5ec\",\n  \"cyan2\": \"#b5f5ec\",\n  \"cyan-3\": \"#87e8de\",\n  \"cyan3\": \"#87e8de\",\n  \"cyan-4\": \"#5cdbd3\",\n  \"cyan4\": \"#5cdbd3\",\n  \"cyan-5\": \"#36cfc9\",\n  \"cyan5\": \"#36cfc9\",\n  \"cyan-6\": \"#13c2c2\",\n  \"cyan6\": \"#13c2c2\",\n  \"cyan-7\": \"#08979c\",\n  \"cyan7\": \"#08979c\",\n  \"cyan-8\": \"#006d75\",\n  \"cyan8\": \"#006d75\",\n  \"cyan-9\": \"#00474f\",\n  \"cyan9\": \"#00474f\",\n  \"cyan-10\": \"#002329\",\n  \"cyan10\": \"#002329\",\n  \"green-1\": \"#f6ffed\",\n  \"green1\": \"#f6ffed\",\n  \"green-2\": \"#d9f7be\",\n  \"green2\": \"#d9f7be\",\n  \"green-3\": \"#b7eb8f\",\n  \"green3\": \"#b7eb8f\",\n  \"green-4\": \"#95de64\",\n  \"green4\": \"#95de64\",\n  \"green-5\": \"#73d13d\",\n  \"green5\": \"#73d13d\",\n  \"green-6\": \"#52c41a\",\n  \"green6\": \"#52c41a\",\n  \"green-7\": \"#389e0d\",\n  \"green7\": \"#389e0d\",\n  \"green-8\": \"#237804\",\n  \"green8\": \"#237804\",\n  \"green-9\": \"#135200\",\n  \"green9\": \"#135200\",\n  \"green-10\": \"#092b00\",\n  \"green10\": \"#092b00\",\n  \"magenta-1\": \"#fff0f6\",\n  \"magenta1\": \"#fff0f6\",\n  \"magenta-2\": \"#ffd6e7\",\n  \"magenta2\": \"#ffd6e7\",\n  \"magenta-3\": \"#ffadd2\",\n  \"magenta3\": \"#ffadd2\",\n  \"magenta-4\": \"#ff85c0\",\n  \"magenta4\": \"#ff85c0\",\n  \"magenta-5\": \"#f759ab\",\n  \"magenta5\": \"#f759ab\",\n  \"magenta-6\": \"#eb2f96\",\n  \"magenta6\": \"#eb2f96\",\n  \"magenta-7\": \"#c41d7f\",\n  \"magenta7\": \"#c41d7f\",\n  \"magenta-8\": \"#9e1068\",\n  \"magenta8\": \"#9e1068\",\n  \"magenta-9\": \"#780650\",\n  \"magenta9\": \"#780650\",\n  \"magenta-10\": \"#520339\",\n  \"magenta10\": \"#520339\",\n  \"pink-1\": \"#fff0f6\",\n  \"pink1\": \"#fff0f6\",\n  \"pink-2\": \"#ffd6e7\",\n  \"pink2\": \"#ffd6e7\",\n  \"pink-3\": \"#ffadd2\",\n  \"pink3\": \"#ffadd2\",\n  \"pink-4\": \"#ff85c0\",\n  \"pink4\": \"#ff85c0\",\n  \"pink-5\": \"#f759ab\",\n  \"pink5\": \"#f759ab\",\n  \"pink-6\": \"#eb2f96\",\n  \"pink6\": \"#eb2f96\",\n  \"pink-7\": \"#c41d7f\",\n  \"pink7\": \"#c41d7f\",\n  \"pink-8\": \"#9e1068\",\n  \"pink8\": \"#9e1068\",\n  \"pink-9\": \"#780650\",\n  \"pink9\": \"#780650\",\n  \"pink-10\": \"#520339\",\n  \"pink10\": \"#520339\",\n  \"red-1\": \"#fff1f0\",\n  \"red1\": \"#fff1f0\",\n  \"red-2\": \"#ffccc7\",\n  \"red2\": \"#ffccc7\",\n  \"red-3\": \"#ffa39e\",\n  \"red3\": \"#ffa39e\",\n  \"red-4\": \"#ff7875\",\n  \"red4\": \"#ff7875\",\n  \"red-5\": \"#ff4d4f\",\n  \"red5\": \"#ff4d4f\",\n  \"red-6\": \"#f5222d\",\n  \"red6\": \"#f5222d\",\n  \"red-7\": \"#cf1322\",\n  \"red7\": \"#cf1322\",\n  \"red-8\": \"#a8071a\",\n  \"red8\": \"#a8071a\",\n  \"red-9\": \"#820014\",\n  \"red9\": \"#820014\",\n  \"red-10\": \"#5c0011\",\n  \"red10\": \"#5c0011\",\n  \"orange-1\": \"#fff7e6\",\n  \"orange1\": \"#fff7e6\",\n  \"orange-2\": \"#ffe7ba\",\n  \"orange2\": \"#ffe7ba\",\n  \"orange-3\": \"#ffd591\",\n  \"orange3\": \"#ffd591\",\n  \"orange-4\": \"#ffc069\",\n  \"orange4\": \"#ffc069\",\n  \"orange-5\": \"#ffa940\",\n  \"orange5\": \"#ffa940\",\n  \"orange-6\": \"#fa8c16\",\n  \"orange6\": \"#fa8c16\",\n  \"orange-7\": \"#d46b08\",\n  \"orange7\": \"#d46b08\",\n  \"orange-8\": \"#ad4e00\",\n  \"orange8\": \"#ad4e00\",\n  \"orange-9\": \"#873800\",\n  \"orange9\": \"#873800\",\n  \"orange-10\": \"#612500\",\n  \"orange10\": \"#612500\",\n  \"yellow-1\": \"#feffe6\",\n  \"yellow1\": \"#feffe6\",\n  \"yellow-2\": \"#ffffb8\",\n  \"yellow2\": \"#ffffb8\",\n  \"yellow-3\": \"#fffb8f\",\n  \"yellow3\": \"#fffb8f\",\n  \"yellow-4\": \"#fff566\",\n  \"yellow4\": \"#fff566\",\n  \"yellow-5\": \"#ffec3d\",\n  \"yellow5\": \"#ffec3d\",\n  \"yellow-6\": \"#fadb14\",\n  \"yellow6\": \"#fadb14\",\n  \"yellow-7\": \"#d4b106\",\n  \"yellow7\": \"#d4b106\",\n  \"yellow-8\": \"#ad8b00\",\n  \"yellow8\": \"#ad8b00\",\n  \"yellow-9\": \"#876800\",\n  \"yellow9\": \"#876800\",\n  \"yellow-10\": \"#614700\",\n  \"yellow10\": \"#614700\",\n  \"volcano-1\": \"#fff2e8\",\n  \"volcano1\": \"#fff2e8\",\n  \"volcano-2\": \"#ffd8bf\",\n  \"volcano2\": \"#ffd8bf\",\n  \"volcano-3\": \"#ffbb96\",\n  \"volcano3\": \"#ffbb96\",\n  \"volcano-4\": \"#ff9c6e\",\n  \"volcano4\": \"#ff9c6e\",\n  \"volcano-5\": \"#ff7a45\",\n  \"volcano5\": \"#ff7a45\",\n  \"volcano-6\": \"#fa541c\",\n  \"volcano6\": \"#fa541c\",\n  \"volcano-7\": \"#d4380d\",\n  \"volcano7\": \"#d4380d\",\n  \"volcano-8\": \"#ad2102\",\n  \"volcano8\": \"#ad2102\",\n  \"volcano-9\": \"#871400\",\n  \"volcano9\": \"#871400\",\n  \"volcano-10\": \"#610b00\",\n  \"volcano10\": \"#610b00\",\n  \"geekblue-1\": \"#f0f5ff\",\n  \"geekblue1\": \"#f0f5ff\",\n  \"geekblue-2\": \"#d6e4ff\",\n  \"geekblue2\": \"#d6e4ff\",\n  \"geekblue-3\": \"#adc6ff\",\n  \"geekblue3\": \"#adc6ff\",\n  \"geekblue-4\": \"#85a5ff\",\n  \"geekblue4\": \"#85a5ff\",\n  \"geekblue-5\": \"#597ef7\",\n  \"geekblue5\": \"#597ef7\",\n  \"geekblue-6\": \"#2f54eb\",\n  \"geekblue6\": \"#2f54eb\",\n  \"geekblue-7\": \"#1d39c4\",\n  \"geekblue7\": \"#1d39c4\",\n  \"geekblue-8\": \"#10239e\",\n  \"geekblue8\": \"#10239e\",\n  \"geekblue-9\": \"#061178\",\n  \"geekblue9\": \"#061178\",\n  \"geekblue-10\": \"#030852\",\n  \"geekblue10\": \"#030852\",\n  \"gold-1\": \"#fffbe6\",\n  \"gold1\": \"#fffbe6\",\n  \"gold-2\": \"#fff1b8\",\n  \"gold2\": \"#fff1b8\",\n  \"gold-3\": \"#ffe58f\",\n  \"gold3\": \"#ffe58f\",\n  \"gold-4\": \"#ffd666\",\n  \"gold4\": \"#ffd666\",\n  \"gold-5\": \"#ffc53d\",\n  \"gold5\": \"#ffc53d\",\n  \"gold-6\": \"#faad14\",\n  \"gold6\": \"#faad14\",\n  \"gold-7\": \"#d48806\",\n  \"gold7\": \"#d48806\",\n  \"gold-8\": \"#ad6800\",\n  \"gold8\": \"#ad6800\",\n  \"gold-9\": \"#874d00\",\n  \"gold9\": \"#874d00\",\n  \"gold-10\": \"#613400\",\n  \"gold10\": \"#613400\",\n  \"lime-1\": \"#fcffe6\",\n  \"lime1\": \"#fcffe6\",\n  \"lime-2\": \"#f4ffb8\",\n  \"lime2\": \"#f4ffb8\",\n  \"lime-3\": \"#eaff8f\",\n  \"lime3\": \"#eaff8f\",\n  \"lime-4\": \"#d3f261\",\n  \"lime4\": \"#d3f261\",\n  \"lime-5\": \"#bae637\",\n  \"lime5\": \"#bae637\",\n  \"lime-6\": \"#a0d911\",\n  \"lime6\": \"#a0d911\",\n  \"lime-7\": \"#7cb305\",\n  \"lime7\": \"#7cb305\",\n  \"lime-8\": \"#5b8c00\",\n  \"lime8\": \"#5b8c00\",\n  \"lime-9\": \"#3f6600\",\n  \"lime9\": \"#3f6600\",\n  \"lime-10\": \"#254000\",\n  \"lime10\": \"#254000\",\n  \"colorText\": \"rgba(0, 0, 0, 0.88)\",\n  \"colorTextSecondary\": \"rgba(0, 0, 0, 0.65)\",\n  \"colorTextTertiary\": \"rgba(0, 0, 0, 0.45)\",\n  \"colorTextQuaternary\": \"rgba(0, 0, 0, 0.25)\",\n  \"colorFill\": \"rgba(0, 0, 0, 0.15)\",\n  \"colorFillSecondary\": \"rgba(0, 0, 0, 0.06)\",\n  \"colorFillTertiary\": \"rgba(0, 0, 0, 0.04)\",\n  \"colorFillQuaternary\": \"rgba(0, 0, 0, 0.02)\",\n  \"colorBgLayout\": \"#f5f5f5\",\n  \"colorBgSpotlight\": \"rgba(0, 0, 0, 0.85)\",\n  \"colorBorderSecondary\": \"#f0f0f0\",\n  \"colorPrimaryBg\": \"#e6f4ff\",\n  \"colorPrimaryBgHover\": \"#bae0ff\",\n  \"colorPrimaryBorder\": \"#91caff\",\n  \"colorPrimaryBorderHover\": \"#69b1ff\",\n  \"colorPrimaryHover\": \"#4096ff\",\n  \"colorPrimaryActive\": \"#0958d9\",\n  \"colorPrimaryTextHover\": \"#4096ff\",\n  \"colorPrimaryText\": \"#1677ff\",\n  \"colorPrimaryTextActive\": \"#0958d9\",\n  \"colorSuccessBg\": \"#f6ffed\",\n  \"colorSuccessBgHover\": \"#d9f7be\",\n  \"colorSuccessBorder\": \"#b7eb8f\",\n  \"colorSuccessBorderHover\": \"#95de64\",\n  \"colorSuccessHover\": \"#95de64\",\n  \"colorSuccessActive\": \"#389e0d\",\n  \"colorSuccessTextHover\": \"#73d13d\",\n  \"colorSuccessText\": \"#52c41a\",\n  \"colorSuccessTextActive\": \"#389e0d\",\n  \"colorErrorBg\": \"#fff2f0\",\n  \"colorErrorBgHover\": \"#fff1f0\",\n  \"colorErrorBorder\": \"#ffccc7\",\n  \"colorErrorBorderHover\": \"#ffa39e\",\n  \"colorErrorHover\": \"#ff7875\",\n  \"colorErrorActive\": \"#d9363e\",\n  \"colorErrorTextHover\": \"#ff7875\",\n  \"colorErrorText\": \"#ff4d4f\",\n  \"colorErrorTextActive\": \"#d9363e\",\n  \"colorWarningBg\": \"#fffbe6\",\n  \"colorWarningBgHover\": \"#fff1b8\",\n  \"colorWarningBorder\": \"#ffe58f\",\n  \"colorWarningBorderHover\": \"#ffd666\",\n  \"colorWarningHover\": \"#ffd666\",\n  \"colorWarningActive\": \"#d48806\",\n  \"colorWarningTextHover\": \"#ffc53d\",\n  \"colorWarningText\": \"#faad14\",\n  \"colorWarningTextActive\": \"#d48806\",\n  \"colorInfoBg\": \"#e6f4ff\",\n  \"colorInfoBgHover\": \"#bae0ff\",\n  \"colorInfoBorder\": \"#91caff\",\n  \"colorInfoBorderHover\": \"#69b1ff\",\n  \"colorInfoHover\": \"#69b1ff\",\n  \"colorInfoActive\": \"#0958d9\",\n  \"colorInfoTextHover\": \"#4096ff\",\n  \"colorInfoText\": \"#1677ff\",\n  \"colorInfoTextActive\": \"#0958d9\",\n  \"colorBgMask\": \"rgba(0, 0, 0, 0.45)\",\n  \"colorWhite\": \"#fff\",\n  \"fontSizeSM\": null,\n  \"fontSizeLG\": null,\n  \"fontSizeXL\": null,\n  \"fontSizeHeading1\": null,\n  \"fontSizeHeading2\": null,\n  \"fontSizeHeading3\": null,\n  \"fontSizeHeading4\": null,\n  \"fontSizeHeading5\": null,\n  \"lineHeight\": null,\n  \"lineHeightLG\": null,\n  \"lineHeightSM\": null,\n  \"lineHeightHeading1\": null,\n  \"lineHeightHeading2\": null,\n  \"lineHeightHeading3\": null,\n  \"lineHeightHeading4\": null,\n  \"lineHeightHeading5\": null,\n  \"sizeXXL\": 48,\n  \"sizeXL\": 32,\n  \"sizeLG\": 16,\n  \"sizeMD\": 16,\n  \"sizeMS\": 12,\n  \"size\": 8,\n  \"sizeSM\": 8,\n  \"sizeXS\": 4,\n  \"sizeXXS\": 4,\n  \"controlHeightSM\": 21,\n  \"controlHeightXS\": 14,\n  \"controlHeightLG\": 35,\n  \"motionDurationFast\": \"0.1s\",\n  \"motionDurationMid\": \"0.2s\",\n  \"motionDurationSlow\": \"0.3s\",\n  \"lineWidthBold\": 2,\n  \"borderRadiusXS\": \"4px\",\n  \"borderRadiusSM\": \"4px\",\n  \"borderRadiusOuter\": \"4px\",\n  \"colorLink\": \"#1677ff\",\n  \"colorLinkHover\": \"#69b1ff\",\n  \"colorLinkActive\": \"#0958d9\",\n  \"colorFillContent\": \"rgba(0, 0, 0, 0.06)\",\n  \"colorFillContentHover\": \"rgba(0, 0, 0, 0.15)\",\n  \"colorFillAlter\": \"rgba(0, 0, 0, 0.02)\",\n  \"colorBgContainerDisabled\": \"rgba(0, 0, 0, 0.04)\",\n  \"colorBorderBg\": \"#fff\",\n  \"colorSplit\": \"rgba(5, 5, 5, 0.06)\",\n  \"colorTextPlaceholder\": \"rgba(0, 0, 0, 0.25)\",\n  \"colorTextDisabled\": \"rgba(0, 0, 0, 0.25)\",\n  \"colorTextHeading\": \"rgba(0, 0, 0, 0.88)\",\n  \"colorTextLabel\": \"rgba(0, 0, 0, 0.65)\",\n  \"colorTextDescription\": \"rgba(0, 0, 0, 0.45)\",\n  \"colorTextLightSolid\": \"#fff\",\n  \"colorHighlight\": \"#ff4d4f\",\n  \"colorBgTextHover\": \"rgba(0, 0, 0, 0.06)\",\n  \"colorBgTextActive\": \"rgba(0, 0, 0, 0.15)\",\n  \"colorIcon\": \"rgba(0, 0, 0, 0.45)\",\n  \"colorIconHover\": \"rgba(0, 0, 0, 0.88)\",\n  \"colorErrorOutline\": \"rgba(255, 38, 5, 0.06)\",\n  \"colorWarningOutline\": \"rgba(255, 215, 5, 0.1)\",\n  \"fontSizeIcon\": null,\n  \"lineWidthFocus\": 4,\n  \"controlOutlineWidth\": 2,\n  \"controlInteractiveSize\": 14,\n  \"controlItemBgHover\": \"rgba(0, 0, 0, 0.04)\",\n  \"controlItemBgActive\": \"#e6f4ff\",\n  \"controlItemBgActiveHover\": \"#bae0ff\",\n  \"controlItemBgActiveDisabled\": \"rgba(0, 0, 0, 0.15)\",\n  \"controlTmpOutline\": \"rgba(0, 0, 0, 0.02)\",\n  \"controlOutline\": \"rgba(5, 145, 255, 0.1)\",\n  \"fontWeightStrong\": 600,\n  \"opacityLoading\": 0.65,\n  \"linkDecoration\": \"none\",\n  \"linkHoverDecoration\": \"none\",\n  \"linkFocusDecoration\": \"none\",\n  \"controlPaddingHorizontal\": 12,\n  \"controlPaddingHorizontalSM\": 8,\n  \"paddingXXS\": 4,\n  \"paddingXS\": 4,\n  \"paddingSM\": 8,\n  \"padding\": 8,\n  \"paddingMD\": 16,\n  \"paddingLG\": 16,\n  \"paddingXL\": 32,\n  \"paddingContentHorizontalLG\": 16,\n  \"paddingContentVerticalLG\": 12,\n  \"paddingContentHorizontal\": 12,\n  \"paddingContentVertical\": 8,\n  \"paddingContentHorizontalSM\": 8,\n  \"paddingContentVerticalSM\": 4,\n  \"marginXXS\": 4,\n  \"marginXS\": 4,\n  \"marginSM\": 8,\n  \"margin\": 8,\n  \"marginMD\": 16,\n  \"marginLG\": 16,\n  \"marginXL\": 32,\n  \"marginXXL\": 48,\n  \"boxShadow\": \"\\n      0 6px 16px 0 rgba(0, 0, 0, 0.08),\\n      0 3px 6px -4px rgba(0, 0, 0, 0.12),\\n      0 9px 28px 8px rgba(0, 0, 0, 0.05)\\n    \",\n  \"boxShadowSecondary\": \"\\n      0 6px 16px 0 rgba(0, 0, 0, 0.08),\\n      0 3px 6px -4px rgba(0, 0, 0, 0.12),\\n      0 9px 28px 8px rgba(0, 0, 0, 0.05)\\n    \",\n  \"boxShadowTertiary\": \"\\n      0 1px 2px 0 rgba(0, 0, 0, 0.03),\\n      0 1px 6px -1px rgba(0, 0, 0, 0.02),\\n      0 2px 4px 0 rgba(0, 0, 0, 0.02)\\n    \",\n  \"screenXS\": 480,\n  \"screenXSMin\": 480,\n  \"screenXSMax\": 575,\n  \"screenSM\": 576,\n  \"screenSMMin\": 576,\n  \"screenSMMax\": 767,\n  \"screenMD\": 768,\n  \"screenMDMin\": 768,\n  \"screenMDMax\": 991,\n  \"screenLG\": 992,\n  \"screenLGMin\": 992,\n  \"screenLGMax\": 1199,\n  \"screenXL\": 1200,\n  \"screenXLMin\": 1200,\n  \"screenXLMax\": 1599,\n  \"screenXXL\": 1600,\n  \"screenXXLMin\": 1600,\n  \"boxShadowPopoverArrow\": \"2px 2px 5px rgba(0, 0, 0, 0.05)\",\n  \"boxShadowCard\": \"\\n      0 1px 2px -2px rgba(0, 0, 0, 0.16),\\n      0 3px 6px 0 rgba(0, 0, 0, 0.12),\\n      0 5px 12px 4px rgba(0, 0, 0, 0.09)\\n    \",\n  \"boxShadowDrawerRight\": \"\\n      -6px 0 16px 0 rgba(0, 0, 0, 0.08),\\n      -3px 0 6px -4px rgba(0, 0, 0, 0.12),\\n      -9px 0 28px 8px rgba(0, 0, 0, 0.05)\\n    \",\n  \"boxShadowDrawerLeft\": \"\\n      6px 0 16px 0 rgba(0, 0, 0, 0.08),\\n      3px 0 6px -4px rgba(0, 0, 0, 0.12),\\n      9px 0 28px 8px rgba(0, 0, 0, 0.05)\\n    \",\n  \"boxShadowDrawerUp\": \"\\n      0 6px 16px 0 rgba(0, 0, 0, 0.08),\\n      0 3px 6px -4px rgba(0, 0, 0, 0.12),\\n      0 9px 28px 8px rgba(0, 0, 0, 0.05)\\n    \",\n  \"boxShadowDrawerDown\": \"\\n      0 -6px 16px 0 rgba(0, 0, 0, 0.08),\\n      0 -3px 6px -4px rgba(0, 0, 0, 0.12),\\n      0 -9px 28px 8px rgba(0, 0, 0, 0.05)\\n    \",\n  \"boxShadowTabsOverflowLeft\": \"inset 10px 0 8px -8px rgba(0, 0, 0, 0.08)\",\n  \"boxShadowTabsOverflowRight\": \"inset -10px 0 8px -8px rgba(0, 0, 0, 0.08)\",\n  \"boxShadowTabsOverflowTop\": \"inset 0 10px 8px -8px rgba(0, 0, 0, 0.08)\",\n  \"boxShadowTabsOverflowBottom\": \"inset 0 -10px 8px -8px rgba(0, 0, 0, 0.08)\",\n  \"_tokenKey\": \"1xy3sb6\",\n  \"_hashId\": \"css-dev-only-do-not-override-mqdxtd\"\n}"
  },
  {
    "path": "chat2db-client/src/theme/abandon/primaryColor.less",
    "content": "html[primary-color='polar-blue'] {\n  //品牌色\n  --color-primary: #1677ff; // 品牌主色\n  --color-primary-bg: #e6f4ff; // 选中背景色，如下拉框选中的颜色\n  --color-primary-hover: rgba(27, 28, 33, 0.18); // 主色悬浮色\n  --color-primary-active: #0958d9; // 主色激活态\n}\n\nhtml[primary-color='polar-green'] {\n  //品牌色\n  --color-primary: #3c8618; // 品牌主色\n  --color-primary-bg: #e6f4ff; // 选中背景色，如下拉框选中的颜色\n  --color-primary-hover: rgba(27, 28, 33, 0.18); // 主色悬浮色\n  --color-primary-active: #26610d; // 主色激活态\n}\n\nhtml[primary-color='golden-purple'] {\n  //品牌色\n  --color-primary: #51258f; // 品牌主色\n  --color-primary-bg: #e6f4ff; // 选中背景色，如下拉框选中的颜色\n  --color-primary-hover: rgba(27, 28, 33, 0.18); // 主色悬浮色\n  --color-primary-active: #361669; // 主色激活态\n}\n"
  },
  {
    "path": "chat2db-client/src/theme/background/dark.ts",
    "content": "import { theme } from 'antd';\nimport { PrimaryColorType } from '@/constants';\nimport { commonToken } from '../common';\n\ntype IAntdPrimaryColor = {\n  [key in PrimaryColorType]: any;\n};\n\n// 主题色\nconst antdPrimaryColor: IAntdPrimaryColor = {\n  [PrimaryColorType.Polar_Green]: {\n    colorPrimary: '#3c8618',\n  },\n  [PrimaryColorType.Golden_Purple]: {\n    colorPrimary: '#7688c9',\n  },\n  [PrimaryColorType.Polar_Blue]: {\n    colorPrimary: '#1677ff',\n  },\n  [PrimaryColorType.Silver]: {\n    colorPrimary: '#c3b7a4',\n  },\n  [PrimaryColorType.Red]: {\n    colorPrimary: '#fd6874',\n  },\n  [PrimaryColorType.Orange]: {\n    colorPrimary: '#ffa940',\n  },\n  [PrimaryColorType.Blue2]: {\n    colorPrimary: '#009cc7',\n  },\n  [PrimaryColorType.Gold]: {\n    colorPrimary: '#b59a6d',\n  },\n};\n\nconst antDarkTheme = {\n  algorithm: [theme.darkAlgorithm, theme.compactAlgorithm],\n  customName: 'dark',\n  antdPrimaryColor,\n  token: {\n    ...commonToken,\n    colorTextBase: '#f1f1f4',\n    colorBgBase: '#0a0b0c',\n    colorHoverBg: 'hsla(0, 0%, 100%, 0.03)',\n    colorBgContainer: '#0a0b0c',\n    colorBgSubtle: '#131418',\n    colorBgElevated: '#0a0b0c',\n    colorBorder: '#36373a66',\n    colorBorderSecondary: '#36373a66',\n  },\n};\n\nexport default antDarkTheme;\n"
  },
  {
    "path": "chat2db-client/src/theme/background/darkDimmed.ts",
    "content": "import { theme } from 'antd';\nimport { PrimaryColorType } from '@/constants';\nimport { commonToken } from '../common';\n\ntype IAntdPrimaryColor = {\n  [key in PrimaryColorType]: any;\n};\n\n// 主题色\nconst antdPrimaryColor: IAntdPrimaryColor = {\n  [PrimaryColorType.Polar_Green]: {\n    colorPrimary: '#3c8618',\n  },\n  [PrimaryColorType.Golden_Purple]: {\n    colorPrimary: '#8276c9',\n  },\n  [PrimaryColorType.Polar_Blue]: {\n    colorPrimary: '#1677ff',\n  },\n  [PrimaryColorType.Silver]: {\n    colorPrimary: '#c3b7a4',\n  },\n  [PrimaryColorType.Red]: {\n    colorPrimary: '#fd6874',\n  },\n  [PrimaryColorType.Orange]: {\n    colorPrimary: '#ffa940',\n  },\n  [PrimaryColorType.Blue2]: {\n    colorPrimary: '#009cc7',\n  },\n  [PrimaryColorType.Gold]: {\n    colorPrimary: '#b59a6d',\n  },\n};\n\nconst antdLightTheme = {\n  algorithm: [theme.darkAlgorithm, theme.compactAlgorithm],\n  customName: 'dark-dimmed',\n  antdPrimaryColor,\n  token: {\n    ...commonToken,\n    colorTextBase: '#f1f1f4',\n    colorBgBase: '#1c2128',\n    colorHoverBg: 'hsla(0, 0%, 100%, 0.03)',\n    colorBgContainer: '#1c2128',\n    colorBgSubtle: '#22272e',\n    colorBgElevated: '#1c2128',\n    colorBorder: '#373e4766',\n    colorBorderSecondary: '#373e4766',\n    controlItemBgActive: '#f1f1f414',\n  },\n};\n\nexport default antdLightTheme;\n"
  },
  {
    "path": "chat2db-client/src/theme/background/light.ts",
    "content": "import { theme } from 'antd';\nimport { PrimaryColorType } from '@/constants';\nimport { commonToken } from '../common';\n\ntype IAntdPrimaryColor = {\n  [key in PrimaryColorType]: any;\n};\n\n// 主题色\nconst antdPrimaryColor: IAntdPrimaryColor = {\n  [PrimaryColorType.Polar_Green]: {\n    colorPrimary: '#039e74',\n  },\n  [PrimaryColorType.Golden_Purple]: {\n    colorPrimary: '#9373ee',\n  },\n  [PrimaryColorType.Polar_Blue]: {\n    colorPrimary: '#587df1',\n  },\n  [PrimaryColorType.Silver]: {\n    colorPrimary: '#8e8374',\n  },\n  [PrimaryColorType.Red]: {\n    colorPrimary: '#fd6874',\n  },\n  [PrimaryColorType.Orange]: {\n    colorPrimary: '#fa8c16',\n  },\n  [PrimaryColorType.Blue2]: {\n    colorPrimary: '#00c3ee',\n  },\n  [PrimaryColorType.Gold]: {\n    colorPrimary: '#9a7d56',\n  },\n};\n\nconst antdLightTheme = {\n  algorithm: [theme.defaultAlgorithm, theme.compactAlgorithm],\n  customName: 'light',\n  antdPrimaryColor,\n  token: {\n    ...commonToken,\n    // colorText: \"#232429\",\n    colorTextBase: '#232429',\n    colorBgBase: '#ffffff',\n    colorHoverBg: 'rgba(0, 0, 0, 0.03)',\n    colorBgContainer: '#ffffff',\n    colorBgSubtle: '#f6f8fa',\n    colorBgElevated: '#ffffff',\n    colorBorder: 'rgba(211, 211, 212, 0.4)',\n    colorBorderSecondary: 'rgba(211, 211, 212, 0.4)',\n  },\n};\n\nexport default antdLightTheme;\n"
  },
  {
    "path": "chat2db-client/src/theme/common.ts",
    "content": "export const commonToken = {\n  fontSize1: 14,\n  wireframe: true,\n  borderRadius: 4,\n  borderRadiusLG: 8,\n};\n"
  },
  {
    "path": "chat2db-client/src/theme/custom/dark.less",
    "content": ":root [theme='dark'] {\n  --custom-color-icon: #5C5D5E;\n}\n"
  },
  {
    "path": "chat2db-client/src/theme/custom/darkDimmed.less",
    "content": ":root [theme='darkDimmed'] {\n  --custom-color-icon: #5C5D5E;\n}\n"
  },
  {
    "path": "chat2db-client/src/theme/custom/light.less",
    "content": "html[theme='light'] {\n  --custom-color-icon: #383d4b;\n}\n"
  },
  {
    "path": "chat2db-client/src/theme/index.ts",
    "content": "import antdDarkTheme from './background/dark';\nimport antdDarkDimmedTheme from './background/darkDimmed';\nimport antdLightTheme from './background/light';\nimport { ThemeType, PrimaryColorType } from '@/constants';\nimport { ITheme } from '@/typings/theme';\nimport lodash from 'lodash';\nimport { theme } from 'antd';\n\nconst antdThemeConfigs = {\n  [ThemeType.Dark]: antdDarkTheme,\n  [ThemeType.Light]: antdLightTheme,\n  [ThemeType.DarkDimmed]: antdDarkDimmedTheme,\n};\n\nexport function getAntdThemeConfig(_theme: ITheme) {\n  const antdThemeConfig = lodash.cloneDeep(antdThemeConfigs[_theme.backgroundColor]);\n  antdThemeConfig.token = {\n    ...antdThemeConfig.token,\n    ...(antdThemeConfig.antdPrimaryColor[_theme.primaryColor as PrimaryColorType] || {}),\n  };\n\n  const token = theme.getDesignToken(antdThemeConfig);\n  injectThemeVar(token as any, _theme.backgroundColor, _theme.primaryColor);\n  return antdThemeConfig;\n}\n\n// TODO: 只插入一次\nexport function injectThemeVar(token: { [key in string]: string }, _theme: ThemeType, primaryColor: PrimaryColorType) {\n  let css = '';\n  Object.keys(token).map((t) => {\n    const attributeName = camelToDash(t);\n    let value = token[t];\n    // 将需要px的数字带上px\n    const joinPxArr = ['fontSize', 'borderRadius', 'borderRadiusLG'];\n    if (joinPxArr.includes(t)) {\n      value = value + 'px';\n    }\n    css = css + `--${attributeName}: ${value};\\n`;\n  });\n\n  const container = `html[theme='${_theme}'],html[primary-color='${primaryColor}']{\n    ${css}\n  }`;\n\n  const style = document.createElement('style'); // 创建style标签\n  style.type = 'text/css';\n  style.appendChild(document.createTextNode(container));\n\n  document.head.appendChild(style); // 将style标签插入到head标签中\n  window._AppThemePack = token;\n}\n\nfunction camelToDash(str: string) {\n  return str.replace(/([A-Z])/g, '-$1').toLowerCase();\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/ai.ts",
    "content": "export enum AIType {\n  CHAT2DBAI = 'CHAT2DBAI',\n  ZHIPUAI = 'ZHIPUAI',\n  BAICHUANAI='BAICHUANAI',\n  WENXINAI='WENXINAI',\n  TONGYIQIANWENAI='TONGYIQIANWENAI',\n  OPENAI = 'OPENAI',\n  AZUREAI = 'AZUREAI',\n  RESTAI = 'RESTAI',\n}\n\nexport interface IRemainingUse {\n  key: string;\n  wechatMpUrl: string;\n  expiry: number;\n  remainingUses: number;\n}\n\nexport interface ILoginAndQrCode {\n  token: string;\n  wechatQrCodeUrl: string;\n  apiKey: string;\n  tip: string;\n}\n\nexport interface IInviteQrCode {\n  wechatQrCodeUrl: string;\n  tip: string;\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/common.ts",
    "content": "export type NonNullable<T> = T extends null | undefined ? never : T;\n\nexport interface IPageResponse<T> {\n  data: T[];\n  pageNo: number;\n  pageSize: number;\n  total: number;\n  hasNextPage?: boolean;\n}\n\nexport interface IPageParams {\n  searchKey?: string;\n  pageNo: number;\n  pageSize: number;\n  refresh?: boolean;\n}\n\nexport interface IPagingData {\n  hasNextPage?: boolean;\n  pageNo: number;\n  pageSize: number;\n  total: number;\n}\n\nexport interface Option {\n  value: number | string;\n  label: string;\n  isLeaf?: boolean;\n  children?: Option[];\n}\n\n\nexport interface IUniversalTableParams {\n  dataSourceId: string;\n  databaseName?: string;\n  schemaName?: string;\n  tableName?: string;\n}\n\n/**\n * 版本返回\n * VersionResponse\n */\nexport interface IVersionResponse {\n  /**\n   * 基础链接\n   * 类似于：http://test.sqlgpt.cn/gateway\n   */\n  baseUrl?: string;\n  /**\n   * 下载链接\n   */\n  downloadLink?: string;\n  /**\n   * 版本\n   */\n  version?: string;\n  /**\n   * 微信公众号名字\n   */\n  wechatMpName?: string;\n}\n\n\n"
  },
  {
    "path": "chat2db-client/src/typings/connection.ts",
    "content": "import { DatabaseTypeCode } from '@/constants';\n\n// 连接 高级配置列表的信息\nexport interface IConnectionExtendInfoItem {\n  key: string;\n  value: string;\n}\n\n// 连接的环境信息\nexport interface IConnectionEnv {\n  id: number;\n  name: string;\n  shortName: string;\n  color: string;\n}\n\n// 连接列表的信息\nexport interface IConnectionListItem {\n  id: number;\n  alias: string;\n  environment: IConnectionEnv;\n  type: DatabaseTypeCode;\n  supportDatabase: boolean;\n  supportSchema: boolean;\n  user: string;\n}\n\n\nexport interface IConnectionDetails {\n  id: number;\n  alias: string;\n  environment: IConnectionEnv;\n  type: DatabaseTypeCode;\n\n  isAdmin: boolean;\n  url: string;\n  user: string;\n  password: string;\n  ConsoleOpenedStatus: 'y' | 'n';\n  extendInfo: IConnectionExtendInfoItem[];\n  environmentId: number;\n  ssh: any;\n  driverConfig: {\n    jdbcDriver: string;\n    jdbcDriverClass: string;\n  };\n  [key: string]: any;\n}\n\nexport interface IConnectionListItem {\n  id: number;\n  alias: string;\n  environment: IConnectionEnv;\n  type: DatabaseTypeCode;\n  supportDatabase: boolean; \n  supportSchema: boolean;\n  user: string;\n}\n\nexport type ICreateConnectionDetails = Omit<IConnectionDetails, 'id'>\n\n"
  },
  {
    "path": "chat2db-client/src/typings/console.ts",
    "content": "import { ConsoleStatus, DatabaseTypeCode, WorkspaceTabType, ConsoleOpenedStatus } from '@/constants';\n\nexport interface ICreateConsoleParams { \n  name?: string;\n  ddl?: string;\n  dataSourceId: number;\n  dataSourceName: string;\n  databaseType: DatabaseTypeCode;\n  databaseName?: string;\n  schemaName?: string;\n  operationType?: WorkspaceTabType;\n  loadSQL?: () => Promise<string>;\n}\n\n// 控制台详情\nexport interface IConsole {\n  id: number; // consoleId\n  name: string; // 控制台名称\n  ddl: string; // 控制台内的sql\n  dataSourceId?: number; // 数据源id\n  dataSourceName?: string; // 数据源名称\n  type?: DatabaseTypeCode; // 数据库类型\n  databaseName?: string; // 数据库名称\n  schemaName?: string; // schema名称\n  status: ConsoleStatus; // 控制台状态\n  connectable: boolean; // 是否可连接\n  tabOpened?: ConsoleOpenedStatus; // 控制台tab是否打开\n  operationType: WorkspaceTabType; // 操作类型\n}\n\nexport type ICreateConsole = Omit<IConsole, 'id' | 'dataSourceName' | 'connectable'>;\n\n"
  },
  {
    "path": "chat2db-client/src/typings/dashboard.ts",
    "content": "// export type IChartType = 'Pie' | 'Column' | 'Line';\nexport enum IChartType {\n  'Pie' = 'Pie',\n  'Column' = 'Column',\n  'Line' = 'Line',\n}\n// export interface IDashboardItem {\n//   name: string;\n//   data: Array<IChartItem[]>;\n// }\n\nexport interface IDashboardItem {\n  id: number;\n  name?: string;\n  description?: string;\n  /** 保存图表布局 二维数据 number[][]  */\n  schema?: string;\n  chartIds?: number[];\n  gmtModified?: number;\n  gmtCreate?: number;\n}\n\n// export interface IChartDataItem {\n//   /** sql内容 */\n//   sqlContext: string;\n//   /** sql返回数据 */\n//   sqlData: any;\n//   /** 图表类型  */\n//   chartType: IChartType;\n//   /** 图表参数 */\n//   chartParam: any;\n// }\n\nexport interface IChartItem {\n  id?: number;\n  /** 图表名称 */\n  name?: string;\n  /** 图表描述 */\n  description?: string;\n  /** 图表参数 */\n  schema?: string;\n  /** 图表类型  */\n  chartType?: IChartType;\n  /** 数据源连接ID */\n  dataSourceId?: number;\n  /** 数据库类型 */\n  type?: string;\n  /** db名称 */\n  databaseName?: string;\n  /** schema名称 */\n  schemaName?: string;\n  /** ddl内容 */\n  ddl?: string;\n  /** 是否链接 */\n  connectable?: boolean;\n  /** sql返回数据 */\n  sqlData?: any;\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/database.ts",
    "content": "import { DatabaseTypeCode, TableDataType } from '@/constants';\n\nexport interface IDatabase {\n  name: string;\n  code: DatabaseTypeCode;\n  img: string;\n  icon: string;\n}\n\nexport interface ITableHeaderItem {\n  dataType: TableDataType;\n  name: string;\n  autoIncrement: boolean | null; // 是否自增\n  columnSize: number | null; // 字段长度\n  comment: string | null; // 字段注释\n  decimalDigits: number | null; // 小数位\n  defaultValue: string | null; // 默认值\n  nullable: boolean | null; // 是否为空\n  primaryKey: boolean | null; // 是否为主键\n}\n\nexport interface IManageResultData {\n  dataList: string[][];\n  headerList: ITableHeaderItem[];\n  description: string;\n  message: string;\n  sql: string;\n  originalSql: string;\n  success: boolean;\n  uuid?: string;\n  duration: number;\n  fuzzyTotal: string;\n  hasNextPage: boolean;\n  sqlType: 'SELECT' | 'UNKNOWN';\n  updateCount?: number; // 如果是修改的话。后端会返回修改的条数\n  canEdit?: boolean; // 返回的数据是否可以编辑\n  tableName?: string; // 如果可以编辑的话。后端会返回表名称。修改需要给后端传递表名\n}\n\n/** 查询结果 配置属性 */\nexport interface IResultConfig {\n  pageNo: number;\n  pageSize: number;\n  total: number | string;\n  hasNextPage: boolean;\n}\n\n/** 不同数据库支持的列字段类型 以及字符集 排列规则列表*/\nexport interface IDatabaseSupportField {\n  columnTypes: IColumnTypes[];\n  charsets: ICharset[];\n  collations: ICollation[];\n  indexTypes: IIndexTypes[];\n  defaultValues: IDefaultValue[];\n}\n\n/** 字段所对应的 字符集*/\nexport interface ICharset {\n  charsetName: string; // 字符集名称\n  defaultCollationName: string; // 字符集默认的排序规则\n}\n\n/** 排列规则*/\nexport interface ICollation {\n  collationName: string;\n}\n\n/** 索引的类型*/\nexport interface IIndexTypes {\n  typeName: string;\n}\n\n/** 不同数据库支持的列字段类型  以及支持调整的选项*/\nexport interface IColumnTypes {\n  typeName: string;\n  supportAutoIncrement: boolean; // 是否支持自增\n  supportCharset: boolean; // 是否支持字符集\n  supportCollation: boolean; // 是否支持排序规则\n  supportComments: boolean; // 是否支持注释\n  supportDefaultValue: boolean; // 是否支持默认值\n  supportExtent: boolean; // 是否支持扩展\n  supportLength: boolean; // 是否支持长度\n  supportNullable: boolean; // 是否支持为空\n  supportScale: boolean; // 是否支持小数位\n  supportValue: boolean; // 是否支持值\n  supportUnit: boolean; // 是否支持单位\n}\n\n/** 不同数据库支持不同的默认值 */\nexport interface IDefaultValue {\n  defaultValue: string; // 默认值\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/editSequence.ts",
    "content": "import { EditColumnOperationType, NullableType } from '@/constants';\n\nexport interface ISequenceInfo {\n    nspname: String,\n    relname: string;\n    comment?: string | null;\n    typname?: string | null;\n    seqcache?: number | null;\n    rolname?: string | null;\n    seqstart?: number | null;\n    seqincrement?: string | null;\n    seqmax?: number | null;\n    seqmin?: number | null;\n    seqcycle?: Boolean | null;\n}"
  },
  {
    "path": "chat2db-client/src/typings/editTable.ts",
    "content": "import { EditColumnOperationType, NullableType } from '@/constants';\n\n// 编辑表时表的基础数据\nexport interface IBaseInfo {\n  name: string;\n  comment?: string | null;\n  charset: string | null; // 字符集\n  engine: string | null; // 引擎\n  incrementValue: string | null; // 自增值\n}\n\nexport interface IColumnItemNew {\n  editStatus: EditColumnOperationType | null; // 操作类型\n\n  key?: string;\n  oldName: string | null; // 老的列名\n  name: string | null; // 列名\n\n  databaseName: string | null; // 数据库名\n  schemaName: string | null; // 模式名\n  tableName: string | null; // 表名\n\n  columnType: string | null; // 列的类型 比如 varchar(100) ,double(10,6)\n  dataType: number | null; // 数据类型\n  defaultValue: string | null; // 默认值\n  autoIncrement: string | null; // 是否自增\n  comment: string | null; // 注释\n  primaryKey: boolean | null; // 是否主键\n  primaryKeyOrder: number | null; // 主键顺序\n  typeName: string | null; // 类型名\n  columnSize: number | null; // 列的长度\n  bufferLength: number | null; // 缓冲区长度\n  decimalDigits: string | null; // 小数位数\n  numPrecRadix: number| null; // 数字精度\n  sqlDataType: string| null; // sql数据类型\n  sqlDatetimeSub: string| null; // sql日期时间子类型\n  charOctetLength:string|  null; // 字符串最大长度\n  ordinalPosition: number| null; // 位置\n  nullable: NullableType | null; //是否为空\n  generatedColumn: string | null; // 是否生成列\n\n  charSetName: string | null; // 字符集名\n  collationName: string | null; // 排序规则名\n  value: string | null; // 值\n}\n\n// \nexport interface IIndexIncludeColumnItem {\n  key?: string; // 前端添加的唯一标识\n  ascOrDesc: string | null; // 升序还是降序\n  cardinality: number | null; // 基数\n  collation: string | null; // 排序规则\n  columnName: string | null; // 列名\n  comment: string | null; // 注释\n  databaseName: string | null; // 数据库名\n  filterCondition: string | null; // 过滤条件\n  indexName: string | null; // 索引名\n  indexQualifier: string | null; // 索引限定符\n  nonUnique: boolean | null; // 是否唯一\n  ordinalPosition: number | null; // 位置\n  schemaName: string | null; // 模式名\n  tableName: string | null; // 表名\n  type: string | null; // 类型\n  pages: number | null; // 页数\n}\n\n// 编辑表时索引的数据结构\nexport interface IIndexItem {\n  key?: string;\n  name: string | null;\n  comment?: string | null;\n  type: any | null;\n  columnList: IIndexIncludeColumnItem[];\n  editStatus: EditColumnOperationType | null; // 操作类型\n}\n\n// 编辑表时整体的数据结构\nexport interface IEditTableInfo extends IBaseInfo {\n  columnList: IColumnItemNew[];\n  indexList: IIndexItem[];\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/index.ts",
    "content": "export * from './common';\nexport * from './connection';\nexport * from './dashboard';\nexport * from './database';\nexport * from './main';\nexport * from './theme';\nexport * from './tree';\nexport * from './setting';\nexport * from './team';\nexport * from './workspace';\nexport * from './editTable';\nexport * from './console';\nexport * from './editSequence';\n"
  },
  {
    "path": "chat2db-client/src/typings/main.ts",
    "content": "import React from 'react';\n\nexport interface INavItem {\n  key: string;\n  icon: string;\n  component?: React.ReactNode;\n  openBrowser?: string;\n  iconFontSize?: number;\n  isLoad: boolean;\n  name: string;\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/resultTable.ts",
    "content": "import { IExecuteSqlParams } from '@/service/sql';\n\nexport enum ExportTypeEnum {\n  CSV = 'CSV',\n  INSERT = 'INSERT',\n  WORD = 'WORD',\n  EXCEL = 'EXCEL',\n  HTML = 'HTML',\n  MARKDOWN = 'MARKDOWN',\n  PDF = 'PDF'\n}\nexport enum ExportSizeEnum {\n  CURRENT_PAGE = 'CURRENT_PAGE',\n  ALL = 'ALL',\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/setting.ts",
    "content": "import { AIType } from './ai';\n\nexport interface IAiConfig {\n  aiSqlSource: AIType;\n  apiKey?: string;\n  apiHost?: string;\n  httpProxyHost?: string;\n  httpProxyPort?: string;\n  stream?: boolean;\n  secretKey?:string;\n  model?: string;\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/team.ts",
    "content": "// ===================== Common ==================\nexport enum ManagementType {\n  DATASOURCE = 'DATASOURCE',\n  TEAM = 'TEAM',\n  USER = 'USER',\n}\n\nexport enum AffiliationType {\n  'USER_TEAM' = 'USER_TEAM',\n  'USER_DATASOURCE' = 'USER_DATASOURCE',\n  'TEAM_USER' = 'TEAM_USER',\n  'TEAM_DATASOURCE' = 'TEAM_DATASOURCE',\n  'DATASOURCE_USER/TEAM' = 'DATASOURCE_USER/TEAM'\n}\n\nexport enum SearchType {\n  DATASOURCE = 'DATASOURCE',\n  TEAM = 'TEAM',\n  USER = 'USER',\n  'USER/TEAM' = 'USER/TEAM'\n}\n\nexport enum StatusType {\n  INVALID = 'INVALID',\n  VALID = 'VALID',\n}\n\nexport enum RoleType {\n  ADMIN = 'ADMIN',\n  USER = 'USER',\n}\n\nexport enum MemberType {\n  TEAM = 'TEAM',\n  USER = 'USER'\n}\n\n\n\n// ===================== DataSource ==================\n\nexport interface IDataSourceVO {\n  /**\n   * 连接别名\n   */\n  alias?: string;\n  /**\n   * 环境\n   */\n  environment?: IEnvironmentVO;\n  /**\n   * 环境id\n   */\n  environmentId?: number;\n  /**\n   * 主键id\n   */\n  id?: number;\n  /**\n   * 连接地址\n   */\n  url?: string;\n}\n\nexport interface IEnvironmentVO {\n  /**\n   * 主键\n   */\n  id?: number;\n  /**\n   * 环境名称\n   */\n  name?: string;\n  /**\n   * 环境缩写\n   */\n  shortName?: string;\n  /**\n   * 样式类型\n   */\n  style?: string;\n}\n\n\nexport interface IDataSourceAccessVO {\n  /**\n   * 授权对象\n   */\n  accessObject: IDataSourceAccessObjectVO;\n  /**\n   * 授权id,根据类型区分是用户还是团队\n   */\n  accessObjectId: number;\n  /**\n   * 授权类型\n   */\n  accessObjectType: RoleType;\n  /**\n   * 主键\n   */\n  id: number;\n}\n\n\nexport interface IDataSourceAccessObjectVO {\n  /**\n   * The name of the code that belongs to the authorization type, such as user account, team\n   * code\n   */\n  code?: string;\n  /**\n   * 授权id,根据类型区分是用户还是团队\n   */\n  id?: number;\n  /**\n   * Code that belongs to the authorization type, such as user name, team name\n   */\n  name?: string;\n  /**\n   * 授权类型\n   */\n  type?: RoleType;\n}\n\n// ===================== User ======================\n\nexport interface IUserVO {\n  /**\n * 主键\n */\n  id: number;\n\n  /**\n   * 邮箱\n   */\n  email: string;\n  /**\n   * 昵称\n   */\n  nickName: string;\n  /**\n   * 密码\n   */\n  password: string;\n  /**\n   * 角色编码\n   */\n  roleCode: RoleType;\n  /**\n   * 用户状态\n   */\n  status: StatusType;\n  /**\n   * 用户名\n   */\n  userName: string;\n}\n\n\nexport interface IUserWithTeamVO {\n  /**\n   * 主键\n   */\n  id?: number;\n  /**\n   * 团队\n   */\n  team?: ITeamVO;\n  /**\n   * user id\n   */\n  userId?: number;\n}\n\nexport interface IUserWithDataSourceVO {\n  id?: number;\n  /**\n   * Data Source\n   */\n  dataSource?: IDataSourceVO;\n  /**\n   * user id\n   */\n  userId?: number;\n}\n\n\n// ===================== Team =====================\n\nexport interface ITeamVO {\n  id?: number;\n  /**\n   * 团队编码\n   */\n  code: string;\n  /**\n   * 团队描述\n   */\n  description?: string;\n  /**\n   * 团队名称\n   */\n  name: string;\n  /**\n   * 团队状态\n   */\n  status: StatusType;\n}\n\nexport interface ITeamWithUserVO {\n  id: number;\n  teamId: number;\n  user: IUserVO;\n}\n\nexport interface ITeamWithDataSourceVO {\n  /**\n   * Data Source\n   */\n  dataSource?: IDataSourceVO;\n  /**\n   * 主键\n   */\n  id: number;\n  /**\n   * team id\n   */\n  teamId?: number;\n}\n\n// ===================== USER/TEAM =====================\nexport interface ITeamAndUserVO {\n  code?: string;\n  id?: number;\n  name?: string;\n  type?: MemberType\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/theme.ts",
    "content": "import { ThemeType, PrimaryColorType } from '@/constants';\n\nexport interface ITheme {\n  backgroundColor: ThemeType;\n  primaryColor: PrimaryColorType;\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/tree.ts",
    "content": "import { TreeNodeType, DatabaseTypeCode } from '@/constants';\n\nexport interface IExtraParams {\n  dataSourceId: number;\n  databaseType: DatabaseTypeCode;\n  dataSourceName: string;\n  databaseName?: string;\n  schemaName?: string;\n  tableName?: string;\n  functionName?: string;\n  procedureName?: string;\n  triggerName?: string;\n}\n\nexport interface ITreeNode {\n  uuid: string;\n  key: string | number;\n  name: string;\n  // 用展示的name\n  // displayName: string;\n  treeNodeType: TreeNodeType; // 节点的类型 表、列、文件等等\n  pretendNodeType?: TreeNodeType; // 伪装的节点类型，当树不连续时，需要用到\n  isLeaf?: boolean; // 是否为叶子节点\n  children?: ITreeNode[] | null;\n  columnType?: string; // 列的类型\n  extraParams?: IExtraParams;\n  pinned?: boolean; // 是否置顶\n  comment?: string; // 表列的注释\n  loadData?: (params:{refresh: boolean}) => void; // 加载数据的方法\n  // 父元素\n  parentNode?: ITreeNode;\n  level?: number; // 层级\n  // 是否展开\n  expanded?: boolean;\n  parentId?: string;\n  // 分页\n  page?: number;\n  pageSize?: number;\n  total?: number;\n}\n\n// 视图  函数  触发器  过程 通用的返回结果\nexport interface IRoutines {\n  name: string; // 名称\n  comment: string; // 描述\n  pinned: boolean; // 是否置顶\n}\n\nexport interface ITable {\n   /**\n    * 表描述\n    */\n   comment?: string | null;\n   /**\n    * 表名称\n    */\n   name: string | null;\n   /**\n    * 是否已经被固定\n    */\n   pinned?: boolean;\n\n}\n"
  },
  {
    "path": "chat2db-client/src/typings/user.ts",
    "content": "export enum IRole {\n  'ADMIN' = 'ADMIN',\n  'USER' = 'USER',\n  'DESKTOP' = 'DESKTOP',\n}\nexport interface IUser {\n  id?: number;\n  userName: string;\n  nickName: string;\n  password?: string;\n  password2?: string;\n  email: string;\n  role?: IRole;\n}\n\nexport type IUserVO = {\n  admin: boolean;\n  id : number;\n  nickName: string; \n  roleCode: string;\n  token: string;\n} | null\n"
  },
  {
    "path": "chat2db-client/src/typings/workspace.ts",
    "content": "import { CreateTabIntroType, WorkspaceTabType, DatabaseTypeCode, ConsoleStatus } from '@/constants';\nimport { ITreeNode } from '@/typings';\n\nexport interface ICreateTabIntro {\n  type: CreateTabIntroType;\n  workspaceTabType: WorkspaceTabType;\n  treeNodeData: ITreeNode;\n}\n\nexport interface IWorkspaceTab {\n  id: number | string; // Tab的id\n  type: WorkspaceTabType; // 工作区tab的类型\n  title: string; // 工作区tab的名称\n  uniqueData?: any;\n}\n\nexport interface IBoundInfo {\n  consoleId?: number;\n  dataSourceId: number;\n  dataSourceName: string;\n  databaseType: DatabaseTypeCode;\n  databaseName?: string;\n  schemaName?: string;\n  status: ConsoleStatus;\n  connectable: boolean;\n  supportDatabase: boolean;\n  supportSchema: boolean;\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/IntelliSense/database.ts",
    "content": "import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';\nimport i18n from '@/i18n';\n\nexport const resetSenseDatabase = () => {\n  intelliSenseDatabase.dispose();\n}\n\nlet intelliSenseDatabase = monaco.languages.registerCompletionItemProvider('sql', {\n  provideCompletionItems: () => {\n    return { suggestions: [] };\n  },\n});\n\nconst checkTableContext = (text) => {\n  const normalizedText = text.trim().toUpperCase();\n  const tableKeywords = ['FROM', 'JOIN', 'INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'UPDATE'];\n\n  for (const keyword of tableKeywords) {\n    if (normalizedText.endsWith(keyword)) {\n      return true;\n    }\n  }\n\n  return false;\n};\n\nconst registerIntelliSenseDatabase = (databaseName: Array<{ name: string; dataSourceName: string }>) => {\n  resetSenseDatabase();\n  intelliSenseDatabase = monaco.languages.registerCompletionItemProvider('sql', {\n    triggerCharacters: [' ', '.'],\n    provideCompletionItems: (model, position) => {\n      const lineContentUntilPosition = model.getValueInRange({\n        startLineNumber: position.lineNumber,\n        startColumn: 1,\n        endLineNumber: position.lineNumber,\n        endColumn: position.column,\n      });\n      const isTableContext = checkTableContext(lineContentUntilPosition);\n\n      return {\n        suggestions: (databaseName || []).map(({ name, dataSourceName }) => ({\n          label: {\n            label: name,\n            detail: dataSourceName ? `(${dataSourceName})` : null,\n            description: i18n('sqlEditor.text.databaseName'),\n          },\n          insertText: name,\n          sortText: isTableContext ? '01' : '08',\n          kind: monaco.languages.CompletionItemKind.Property,\n        })),\n      };\n    },\n  });\n};\n\nexport { intelliSenseDatabase, registerIntelliSenseDatabase };\n"
  },
  {
    "path": "chat2db-client/src/utils/IntelliSense/field.ts",
    "content": "import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';\nimport sqlService from '@/service/sql';\nimport i18n from '@/i18n';\n\nlet fieldList: Record<string, Array<{ name: string; tableName: string }>> = {};\n\n/** 当前库下的表 */\nlet intelliSenseField = monaco.languages.registerCompletionItemProvider('sql', {\n  provideCompletionItems: () => {\n    return {\n      suggestions: [],\n    };\n  },\n});\n\nexport const resetSenseField = () => {\n  intelliSenseField.dispose();\n}\n\nconst addIntelliSenseField = async (props: {\n  tableName: string;\n  dataSourceId: number;\n  databaseName: string;\n  schemaName?: string;\n}) => {\n  const { tableName, dataSourceId, databaseName, schemaName } = props;\n\n  if (!fieldList[tableName]) {\n    const data = await sqlService.getAllFieldByTable({\n      dataSourceId,\n      databaseName,\n      schemaName,\n      tableName,\n    });\n    fieldList[tableName] = data;\n  }\n};\n\nfunction checkFieldContext(text) {\n  const normalizedText = text.trim().toUpperCase();\n  const columnKeywords = ['SELECT', 'WHERE', 'AND', 'OR', 'GROUP BY', 'ORDER BY', 'SET'];\n\n  for (const keyword of columnKeywords) {\n    if (normalizedText.endsWith(keyword)) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nconst registerIntelliSenseField = (tableList: string[], dataSourceId, databaseName, schemaName) => {\n  resetSenseField();\n  fieldList = {};\n  intelliSenseField = monaco.languages.registerCompletionItemProvider('sql', {\n    triggerCharacters: [' ', ',', '.', '('],\n    provideCompletionItems: async (model, position) => {\n      // 获取到当前行文本\n      const textUntilPosition = model.getValueInRange({\n        startLineNumber: position.lineNumber,\n        startColumn: 1,\n        endLineNumber: position.lineNumber,\n        endColumn: position.column,\n      });\n\n      const isFieldContext = checkFieldContext(textUntilPosition);\n      const match = textUntilPosition.match(/(\\b\\w+\\b)[^\\w]*$/);\n\n      let word;\n      if (match) {\n        word = match[1];\n      }\n\n      if (!word) {\n        return; // 如果没有匹配到，直接返回\n      }\n      if (word && tableList.includes(word) && !fieldList[word]) {\n        const data = await sqlService.getAllFieldByTable({\n          dataSourceId,\n          databaseName,\n          schemaName,\n          tableName: word,\n        });\n        fieldList[word] = data;\n      }\n\n      const suggestions: monaco.languages.CompletionItem[] = Object.keys(fieldList).reduce((acc, cur) => {\n        const arr = fieldList[cur].map((fieldObj) => ({\n          label: {\n            label: fieldObj.name,\n            detail: `(${fieldObj.tableName})`,\n            description: i18n('sqlEditor.text.fieldName'),\n          },\n          kind: monaco.languages.CompletionItemKind.Field,\n          insertText: fieldObj.name,\n          sortText: isFieldContext ? '01' : '08',\n        }));\n\n        return [...acc, ...arr];\n      }, []);\n\n      return {\n        suggestions,\n      };\n    },\n  });\n};\n\nexport { intelliSenseField, registerIntelliSenseField, addIntelliSenseField };\n"
  },
  {
    "path": "chat2db-client/src/utils/IntelliSense/index.ts",
    "content": "import { intelliSenseKeyword, registerIntelliSenseKeyword, resetSenseKeyword } from './keyword';\nimport { intelliSenseDatabase, registerIntelliSenseDatabase, resetSenseDatabase } from './database';\nimport { intelliSenseTable, registerIntelliSenseTable, resetSenseTable } from './table';\nimport { intelliSenseField, registerIntelliSenseField, resetSenseField } from './field';\nimport { intelliSenseView, registerIntelliSenseView, resetSenseView } from './view';\nexport {\n  intelliSenseKeyword,\n  registerIntelliSenseKeyword,\n  resetSenseKeyword,\n  intelliSenseDatabase,\n  registerIntelliSenseDatabase,\n  resetSenseDatabase,\n  intelliSenseTable,\n  registerIntelliSenseTable,\n  resetSenseTable,\n  intelliSenseField,\n  registerIntelliSenseField,\n  resetSenseField,\n  intelliSenseView,\n  registerIntelliSenseView,\n  resetSenseView,\n};\n"
  },
  {
    "path": "chat2db-client/src/utils/IntelliSense/keyword.ts",
    "content": "import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';\nimport { DatabaseTypeCode } from '@/constants';\nimport intelliSense from '@/constants/IntelliSense';\nimport i18n from '@/i18n';\n\n/** 关键词 */\nconst getSQLKeywords = (keywords: string[]) => {\n  return keywords.map((key: any) => ({\n    label: {\n      label: key,\n      detail: '',\n      description: i18n('sqlEditor.text.keyword'),\n    },\n    kind: monaco.languages.CompletionItemKind.Text,\n    insertText: key,\n    sortText: '08',\n  }));\n};\n\n/** 函数 */\nconst getSQLFunctions = (functions: string[]) => {\n  return functions.map((key: any) => ({\n    label: {\n      label: key,\n      detail: '',\n      description: i18n('sqlEditor.text.function'),\n      sortText: '09',\n    },\n    // kind: monaco.languages.CompletionItemKind.Method,\n    kind: monaco.languages.CompletionItemKind.Function,\n    insertText: key,\n  }));\n};\n\nexport const resetSenseKeyword = () => {\n  intelliSenseKeyword.dispose(); \n}\n\nlet intelliSenseKeyword = monaco.languages.registerCompletionItemProvider('sql', {\n  provideCompletionItems: () => {\n    return { suggestions: [] };\n  },\n});\n\nconst registerIntelliSenseKeyword = (databaseCode?: DatabaseTypeCode) => {\n  resetSenseKeyword();\n  intelliSenseKeyword = monaco.languages.registerCompletionItemProvider('sql', {\n    triggerCharacters: [' ', '('], // 触发提示的字符\n    provideCompletionItems: (model, position) => {\n      // 获取所有的编辑器实例\n      const commonIntelliSense = Object.values(intelliSense).find((v) => v.type === databaseCode);\n\n      if (commonIntelliSense) {\n        return {\n          suggestions: [\n            ...getSQLKeywords(commonIntelliSense?.keywords),\n            ...getSQLFunctions(commonIntelliSense?.functions),\n          ],\n        };\n      } else {\n        const intelliSenseMySQL = Object.values(intelliSense).find((v) => v.type === DatabaseTypeCode.MYSQL);\n        if (!intelliSenseMySQL) return { suggestions: [] };\n        return {\n          suggestions: [\n            ...getSQLKeywords(intelliSenseMySQL?.keywords),\n            ...getSQLFunctions(intelliSenseMySQL?.functions),\n          ],\n        };\n      }\n    },\n  });\n};\n\nexport { intelliSenseKeyword, registerIntelliSenseKeyword };\n"
  },
  {
    "path": "chat2db-client/src/utils/IntelliSense/table.ts",
    "content": "import { DatabaseTypeCode } from '@/constants';\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api';\nimport { addIntelliSenseField } from './field';\nimport i18n from '@/i18n';\nimport { compatibleDataBaseName } from '../database';\n\nexport const resetSenseTable = () => {\n  intelliSenseTable.dispose();\n};\n\n/** 当前库下的表 */\nlet intelliSenseTable = monaco.languages.registerCompletionItemProvider('sql', {\n  provideCompletionItems: () => {\n    return { suggestions: [] };\n  },\n});\n\nconst checkTableContext = (text) => {\n  const normalizedText = text.trim().toUpperCase();\n  const tableKeywords = ['FROM', 'JOIN', 'INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'UPDATE'];\n\n  for (const keyword of tableKeywords) {\n    if (normalizedText.endsWith(keyword)) {\n      return true;\n    }\n  }\n\n  return false;\n};\n\nconst handleInsertText = (keyword: string, tableName: string, databaseCode: DatabaseTypeCode) => {\n  if (/^[\\\"\\`\\[]/.test(keyword)) {\n    return tableName;\n  }\n\n  return compatibleDataBaseName(tableName, databaseCode);\n};\n\nconst registerIntelliSenseTable = (\n  tableList: Array<{ name: string; comment: string }>,\n  databaseCode: DatabaseTypeCode,\n  dataSourceId?: number,\n  databaseName?: string | null,\n  schemaName?: string | null,\n) => {\n  monaco.editor.registerCommand('addFieldList', (_: any, ...args: any[]) => {\n    addIntelliSenseField(args[0]);\n    return;\n  });\n\n  resetSenseTable();\n  intelliSenseTable = monaco.languages.registerCompletionItemProvider('sql', {\n    triggerCharacters: [' ', '.'],\n    provideCompletionItems: (model, position) => {\n      const lineContentUntilPosition = model.getValueInRange({\n        startLineNumber: position.lineNumber,\n        startColumn: 1,\n        endLineNumber: position.lineNumber,\n        endColumn: position.column,\n      });\n\n      const isTableContext = checkTableContext(lineContentUntilPosition);\n      // 获取触发提示的字符\n      const match = lineContentUntilPosition.match(/\\S+$/);\n      const word = match ? match[0] : '';\n\n      return {\n        suggestions: (tableList || []).map((tableName) => ({\n          label: {\n            label: tableName.name,\n            detail: databaseName ? `(${databaseName})` : null,\n            description: i18n('sqlEditor.text.tableName'),\n          },\n          kind: monaco.languages.CompletionItemKind.Folder,\n          insertText: handleInsertText(word, tableName.name, databaseCode),\n          // range: monaco.Range.fromPositions(position),\n          // documentation: tableName.comment,\n          sortText: isTableContext ? '01' : '08',\n          command: {\n            id: 'addFieldList',\n            title: 'addFieldList',\n            arguments: [\n              {\n                tableName: tableName.name,\n                dataSourceId,\n                databaseName,\n                schemaName,\n              },\n            ],\n          },\n        })),\n      };\n    },\n  });\n};\n\nexport { intelliSenseTable, registerIntelliSenseTable };\n"
  },
  {
    "path": "chat2db-client/src/utils/IntelliSense/view.ts",
    "content": "import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';\nimport i18n from '@/i18n';\n\nexport const resetSenseView = () => {\n  intelliSenseView.dispose();\n\n}\n\n/** 当前库下的表 */\nlet intelliSenseView = monaco.languages.registerCompletionItemProvider('sql', {\n  provideCompletionItems: () => {\n    return { suggestions: [] };\n  },\n});\n\nconst checkViewContext = (text) => {\n  const normalizedText = text.trim().toUpperCase();\n  const tableKeywords = ['FROM', 'JOIN', 'INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'UPDATE'];\n\n  for (const keyword of tableKeywords) {\n    if (normalizedText.endsWith(keyword)) {\n      return true;\n    }\n  }\n\n  return false;\n};\n\nconst registerIntelliSenseView = (\n  viewList: string[],\n  databaseName?: string | null,\n) => {\n  resetSenseView();\n  intelliSenseView = monaco.languages.registerCompletionItemProvider('sql', {\n    triggerCharacters: [' '],\n    provideCompletionItems: (model, position) => {\n      const lineContentUntilPosition = model.getValueInRange({\n        startLineNumber: position.lineNumber,\n        startColumn: 1,\n        endLineNumber: position.lineNumber,\n        endColumn: position.column,\n      });\n\n      const isViewContext = checkViewContext(lineContentUntilPosition);\n\n      return {\n        suggestions: (viewList || []).map((viewName) => {\n          return {\n            label: {\n              label: viewName,\n              detail: databaseName ? `(${databaseName})` : null,\n              description: i18n('sqlEditor.text.viewName'),\n            },\n            kind: monaco.languages.CompletionItemKind.Unit,\n            insertText: viewName,\n            // range: monaco.Range.fromPositions(position),\n            // documentation: tableName.comment,\n            sortText: isViewContext ? '01' : '08'\n          };\n        }),\n      };\n    },\n  });\n};\n\nexport { intelliSenseView, registerIntelliSenseView };\n"
  },
  {
    "path": "chat2db-client/src/utils/check.ts",
    "content": "// type NonNullable<T> = T extends null | undefined ? never : T;\n\nconst toString = Object.prototype.toString;\n\nconst isType =\n  <T>(type: string | string[]) =>\n  (obj: unknown): obj is T =>\n    getType(obj) === `[object ${type}]`;\nexport const getType = (obj: any) => toString.call(obj);\nexport const isArr = Array.isArray;\nexport const isValid = (val: any) => val !== null && val !== undefined;\nexport const isFn = (val: any): val is Function => typeof val === 'function';\nexport const isPlainObj = isType<object>('Object');\nexport const isStr = isType<string>('String');\nexport const isBool = isType<boolean>('Boolean');\nexport const isNum = isType<number>('Number');\nexport const isMap = (val: any): val is Map<any, any> => val && val instanceof Map;\nexport const isSet = (val: any): val is Set<any> => val && val instanceof Set;\nexport const isWeakMap = (val: any): val is WeakMap<any, any> => val && val instanceof WeakMap;\nexport const isWeakSet = (val: any): val is WeakSet<any> => val && val instanceof WeakSet;\nexport const isNumberLike = (index: any): index is number => isNum(index) || /^\\d+$/.test(index);\nexport const isObj = (val: unknown): val is object => typeof val === 'object';\nexport const isRegExp = isType<RegExp>('RegExp');\nexport const isReactElement = (obj: any): boolean => obj && obj['$$typeof'] && obj['_owner'];\nexport const isHTMLElement = (target: any): target is EventTarget => {\n  return Object.prototype.toString.call(target).indexOf('HTML') > -1;\n};\n"
  },
  {
    "path": "chat2db-client/src/utils/database.ts",
    "content": "import { DatabaseTypeCode } from '@/constants/common';\nimport { IWorkspaceModelType } from '@/models/workspace';\nimport { Option } from '@/typings/common';\n\nexport function handleDatabaseAndSchema(databaseAndSchema: IWorkspaceModelType['state']['databaseAndSchema']) {\n  let newCascaderOptions: Option[] = [];\n  if (databaseAndSchema.databases) {\n    newCascaderOptions = (databaseAndSchema?.databases || []).map((t) => {\n      let schemasList: Option[] = [];\n      if (t.schemas) {\n        schemasList = t.schemas.map((t) => {\n          return {\n            value: t.name,\n            label: t.name,\n            type: 'schema',\n            isLeaf: true,\n          };\n        });\n      }\n      return {\n        value: t.name,\n        label: t.name,\n        type: 'database',\n        children: schemasList,\n        isLeaf: schemasList.length === 0,\n      };\n    });\n  } else if (databaseAndSchema?.schemas) {\n    newCascaderOptions = (databaseAndSchema?.schemas || []).map((t) => {\n      return {\n        value: t.name,\n        label: t.name,\n        type: 'schema',\n        isLeaf: true,\n      };\n    });\n  }\n  return newCascaderOptions;\n}\n\n/**\n * 兼容处理数据库名称\n * @param databaseName\n * @param databaseType\n * @returns\n */\nexport function compatibleDataBaseName(databaseName: string, databaseType: DatabaseTypeCode) {\n  //\"\"  oracele  sqlite postgrsql  h2 dm\n  // ` MYSQL clickhouse MariaDB\n  // [ sqlserver\n  if (\n    [\n      DatabaseTypeCode.ORACLE,\n      DatabaseTypeCode.SQLITE,\n      DatabaseTypeCode.POSTGRESQL,\n      DatabaseTypeCode.H2,\n      DatabaseTypeCode.DB2,\n      DatabaseTypeCode.KINGBASE,\n      DatabaseTypeCode.DM,\n    ].includes(databaseType)\n  ) {\n    return `\"${databaseName}\"`;\n  } else if ([DatabaseTypeCode.SQLSERVER].includes(databaseType)) {\n    return `[${databaseName}]`;\n  } else if (\n    [DatabaseTypeCode.MYSQL, DatabaseTypeCode.CLICKHOUSE, DatabaseTypeCode.TIMEPLUS, DatabaseTypeCode.MARIADB].includes(\n      databaseType,\n    )\n  ) {\n    return `\\`${databaseName}\\``;\n  } else {\n    return `${databaseName}`;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/date.ts",
    "content": "export function formatDate(date: any, fmt = 'yyyy-MM-dd') {\n  if (!date) {\n    return '';\n  }\n  if (typeof date == 'number' || typeof date == 'string') {\n    date = new Date(date);\n  }\n  if (!(date instanceof Date) || isNaN(date.getTime())) {\n    return '';\n  }\n  var o: any = {\n    'M+': date.getMonth() + 1,\n    'd+': date.getDate(),\n    'h+': date.getHours(),\n    'm+': date.getMinutes(),\n    's+': date.getSeconds(),\n    'q+': Math.floor((date.getMonth() + 3) / 3),\n    S: date.getMilliseconds(),\n  };\n  if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));\n  for (var k in o)\n    if (new RegExp('(' + k + ')').test(fmt))\n      fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));\n  return fmt;\n}\n\n// 带有时区的时间戳转换为0时区时间戳\nexport function transitionTimezoneTimestamp(timestamp: number) {\n  const timezoneOffset = new Date().getTimezoneOffset() * 60 * 1000\n  return timestamp + timezoneOffset\n}\n\n// 通过0时区的时间戳计算出用户的时间戳\nexport function getUserTimezoneTimestamp(timestamp: number | string) {\n  const timezoneOffset = new Date().getTimezoneOffset() * 60 * 1000\n  return +timestamp - timezoneOffset\n}"
  },
  {
    "path": "chat2db-client/src/utils/eventSource.ts",
    "content": "import { EventSourcePolyfill } from 'event-source-polyfill';\n\nconst connectToEventSource = (params: {\n  url: string;\n  uid: string;\n  onOpen: Function;\n  onMessage: Function;\n  onError: Function;\n}) => {\n  const { url, uid, onOpen, onMessage, onError } = params;\n\n  if (!url || !onMessage || !onError) {\n    throw new Error('url, onMessage, and onError are required');\n  }\n\n  const DBHUB = localStorage.getItem('DBHUB');\n  const p = {\n    headers: {\n      uid,\n      DBHUB,\n    },\n  };\n  const eventSource = new EventSourcePolyfill(`${window._BaseURL}${url}`, p);\n\n  eventSource.onopen = () => {\n    onOpen();\n  };\n\n  eventSource.onmessage = (event) => {\n    onMessage(event.data);\n  };\n\n  eventSource.onerror = (error) => {\n    onError(error);\n    console.error('EventSourcePolyfill error:', error);\n  };\n\n  // 返回一个关闭 eventSource 的函数，以便在需要时调用它\n  return () => {\n    eventSource.close();\n  };\n};\n\nexport default connectToEventSource;\n"
  },
  {
    "path": "chat2db-client/src/utils/file.ts",
    "content": "/**\n * 文件下载 \n * @param url \n * @param params \n */\nexport function downloadFile(url: string, params: any) {\n  // 创建POST请求\n  fetch(url, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json', // 或者根据服务端的要求设置其他的内容类型\n    },\n    body: JSON.stringify(params), // 将参数转换为JSON字符串\n  })\n    .then((response) => {\n      // 从content-disposition头中获取文件名\n      const contentDisposition = response.headers.get('content-disposition');\n      const filename = contentDisposition ? decodeURIComponent(contentDisposition.split(\"''\")[1]) : 'file.txt';\n\n      // 获取返回的Blob数据\n      return response.blob().then((blob) => ({ blob, filename }));\n    })\n    .then(({ blob, filename }) => {\n      // 创建一个代表Blob对象的URL\n      const blobUrl = URL.createObjectURL(blob);\n\n      // 创建一个隐藏的 <a> 标签，并设置其 href 属性\n      const a = document.createElement('a');\n      a.style.display = 'none';\n      a.href = blobUrl;\n\n      // 使用从响应头解析的文件名\n      a.download = filename;\n\n      // 将 <a> 标签附加到 DOM，并触发点击事件\n      document.body.appendChild(a);\n      a.click();\n\n      // 清理：从 DOM 中移除 <a> 标签，并释放Blob URL\n      document.body.removeChild(a);\n      URL.revokeObjectURL(blobUrl);\n    })\n    .catch((error) => {\n      console.error('下载文件失败:', error);\n    });\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/getTree.ts",
    "content": "export const getTreeConfig = {\n\n}"
  },
  {
    "path": "chat2db-client/src/utils/index.ts",
    "content": "import { ThemeType } from '@/constants';\nimport { ITreeNode } from '@/typings';\nimport lodash from 'lodash';\n\nexport function getOsTheme() {\n  return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches\n    ? ThemeType.Dark\n    : ThemeType.Light;\n}\n\nexport function deepClone(target: any) {\n  const map = new WeakMap();\n\n  function isObject(_target: any) {\n    return (typeof _target === 'object' && _target) || typeof _target === 'function';\n  }\n\n  function clone(data: any) {\n    if (!isObject(data)) {\n      return data;\n    }\n    if ([Date, RegExp].includes(data.constructor)) {\n      return new data.constructor(data);\n    }\n    if (typeof data === 'function') {\n      return new Function('return ' + data.toString())();\n    }\n    const exist = map.get(data);\n    if (exist) {\n      return exist;\n    }\n    if (data instanceof Map) {\n      const result = new Map();\n      map.set(data, result);\n      data.forEach((val, key) => {\n        if (isObject(val)) {\n          result.set(key, clone(val));\n        } else {\n          result.set(key, val);\n        }\n      });\n      return result;\n    }\n    if (data instanceof Set) {\n      const result = new Set();\n      map.set(data, result);\n      data.forEach((val) => {\n        if (isObject(val)) {\n          result.add(clone(val));\n        } else {\n          result.add(val);\n        }\n      });\n      return result;\n    }\n    const keys = Reflect.ownKeys(data);\n    const allDesc = Object.getOwnPropertyDescriptors(data);\n    const result = Object.create(Object.getPrototypeOf(data), allDesc);\n    map.set(data, result);\n    keys.forEach((key) => {\n      const val = data[key];\n      if (isObject(val)) {\n        result[key] = clone(val);\n      } else {\n        result[key] = val;\n      }\n    });\n    return result;\n  }\n\n  return clone(target);\n}\n\n// 模糊匹配树并且高亮\nexport function approximateTreeNode(treeData: ITreeNode[], target: string = '', isDelete = true) {\n  if (target) {\n    const newTree: ITreeNode[] = lodash.cloneDeep(treeData || []);\n    newTree.map((item, index) => {\n      // 暂时不递归，只搜索datasource\n      // if(item.children?.length){\n      //   item.children = approximateTreeNode(item.children, target,false);\n      // }\n      if (item.name?.toUpperCase()?.indexOf(target?.toUpperCase()) == -1 && isDelete) {\n        delete newTree[index];\n      } else {\n        item.name = item.name?.replace(target, `<span style='color:red;'>${target}</span>`);\n      }\n    });\n    return newTree.filter((i) => i);\n  } else {\n    return treeData;\n  }\n}\n\n// 模糊匹配树并且高亮\nexport function approximateList<T, K extends keyof T>(\n  data: T[],\n  target: string,\n  // @ts-ignore'\n  keyName: K = 'name',\n  isDelete = true,\n) {\n  if (target) {\n    const newData: T[] = lodash.cloneDeep(data || []);\n    newData.map((item, index) => {\n      // 暂时不递归，只搜索datasource\n      // if(item.children?.length){\n      //   item.children = approximateTreeNode(item.children, target,false);\n      // }\n      // @ts-ignore'\n      if (item[keyName]?.toUpperCase()?.indexOf(target?.toUpperCase()) == -1 && isDelete) {\n        delete newData[index];\n      } else {\n        // @ts-ignore'\n        item[keyName] = item[keyName]?.replace(target, `<span style='color:red;'>${target}</span>`);\n      }\n    });\n    return newData.filter((i) => i);\n  } else {\n    return data;\n  }\n}\n\n// 获取var变量的值\nexport const callVar = (css: string) => {\n  return getComputedStyle(document.documentElement).getPropertyValue(css).trim();\n};\n\n// 给我一个 obj[]， 和 obj的 key 和 value，给你返index\nexport function findObjListValue<T, K extends keyof T>(list: T[], key: K, value: any) {\n  let flag = -1;\n  list.forEach((t: T, index) => {\n    Object.keys(t).forEach((j: K) => {\n      if (j === key && t[j] === value) {\n        flag = index;\n      }\n    });\n  });\n  return flag;\n}\n\n// 清理就版本不兼容的LocalStorage\nexport function clearOlderLocalStorage() {\n  if (localStorage.getItem('app-local-storage-versions') !== 'v4') {\n    localStorage.clear();\n    localStorage.setItem('app-local-storage-versions', 'v4');\n  }\n}\n\n// 退出登录清理一些记录位置的localStorage\nexport function logoutClearSomeLocalStorage() {\n  localStorage.removeItem('current-workspace-database');\n  localStorage.removeItem('cur-connection');\n  localStorage.removeItem('active-console-id');\n  localStorage.removeItem('curPage');\n}\n\n// 判断是否需要更新版本\nexport function isVersionHigher(version: string, currentVersion: string): boolean {\n  // 按照 . 分割版本号\n  const versionParts = version.split('.');\n  const currentVersionParts = currentVersion.split('.');\n\n  // 按照从左到右的顺序比较每一位的大小\n  for (let i = 0; i < versionParts.length; i++) {\n    const part = parseInt(versionParts[i]);\n    const currentPart = parseInt(currentVersionParts[i] || '0');\n\n    if (part > currentPart) {\n      return true;\n    } else if (part < currentPart) {\n      return false;\n    }\n  }\n\n  // 如果两个版本号完全相等，则返回false\n  return false;\n}\n\n// 获取应用的一些基本信息\nexport function getApplicationMessage() {\n  const env = __ENV__;\n  const versions = __APP_VERSION__;\n  const buildTime = __BUILD_TIME__;\n  const userAgent = navigator.userAgent;\n  return {\n    env,\n    versions,\n    buildTime,\n    userAgent,\n  };\n}\n\n// os is mac or windows\nexport function osNow(): {\n  isMac: boolean;\n  isWin: boolean;\n} {\n  const agent = navigator.userAgent.toLowerCase();\n  const isMac = /macintosh|mac os x/i.test(navigator.userAgent);\n  const isWin =\n    agent.indexOf('win32') >= 0 ||\n    agent.indexOf('wow32') >= 0 ||\n    agent.indexOf('win64') >= 0 ||\n    agent.indexOf('wow64') >= 0;\n  return {\n    isMac,\n    isWin,\n  };\n}\n\n// 桌面端用hash模式，web端用history模式，路由跳转\nexport function navigate(path: string) {\n  if (__ENV__ === 'desktop') {\n    window.location.replace(`#${path}`);\n  } else {\n    window.location.replace(path);\n  }\n}\n\n// 获取cookie\nexport function getCookie(name: string) {\n  const arr = document.cookie.match(new RegExp('(^| )' + name + '=([^;]*)(;|$)'));\n  if (arr != null) {\n    return decodeURIComponent(arr[2]);\n  }\n  return null;\n}\n\n// 判断两个版本的大小\nexport function compareVersion(version1: string, version2: string) {\n  const v1 = version1.split('.');\n  const v2 = version2.split('.');\n  const len = Math.max(v1.length, v2.length);\n  \n  while (v1.length < len) {\n    v1.push('0');\n  }\n  while (v2.length < len) {\n    v2.push('0');\n  }\n\n  for (let i = 0; i < len; i++) {\n    const num1 = parseInt(v1[i]);\n    const num2 = parseInt(v2[i]);\n\n    if (num1 > num2) {\n      return 1;\n    } else if (num1 < num2) {\n      return -1;\n    }\n  }\n\n  return 0;\n}\n\n// 把剪切板的内容转成二维数组\nexport function clipboardToArray(text: string): Array<Array<string | null>> {\n  if (!text) {\n    return [[]];\n  }\n  try {\n    const rows = text.split('\\n');\n    const array2D = rows.map((row) => row.split('\\t'));\n    return array2D;\n  } catch {\n    console.log('copy error');\n    return [[]];\n  }\n}\n\n// Copy\nexport function copy(message: string) {\n  // clipboardCopy(message);\n  navigator.clipboard.writeText(message);\n}\n\n// 二维数组复制\nexport function tableCopy(array2D: Array<Array<string | null>>) {\n  try {\n    const text = array2D.map((row) => row.join('\\t')).join('\\n');\n    navigator.clipboard.writeText(text);\n  } catch {\n    console.log('copy error');\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/localStorage.ts",
    "content": "import { ThemeType, PrimaryColorType, LangType } from '@/constants';\nimport { ICurWorkspaceParams } from '@/models/workspace';\nimport { getCookie } from '@/utils';\n\nexport function getLang(): LangType {\n  return (localStorage.getItem('lang') as LangType) || 'en-us';\n}\n\nexport function setLang(lang: LangType) {\n  return localStorage.setItem('lang', lang);\n}\n\nexport function getTheme(): ThemeType {\n  const themeColor: any = localStorage.getItem('theme') as ThemeType\n  if (themeColor) {\n    return themeColor\n  }\n  localStorage.setItem('theme', ThemeType.Light)\n  // 默认主题色\n  return ThemeType.Light\n}\n\nexport function setTheme(theme: ThemeType) {\n  return localStorage.setItem('theme', theme);\n}\n\nexport function getPrimaryColor(): PrimaryColorType {\n  const primaryColor = localStorage.getItem('primary-color') as PrimaryColorType\n  if (primaryColor) {\n    return primaryColor\n  }\n  localStorage.setItem('primary-color', PrimaryColorType.Golden_Purple)\n  // 默认主题色\n  return PrimaryColorType.Golden_Purple\n}\n\nexport function setPrimaryColor(primaryColor: PrimaryColorType) {\n  return localStorage.setItem('primary-color', primaryColor);\n}\n\nexport function setCurrentWorkspaceDatabase(value: ICurWorkspaceParams) {\n  return localStorage.setItem(`current-workspace-database`, JSON.stringify(value));\n}\n\nexport function getCurrentWorkspaceDatabase(): ICurWorkspaceParams {\n  const curWorkspaceParams = localStorage.getItem(`current-workspace-database`);\n\n  if (curWorkspaceParams) {\n    return JSON.parse(curWorkspaceParams);\n  }\n  return {} as ICurWorkspaceParams;\n}\n\nexport function getCurConnection() {\n  const curConnection = localStorage.getItem(`cur-connection`);\n  if (curConnection) {\n    return JSON.parse(curConnection || '{}');\n  }\n  return undefined;\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/lodash.ts",
    "content": ""
  },
  {
    "path": "chat2db-client/src/utils/sort.ts",
    "content": "/**\n * 比较两个字符串数字的大小，包含大数情况\n * @param a\n * @param b\n * @returns\n */\nexport function compareStrings(a: string, b: string) {\n  \n  if (!a  ) {\n    return -1;\n  }\n\n  if (!b) {\n    return 1;\n  }\n\n  // 比较字符串长度\n  if (a.length !== b.length) {\n    return a.length - b.length;\n  }\n\n  // 逐位比较字符的ASCII码值\n  for (let i = 0; i < a.length; i++) {\n    if (a[i] !== b[i]) {\n      return a.charCodeAt(i) - b.charCodeAt(i);\n    }\n  }\n\n  // 如果两个字符串完全相等，则返回0\n  return 0;\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/sql.ts",
    "content": "import { DatabaseTypeCode } from '@/constants';\nimport sqlServer from '@/service/sql';\nimport { format } from 'sql-formatter';\n\n/**\n * 格式化sql\n */\nexport function formatSql(sql: string, dbType: DatabaseTypeCode) {\n  const arr = [\n    'bigquery',\n    'db2',\n    'hive',\n    'mariadb',\n    'mysql',\n    'n1ql',\n    'plsql',\n    'postgresql',\n    'redshift',\n    'spark',\n    'sqlite',\n    'sql',\n    'trino',\n    'transactsql',\n    'singlestoredb',\n    'snowflake',\n  ];\n  const language = arr.includes(dbType.toLowerCase()) ? dbType.toLowerCase() : 'sql';\n  return new Promise((r: (sql: string) => void) => {\n    let formatRes = '';\n    // debugger;\n    try {\n      formatRes = format(sql || '', { language });\n      // formatRes = '';\n    } catch (e) {\n      console.log('Frontend format sql error', e);\n    }\n    // // 如果格式化失败，直接返回原始sql\n    if (!formatRes) {\n      sqlServer\n        .sqlFormat({\n          sql,\n          dbType,\n        })\n        .then((res) => {\n          formatRes = res;\n          r(formatRes);\n        });\n    } else {\n      r(formatRes);\n    }\n  });\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/timezone.ts",
    "content": "export function getLinkBasedOnTimezone(): string {\n  // 获取当前时区\n  const timezone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n  // 定义中国时区的链接和非中国时区的链接\n  const chinaLink = \"https://chat2db-ai.com\";\n  const nonChinaLink = \"https://chat2db.ai\";\n\n  // 判断时区是否为中国的时区，这里简化为检查是否为\"Asia/Shanghai\"或\"Asia/Chongqing\"\n  // 你也可以根据需要检查时区偏移是否为UTC+8\n  if (timezone === \"Asia/Shanghai\" || timezone === \"Asia/Chongqing\") {\n    return chinaLink;\n  } else {\n    return nonChinaLink;\n  }\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/url.ts",
    "content": "/**\n * 获取url参数\n * @param paramName\n * @returns\n */\n\nexport function getUrlParam(paramName) {\n  // 获取当前URL\n  const currentUrl = window.location.href;\n\n  // 获取URL中的查询字符串\n  const queryString = currentUrl.split('?')[1];\n\n  // 如果没有查询字符串，直接返回\n  if (queryString === undefined) {\n    return;\n  }\n\n  // 将查询字符串分割成数组\n  const paramList = queryString.split('&');\n\n  // 遍历每个参数\n  for (let i = 0; i < paramList.length; i++) {\n    // 检查每个参数的名称是否匹配\n    const param = paramList[i].split('=')[0];\n    if (param === paramName) {\n      // 返回参数值\n      return paramList[i].split('=')[1];\n    }\n  }\n\n  // 如果没有找到参数，则返回null\n  return null;\n}\n/**\n * 更新URL的参数\n * @param key\n * @param value\n * @returns\n */\nexport function updateQueryStringParameter(key, value) {\n  const uri = window.location.href;\n  if (!value) {\n    return uri;\n  }\n  const re = new RegExp('([?&])' + key + '=.*?(&|$)', 'i');\n  const separator = uri.indexOf('?') !== -1 ? '&' : '?';\n  if (uri.match(re)) {\n    return uri.replace(re, '$1' + key + '=' + value + '$2');\n  } else {\n    return uri + separator + key + '=' + value;\n  }\n}\n\n/**\n * 格式化参数\n * @param obj\n * @returns\n */\nexport function formatParams(obj: { [key: string]: any }) {\n  const params = new URLSearchParams();\n  Object.entries(obj).forEach(([key, value]) => {\n    if (value === undefined || value === null) {\n      return;\n    }\n    if (Array.isArray(value)) {\n      value.forEach((item) => {\n        params.append(key, item);\n      });\n    } else {\n      params.append(key, value);\n    }\n  });\n  return params.toString();\n}\n\n/**\n * 生成url\n * @param key\n * @returns\n */\n\nexport function generateUrl(key: string) { \n  if (__ENV__ === 'desktop') {\n    return window.location.href.split('/#/')[0] + '/#/' + key;\n  }\n  return window.location.origin + '/' + key;\n}\n"
  },
  {
    "path": "chat2db-client/src/utils/webpack.ts",
    "content": "// .umirc里无法识别到@/xxxx 所以单独开了webpack.ts，这个文件只可以写函数，不可以引入其他组件\n\n// 分割出yarn启动命令里添加的参数\nexport function extractYarnConfig(argv: string[]){\n  const newArgv = argv.slice(2)\n  const yarn_config:{[k in string]: string} = {}\n  newArgv.forEach(t=>{\n    if(t && t.startsWith(\"--\")){\n      const regex = /--(.+?)=(.+)/;\n      const matches = t.match(regex);\n      if (matches) {\n        const key = matches[1];\n        const value = matches[2];\n        yarn_config[key] = value\n      }\n    }\n  })\n  return yarn_config\n}\n\nexport function formatDate(date: any, fmt = 'yyyy-MM-dd') {\n  if (!date) {\n    return '';\n  }\n  if (typeof date == 'number' || typeof date == 'string') {\n    date = new Date(date);\n  }\n  if (!(date instanceof Date) || isNaN(date.getTime())) {\n    return '';\n  }\n  var o: any = {\n    'M+': date.getMonth() + 1,\n    'd+': date.getDate(),\n    'h+': date.getHours(),\n    'm+': date.getMinutes(),\n    's+': date.getSeconds(),\n    'q+': Math.floor((date.getMonth() + 3) / 3),\n    S: date.getMilliseconds(),\n  };\n  if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));\n  for (var k in o)\n    if (new RegExp('(' + k + ')').test(fmt))\n      fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));\n  return fmt;\n}\n\n// 带有时区的时间戳转换为0时区时间戳\nexport function transitionTimezoneTimestamp(timestamp: number) {\n  const timezoneOffset = new Date().getTimezoneOffset() * 60 * 1000\n  return timestamp + timezoneOffset\n}\n"
  },
  {
    "path": "chat2db-client/tsconfig.json",
    "content": "{\n  \"extends\": \"./src/.umi/tsconfig.json\",\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"jsx\": \"react-jsx\",\n    \"noImplicitAny\": false\n  }\n}\n"
  },
  {
    "path": "chat2db-client/typings.d.ts",
    "content": "import 'umi/typings';\nimport { IVersionResponse } from '@/typings';\n\ndeclare module 'monaco-editor/esm/vs/basic-languages/sql/sql';\ndeclare module 'monaco-editor/esm/vs/language/typescript/ts.worker.js';\ndeclare module 'monaco-editor/esm/vs/editor/editor.worker.js';\ndeclare namespace NodeJS {\n  interface ProcessEnv {\n    readonly NODE_ENV: 'development' | 'production'\n    readonly UMI_ENV: string\n    readonly __ENV: string;\n  }\n}\n\ndeclare global {\n  interface Window {\n    _Lang: string;\n    _APP_PORT: string;\n    _BUILD_TIME: string;\n    _BaseURL: string;\n    _AppThemePack: { [key in string]: string };\n    _appGatewayParams: IVersionResponse;\n    _notificationApi: any;\n    _indexedDB: any;\n    electronApi?: {\n      startServerForSpawn: () => void;\n      quitApp: () => void;\n      setBaseURL: (baseUrl: string) => void;\n      registerAppMenu: (data: any) => void;\n      setForceQuitCode: (code: boolean) => void;\n      setMaximize: () => void;\n      getPlatform: () => {\n        isLinux: boolean,\n        isWin: boolean,\n        isMac: boolean,\n      };\n      minimizeWindow: () => void;\n      closeWindow: () => void;\n      isMaximized: () => boolean;\n    };\n  }\n  const __APP_VERSION__: string;\n  const __BUILD_TIME__: string;\n  const __ENV__: string;\n  const __APP_PORT__: string;\n}\n"
  },
  {
    "path": "chat2db-server/.apifox-helper.properties",
    "content": "# Configuration generated by easy-yapi plug-in\n# Convert date to long\njson.rule.convert[java.util.Date]=java.lang.Long\njson.rule.convert[java.sql.Timestamp]=java.lang.Long\njson.rule.convert[java.time.LocalDateTime]=java.lang.Long\njson.rule.convert[java.time.LocalDate]=java.lang.Long\n\n# Use version to modify tags\napi.tag=#version\n\n# ignore serialVersionUID\nconstant.field.ignore=groovy:it.name()==\"serialVersionUID\"\n\n# sprnSupport for Jackson annotations\njson.rule.field.ignore=@com.fasterxml.jackson.annotation.JsonIgnore#value\n\nfield.demo=#demo\nfield.default.value=#default\n#apifox mock\nfield.mock=#mock"
  },
  {
    "path": "chat2db-server/.easy.api.config",
    "content": "# Configuration generated by easy-yapi plug-in\n# Convert date to long\njson.rule.convert[java.util.Date]=java.lang.Long\njson.rule.convert[java.sql.Timestamp]=java.lang.Long\njson.rule.convert[java.time.LocalDateTime]=java.lang.Long\njson.rule.convert[java.time.LocalDate]=java.lang.Long\n\n# Use version to modify tags\napi.tag=#version\n\n# ignore serialVersionUID\nconstant.field.ignore=groovy:it.name()==\"serialVersionUID\"\n\n# sprnSupport for Jackson annotations\njson.rule.field.ignore=@com.fasterxml.jackson.annotation.JsonIgnore#value\n"
  },
  {
    "path": "chat2db-server/.gitignore",
    "content": "target/\nrebel.xml\nrebel-remote.xml\n.gradle\n.flattened-pom.xml\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\n\n## The code generated by front-end packaging does not need to be submitted to github\n/chat2db-server-start/src/main/resources/static/front/\n/chat2db-server-start/src/main/resources/thymeleaf/\n"
  },
  {
    "path": "chat2db-server/README.md",
    "content": "# 内部协作规范\n## 接口规范\n不会使用的参照：https://yuque.antfin-inc.com/docs/share/8a5ff21a-6367-4c77-9e3c-1d5ae9570060?# 《yapi》\n## 国际化处理方案\n* 在`messages.properties` 文件下新增code\n  * 规范是 `作用域.描述` ，比如 `dataSource.sqlAnalysisError`\n* 方案1：在需要提示用户的地方抛出业务异常\n```java\n// 框架会将 dataSource.sqlAnalysisError 翻译成对应的异常，并返回给前端\nthrow new BusinessException(\"dataSource.sqlAnalysisError\");\n```\n* 方案2：不用异常直接获取国际化\n```java\n// 直接可以获取国际化翻译的文案\nI18nUtils.getMessage(\"dataSource.sqlAnalysisError\")\n```\n### 国际化中文乱码\nEditor -> File Encodeings -> Defualt encoding for properties files: 改成 utf-8\n### 编辑国际化文件\n建议安装插件 `Resource Bundle Editor`去编辑，,点击`messages.properties`，下方有个`Resource Bundle` 就可以编辑了。"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n    <artifactId>chat2db-clickhouse</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/ClickHouseDBManage.java",
    "content": "package ai.chat2db.plugin.clickhouse;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.*;\nimport java.util.Objects;\n\npublic class ClickHouseDBManage extends DefaultDBManage implements DBManage {\n    @Override\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        exportTablesOrViewsOrDictionaries(connection, databaseName, schemaName,asyncContext);\n        exportFunctions(connection, asyncContext);\n    }\n\n    private void exportFunctions(Connection connection, AsyncContext asyncContext) throws SQLException {\n        String sql =\"SELECT name,create_query from system.functions where origin='SQLUserDefined'\";\n        try(ResultSet resultSet=connection.createStatement().executeQuery(sql)){\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP FUNCTION IF EXISTS \").append(resultSet.getString(\"name\")).append(\";\")\n                        .append(\"\\n\")\n                        .append(resultSet.getString(\"create_query\")).append(\";\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportTablesOrViewsOrDictionaries(Connection connection,String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql =String.format(\"SELECT create_table_query, has_own_data,engine,name from system.`tables` WHERE `database`='%s'\", databaseName);\n        try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) {\n            while (resultSet.next()) {\n\n                String ddl = resultSet.getString(\"create_table_query\");\n                boolean dataFlag = resultSet.getInt(\"has_own_data\") == 1;\n                String tableType = resultSet.getString(\"engine\");\n                String tableOrViewName = resultSet.getString(\"name\");\n                if (Objects.equals(\"View\", tableType)) {\n                    StringBuilder sqlBuilder = new StringBuilder();\n                    sqlBuilder.append(\"DROP VIEW IF EXISTS \").append(databaseName).append(\".\").append(tableOrViewName)\n                            .append(\";\").append(\"\\n\").append(ddl).append(\";\").append(\"\\n\");\n                    asyncContext.write(sqlBuilder.toString());\n                } else if (Objects.equals(\"Dictionary\", tableType)) {\n                    StringBuilder sqlBuilder = new StringBuilder();\n                    sqlBuilder.append(\"DROP DICTIONARY IF EXISTS \").append(databaseName).append(\".\").append(tableOrViewName)\n                            .append(\";\").append(\"\\n\").append(ddl).append(\";\").append(\"\\n\");\n                    asyncContext.write(sqlBuilder.toString());\n                } else {\n                    StringBuilder sqlBuilder = new StringBuilder();\n                    sqlBuilder.append(\"DROP TABLE IF EXISTS \").append(databaseName).append(\".\").append(tableOrViewName)\n                            .append(\";\").append(\"\\n\").append(ddl).append(\";\").append(\"\\n\");\n                    asyncContext.write(sqlBuilder.toString());\n                    if (asyncContext.isContainsData() && dataFlag) {\n                        exportTableData(connection, databaseName,schemaName, tableOrViewName, asyncContext);\n                    }\n                }\n            }\n        }\n    }\n\n\n    @Override\n    public Connection getConnection(ConnectInfo connectInfo) {\n        String url = setDatabaseInJdbcUrl(connectInfo);\n        connectInfo.setUrl(url);\n\n        return super.getConnection(connectInfo);\n    }\n\n    private String setDatabaseInJdbcUrl(ConnectInfo connectInfo) {\n        String databaseName;\n        String url = connectInfo.getUrl();\n        if (StringUtils.isBlank((databaseName = connectInfo.getDatabaseName())) && StringUtils.isBlank((databaseName = connectInfo.getSchemaName()))) {\n            return url;\n        }\n\n        String connectAddress = connectInfo.getHost() + \":\" + connectInfo.getPort();\n        String[] addressSplit = url.split(connectAddress);\n        String connectParams = addressSplit.length == 2 ? addressSplit[1] : \"\";\n        if (connectParams.startsWith(\"/\")) {\n            // Remove / from connection parameters\n            connectParams = connectParams.substring(1);\n            if (connectParams.startsWith(databaseName)) {\n                // 删除连接参数中的数据库名\n                connectParams = connectParams.substring(databaseName.length());\n            } else {\n                // 是否有连接参数\n                int beginIndex = connectParams.indexOf(\"?\");\n                // 无连接参数直接设置 \"\"\n                if (beginIndex == -1) {\n                    connectParams = \"\";\n                } else {\n                    // 删除连接参数前的数据库名\n                    connectParams = connectParams.substring(beginIndex);\n                }\n            }\n        }\n        // Add database name\n        return addressSplit[0] + connectAddress + \"/\" + databaseName + connectParams;\n    }\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"DROP TABLE IF EXISTS \" + databaseName + \".\" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n\n    @Override\n    public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName,boolean copyData) throws SQLException {\n        String sql = \"CREATE TABLE \" + newTableName + \" AS \" + tableName + \"\";\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n        if(copyData){\n            sql = \"INSERT INTO \" + newTableName + \" SELECT * FROM \" + tableName;\n            SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/ClickHouseMetaData.java",
    "content": "package ai.chat2db.plugin.clickhouse;\n\nimport ai.chat2db.plugin.clickhouse.builder.ClickHouseSqlBuilder;\nimport ai.chat2db.plugin.clickhouse.type.ClickHouseColumnTypeEnum;\nimport ai.chat2db.plugin.clickhouse.type.ClickHouseEngineTypeEnum;\nimport ai.chat2db.plugin.clickhouse.type.ClickHouseIndexTypeEnum;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport jakarta.validation.constraints.NotEmpty;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport static ai.chat2db.spi.util.SortUtils.sortDatabase;\n\npublic class ClickHouseMetaData extends DefaultMetaService implements MetaData {\n\n\n    private static String ROUTINES_SQL\n            =\n            \"SELECT SPECIFIC_NAME, ROUTINE_COMMENT, ROUTINE_DEFINITION FROM information_schema.routines WHERE \"\n                    + \"routine_type = '%s' AND ROUTINE_SCHEMA ='%s'  AND \"\n                    + \"routine_name = '%s';\";\n    private static String TRIGGER_SQL\n            = \"SELECT TRIGGER_NAME,EVENT_MANIPULATION, ACTION_STATEMENT  FROM INFORMATION_SCHEMA.TRIGGERS where \"\n            + \"TRIGGER_SCHEMA = '%s' AND TRIGGER_NAME = '%s';\";\n    private static String TRIGGER_SQL_LIST\n            = \"SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS where TRIGGER_SCHEMA = '%s';\";\n    private static String SELECT_TABLE_COLUMNS = \"select * from `system`.columns where table ='%s' and database='%s';\";\n    private static String VIEW_SQL\n            = \"SELECT create_table_query from system.`tables` WHERE `database`='%s' and name='%s'\";\n    private List<String> systemDatabases = Arrays.asList(\"information_schema\", \"system\");\n    public static final String FUNCTION_SQL = \"SELECT name,create_query as ddl from system.functions where origin='SQLUserDefined'\";\n\n    public static String format(String tableName) {\n        return \"`\" + tableName + \"`\";\n    }\n\n    @Override\n    public List<Function> functions(Connection connection, String databaseName, String schemaName) {\n        return SQLExecutor.getInstance().execute(connection, FUNCTION_SQL, resultSet -> {\n            List<Function> functions = new ArrayList<>();\n            while (resultSet.next()) {\n                Function function = new Function();\n                function.setFunctionName(resultSet.getString(\"name\"));\n                functions.add(function);\n            }\n            return functions;\n        });\n    }\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        List<Database> list = SQLExecutor.getInstance().execute(connection, \"SELECT name FROM system.databases;\", resultSet -> {\n            List<Database> databases = new ArrayList<>();\n            try {\n                while (resultSet.next()) {\n                    String dbName = resultSet.getString(\"name\");\n                    Database database = new Database();\n                    database.setName(dbName);\n                    databases.add(database);\n                }\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            }\n            return databases;\n        });\n        return sortDatabase(list, systemDatabases, connection);\n    }\n\n    @Override\n    public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           @NotEmpty String tableName) {\n        String sql = \"SHOW CREATE TABLE \" + format(schemaName) + \".\"\n                + format(tableName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            if (resultSet.next()) {\n                return resultSet.getString(\"Create Table\");\n            }\n            return null;\n        });\n    }\n\n    @Override\n    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,\n                             String functionName) {\n        return SQLExecutor.getInstance().execute(connection, FUNCTION_SQL, resultSet -> {\n            Function function = new Function();\n            function.setDatabaseName(databaseName);\n            function.setSchemaName(schemaName);\n            function.setFunctionName(functionName);\n            if (resultSet.next()) {\n/*                function.setSpecificName(resultSet.getString(\"SPECIFIC_NAME\"));\n                function.setRemarks(resultSet.getString(\"ROUTINE_COMMENT\"));*/\n                function.setFunctionBody(resultSet.getString(\"ddl\"));\n            }\n            return function;\n        });\n\n    }\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        List<Trigger> triggers = new ArrayList<>();\n        String sql = String.format(TRIGGER_SQL_LIST, schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Trigger trigger = new Trigger();\n                trigger.setTriggerName(resultSet.getString(\"TRIGGER_NAME\"));\n                trigger.setSchemaName(schemaName);\n                trigger.setDatabaseName(databaseName);\n                triggers.add(trigger);\n            }\n            return triggers;\n        });\n    }\n\n    @Override\n    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           String triggerName) {\n\n        String sql = String.format(TRIGGER_SQL, databaseName, triggerName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Trigger trigger = new Trigger();\n            trigger.setDatabaseName(databaseName);\n            trigger.setSchemaName(schemaName);\n            trigger.setTriggerName(triggerName);\n            if (resultSet.next()) {\n                trigger.setTriggerBody(resultSet.getString(\"ACTION_STATEMENT\"));\n            }\n            return trigger;\n        });\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n                               String procedureName) {\n        String sql = String.format(ROUTINES_SQL, \"PROCEDURE\", schemaName, procedureName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Procedure procedure = new Procedure();\n            procedure.setDatabaseName(databaseName);\n            procedure.setSchemaName(schemaName);\n            procedure.setProcedureName(procedureName);\n            if (resultSet.next()) {\n                procedure.setSpecificName(resultSet.getString(\"SPECIFIC_NAME\"));\n                procedure.setRemarks(resultSet.getString(\"ROUTINE_COMMENT\"));\n                procedure.setProcedureBody(resultSet.getString(\"ROUTINE_DEFINITION\"));\n            }\n            return procedure;\n        });\n    }\n\n    @Override\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = String.format(SELECT_TABLE_COLUMNS, tableName, databaseName);\n        List<TableColumn> tableColumns = new ArrayList<>();\n\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                TableColumn column = new TableColumn();\n                column.setDatabaseName(databaseName);\n                column.setTableName(tableName);\n                column.setOldName(resultSet.getString(\"name\"));\n                column.setName(resultSet.getString(\"name\"));\n                String dataType = resultSet.getString(\"type\");\n                if (dataType.startsWith(\"Nullable(\")) {\n                    dataType = dataType.substring(9, dataType.length() - 1);\n                    column.setNullable(1);\n                }\n                column.setColumnType(dataType);\n                column.setDefaultValue(resultSet.getString(\"default_expression\"));\n//                column.setAutoIncrement(resultSet.getString(\"EXTRA\").contains(\"auto_increment\"));\n                column.setComment(resultSet.getString(\"comment\"));\n                column.setOrdinalPosition(resultSet.getInt(\"position\"));\n                column.setDecimalDigits(resultSet.getInt(\"numeric_scale\"));\n                /*column.setCharSetName(resultSet.getString(\"CHARACTER_SET_NAME\"));\n                column.setCollationName(resultSet.getString(\"COLLATION_NAME\"));*/\n                setColumnSize(column, dataType);\n                tableColumns.add(column);\n            }\n            return tableColumns;\n        });\n    }\n\n    private void setColumnSize(TableColumn column, String columnType) {\n        try {\n            if (columnType.contains(\"(\")) {\n                String size = columnType.substring(columnType.indexOf(\"(\") + 1, columnType.indexOf(\")\"));\n                if (\"SET\".equalsIgnoreCase(column.getColumnType()) || \"ENUM\".equalsIgnoreCase(column.getColumnType())) {\n                    column.setValue(size);\n                } else {\n                    if (size.contains(\",\")) {\n                        String[] sizes = size.split(\",\");\n                        if (StringUtils.isNotBlank(sizes[0])) {\n                            column.setColumnSize(Integer.parseInt(sizes[0]));\n                        }\n                        if (StringUtils.isNotBlank(sizes[1])) {\n                            column.setDecimalDigits(Integer.parseInt(sizes[1]));\n                        }\n                    } else {\n                        column.setColumnSize(Integer.parseInt(size));\n                    }\n                }\n            }\n        } catch (Exception e) {\n        }\n    }\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_SQL, databaseName, viewName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Table table = new Table();\n            table.setDatabaseName(databaseName);\n            table.setSchemaName(schemaName);\n            table.setName(viewName);\n            if (resultSet.next()) {\n                table.setDdl(resultSet.getString(\"create_table_query\"));\n            }\n            return table;\n        });\n    }\n\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        StringBuilder queryBuf = new StringBuilder(\"SHOW INDEX FROM \");\n        queryBuf.append(\"`\").append(schemaName).append(\"`\");\n        queryBuf.append(\" FROM \");\n        queryBuf.append(\"`\").append(databaseName).append(\"`\");\n        return SQLExecutor.getInstance().execute(connection, queryBuf.toString(), resultSet -> {\n            LinkedHashMap<String, TableIndex> map = new LinkedHashMap();\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"Key_name\");\n\n                TableIndex index = new TableIndex();\n                index.setDatabaseName(databaseName);\n                index.setSchemaName(schemaName);\n                index.setTableName(tableName);\n                index.setName(keyName);\n                index.setUnique(!resultSet.getBoolean(\"Non_unique\"));\n                index.setType(resultSet.getString(\"Index_type\"));\n//                    index.setComment(resultSet.getString(\"Index_comment\"));\n                List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n                tableIndexColumns.addAll(getTableIndexColumn(resultSet));\n                index.setColumnList(tableIndexColumns);\n                if (\"PRIMARY\".equalsIgnoreCase(keyName)) {\n                    index.setType(ClickHouseIndexTypeEnum.PRIMARY.getName());\n                }\n                map.put(keyName, index);\n            }\n            return map.values().stream().collect(Collectors.toList());\n        });\n\n    }\n\n    private List<TableIndexColumn> getTableIndexColumn(ResultSet resultSet) throws SQLException {\n        List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n        String name = StringUtils.isBlank(resultSet.getString(\"column_name\")) ? resultSet.getString(\"expression\") : resultSet.getString(\"column_name\");\n        if (StringUtils.isNotBlank(name)) {\n            String[] split = name.split(\",\");\n            for (String columName : split) {\n                TableIndexColumn tableIndexColumn = new TableIndexColumn();\n                tableIndexColumn.setColumnName(columName);\n                tableIndexColumn.setOrdinalPosition(resultSet.getShort(\"seq_in_index\"));\n                tableIndexColumn.setCollation(resultSet.getString(\"collation\"));\n                tableIndexColumn.setCardinality(resultSet.getLong(\"cardinality\"));\n                tableIndexColumn.setSubPart(resultSet.getLong(\"sub_part\"));\n                tableIndexColumns.add(tableIndexColumn);\n            }\n        }\n        return tableIndexColumns;\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new ClickHouseSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(ClickHouseColumnTypeEnum.getTypes())\n                .engineTypes(ClickHouseEngineTypeEnum.getTypes())\n                .indexTypes(ClickHouseIndexTypeEnum.getIndexTypes())\n                .build();\n    }\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"`\" + name + \"`\").collect(Collectors.joining(\".\"));\n\n    }\n\n\n    @Override\n    public List<String> getSystemDatabases() {\n        return systemDatabases;\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/ClickHousePlugin.java",
    "content": "package ai.chat2db.plugin.clickhouse;\n\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class ClickHousePlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"clickhouse.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new ClickHouseMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new ClickHouseDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/builder/ClickHouseSqlBuilder.java",
    "content": "package ai.chat2db.plugin.clickhouse.builder;\n\nimport ai.chat2db.plugin.clickhouse.type.ClickHouseColumnTypeEnum;\nimport ai.chat2db.plugin.clickhouse.type.ClickHouseIndexTypeEnum;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.List;\n\n\npublic class ClickHouseSqlBuilder extends DefaultSqlBuilder {\n    @Override\n    public String buildCreateTableSql(Table table) {\n        // Initialize StringBuilder to build the SQL script\n        StringBuilder script = new StringBuilder(\"CREATE TABLE \");\n        \n        // Append the database name, if present\n        appendDatabaseName(script, table.getDatabaseName());\n        \n        // Append the table name\n        script.append(\"`\").append(table.getName()).append(\"`\").append(\" (\").append(\"\\n\");\n\n        // append column\n        appendColumns(script, table.getColumnList());\n\n        // append index\n        appendIndexes(script, table.getIndexList());\n\n        // Remove the last comma\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n)\");\n\n        // Append the engine, if present\n       appendEngine(script, table.getEngine());\n\n       // append primary key\n       appendPrimaryKey(script, table.getIndexList());\n       \n        // Append the comment, if present\n        appendComment(script, table.getComment());\n\n         // Append a semicolon to complete the SQL statement\n        script.append(\";\");\n       \n        // Return the complete SQL script\n        return script.toString();\n    }\n\n    // Method to append the database name to the SQL script\n    private void appendDatabaseName(StringBuilder script, String databaseName) {\n        if (StringUtils.isNotBlank(databaseName)) {\n            script.append(\"`\").append(databaseName).append(\"`.\");\n        }\n    }\n\n    // Method to append columns to the SQL script\n    private void appendColumns(StringBuilder script, List<TableColumn> columns) {\n        for (TableColumn column : columns) {\n            // Check if column name and type are not blank\n            if (StringUtils.isNotBlank(column.getName()) && StringUtils.isNotBlank(column.getColumnType())) {\n                // Get the column type enum and append the column SQL to the script\n                ClickHouseColumnTypeEnum typeEnum = ClickHouseColumnTypeEnum.getByType(column.getColumnType());\n                script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n            }\n        }\n    }\n\n    // Method to append indexes to the SQL script\n    private void appendIndexes(StringBuilder script, List<TableIndex> indexes) {\n        for (TableIndex index : indexes) {\n            // Check if index name and type are not blank\n            if (StringUtils.isNotBlank(index.getName()) && StringUtils.isNotBlank(index.getType())) {\n                // Get the index type enum and append the index script to the script\n                ClickHouseIndexTypeEnum indexTypeEnum = ClickHouseIndexTypeEnum.getByType(index.getType());\n                if (!ClickHouseIndexTypeEnum.PRIMARY.equals(indexTypeEnum)) {\n                    script.append(\"\\t\").append(indexTypeEnum.buildIndexScript(index)).append(\",\\n\");\n                }\n            }\n        }\n    }\n\n    // Method to append the engine to the SQL script\n    private void appendEngine(StringBuilder script, String engine) {\n        if (StringUtils.isNotBlank(engine)) {\n            script.append(\" ENGINE=\").append(engine).append(\"\\n\");\n        }\n    }\n\n    // Method to append the primary key to the SQL script\n    private void appendPrimaryKey(StringBuilder script, List<TableIndex> indexes) {\n        for (TableIndex index : indexes) {\n            // Check if index name and type are not blank\n            if (StringUtils.isNotBlank(index.getName()) && StringUtils.isNotBlank(index.getType())) {\n                // Get the index type enum and append the index script to the script\n                ClickHouseIndexTypeEnum indexTypeEnum = ClickHouseIndexTypeEnum.getByType(index.getType());\n                if (ClickHouseIndexTypeEnum.PRIMARY.equals(indexTypeEnum)) {\n                    script.append(\"\\t\").append(indexTypeEnum.buildIndexScript(index)).append(\"\\n\");\n                }\n            }\n        }\n    }\n\n    // Method to append the comment to the SQL script\n    private void appendComment(StringBuilder script, String comment) {\n        if (StringUtils.isNotBlank(comment)) {\n            script.append(\" COMMENT '\").append(comment).append(\"'\");\n        }\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"ALTER TABLE \");\n        if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {\n            script.append(\"`\").append(oldTable.getDatabaseName()).append(\"`\").append(\".\");\n        }\n        script.append(\"`\").append(oldTable.getName()).append(\"`\").append(\"\\n\");\n\n        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {\n            script.append(\"\\t\").append(\"MODIFY COMMENT\").append(\"'\").append(newTable.getComment()).append(\"'\").append(\",\\n\");\n        }\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if (StringUtils.isNotBlank(tableColumn.getEditStatus()) && StringUtils.isNotBlank(tableColumn.getColumnType()) && StringUtils.isNotBlank(tableColumn.getName())) {\n                ClickHouseColumnTypeEnum typeEnum = ClickHouseColumnTypeEnum.getByType(tableColumn.getColumnType());\n                if(typeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\",\\n\");\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                ClickHouseIndexTypeEnum clickHouseIndexTypeEnum = ClickHouseIndexTypeEnum\n                        .getByType(tableIndex.getType());\n                if(clickHouseIndexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(clickHouseIndexTypeEnum.buildModifyIndex(tableIndex)).append(\",\\n\");\n            }\n        }\n\n        if (script.length() > 2) {\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            script.append(\";\");\n        }\n\n        return script.toString();\n    }\n\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);\n        sqlBuilder.append(sql);\n        if (offset == 0) {\n            sqlBuilder.append(\"\\n LIMIT \");\n            sqlBuilder.append(pageSize);\n        } else {\n            sqlBuilder.append(\"\\n LIMIT \");\n            sqlBuilder.append(offset);\n            sqlBuilder.append(\",\");\n            sqlBuilder.append(pageSize);\n        }\n        return sqlBuilder.toString();\n    }\n\n\n    @Override\n    public String buildCreateDatabaseSql(Database database) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE DATABASE `\" + database.getName() + \"`\");\n        if(StringUtils.isNotBlank(database.getComment())){\n            sqlBuilder.append(\";ALTER DATABASE \").append(database.getName()).append(\" COMMENT '\").append(database.getComment()).append(\"';\");\n        }\n        return sqlBuilder.toString();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/clickhouse.json",
    "content": "{\n  \"dbType\": \"CLICKHOUSE\",\n  \"supportDatabase\": false,\n  \"supportSchema\":true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:clickhouse://localhost:8123/\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/clickhouse-jdbc-0.3.2-patch8-http.jar\"\n      ],\n      \"jdbcDriver\": \"clickhouse-jdbc-0.3.2-patch8-http.jar\",\n      \"jdbcDriverClass\": \"com.clickhouse.jdbc.ClickHouseDriver\"\n    }\n  ],\n  \"name\": \"ClickHouse\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/type/ClickHouseColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.clickhouse.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum ClickHouseColumnTypeEnum implements ColumnBuilder {\n\n    String(\"String\", false, false, true, false, false, false, true, true, false, false),\n    Int8(\"Int8\", false, false, true, false, false, false, true, true, false, false),\n    Int16(\"Int16\", false, false, true, false, false, false, true, true, false, false),\n    Int32(\"Int32\", false, false, true, false, false, false, true, true, false, false),\n    Int64(\"Int64\", false, false, true, false, false, false, true, true, false, false),\n    Int128(\"Int128\", false, false, true, false, false, false, true, true, false, false),\n    Int256(\"Int256\", false, false, true, false, false, false, true, true, false, false),\n    UInt8(\"UInt8\", false, false, true, false, false, false, true, true, false, false),\n    UInt16(\"UInt16\", false, false, true, false, false, false, true, true, false, false),\n    UInt32(\"UInt32\", false, false, true, false, false, false, true, true, false, false),\n    UInt64(\"UInt64\", false, false, true, false, false, false, true, true, false, false),\n    UInt128(\"UInt128\", false, false, true, false, false, false, true, true, false, false),\n    UInt256(\"UInt256\", false, false, true, false, false, false, true, true, false, false),\n    Float32(\"Float32\", false, false, true, false, false, false, true, true, false, false),\n    Float64(\"Float64\", false, false, true, false, false, false, true, true, false, false),\n    Decimal(\"Decimal\", true, true, true, false, false, false, true, true, false, false),\n    Boolean(\"Boolean\", false, false, true, false, false, false, true, true, false, false),\n    FixedString(\"FixedString\", false, false, true, false, false, false, true, true, false, false),\n    UUID(\"UUID\", false, false, true, false, false, false, true, true, false, false),\n    Date(\"Date\", false, false, true, false, false, false, true, true, false, false),\n    DATE32(\"DATE32\", false, false, true, false, false, false, true, true, false, false),\n    DateTime(\"DateTime\", false, false, true, false, false, false, true, true, false, false),\n    DateTime64(\"DateTime64\", false, false, true, false, false, false, true, true, false, false),\n    Enum8(\"Enum8\", false, false, true, false, false, false, true, true, false, false),\n    Enum16(\"Enum16\", false, false, true, false, false, false, true, true, false, false),\n    Array(\"Array\", false, false, false, false, false, false, true, true, false, false),\n    JSON(\"JSON\", false, false, true, false, false, false, true, true, false, false),\n    Nested(\"Nested\", false, false, true, false, false, false, true, true, false, false),\n    Map(\"Map\", true, true, true, false, false, false, true, true, false, false),\n    IPv4(\"IPv4\", false, false, true, false, false, false, true, true, false, false),\n    IPv6(\"IPv6\", false, false, true, false, false, false, true, true, false, false),\n    Point(\"Point\", false, false, true, false, false, false, true, true, false, false),\n    Ring(\"Ring\", false, false, true, false, false, false, true, true, false, false),\n    Polygon(\"Polygon\", false, false, true, false, false, false, true, true, false, false),\n    MultiPolygon(\"MultiPolygon\", false, false, true, false, false, false, true, true, false, false),\n    AggregateFunction(\"AggregateFunction\", true, true, true, false, false, false, true, true, false, false),\n    SimpleAggregateFunction(\"SimpleAggregateFunction\", true, true, true, false, false, false, true, true, false, false),\n    ;\n    private static Map<String, ClickHouseColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (ClickHouseColumnTypeEnum value : ClickHouseColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n    private ColumnType columnType;\n\n\n    ClickHouseColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportValue) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, supportValue, false);\n    }\n\n    public static ClickHouseColumnTypeEnum getByType(String dataType) {\n       return COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n    }\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(ClickHouseColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        ClickHouseColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n\n        script.append(buildNullableAndDataType(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        script.append(buildComment(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\"DROP COLUMN `\", tableColumn.getName() + \"`\");\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\"ADD COLUMN \", buildCreateColumnSql(tableColumn));\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            String modifyColumn = \"\";\n            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n                modifyColumn = StringUtils.join(\"RENAME COLUMN `\", tableColumn.getOldName(), \"` TO `\", tableColumn.getName(),\n                        \"`, \", buildCreateColumnSql(tableColumn));\n            }\n            return StringUtils.join(modifyColumn, \"MODIFY COLUMN \", buildCreateColumnSql(tableColumn));\n        }\n        return \"\";\n    }\n\n    private String buildComment(TableColumn column, ClickHouseColumnTypeEnum type) {\n        if (!type.columnType.isSupportComments() || StringUtils.isEmpty(column.getComment())) {\n            return \"\";\n        }\n        return StringUtils.join(\"COMMENT '\", column.getComment(), \"'\");\n    }\n\n    private String buildDefaultValue(TableColumn column, ClickHouseColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {\n            return \"\";\n        }\n\n        if (\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if (\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        if (Arrays.asList(Enum8,Enum16).contains(type)) {\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        if (Arrays.asList(Date).contains(type)) {\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        if (Arrays.asList(DateTime).contains(type)) {\n            if (\"CURRENT_TIMESTAMP\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n                return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n            }\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildNullableAndDataType(TableColumn column, ClickHouseColumnTypeEnum type) {\n        StringBuilder script = new StringBuilder();\n        script.append(buildDataType(column, type));\n\n        if (!type.getColumnType().isSupportNullable()) {\n            return script.toString();\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"Nullable(\"+script.append(\")\").toString();\n        } else {\n            return script.toString();\n        }\n    }\n\n    private String buildDataType(TableColumn column, ClickHouseColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(FixedString).contains(type)) {\n            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n        }\n\n\n        if (Arrays.asList(Decimal).contains(type)) {\n            if (column.getColumnSize() == null || column.getDecimalDigits() == null) {\n                return columnType;\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \")\");\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n            }\n        }\n\n        return columnType;\n\n\n    }\n\n    public String buildColumn(TableColumn column) {\n        ClickHouseColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n        script.append(buildDataType(column, type)).append(\" \");\n        if (StringUtils.isNoneBlank(column.getComment())) {\n            script.append(\"COMMENT\").append(\" \").append(\"'\").append(column.getComment()).append(\"'\").append(\" \");\n        }\n        return script.toString();\n    }\n\n    private String unsignedDataType(String dataTypeName, String middle) {\n        String[] split = dataTypeName.split(\" \");\n        if (split.length == 2) {\n            return StringUtils.join(split[0], middle, split[1]);\n        }\n        return StringUtils.join(dataTypeName, middle);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/type/ClickHouseEngineTypeEnum.java",
    "content": "package ai.chat2db.plugin.clickhouse.type;\n\nimport ai.chat2db.spi.model.EngineType;\nimport com.google.common.collect.Maps;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum ClickHouseEngineTypeEnum {\n    AzureBlobStorage(\"AzureBlobStorage\",false,true,false,false,true,false,false,false                                                ),\n    KeeperMap(\"KeeperMap\",false,true,false,false,false,true,false,false                                                               ),\n    SQLite(\"SQLite\",false,false,false,false,false,false,false,false                                                                ),\n    ExternalDistributed(\"ExternalDistributed\",false,false,false,false,false,false,false,false                                       ),\n    PostgreSQL(\"PostgreSQL\",false,false,false,false,false,false,false,false                                                        ),\n    NATS(\"NATS\",false,false,false,false,true,false,false,false                                                                       ),\n    RabbitMQ(\"RabbitMQ\",false,false,false,false,true,false,false,false                                                               ),\n    Kafka(\"Kafka\",false,false,false,false,true,false,false,false                                                                      ),\n    MongoDB(\"MongoDB\",false,false,false,false,false,false,false,false                                                               ),\n    FileLog(\"FileLog\",false,false,false,false,true,false,false,false                                                                ),\n    Dictionary(\"Dictionary\",false,false,false,false,false,false,false,false                                                        ),\n    MySQL(\"MySQL\",false,false,false,false,true,false,false,false                                                                      ),\n    S3Queue(\"S3Queue\",false,false,false,false,true,false,false,false                                                                ),\n    HDFS(\"HDFS\",false,true,false,false,false,false,false,false                                                                       ),\n    MaterializedPostgreSQL(\"MaterializedPostgreSQL\",false,true,false,false,true,false,false,false                                  ),\n    S3(\"S3\",false,true,false,false,true,false,false,false                                                                          ),\n    FuzzJSON(\"FuzzJSON\",false,false,false,false,false,false,false,false                                                              ),\n    OSS(\"OSS\",false,true,false,false,true,false,false,false                                                                         ),\n    WindowView(\"WindowView\",false,false,false,false,false,false,false,false                                                        ),\n    Distributed(\"Distributed\",false,false,false,false,true,true,false,false                                                         ),\n    ReplicatedSummingMergeTree(\"ReplicatedSummingMergeTree\",true,true,true,true,true,true,true,true                                ),\n    ExecutablePool(\"ExecutablePool\",false,false,false,false,true,false,false,false                                                 ),\n    COSN(\"COSN\",false,true,false,false,true,false,false,false                                                                        ),\n    Iceberg(\"Iceberg\",false,false,false,false,false,false,false,false                                                               ),\n    MaterializedView(\"MaterializedView\",false,false,false,false,false,false,false,false                                              ),\n    View(\"View\",false,false,false,false,false,false,false,false                                                                      ),\n    JDBC(\"JDBC\",false,false,false,false,false,false,false,false                                                                      ),\n    Join(\"Join\",false,false,false,false,true,false,false,false                                                                       ),\n    Executable(\"Executable\",false,false,false,false,true,false,false,false                                                         ),\n    Set(\"Set\",false,false,false,false,true,false,false,false                                                                        ),\n    Redis(\"Redis\",false,true,false,false,false,true,false,false                                                                       ),\n    GenerateRandom(\"GenerateRandom\",false,false,false,false,false,false,false,false                                                ),\n    LiveView(\"LiveView\",false,false,false,false,false,false,false,false                                                              ),\n    MergeTree(\"MergeTree\",true,true,true,false,true,true,true,false                                                                   ),\n    ReplicatedReplacingMergeTree(\"ReplicatedReplacingMergeTree\",true,true,true,true,true,true,true,true                              ),\n    Memory(\"Memory\",false,false,false,false,true,true,false,false                                                                  ),\n    Buffer(\"Buffer\",false,false,false,false,false,true,false,false                                                                 ),\n    URL(\"URL\",false,false,false,false,true,false,false,false                                                                        ),\n    ReplicatedVersionedCollapsingMergeTree(\"ReplicatedVersionedCollapsingMergeTree\",true,true,true,true,true,true,true,true        ),\n    VersionedCollapsingMergeTree(\"VersionedCollapsingMergeTree\",true,true,true,false,true,true,true,false                            ),\n    Hive(\"Hive\",false,true,false,false,true,false,false,false                                                                        ),\n    ReplacingMergeTree(\"ReplacingMergeTree\",true,true,true,false,true,true,true,false                                              ),\n    ReplicatedAggregatingMergeTree(\"ReplicatedAggregatingMergeTree\",true,true,true,true,true,true,true,true                        ),\n    ReplicatedMergeTree(\"ReplicatedMergeTree\",true,true,true,true,true,true,true,true                                               ),\n    DeltaLake(\"DeltaLake\",false,false,false,false,false,false,false,false                                                             ),\n    EmbeddedRocksDB(\"EmbeddedRocksDB\",true,true,false,false,false,true,false,false                                                  ),\n    ReplicatedCollapsingMergeTree(\"ReplicatedCollapsingMergeTree\",true,true,true,true,true,true,true,true                             ),\n    File(\"File\",false,false,false,false,true,false,false,false                                                                       ),\n    TinyLog(\"TinyLog\",false,false,false,false,true,false,false,false                                                                ),\n    ReplicatedGraphiteMergeTree(\"ReplicatedGraphiteMergeTree\",true,true,true,true,true,true,true,true                               ),\n    SummingMergeTree(\"SummingMergeTree\",true,true,true,false,true,true,true,false                                                    ),\n    Hudi(\"Hudi\",false,false,false,false,false,false,false,false                                                                      ),\n    GraphiteMergeTree(\"GraphiteMergeTree\",true,true,true,false,true,true,true,false                                                   ),\n    CollapsingMergeTree(\"CollapsingMergeTree\",true,true,true,false,true,true,true,false                                             ),\n    Merge(\"Merge\",false,false,false,false,false,false,false,false                                                                     ),\n    AggregatingMergeTree(\"AggregatingMergeTree\",true,true,true,false,true,true,true,false                                            ),\n    ODBC(\"ODBC\",false,false,false,false,false,false,false,false                                                                      ),\n    Null(\"Null\",false,false,false,false,false,true,false,false                                                                       ),\n    StripeLog(\"StripeLog\",false,false,false,false,true,false,false,false                                                              ),\n    Log(\"Log\",false,false,false,false,true,false,false,false                                                                        ),\n\n    ;\n    private static Map<String, ClickHouseEngineTypeEnum> ENGINE_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (ClickHouseEngineTypeEnum value : ClickHouseEngineTypeEnum.values()) {\n            ENGINE_TYPE_MAP.put(value.getEngineType().getName(), value);\n        }\n    }\n\n    private EngineType engineType;\n\n\n    ClickHouseEngineTypeEnum(String name, boolean supportTTL, boolean supportSortOrder, boolean supportSkippingIndices, boolean supportDeduplication, boolean supportSettings, boolean supportParallelInsert, boolean supportProjections, boolean supportReplication) {\n        this.engineType = new EngineType(name, supportTTL, supportSortOrder, supportSkippingIndices, supportDeduplication, supportSettings, supportParallelInsert, supportProjections, supportReplication);\n    }\n\n    public static ClickHouseEngineTypeEnum getByType(String dataType) {\n        return ENGINE_TYPE_MAP.get(dataType.toUpperCase());\n    }\n\n    public static List<EngineType> getTypes() {\n        return Arrays.stream(ClickHouseEngineTypeEnum.values()).map(engineTypeEnum ->\n                engineTypeEnum.getEngineType()\n        ).toList();\n    }\n\n    public EngineType getEngineType() {\n        return engineType;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/type/ClickHouseIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.clickhouse.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum ClickHouseIndexTypeEnum {\n\n    PRIMARY(\"Primary\", \"PRIMARY KEY\"),\n    MINMAX(\"MINMAX\", \"INDEX\"),\n    SET(\"SET\", \"INDEX\"),\n    BLOOM_FILTER(\"BLOOM_FILTER\", \"INDEX\"),\n    TOKENBF_V1(\"TOKENBF_V1\", \"INDEX\"),\n    NGRAMBF_V1(\"NGRAMBF_V1\", \"INDEX\"),\n    INVERTED(\"INVERTED\", \"INDEX\"),\n    HYPOTHESIS(\"HYPOTHESIS\", \"INDEX\"),\n    ANNOY(\"ANNOY\", \"INDEX\"),\n    USEARCH(\"USEARCH\", \"INDEX\"),\n\n    ;\n\n    private String name;\n    private String keyword;\n    private IndexType indexType;\n\n    ClickHouseIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n    public static ClickHouseIndexTypeEnum getByType(String type) {\n        for (ClickHouseIndexTypeEnum value : ClickHouseIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(ClickHouseIndexTypeEnum.values()).stream().map(ClickHouseIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(keyword).append(\" \");\n        script.append(buildIndexName(tableIndex)).append(\" \");\n        script.append(buildIndexColumn(tableIndex)).append(\" \");\n        script.append(buildIndexType(tableIndex)).append(\" \");\n        return script.toString();\n    }\n\n    private String buildIndexType(TableIndex tableIndex) {\n        if (this.equals(PRIMARY)) {\n            return \"\";\n        } else {\n            return \"TYPE \" + name ;\n        }\n    }\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"`\").append(column.getColumnName()).append(\"`\");\n                script.append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        if (this.equals(PRIMARY)) {\n            return \"\";\n        } else {\n            return \"`\" + tableIndex.getName() + \"`\";\n        }\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (this.equals(PRIMARY)) {\n            return \"\";\n        }\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\"DROP INDEX `\", tableIndex.getOldName(), \"`\");\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\"DROP INDEX `\", tableIndex.getOldName(),\n                    \"`,\\n ADD \", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\"ADD \", buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.clickhouse.ClickHousePlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n    <artifactId>chat2db-db2</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/DB2DBManage.java",
    "content": "package ai.chat2db.plugin.db2;\n\nimport ai.chat2db.plugin.db2.constant.SQLConstant;\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n@Slf4j\npublic class DB2DBManage extends DefaultDBManage implements DBManage {\n\n    private static String PROCEDURE_SQL = \"SELECT COUNT(*) AS procedure_count\\n\" +\n            \"FROM SYSCAT.PROCEDURES\\n\" +\n            \"WHERE PROCNAME = '%s';\";\n\n    @Override\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        exportTables(connection, databaseName, schemaName, asyncContext);\n        exportViews(connection, schemaName, asyncContext);\n        exportProceduresAndFunctions(connection, schemaName, asyncContext);\n        exportTriggers(connection, schemaName, asyncContext);\n    }\n\n    private void exportTables(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getTables(null, schemaName, null, new String[]{\"TABLE\", \"SYSTEM TABLE\"})) {\n            while (resultSet.next()) {\n                exportTable(connection, databaseName, schemaName, resultSet.getString(\"TABLE_NAME\"), asyncContext);\n            }\n        }\n    }\n\n\n    public void exportTable(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {\n        try {\n            SQLExecutor.getInstance().execute(connection, SQLConstant.TABLE_DDL_FUNCTION_SQL, resultSet -> null);\n        } catch (Exception e) {\n            //log.error(\"Failed to create function\", e);\n        }\n        String sql = String.format(\"select %s.GENERATE_TABLE_DDL('%s', '%s') as sql from %s;\", schemaName, schemaName, tableName, tableName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"sql\")).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n                if (asyncContext.isContainsData()) {\n                    exportTableData(connection, databaseName, schemaName, tableName, asyncContext);\n                }\n            }\n        }\n    }\n\n\n    private void exportViews(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"select TEXT from syscat.views where VIEWSCHEMA='%s';\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                String ddl = resultSet.getString(\"TEXT\");\n                sqlBuilder.append(ddl).append(\";\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportProceduresAndFunctions(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"select TEXT from syscat.routines where ROUTINESCHEMA='%s';\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                String ddl = resultSet.getString(\"TEXT\");\n                sqlBuilder.append(ddl).append(\";\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n\n    private void exportTriggers(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"select * from SYSCAT.TRIGGERS where TRIGSCHEMA = '%s';\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                String ddl = resultSet.getString(\"TEXT\");\n                sqlBuilder.append(ddl).append(\";\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            boolean isCreateOrReplace = procedureBody.trim().toUpperCase().startsWith(\"CREATE OR REPLACE \");\n\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, schemaName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String checkProcedureSQL = String.format(PROCEDURE_SQL, procedure.getProcedureName().toUpperCase());\n            String finalProcedureBody = procedureBody;\n            SQLExecutor.getInstance().execute(connection, checkProcedureSQL, resultSet -> {\n                if (resultSet.next()) {\n                    int count = resultSet.getInt(1);\n                    if (count >= 1) {\n                        if (isCreateOrReplace) {\n                            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet2 -> {});\n                        } else {\n                            throw new SQLException(\"Procedure with the same name already exists.\");\n                        }\n                    }\n                }\n            });\n            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet -> {});\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n        if (ObjectUtils.anyNull(connectInfo) || StringUtils.isEmpty(connectInfo.getSchemaName())) {\n            return;\n        }\n        String schemaName = connectInfo.getSchemaName();\n        try {\n            SQLExecutor.getInstance().execute(connection, \"SET SCHEMA \\\"\" + schemaName + \"\\\"\");\n        } catch (SQLException e) {\n\n        }\n    }\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"DROP TABLE \" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName, boolean copyData) throws SQLException {\n        String sql = \"CREATE TABLE \" + newTableName + \" LIKE \" + tableName + \" INCLUDING INDEXES\";\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n        if (copyData) {\n            sql = \"INSERT INTO \" + newTableName + \" SELECT * FROM \" + tableName;\n            SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n        }\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/DB2MetaData.java",
    "content": "package ai.chat2db.plugin.db2;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport ai.chat2db.plugin.db2.builder.DB2SqlBuilder;\nimport ai.chat2db.plugin.db2.constant.SQLConstant;\nimport ai.chat2db.plugin.db2.type.DB2ColumnTypeEnum;\nimport ai.chat2db.plugin.db2.type.DB2DefaultValueEnum;\nimport ai.chat2db.plugin.db2.type.DB2IndexTypeEnum;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.SortUtils;\nimport com.google.common.collect.Lists;\nimport org.apache.commons.lang3.StringUtils;\n\npublic class DB2MetaData extends DefaultMetaService implements MetaData {\n\n    private List<String> systemSchemas = Arrays.asList(\"NULLID\",\"SQLJ\",\"SYSCAT\",\"SYSFUN\",\"SYSIBM\",\"SYSIBMADM\",\"SYSIBMINTERNAL\",\"SYSIBMTS\",\"SYSPROC\",\"SYSPUBLIC\",\"SYSSTAT\",\"SYSTOOLS\");\n    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);\n        return SortUtils.sortSchema(schemas, systemSchemas);\n    }\n\n\n    @Override\n    public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {\n        try {\n            SQLExecutor.getInstance().execute(connection, SQLConstant.TABLE_DDL_FUNCTION_SQL, resultSet -> null);\n        } catch (Exception e) {\n            //log.error(\"Failed to create function\", e);\n        }\n        String ddlSql = String.format(\"select %s.GENERATE_TABLE_DDL('%s', '%s') as sql from %s;\",schemaName,schemaName,tableName,tableName);\n        return SQLExecutor.getInstance().execute(connection, ddlSql, resultSet -> {\n            try {\n                if (resultSet.next()) {\n                    return resultSet.getString(\"sql\");\n                }\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            }\n            return null;\n        });\n    }\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new DB2SqlBuilder();\n    }\n\n    private static String IDX_SQL = \"SELECT i.INDNAME, i.UNIQUERULE, i.REMARKS, ic.COLNAME, ic.COLSEQ, ic.COLORDER FROM SYSCAT.INDEXES i JOIN SYSCAT.INDEXCOLUSE ic ON i.INDNAME = ic.INDNAME AND i.INDSCHEMA = ic.INDSCHEMA WHERE i.TABNAME = '%s' AND i.INDSCHEMA = '%s' ORDER BY i.INDNAME, ic.COLSEQ\";\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = String.format(IDX_SQL, tableName, schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            LinkedHashMap<String, TableIndex> map = new LinkedHashMap();\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"INDNAME\");\n                TableIndex tableIndex = map.get(keyName);\n                if (tableIndex != null) {\n                    List<TableIndexColumn> columnList = tableIndex.getColumnList();\n                    columnList.add(getTableIndexColumn(resultSet));\n                    columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition))\n                            .collect(Collectors.toList());\n                    tableIndex.setColumnList(columnList);\n                } else {\n                    TableIndex index = new TableIndex();\n                    index.setDatabaseName(databaseName);\n                    index.setSchemaName(schemaName);\n                    index.setTableName(tableName);\n                    index.setName(keyName);\n                    index.setComment(resultSet.getString(\"REMARKS\"));\n                    List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n                    tableIndexColumns.add(getTableIndexColumn(resultSet));\n                    index.setColumnList(tableIndexColumns);\n                    String uniquerule = resultSet.getString(\"UNIQUERULE\");\n                    if(\"P\".equalsIgnoreCase(uniquerule)) {\n                        index.setType(DB2IndexTypeEnum.PRIMARY_KEY.getName());\n                        index.setUnique(true);\n                    }else if(\"U\".equalsIgnoreCase(uniquerule)){\n                        index.setType(DB2IndexTypeEnum.UNIQUE.getName());\n                        index.setUnique(true);\n                    }else {\n                        index.setType(DB2IndexTypeEnum.NORMAL.getName());\n                        index.setUnique(false);\n                    }\n                    map.put(keyName, index);\n                }\n            }\n            return map.values().stream().collect(Collectors.toList());\n        });\n\n    }\n\n    private static String VIEW_DDL_SQL=\"select TEXT from syscat.views where VIEWSCHEMA='%s' and VIEWNAME='%s';\";\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_DDL_SQL, schemaName, viewName);\n        Table table = new Table();\n        table.setDatabaseName(databaseName);\n        table.setSchemaName(schemaName);\n        table.setName(viewName);\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            if (resultSet.next()) {\n                table.setDdl(resultSet.getString(\"TEXT\")+\";\");\n            }\n        });\n        return table;\n    }\n\n    private static String ROUTINE_DDL_SQL=\"select TEXT from syscat.routines where ROUTINESCHEMA='%s' and ROUTINENAME='%s' and ROUTINETYPE='%s';\";\n\n    @Override\n    public Function function(Connection connection, String databaseName, String schemaName, String functionName) {\n        Function function = new Function();\n       function.setDatabaseName(databaseName);\n       function.setSchemaName(schemaName);\n       function.setFunctionName(functionName);\n        String sql = String.format(ROUTINE_DDL_SQL, schemaName, functionName,'F');\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            if (resultSet.next()) {\n                function.setFunctionBody(resultSet.getString(\"TEXT\")+\";\");\n            }\n        });\n        return function;\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, String databaseName, String schemaName, String procedureName) {\n        Procedure procedure = new Procedure();\n        procedure.setDatabaseName(databaseName);\n        procedure.setSchemaName(schemaName);\n        procedure.setProcedureName(procedureName);\n        String sql = String.format(ROUTINE_DDL_SQL, schemaName, procedureName,'P');\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            if (resultSet.next()) {\n                procedure.setProcedureBody(resultSet.getString(\"TEXT\")+\";\");\n            }\n        });\n        return procedure;\n    }\n\n    private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException {\n        TableIndexColumn tableIndexColumn = new TableIndexColumn();\n        tableIndexColumn.setColumnName(resultSet.getString(\"COLNAME\"));\n        tableIndexColumn.setOrdinalPosition(resultSet.getShort(\"COLSEQ\"));\n//        tableIndexColumn.setCollation(resultSet.getString(\"Collation\"));\n//        tableIndexColumn.setCardinality(resultSet.getLong(\"Cardinality\"));\n//        tableIndexColumn.setSubPart(resultSet.getLong(\"Sub_part\"));\n        String collation = resultSet.getString(\"COLORDER\");\n        if (\"A\".equalsIgnoreCase(collation)) {\n            tableIndexColumn.setAscOrDesc(\"ASC\");\n        } else if (\"D\".equalsIgnoreCase(collation)) {\n            tableIndexColumn.setAscOrDesc(\"DESC\");\n        }\n        return tableIndexColumn;\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(DB2ColumnTypeEnum.getTypes())\n                .charsets(Lists.newArrayList())\n                .collations(Lists.newArrayList())\n                .indexTypes(DB2IndexTypeEnum.getIndexTypes())\n                .defaultValues(DB2DefaultValueEnum.getDefaultValues())\n                .build();\n    }\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"\\\"\" + name + \"\\\"\").collect(Collectors.joining(\".\"));\n    }\n\n    @Override\n    public List<String> getSystemSchemas() {\n        return systemSchemas;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/DB2Plugin.java",
    "content": "package ai.chat2db.plugin.db2;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class DB2Plugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"db2.json\", DBConfig.class);\n\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new DB2MetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new DB2DBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/builder/DB2SqlBuilder.java",
    "content": "package ai.chat2db.plugin.db2.builder;\n\nimport ai.chat2db.plugin.db2.type.DB2ColumnTypeEnum;\nimport ai.chat2db.plugin.db2.type.DB2IndexTypeEnum;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Schema;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport org.apache.commons.lang3.StringUtils;\n\npublic class DB2SqlBuilder extends DefaultSqlBuilder {\n\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"CREATE TABLE \").append(\"\\\"\").append(table.getSchemaName()).append(\"\\\".\\\"\").append(table.getName()).append(\"\\\" (\").append(\"\\n\");\n\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            DB2ColumnTypeEnum typeEnum = DB2ColumnTypeEnum.getByType(column.getColumnType());\n            if (typeEnum == null) {\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n);\");\n\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {\n                continue;\n            }\n            DB2IndexTypeEnum indexTypeEnum = DB2IndexTypeEnum.getByType(tableIndex.getType());\n            if (indexTypeEnum == null) {\n                continue;\n            }\n            script.append(\"\\n\").append(\"\").append(indexTypeEnum.buildIndexScript(tableIndex)).append(\";\");\n            if(StringUtils.isNotBlank(tableIndex.getComment())){\n                script.append(\"\\n\").append(indexTypeEnum.buildIndexComment(tableIndex)).append(\";\");\n            }\n\n        }\n\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType()) || StringUtils.isBlank(column.getComment())) {\n                continue;\n            }\n            script.append(\"\\n\").append(buildComment(column)).append(\";\");\n        }\n\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\"\\n\").append(buildTableComment(table)).append(\";\");\n        }\n\n\n        return script.toString();\n    }\n\n    private String buildTableComment(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"COMMENT ON TABLE \").append(\"\\\"\").append(table.getSchemaName()).append(\"\\\".\\\"\").append(table.getName()).append(\"\\\" IS '\").append(table.getComment()).append(\"'\");\n        return script.toString();\n    }\n\n    private String buildComment(TableColumn column) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"COMMENT ON COLUMN \").append(\"\\\"\").append(column.getSchemaName()).append(\"\\\".\\\"\").append(column.getTableName()).append(\"\\\".\\\"\").append(column.getName()).append(\"\\\" IS '\").append(column.getComment()).append(\"'\");\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(oldTable.getSchemaName()).append(\"\\\".\\\"\").append(oldTable.getName()).append(\"\\\"\");\n            script.append(\" \").append(\"RENAME TO \").append(\"\\\"\").append(newTable.getName()).append(\"\\\"\").append(\";\\n\");\n        }\n        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {\n            script.append(\"\").append(buildTableComment(newTable)).append(\";\\n\");\n        }\n\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if (StringUtils.isNotBlank(tableColumn.getEditStatus())) {\n                DB2ColumnTypeEnum typeEnum = DB2ColumnTypeEnum.getByType(tableColumn.getColumnType());\n                if (typeEnum == null) {\n                    continue;\n                }\n                script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\";\\n\");\n                if (StringUtils.isNotBlank(tableColumn.getComment())) {\n                    script.append(\"\\n\").append(buildComment(tableColumn)).append(\";\\n\");\n                }\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                DB2IndexTypeEnum mysqlIndexTypeEnum = DB2IndexTypeEnum.getByType(tableIndex.getType());\n                if (mysqlIndexTypeEnum == null) {\n                    continue;\n                }\n                script.append(\"\\t\").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(\";\\n\");\n                if(StringUtils.isNotBlank(tableIndex.getComment())) {\n                    script.append(\"\\n\").append(mysqlIndexTypeEnum.buildIndexComment(tableIndex)).append(\";\\n\");\n                }\n\n            }\n        }\n        if (script.length() > 2) {\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            script.append(\";\");\n        }\n\n        return script.toString();\n    }\n\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        int startRow = offset + 1;\n        int endRow = offset + pageSize;\n        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120);\n        sqlBuilder.append(\"SELECT * FROM (SELECT TMP_PAGE.*,ROWNUMBER() OVER() AS CAHT2DB_AUTO_ROW_ID FROM ( \\n\");\n        sqlBuilder.append(sql);\n        sqlBuilder.append(\"\\n ) AS TMP_PAGE) TMP_PAGE WHERE CAHT2DB_AUTO_ROW_ID BETWEEN \");\n        sqlBuilder.append(startRow);\n        sqlBuilder.append(\" AND \");\n        sqlBuilder.append(endRow);\n        return sqlBuilder.toString();\n    }\n\n    @Override\n    public String buildCreateSchemaSql(Schema schema) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE SCHEMA \\\"\" + schema.getName() + \"\\\";\");\n\n        if (StringUtils.isNotBlank(schema.getComment())) {\n            sqlBuilder.append(\"\\nCOMMENT ON SCHEMA \\\"\").append(schema.getName()).append(\"\\\" IS '\").append(schema.getComment()).append(\"';\");\n        }\n\n        return sqlBuilder.toString();\n    }\n\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/constant/SQLConstant.java",
    "content": "package ai.chat2db.plugin.db2.constant;\n\n/**\n * @author: zgq\n * @date: 2024年03月16日 10:11\n */\npublic class SQLConstant {\n\n    public static final String TABLE_DDL_FUNCTION_SQL\n            = \"\"\"\n              CREATE OR REPLACE FUNCTION generate_table_ddl(schema_name VARCHAR(128), table_name VARCHAR(128))\n                  RETURNS CLOB\n                  LANGUAGE SQL\n              BEGIN\n                  DECLARE ddl CLOB;\n\n                  -- 获取表的注释信息\n                  DECLARE table_remarks CLOB;\n                  SELECT REMARKS\n                  INTO table_remarks\n                  FROM SYSCAT.TABLES\n                  WHERE TABSCHEMA = schema_name\n                    AND TABNAME = table_name;\n\n                  -- 拼接表的创建语句\n                  SET ddl = 'CREATE TABLE ' || table_name || ' (';\n\n                  -- 获取表的字段信息并拼接到DDL语句中\n                  FOR col_info AS\n                      SELECT COLNAME, TYPENAME, LENGTH, SCALE, NULLS, DEFAULT as default, REMARKS\n                      FROM SYSCAT.COLUMNS\n                      WHERE TABSCHEMA = schema_name\n                        AND TABNAME = table_name\n                      ORDER BY COLNO\n                      DO\n                          SET ddl = ddl || col_info.COLNAME || ' ';\n                          IF col_info.TYPENAME = 'INTEGER' THEN\n                              SET ddl = ddl || col_info.TYPENAME;\n                          ELSE\n                              SET ddl = ddl || col_info.TYPENAME;\n                              IF col_info.LENGTH IS NOT NULL THEN\n                                  SET ddl = ddl || '(' || col_info.LENGTH;\n                                  IF col_info.TYPENAME != 'VARCHAR' AND col_info.SCALE IS NOT NULL THEN\n                                      SET ddl = ddl || ',' || col_info.SCALE;\n                                  END IF;\n                                  SET ddl = ddl || ')';\n                              END IF;\n                          END IF;\n                          IF col_info.NULLS = 'N' THEN\n                              SET ddl = ddl || ' NOT NULL';\n                          END IF;\n                          IF col_info.default IS NOT NULL THEN\n                              SET ddl = ddl || ' DEFAULT ' || col_info.default;\n                          END IF;\n                          SET ddl = ddl || ','; -- 添加字段定义结束符\n                      END FOR;\n\n                  -- 删除最后一个逗号\n                  SET ddl = LEFT(ddl, LENGTH(ddl) - 1);\n                  SET ddl = ddl || ');';\n\n                  -- 添加表的注释\n                  IF table_remarks IS NOT NULL THEN\n                      SET ddl = ddl || 'comment on table ' || table_name || ' is ''' || table_remarks || ''';';\n                  END IF;\n\n                  for column as\n                      SELECT COLNAME, REMARKS\n                      FROM SYSCAT.COLUMNS\n                      WHERE TABSCHEMA = schema_name\n                        AND TABNAME = table_name\n                      ORDER BY COLNO\n                      do\n                          if column.REMARKS is not null then\n                              set ddl = ddl || 'comment on column ' || table_name || '.' || column.COLNAME || ' is ''' ||\n                                        column.REMARKS || ''';';\n                          end if;\n                      end for;\n\n                  -- 获取表的索引信息并拼接到DDL语句中\n                  FOR index_info AS\n                      SELECT INDNAME, SUBSTR(COLNAMES, 2) AS COLNAMES, UNIQUERULE\n                      FROM SYSCAT.INDEXES\n                      WHERE TABSCHEMA = schema_name\n                        AND TABNAME = table_name\n                      DO\n                          IF index_info.UNIQUERULE = 'P' THEN\n                              SET ddl = ddl || ' ALTER TABLE ' || table_name || ' ADD PRIMARY KEY (' ||\n                                        index_info.COLNAMES || ');';\n                          ELSEIF index_info.UNIQUERULE = 'U' THEN\n                              SET ddl = ddl || ' CREATE UNIQUE INDEX ' || index_info.INDNAME || ' ON ' ||\n                                        table_name || ' (' || index_info.COLNAMES || ');';\n                          END IF;\n                      END FOR;\n\n                  RETURN ddl;\n              END;\"\"\";\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/db2.json",
    "content": "{\n  \"dbType\": \"DB2\",\n  \"supportDatabase\": false,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:db2://localhost:50000/\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/db2jcc4_4.26.14.jar\"\n      ],\n      \"jdbcDriver\": \"db2jcc4_4.26.14.jar\",\n      \"jdbcDriverClass\": \"com.ibm.db2.jcc.DB2Driver\"\n    }\n  ],\n  \"name\": \"DB2\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/type/DB2ColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.db2.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum DB2ColumnTypeEnum implements ColumnBuilder {\n\n    ANCHOR(\"ANCHOR\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BIGINT(\"BIGINT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BINARY(\"BINARY\", false, false, true, false, false, false, true, true, false, false),\n\n\n   // BIT(\"BIT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BLOB(\"BLOB\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BOOLEAN(\"BOOLEAN\", false, false, true, false, false, false, true, true, false, false),\n\n\n    CHAR(\"CHAR\", true, false, true, false, false, false, true, true, false, true),\n\n//    CHAR_VARYING(\"CHAR VARYING\", true, false, true, false, false, false, true, true, false, true),\n//\n    CHARACTER(\"CHARACTER\", true, false, true, false, false, false, true, true, false, true),\n//\n//    CHARACTER_VARYING(\"CHARACTER VARYING\", true, false, true, false, false, false, true, true, false, true),\n\n    CLOB(\"CLOB\", false, false, true, false, false, false, true, true, false, false),\n\n\n    COURSE(\"COURSE\", false, false, true, false, false, false, true, true, false, false),\n\n\n\n    DATE(\"DATE\", false, false, true, false, false, false, true, true, false, false),\n\n\n    DB2SECURITYLABEL(\"DB2SECURITYLABEL\", false, false, true, false, false, false, true, true, false, false),\n\n\n    DBCLOB(\"DBCLOB\", false, false, true, false, false, false, true, true, false, false),\n\n\n    DEC(\"DEC\", true, true, true, false, false, false, true, true, false, false),\n\n    DECFLOAT(\"DECFLOAT\", false, false, true, false, false, false, true, true, false, false),\n\n    DECIMAL(\"DECIMAL\", true, true, true, false, false, false, true, true, false, false),\n\n    DOUBLE(\"DOUBLE\", false, false, true, false, false, false, true, true, false, false),\n\n\n    FLOAT(\"FLOAT\", true, false, true, false, false, false, true, true, false, false),\n\n\n    GRAPHIC(\"GRAPHIC\", false, false, true, false, false, false, true, true, false, false),\n\n    INT(\"INT\", false, false, true, false, false, false, true, true, false, false),\n\n    INTEGER(\"INTEGER\", false, false, true, false, false, false, true, true, false, false),\n\n\n    LONG(\"LONG\", false, false, true, false, false, false, true, true, false, false),\n\n\n    NCHAR(\"NCHAR\", true, false, true, false, false, false, true, true, false, true),\n\n    NCLOB(\"NCLOB\", false, false, true, false, false, false, true, true, false, false),\n\n   // LONGVARBINARY(\"LONGVARBINARY\", false, false, true, false, false, false, true, true, false, false),\n\n\n   // LONGVARCHAR(\"LONGVARCHAR\", true, false, true, false, false, false, true, true, false, false),\n\n    NUM(\"NUM\", true, true, true, false, false, false, true, true, false, false),\n\n    NUMBERIC(\"NUMBERIC\", true, true, true, false, false, false, true, true, false, false),\n\n\n    NVARCHAR(\"NVARCHAR\", true, false, true, false, false, false, true, true, false, true),\n\n\n    REAL(\"REAL\", false, false, true, false, false, false, true, true, false, false),\n\n\n    REF(\"REF\", false, false, true, false, false, false, true, true, false, false),\n\n\n\n\n    SMALLINT(\"SMALLINT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    TIME(\"TIME\", false, false, true, false, false, false, true, true, false, false),\n\n\n   // TIME_WITH_TIME_ZONE(\"TIME WITH TIME ZONE\", false, false, true, false, false, false, true, true, false, false),\n\n\n    TIMESTAMP(\"TIMESTAMP\", false, false, true, false, false, false, true, true, false, false),\n\n\n   // TIMESTAMP_WITH_TIME_ZONE(\"TIMESTAMP WITH TIME ZONE\", false, false, true, false, false, false, true, true, false, false),\n\n\n   // TINYINT(\"TINYINT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    VARBINARY(\"VARBINARY\", false, false, true, false, false, false, true, true, false, false),\n\n\n    VARCHAR(\"VARCHAR\", true, false, true, false, false, false, true, true, false, true),\n\n\n    //VARCHAR2(\"VARCHAR2\", true, false, true, false, false, false, true, true, false, true),\n\n    XML(\"XML\", false, false, true, false, false, false, true, true, false, false),\n    ;\n    private ColumnType columnType;\n\n    public static DB2ColumnTypeEnum getByType(String dataType) {\n       return COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n    }\n\n    private static Map<String, DB2ColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (DB2ColumnTypeEnum value : DB2ColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n\n    DB2ColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportUnit) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, false, supportUnit);\n    }\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        DB2ColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"\\\"\").append(column.getName()).append(\"\\\"\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        script.append(buildNullable(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n\n    private String buildNullable(TableColumn column, DB2ColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportNullable()) {\n            return \"\";\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"NULL\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDefaultValue(TableColumn column, DB2ColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {\n            return \"\";\n        }\n\n        if (\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if (\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildDataType(TableColumn column, DB2ColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(CHAR, VARCHAR,NCHAR,CHARACTER,NVARCHAR).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            script.append(columnType);\n            if (column.getColumnSize() != null && StringUtils.isEmpty(column.getUnit())) {\n                script.append(\"(\").append(column.getColumnSize()).append(\")\");\n            } else if (column.getColumnSize() != null && !StringUtils.isEmpty(column.getUnit())) {\n                script.append(\"(\").append(column.getColumnSize()).append(\" \").append(column.getUnit()).append(\")\");\n            }\n            return script.toString();\n        }\n\n        if (Arrays.asList(DEC,DECIMAL, FLOAT, NUM, TIMESTAMP, NUMBERIC).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            script.append(columnType);\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\")\");\n            } else if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\",\").append(column.getDecimalDigits()).append(\")\");\n            }\n            return script.toString();\n        }\n\n        return columnType;\n    }\n\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"DROP COLUMN \").append(\"\\\"\").append(tableColumn.getName()).append(\"\\\"\");\n            return script.toString();\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"ADD (\").append(buildCreateColumnSql(tableColumn)).append(\")\");\n            return script.toString();\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"MODIFY (\").append(buildCreateColumnSql(tableColumn)).append(\") \\n\");\n\n            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n                script.append(\";\");\n                script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n                script.append(\" \").append(\"RENAME COLUMN \").append(\"\\\"\").append(tableColumn.getOldName()).append(\"\\\"\").append(\" TO \").append(\"\\\"\").append(tableColumn.getName()).append(\"\\\"\");\n\n            }\n            return script.toString();\n\n        }\n        return \"\";\n    }\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(DB2ColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/type/DB2DefaultValueEnum.java",
    "content": "package ai.chat2db.plugin.db2.type;\n\nimport ai.chat2db.spi.model.DefaultValue;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum DB2DefaultValueEnum {\n    EMPTY_STRING(\"EMPTY_STRING\"),\n    NULL(\"NULL\"),\n    ;\n    private DefaultValue defaultValue;\n\n    DB2DefaultValueEnum(String defaultValue) {\n        this.defaultValue = new DefaultValue(defaultValue);\n    }\n\n\n    public DefaultValue getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static List<DefaultValue> getDefaultValues() {\n        return Arrays.stream(DB2DefaultValueEnum.values()).map(DB2DefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/type/DB2IndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.db2.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum DB2IndexTypeEnum {\n\n    PRIMARY_KEY(\"Primary\", \"PRIMARY KEY\"),\n\n    NORMAL(\"Normal\", \"INDEX\"),\n\n    UNIQUE(\"Unique\", \"UNIQUE INDEX\");\n\n   // BITMAP(\"BITMAP\", \"BITMAP INDEX\");\n\n\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    private IndexType indexType;\n\n\n    public String getName() {\n        return name;\n    }\n\n    private String name;\n\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    private String keyword;\n\n    DB2IndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n\n    public static DB2IndexTypeEnum getByType(String type) {\n        for (DB2IndexTypeEnum value : DB2IndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        if (PRIMARY_KEY.equals(this)) {\n            script.append(\"ALTER TABLE \\\"\").append(tableIndex.getSchemaName()).append(\"\\\".\\\"\").append(tableIndex.getTableName()).append(\"\\\" ADD PRIMARY KEY \").append(buildIndexColumn(tableIndex));\n        } else {\n            if (UNIQUE.equals(this)) {\n                script.append(\"CREATE UNIQUE INDEX \");\n            } else {\n                script.append(\"CREATE INDEX \");\n            }\n            script.append(buildIndexName(tableIndex)).append(\" ON \\\"\").append(tableIndex.getSchemaName()).append(\"\\\".\\\"\").append(tableIndex.getTableName()).append(\"\\\" \").append(buildIndexColumn(tableIndex));\n        }\n\n        return script.toString();\n    }\n\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"\\\"\").append(column.getColumnName()).append(\"\\\"\");\n                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {\n                    script.append(\" \").append(column.getAscOrDesc());\n                }\n                script.append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    public String buildIndexComment(TableIndex tableIndex) {\n        if (StringUtils.isBlank(tableIndex.getComment()) || EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return \"\";\n        } else if (NORMAL.equals(this) || UNIQUE.equals(this)) {\n            return StringUtils.join(\"COMMENT ON INDEX\", \" \",\n                    \"\\\"\", tableIndex.getName(), \"\\\" IS '\", tableIndex.getComment(), \"';\");\n        } else {\n            return StringUtils.join(\"COMMENT ON CONSTRAINT\", \" \\\"\", tableIndex.getName(), \"\\\" ON \\\"\", tableIndex.getSchemaName(),\n                    \"\\\".\\\"\", tableIndex.getTableName(), \"\\\" IS '\", tableIndex.getComment(), \"';\");\n        }\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        return \"\\\"\" + tableIndex.getSchemaName() + \"\\\".\" + \"\\\"\" + tableIndex.getName() + \"\\\"\";\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex), \";\\n\", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (DB2IndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {\n            String tableName = \"\\\"\" + tableIndex.getSchemaName() + \"\\\".\" + \"\\\"\" + tableIndex.getTableName() + \"\\\"\";\n            return StringUtils.join(\"ALTER TABLE \",tableName,\" DROP PRIMARY KEY\");\n        }\n        StringBuilder script = new StringBuilder();\n        script.append(\"DROP INDEX \");\n        script.append(buildIndexName(tableIndex));\n\n        return script.toString();\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(DB2IndexTypeEnum.values()).stream().map(DB2IndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-db2/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.db2.DB2Plugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n\n    <artifactId>chat2db-dm</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMDBManage.java",
    "content": "package ai.chat2db.plugin.dm;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\n@Slf4j\npublic class DMDBManage extends DefaultDBManage implements DBManage {\n    private String format(String tableName) {\n        return \"\\\"\" + tableName + \"\\\"\";\n    }\n\n    private static String ROUTINES_SQL\n            = \"SELECT OWNER, NAME, TEXT FROM ALL_SOURCE WHERE TYPE = '%s' AND OWNER = '%s' AND NAME = '%s' ORDER BY LINE\";\n    private static String TRIGGER_SQL_LIST = \"SELECT OWNER, TRIGGER_NAME FROM ALL_TRIGGERS WHERE OWNER = '%s'\";\n\n    private static String TRIGGER_SQL\n            = \"SELECT OWNER, TRIGGER_NAME, TABLE_OWNER, TABLE_NAME, TRIGGERING_TYPE, TRIGGERING_EVENT, STATUS, TRIGGER_BODY \"\n            + \"FROM ALL_TRIGGERS WHERE OWNER = '%s' AND TRIGGER_NAME = '%s'\";\n\n    private static String PROCEDURE_SQL = \"SELECT COUNT(*)\\n\" +\n            \"FROM DBA_OBJECTS\\n\" +\n            \"WHERE OBJECT_TYPE = 'PROCEDURE' \\n\" +\n            \"AND OWNER = '%s' \\n\" +\n            \"AND OBJECT_NAME = '%s'\";\n\n    @Override\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        exportTables(connection, databaseName, schemaName, asyncContext);\n        exportViews(connection, schemaName, asyncContext);\n        exportProcedures(connection, schemaName, asyncContext);\n        exportTriggers(connection, schemaName, asyncContext);\n    }\n\n    private void exportTables(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT TABLE_NAME FROM ALL_TABLES where OWNER='%s' and TABLESPACE_NAME='MAIN'\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String tableName = resultSet.getString(\"TABLE_NAME\");\n                exportTable(connection, databaseName, tableName, schemaName, asyncContext);\n            }\n        }\n    }\n\n\n    public void exportTable(Connection connection, String databaseName, String tableName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = \"\"\"\n                SELECT\n                    (SELECT comments FROM user_tab_comments WHERE table_name = '%s') AS comments,\n                    (SELECT dbms_metadata.get_ddl('TABLE', '%s', '%s') FROM dual) AS ddl\n                FROM dual;\n                \"\"\";\n        try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(String.format(sql, tableName, tableName, schemaName))) {\n            String formatSchemaName = format(schemaName);\n            String formatTableName = format(tableName);\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP TABLE IF EXISTS \").append(formatSchemaName).append(\".\").append(formatTableName)\n                        .append(\";\").append(\"\\n\")\n                        .append(resultSet.getString(\"ddl\")).append(\"\\n\");\n                String comment = resultSet.getString(\"comments\");\n                if (StringUtils.isNotBlank(comment)) {\n                    sqlBuilder.append(\"COMMENT ON TABLE \").append(formatSchemaName).append(\".\").append(formatTableName)\n                            .append(\" IS \").append(\"'\").append(comment).append(\"';\");\n                }\n                asyncContext.write(sqlBuilder.toString());\n                exportTableColumnComment(connection, schemaName, tableName, asyncContext);\n            }\n            if (asyncContext.isContainsData()) {\n                exportTableData(connection, databaseName, schemaName, tableName, asyncContext);\n            }\n        }\n    }\n\n    private void exportTableColumnComment(Connection connection, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"select COLNAME,COMMENT$ from SYS.SYSCOLUMNCOMMENTS\\n\" +\n                \"where SCHNAME = '%s' and TVNAME = '%s'and TABLE_TYPE = 'TABLE';\", schemaName, tableName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                String columnName = resultSet.getString(\"COLNAME\");\n                String comment = resultSet.getString(\"COMMENT$\");\n                sqlBuilder.append(\"COMMENT ON COLUMN \").append(format(schemaName)).append(\".\").append(format(tableName))\n                        .append(\".\").append(format(columnName)).append(\" IS \").append(\"'\").append(comment).append(\"';\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n\n    private void exportViews(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getTables(null, schemaName, null, new String[]{\"VIEW\"})) {\n            while (resultSet.next()) {\n                String viewName = resultSet.getString(\"TABLE_NAME\");\n                exportView(connection, viewName, schemaName, asyncContext);\n            }\n        }\n    }\n\n    private void exportView(Connection connection, String viewName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT DBMS_METADATA.GET_DDL('VIEW','%s','%s') as ddl FROM DUAL;\", viewName, schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"ddl\")).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportProcedures(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getProcedures(null, schemaName, null)) {\n            while (resultSet.next()) {\n                String procedureName = resultSet.getString(\"PROCEDURE_NAME\");\n                exportProcedure(connection, schemaName, procedureName, asyncContext);\n            }\n        }\n    }\n\n    private void exportProcedure(Connection connection, String schemaName, String procedureName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(ROUTINES_SQL, \"PROC\", schemaName, procedureName);\n        try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"TEXT\")).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportTriggers(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(TRIGGER_SQL_LIST, schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String triggerName = resultSet.getString(\"TRIGGER_NAME\");\n                exportTrigger(connection, schemaName, triggerName, asyncContext);\n            }\n        }\n    }\n\n    private void exportTrigger(Connection connection, String schemaName, String triggerName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(TRIGGER_SQL, schemaName, triggerName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"TRIGGER_BODY\")).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            boolean isCreateOrReplace = procedureBody.trim().toUpperCase().startsWith(\"CREATE OR REPLACE \");\n\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, schemaName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String checkProcedureSQL = String.format(PROCEDURE_SQL, schemaName.toUpperCase(),procedure.getProcedureName().toUpperCase());\n            String finalProcedureBody = procedureBody;\n            SQLExecutor.getInstance().execute(connection, checkProcedureSQL, resultSet -> {\n                if (resultSet.next()) {\n                    int count = resultSet.getInt(1);\n                    if (count >= 1) {\n                        if (isCreateOrReplace) {\n                            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet2 -> {});\n                        } else {\n                            throw new SQLException(\"Procedure with the same name already exists.\");\n                        }\n                    }\n                }\n            });\n            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet -> {});\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n        if (ObjectUtils.anyNull(connectInfo) || StringUtils.isEmpty(connectInfo.getSchemaName())) {\n            return;\n        }\n        String schemaName = connectInfo.getSchemaName();\n        try {\n            SQLExecutor.getInstance().execute(connection, \"SET SCHEMA \\\"\" + schemaName + \"\\\"\");\n        } catch (SQLException e) {\n            log.error(\"connectDatabase error\", e);\n        }\n    }\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"DROP TABLE IF EXISTS \" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMMetaData.java",
    "content": "package ai.chat2db.plugin.dm;\n\nimport ai.chat2db.plugin.dm.builder.DMSqlBuilder;\nimport ai.chat2db.plugin.dm.type.DMColumnTypeEnum;\nimport ai.chat2db.plugin.dm.type.DMDefaultValueEnum;\nimport ai.chat2db.plugin.dm.type.DMIndexTypeEnum;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.SortUtils;\nimport com.google.common.collect.Lists;\nimport jakarta.validation.constraints.NotEmpty;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n@Slf4j\npublic class DMMetaData extends DefaultMetaService implements MetaData {\n\n    private List<String> systemSchemas = Arrays.asList(\"CTISYS\", \"SYS\", \"SYSDBA\", \"SYSSSO\", \"SYSAUDITOR\");\n\n    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);\n        return SortUtils.sortSchema(schemas, systemSchemas);\n    }\n\n    private String format(String tableName) {\n        return \"\\\"\" + tableName + \"\\\"\";\n    }\n\n    private static String tableDDL = \"SELECT dbms_metadata.get_ddl('TABLE', '%s','%s') as ddl FROM dual ;\";\n\n    public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {\n        String tableDDLSql = String.format(tableDDL, tableName, schemaName);\n        StringBuilder ddlBuilder = new StringBuilder();\n        SQLExecutor.getInstance().execute(connection, tableDDLSql, resultSet -> {\n            if (resultSet.next()) {\n                String ddl = resultSet.getString(\"ddl\");\n                ddlBuilder.append(ddl).append(\"\\n\");\n            }\n        });\n        MetaData metaData = Chat2DBContext.getMetaData();\n        List<Table> tables = metaData.tables(connection, databaseName, schemaName, tableName);\n        if (CollectionUtils.isNotEmpty(tables)) {\n            String tableComment = tables.get(0).getComment();\n            if (StringUtils.isNotBlank(tableComment)) {\n                ddlBuilder.append(\"COMMENT ON TABLE \").append(format(schemaName)).append(\".\").append(format(tableName))\n                        .append(\" IS '\").append(tableComment.replace(\"'\", \"''\")).append(\"'\").append(\";\").append(\"\\n\");\n            }\n        }\n        List<TableColumn> columns = metaData.columns(connection, databaseName, schemaName, tableName);\n        if (CollectionUtils.isNotEmpty(columns)) {\n            for (TableColumn column : columns) {\n                String columnName = column.getName();\n                String comment = column.getComment();\n                if (StringUtils.isNotBlank(comment)) {\n                    ddlBuilder.append(\"COMMENT ON COLUMN \").append(format(schemaName)).append(\".\").append(format(tableName))\n                            .append(\".\").append(format(columnName)).append(\" IS \")\n                            .append(\"'\").append(comment.replace(\"'\", \"''\"))\n                            .append(\"';\").append(\"\\n\");\n                }\n            }\n        }\n        if (tableName.startsWith(\"V$\")){\n            return ddlBuilder.toString();\n        }\n        List<TableIndex> indexes = metaData.indexes(connection, databaseName, schemaName, tableName);\n        if (CollectionUtils.isNotEmpty(indexes)) {\n            for (TableIndex index : indexes) {\n                String indexName = index.getName();\n                if (StringUtils.isNotBlank(indexName)) {\n                    String sql = \"select DBMS_METADATA.GET_DDL('INDEX','%s') as INDEX_DDL\";\n                    try {\n                        SQLExecutor.getInstance().execute(connection, String.format(sql,indexName), resultSet -> {\n                            if (resultSet.next()) {\n                                ddlBuilder.append(resultSet.getString(\"INDEX_DDL\")).append(\"\\n\");\n                            }\n                        });\n                    } catch (Exception e) {\n                        log.warn(\"Failed to get the DDL of the index.\");\n                        for (TableIndex tableIndex : indexes) {\n                            DMIndexTypeEnum indexTypeEnum = DMIndexTypeEnum.getByType(tableIndex.getType());\n                            ddlBuilder.append(\"\\n\").append(indexTypeEnum.buildIndexScript(tableIndex)).append(\";\");\n                        }\n                    }\n                }\n            }\n        }\n        return ddlBuilder.toString();\n    }\n\n    private static String ROUTINES_SQL\n            = \"SELECT OWNER, NAME, TEXT FROM ALL_SOURCE WHERE TYPE = '%s' AND OWNER = '%s' AND NAME = '%s' ORDER BY LINE\";\n\n    @Override\n    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,\n                             String functionName) {\n\n        String sql = String.format(ROUTINES_SQL, \"PROC\", schemaName, functionName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            StringBuilder sb = new StringBuilder();\n            while (resultSet.next()) {\n                sb.append(resultSet.getString(\"TEXT\") + \"\\n\");\n            }\n            Function function = new Function();\n            function.setDatabaseName(databaseName);\n            function.setSchemaName(schemaName);\n            function.setFunctionName(functionName);\n            function.setFunctionBody(sb.toString());\n            return function;\n\n        });\n\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n                               String procedureName) {\n        String sql = String.format(ROUTINES_SQL, \"PROC\", schemaName, procedureName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            StringBuilder sb = new StringBuilder();\n            while (resultSet.next()) {\n                sb.append(resultSet.getString(\"TEXT\") + \"\\n\");\n            }\n            Procedure procedure = new Procedure();\n            procedure.setDatabaseName(databaseName);\n            procedure.setSchemaName(schemaName);\n            procedure.setProcedureName(procedureName);\n            procedure.setProcedureBody(sb.toString());\n            return procedure;\n        });\n    }\n\n    private static String TRIGGER_SQL\n            = \"SELECT OWNER, TRIGGER_NAME, TABLE_OWNER, TABLE_NAME, TRIGGERING_TYPE, TRIGGERING_EVENT, STATUS, TRIGGER_BODY \"\n            + \"FROM ALL_TRIGGERS WHERE OWNER = '%s' AND TRIGGER_NAME = '%s'\";\n\n    private static String TRIGGER_SQL_LIST = \"SELECT OWNER, TRIGGER_NAME FROM ALL_TRIGGERS WHERE OWNER = '%s'\";\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        List<Trigger> triggers = new ArrayList<>();\n        String sql = String.format(TRIGGER_SQL_LIST, schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Trigger trigger = new Trigger();\n                trigger.setTriggerName(resultSet.getString(\"TRIGGER_NAME\"));\n                trigger.setSchemaName(schemaName);\n                trigger.setDatabaseName(databaseName);\n                triggers.add(trigger);\n            }\n            return triggers;\n        });\n    }\n\n    @Override\n    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           String triggerName) {\n\n        String sql = String.format(TRIGGER_SQL, schemaName, triggerName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Trigger trigger = new Trigger();\n            trigger.setDatabaseName(databaseName);\n            trigger.setSchemaName(schemaName);\n            trigger.setTriggerName(triggerName);\n            if (resultSet.next()) {\n                trigger.setTriggerBody(resultSet.getString(\"TRIGGER_BODY\"));\n            }\n            return trigger;\n        });\n    }\n\n    private static String VIEW_SQL\n            = \"SELECT OWNER, VIEW_NAME, TEXT FROM ALL_VIEWS WHERE OWNER = '%s' AND VIEW_NAME = '%s'\";\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_SQL, schemaName, viewName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Table table = new Table();\n            table.setDatabaseName(databaseName);\n            table.setSchemaName(schemaName);\n            table.setName(viewName);\n            if (resultSet.next()) {\n                table.setDdl(resultSet.getString(\"TEXT\"));\n            }\n            return table;\n        });\n    }\n\n    private static String INDEX_SQL = \"SELECT i.TABLE_NAME, i.INDEX_TYPE, i.INDEX_NAME, i.UNIQUENESS ,c.COLUMN_NAME, c.COLUMN_POSITION, c.DESCEND, cons.CONSTRAINT_TYPE FROM ALL_INDEXES i JOIN ALL_IND_COLUMNS c ON i.INDEX_NAME = c.INDEX_NAME AND i.TABLE_NAME = c.TABLE_NAME AND i.TABLE_OWNER = c.TABLE_OWNER LEFT JOIN ALL_CONSTRAINTS cons ON i.INDEX_NAME = cons.INDEX_NAME AND i.TABLE_NAME = cons.TABLE_NAME AND i.TABLE_OWNER = cons.OWNER WHERE i.TABLE_OWNER = '%s' AND i.TABLE_NAME = '%s' ORDER BY i.INDEX_NAME, c.COLUMN_POSITION;\";\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = String.format(INDEX_SQL, schemaName, tableName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            LinkedHashMap<String, TableIndex> map = new LinkedHashMap();\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"INDEX_NAME\");\n                TableIndex tableIndex = map.get(keyName);\n                if (tableIndex != null) {\n                    List<TableIndexColumn> columnList = tableIndex.getColumnList();\n                    columnList.add(getTableIndexColumn(resultSet));\n                    columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition))\n                            .collect(Collectors.toList());\n                    tableIndex.setColumnList(columnList);\n                } else {\n                    TableIndex index = new TableIndex();\n                    index.setDatabaseName(databaseName);\n                    index.setSchemaName(schemaName);\n                    index.setTableName(tableName);\n                    index.setName(keyName);\n                    index.setUnique(\"UNIQUE\".equalsIgnoreCase(resultSet.getString(\"UNIQUENESS\")));\n//                    index.setType(resultSet.getString(\"Index_type\"));\n//                    index.setComment(resultSet.getString(\"Index_comment\"));\n                    List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n                    tableIndexColumns.add(getTableIndexColumn(resultSet));\n                    index.setColumnList(tableIndexColumns);\n                    if (\"P\".equalsIgnoreCase(resultSet.getString(\"CONSTRAINT_TYPE\"))) {\n                        index.setType(DMIndexTypeEnum.PRIMARY_KEY.getName());\n                    } else if (index.getUnique()) {\n                        index.setType(DMIndexTypeEnum.UNIQUE.getName());\n                    } else if (\"BITMAP\".equalsIgnoreCase(resultSet.getString(\"INDEX_TYPE\"))) {\n                        index.setType(DMIndexTypeEnum.BITMAP.getName());\n                    } else {\n                        index.setType(DMIndexTypeEnum.NORMAL.getName());\n                    }\n                    map.put(keyName, index);\n                }\n            }\n            return map.values().stream().collect(Collectors.toList());\n        });\n\n    }\n\n    private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException {\n        TableIndexColumn tableIndexColumn = new TableIndexColumn();\n        tableIndexColumn.setColumnName(resultSet.getString(\"COLUMN_NAME\"));\n        tableIndexColumn.setOrdinalPosition(resultSet.getShort(\"COLUMN_POSITION\"));\n//        tableIndexColumn.setCollation(resultSet.getString(\"Collation\"));\n//        tableIndexColumn.setCardinality(resultSet.getLong(\"Cardinality\"));\n//        tableIndexColumn.setSubPart(resultSet.getLong(\"Sub_part\"));\n        String collation = resultSet.getString(\"DESCEND\");\n        if (\"ASC\".equalsIgnoreCase(collation)) {\n            tableIndexColumn.setAscOrDesc(\"ASC\");\n        } else if (\"DESC\".equalsIgnoreCase(collation)) {\n            tableIndexColumn.setAscOrDesc(\"DESC\");\n        }\n        return tableIndexColumn;\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new DMSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(DMColumnTypeEnum.getTypes())\n                .charsets(Lists.newArrayList())\n                .collations(Lists.newArrayList())\n                .indexTypes(DMIndexTypeEnum.getIndexTypes())\n                .defaultValues(DMDefaultValueEnum.getDefaultValues())\n                .build();\n    }\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"\\\"\" + name + \"\\\"\").collect(Collectors.joining(\".\"));\n    }\n\n\n    @Override\n    public List<String> getSystemSchemas() {\n        return systemSchemas;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMPlugin.java",
    "content": "package ai.chat2db.plugin.dm;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class DMPlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"dm.json\", DBConfig.class);\n\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new DMMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new DMDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/builder/DMSqlBuilder.java",
    "content": "package ai.chat2db.plugin.dm.builder;\n\nimport ai.chat2db.plugin.dm.type.DMColumnTypeEnum;\nimport ai.chat2db.plugin.dm.type.DMIndexTypeEnum;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Schema;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Objects;\n\npublic class DMSqlBuilder  extends DefaultSqlBuilder {\n\n\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"CREATE TABLE \").append(\"\\\"\").append(table.getSchemaName()).append(\"\\\".\\\"\").append(table.getName()).append(\"\\\" (\").append(\"\\n\");\n\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            DMColumnTypeEnum typeEnum = DMColumnTypeEnum.getByType(column.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n);\");\n\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {\n                continue;\n            }\n            DMIndexTypeEnum indexTypeEnum = DMIndexTypeEnum.getByType(tableIndex.getType());\n            if(indexTypeEnum == null){\n                continue;\n            }\n            script.append(\"\\n\").append(\"\").append(indexTypeEnum.buildIndexScript(tableIndex)).append(\";\");\n        }\n\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType()) || StringUtils.isBlank(column.getComment())) {\n                continue;\n            }\n            script.append(\"\\n\").append(buildComment(column)).append(\";\");\n        }\n\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\"\\n\").append(buildTableComment(table)).append(\";\");\n        }\n\n\n        return script.toString();\n    }\n\n    private String buildTableComment(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"COMMENT ON TABLE \").append(\"\\\"\").append(table.getSchemaName()).append(\"\\\".\\\"\").append(table.getName()).append(\"\\\" IS '\").append(table.getComment()).append(\"'\");\n        return script.toString();\n    }\n\n    private String buildComment(TableColumn column) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"COMMENT ON COLUMN \").append(\"\\\"\").append(column.getSchemaName()).append(\"\\\".\\\"\").append(column.getTableName()).append(\"\\\".\\\"\").append(column.getName()).append(\"\\\" IS '\").append(column.getComment()).append(\"'\");\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(oldTable.getSchemaName()).append(\"\\\".\\\"\").append(oldTable.getName()).append(\"\\\"\");\n            script.append(\" \").append(\"RENAME TO \").append(\"\\\"\").append(newTable.getName()).append(\"\\\"\").append(\";\\n\");\n        }\n        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {\n            script.append(\"\").append(buildTableComment(newTable)).append(\";\\n\");\n        }\n\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            String editStatus = tableColumn.getEditStatus();\n            if (StringUtils.isNotBlank(editStatus)) {\n                DMColumnTypeEnum typeEnum = DMColumnTypeEnum.getByType(tableColumn.getColumnType());\n                if(typeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\";\\n\");\n                if (StringUtils.isNotBlank(tableColumn.getComment())&&!Objects.equals(EditStatus.DELETE.toString(),editStatus)) {\n                    script.append(\"\\n\").append(buildComment(tableColumn)).append(\";\\n\");\n                }\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                DMIndexTypeEnum mysqlIndexTypeEnum = DMIndexTypeEnum.getByType(tableIndex.getType());\n                if(mysqlIndexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(\";\\n\");\n            }\n        }\n        if (script.length() > 2) {\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            script.append(\";\");\n        }\n\n        return script.toString();\n    }\n\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        StringBuilder sqlStr = new StringBuilder(sql.length() + 17);\n        sqlStr.append(sql);\n        if (offset == 0) {\n            sqlStr.append(\" LIMIT \");\n            sqlStr.append(pageSize);\n        } else {\n            sqlStr.append(\" LIMIT \");\n            sqlStr.append(pageSize);\n            sqlStr.append(\" OFFSET \");\n            sqlStr.append(offset);\n        }\n        return sqlStr.toString();\n    }\n\n    @Override\n    public String buildCreateSchemaSql(Schema schema) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE SCHEMA \\\"\"+schema.getName()+\"\\\"\");\n        if(StringUtils.isNotBlank(schema.getOwner())){\n            sqlBuilder.append(\" AUTHORIZATION \").append(schema.getOwner());\n        }\n\n        return sqlBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/dm.json",
    "content": "{\n  \"dbType\": \"DM\",\n  \"supportDatabase\": false,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:dm://localhost:5236/\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/DmJdbcDriver18-8.1.2.141.jar\"\n      ],\n      \"jdbcDriver\": \"DmJdbcDriver18-8.1.2.141.jar\",\n      \"jdbcDriverClass\": \"dm.jdbc.driver.DmDriver\"\n    }\n  ],\n  \"name\": \"DM\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/type/DMColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.dm.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum DMColumnTypeEnum implements ColumnBuilder {\n\n\n    BFILE(\"BFILE\", false, false, true, false, false, false, true, true, false, false),\n\n    BIGINT(\"BIGINT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BINARY(\"BINARY\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BIT(\"BIT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BLOB(\"BLOB\", false, false, true, false, false, false, true, true, false, false),\n\n\n    CHAR(\"CHAR\", true, false, true, false, false, false, true, true, false, true),\n\n//    CHAR_VARYING(\"CHAR VARYING\", true, false, true, false, false, false, true, true, false, true),\n//\n//    CHARACTER(\"CHARACTER\", true, false, true, false, false, false, true, true, false, true),\n//\n//    CHARACTER_VARYING(\"CHARACTER VARYING\", true, false, true, false, false, false, true, true, false, true),\n\n    CLOB(\"CLOB\", false, false, true, false, false, false, true, true, false, false),\n\n    DATE(\"DATE\", false, false, true, false, false, false, true, true, false, false),\n\n    DECIMAL(\"DECIMAL\", true, true, true, false, false, false, true, true, false, false),\n    DEC(\"DEC\", true, true, true, false, false, false, true, true, false, false),\n\n    DOUBLE(\"DOUBLE\", false, false, true, false, false, false, true, true, false, false),\n\n\n    FLOAT(\"FLOAT\", true, false, true, false, false, false, true, true, false, false),\n\n    INT(\"INT\", false, false, true, true, false, false, true, true, false, false),\n\n    // INTEGER(\"INTEGER\", false, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_DAY(\"INTERVAL DAY\", false, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_DAY_TO_HOUR(\"INTERVAL DAY TO HOUR\", true, false, true, false, false, false, true, true, false, false),\n\n\n    INTERVAL_DAY_TO_MINUTE(\"INTERVAL DAY TO MINUTE\", true, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_DAY_TO_SECOND(\"INTERVAL DAY TO SECOND\", true, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_HOUR(\"INTERVAL HOUR\", false, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_HOUR_TO_MINUTE(\"INTERVAL HOUR TO MINUTE\", true, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_HOUR_TO_SECOND(\"INTERVAL HOUR TO SECOND\", true, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_MINUTE(\"INTERVAL MINUTE\", false, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_MINUTE_TO_SECOND(\"INTERVAL MINUTE TO SECOND\", true, false, true, false, false, false, true, true, false, false),\n\n\n    INTERVAL_MONTH(\"INTERVAL MONTH\", false, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_SECOND(\"INTERVAL SECOND\", false, false, true, false, false, false, true, true, false, false),\n\n\n    INTERVAL_YEAR(\"INTERVAL YEAR\", false, false, true, false, false, false, true, true, false, false),\n\n\n    INTERVAL_YEAR_TO_MONTH(\"INTERVAL YEAR TO MONTH\", true, false, true, false, false, false, true, true, false, false),\n\n    LONGVARBINARY(\"LONGVARBINARY\", false, false, true, false, false, false, true, true, false, false),\n\n\n    LONGVARCHAR(\"LONGVARCHAR\", false, false, true, false, false, false, true, true, false, false),\n    TEXT(\"TEXT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    NUMERIC(\"NUMERIC\", true, true, true, false, false, false, true, true, false, false),\n\n\n    NUMBER(\"NUMBER\", true, true, true, false, false, false, true, true, false, false),\n\n    REAL(\"REAL\", false, false, true, false, false, false, true, true, false, false),\n\n\n    SMALLINT(\"SMALLINT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    TIME(\"TIME\", false, false, true, false, false, false, true, true, false, false),\n\n\n    TIME_WITH_TIME_ZONE(\"TIME WITH TIME ZONE\", false, false, true, false, false, false, true, true, false, false),\n\n\n    TIMESTAMP(\"TIMESTAMP\", false, false, true, false, false, false, true, true, false, false),\n\n\n    TIMESTAMP_WITH_TIME_ZONE(\"TIMESTAMP WITH TIME ZONE\", false, false, true, false, false, false, true, true, false, false),\n\n\n    TINYINT(\"TINYINT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    VARBINARY(\"VARBINARY\", false, false, true, false, false, false, true, true, false, false),\n\n\n    VARCHAR(\"VARCHAR\", true, false, true, false, false, false, true, true, false, true),\n\n\n    VARCHAR2(\"VARCHAR2\", true, false, true, false, false, false, true, true, false, true),\n\n    DATETIME(\"DATETIME\", false, false, true, false, false, false, true, true, false, false),\n    ;\n    private ColumnType columnType;\n\n    public static DMColumnTypeEnum getByType(String dataType) {\n        String type = SqlUtils.removeDigits(dataType.toUpperCase());\n        return COLUMN_TYPE_MAP.get(type);\n    }\n\n    private static Map<String, DMColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (DMColumnTypeEnum value : DMColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n\n    DMColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportUnit) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, false, supportUnit);\n    }\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        DMColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"\\\"\").append(column.getName()).append(\"\\\"\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        script.append(buildAutoIncrement(column,type)).append(\" \");\n\n        script.append(buildNullable(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n    private String buildAutoIncrement(TableColumn column, DMColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportAutoIncrement()){\n            return \"\";\n        }\n        if (column.getAutoIncrement() != null && column.getAutoIncrement()\n                && column.getSeed() != null && column.getSeed() > 0 && column.getIncrement() != null && column.getIncrement() > 0) {\n            return \"IDENTITY(\" + column.getSeed() + \",\" + column.getIncrement() + \")\";\n        }\n        if (column.getAutoIncrement() != null && column.getAutoIncrement()) {\n            return \"IDENTITY(1,1)\";\n        }\n        return \"\";\n    }\n\n    private String buildNullable(TableColumn column, DMColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportNullable()) {\n            return \"\";\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"NULL\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDefaultValue(TableColumn column, DMColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {\n            return \"\";\n        }\n\n        if (\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if (\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildDataType(TableColumn column, DMColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(CHAR, VARCHAR, VARCHAR2, LONGVARCHAR,TEXT).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            script.append(columnType);\n            if (column.getColumnSize() != null && StringUtils.isEmpty(column.getUnit())) {\n                script.append(\"(\").append(column.getColumnSize()).append(\")\");\n            } else if (column.getColumnSize() != null && !StringUtils.isEmpty(column.getUnit())) {\n                script.append(\"(\").append(column.getColumnSize()).append(\" \").append(column.getUnit()).append(\")\");\n            }\n            return script.toString();\n        }\n\n        if (Arrays.asList(DECIMAL, DEC, FLOAT, NUMBER, TIMESTAMP, NUMERIC).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            script.append(columnType);\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\")\");\n            } else if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\",\").append(column.getDecimalDigits()).append(\")\");\n            }\n            return script.toString();\n        }\n\n        if (Arrays.asList(TIMESTAMP_WITH_TIME_ZONE).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            if (column.getColumnSize() == null) {\n                script.append(columnType);\n            } else {\n                String[] split = columnType.split(\"TIMESTAMP\");\n                script.append(\"TIMESTAMP\").append(\"(\").append(column.getColumnSize()).append(\")\").append(split[1]);\n            }\n            return script.toString();\n        }\n\n        if (Arrays.asList(INTERVAL_DAY_TO_HOUR,\n                INTERVAL_DAY_TO_MINUTE, INTERVAL_DAY_TO_SECOND,\n                INTERVAL_HOUR_TO_MINUTE,\n                INTERVAL_HOUR_TO_SECOND,\n                INTERVAL_MINUTE_TO_SECOND,\n                INTERVAL_YEAR_TO_MONTH).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            if (column.getColumnSize() == null) {\n                script.append(columnType);\n            } else {\n                String[] split = columnType.split(\" \");\n                if (split.length == 4) {\n                    script.append(split[0]).append(\" \").append(split[1]).append(\" (\").append(column.getColumnSize()).append(\") \").append(split[2]).append(\" \").append(split[3]);\n                }\n            }\n            return script.toString();\n        }\n\n        return columnType;\n    }\n\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"DROP COLUMN \").append(\"\\\"\").append(tableColumn.getName()).append(\"\\\"\");\n            return script.toString();\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"ADD (\").append(buildCreateColumnSql(tableColumn)).append(\")\");\n            return script.toString();\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"MODIFY (\").append(buildCreateColumnSql(tableColumn)).append(\") \\n\");\n\n            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n                script.append(\";\");\n                script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n                script.append(\" \").append(\"RENAME COLUMN \").append(\"\\\"\").append(tableColumn.getOldName()).append(\"\\\"\").append(\" TO \").append(\"\\\"\").append(tableColumn.getName()).append(\"\\\"\");\n\n            }\n            return script.toString();\n\n        }\n        return \"\";\n    }\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(DMColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/type/DMDefaultValueEnum.java",
    "content": "package ai.chat2db.plugin.dm.type;\n\nimport ai.chat2db.spi.model.DefaultValue;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum DMDefaultValueEnum {\n    EMPTY_STRING(\"EMPTY_STRING\"),\n    NULL(\"NULL\"),\n    ;\n    private DefaultValue defaultValue;\n\n    DMDefaultValueEnum(String defaultValue) {\n        this.defaultValue = new DefaultValue(defaultValue);\n    }\n\n\n    public DefaultValue getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static List<DefaultValue> getDefaultValues() {\n        return Arrays.stream(DMDefaultValueEnum.values()).map(DMDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/type/DMIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.dm.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum DMIndexTypeEnum {\n\n    PRIMARY_KEY(\"Primary\", \"PRIMARY KEY\"),\n\n    NORMAL(\"Normal\", \"INDEX\"),\n\n    UNIQUE(\"Unique\", \"UNIQUE INDEX\"),\n\n    BITMAP(\"BITMAP\", \"BITMAP INDEX\");\n\n\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    private IndexType indexType;\n\n\n    public String getName() {\n        return name;\n    }\n\n    private String name;\n\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    private String keyword;\n\n    DMIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n\n    public static DMIndexTypeEnum getByType(String type) {\n        for (DMIndexTypeEnum value : DMIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        if (PRIMARY_KEY.equals(this)) {\n            script.append(\"ALTER TABLE \\\"\").append(tableIndex.getSchemaName()).append(\"\\\".\\\"\").append(tableIndex.getTableName()).append(\"\\\" ADD PRIMARY KEY \").append(buildIndexColumn(tableIndex));\n        } else {\n            if (UNIQUE.equals(this)) {\n                script.append(\"CREATE UNIQUE INDEX \");\n            } else {\n                script.append(\"CREATE INDEX \");\n            }\n            script.append(buildIndexName(tableIndex)).append(\" ON \\\"\").append(tableIndex.getSchemaName()).append(\"\\\".\\\"\").append(tableIndex.getTableName()).append(\"\\\" \").append(buildIndexColumn(tableIndex));\n        }\n        return script.toString();\n    }\n\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"\\\"\").append(column.getColumnName()).append(\"\\\"\");\n                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {\n                    script.append(\" \").append(column.getAscOrDesc());\n                }\n                script.append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        return \"\\\"\" + tableIndex.getSchemaName() + \"\\\".\" + \"\\\"\" + tableIndex.getName() + \"\\\"\";\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex), \";\\n\", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (DMIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {\n            String tableName = \"\\\"\" + tableIndex.getSchemaName() + \"\\\".\" + \"\\\"\" + tableIndex.getTableName() + \"\\\"\";\n            return StringUtils.join(\"ALTER TABLE \",tableName,\" DROP PRIMARY KEY\");\n        }\n        StringBuilder script = new StringBuilder();\n        script.append(\"DROP INDEX \");\n        script.append(buildIndexName(tableIndex));\n\n        return script.toString();\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(DMIndexTypeEnum.values()).stream().map(DMIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-dm/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.dm.DMPlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-h2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n    <artifactId>chat2db-h2</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2DBManage.java",
    "content": "package ai.chat2db.plugin.h2;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.Objects;\n\npublic class H2DBManage extends DefaultDBManage implements DBManage {\n    @Override\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        exportSchema(connection, schemaName, asyncContext);\n    }\n\n    private void exportSchema(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SCRIPT NODATA NOPASSWORDS NOSETTINGS DROP SCHEMA %s;\", schemaName);\n        if (asyncContext.isContainsData()) {\n            sql = sql.replace(\"NODATA\", \"\");\n        }\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String script = resultSet.getString(\"SCRIPT\");\n                if (!(script.startsWith(\"CREATE USER\")||script.startsWith(\"--\"))) {\n                    StringBuilder sqlBuilder = new StringBuilder();\n                    sqlBuilder.append(script);\n                    sqlBuilder.append(\"\\n\");\n                    asyncContext.write(sqlBuilder.toString());\n                }\n            }\n        }\n\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n        if (ObjectUtils.anyNull(connectInfo) || StringUtils.isEmpty(connectInfo.getSchemaName())) {\n            return;\n        }\n        String schemaName = connectInfo.getSchemaName();\n        try {\n            SQLExecutor.getInstance().execute(connection, \"SET SCHEMA \\\"\" + schemaName + \"\\\"\");\n        } catch (SQLException e) {\n\n        }\n    }\n\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"DROP TABLE \" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2Meta.java",
    "content": "package ai.chat2db.plugin.h2;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport ai.chat2db.plugin.h2.builder.H2SqlBuilder;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.SortUtils;\nimport jakarta.validation.constraints.NotEmpty;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n@Slf4j\npublic class H2Meta extends DefaultMetaService implements MetaData {\n\n\n    private List<String> systemSchemas = Arrays.asList(\"INFORMATION_SCHEMA\");\n    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);\n        return SortUtils.sortSchema(schemas, systemSchemas);\n    }\n    @Override\n    public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,\n        @NotEmpty String tableName) {\n        return getDDL(connection, databaseName, schemaName, tableName);\n    }\n\n    private String getDDL(Connection connection, String databaseName, String schemaName, String tableName) {\n\n         try{\n            List<String> columnDefinitions = getColumnDefinitions(connection, databaseName, schemaName, tableName);\n            Map<String, List<String>> indexMap = getIndexInfo(connection, databaseName, schemaName, tableName);   \n            StringBuilder createTableDDL = new StringBuilder(\"CREATE TABLE \");\n            createTableDDL.append(tableName).append(\" (\\n\");\n            createTableDDL.append(String.join(\",\\n\", columnDefinitions));\n            createTableDDL.append(\"\\n);\\n\");\n\n             // Output index information\n            for (Map.Entry<String, List<String>> entry : indexMap.entrySet()) {\n                String indexName = entry.getKey();\n                List<String> columnList = entry.getValue();\n                String indexColumns = String.join(\", \", columnList);\n                String createIndexDDL = String.format(\"CREATE INDEX %s ON %s (%s);\", indexName, tableName, indexColumns);\n                createTableDDL.append(createIndexDDL);\n            }\n            return createTableDDL.toString();\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return \"\";\n    }\n\n    private List<String> getColumnDefinitions(Connection connection, String databaseName, String schemaName, String tableName) {\n        List<String> columnDefinitions = new ArrayList<>();\n        try (ResultSet columns = connection.getMetaData().getColumns(databaseName, schemaName, tableName, null)) {\n            while (columns.next()) {\n                String columnName = columns.getString(\"COLUMN_NAME\");\n                String columnType = columns.getString(\"TYPE_NAME\");\n                int columnSize = columns.getInt(\"COLUMN_SIZE\");\n                String remarks = columns.getString(\"REMARKS\");\n                String defaultValue = columns.getString(\"COLUMN_DEF\");\n                String nullable = columns.getInt(\"NULLABLE\") == ResultSetMetaData.columnNullable ? \"NULL\" : \"NOT NULL\";\n                StringBuilder columnDefinition = new StringBuilder();\n                columnDefinition.append(columnName).append(\" \").append(columnType);\n                if (columnSize != 0) {\n                    columnDefinition.append(\"(\").append(columnSize).append(\")\");\n                }\n                columnDefinition.append(\" \").append(nullable);\n                if (defaultValue != null) {\n                    columnDefinition.append(\" DEFAULT \").append(defaultValue);\n                }\n                if (remarks != null) {\n                    columnDefinition.append(\" COMMENT '\").append(remarks).append(\"'\");\n                }\n                columnDefinitions.add(columnDefinition.toString());\n            }\n        } catch (Exception e) {\n           throw new RuntimeException(e);\n        }\n        return columnDefinitions;\n    }\n\n    private Map<String, List<String>> getIndexInfo(Connection connection, String databaseName, String schemaName, String tableName) {\n        Map<String, List<String>> indexMap = new HashMap<>();\n        try (ResultSet indexes = connection.getMetaData().getIndexInfo(databaseName, schemaName, tableName, false, false)) {\n            while (indexes.next()) {\n                String indexName = indexes.getString(\"INDEX_NAME\");\n                String columnName = indexes.getString(\"COLUMN_NAME\");\n                if (indexName != null) {\n                    indexMap.computeIfAbsent(indexName, k -> new ArrayList<>()).add(columnName);\n                }\n            }\n        } catch (SQLException e) {\n           throw new RuntimeException(e);\n        }\n        return indexMap;\n    }\n\n    private static String ROUTINES_SQL\n        =\n        \"SELECT SPECIFIC_NAME, ROUTINE_DEFINITION FROM information_schema.routines WHERE \"\n            + \"routine_type = '%s' AND ROUTINE_SCHEMA ='%s'  AND \"\n            + \"routine_name = '%s';\";\n\n    @Override\n    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,\n        String functionName) {\n\n        String sql = String.format(ROUTINES_SQL, \"FUNCTION\", databaseName, functionName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Function function = new Function();\n            function.setDatabaseName(databaseName);\n            function.setSchemaName(schemaName);\n            function.setFunctionName(functionName);\n            if (resultSet.next()) {\n                function.setSpecificName(resultSet.getString(\"SPECIFIC_NAME\"));\n                function.setFunctionBody(resultSet.getString(\"ROUTINE_DEFINITION\"));\n            }\n\n            return function;\n        });\n\n    }\n\n    private static String TRIGGER_SQL\n        = \"SELECT TRIGGER_NAME,JAVA_CLASS  FROM INFORMATION_SCHEMA.TRIGGERS where \"\n        + \"TRIGGER_SCHEMA = '%s' AND TRIGGER_NAME = '%s';\";\n\n    private static String TRIGGER_SQL_LIST\n        = \"SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS where TRIGGER_CATALOG = '%s' AND TRIGGER_SCHEMA = '%s';\";\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        List<Trigger> triggers = new ArrayList<>();\n        String sql = String.format(TRIGGER_SQL_LIST, databaseName,schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Trigger trigger = new Trigger();\n                trigger.setTriggerName(resultSet.getString(\"TRIGGER_NAME\"));\n                trigger.setSchemaName(schemaName);\n                trigger.setDatabaseName(databaseName);\n                triggers.add(trigger);\n            }\n            return triggers;\n        });\n    }\n\n    @Override\n    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,\n        String triggerName) {\n\n        String sql = String.format(TRIGGER_SQL, databaseName, triggerName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Trigger trigger = new Trigger();\n            trigger.setDatabaseName(databaseName);\n            trigger.setSchemaName(schemaName);\n            trigger.setTriggerName(triggerName);\n            if (resultSet.next()) {\n                trigger.setTriggerBody(resultSet.getString(\"JAVA_CLASS\"));\n            }\n            return trigger;\n        });\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n        String procedureName) {\n        String sql = String.format(ROUTINES_SQL, \"PROCEDURE\", databaseName, procedureName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Procedure procedure = new Procedure();\n            procedure.setDatabaseName(databaseName);\n            procedure.setSchemaName(schemaName);\n            procedure.setProcedureName(procedureName);\n            if (resultSet.next()) {\n                procedure.setSpecificName(resultSet.getString(\"SPECIFIC_NAME\"));\n                procedure.setProcedureBody(resultSet.getString(\"ROUTINE_DEFINITION\"));\n            }\n            return procedure;\n        });\n    }\n\n    private static String VIEW_SQL\n        = \"SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_CATALOG = '%s' AND TABLE_SCHEMA = '%s' \"\n        + \"AND TABLE_NAME = '%s';\";\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_SQL, databaseName, schemaName, viewName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Table table = new Table();\n            table.setDatabaseName(databaseName);\n            table.setSchemaName(schemaName);\n            table.setName(viewName);\n            if (resultSet.next()) {\n                table.setDdl(resultSet.getString(\"VIEW_DEFINITION\"));\n            }\n            return table;\n        });\n    }\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new H2SqlBuilder();\n    }\n\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"\\\"\" + name + \"\\\"\").collect(Collectors.joining(\".\"));\n    }\n\n    @Override\n    public List<String> getSystemSchemas() {\n        return systemSchemas;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2Plugin.java",
    "content": "package ai.chat2db.plugin.h2;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class H2Plugin extends DefaultMetaService implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"h2.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new H2Meta();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new H2DBManage();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/builder/H2SqlBuilder.java",
    "content": "package ai.chat2db.plugin.h2.builder;\n\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Schema;\nimport org.apache.commons.lang3.StringUtils;\n\npublic class H2SqlBuilder extends DefaultSqlBuilder  {\n\n    @Override\n    public String buildCreateSchemaSql(Schema schema) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE SCHEMA \\\"\" + schema.getName() + \"\\\";\");\n\n        if (StringUtils.isNotBlank(schema.getComment())) {\n            sqlBuilder.append(\"\\nCOMMENT ON SCHEMA \\\"\").append(schema.getName()).append(\"\\\" IS '\").append(schema.getComment()).append(\"';\");\n        }\n\n        return sqlBuilder.toString();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/h2.json",
    "content": "{\n  \"dbType\": \"H2\",\n  \"supportDatabase\": true,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:h2:tcp://localhost:9092/\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/h2-2.1.214.jar\"\n      ],\n      \"jdbcDriver\": \"h2-2.1.214.jar\",\n      \"jdbcDriverClass\": \"org.h2.Driver\"\n    }\n  ],\n  \"name\": \"H2\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-h2/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.h2.H2Plugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n\n    <artifactId>chat2db-hive</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/HiveCommandExecutor.java",
    "content": "package ai.chat2db.plugin.hive;\n\nimport ai.chat2db.spi.model.Command;\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.spi.model.Header;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\npublic class HiveCommandExecutor extends SQLExecutor {\n\n    /**\n     * Execute command\n     */\n    @Override\n    public List<ExecuteResult> execute(Command command) {\n        List<ExecuteResult> result = new ArrayList<>();\n        result = super.execute(command);\n        if (CollectionUtils.isNotEmpty(result)) {\n            for (ExecuteResult executeResult : result) {\n                if (executeResult.getHeaderList() != null) {\n                    for (Header header : executeResult.getHeaderList()) {\n                        header.setName(formatTableName(header.getName()));\n                    }\n                }\n            }\n        }\n        return result;\n    }\n\n\n    /**\n     * Execute command\n     */\n    @Override\n    public ExecuteResult executeUpdate(String sql, Connection connection, int n) throws SQLException {\n        return super.executeUpdate(sql, connection, n);\n    }\n\n\n    /**\n     *\n     */\n    @Override\n    public ExecuteResult execute(final String sql, Connection connection, boolean limitRowSize, Integer offset,\n                                 Integer count)\n            throws SQLException {\n        return super.execute(sql, connection, limitRowSize, offset, count);\n    }\n\n    public static String formatTableName(String tableName) {\n        if (StringUtils.isBlank(tableName)) {\n            return tableName;\n        }\n        if (tableName.contains(\".\")) {\n            String[] split = tableName.split(\"\\\\.\");\n            return split[1];\n        }\n        return tableName;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/HiveDBManage.java",
    "content": "package ai.chat2db.plugin.hive;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport org.springframework.util.StringUtils;\n\npublic class HiveDBManage extends DefaultDBManage implements DBManage {\n\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        if (StringUtils.isEmpty(database)) {\n            return;\n        }\n        try {\n            SQLExecutor.getInstance().execute(connection,\"use \" + database );\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"drop table if exists \" +tableName;\n        SQLExecutor.getInstance().execute(connection,sql, resultSet -> null);\n    }\n\n    @Override\n    public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName,boolean copyData) throws SQLException {\n        String sql = \"CREATE TABLE \" + newTableName + \"LIKE \" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n        if(copyData){\n            sql = \"INSERT INTO \" + newTableName + \" SELECT * FROM \" + tableName;\n            SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/HiveMetaData.java",
    "content": "package ai.chat2db.plugin.hive;\n\nimport ai.chat2db.plugin.hive.builder.HiveSqlBuilder;\nimport ai.chat2db.plugin.hive.type.HiveColumnTypeEnum;\nimport ai.chat2db.plugin.hive.type.HiveIndexTypeEnum;\nimport ai.chat2db.spi.CommandExecutor;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport jakarta.validation.constraints.NotEmpty;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.Reader;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class HiveMetaData extends DefaultMetaService implements MetaData {\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        List<Database> databases = new ArrayList<>();\n        return SQLExecutor.getInstance().execute(connection,\"show databases\", resultSet -> {\n            try {\n                while (resultSet.next()) {\n                    String databaseName = resultSet.getString(\"database_name\");\n                    Database database = new Database();\n                    database.setName(databaseName);\n                    databases.add(database);\n                }\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            }\n            return databases;\n        });\n    }\n\n    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        List<Schema> schemas = new ArrayList<>();\n        schemas.add(Schema.builder().databaseName(databaseName).name(databaseName).build());\n        return schemas;\n    }\n\n    @Override\n    public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           @NotEmpty String tableName) {\n        String sql = \"SHOW CREATE TABLE \" + format(databaseName) + \".\"\n                + format(tableName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            StringBuilder sb = new StringBuilder();\n            while (resultSet.next()) {\n                // 拼接建表语句\n                sb.append(resultSet.getString(\"createtab_stmt\"));\n                sb.append(\"\\r\\n\");\n            }\n            if (sb.length() > 0) {\n                sb = sb.delete(sb.length() - 2, sb.length());\n                sb.append(\";\");\n                return sb.toString();\n            }\n            return null;\n        });\n    }\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).skip(1).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"`\" + name + \"`\").collect(Collectors.joining(\".\"));\n    }\n\n    @Override\n    public CommandExecutor getCommandExecutor() {\n        return new HiveCommandExecutor();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(HiveColumnTypeEnum.getTypes())\n                //.charsets(HiveCharsetEnum.getCharsets())\n                //.collations(HiveCollationEnum.getCollations())\n                .indexTypes(HiveIndexTypeEnum.getIndexTypes())\n                //.defaultValues(HiveDefaultValueEnum.getDefaultValues())\n                .build();\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new HiveSqlBuilder();\n    }\n\n\n    private static String SELECT_TAB_COLS = \"DESCRIBE FORMATTED `%s`.`%s`\";\n    // TODO 待完善\n    @Override\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = String.format(SELECT_TAB_COLS, databaseName, tableName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            List<TableColumn> tableColumns = new ArrayList<>();\n            Map<String, String> detailTableInfo = new HashMap<>();\n            Map<String, String> tableParams = new HashMap<>();\n            Map<String, String> storageInfo = new HashMap<>();\n            Map<String, String> storageDescParams = new HashMap<>();\n            Map<String, Map<String, String>> constraints = new HashMap<>();\n            List<Map<String, String>> columns = new ArrayList<>();\n            List<Map<String, String>> partitions = new ArrayList<>();\n            Map<String, String> moduleMap = getDescTableModule();\n\n            String infoModule = \"\";\n            while (resultSet.next()) {\n                String title = resultSet.getString(1).trim();\n                if ((\"\".equals(title) && resultSet.getString(2) == null) || \"# Constraints\".equals(title)) {\n                    continue;\n                }\n                if (moduleMap.containsKey(title)) {\n                    if (\"partition_info\".equals(infoModule) && \"col_name\".equals(moduleMap.get(title))) {\n                        continue;\n                    }\n                    infoModule = moduleMap.get(title);\n                    continue;\n                }\n\n                String key = null;\n                String value = null;\n                switch (infoModule) {\n                    case \"col_name\":\n                        Map<String, String> map = new HashMap<>();\n                        int colNum = resultSet.getMetaData().getColumnCount();\n                        for (int col = 1; col <= colNum; col++) {\n                            String columnName = resultSet.getMetaData().getColumnName(col);\n                            String columnValue = resultSet.getString(columnName);\n                            map.put(columnName, columnValue);\n                        }\n                        columns.add(map);\n                        break;\n                    case \"table_info\":\n                        key = resultSet.getString(1).trim().replace(\":\", \"\");\n                        value = resultSet.getString(2).trim();\n                        detailTableInfo.put(key, value);\n                        break;\n\n                    case \"table_param\":\n                        key = resultSet.getString(2).trim().replace(\":\", \"\");\n                        value = resultSet.getString(3).trim();\n                        tableParams.put(key, value);\n                        break;\n\n                    case \"storage_info\":\n                        key = resultSet.getString(1).trim().replace(\":\", \"\");\n                        value = resultSet.getString(2).trim();\n                        storageInfo.put(key, value);\n                        break;\n\n                    case \"storage_desc\":\n                        key = resultSet.getString(2).trim().replace(\":\", \"\");\n                        value = resultSet.getString(3).trim();\n                        storageDescParams.put(key, value);\n                        break;\n                    case \"primary_key\":\n                        Map<String, String> primaryKeyMap = constraints.getOrDefault(\"primaryKey\", new HashMap<>());\n                        if (\"Table:\".equals(title.trim())) {\n                            resultSet.next();\n                        }\n                        String primaryKeyName = resultSet.getString(2).trim();\n                        resultSet.next();\n\n                        key = resultSet.getString(2).trim();\n                        primaryKeyMap.put(key, primaryKeyName);\n\n                        constraints.put(\"primaryKey\", primaryKeyMap);\n                        break;\n                    case \"not_null_constraint\":\n                        Map<String, String> notNullMap = constraints.getOrDefault(\"notnull\", new HashMap<>());\n                        if (\"Table:\".equals(title.trim())) {\n                            resultSet.next();\n                        }\n\n                        String notNullConstraintName = resultSet.getString(2).trim();\n                        resultSet.next();\n\n                        key = resultSet.getString(2).trim();\n                        notNullMap.put(key, notNullConstraintName);\n\n                        constraints.put(\"notnull\", notNullMap);\n                        break;\n\n                    case \"default_constraint\":\n                        Map<String, String> defaultMap = constraints.getOrDefault(\"default\", new HashMap<>());\n                        if (\"Table:\".equals(title.trim())) { resultSet.next();}\n\n                        String defaultConstraintName = resultSet.getString(2).trim();\n                        resultSet.next();\n\n                        key = resultSet.getString(1).trim().split(\":\")[1];\n                        value = resultSet.getString(2).trim();\n                        int valueIndex = value.indexOf(\":\");\n                        value = value.substring(valueIndex + 1);\n\n                        defaultMap.put(key + \"_constraintName\", defaultConstraintName);\n\n                        constraints.put(\"default\", defaultMap);\n                        break;\n\n                    case \"partition_info\":\n                        Map<String, String> partitionMap = new HashMap<>();\n                        int partitionColNum = resultSet.getMetaData().getColumnCount();\n                        for (int col = 0; col < partitionColNum; col++) {\n                            String columnName = resultSet.getMetaData().getColumnName(col + 1);\n                            String columnValue = resultSet.getString(columnName);\n                            partitionMap.put(columnName, columnValue);\n                        }\n                        partitions.add(partitionMap);\n                        break;\n                    default:\n                        System.out.print(\"unknown module,please update method to support it : \" + infoModule);\n\n                }\n\n\n            }\n\n            for (Map<String, String> columnMap : columns) {\n                TableColumn tableColumn = new TableColumn();\n                tableColumn.setTableName(tableName);\n                tableColumn.setSchemaName(schemaName);\n                tableColumn.setName(columnMap.get(\"col_name\"));\n                tableColumn.setColumnType(columnMap.get(\"data_type\"));\n                tableColumn.setComment(columnMap.get(\"comment\"));\n                if (constraints.get(\"primaryKey\") != null && constraints.get(\"primaryKey\").keySet().contains(columnMap.get(\"col_name\"))) {\n                    tableColumn.setPrimaryKey(true);\n                }\n                if (constraints.get(\"notnull\") !=null && constraints.get(\"notnull\").keySet().contains(columnMap.get(\"col_name\"))) {\n                    tableColumn.setNullable(1);\n                }\n                tableColumns.add(tableColumn);\n\n            }\n\n            return tableColumns;\n        });\n    }\n\n    private static Map<String, String> getDescTableModule() {\n        Map<String, String> descTableModule = new HashMap<>();\n\n        descTableModule.put(\"# col_name\", \"col_name\");\n        descTableModule.put(\"# Detailed Table Information\", \"table_info\");\n        descTableModule.put(\"Table Parameters:\", \"table_param\");\n        descTableModule.put(\"# Storage Information\", \"storage_info\");\n        descTableModule.put(\"Storage Desc Params:\", \"storage_desc\");\n        descTableModule.put(\"# Not Null Constraints\", \"not_null_constraint\");\n        descTableModule.put(\"# Default Constraints\", \"default_constraint\");\n        descTableModule.put(\"# Partition Information\", \"partition_info\");\n        descTableModule.put(\"# Primary Key\", \"primary_key\");\n\n        return descTableModule;\n    }\n\n    public static String format(String name) {\n        return \"`\" + name + \"`\";\n    }\n\n    private static String VIEW_SQL\n            = \"SHOW CREATE TABLE `%s`.`%s`\";\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_SQL, databaseName, viewName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Table table = new Table();\n            table.setDatabaseName(databaseName);\n            table.setSchemaName(schemaName);\n            table.setName(viewName);\n            StringBuilder sb = new StringBuilder();\n            while (resultSet.next()) {\n                // 拼接建表语句\n                sb.append(resultSet.getString(\"createtab_stmt\"));\n                sb.append(\"\\r\\n\");\n            }\n            if (sb.length() > 0) {\n                sb = sb.delete(sb.length() - 2, sb.length());\n                sb.append(\";\");\n                table.setDdl(sb.toString());\n            }\n            return table;\n        });\n    }\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/HivePlugin.java",
    "content": "package ai.chat2db.plugin.hive;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class HivePlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"hive.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new HiveMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new HiveDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java",
    "content": "package ai.chat2db.plugin.hive.builder;\n\nimport ai.chat2db.plugin.hive.type.HiveColumnTypeEnum;\nimport ai.chat2db.plugin.hive.type.HiveIndexTypeEnum;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.List;\n\n\npublic class HiveSqlBuilder extends DefaultSqlBuilder implements SqlBuilder<Table> {\n\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"CREATE TABLE \");\n        if (StringUtils.isNotBlank(table.getDatabaseName())) {\n            script.append(\"`\").append(table.getDatabaseName()).append(\"`\").append(\".\");\n        }\n        script.append(\"`\").append(table.getName()).append(\"`\").append(\" (\").append(\"\\n\");\n\n        // append column\n        appendColumns(script, table.getColumnList());\n\n        // append primary key and index\n        appendIndexes(script, table.getIndexList());\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n)\");\n\n        // append engine, charset, collate, auto_increment, comment, and partition\n        appendTableOptions(script, table);\n        script.append(\";\");       \n\n        return script.toString();\n    }\n\n    private void appendColumns(StringBuilder script, List<TableColumn> columns) {\n        for (TableColumn column : columns) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            HiveColumnTypeEnum typeEnum = HiveColumnTypeEnum.getByType(column.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n    }\n\n    private void appendIndexes(StringBuilder script, List<TableIndex> indexes) {\n        for (TableIndex index : indexes) {\n            if (StringUtils.isBlank(index.getName()) || StringUtils.isBlank(index.getType())) {\n                continue;\n            }\n                HiveIndexTypeEnum hiveIndexTypeEnum = HiveIndexTypeEnum.getByType(index.getType());\n                script.append(\"\\t\").append(\"\").append(hiveIndexTypeEnum.buildIndexScript(index)).append(\",\\n\");\n\n        }\n    }\n\n    private void appendTableOptions(StringBuilder script, Table table) {\n        if (StringUtils.isNotBlank(table.getEngine())) {\n            script.append(\" ENGINE=\").append(table.getEngine());\n        }\n\n        if (StringUtils.isNotBlank(table.getCharset())) {\n            script.append(\" DEFAULT CHARACTER SET=\").append(table.getCharset());\n        }\n\n        if (StringUtils.isNotBlank(table.getCollate())) {\n            script.append(\" COLLATE=\").append(table.getCollate());\n        }\n\n        if (table.getIncrementValue() != null) {\n            script.append(\" AUTO_INCREMENT=\").append(table.getIncrementValue());\n        }\n\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\" COMMENT '\").append(table.getComment()).append(\"'\");\n        }\n\n        if (StringUtils.isNotBlank(table.getPartition())) {\n            script.append(\" \\n\").append(table.getPartition());\n        }\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n        boolean isModify = false;\n        script.append(\"ALTER TABLE \");\n        if (StringUtils.isNotBlank(newTable.getDatabaseName())) {\n            script.append(\"`\").append(newTable.getDatabaseName()).append(\"`\").append(\".\");\n        }\n        script.append(\"`\").append(oldTable.getName()).append(\"`\").append(\"\\n\");\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(\"\\t\").append(\"RENAME TO \").append(\"`\").append(newTable.getName()).append(\"`\").append(\";\\n\");\n            isModify = true;\n        }\n        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {\n            if (isModify) {\n                script.append(\"ALTER TABLE \");\n                if (StringUtils.isNotBlank(newTable.getDatabaseName())) {\n                    script.append(\"`\").append(newTable.getDatabaseName()).append(\"`\").append(\".\");\n                }\n                script.append(\"`\").append(newTable.getName()).append(\"`\").append(\"\\n\");\n            }\n            script.append(\"\\t\").append(\"SET TBLPROPERTIES ('comment' = \").append(\"'\").append(newTable.getComment()).append(\"'),\\n\");\n        }\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if (StringUtils.isNotBlank(tableColumn.getEditStatus()) && StringUtils.isNotBlank(tableColumn.getColumnType()) && StringUtils.isNotBlank(tableColumn.getName())) {\n                HiveColumnTypeEnum typeEnum = HiveColumnTypeEnum.getByType(tableColumn.getColumnType());\n                if(typeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\",\\n\");\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                HiveIndexTypeEnum hiveIndexTypeEnum = HiveIndexTypeEnum.getByType(tableIndex.getType());\n                if(hiveIndexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(hiveIndexTypeEnum.buildModifyIndex(tableIndex)).append(\",\\n\");\n            }\n        }\n\n        // append reorder column\n       // script.append(buildGenerateReorderColumnSql(oldTable, newTable));\n\n        if (script.length() > 2) {\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            script.append(\";\");\n        }\n\n        return script.toString();\n    }\n\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);\n        sqlBuilder.append(sql);\n        if (offset == 0) {\n            sqlBuilder.append(\"\\n LIMIT \");\n            sqlBuilder.append(pageSize);\n        } else {\n            sqlBuilder.append(\"\\n LIMIT \");\n            sqlBuilder.append(offset);\n            sqlBuilder.append(\",\");\n            sqlBuilder.append(pageSize);\n        }\n        return sqlBuilder.toString();\n    }\n\n\n    @Override\n    public String buildCreateDatabaseSql(Database database) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE DATABASE `\" + database.getName() + \"`\");\n        if (StringUtils.isNotBlank(database.getComment())) {\n            sqlBuilder.append(\"\\r\\n COMMENT '\").append(database.getComment()).append(\"'\");\n\n        }\n        return sqlBuilder.toString();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/hive.json",
    "content": "{\n  \"dbType\": \"HIVE\",\n  \"supportDatabase\": true,\n  \"supportSchema\": false,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:hive2://localhost:10000/\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/hive-jdbc-3.1.2-standalone.jar\"\n      ],\n      \"jdbcDriver\": \"hive-jdbc-3.1.2-standalone.jar\",\n      \"jdbcDriverClass\": \"org.apache.hive.jdbc.HiveDriver\"\n    }\n  ],\n  \"name\": \"Hive\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/type/HiveColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.hive.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum HiveColumnTypeEnum implements ColumnBuilder {\n\n    // Numeric Types\n    TINYINT(\"TINYINT\", true, false, true, true, false, false, true, true, false, false),\n\n    SMALLINT(\"SMALLINT\", false, false, true, true, false, false, true, true, false, false),\n\n    INT(\"INT\", false, false, true, true, false, false, true, true, false, false),\n\n    INTEGER(\"INTEGER\", false, false, true, true, false, false, true, true, false, false),\n\n    BIGINT(\"BIGINT\", false, false, true, true, false, false, true, true, false, false),\n\n    FLOAT(\"FLOAT\", true, true, true, false, false, false, true, true, false, false),\n\n    DOUBLE(\"DOUBLE\", true, true, true, false, false, false, true, true, false, false),\n\n    DECIMAL(\"DECIMAL\", true, true, true, false, false, false, true, true, false, false),\n\n    // hive 3.0.0+\n    NUMERIC(\"NUMERIC\", true, true, true, false, false, false, true, true, false, false),\n\n    // Date/Time Types\n    TIMESTAMP(\"TIMESTAMP\", true, false, true, false, false, false, true, true, true, false),\n\n    DATE(\"DATE\", false, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL(\"INTERVAL\", true, false, true, false, false, false, true, true, true, false),\n\n    // String Types\n    STRING(\"STRING\", true, false, true, false, true, true, true, true, false, false),\n\n    CHAR(\"CHAR\", true, false, true, false, true, true, true, true, false, false),\n\n    VARCHAR(\"VARCHAR\", true, false, true, false, true, true, true, true, false, false),\n\n\n    // Misc Types\n    BOOLEAN(\"BOOLEAN\", false, false, true, true, false, false, true, true, false, false),\n\n    BINARY(\"BINARY\", true, false, true, false, false, false, true, true, false, false),\n\n\n    // Complex Types\n    ARRAY(\"ARRAY\", true, false, true, false, true, true, true, true, false, false),\n\n    MAP(\"MAP\", true, false, true, false, true, true, true, true, false, false),\n\n    STRUCT(\"STRUCT\", true, false, true, false, true, true, true, true, false, false),\n\n    UNIONTYPE(\"UNIONTYPE\", true, false, true, false, true, true, true, true, false, false),\n\n\n\n\n    ;\n\n    private ColumnType columnType;\n\n    public static HiveColumnTypeEnum getByType(String dataType) {\n        return COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n\n    HiveColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportValue) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent,supportValue,false);\n    }\n\n    private static Map<String, HiveColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (HiveColumnTypeEnum value : HiveColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        HiveColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildCharset(column,type)).append(\" \");\n\n        script.append(buildCollation(column,type)).append(\" \");\n\n        if(!EditStatus.ADD.name().equals(column.getEditStatus())) {\n            script.append(buildNullable(column,type)).append(\" \");\n        }\n\n        //script.append(buildDefaultValue(column,type)).append(\" \");\n\n        //script.append(buildExt(column,type)).append(\" \");\n\n        //script.append(buildAutoIncrement(column,type)).append(\" \");\n\n        script.append(buildComment(column,type)).append(\" \");\n\n        return script.toString();\n    }\n\n    private String buildCharset(TableColumn column, HiveColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportCharset() || StringUtils.isEmpty(column.getCharSetName())){\n            return \"\";\n        }\n        return StringUtils.join(\"CHARACTER SET \",column.getCharSetName());\n    }\n\n    private String buildCollation(TableColumn column, HiveColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportCollation() || StringUtils.isEmpty(column.getCollationName())){\n            return \"\";\n        }\n        return StringUtils.join(\"COLLATE \",column.getCollationName());\n    }\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\"DROP COLUMN `\", tableColumn.getName() + \"`\");\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\"ADD COLUMNS (\", buildCreateColumnSql(tableColumn), \")\");\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n                return StringUtils.join(\"CHANGE COLUMN `\", tableColumn.getOldName(), \"` \", buildCreateColumnSql(tableColumn));\n            } else {\n                return StringUtils.join(\"CHANGE `\", tableColumn.getOldName(), \"` \", buildCreateColumnSql(tableColumn));\n            }\n        }\n        return \"\";\n    }\n\n    private String buildAutoIncrement(TableColumn column, HiveColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportAutoIncrement()){\n            return \"\";\n        }\n        if (column.getAutoIncrement() != null && column.getAutoIncrement()) {\n            return \"AUTO_INCREMENT\";\n        }\n        return \"\";\n    }\n\n    private String buildComment(TableColumn column, HiveColumnTypeEnum type) {\n        if(!type.columnType.isSupportComments() || StringUtils.isEmpty(column.getComment())){\n            return \"\";\n        }\n        return StringUtils.join(\"COMMENT '\",column.getComment(),\"'\");\n    }\n\n    private String buildExt(TableColumn column, HiveColumnTypeEnum type) {\n        if(!type.columnType.isSupportExtent() || StringUtils.isEmpty(column.getExtent())){\n            return \"\";\n        }\n        return column.getComment();\n    }\n\n    private String buildDefaultValue(TableColumn column, HiveColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())){\n            return \"\";\n        }\n\n        if(\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())){\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if(\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())){\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        if(Arrays.asList(CHAR,VARCHAR,BINARY).contains(type)){\n            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n        }\n\n        if(Arrays.asList(DATE).contains(type)){\n            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n        }\n\n        if(Arrays.asList(TIMESTAMP).contains(type)){\n            if(\"CURRENT_TIMESTAMP\".equalsIgnoreCase(column.getDefaultValue().trim())){\n                return StringUtils.join(\"DEFAULT \",column.getDefaultValue());\n            }\n            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n        }\n\n        return StringUtils.join(\"DEFAULT \",column.getDefaultValue());\n    }\n\n    private String buildNullable(TableColumn column,HiveColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportNullable()){\n            return \"\";\n        }\n        if (column.getNullable()!=null && 1==column.getNullable()) {\n            return \"\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDataType(TableColumn column, HiveColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(BINARY, VARCHAR, CHAR).contains(type)) {\n            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n        }\n\n\n        if (Arrays.asList(TIMESTAMP).contains(type)) {\n            if (column.getColumnSize() == null || column.getColumnSize() == 0) {\n                return columnType;\n            } else {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n            }\n        }\n\n\n        if (Arrays.asList(DECIMAL, FLOAT, DOUBLE,TINYINT).contains(type)) {\n            if (column.getColumnSize() == null || column.getDecimalDigits() == null) {\n                return columnType;\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \")\");\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n            }\n        }\n\n        return columnType;\n    }\n\n    public String buildColumn(TableColumn column) {\n        HiveColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n        script.append(buildDataType(column, type)).append(\" \");\n        return script.toString();\n    }\n\n    private String unsignedDataType(String dataTypeName, String middle) {\n        String[] split = dataTypeName.split(\" \");\n        if (split.length == 2) {\n            return StringUtils.join(split[0], middle, split[1]);\n        }\n        return StringUtils.join(dataTypeName, middle);\n    }\n\n    public static List<ColumnType> getTypes(){\n       return Arrays.stream(HiveColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/type/HiveIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.hive.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum HiveIndexTypeEnum {\n\n    PRIMARY_KEY(\"Primary\", \"PRIMARY KEY\"),\n\n    NORMAL(\"Normal\", \"INDEX\"),\n\n    UNIQUE(\"Unique\", \"UNIQUE INDEX\"),\n\n    FULLTEXT(\"Fulltext\", \"FULLTEXT INDEX\"),\n\n    SPATIAL(\"Spatial\", \"SPATIAL INDEX\");\n\n    public String getName() {\n        return name;\n    }\n\n    private String name;\n\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    private String keyword;\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    private IndexType indexType;\n\n    HiveIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n\n    public static HiveIndexTypeEnum getByType(String type) {\n        for (HiveIndexTypeEnum value : HiveIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(keyword).append(\" \");\n\n        script.append(buildIndexName(tableIndex)).append(\" \");\n\n        script.append(buildIndexColumn(tableIndex)).append(\" \");\n\n        script.append(buildIndexComment(tableIndex)).append(\" \");\n\n        return script.toString();\n    }\n\n    private String buildIndexComment(TableIndex tableIndex) {\n        if(StringUtils.isBlank(tableIndex.getComment())){\n            return \"\";\n        }else {\n            return StringUtils.join(\"COMMENT '\",tableIndex.getComment(),\"'\");\n        }\n\n    }\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if(StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"`\").append(column.getColumnName()).append(\"`\");\n                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {\n                    script.append(\" \").append(column.getAscOrDesc());\n                }\n                script.append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        if(this.equals(PRIMARY_KEY)){\n            return \"\";\n        }else {\n            return \"`\"+tableIndex.getName()+\"`\";\n        }\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex),\",\\n\", \"ADD \", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\"ADD \", buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (HiveIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {\n            return StringUtils.join(\"DROP PRIMARY KEY\");\n        }\n        return StringUtils.join(\"DROP INDEX `\", tableIndex.getOldName(),\"`\");\n    }\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(HiveIndexTypeEnum.values()).stream().map(HiveIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-hive/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.hive.HivePlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n\n    <artifactId>chat2db-kingbase</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/java/ai/chat2db/plugin/kingbase/KingBaseDBManage.java",
    "content": "package ai.chat2db.plugin.kingbase;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.Function;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n@Slf4j\npublic class KingBaseDBManage extends DefaultDBManage implements DBManage {\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            String parameterSignature = extractParameterSignature(procedureBody);\n\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, schemaName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String dropSql = \"DROP PROCEDURE IF EXISTS \" + procedureNewName + parameterSignature;\n            SQLExecutor.getInstance().execute(connection, dropSql, resultSet -> {\n            });\n            SQLExecutor.getInstance().execute(connection, procedureBody, resultSet -> {\n            });\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        try {\n            ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n            if (!StringUtils.isEmpty(connectInfo.getSchemaName())) {\n                SQLExecutor.getInstance().execute(connection, \"SET search_path TO \\\"\" + connectInfo.getSchemaName() + \"\\\"\");\n            }\n        } catch (Exception e) {\n            log.error(\"connectDatabase error\", e);\n        }\n    }\n\n    @Override\n    public Connection getConnection(ConnectInfo connectInfo) {\n        String url = connectInfo.getUrl();\n        String database = connectInfo.getDatabaseName();\n        if (database != null && !database.isEmpty()) {\n            url = replaceDatabaseInJdbcUrl(url, database);\n        }\n        connectInfo.setUrl(url);\n\n        return super.getConnection(connectInfo);\n    }\n\n\n    public String replaceDatabaseInJdbcUrl(String url, String newDatabase) {\n        // First split the string at the \"?\" character and process the query parameters\n        String[] urlAndParams = url.split(\"\\\\?\");\n        String urlWithoutParams = urlAndParams[0];\n\n        // Split string at \"/\" character in URL\n        String[] parts = urlWithoutParams.split(\"/\");\n\n        // Take the last part, the database name, and replace it with the new database name\n        parts[parts.length - 1] = newDatabase;\n\n        // Reassemble the modified parts into a URL\n        String newUrlWithoutParams = String.join(\"/\", parts);\n\n        // If query parameters exist, add them again\n        String newUrl = urlAndParams.length > 1 ? newUrlWithoutParams + \"?\" + urlAndParams[1] : newUrlWithoutParams;\n\n        return newUrl;\n    }\n\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"drop table if exists \" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void deleteProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) {\n        String procedureBody = procedure.getProcedureBody();\n        String parameterSignature = extractParameterSignature(procedureBody);\n        String procedureNewName = getSchemaOrProcedureName(procedure.getProcedureBody(), schemaName, procedure);\n        String sql = \"DROP PROCEDURE \" + procedureNewName + parameterSignature;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> {});\n    }\n\n    @Override\n    public void deleteFunction(Connection connection, String databaseName, String schemaName, Function function) {\n        String functionBody = function.getFunctionBody();\n        String parameterSignature = extractParameterSignature(functionBody);\n        String functionNewName = getSchemaOrFunctionName(functionBody, schemaName, function);\n        String sql = \"DROP FUNCTION\" + functionNewName + parameterSignature;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> {});\n    }\n\n    @Override\n    public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName, boolean copyData) throws SQLException {\n        String sql = \"\";\n        if (copyData) {\n            sql = \"CREATE TABLE \" + newTableName + \" AS TABLE \" + tableName + \" WITH DATA\";\n        } else {\n            sql = \"CREATE TABLE \" + newTableName + \" AS TABLE \" + tableName + \" WITH NO DATA\";\n        }\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    private String extractParameterSignature(String input) {\n        int depth = 0, start = -1;\n        for (int i = 0; i < input.length(); i++) {\n            char c = input.charAt(i);\n            if (c == '(') {\n                if (depth++ == 0) start = i;\n            } else if (c == ')' && --depth == 0 && start != -1) {\n                return \"(\" + input.substring(start + 1, i) + \")\";\n            }\n        }\n        if (depth == 0) {\n            return \"\";\n        }\n        return null;\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n\n    private static String getSchemaOrFunctionName(String functionBody, String schemaName, Function function) {\n        if (functionBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return function.getFunctionName();\n        } else {\n            return schemaName + \".\" + function.getFunctionName();\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/java/ai/chat2db/plugin/kingbase/KingBaseMetaData.java",
    "content": "package ai.chat2db.plugin.kingbase;\n\nimport ai.chat2db.plugin.kingbase.builder.KingBaseSqlBuilder;\nimport ai.chat2db.plugin.kingbase.type.KingBaseColumnTypeEnum;\nimport ai.chat2db.plugin.kingbase.type.KingBaseDefaultValueEnum;\nimport ai.chat2db.plugin.kingbase.type.KingBaseIndexTypeEnum;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport com.google.common.collect.Lists;\nimport jakarta.validation.constraints.NotEmpty;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static ai.chat2db.spi.util.SortUtils.sortDatabase;\n\npublic class KingBaseMetaData extends DefaultMetaService implements MetaData {\n\n\n    private static final String SELECT_KEY_INDEX = \"SELECT ccu.table_schema AS Foreign_schema_name, ccu.table_name AS Foreign_table_name, ccu.column_name AS Foreign_column_name, constraint_type AS Constraint_type, tc.CONSTRAINT_NAME AS Key_name, tc.TABLE_NAME, kcu.Column_name, tc.is_deferrable, tc.initially_deferred FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE tc.TABLE_SCHEMA = '%s'  AND tc.TABLE_NAME = '%s';\";\n\n\n    private List<String> systemDatabases = Arrays.asList(\"SAMPLES\", \"SECURITY\");\n\n\n    private List<String> systemSchemas = Arrays.asList(\"pg_toast\",\"pg_temp_1\",\"pg_toast_temp_1\",\"pg_catalog\",\"information_schema\");\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        List<Database> list = SQLExecutor.getInstance().execute(connection, \"SELECT datname FROM sys_database\", resultSet -> {\n            List<Database> databases = new ArrayList<>();\n            try {\n                while (resultSet.next()) {\n                    String dbName = resultSet.getString(\"datname\");\n                    if (\"template0\".equalsIgnoreCase(dbName) || \"template1\".equalsIgnoreCase(dbName) ||\n                            \"template2\".equalsIgnoreCase(dbName)) {\n                        continue;\n                    }\n                    Database database = new Database();\n                    database.setName(dbName);\n                    databases.add(database);\n                }\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            }\n            return databases;\n        });\n        return sortDatabase(list, systemDatabases, connection);\n    }\n\n    private static final String SELECT_TABLE_INDEX = \"SELECT tmp.INDISPRIMARY AS Index_primary, tmp.TABLE_SCHEM, tmp.TABLE_NAME, tmp.NON_UNIQUE, tmp.INDEX_QUALIFIER, tmp.INDEX_NAME AS Key_name, tmp.indisclustered, tmp.ORDINAL_POSITION AS Seq_in_index, trim(BOTH '\\\"' FROM sys_get_indexdef( tmp.CI_OID, tmp.ORDINAL_POSITION, FALSE) ) AS Column_name, CASE tmp.AM_NAME WHEN 'btree' THEN CASE tmp.I_INDOPTION [ tmp.ORDINAL_POSITION - 1 ] & 1 :: SMALLINT WHEN 1 THEN 'D' ELSE'A' END ELSE NULL END AS Collation, tmp.CARDINALITY, tmp.PAGES, tmp.FILTER_CONDITION , tmp.AM_NAME AS Index_method, tmp.DESCRIPTION AS Index_comment FROM ( SELECT n.nspname AS TABLE_SCHEM, ct.relname AS TABLE_NAME, NOT i.indisunique AS NON_UNIQUE, NULL AS INDEX_QUALIFIER, ci.relname AS INDEX_NAME, i.INDISPRIMARY , i.indisclustered , ( information_schema._sys_expandarray ( i.indkey ) ).n AS ORDINAL_POSITION, ci.reltuples AS CARDINALITY, ci.relpages AS PAGES, sys_get_expr ( i.indpred, i.indrelid ) AS FILTER_CONDITION, ci.OID AS CI_OID, i.indoption AS I_INDOPTION, am.amname AS AM_NAME , d.description FROM sys_class ct JOIN sys_namespace n ON ( ct.relnamespace = n.OID ) JOIN sys_index i ON ( ct.OID = i.indrelid ) JOIN sys_class ci ON ( ci.OID = i.indexrelid ) JOIN sys_am am ON ( ci.relam = am.OID ) left outer join sys_description d on i.indexrelid = d.objoid WHERE n.nspname = '%s' AND ct.relname = '%s' ) AS tmp\";\n\n    private static final String SELECT_TABLE_INDEX_8R6 = \"SELECT tmp.INDISPRIMARY AS Index_primary, tmp.TABLE_SCHEM, tmp.TABLE_NAME, tmp.NON_UNIQUE, tmp.INDEX_QUALIFIER, tmp.INDEX_NAME AS Key_name, tmp.indisclustered, tmp.ORDINAL_POSITION AS Seq_in_index, trim(BOTH '\\\"' FROM sys_get_indexdef( tmp.CI_OID, tmp.ORDINAL_POSITION, FALSE) ) AS Column_name, CASE tmp.AM_NAME WHEN 'btree' THEN CASE tmp.I_INDOPTION [ tmp.ORDINAL_POSITION - 1 ] & 1 :: SMALLINT WHEN 1 THEN 'D' ELSE'A' END ELSE NULL END AS Collation, tmp.CARDINALITY, tmp.PAGES, tmp.FILTER_CONDITION , tmp.AM_NAME AS Index_method, tmp.DESCRIPTION AS Index_comment FROM ( SELECT n.nspname AS TABLE_SCHEM, ct.relname AS TABLE_NAME, NOT i.indisunique AS NON_UNIQUE, NULL AS INDEX_QUALIFIER, ci.relname AS INDEX_NAME, i.INDISPRIMARY , i.indisclustered , ( information_schema._pg_expandarray ( i.indkey ) ).n AS ORDINAL_POSITION, ci.reltuples AS CARDINALITY, ci.relpages AS PAGES, sys_get_expr ( i.indpred, i.indrelid ) AS FILTER_CONDITION, ci.OID AS CI_OID, i.indoption AS I_INDOPTION, am.amname AS AM_NAME , d.description FROM sys_class ct JOIN sys_namespace n ON ( ct.relnamespace = n.OID ) JOIN sys_index i ON ( ct.OID = i.indrelid ) JOIN sys_class ci ON ( ci.OID = i.indexrelid ) JOIN sys_am am ON ( ci.relam = am.OID ) left outer join sys_description d on i.indexrelid = d.objoid WHERE n.nspname = '%s' AND ct.relname = '%s' ) AS tmp\";\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        String constraintSql = String.format(SELECT_KEY_INDEX, schemaName, tableName);\n        Map<String, String> constraintMap = new HashMap();\n        LinkedHashMap<String, TableIndex> foreignMap = new LinkedHashMap();\n        SQLExecutor.getInstance().execute(connection, constraintSql, resultSet -> {\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"Key_name\");\n                String constraintType = resultSet.getString(\"Constraint_type\");\n                constraintMap.put(keyName, constraintType);\n                if (StringUtils.equalsIgnoreCase(constraintType, KingBaseIndexTypeEnum.FOREIGN.getKeyword())) {\n                    TableIndex tableIndex = foreignMap.get(keyName);\n                    String columnName = resultSet.getString(\"Column_name\");\n                    if (tableIndex == null) {\n                        tableIndex = new TableIndex();\n                        tableIndex.setDatabaseName(databaseName);\n                        tableIndex.setSchemaName(schemaName);\n                        tableIndex.setTableName(tableName);\n                        tableIndex.setName(keyName);\n                        tableIndex.setForeignSchemaName(resultSet.getString(\"Foreign_schema_name\"));\n                        tableIndex.setForeignTableName(resultSet.getString(\"Foreign_table_name\"));\n                        tableIndex.setForeignColumnNamelist(Lists.newArrayList(columnName));\n                        tableIndex.setType(KingBaseIndexTypeEnum.FOREIGN.getName());\n                        foreignMap.put(keyName, tableIndex);\n                    } else {\n                        tableIndex.getForeignColumnNamelist().add(columnName);\n                    }\n                }\n            }\n            return null;\n        });\n        String version = getDbVersion();\n        String sql = String.format(SELECT_TABLE_INDEX, schemaName, tableName);\n        if(version.startsWith(\"12.\")|| version.startsWith(\"9.\")) {\n            sql = String.format(SELECT_TABLE_INDEX_8R6, schemaName, tableName);\n        }\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            LinkedHashMap<String, TableIndex> map = new LinkedHashMap(foreignMap);\n\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"Key_name\");\n                TableIndex tableIndex = map.get(keyName);\n                if (tableIndex != null) {\n                    List<TableIndexColumn> columnList = tableIndex.getColumnList();\n                    columnList.add(getTableIndexColumn(resultSet));\n                    columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition))\n                            .collect(Collectors.toList());\n                    tableIndex.setColumnList(columnList);\n                } else {\n                    TableIndex index = new TableIndex();\n                    index.setDatabaseName(databaseName);\n                    index.setSchemaName(schemaName);\n                    index.setTableName(tableName);\n                    index.setName(keyName);\n                    index.setUnique(!StringUtils.equals(\"t\", resultSet.getString(\"NON_UNIQUE\")));\n                    index.setMethod(resultSet.getString(\"Index_method\"));\n                    index.setComment(resultSet.getString(\"Index_comment\"));\n                    List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n                    tableIndexColumns.add(getTableIndexColumn(resultSet));\n                    index.setColumnList(tableIndexColumns);\n                    String constraintType = constraintMap.get(keyName);\n                    if (StringUtils.equals(\"t\", resultSet.getString(\"Index_primary\"))) {\n                        index.setType(KingBaseIndexTypeEnum.PRIMARY.getName());\n                    } else if (StringUtils.equalsIgnoreCase(constraintType, KingBaseIndexTypeEnum.UNIQUE.getName())) {\n                        index.setType(KingBaseIndexTypeEnum.UNIQUE.getName());\n                    } else {\n                        index.setType(KingBaseIndexTypeEnum.NORMAL.getName());\n                    }\n                    map.put(keyName, index);\n                }\n            }\n            return map.values().stream().collect(Collectors.toList());\n        });\n    }\n\n    private String getDbVersion(){\n        String version = Chat2DBContext.getDbVersion();\n        if(StringUtils.isNotBlank(version)){\n            return version;\n        }\n        return \"\";\n    }\n\n    private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException {\n        TableIndexColumn tableIndexColumn = new TableIndexColumn();\n        tableIndexColumn.setColumnName(resultSet.getString(\"Column_name\"));\n        tableIndexColumn.setOrdinalPosition(resultSet.getShort(\"Seq_in_index\"));\n        tableIndexColumn.setCollation(resultSet.getString(\"Collation\"));\n        tableIndexColumn.setAscOrDesc(resultSet.getString(\"Collation\"));\n        return tableIndexColumn;\n    }\n\n    private static String ROUTINES_SQL = \" SELECT p.proname, p.prokind, sys_catalog.sys_get_functiondef(p.oid) as \\\"code\\\" FROM sys_catalog.sys_proc p where p.proname='%s'\";\n\n    @Override\n    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,\n                             String functionName) {\n\n        String sql = String.format(ROUTINES_SQL, \"f\", functionName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Function function = new Function();\n            function.setDatabaseName(databaseName);\n            function.setSchemaName(schemaName);\n            function.setFunctionName(functionName);\n            if (resultSet.next()) {\n                function.setFunctionBody(resultSet.getString(\"code\"));\n            }\n            return function;\n        });\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n                               String procedureName) {\n        String sql = String.format(ROUTINES_SQL, procedureName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Procedure procedure = new Procedure();\n            procedure.setDatabaseName(databaseName);\n            procedure.setSchemaName(schemaName);\n            procedure.setProcedureName(procedureName);\n            if (resultSet.next()) {\n                procedure.setProcedureBody(resultSet.getString(\"code\"));\n            }\n            return procedure;\n        });\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new KingBaseSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(KingBaseColumnTypeEnum.getTypes())\n                //.charsets(PostgreSQLCharsetEnum.getCharsets())\n                //.collations(PostgreSQLCollationEnum.getCollations())\n                .indexTypes(KingBaseIndexTypeEnum.getIndexTypes())\n                .defaultValues(KingBaseDefaultValueEnum.getDefaultValues())\n                .build();\n    }\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"\\\"\" + name + \"\\\"\").collect(Collectors.joining(\".\"));\n    }\n\n    @Override\n    public List<String> getSystemDatabases() {\n        return systemDatabases;\n    }\n\n    @Override\n    public List<String> getSystemSchemas() {\n        return systemSchemas;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/java/ai/chat2db/plugin/kingbase/KingBasePlugin.java",
    "content": "package ai.chat2db.plugin.kingbase;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class KingBasePlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"kingbase.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new KingBaseMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new KingBaseDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/java/ai/chat2db/plugin/kingbase/builder/KingBaseSqlBuilder.java",
    "content": "package ai.chat2db.plugin.kingbase.builder;\n\nimport ai.chat2db.plugin.kingbase.type.KingBaseColumnTypeEnum;\nimport ai.chat2db.plugin.kingbase.type.KingBaseIndexTypeEnum;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.*;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n\npublic class KingBaseSqlBuilder extends DefaultSqlBuilder {\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"CREATE TABLE \");\n        script.append(\"\\\"\").append(table.getName()).append(\"\\\"\").append(\" (\").append(\" \").append(\"\\n\");\n        // append column\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            KingBaseColumnTypeEnum typeEnum = KingBaseColumnTypeEnum.getByType(column.getColumnType());\n            if(typeEnum ==null){\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n        Map<Boolean, List<TableIndex>> tableIndexMap = table.getIndexList().stream()\n                .collect(Collectors.partitioningBy(v -> KingBaseIndexTypeEnum.NORMAL.getName().equals(v.getType())));\n        // append constraint key\n        List<TableIndex> constraintList = tableIndexMap.get(Boolean.FALSE);\n        if (CollectionUtils.isNotEmpty(constraintList)) {\n            for (TableIndex index : constraintList) {\n                if (StringUtils.isBlank(index.getName()) || StringUtils.isBlank(index.getType())) {\n                    continue;\n                }\n                KingBaseIndexTypeEnum indexTypeEnum = KingBaseIndexTypeEnum.getByType(index.getType());\n                if(indexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(\"\").append(indexTypeEnum.buildIndexScript(index));\n                script.append(\",\\n\");\n            }\n\n        }\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n)\");\n        if(StringUtils.isNotBlank(table.getTablespace())){\n            script.append(\" TABLESPACE \\\"\").append(table.getTablespace()).append(\"\\\";\");\n        }else {\n            script.append(\" TABLESPACE \\\"SYS_DEFAULT\\\";\");\n        }\n        // append index\n        List<TableIndex> tableIndexList = tableIndexMap.get(Boolean.TRUE);\n        for (TableIndex tableIndex : tableIndexList) {\n            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {\n                continue;\n            }\n            script.append(\"\\n\");\n            KingBaseIndexTypeEnum indexTypeEnum = KingBaseIndexTypeEnum.getByType(tableIndex.getType());\n            if(indexTypeEnum == null){\n                continue;\n            }\n            script.append(\"\").append(indexTypeEnum.buildIndexScript(tableIndex)).append(\";\");\n        }\n\n        // append comment\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\"\\n\");\n            script.append(\"COMMENT ON TABLE\").append(\" \").append(\"\\\"\").append(table.getName()).append(\"\\\" IS '\")\n                    .append(table.getComment()).append(\"';\\n\");\n        }\n        List<TableColumn> tableColumnList = table.getColumnList().stream().filter(v -> StringUtils.isNotBlank(v.getComment())).toList();\n        for (TableColumn tableColumn : tableColumnList) {\n            KingBaseColumnTypeEnum typeEnum = KingBaseColumnTypeEnum.getByType(tableColumn.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            script.append(typeEnum.buildComment(tableColumn, typeEnum)).append(\"\\n\");\n            ;\n        }\n        List<TableIndex> indexList = table.getIndexList().stream().filter(v -> StringUtils.isNotBlank(v.getComment())).toList();\n        for (TableIndex index : indexList) {\n            KingBaseIndexTypeEnum indexEnum = KingBaseIndexTypeEnum.getByType(index.getType());\n            if(indexEnum == null){\n                continue;\n            }\n            script.append(indexEnum.buildIndexComment(index)).append(\"\\n\");\n        }\n\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(oldTable.getName()).append(\"\\\"\");\n            script.append(\"\\t\").append(\"RENAME TO \").append(\"\\\"\").append(newTable.getName()).append(\"\\\"\").append(\";\\n\");\n\n        }\n        newTable.setColumnList(newTable.getColumnList().stream().filter(v -> StringUtils.isNotBlank(v.getEditStatus())).toList());\n        newTable.setIndexList(newTable.getIndexList().stream().filter(v -> StringUtils.isNotBlank(v.getEditStatus())).toList());\n\n        //update name\n        List<TableColumn> columnNameList = newTable.getColumnList().stream().filter(v ->\n                v.getOldName() != null && !StringUtils.equals(v.getOldName(), v.getName())).toList();\n        for (TableColumn tableColumn : columnNameList) {\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(newTable.getName()).append(\"\\\" \").append(\"RENAME COLUMN \\\"\")\n                    .append(tableColumn.getOldName()).append(\"\\\" TO \\\"\").append(tableColumn.getName()).append(\"\\\";\\n\");\n        }\n\n        Map<Boolean, List<TableIndex>> tableIndexMap = newTable.getIndexList().stream()\n                .collect(Collectors.partitioningBy(v -> KingBaseIndexTypeEnum.NORMAL.getName().equals(v.getType())));\n        StringBuilder scriptModify = new StringBuilder();\n        Boolean modify = false;\n        scriptModify.append(\"ALTER TABLE \").append(\"\\\"\").append(newTable.getName()).append(\"\\\" \\n\");\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            KingBaseColumnTypeEnum typeEnum = KingBaseColumnTypeEnum.getByType(tableColumn.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            scriptModify.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\",\\n\");\n            modify = true;\n\n        }\n\n        // append modify constraint\n        for (TableIndex tableIndex : tableIndexMap.get(Boolean.FALSE)) {\n            if (StringUtils.isNotBlank(tableIndex.getType())) {\n                KingBaseIndexTypeEnum indexTypeEnum = KingBaseIndexTypeEnum.getByType(tableIndex.getType());\n                if(indexTypeEnum == null){\n                    continue;\n                }\n                scriptModify.append(\"\\t\").append(indexTypeEnum.buildModifyIndex(tableIndex)).append(\",\\n\");\n                modify = true;\n            }\n        }\n\n        if (BooleanUtils.isTrue(modify)) {\n            script.append(scriptModify);\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            script.append(\";\\n\");\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : tableIndexMap.get(Boolean.TRUE)) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                KingBaseIndexTypeEnum indexTypeEnum = KingBaseIndexTypeEnum.getByType(tableIndex.getType());\n                if(indexTypeEnum == null){\n                    continue;\n                }\n                script.append(indexTypeEnum.buildModifyIndex(tableIndex)).append(\";\\n\");\n            }\n        }\n\n        // append comment\n        if (!StringUtils.equals(oldTable.getComment(), newTable.getComment())) {\n            script.append(\"\\n\");\n            script.append(\"COMMENT ON TABLE\").append(\" \").append(\"\\\"\").append(newTable.getName()).append(\"\\\" IS '\")\n                    .append(newTable.getComment()).append(\"';\\n\");\n        }\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            KingBaseColumnTypeEnum typeEnum = KingBaseColumnTypeEnum.getByType(tableColumn.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            script.append(typeEnum.buildComment(tableColumn, typeEnum)).append(\"\\n\");\n            ;\n        }\n        List<TableIndex> indexList = newTable.getIndexList().stream().filter(v -> StringUtils.isNotBlank(v.getComment())).toList();\n        for (TableIndex index : indexList) {\n            KingBaseIndexTypeEnum indexEnum = KingBaseIndexTypeEnum.getByType(index.getType());\n            if(indexEnum == null){\n                continue;\n            }\n            script.append(indexEnum.buildIndexComment(index)).append(\"\\n\");\n        }\n\n        return script.toString();\n    }\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        StringBuilder sqlStr = new StringBuilder(sql.length() + 17);\n        sqlStr.append(sql);\n        if (offset == 0) {\n            sqlStr.append(\" LIMIT \");\n            sqlStr.append(pageSize);\n        } else {\n            sqlStr.append(\" LIMIT \");\n            sqlStr.append(pageSize);\n            sqlStr.append(\" OFFSET \");\n            sqlStr.append(offset);\n        }\n        return sqlStr.toString();\n    }\n\n    @Override\n    public String buildCreateDatabaseSql(Database database) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE DATABASE \"+database.getName());\n        String owner = database.getOwner();\n        if (StringUtils.isBlank(owner)) {\n            owner = \"SYSTEM\";\n        }\n        sqlBuilder.append(\" WITH  OWNER = \\\"\").append(owner).append(\"\\\"\");\n        if (StringUtils.isNotBlank(database.getCharset())) {\n            sqlBuilder.append(\" ENCODING  \").append(database.getCharset()).append(\"\");\n        }\n        sqlBuilder.append(\";\\n\");\n\n        if (StringUtils.isNotBlank(database.getComment())) {\n            sqlBuilder.append(\"COMMENT ON DATABASE \").append(database.getName()).append(\" IS '\").append(database.getComment()).append(\"';\");\n        }\n        return sqlBuilder.toString();\n    }\n\n\n    @Override\n    public String buildCreateSchemaSql(Schema schema){\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE SCHEMA \"+schema.getName()+\"\");\n        String owner = schema.getOwner();\n        if(StringUtils.isBlank(schema.getOwner())){\n            owner = \"SYSTEM\";\n        }\n        sqlBuilder.append(\" AUTHORIZATION \\\"\").append(owner).append(\"\\\"\");\n        return sqlBuilder.toString();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/java/ai/chat2db/plugin/kingbase/kingbase.json",
    "content": "{\n  \"dbType\": \"KINGBASE\",\n  \"supportDatabase\": true,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:kingbase8://localhost:54321/\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/kingbase8-8.6.0.jar\"\n      ],\n      \"jdbcDriver\": \"kingbase8-8.6.0.jar\",\n      \"jdbcDriverClass\": \"com.kingbase8.Driver\"\n    },\n    {\n      \"url\": \"jdbc:kingbase8://localhost:54321/\",\n      \"custom\": false,\n      \"defaultDriver\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/kingbase8-8.2.0.jar\"\n      ],\n      \"jdbcDriver\": \"kingbase8-8.2.0.jar\",\n      \"jdbcDriverClass\": \"com.kingbase8.Driver\"\n    }\n  ],\n  \"name\": \"KingBase\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/java/ai/chat2db/plugin/kingbase/type/KingBaseColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.kingbase.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum KingBaseColumnTypeEnum implements ColumnBuilder {\n\n    BIGSERIAL(\"BIGSERIAL\", false, false, true, false, false, false, true, true, false, false),\n    BIT(\"BIT\", true, false, true, false, false, false, true, true, false, false),\n    BOOL(\"BOOL\", false, false, true, false, false, false, true, true, false, false),\n    BOX(\"BOX\", false, false, true, false, false, false, true, true, false, false),\n    BYTEA(\"BYTEA\", false, false, true, false, false, false, true, true, false, false),\n\n    CHARACTER(\"CHARACTER\", true, false, true, false, false, true, true, true, false, false),\n\n    CHARACTER_VARYING(\"CHARACTER VARYING\", true, false, true, false, false, true, true, true, false, false),\n    CHAR(\"CHAR\", true, false, true, false, false, true, true, true, false, false),\n\n    CID(\"CID\", false, false, true, false, false, false, true, true, false, false),\n    CIDR(\"CIDR\", false, false, true, false, false, false, true, true, false, false),\n\n    CIRCLE(\"CIRCLE\", false, false, true, false, false, false, true, true, false, false),\n\n    CLOB(\"CLOB\", false, false, true, false, false, false, true, true, false, false),\n    DATE(\"DATE\", false, false, true, false, false, false, true, true, false, false),\n    DECIMAL(\"DECIMAL\", true, false, true, false, false, false, true, true, false, false),\n    FLOAT4(\"FLOAT4\", false, false, true, false, false, false, true, true, false, false),\n    FLOAT8(\"FLOAT8\", false, false, true, false, false, false, true, true, false, false),\n\n    INTEGER(\"INTEGER\", false, false, true, false, false, false, true, true, false, false),\n    INET(\"INET\", false, false, true, false, false, false, true, true, false, false),\n    INT2(\"INT2\", false, false, true, false, false, false, true, true, false, false),\n    INT4(\"INT4\", false, false, true, false, false, false, true, true, false, false),\n    INT8(\"INT8\", false, false, true, false, false, false, true, true, false, false),\n    INTERVAL(\"INTERVAL\", false, false, true, false, false, false, true, true, false, false),\n    JSON(\"JSON\", false, false, true, false, false, false, true, true, false, false),\n    JSONB(\"JSONB\", false, false, true, false, false, false, true, true, false, false),\n    LINE(\"LINE\", false, false, true, false, false, false, true, true, false, false),\n    LSEG(\"LSEG\", false, false, true, false, false, false, true, true, false, false),\n    MACADDR(\"MACADDR\", false, false, true, false, false, false, true, true, false, false),\n    MONEY(\"MONEY\", false, false, true, false, false, false, true, true, false, false),\n    NUMERIC(\"NUMERIC\", true, false, true, false, false, false, true, true, false, false),\n    PATH(\"PATH\", false, false, true, false, false, false, true, true, false, false),\n    POINT(\"POINT\", false, false, true, false, false, false, true, true, false, false),\n    POLYGON(\"POLYGON\", false, false, true, false, false, false, true, true, false, false),\n    SERIAL(\"SERIAL\", false, false, true, false, false, false, true, true, false, false),\n    SERIAL2(\"SERIAL2\", false, false, true, false, false, false, true, true, false, false),\n    SERIAL4(\"SERIAL4\", false, false, true, false, false, false, true, true, false, false),\n    SERIAL8(\"SERIAL8\", false, false, true, false, false, false, true, true, false, false),\n    SMALLSERIAL(\"SMALLSERIAL\", false, false, true, false, false, false, true, true, false, false),\n    TEXT(\"TEXT\", false, false, true, false, false, true, true, true, false, false),\n    TIME(\"TIME\", true, false, true, false, false, false, true, true, false, false),\n    TIMESTAMP(\"TIMESTAMP\", true, false, true, false, false, false, true, true, false, false),\n    TIMESTAMPTZ(\"TIMESTAMPTZ\", true, false, true, false, false, false, true, true, false, false),\n    TIMETZ(\"TIMETZ\", true, false, true, false, false, false, true, true, false, false),\n    TSQUERY(\"TSQUERY\", false, false, true, false, false, false, true, true, false, false),\n    TSVECTOR(\"TSVECTOR\", false, false, true, false, false, false, true, true, false, false),\n    TXID_SNAPSHOT(\"TXID_SNAPSHOT\", false, false, true, false, false, false, true, true, false, false),\n    UUID(\"UUID\", false, false, true, false, false, false, true, true, false, false),\n    VARBIT(\"VARBIT\", true, false, true, false, false, false, true, true, false, false),\n    VARCHAR(\"VARCHAR\", true, false, true, false, false, true, true, true, false, false),\n    XML(\"XML\", false, false, true, false, false, false, true, true, false, false),\n\n    ;\n\n    private static Map<String, KingBaseColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (KingBaseColumnTypeEnum value : KingBaseColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n    private ColumnType columnType;\n\n\n    KingBaseColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportValue) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, supportValue, false);\n    }\n\n    public static KingBaseColumnTypeEnum getByType(String dataType) {\n        return COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n    }\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(KingBaseColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        KingBaseColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"\\\"\").append(column.getName()).append(\"\\\"\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n\n        script.append(buildCollation(column, type)).append(\" \");\n\n        script.append(buildNullable(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n    private String buildCollation(TableColumn column, KingBaseColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportCollation() || StringUtils.isEmpty(column.getCollationName())) {\n            return \"\";\n        }\n        return StringUtils.join(\"\\\"\", column.getCollationName(), \"\\\"\");\n    }\n\n    @Override\n    public String buildModifyColumn(TableColumn column) {\n\n        if (EditStatus.DELETE.name().equals(column.getEditStatus())) {\n            return StringUtils.join(\"DROP COLUMN `\", column.getName() + \"`\");\n        }\n        if (EditStatus.ADD.name().equals(column.getEditStatus())) {\n            return StringUtils.join(\"ADD COLUMN \", buildCreateColumnSql(column));\n        }\n        if (EditStatus.MODIFY.name().equals(column.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER COLUMN \\\"\").append(column.getName()).append(\"\\\" TYPE \").append(buildDataType(column, this)).append(\",\\n\");\n            if (column.getNullable() != null && 1 == column.getNullable()) {\n                script.append(\"\\t\").append(\"ALTER COLUMN \\\"\").append(column.getName()).append(\"\\\" DROP NOT NULL ,\\n\");\n            } else {\n                script.append(\"\\t\").append(\"ALTER COLUMN \\\"\").append(column.getName()).append(\"\\\" SET NOT NULL ,\\n\");\n\n            }\n            String defaultValue = buildDefaultValue(column, this);\n            if (StringUtils.isNotBlank(defaultValue)) {\n                script.append(\"ALTER COLUMN \\\"\").append(column.getName()).append(\"\\\" SET \").append(defaultValue).append(\",\\n\");\n            }\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            return script.toString();\n        }\n        return \"\";\n    }\n\n    public String buildComment(TableColumn column, KingBaseColumnTypeEnum type) {\n        if (!this.columnType.isSupportComments() || column.getComment() == null\n                || EditStatus.DELETE.name().equals(column.getEditStatus())) {\n            return \"\";\n        }\n        return StringUtils.join(\"COMMENT ON COLUMN\", \" \\\"\", column.getTableName(),\n                \"\\\".\\\"\", column.getName(), \"\\\" IS '\", column.getComment(), \"';\");\n    }\n\n    private String buildDefaultValue(TableColumn column, KingBaseColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {\n            return \"\";\n        }\n\n        if(\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())){\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if(\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())){\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        if (Arrays.asList(CHAR, VARCHAR).contains(type)) {\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        if (Arrays.asList(TIMESTAMP, TIME, TIMETZ, TIMESTAMPTZ, DATE).contains(type)) {\n            if (\"CURRENT_TIMESTAMP\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n                return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n            }\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildNullable(TableColumn column, KingBaseColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportNullable()) {\n            return \"\";\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"NULL\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDataType(TableColumn column, KingBaseColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(VARCHAR, CHAR,CHARACTER).contains(type)) {\n            if (column.getColumnSize() == null ) {\n                return columnType;\n            }\n            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n        }\n\n        if (Arrays.asList(VARBIT, BIT).contains(type)) {\n            if (column.getColumnSize() == null ) {\n                return columnType;\n            }\n            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n        }\n\n        if (Arrays.asList(TIME, TIMETZ, TIMESTAMPTZ, TIMESTAMP).contains(type)) {\n            if (column.getColumnSize() == null || column.getColumnSize() == 0) {\n                return columnType;\n            } else {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n            }\n        }\n\n        if (Arrays.asList(DECIMAL, NUMERIC).contains(type)) {\n            if (column.getColumnSize() == null && column.getDecimalDigits() == null) {\n                return columnType;\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \")\");\n            } else {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n            }\n        }\n        return columnType;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/java/ai/chat2db/plugin/kingbase/type/KingBaseDefaultValueEnum.java",
    "content": "package ai.chat2db.plugin.kingbase.type;\n\nimport ai.chat2db.spi.model.DefaultValue;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum KingBaseDefaultValueEnum {\n    EMPTY_STRING(\"EMPTY_STRING\"),\n    NULL(\"NULL\"),\n    ;\n    private DefaultValue defaultValue;\n\n    KingBaseDefaultValueEnum(String defaultValue) {\n        this.defaultValue = new DefaultValue(defaultValue);\n    }\n\n\n    public DefaultValue getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static List<DefaultValue> getDefaultValues() {\n        return Arrays.stream(KingBaseDefaultValueEnum.values()).map(KingBaseDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/java/ai/chat2db/plugin/kingbase/type/KingBaseIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.kingbase.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum KingBaseIndexTypeEnum {\n\n    PRIMARY(\"Primary\", \"PRIMARY KEY\"),\n\n    FOREIGN(\"Foreign\", \"FOREIGN KEY\"),\n\n    NORMAL(\"Normal\", \"INDEX\"),\n\n    UNIQUE(\"Unique\", \"UNIQUE\"),\n    ;\n\n    private String name;\n    private String keyword;\n\n    private IndexType indexType;\n\n\n    KingBaseIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType =new IndexType(name);\n    }\n\n    public static KingBaseIndexTypeEnum getByType(String type) {\n        for (KingBaseIndexTypeEnum value : KingBaseIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(KingBaseIndexTypeEnum.values()).stream().map(KingBaseIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        if (NORMAL.equals(this)) {\n            script.append(\"CREATE\").append(\" \");\n            script.append(buildIndexUnique(tableIndex)).append(\" \");\n            script.append(buildIndexConcurrently(tableIndex)).append(\" \");\n            script.append(buildIndexName(tableIndex)).append(\" \");\n            script.append(\"ON \").append(\"\\\"\").append(tableIndex.getTableName()).append(\"\\\"\").append(\" \");\n            script.append(buildIndexMethod(tableIndex)).append(\" \");\n            script.append(buildIndexColumn(tableIndex));\n        } else {\n            script.append(\"CONSTRAINT\").append(\" \");\n            script.append(buildIndexName(tableIndex)).append(\" \");\n            script.append(keyword).append(\" \");\n            script.append(buildIndexColumn(tableIndex));\n            script.append(buildForeignColum(tableIndex));\n        }\n        return script.toString();\n    }\n\n    private String buildForeignColum(TableIndex tableIndex) {\n        if (FOREIGN.equals(this)) {\n            StringBuilder script = new StringBuilder();\n            script.append(\" REFERENCES \");\n            if (StringUtils.isNotBlank(tableIndex.getForeignSchemaName())) {\n                script.append(tableIndex.getForeignSchemaName()).append(\".\");\n            }\n            if (StringUtils.isNotBlank(tableIndex.getForeignTableName())) {\n                script.append(tableIndex.getForeignTableName()).append(\" \");\n            }\n            if (CollectionUtils.isNotEmpty(tableIndex.getForeignColumnNamelist())) {\n                script.append(\"(\");\n                for (String column : tableIndex.getForeignColumnNamelist()) {\n                    if (StringUtils.isNotBlank(column)) {\n                        script.append(\"\\\"\").append(column).append(\"\\\"\").append(\",\");\n                    }\n                }\n                script.deleteCharAt(script.length() - 1);\n                script.append(\")\");\n            }\n            return script.toString();\n        }\n        return \"\";\n    }\n\n    private String buildIndexMethod(TableIndex tableIndex) {\n        if (StringUtils.isNotBlank(tableIndex.getMethod())) {\n            return \"USING \" + tableIndex.getMethod();\n        } else {\n            return \"\";\n        }\n    }\n\n    private String buildIndexConcurrently(TableIndex tableIndex) {\n        if (BooleanUtils.isTrue(tableIndex.getConcurrently())) {\n            return \"CONCURRENTLY\";\n        } else {\n            return \"\";\n        }\n    }\n\n    private String buildIndexUnique(TableIndex tableIndex) {\n        if (BooleanUtils.isTrue(tableIndex.getUnique())) {\n            return \"UNIQUE \" + keyword;\n        } else {\n            return keyword;\n        }\n    }\n\n    public String buildIndexComment(TableIndex tableIndex) {\n        if (StringUtils.isBlank(tableIndex.getComment()) || EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return \"\";\n        } else if (NORMAL.equals(this)) {\n            return StringUtils.join(\"COMMENT ON INDEX\", \" \",\n                    \"\\\"\", tableIndex.getName(), \"\\\" IS '\", tableIndex.getComment(), \"';\");\n        } else {\n            return StringUtils.join(\"COMMENT ON CONSTRAINT\", \" \\\"\", tableIndex.getName(), \"\\\" ON \\\"\", tableIndex.getSchemaName(),\n                    \"\\\".\\\"\", tableIndex.getTableName(), \"\\\" IS '\", tableIndex.getComment(), \"';\");\n        }\n    }\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"\\\"\").append(column.getColumnName()).append(\"\\\"\").append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        return \"\\\"\" + tableIndex.getName() + \"\\\"\";\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        boolean isNormal = NORMAL.equals(this);\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex), isNormal ? \";\\n\" : \",\\n\\tADD \", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(isNormal ? \"\" : \"ADD \", buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (NORMAL.equals(this)) {\n            return StringUtils.join(\"DROP INDEX \\\"\", tableIndex.getOldName(), \"\\\"\");\n        }\n        return StringUtils.join(\"DROP CONSTRAINT \\\"\", tableIndex.getOldName(), \"\\\"\");\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-kingbase/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.kingbase.KingBasePlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-mysql</artifactId>\n            <version>2.0.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n    <artifactId>chat2db-mariadb</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/MariaDBManage.java",
    "content": "package ai.chat2db.plugin.mariadb;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport ai.chat2db.plugin.mysql.MysqlDBManage;\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.Function;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.SQLExecutor;\n\npublic class MariaDBManage extends MysqlDBManage implements DBManage {\n\n    String PROCEDURE_SQL = \"SELECT COUNT(*)\\n\" +\n            \"FROM information_schema.ROUTINES\\n\" +\n            \"WHERE ROUTINE_TYPE = 'PROCEDURE'\\n\" +\n            \"AND ROUTINE_NAME = '%s'\\n\" +\n            \"AND ROUTINE_SCHEMA = '%s'\";\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            boolean isCreateOrReplace = procedureBody.trim().toUpperCase().startsWith(\"CREATE OR REPLACE \");\n\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, databaseName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String checkProcedureSQL = String.format(PROCEDURE_SQL, procedure.getProcedureName().toUpperCase(),schemaName.toUpperCase());\n            String finalProcedureBody = procedureBody;\n            SQLExecutor.getInstance().execute(connection, checkProcedureSQL, resultSet -> {\n                if (resultSet.next()) {\n                    int count = resultSet.getInt(1);\n                    if (count >= 1) {\n                        if (isCreateOrReplace) {\n                            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet2 -> {\n                            });\n                        } else {\n                            throw new SQLException(\"Procedure with the same name already exists.\");\n                        }\n                    }\n                }\n            });\n            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet -> {});\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n    }\n\n    @Override\n    public void deleteProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) {\n        String procedureNewName = getSchemaOrProcedureName(procedure.getProcedureBody(), databaseName, procedure);\n        String sql = \"DROP PROCEDURE \" + procedureNewName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void deleteFunction(Connection connection, String databaseName, String schemaName, Function function) {\n        String functionNewName = getSchemaOrFunctionName(function.getFunctionBody(), databaseName, function);\n        String sql = \"DROP FUNCTION \" + functionNewName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n\n    private static String getSchemaOrFunctionName(String functionBody, String schemaName, Function function) {\n        if (functionBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return function.getFunctionName();\n        } else {\n            return schemaName + \".\" + function.getFunctionName();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/MariaDBMetaData.java",
    "content": "package ai.chat2db.plugin.mariadb;\n\n\nimport ai.chat2db.plugin.mariadb.value.MariaDBValueProcessor;\nimport ai.chat2db.plugin.mysql.MysqlMetaData;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.ValueProcessor;\n\npublic class MariaDBMetaData extends MysqlMetaData implements MetaData {\n\n    @Override\n    public ValueProcessor getValueProcessor() {\n        return new MariaDBValueProcessor();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/MariaDBPlugin.java",
    "content": "package ai.chat2db.plugin.mariadb;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class MariaDBPlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"mariadb.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new MariaDBMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new MariaDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/mariadb.json",
    "content": "{\n  \"dbType\": \"MARIADB\",\n  \"supportDatabase\": true,\n  \"supportSchema\": false,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:mariadb://localhost:3306/\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/mariadb-java-client-3.0.8.jar\"\n      ],\n      \"jdbcDriver\": \"mariadb-java-client-3.0.8.jar\",\n      \"jdbcDriverClass\": \"org.mariadb.jdbc.Driver\"\n    }\n  ],\n  \"name\": \"MariaDB\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/value/MariaDBValueProcessor.java",
    "content": "package ai.chat2db.plugin.mariadb.value;\n\nimport ai.chat2db.plugin.mariadb.value.factory.MariaDBValueProcessorFactory;\nimport ai.chat2db.plugin.mysql.value.MysqlValueProcessor;\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Objects;\n\n/**\n * @author: zgq\n * @date: 2024年05月24日 21:02\n * <br>\n *  TODO:\n *      attribute: [zerofill] example tinyint[5] zerofill 34->00034\n */\npublic class MariaDBValueProcessor extends MysqlValueProcessor {\n\n\n    private static final Logger log = LoggerFactory.getLogger(MariaDBValueProcessor.class);\n\n    @Override\n    public String getJdbcValue(JDBCDataValue dataValue) {\n        Object value = dataValue.getObject();\n        if (Objects.isNull(value)) {\n            // example: [date]->0000-00-00\n            String stringValue = dataValue.getStringValue();\n            if (Objects.nonNull(stringValue)) {\n                return stringValue;\n            }\n            return null;\n        }\n        if (value instanceof String emptyStr) {\n            if (StringUtils.isBlank(emptyStr)) {\n                return emptyStr;\n            }\n        }\n        return convertJDBCValueByType(dataValue);\n    }\n\n\n    @Override\n    public String getJdbcSqlValueString(JDBCDataValue dataValue) {\n        Object value = dataValue.getObject();\n        if (Objects.isNull(value)) {\n            //  example: [date]->0000-00-00\n            String stringValue = dataValue.getStringValue();\n            if (Objects.nonNull(stringValue)) {\n                return EasyStringUtils.escapeAndQuoteString(stringValue);\n            }\n            return \"NULL\";\n        }\n        if (value instanceof String stringValue) {\n            if (StringUtils.isBlank(stringValue)) {\n                return EasyStringUtils.quoteString(stringValue);\n            }\n        }\n        return convertJDBCValueStrByType(dataValue);\n    }\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        try {\n            DefaultValueProcessor valueProcessor = MariaDBValueProcessorFactory.getValueProcessor(dataValue.getDateTypeName());\n            if (Objects.isNull(valueProcessor)) {\n                return super.convertSQLValueByType(dataValue);\n            }\n            return valueProcessor.convertSQLValueByType(dataValue);\n        } catch (Exception e) {\n            log.warn(\"convertSQLValueByType error\", e);\n            return super.convertSQLValueByType(dataValue);\n        }\n    }\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        String type = dataValue.getType();\n        try {\n            DefaultValueProcessor valueProcessor = MariaDBValueProcessorFactory.getValueProcessor(type);\n            if (Objects.isNull(valueProcessor)) {\n                return super.convertJDBCValueByType(dataValue);\n            }\n            return valueProcessor.convertJDBCValueByType(dataValue);\n        } catch (Exception e) {\n            log.warn(\"convertJDBCValueByType error\", e);\n            return super.convertJDBCValueByType(dataValue);\n        }\n    }\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        String type = dataValue.getType();\n        try {\n            DefaultValueProcessor valueProcessor = MariaDBValueProcessorFactory.getValueProcessor(type);\n            if (Objects.isNull(valueProcessor)) {\n                return super.convertJDBCValueByType(dataValue);\n            }\n            return valueProcessor.convertJDBCValueStrByType(dataValue);\n        } catch (Exception e) {\n            log.warn(\"convertJDBCValueStrByType error\", e);\n            return super.convertJDBCValueStrByType(dataValue);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/value/factory/MariaDBValueProcessorFactory.java",
    "content": "package ai.chat2db.plugin.mariadb.value.factory;\n\nimport ai.chat2db.plugin.mariadb.value.sub.MariaDBBitProcessor;\nimport ai.chat2db.plugin.mariadb.value.sub.MariaDBGeometryProcessor;\nimport ai.chat2db.plugin.mariadb.value.sub.MariaDBTimestampProcessor;\nimport ai.chat2db.plugin.mariadb.value.sub.MariaDBYearProcessor;\nimport ai.chat2db.plugin.mysql.type.MysqlColumnTypeEnum;\nimport ai.chat2db.plugin.mysql.value.sub.*;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\n\nimport java.util.Map;\n\n/**\n * @author: zgq\n * @date: 2024年06月03日 23:16\n */\npublic class MariaDBValueProcessorFactory {\n\n    private static final Map<String, DefaultValueProcessor> PROCESSOR_MAP;\n\n    static {\n        MariaDBGeometryProcessor mariaDBGeometryProcessor = new MariaDBGeometryProcessor();\n        MysqlVarBinaryProcessor mysqlVarBinaryProcessor = new MysqlVarBinaryProcessor();\n        MariaDBTimestampProcessor mariaDBTimestampProcessor = new MariaDBTimestampProcessor();\n        MysqlTextProcessor mysqlTextProcessor = new MysqlTextProcessor();\n        PROCESSOR_MAP = Map.ofEntries(\n                //text\n                Map.entry(MysqlColumnTypeEnum.TEXT.name(), mysqlTextProcessor),\n                Map.entry(MysqlColumnTypeEnum.TINYTEXT.name(), mysqlTextProcessor),\n                Map.entry(MysqlColumnTypeEnum.MEDIUMTEXT.name(), mysqlTextProcessor),\n                Map.entry(MysqlColumnTypeEnum.LONGTEXT.name(), mysqlTextProcessor),\n                // geometry\n                Map.entry(MysqlColumnTypeEnum.GEOMETRY.name(), mariaDBGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.POINT.name(), mariaDBGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.LINESTRING.name(), mariaDBGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.POLYGON.name(), mariaDBGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.MULTIPOINT.name(), mariaDBGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.MULTILINESTRING.name(), mariaDBGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.MULTIPOLYGON.name(), mariaDBGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.GEOMETRYCOLLECTION.name(), mariaDBGeometryProcessor),\n                // binary\n                Map.entry(MysqlColumnTypeEnum.VARBINARY.name(), mysqlVarBinaryProcessor),\n                Map.entry(MysqlColumnTypeEnum.BLOB.name(), mysqlVarBinaryProcessor),\n                Map.entry(MysqlColumnTypeEnum.LONGBLOB.name(), mysqlVarBinaryProcessor),\n                Map.entry(MysqlColumnTypeEnum.TINYBLOB.name(), mysqlVarBinaryProcessor),\n                Map.entry(MysqlColumnTypeEnum.MEDIUMBLOB.name(), mysqlVarBinaryProcessor),\n                // timestamp\n                Map.entry(MysqlColumnTypeEnum.TIMESTAMP.name(), mariaDBTimestampProcessor),\n                Map.entry(MysqlColumnTypeEnum.DATETIME.name(), mariaDBTimestampProcessor),\n                //others\n                Map.entry(MysqlColumnTypeEnum.YEAR.name(), new MariaDBYearProcessor()),\n                Map.entry(MysqlColumnTypeEnum.BIT.name(), new MariaDBBitProcessor()),\n                Map.entry(MysqlColumnTypeEnum.DECIMAL.name(), new MysqlDecimalProcessor()),\n                Map.entry(MysqlColumnTypeEnum.BINARY.name(), new MysqlBinaryProcessor())\n        );\n    }\n\n    public static DefaultValueProcessor getValueProcessor(String type) {\n        return  PROCESSOR_MAP.get(type);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/value/sub/MariaDBBitProcessor.java",
    "content": "package ai.chat2db.plugin.mariadb.value.sub;\n\nimport ai.chat2db.plugin.mysql.value.template.MysqlDmlValueTemplate;\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.SQLException;\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 13:08\n */\npublic class MariaDBBitProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return getString(dataValue.getValue());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return getValue(dataValue, s -> s);\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return getValue(dataValue, this::wrap);\n    }\n\n    private String getValue(JDBCDataValue dataValue, Function<String, String> function) {\n        try {\n            //mariadb tinyint(1)\n            if ((dataValue.getMetaData().getColumnType(dataValue.getColumnIndex()) == -7)) {\n                return String.valueOf(dataValue.getInt());\n            }\n        } catch (SQLException e) {\n            super.convertJDBCValueByType(dataValue);\n        }\n        int precision = dataValue.getPrecision();\n        byte[] bytes = dataValue.getBytes();\n        if (precision == 1) {\n            //bit(1) [1 -> true] [0 -> false]\n            if (bytes.length == 1 && (bytes[0] == 0 || bytes[0] == 1)) {\n                return String.valueOf(dataValue.getBoolean());\n            }\n        }\n        //bit(m) m: 2~64\n        return function.apply(EasyStringUtils.getBitString(bytes, precision));\n    }\n\n    public String getString(String value) {\n\n        if (Objects.equals(\"true\", value.toLowerCase())) {\n            return \"1\";\n        }\n        if (Objects.equals(\"false\", value.toLowerCase())) {\n            return \"0\";\n        }\n        if (StringUtils.isBlank(value)) {\n            return \"NULL\";\n        }\n        return MysqlDmlValueTemplate.wrapBit(value);\n    }\n\n    private String wrap(String value) {\n        return MysqlDmlValueTemplate.wrapBit(value);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/value/sub/MariaDBGeometryProcessor.java",
    "content": "package ai.chat2db.plugin.mariadb.value.sub;\n\nimport ai.chat2db.plugin.mysql.value.template.MysqlDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.WKBReader;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 12:42\n */\npublic class MariaDBGeometryProcessor extends DefaultValueProcessor {\n\n\n    private static final Logger log = LoggerFactory.getLogger(MariaDBGeometryProcessor.class);\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return MysqlDmlValueTemplate.wrapGeometry(dataValue.getValue());\n    }\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        try {\n            Geometry dbGeometry = null;\n            byte[] geometryAsBytes = dataValue.getBytes();\n            if (geometryAsBytes != null) {\n                if (geometryAsBytes.length < 5) {\n                    throw new Exception(\"Invalid geometry inputStream - less than five bytes\");\n                }\n\n                //first four bytes of the geometry are the SRID,\n                //followed by the actual WKB.  Determine the SRID\n                //here\n                byte[] sridBytes = new byte[4];\n                System.arraycopy(geometryAsBytes, 0, sridBytes, 0, 4);\n                boolean bigEndian = (geometryAsBytes[4] == 0x00);\n\n                int srid = 0;\n                if (bigEndian) {\n                    for (int i = 0; i < sridBytes.length; i++) {\n                        srid = (srid << 8) + (sridBytes[i] & 0xff);\n                    }\n                } else {\n                    for (int i = 0; i < sridBytes.length; i++) {\n                        srid += (sridBytes[i] & 0xff) << (8 * i);\n                    }\n                }\n\n                //use the JTS WKBReader for WKB parsing\n                WKBReader wkbReader = new WKBReader();\n\n                //copy the byte array, removing the first four\n                //SRID bytes\n                byte[] wkb = new byte[geometryAsBytes.length - 4];\n                System.arraycopy(geometryAsBytes, 4, wkb, 0, wkb.length);\n                dbGeometry = wkbReader.read(wkb);\n                dbGeometry.setSRID(srid);\n            }\n            return dbGeometry != null ? dbGeometry.toString() : null;\n        } catch (Exception e) {\n            log.warn(\"Error converting database geometry\", e);\n            return dataValue.getStringValue();\n        }\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return MysqlDmlValueTemplate.wrapGeometry(convertJDBCValueByType(dataValue));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/value/sub/MariaDBTimestampProcessor.java",
    "content": "package ai.chat2db.plugin.mariadb.value.sub;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 18:26\n */\npublic class MariaDBTimestampProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return EasyStringUtils.quoteString(dataValue.getValue());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getStringValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return EasyStringUtils.quoteString(dataValue.getStringValue());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/java/ai/chat2db/plugin/mariadb/value/sub/MariaDBYearProcessor.java",
    "content": "package ai.chat2db.plugin.mariadb.value.sub;\n\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * 功能描述\n *\n * @author: zgq\n * @date: 2024年07月15日 20:19\n */\npublic class MariaDBYearProcessor  extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return dataValue.getValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getStringValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return dataValue.getStringValue();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mariadb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.mariadb.MariaDBPlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mongodb/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n\n    <artifactId>chat2db-mongodb</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mongodb/src/main/java/ai/chat2db/plugin/mongodb/MongodbCommandExecutor.java",
    "content": "package ai.chat2db.plugin.mongodb;\n\nimport ai.chat2db.spi.model.Command;\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.spi.sql.SQLExecutor;\n\nimport java.util.List;\n\npublic class MongodbCommandExecutor extends SQLExecutor {\n\n    @Override\n    public List<ExecuteResult> executeSelectTable(Command command) {\n        String sql = \"db.\" + command.getTableName() + \".find()\";\n        command.setScript(sql);\n        return execute(command);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mongodb/src/main/java/ai/chat2db/plugin/mongodb/MongodbManage.java",
    "content": "package ai.chat2db.plugin.mongodb;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.springframework.util.StringUtils;\n\npublic class MongodbManage extends DefaultDBManage implements DBManage {\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n        if (ObjectUtils.anyNull(connectInfo) || StringUtils.isEmpty(connectInfo.getSchemaName())) {\n            return;\n        }\n        String schemaName = connectInfo.getSchemaName();\n        if (StringUtils.isEmpty(schemaName)) {\n            return;\n        }\n        try {\n            SQLExecutor.getInstance().execute(connection, \"use \" + schemaName + \";\");\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \" db. \" + tableName + \".drop();\";\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mongodb/src/main/java/ai/chat2db/plugin/mongodb/MongodbMetaData.java",
    "content": "package ai.chat2db.plugin.mongodb;\n\nimport ai.chat2db.spi.CommandExecutor;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.Database;\nimport com.google.common.collect.Lists;\n\nimport java.sql.Connection;\nimport java.util.List;\n\n\n\npublic class MongodbMetaData extends DefaultMetaService implements MetaData {\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        return Lists.newArrayList();\n    }\n\n    @Override\n    public CommandExecutor getCommandExecutor() {\n        return new MongodbCommandExecutor();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mongodb/src/main/java/ai/chat2db/plugin/mongodb/MongodbPlugin.java",
    "content": "package ai.chat2db.plugin.mongodb;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class MongodbPlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"mongodb.json\", DBConfig.class);\n\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new MongodbMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new MongodbManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mongodb/src/main/java/ai/chat2db/plugin/mongodb/mongodb.json",
    "content": "{\n  \"dbType\": \"MONGODB\",\n  \"supportDatabase\": false,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:mongodb://localhost:27017\",\n      \"defaultDriver\":true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/mongo-jdbc-standalone-1.18.jar\"\n      ],\n      \"jdbcDriver\": \"mongo-jdbc-standalone-1.18.jar\",\n      \"jdbcDriverClass\": \"com.dbschema.MongoJdbcDriver\"\n    }\n  ],\n  \"name\": \"Mongodb\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mongodb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.mongodb.MongodbPlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-mysql</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlDBManage.java",
    "content": "package ai.chat2db.plugin.mysql;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.model.Function;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport cn.hutool.core.date.DateUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.util.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Date;\n\nimport static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;\n\n@Slf4j\npublic class MysqlDBManage extends DefaultDBManage implements DBManage {\n\n    private static String PROCEDURE_SQL = \"SELECT COUNT(*) FROM INFORMATION_SCHEMA.ROUTINES \" +\n            \"WHERE ROUTINE_SCHEMA = '%s' AND ROUTINE_NAME = '%s' AND ROUTINE_TYPE = 'PROCEDURE'\";\n\n    @Override\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        asyncContext.write(String.format(EXPORT_TITLE, DateUtil.format(new Date(), NORM_DATETIME_PATTERN)));\n        exportTables(connection, databaseName, schemaName, asyncContext);\n        asyncContext.setProgress(50);\n        exportViews(connection, databaseName, asyncContext);\n        asyncContext.setProgress(60);\n        exportProcedures(connection, asyncContext);\n        asyncContext.setProgress(70);\n        exportTriggers(connection, asyncContext);\n        asyncContext.setProgress(90);\n        exportFunctions(connection, databaseName, asyncContext);\n        asyncContext.finish();\n    }\n\n    private void exportFunctions(Connection connection, String databaseName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getFunctions(databaseName, null, null)) {\n            while (resultSet.next()) {\n                exportFunction(connection, resultSet.getString(\"FUNCTION_NAME\"), asyncContext);\n            }\n\n        }\n    }\n\n    private void exportFunction(Connection connection, String functionName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SHOW CREATE FUNCTION %s;\", functionName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                asyncContext.write(String.format(FUNCTION_TITLE, functionName));\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP FUNCTION IF EXISTS \").append(functionName).append(\";\").append(\"\\n\");\n\n                sqlBuilder.append(\"delimiter ;;\").append(\"\\n\").append(resultSet.getString(\"Create Function\")).append(\";;\")\n                        .append(\"\\n\").append(\"delimiter ;\").append(\"\\n\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportTables(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        asyncContext.write(\"SET FOREIGN_KEY_CHECKS=0;\");\n        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, null, null, new String[]{\"TABLE\", \"SYSTEM TABLE\"})) {\n            while (resultSet.next()) {\n                String tableName = resultSet.getString(\"TABLE_NAME\");\n                exportTable(connection, databaseName, schemaName, tableName, asyncContext);\n            }\n        }\n        asyncContext.write(\"SET FOREIGN_KEY_CHECKS=1;\");\n    }\n\n\n    public void exportTable(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"show create table %s \", tableName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                asyncContext.write(String.format(TABLE_TITLE, tableName));\n                sqlBuilder.append(\"DROP TABLE IF EXISTS \").append(format(tableName)).append(\";\").append(\"\\n\")\n                        .append(resultSet.getString(\"Create Table\")).append(\";\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n                if (asyncContext.isContainsData()) {\n                    exportTableData(connection, databaseName, schemaName, tableName, asyncContext);\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"export table error\", e);\n            asyncContext.error(String.format(\"export table %s error:%s\", tableName, e.getMessage()));\n        }\n    }\n\n\n    private void exportViews(Connection connection, String databaseName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, null, null, new String[]{\"VIEW\"})) {\n            while (resultSet.next()) {\n                exportView(connection, resultSet.getString(\"TABLE_NAME\"), asyncContext);\n            }\n        }\n    }\n\n    private void exportView(Connection connection, String viewName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"show create view %s \", viewName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                asyncContext.write(String.format(VIEW_TITLE, viewName));\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP VIEW IF EXISTS \").append(format(viewName)).append(\";\").append(\"\\n\")\n                        .append(resultSet.getString(\"Create View\")).append(\";\").append(\"\\n\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportProcedures(Connection connection, AsyncContext asyncContext) throws SQLException {\n        String sql = \"SHOW PROCEDURE STATUS WHERE Db = DATABASE()\";\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                exportProcedure(connection, resultSet.getString(\"Name\"), asyncContext);\n            }\n        }\n    }\n\n    private void exportProcedure(Connection connection, String procedureName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"show create procedure %s \", procedureName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                asyncContext.write(String.format(PROCEDURE_TITLE, procedureName));\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP PROCEDURE IF EXISTS \").append(format(procedureName)).append(\";\").append(\"\\n\")\n                        .append(\"delimiter ;;\").append(\"\\n\").append(resultSet.getString(\"Create Procedure\")).append(\";;\")\n                        .append(\"\\n\").append(\"delimiter ;\").append(\"\\n\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportTriggers(Connection connection, AsyncContext asyncContext) throws SQLException {\n        String sql = \"SHOW TRIGGERS\";\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String triggerName = resultSet.getString(\"Trigger\");\n                exportTrigger(connection, triggerName, asyncContext);\n            }\n        }\n    }\n\n    private void exportTrigger(Connection connection, String triggerName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"show create trigger %s \", triggerName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                asyncContext.write(String.format(TRIGGER_TITLE, triggerName));\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP TRIGGER IF EXISTS \").append(format(triggerName)).append(\";\").append(\"\\n\")\n                        .append(\"delimiter ;;\").append(\"\\n\").append(resultSet.getString(\"SQL Original Statement\")).append(\";;\")\n                        .append(\"\\n\").append(\"delimiter ;\").append(\"\\n\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, databaseName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String checkProcedureSQL = String.format(PROCEDURE_SQL, databaseName, procedure.getProcedureName());\n            SQLExecutor.getInstance().execute(connection, checkProcedureSQL, resultSet -> {\n                try {\n                    if (resultSet.next()) {\n                        int count = resultSet.getInt(1);\n                        if (count >= 1) {\n                            throw new Exception(\"Procedure already exists\");\n                        }\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            });\n            SQLExecutor.getInstance().execute(connection, procedureBody, resultSet -> {});\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        if (StringUtils.isEmpty(database)) {\n            return;\n        }\n        try {\n            SQLExecutor.getInstance().execute(connection, \"use `\" + database + \"`;\");\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"DROP TABLE \" + format(tableName);\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void deleteProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) {\n        String procedureNewName = getSchemaOrProcedureName(procedure.getProcedureBody(), databaseName, procedure);\n        String sql = \"DROP PROCEDURE \" + procedureNewName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void deleteFunction(Connection connection, String databaseName, String schemaName, Function function) {\n        String functionNewName = getSchemaOrFunctionName(function.getFunctionBody(), databaseName, function);\n        String sql = \"DROP FUNCTION \" + functionNewName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n\n    private static String getSchemaOrFunctionName(String functionBody, String schemaName, Function function) {\n        if (functionBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return function.getFunctionName();\n        } else {\n            return schemaName + \".\" + function.getFunctionName();\n        }\n    }\n\n    public static String format(String tableName) {\n        return \"`\" + tableName + \"`\";\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlMetaData.java",
    "content": "package ai.chat2db.plugin.mysql;\n\nimport ai.chat2db.plugin.mysql.builder.MysqlSqlBuilder;\nimport ai.chat2db.plugin.mysql.type.*;\nimport ai.chat2db.plugin.mysql.value.MysqlValueProcessor;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport jakarta.validation.constraints.NotEmpty;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static ai.chat2db.spi.util.SortUtils.sortDatabase;\n\npublic class MysqlMetaData extends DefaultMetaService implements MetaData {\n\n    private List<String> systemDatabases = Arrays.asList(\"information_schema\", \"performance_schema\", \"mysql\", \"sys\");\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        List<Database> databases = SQLExecutor.getInstance().databases(connection);\n        return sortDatabase(databases, systemDatabases, connection);\n    }\n\n\n    private static String TABLES_SQL\n            = \"SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, VERSION, TABLE_ROWS, DATA_LENGTH, AUTO_INCREMENT, CREATE_TIME, UPDATE_TIME, TABLE_COLLATION, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = '%s'\";\n    @Override\n    public List<Table> tables(Connection connection, @NotEmpty String databaseName, String schemaName, String tableName) {\n        String sql = String.format(TABLES_SQL, databaseName);\n        if(StringUtils.isNotBlank(tableName)){\n            sql += \" AND TABLE_NAME = '\" + tableName + \"'\";\n        }\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            List<Table> tables = new ArrayList<>();\n            while (resultSet.next()) {\n                Table table = new Table();\n                table.setDatabaseName(databaseName);\n                table.setSchemaName(schemaName);\n                table.setName(resultSet.getString(\"TABLE_NAME\"));\n                table.setEngine(resultSet.getString(\"ENGINE\"));\n                table.setRows(resultSet.getLong(\"TABLE_ROWS\"));\n                table.setDataLength(resultSet.getLong(\"DATA_LENGTH\"));\n                table.setCreateTime(resultSet.getString(\"CREATE_TIME\"));\n                table.setUpdateTime(resultSet.getString(\"UPDATE_TIME\"));\n                table.setCollate(resultSet.getString(\"TABLE_COLLATION\"));\n                table.setComment(resultSet.getString(\"TABLE_COMMENT\"));\n                tables.add(table);\n            }\n            return tables;\n        });\n    }\n\n\n    @Override\n    public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           @NotEmpty String tableName) {\n        String sql = \"SHOW CREATE TABLE \" + format(databaseName) + \".\"\n                + format(tableName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            if (resultSet.next()) {\n                return resultSet.getString(\"Create Table\");\n            }\n            return null;\n        });\n    }\n\n    public static String format(String tableName) {\n        return \"`\" + tableName + \"`\";\n    }\n\n    private static String ROUTINES_SQL\n            =\n            \"SELECT SPECIFIC_NAME, ROUTINE_COMMENT, ROUTINE_DEFINITION FROM information_schema.routines WHERE \"\n                    + \"routine_type = '%s' AND ROUTINE_SCHEMA ='%s'  AND \"\n                    + \"routine_name = '%s';\";\n\n    @Override\n    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,\n                             String functionName) {\n\n        String functionInfoSql = String.format(ROUTINES_SQL, \"FUNCTION\", databaseName, functionName);\n        Function function = SQLExecutor.getInstance().execute(connection, functionInfoSql, resultSet -> {\n            Function f = new Function();\n            f.setDatabaseName(databaseName);\n            f.setSchemaName(schemaName);\n            f.setFunctionName(functionName);\n            if (resultSet.next()) {\n                f.setSpecificName(resultSet.getString(\"SPECIFIC_NAME\"));\n                f.setRemarks(resultSet.getString(\"ROUTINE_COMMENT\"));\n            }\n            return f;\n        });\n        String functionDDlSql = String.format(\"SHOW CREATE FUNCTION %s\", functionName);\n        SQLExecutor.getInstance().execute(connection, functionDDlSql, resultSet -> {\n            if (resultSet.next()) {\n                function.setFunctionBody(resultSet.getString(\"Create Function\"));\n            }\n        });\n        return function;\n\n    }\n\n    private static String TRIGGER_SQL\n            = \"SELECT TRIGGER_NAME,EVENT_MANIPULATION, ACTION_STATEMENT  FROM INFORMATION_SCHEMA.TRIGGERS where \"\n            + \"TRIGGER_SCHEMA = '%s' AND TRIGGER_NAME = '%s';\";\n\n    private static String TRIGGER_SQL_LIST\n            = \"SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS where TRIGGER_SCHEMA = '%s';\";\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        List<Trigger> triggers = new ArrayList<>();\n        String sql = String.format(TRIGGER_SQL_LIST, databaseName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Trigger trigger = new Trigger();\n                trigger.setTriggerName(resultSet.getString(\"TRIGGER_NAME\"));\n                trigger.setSchemaName(schemaName);\n                trigger.setDatabaseName(databaseName);\n                triggers.add(trigger);\n            }\n            return triggers;\n        });\n    }\n\n\n    @Override\n    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           String triggerName) {\n\n        String sql = String.format(TRIGGER_SQL, databaseName, triggerName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Trigger trigger = new Trigger();\n            trigger.setDatabaseName(databaseName);\n            trigger.setSchemaName(schemaName);\n            trigger.setTriggerName(triggerName);\n            if (resultSet.next()) {\n                trigger.setTriggerBody(resultSet.getString(\"ACTION_STATEMENT\"));\n            }\n            return trigger;\n        });\n    }\n\n    @Override\n    public List<Procedure> procedures(Connection connection, String databaseName, String schemaName) {\n        String sql = \"SHOW PROCEDURE STATUS WHERE Db = DATABASE()\";\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            ArrayList<Procedure> procedures = new ArrayList<>();\n            while (resultSet.next()) {\n                Procedure procedure = new Procedure();\n                procedure.setProcedureName(resultSet.getString(\"Name\"));\n                procedures.add(procedure);\n            }\n            return procedures;\n        });\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n                               String procedureName) {\n        String routinesSql = String.format(ROUTINES_SQL, \"PROCEDURE\", databaseName, procedureName);\n        String showCreateProcedureSql = \"SHOW CREATE PROCEDURE \" + procedureName;\n        Procedure procedure = SQLExecutor.getInstance().execute(connection, routinesSql, resultSet -> {\n            Procedure p = new Procedure();\n            p.setDatabaseName(databaseName);\n            p.setSchemaName(schemaName);\n            p.setProcedureName(procedureName);\n            if (resultSet.next()) {\n                p.setSpecificName(resultSet.getString(\"SPECIFIC_NAME\"));\n                p.setRemarks(resultSet.getString(\"ROUTINE_COMMENT\"));\n            }\n            return p;\n        });\n        SQLExecutor.getInstance().execute(connection, showCreateProcedureSql, resultSet -> {\n            if (resultSet.next()) {\n                procedure.setProcedureBody(resultSet.getString(\"Create Procedure\"));\n            }\n        });\n        return procedure;\n    }\n\n    private static String SELECT_TABLE_COLUMNS = \"SELECT * FROM information_schema.COLUMNS  WHERE TABLE_SCHEMA =  '%s'  AND TABLE_NAME =  '%s'  order by ORDINAL_POSITION\";\n\n    @Override\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = String.format(SELECT_TABLE_COLUMNS, databaseName, tableName);\n        List<TableColumn> tableColumns = new ArrayList<>();\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                TableColumn column = new TableColumn();\n                column.setDatabaseName(databaseName);\n                column.setTableName(tableName);\n                column.setOldName(resultSet.getString(\"COLUMN_NAME\"));\n                column.setName(resultSet.getString(\"COLUMN_NAME\"));\n                //column.setColumnType(resultSet.getString(\"COLUMN_TYPE\"));\n                column.setColumnType(resultSet.getString(\"DATA_TYPE\").toUpperCase());\n                //column.setDataType(resultSet.getInt(\"DATA_TYPE\"));\n                column.setDefaultValue(resultSet.getString(\"COLUMN_DEFAULT\"));\n                column.setAutoIncrement(resultSet.getString(\"EXTRA\").contains(\"auto_increment\"));\n                column.setComment(resultSet.getString(\"COLUMN_COMMENT\"));\n                column.setPrimaryKey(\"PRI\".equalsIgnoreCase(resultSet.getString(\"COLUMN_KEY\")));\n                column.setNullable(\"YES\".equalsIgnoreCase(resultSet.getString(\"IS_NULLABLE\")) ? 1 : 0);\n                column.setOrdinalPosition(resultSet.getInt(\"ORDINAL_POSITION\"));\n                column.setDecimalDigits(resultSet.getInt(\"NUMERIC_SCALE\"));\n                column.setCharSetName(resultSet.getString(\"CHARACTER_SET_NAME\"));\n                column.setCollationName(resultSet.getString(\"COLLATION_NAME\"));\n                setColumnSize(column, resultSet.getString(\"COLUMN_TYPE\"));\n                tableColumns.add(column);\n            }\n            return tableColumns;\n        });\n    }\n\n    private void setColumnSize(TableColumn column, String columnType) {\n        try {\n            if (columnType.contains(\"(\")) {\n                String size = columnType.substring(columnType.indexOf(\"(\") + 1, columnType.indexOf(\")\"));\n                if (\"SET\".equalsIgnoreCase(column.getColumnType()) || \"ENUM\".equalsIgnoreCase(column.getColumnType())) {\n                    column.setValue(size);\n                } else {\n                    if (size.contains(\",\")) {\n                        String[] sizes = size.split(\",\");\n                        if (StringUtils.isNotBlank(sizes[0])) {\n                            column.setColumnSize(Integer.parseInt(sizes[0]));\n                        }\n                        if (StringUtils.isNotBlank(sizes[1])) {\n                            column.setDecimalDigits(Integer.parseInt(sizes[1]));\n                        }\n                    } else {\n                        column.setColumnSize(Integer.parseInt(size));\n                    }\n                }\n            }\n        } catch (Exception e) {\n        }\n    }\n\n    private static String VIEW_DDL_SQL = \"show create view %s\";\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_DDL_SQL, viewName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Table table = new Table();\n            table.setDatabaseName(databaseName);\n            table.setSchemaName(schemaName);\n            table.setName(viewName);\n            if (resultSet.next()) {\n                table.setDdl(resultSet.getString(\"Create View\"));\n            }\n            return table;\n        });\n    }\n\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        StringBuilder queryBuf = new StringBuilder(\"SHOW INDEX FROM \");\n        queryBuf.append(\"`\").append(tableName).append(\"`\");\n        queryBuf.append(\" FROM \");\n        queryBuf.append(\"`\").append(databaseName).append(\"`\");\n        return SQLExecutor.getInstance().execute(connection, queryBuf.toString(), resultSet -> {\n            LinkedHashMap<String, TableIndex> map = new LinkedHashMap();\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"Key_name\");\n                TableIndex tableIndex = map.get(keyName);\n                if (tableIndex != null) {\n                    List<TableIndexColumn> columnList = tableIndex.getColumnList();\n                    columnList.add(getTableIndexColumn(resultSet));\n                    columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition))\n                            .collect(Collectors.toList());\n                    tableIndex.setColumnList(columnList);\n                } else {\n                    TableIndex index = new TableIndex();\n                    index.setDatabaseName(databaseName);\n                    index.setSchemaName(schemaName);\n                    index.setTableName(tableName);\n                    index.setName(keyName);\n                    index.setUnique(!resultSet.getBoolean(\"Non_unique\"));\n                    index.setType(resultSet.getString(\"Index_type\"));\n                    index.setComment(resultSet.getString(\"Index_comment\"));\n                    List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n                    tableIndexColumns.add(getTableIndexColumn(resultSet));\n                    index.setColumnList(tableIndexColumns);\n                    if (\"PRIMARY\".equalsIgnoreCase(keyName)) {\n                        index.setType(MysqlIndexTypeEnum.PRIMARY_KEY.getName());\n                    } else if (index.getUnique()) {\n                        index.setType(MysqlIndexTypeEnum.UNIQUE.getName());\n                    } else if (\"SPATIAL\".equalsIgnoreCase(index.getType())) {\n                        index.setType(MysqlIndexTypeEnum.SPATIAL.getName());\n                    } else if (\"FULLTEXT\".equalsIgnoreCase(index.getType())) {\n                        index.setType(MysqlIndexTypeEnum.FULLTEXT.getName());\n                    } else {\n                        index.setType(MysqlIndexTypeEnum.NORMAL.getName());\n                    }\n                    map.put(keyName, index);\n                }\n            }\n            return map.values().stream().collect(Collectors.toList());\n        });\n\n    }\n\n    private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException {\n        TableIndexColumn tableIndexColumn = new TableIndexColumn();\n        tableIndexColumn.setColumnName(resultSet.getString(\"Column_name\"));\n        tableIndexColumn.setOrdinalPosition(resultSet.getShort(\"Seq_in_index\"));\n        tableIndexColumn.setCollation(resultSet.getString(\"Collation\"));\n        tableIndexColumn.setCardinality(resultSet.getLong(\"Cardinality\"));\n        tableIndexColumn.setSubPart(resultSet.getLong(\"Sub_part\"));\n        String collation = resultSet.getString(\"Collation\");\n        if (\"a\".equalsIgnoreCase(collation)) {\n            tableIndexColumn.setAscOrDesc(\"ASC\");\n        } else if (\"d\".equalsIgnoreCase(collation)) {\n            tableIndexColumn.setAscOrDesc(\"DESC\");\n        }\n        return tableIndexColumn;\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new MysqlSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(MysqlColumnTypeEnum.getTypes())\n                .charsets(MysqlCharsetEnum.getCharsets())\n                .collations(MysqlCollationEnum.getCollations())\n                .indexTypes(MysqlIndexTypeEnum.getIndexTypes())\n                .defaultValues(MysqlDefaultValueEnum.getDefaultValues())\n                .build();\n    }\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"`\" + name + \"`\").collect(Collectors.joining(\".\"));\n    }\n\n//    @Override\n//    public ValueHandler getValueHandler() {\n//        return new MysqlValueHandler();\n//    }\n\n    @Override\n    public ValueProcessor getValueProcessor() {\n        return new MysqlValueProcessor();\n    }\n\n    @Override\n    public List<String> getSystemDatabases() {\n        return systemDatabases;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlPlugin.java",
    "content": "package ai.chat2db.plugin.mysql;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class MysqlPlugin implements Plugin {\n\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"mysql.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new MysqlMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new MysqlDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlValueHandler.java",
    "content": "//package ai.chat2db.plugin.mysql;\n//\n//import ai.chat2db.plugin.mysql.type.MysqlColumnTypeEnum;\n//import ai.chat2db.plugin.mysql.value.GeometryValueHandler;\n//import ai.chat2db.spi.ValueHandler;\n//import ai.chat2db.spi.jdbc.DefaultValueHandler;\n//\n//import java.sql.ResultSet;\n//import java.sql.SQLException;\n//import java.util.Map;\n//\n//public class MysqlValueHandler extends DefaultValueHandler {\n//\n//    private static final Map<String, ValueHandler> VALUE_HANDLER_MAP = Map.of(\n//            MysqlColumnTypeEnum.GEOMETRY.name(), new GeometryValueHandler()\n//    );\n//\n//    @Override\n//    public String getString(ResultSet rs, int index, boolean limitSize) throws SQLException {\n//        try {\n//            Object obj = rs.getObject(index);\n//            if (obj == null) {\n//                return null;\n//            }\n//            String columnTypeName = rs.getMetaData().getColumnTypeName(index);\n//            if (MysqlColumnTypeEnum.GEOMETRY.name().equalsIgnoreCase(columnTypeName)\n//                    || MysqlColumnTypeEnum.POINT.name().equalsIgnoreCase(columnTypeName)\n//                    || MysqlColumnTypeEnum.LINESTRING.name().equalsIgnoreCase(columnTypeName)\n//                    || MysqlColumnTypeEnum.POLYGON.name().equalsIgnoreCase(columnTypeName)\n//                    || MysqlColumnTypeEnum.MULTIPOINT.name().equalsIgnoreCase(columnTypeName)\n//                    || MysqlColumnTypeEnum.MULTILINESTRING.name().equalsIgnoreCase(columnTypeName)\n//                    || MysqlColumnTypeEnum.MULTIPOLYGON.name().equalsIgnoreCase(columnTypeName)\n//                    || MysqlColumnTypeEnum.GEOMETRYCOLLECTION.name().equalsIgnoreCase(columnTypeName)\n//            ) {\n//                ValueHandler handler = VALUE_HANDLER_MAP.get(MysqlColumnTypeEnum.GEOMETRY.name());\n//                return handler.getString(rs, index, limitSize);\n//            } else {\n//                return super.getString(rs, index, limitSize);\n//            }\n//        }catch (Exception e){\n//            return rs.getString(index);\n//        }\n//    }\n//\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/builder/MysqlSqlBuilder.java",
    "content": "package ai.chat2db.plugin.mysql.builder;\n\nimport ai.chat2db.plugin.mysql.type.MysqlColumnTypeEnum;\nimport ai.chat2db.plugin.mysql.type.MysqlIndexTypeEnum;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.util.SqlUtils;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.*;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.Collectors;\n\n\npublic class MysqlSqlBuilder extends DefaultSqlBuilder {\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"CREATE TABLE \");\n        if (StringUtils.isNotBlank(table.getDatabaseName())) {\n            script.append(\"`\").append(table.getDatabaseName()).append(\"`\").append(\".\");\n        }\n        script.append(\"`\").append(table.getName()).append(\"`\").append(\" (\").append(\"\\n\");\n\n        // append column\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            MysqlColumnTypeEnum typeEnum = MysqlColumnTypeEnum.getByType(column.getColumnType());\n            if (typeEnum == null) {\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n\n        // append primary key and index\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {\n                continue;\n            }\n            MysqlIndexTypeEnum mysqlIndexTypeEnum = MysqlIndexTypeEnum.getByType(tableIndex.getType());\n            if (mysqlIndexTypeEnum == null) {\n                continue;\n            }\n            script.append(\"\\t\").append(\"\").append(mysqlIndexTypeEnum.buildIndexScript(tableIndex)).append(\",\\n\");\n        }\n\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n)\");\n\n\n        if (StringUtils.isNotBlank(table.getEngine())) {\n            script.append(\" ENGINE=\").append(table.getEngine());\n        }\n\n        if (StringUtils.isNotBlank(table.getCharset())) {\n            script.append(\" DEFAULT CHARACTER SET=\").append(table.getCharset());\n        }\n\n        if (StringUtils.isNotBlank(table.getCollate())) {\n            script.append(\" COLLATE=\").append(table.getCollate());\n        }\n\n        if (table.getIncrementValue() != null) {\n            script.append(\" AUTO_INCREMENT=\").append(table.getIncrementValue());\n        }\n\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\" COMMENT='\").append(table.getComment()).append(\"'\");\n        }\n\n        if (StringUtils.isNotBlank(table.getPartition())) {\n            script.append(\" \\n\").append(table.getPartition());\n        }\n        script.append(\";\");\n\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder tableBuilder = new StringBuilder();\n        tableBuilder.append(\"ALTER TABLE \");\n        if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {\n            tableBuilder.append(\"`\").append(oldTable.getDatabaseName()).append(\"`\").append(\".\");\n        }\n        tableBuilder.append(\"`\").append(oldTable.getName()).append(\"`\").append(\"\\n\");\n\n        StringBuilder script = new StringBuilder();\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(\"\\t\").append(\"RENAME TO \").append(\"`\").append(newTable.getName()).append(\"`\").append(\",\\n\");\n        }\n        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {\n            script.append(\"\\t\").append(\"COMMENT=\").append(\"'\").append(newTable.getComment()).append(\"'\").append(\",\\n\");\n        }\n        if (oldTable.getIncrementValue() != newTable.getIncrementValue()) {\n            script.append(\"\\t\").append(\"AUTO_INCREMENT=\").append(newTable.getIncrementValue()).append(\",\\n\");\n        }\n\n        // 判断新增字段\n        List<TableColumn> addColumnList = new ArrayList<>();\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if (tableColumn.getEditStatus() != null ? tableColumn.getEditStatus().equals(\"ADD\") : false) {\n                addColumnList.add(tableColumn);\n            }\n        }\n\n        // 判断移动的字段\n        List<TableColumn> moveColumnList = movedElements(oldTable.getColumnList(), newTable.getColumnList());\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if ((StringUtils.isNotBlank(tableColumn.getEditStatus()) && StringUtils.isNotBlank(tableColumn.getColumnType())\n                    && StringUtils.isNotBlank(tableColumn.getName())) || moveColumnList.contains(tableColumn) || addColumnList.contains(tableColumn)) {\n                MysqlColumnTypeEnum typeEnum = MysqlColumnTypeEnum.getByType(tableColumn.getColumnType());\n                if (typeEnum == null) {\n                    continue;\n                }\n                if (moveColumnList.contains(tableColumn) || addColumnList.contains(tableColumn)) {\n                    script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn, true, findPrevious(tableColumn, newTable))).append(\",\\n\");\n                } else {\n                    script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\",\\n\");\n                }\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                MysqlIndexTypeEnum mysqlIndexTypeEnum = MysqlIndexTypeEnum.getByType(tableIndex.getType());\n                if (mysqlIndexTypeEnum == null) {\n                    continue;\n                }\n                script.append(\"\\t\").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(\",\\n\");\n            }\n        }\n\n        // append reorder column\n        // script.append(buildGenerateReorderColumnSql(oldTable, newTable));\n\n        if (script.length() > 2) {\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            script.append(\";\");\n            return tableBuilder.append(script).toString();\n        } else {\n            return StringUtils.EMPTY;\n        }\n\n    }\n\n    private String findPrevious(TableColumn tableColumn, Table newTable) {\n        int index = newTable.getColumnList().indexOf(tableColumn);\n        if (index == 0) {\n            return \"-1\";\n        }\n        // Find the previous column that is not deleted\n        for (int i = index - 1; i >= 0; i--) {\n            if (newTable.getColumnList().get(i).getEditStatus() == null || !newTable.getColumnList().get(i).getEditStatus().equals(EditStatus.DELETE.name())) {\n                return newTable.getColumnList().get(i).getName();\n            }\n        }\n        return \"-1\";\n    }\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);\n        sqlBuilder.append(sql);\n        if (offset == 0) {\n            sqlBuilder.append(\"\\n LIMIT \");\n            sqlBuilder.append(pageSize);\n        } else {\n            sqlBuilder.append(\"\\n LIMIT \");\n            sqlBuilder.append(offset);\n            sqlBuilder.append(\",\");\n            sqlBuilder.append(pageSize);\n        }\n        return sqlBuilder.toString();\n    }\n\n\n    @Override\n    public String buildCreateDatabaseSql(Database database) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE DATABASE `\" + database.getName() + \"`\");\n        if (StringUtils.isNotBlank(database.getCharset())) {\n            sqlBuilder.append(\" DEFAULT CHARACTER SET=\").append(database.getCharset());\n        }\n        if (StringUtils.isNotBlank(database.getCollation())) {\n            sqlBuilder.append(\" COLLATE=\").append(database.getCollation());\n        }\n        return sqlBuilder.toString();\n    }\n\n    public static List<TableColumn> movedElements(List<TableColumn> original, List<TableColumn> modified) {\n        int[][] dp = new int[original.size() + 1][modified.size() + 1];\n\n        // 构建DP表\n        for (int i = 1; i <= original.size(); i++) {\n            for (int j = 1; j <= modified.size(); j++) {\n                if (original.get(i - 1).getName().equals(modified.get(j - 1).getOldName())) {\n                    dp[i][j] = dp[i - 1][j - 1] + 1;\n                } else {\n                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);\n                }\n            }\n        }\n\n        // 追踪LCS，找出移动了位置的元素\n        List<TableColumn> moved = new ArrayList<>();\n        int i = original.size();\n        int j = modified.size();\n        while (i > 0 && j > 0) {\n            if (original.get(i - 1).equals(modified.get(j - 1))) {\n                i--;\n                j--;\n            } else if (dp[i - 1][j] >= dp[i][j - 1]) {\n                moved.add(original.get(i - 1));\n                // modified List中找到original.get(i-1)的位置\n//                System.out.println(\"Moved elements:\"+ original.get(i-1).getName() + \" after \" + modified.indexOf(original.get(i-1)) );\n                i--;\n            } else {\n                j--;\n            }\n        }\n\n        // 这里添加原始列表中未被包含在LCS中的元素\n        while (i > 0) {\n            moved.add(original.get(i - 1));\n            i--;\n        }\n\n        return moved;\n    }\n\n    public String buildGenerateReorderColumnSql(Table oldTable, Table newTable) {\n        StringBuilder sql = new StringBuilder();\n        int n = 0;\n        // Create a map to store the index of each column in the old table's column list\n        Map<String, Integer> oldColumnIndexMap = new HashMap<>();\n        for (int i = 0; i < oldTable.getColumnList().size(); i++) {\n            oldColumnIndexMap.put(oldTable.getColumnList().get(i).getName(), i);\n        }\n        String[] oldColumnArray = oldTable.getColumnList().stream().map(TableColumn::getName).toArray(String[]::new);\n        String[] newColumnArray = newTable.getColumnList().stream().map(TableColumn::getName).toArray(String[]::new);\n\n        Set<String> oldColumnSet = new HashSet<>(Arrays.asList(oldColumnArray));\n        Set<String> newColumnSet = new HashSet<>(Arrays.asList(newColumnArray));\n        if (!oldColumnSet.equals(newColumnSet)) {\n            return \"\";\n        }\n\n        buildSql(oldColumnArray, newColumnArray, sql, oldTable, newTable, n);\n\n        return sql.toString();\n    }\n\n    private String[] buildSql(String[] originalArray, String[] targetArray, StringBuilder sql, Table oldTable, Table newTable, int n) {\n        // Complete the first move first\n        if (!originalArray[0].equals(targetArray[0])) {\n            int a = findIndex(originalArray, targetArray[0]);\n            TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[a])).findFirst().get();\n            String[] newArray = moveElement(originalArray, a, 0, targetArray, new AtomicInteger(0));\n            sql.append(\" MODIFY COLUMN \");\n            MysqlColumnTypeEnum typeEnum = MysqlColumnTypeEnum.getByType(column.getColumnType());\n            sql.append(typeEnum.buildColumn(column));\n            sql.append(\" FIRST;\\n\");\n            n++;\n            if (Arrays.equals(newArray, targetArray)) {\n                return newArray;\n            }\n            String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);\n            if (Arrays.equals(resultArray, targetArray)) {\n                return resultArray;\n            }\n        }\n\n        // After completing the last move\n        int max = originalArray.length - 1;\n        if (!originalArray[max].equals(targetArray[max])) {\n            int a = findIndex(originalArray, targetArray[max]);\n            //System.out.println(\"Move \" + originalArray[a] + \" after \" + (a > 0 ? originalArray[max] : \"start\"));\n            TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[a])).findFirst().get();\n            String[] newArray = moveElement(originalArray, a, max, targetArray, new AtomicInteger(0));\n            if (n > 0) {\n                sql.append(\"ALTER TABLE \");\n                if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {\n                    sql.append(\"`\").append(oldTable.getDatabaseName()).append(\"`\").append(\".\");\n                }\n                sql.append(\"`\").append(oldTable.getName()).append(\"`\").append(\"\\n\");\n            }\n            sql.append(\" MODIFY COLUMN \");\n            MysqlColumnTypeEnum typeEnum = MysqlColumnTypeEnum.getByType(column.getColumnType());\n            sql.append(typeEnum.buildColumn(column));\n            sql.append(\" AFTER \");\n            sql.append(oldTable.getColumnList().get(max).getName());\n            sql.append(\";\\n\");\n            n++;\n            if (Arrays.equals(newArray, targetArray)) {\n                return newArray;\n            }\n            String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);\n            if (Arrays.equals(resultArray, targetArray)) {\n                return resultArray;\n            }\n        }\n\n\n        for (int i = 0; i < originalArray.length; i++) {\n            int a = findIndex(targetArray, originalArray[i]);\n            if (i != a && isMoveValid(originalArray, targetArray, i, a)) {\n                // Find name a in oldTable.getColumnList\n                int finalI = i;\n                TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[finalI])).findFirst().get();\n                if (n > 0) {\n                    sql.append(\"ALTER TABLE \");\n                    if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {\n                        sql.append(\"`\").append(oldTable.getDatabaseName()).append(\"`\").append(\".\");\n                    }\n                    sql.append(\"`\").append(oldTable.getName()).append(\"`\").append(\"\\n\");\n                }\n                sql.append(\" MODIFY COLUMN \");\n                MysqlColumnTypeEnum typeEnum = MysqlColumnTypeEnum.getByType(column.getColumnType());\n                sql.append(typeEnum.buildColumn(column));\n                sql.append(\" AFTER \");\n                AtomicInteger continuousDataCount = new AtomicInteger(0);\n                String[] newArray = moveElement(originalArray, i, a, targetArray, continuousDataCount);\n                if (i < a) {\n                    sql.append(originalArray[a + continuousDataCount.get()]);\n                } else {\n                    sql.append(originalArray[a - 1]);\n                }\n\n                sql.append(\";\\n\");\n                n++;\n\n                if (Arrays.equals(newArray, targetArray)) {\n                    return newArray;\n                }\n                String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);\n                if (Arrays.equals(resultArray, targetArray)) {\n                    return resultArray;\n                }\n            }\n        }\n        return null;\n    }\n\n    private static int findIndex(String[] array, String element) {\n        for (int i = 0; i < array.length; i++) {\n            if (array[i].equals(element)) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    private static boolean isMoveValid(String[] originalArray, String[] targetArray, int i, int a) {\n        return ((i == 0 || a == 0 || !originalArray[i - 1].equals(targetArray[a - 1])) &&\n                (i >= originalArray.length - 1 || a >= targetArray.length - 1 || !originalArray[i + 1].equals(targetArray[a + 1])))\n                || (i > 0 && a > 0 && !originalArray[i - 1].equals(targetArray[a - 1]));\n    }\n\n    private static String[] moveElement(String[] originalArray, int from, int to, String[] targetArray, AtomicInteger continuousDataCount) {\n        String[] newArray = new String[originalArray.length];\n        System.arraycopy(originalArray, 0, newArray, 0, originalArray.length);\n        String temp = newArray[from];\n        // 是否有连续移动数据\n        boolean isContinuousData = false;\n        // 连续数据数量\n        if (from < to) {\n            for (int i = to; i < originalArray.length - 1; i++) {\n                if (originalArray[i + 1].equals(targetArray[findIndex(targetArray, originalArray[i]) + 1])) {\n                    continuousDataCount.set(continuousDataCount.incrementAndGet());\n                } else {\n                    break;\n                }\n            }\n            if (continuousDataCount.get() > 0) {\n                System.arraycopy(originalArray, from + 1, newArray, from, to - from + 1);\n                isContinuousData = true;\n            } else {\n                System.arraycopy(originalArray, from + 1, newArray, from, to - from);\n            }\n        } else {\n            System.arraycopy(originalArray, to, newArray, to + 1, from - to);\n        }\n        if (isContinuousData) {\n            newArray[to + continuousDataCount.get()] = temp;\n        } else {\n            newArray[to] = temp;\n        }\n        return newArray;\n    }\n\n\n    @Override\n    protected void buildTableName(String databaseName, String schemaName, String tableName, StringBuilder script) {\n        if (StringUtils.isNotBlank(databaseName)) {\n            script.append(SqlUtils.quoteObjectName(databaseName, \"`\")).append('.');\n        }\n        script.append(SqlUtils.quoteObjectName(tableName, \"`\"));\n    }\n\n    /**\n     * @param columnList\n     * @param script\n     */\n    @Override\n    protected void buildColumns(List<String> columnList, StringBuilder script) {\n        if (CollectionUtils.isNotEmpty(columnList)) {\n            script.append(\" (\")\n                    .append(columnList.stream().map(s -> SqlUtils.quoteObjectName(s, \"`\")).collect(Collectors.joining(\",\")))\n                    .append(\") \");\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/builder/form.json",
    "content": "{\n  \"baseInfo\": {\n    \"items\": [\n      {\n        \"defaultValue\": \"@localhost\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"名称\",\n        \"labelNameEN\": \"Name\",\n        \"name\": \"alias\",\n        \"required\": true,\n        \"width\": 100,\n      },\n      {\n        \"defaultValue\": \"localhost\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"主机\",\n        \"labelNameEN\": \"Host\",\n        \"name\": \"host\",\n        \"required\": true,\n        \"width\": 70,\n      },\n      {\n        \"defaultValue\": \"3306\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"端口\",\n        \"labelNameEN\": \"Port\",\n        \"name\": \"port\",\n        \"labelTextAlign\": \"right\",\n        \"required\": true,\n        \"width\": 30,\n      },\n      {\n        \"defaultValue\": AuthenticationType.USERANDPASSWORD,\n        \"inputType\": InputType.SELECT,\n        \"labelNameCN\": \"身份验证\",\n        \"labelNameEN\": \"Authentication\",\n        \"name\": \"authentication\",\n        \"required\": true,\n        \"selects\": [\n          {\n            \"items\": [\n              {\n                \"defaultValue\": \"root\",\n                \"inputType\": \"INPUT\",\n                \"labelNameCN\": \"用户名\",\n                \"labelNameEN\": \"User\",\n                \"name\": \"user\",\n                \"required\": true,\n                \"width\": 100,\n              },\n              {\n                \"defaultValue\": \",\n                \"inputType\": InputType.PASSWORD,\n                \"labelNameCN\": \"密码\",\n                \"labelNameEN\": \"Password\",\n                \"name\": \"password\",\n                \"required\": true,\n                \"width\": 100,\n              },\n            ],\n            \"label\": \"User&Password\",\n            \"value\": AuthenticationType.USERANDPASSWORD,\n          },\n          {\n            \"label\": \"NONE\",\n            \"value\": \"NONE,\n          },\n        ],\n        \"width\": 50\n      },\n      {\n        \"defaultValue\": \"\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"数据库\",\n        \"labelNameEN\": \"Database\",\n        \"name\": \"database\",\n        \"required\": false,\n        \"width\": 100\n      },\n      {\n        \"defaultValue\": \"jdbc:mysql://localhost:3306\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"URL\",\n        \"labelNameEN\": \"URL\",\n        \"name\": \"url\",\n        \"required\": true,\n        \"width\": 100\n      },\n      {\n        \"defaultValue\": \"8.0\",\n        \"inputType\": \"SELECT\",\n        \"labelNameCN\": \"JDBC驱动\",\n        \"labelNameEN\": \"JDBC Driver\",\n        \"name\": \"jdbc\",\n        \"required\": true,\n        \"selects\": [\n          {\n            \"value\": \"8.0\"\n          },\n          {\n            \"value\": \"5.0\"\n          }\n        ],\n        \"width\": 100\n      }\n    ],\n    \"pattern\": \"/jdbc:mysql:\\/\\/(.*):(\\\\d+)(\\/(\\\\w+))?/\",\n    \"template\": \"jdbc:mysql://{host}:{port}/{database}\"\n  },\n  \"ssh\": {\n    \"items\": [\n      {\n        \"defaultValue\": \"false\",\n        \"inputType\": \"SELECT\",\n        \"labelNameCN\": \"使用SSH\",\n        \"labelNameEN\": \"USE SSH\",\n        \"name\": \"use\",\n        \"required\": false,\n        \"selects\": [\n          {\n            \"value\": \"false\"\n          },\n          {\n            \"value\": \"true\"\n          }\n        ],\n        \"width\": 100\n      },\n      {\n        \"defaultValue\": \"\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"SSH 主机\",\n        \"labelNameEN\": \"SSH Hostname\",\n        \"name\": \"hostName\",\n        \"required\": false,\n        \"width\": 70\n      },\n      {\n        \"defaultValue\": \"22\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"SSH 端口\",\n        \"labelNameEN\": \"Port\",\n        \"name\": \"port\",\n        \"required\": false,\n        \"width\": 28\n      },\n      {\n        \"defaultValue\": \"root\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"用户名\",\n        \"labelNameEN\": \"SSH UserName\",\n        \"name\": \"userName\",\n        \"required\": false,\n        \"width\": 70\n      },\n      {\n        \"defaultValue\": \"3306\",\n        \"inputType\": \"INPUT\",\n        \"labelNameCN\": \"本地端口\",\n        \"labelNameEN\": \"LocalPort\",\n        \"name\": \"localPort\",\n        \"required\": false,\n        \"width\": 28\n      },\n      {\n        \"defaultValue\": \"\",\n        \"inputType\": \"PASSWORD\",\n        \"labelNameCN\": \"密码\",\n        \"labelNameEN\": \"Password\",\n        \"name\": \"password\",\n        \"required\": true,\n        \"width\": 100\n      }\n    ]\n  },\n  \"extendInfo\": [\n    {\n      \"key\":\"zeroDateTimeBehavior\",\n      \"value\":\"convertToNull\"\n    }\n  ],\n  \"type\":\"MYSQL\"\n}"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/mysql.json",
    "content": "{\n  \"dbType\": \"MYSQL\",\n  \"supportDatabase\": true,\n  \"supportSchema\": false,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:mysql://localhost:3306/\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/mysql-connector-java-8.0.30.jar\"\n      ],\n      \"jdbcDriver\": \"mysql-connector-java-8.0.30.jar\",\n      \"jdbcDriverClass\": \"com.mysql.cj.jdbc.Driver\",\n      \"extendInfo\": [\n        {\n          \"key\": \"zeroDateTimeBehavior\",\n          \"value\": \"convertToNull\",\n          \"required\": false\n        },\n        {\n          \"key\": \"tinyInt1isBit\",\n          \"value\": \"false\",\n          \"required\": false\n        }\n      ]\n    },\n    {\n      \"url\": \"jdbc:mysql://localhost:3306/\",\n      \"defaultDriver\": false,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/mysql-connector-java-5.1.47.jar\"\n      ],\n      \"jdbcDriver\": \"mysql-connector-java-5.1.47.jar\",\n      \"jdbcDriverClass\": \"com.mysql.jdbc.Driver\",\n      \"extendInfo\": [\n        {\n          \"key\": \"characterEncoding\",\n          \"value\": \"UTF-8\",\n          \"required\": false\n        },\n        {\n          \"key\": \"tinyInt1isBit\",\n          \"value\": \"false\",\n          \"required\": false\n        }\n      ]\n    }\n  ],\n  \"name\": \"Mysql\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlCharsetEnum.java",
    "content": "package ai.chat2db.plugin.mysql.type;\n\nimport ai.chat2db.spi.model.Charset;\nimport org.checkerframework.checker.units.qual.C;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum MysqlCharsetEnum {\n\n    UTF8(\"utf8\", \"utf8_general_ci\"),\n    BIG5(\"big5\", \"big5_chinese_ci\"),\n    DEC8(\"dec8\", \"dec8_swedish_ci\"),\n    CP850(\"cp850\", \"cp850_general_ci\"),\n    HP8(\"hp8\", \"hp8_english_ci\"),\n    KOI8R(\"koi8r\", \"koi8r_general_ci\"),\n    LATIN1(\"latin1\", \"latin1_swedish_ci\"),\n    LATIN2(\"latin2\", \"latin2_general_ci\"),\n    SWE7(\"swe7\", \"swe7_swedish_ci\"),\n    ASCII(\"ascii\", \"ascii_general_ci\"),\n    UJIS(\"ujis\", \"ujis_japanese_ci\"),\n    SJIS(\"sjis\", \"sjis_japanese_ci\"),\n    HEBREW(\"hebrew\", \"hebrew_general_ci\"),\n    TIS620(\"tis620\", \"tis620_thai_ci\"),\n    EUCKR(\"euckr\", \"euckr_korean_ci\"),\n    KOI8U(\"koi8u\", \"koi8u_general_ci\"),\n    GB2312(\"gb2312\", \"gb2312_chinese_ci\"),\n    GREEK(\"greek\", \"greek_general_ci\"),\n    CP1250(\"cp1250\", \"cp1250_general_ci\"),\n    GBK(\"gbk\", \"gbk_chinese_ci\"),\n    LATIN5(\"latin5\", \"latin5_turkish_ci\"),\n    ARMSCII8(\"armscii8\", \"armscii8_general_ci\"),\n    UCS2(\"ucs2\", \"ucs2_general_ci\"),\n    CP866(\"cp866\", \"cp866_general_ci\"),\n    KEYBCS2(\"keybcs2\", \"keybcs2_general_ci\"),\n    MACCE(\"macce\", \"macce_general_ci\"),\n    MACROMAN(\"macroman\", \"macroman_general_ci\"),\n    CP852(\"cp852\", \"cp852_general_ci\"),\n    LATIN7(\"latin7\", \"latin7_general_ci\"),\n    UTF8MB4(\"utf8mb4\", \"utf8mb4_general_ci\"),\n    CP1251(\"cp1251\", \"cp1251_general_ci\"),\n    UTF16(\"utf16\", \"utf16_general_ci\"),\n    UTF16LE(\"utf16le\", \"utf16le_general_ci\"),\n    CP1256(\"cp1256\", \"cp1256_general_ci\"),\n    CP1257(\"cp1257\", \"cp1257_general_ci\"),\n    UTF32(\"utf32\", \"utf32_general_ci\"),\n    BINARY(\"binary\", \"binary\"),\n    GEOSTD8(\"geostd8\", \"geostd8_general_ci\"),\n    CP932(\"cp932\", \"cp932_japanese_ci\"),\n    EUCJPMS(\"eucjpms\", \"eucjpms_japanese_ci\"),\n    GB18030(\"gb18030\", \"gb18030_chinese_ci\");\n    private Charset charset;\n\n    MysqlCharsetEnum(String charsetName, String defaultCollationName) {\n        this.charset = new Charset(charsetName, defaultCollationName);\n    }\n\n\n    public Charset getCharset() {\n        return charset;\n    }\n\n    public static List<Charset> getCharsets() {\n        return Arrays.stream(MysqlCharsetEnum.values()).map(MysqlCharsetEnum::getCharset).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlCollationEnum.java",
    "content": "package ai.chat2db.plugin.mysql.type;\n\nimport ai.chat2db.spi.model.Collation;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum MysqlCollationEnum {\n\n    UTF8_GENERAL_CI(\"utf8_general_ci\"),\n\n    UTF8MB4_GENERAL_CI(\"utf8mb4_general_ci\"),\n\n    BIG5_CHINESE_CI(\"big5_chinese_ci\"),\n    DEC8_SWEDISH_CI(\"dec8_swedish_ci\"),\n\n    HP8_ENGLISH_CI(\"hp8_english_ci\"),\n    KOI8R_GENERAL_CI(\"koi8r_general_ci\"),\n    LATIN1_SWEDISH_CI(\"latin1_swedish_ci\"),\n    LATIN2_GENERAL_CI(\"latin2_general_ci\"),\n    SWE7_SWEDISH_CI(\"swe7_swedish_ci\"),\n    ASCII_GENERAL_CI(\"ascii_general_ci\"),\n    UJIS_JAPANESE_CI(\"ujis_japanese_ci\"),\n    SJIS_JAPANESE_CI(\"sjis_japanese_ci\"),\n    HEBREW_GENERAL_CI(\"hebrew_general_ci\"),\n    TIS620_THAI_CI(\"tis620_thai_ci\"),\n    EUCKR_KOREAN_CI(\"euckr_korean_ci\"),\n    KOI8U_GENERAL_CI(\"koi8u_general_ci\"),\n    GB2312_CHINESE_CI(\"gb2312_chinese_ci\"),\n    GREEK_GENERAL_CI(\"greek_general_ci\"),\n    CP1250_GENERAL_CI(\"cp1250_general_ci\"),\n    GBK_CHINESE_CI(\"gbk_chinese_ci\"),\n    LATIN5_TURKISH_CI(\"latin5_turkish_ci\"),\n    ARMSCII8_GENERAL_CI(\"armscii8_general_ci\"),\n    CP1250_CZECH_CS(\"cp1250_czech_cs\"),\n    UCS2_GENERAL_CI(\"ucs2_general_ci\"),\n    CP866_GENERAL_CI(\"cp866_general_ci\"),\n    KEYBCS2_GENERAL_CI(\"keybcs2_general_ci\"),\n    MACCE_GENERAL_CI(\"macce_general_ci\"),\n    MACROMAN_GENERAL_CI(\"macroman_general_ci\"),\n    CP852_GENERAL_CI(\"cp852_general_ci\"),\n    LATIN7_GENERAL_CI(\"latin7_general_ci\"),\n    CP1251_GENERAL_CI(\"cp1251_general_ci\"),\n    UTF16_GENERAL_CI(\"utf16_general_ci\"),\n    UTF16LE_GENERAL_CI(\"utf16le_general_ci\"),\n    CP1256_GENERAL_CI(\"cp1256_general_ci\"),\n    CP1257_GENERAL_CI(\"cp1257_general_ci\"),\n    UTF32_GENERAL_CI(\"utf32_general_ci\"),\n    BINARY(\"binary\"),\n    GEOSTD8_GENERAL_CI(\"geostd8_general_ci\"),\n    CP932_JAPANESE_CI(\"cp932_japanese_ci\"),\n    EUCJPMS_JAPANESE_CI(\"eucjpms_japanese_ci\"),\n    GB18030_CHINESE_CI(\"gb18030_chinese_ci\"),\n    ;\n    private Collation collation;\n\n    MysqlCollationEnum(String collationName) {\n        this.collation = new Collation(collationName);\n    }\n\n    public Collation getCollation() {\n        return collation;\n    }\n\n\n    public static List<Collation> getCollations() {\n        return Arrays.asList(MysqlCollationEnum.values()).stream().map(MysqlCollationEnum::getCollation).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.mysql.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum MysqlColumnTypeEnum implements ColumnBuilder {\n\n    BIT(\"BIT\", true, false, true, false, false, false, true, true, false, false),\n\n    TINYINT(\"TINYINT\", false, false, true, true, false, false, true, true, false, false),\n\n    TINYINT_UNSIGNED(\"TINYINT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n\n    SMALLINT(\"SMALLINT\", false, false, true, true, false, false, true, true, false, false),\n\n    SMALLINT_UNSIGNED(\"SMALLINT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n\n    MEDIUMINT(\"MEDIUMINT\", false, false, true, true, false, false, true, true, false, false),\n\n    MEDIUMINT_UNSIGNED(\"MEDIUMINT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n\n    INT(\"INT\", false, false, true, true, false, false, true, true, false, false),\n\n\n    INT_UNSIGNED(\"INT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n\n    BIGINT(\"BIGINT\", false, false, true, true, false, false, true, true, false, false),\n\n\n    BIGINT_UNSIGNED(\"BIGINT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n\n\n    DECIMAL(\"DECIMAL\", true, true, true, false, false, false, true, true, false, false),\n\n    DECIMAL_UNSIGNED(\"DECIMAL UNSIGNED\", true, true, true, false, false, false, true, true, false, false),\n\n\n    FLOAT(\"FLOAT\", true, true, true, false, false, false, true, true, false, false),\n\n    FLOAT_UNSIGNED(\"FLOAT UNSIGNED\", true, true, true, false, false, false, true, true, false, false),\n\n    DOUBLE(\"DOUBLE\", true, true, true, false, false, false, true, true, false, false),\n\n    DOUBLE_UNSIGNED(\"DOUBLE UNSIGNED\", true, true, true, false, false, false, true, true, false, false),\n    DATE(\"DATE\", false, false, true, false, false, false, true, true, false, false),\n    DATETIME(\"DATETIME\", true, false, true, false, false, false, true, true, true, false),\n\n    TIMESTAMP(\"TIMESTAMP\", true, false, true, false, false, false, true, true, true, false),\n    TIME(\"TIME\", true, false, true, false, false, false, true, true, false, false),\n    YEAR(\"YEAR\", false, false, true, false, false, false, true, true, false, false),\n    CHAR(\"CHAR\", true, false, true, false, true, true, true, true, false, false),\n\n    VARCHAR(\"VARCHAR\", true, false, true, false, true, true, true, true, false, false),\n\n    BINARY(\"BINARY\", true, false, true, false, false, false, true, true, false, false),\n\n    VARBINARY(\"VARBINARY\", true, false, true, false, false, false, true, true, false, false),\n\n    TINYBLOB(\"TINYBLOB\", false, false, true, false, false, false, true, false, false, false),\n\n    BLOB(\"BLOB\", false, false, true, false, false, false, true, false, false, false),\n\n    MEDIUMBLOB(\"MEDIUMBLOB\", false, false, true, false, false, false, true, false, false, false),\n\n    LONGBLOB(\"LONGBLOB\", false, false, true, false, false, false, true, false, false, false),\n\n    TINYTEXT(\"TINYTEXT\", false, false, true, false, true, true, true, false, false, false),\n\n    TEXT(\"TEXT\", false, false, true, false, true, true, true, false, false, false),\n\n    MEDIUMTEXT(\"MEDIUMTEXT\", false, false, true, false, true, true, true, false, false, false),\n\n    LONGTEXT(\"LONGTEXT\", false, false, true, false, true, true, true, false, false, false),\n\n\n    ENUM(\"ENUM\", false, false, true, false, true, true, true, true, true, true),\n\n\n    BOOL(\"BOOL\", false, false, true, true, false, false, true, true, false, false),\n\n    INTEGER(\"INTEGER\", false, false, true, true, false, false, true, true, false, false),\n\n    INTEGER_UNSIGNED(\"INTEGER UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n\n    REAL(\"REAL\", true, true, true, false, false, false, true, true, false, false),\n\n    SET(\"SET\", false, false, true, false, true, true, true, true, true, true),\n\n\n    GEOMETRY(\"GEOMETRY\", false, false, true, false, false, false, true, false, false, false),\n\n    POINT(\"POINT\", false, false, true, false, false, false, true, false, false, false),\n\n    LINESTRING(\"LINESTRING\", false, false, true, false, false, false, true, false, false, false),\n\n    POLYGON(\"POLYGON\", false, false, true, false, false, false, true, false, false, false),\n\n    MULTIPOINT(\"MULTIPOINT\", false, false, true, false, false, false, true, false, false, false),\n\n    MULTILINESTRING(\"MULTILINESTRING\", false, false, true, false, false, false, true, false, false, false),\n\n    MULTIPOLYGON(\"MULTIPOLYGON\", false, false, true, false, false, false, true, false, false, false),\n\n    GEOMETRYCOLLECTION(\"GEOMETRYCOLLECTION\", false, false, true, false, false, false, true, false, false, false),\n\n    JSON(\"JSON\", false, false, true, false, false, false, true, false, false, false);\n\n    private ColumnType columnType;\n\n    public static MysqlColumnTypeEnum getByType(String dataType) {\n        return COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n\n    MysqlColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent,boolean supportValue) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent,supportValue,false);\n    }\n\n    private static Map<String, MysqlColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (MysqlColumnTypeEnum value : MysqlColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        MysqlColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildCharset(column,type)).append(\" \");\n\n        script.append(buildCollation(column,type)).append(\" \");\n\n        script.append(buildNullable(column,type)).append(\" \");\n\n        script.append(buildDefaultValue(column,type)).append(\" \");\n\n        script.append(buildExt(column,type)).append(\" \");\n\n        script.append(buildAutoIncrement(column,type)).append(\" \");\n\n        script.append(buildComment(column,type)).append(\" \");\n\n        return script.toString();\n    }\n\n    private String buildCharset(TableColumn column, MysqlColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportCharset() || StringUtils.isEmpty(column.getCharSetName())){\n            return \"\";\n        }\n        return StringUtils.join(\"CHARACTER SET \",column.getCharSetName());\n    }\n\n    private String buildCollation(TableColumn column, MysqlColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportCollation() || StringUtils.isEmpty(column.getCollationName())){\n            return \"\";\n        }\n        return StringUtils.join(\"COLLATE \",column.getCollationName());\n    }\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\"DROP COLUMN `\", tableColumn.getName() + \"`\");\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\"ADD COLUMN \", buildCreateColumnSql(tableColumn));\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n                return StringUtils.join(\"CHANGE COLUMN `\", tableColumn.getOldName(), \"` \", buildCreateColumnSql(tableColumn));\n            } else {\n                return StringUtils.join(\"MODIFY COLUMN \", buildCreateColumnSql(tableColumn));\n            }\n        }\n        return \"\";\n    }\n\n    public String buildModifyColumn(TableColumn tableColumn, boolean isMove, String columnName) {\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\"DROP COLUMN `\", tableColumn.getName() + \"`\");\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            if (isMove){\n                if (columnName.equals(\"-1\")) {\n                    return StringUtils.join(\"ADD COLUMN \", buildCreateColumnSql(tableColumn),\" FIRST\");\n                } else {\n                    return StringUtils.join(\"ADD COLUMN \", buildCreateColumnSql(tableColumn), \" AFTER \", columnName);\n                }\n            }\n            return StringUtils.join(\"ADD COLUMN \", buildCreateColumnSql(tableColumn));\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n                return StringUtils.join(\"CHANGE COLUMN `\", tableColumn.getOldName(), \"` \", buildCreateColumnSql(tableColumn));\n            } else {\n                return StringUtils.join(\"MODIFY COLUMN \", buildCreateColumnSql(tableColumn));\n            }\n        }\n        if (isMove) {\n            if (columnName.equals(\"-1\")) {\n                return StringUtils.join(\"MODIFY COLUMN \", buildCreateColumnSql(tableColumn),\" FIRST\");\n            } else {\n                return StringUtils.join(\"MODIFY COLUMN \", buildCreateColumnSql(tableColumn), \" AFTER \", columnName);\n            }\n        }\n        return \"\";\n    }\n\n    private String buildAutoIncrement(TableColumn column, MysqlColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportAutoIncrement()){\n            return \"\";\n        }\n        if (column.getAutoIncrement() != null && column.getAutoIncrement()) {\n            return \"AUTO_INCREMENT\";\n        }\n        return \"\";\n    }\n\n    private String buildComment(TableColumn column, MysqlColumnTypeEnum type) {\n        if(!type.columnType.isSupportComments() || StringUtils.isEmpty(column.getComment())){\n            return \"\";\n        }\n        return StringUtils.join(\"COMMENT '\",column.getComment(),\"'\");\n    }\n\n    private String buildExt(TableColumn column, MysqlColumnTypeEnum type) {\n        if(!type.columnType.isSupportExtent() || StringUtils.isEmpty(column.getExtent())){\n            return \"\";\n        }\n        return column.getComment();\n    }\n\n    private String buildDefaultValue(TableColumn column, MysqlColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())){\n            return \"\";\n        }\n\n        if(\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())){\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if(\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())){\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        if(Arrays.asList(CHAR,VARCHAR,BINARY,VARBINARY, SET,ENUM).contains(type)){\n            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n        }\n\n        if(Arrays.asList(DATE,TIME,YEAR).contains(type)){\n            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n        }\n\n        if(Arrays.asList(DATETIME,TIMESTAMP).contains(type)){\n            if(\"CURRENT_TIMESTAMP\".equalsIgnoreCase(column.getDefaultValue().trim())){\n                return StringUtils.join(\"DEFAULT \",column.getDefaultValue());\n            }\n            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n        }\n\n        return StringUtils.join(\"DEFAULT \",column.getDefaultValue());\n    }\n\n    private String buildNullable(TableColumn column,MysqlColumnTypeEnum type) {\n        if(!type.getColumnType().isSupportNullable()){\n            return \"\";\n        }\n        if (column.getNullable()!=null && 1==column.getNullable()) {\n            return \"NULL\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDataType(TableColumn column, MysqlColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(BINARY, VARBINARY, VARCHAR, CHAR).contains(type)) {\n            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n        }\n\n        if (Arrays.asList(BIT).contains(type)) {\n            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n        }\n\n        if (Arrays.asList(TIME, DATETIME, TIMESTAMP).contains(type)) {\n            if (column.getColumnSize() == null || column.getColumnSize() == 0) {\n                return columnType;\n            } else {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n            }\n        }\n\n\n        if (Arrays.asList(DECIMAL, FLOAT, DOUBLE).contains(type)) {\n            if (column.getColumnSize() == null || column.getDecimalDigits() == null) {\n                return columnType;\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \")\");\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n            }\n        }\n\n        if (Arrays.asList(DECIMAL_UNSIGNED, FLOAT_UNSIGNED, DECIMAL_UNSIGNED).contains(type)) {\n            if (column.getColumnSize() == null || column.getDecimalDigits() == null) {\n                return columnType;\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                return unsignedDataType(columnType, \"(\" + column.getColumnSize() + \")\");\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n                return unsignedDataType(columnType, \"(\" + column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n            }\n        }\n\n        if(Arrays.asList(SET,ENUM).contains(type)){\n            if(!StringUtils.isEmpty( column.getValue())){\n                return StringUtils.join(columnType,\"(\",column.getValue(),\")\");\n            }\n            //List<String> enumList = column.\n        }\n\n        return columnType;\n    }\n\n    public String buildColumn(TableColumn column) {\n        MysqlColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n        script.append(buildDataType(column, type)).append(\" \");\n        if (StringUtils.isNoneBlank(column.getComment())) {\n            script.append(\"COMMENT\").append(\" \").append(\"'\").append(column.getComment()).append(\"'\").append(\" \");\n        }\n        return script.toString();\n    }\n\n    private String unsignedDataType(String dataTypeName, String middle) {\n        String[] split = dataTypeName.split(\" \");\n        if (split.length == 2) {\n            return StringUtils.join(split[0], middle, split[1]);\n        }\n        return StringUtils.join(dataTypeName, middle);\n    }\n\n    public static List<ColumnType> getTypes(){\n       return Arrays.stream(MysqlColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlDefaultValueEnum.java",
    "content": "package ai.chat2db.plugin.mysql.type;\n\nimport ai.chat2db.spi.model.DefaultValue;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum MysqlDefaultValueEnum {\n\n    EMPTY_STRING(\"EMPTY_STRING\"),\n    NULL(\"NULL\"),\n    CURRENT_TIMESTAMP(\"CURRENT_TIMESTAMP\"),\n    ;\n    private DefaultValue defaultValue;\n\n    MysqlDefaultValueEnum(String defaultValue) {\n        this.defaultValue = new DefaultValue(defaultValue);\n    }\n\n\n    public DefaultValue getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static List<DefaultValue> getDefaultValues() {\n        return Arrays.stream(MysqlDefaultValueEnum.values()).map(MysqlDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.mysql.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum MysqlIndexTypeEnum {\n\n    PRIMARY_KEY(\"Primary\", \"PRIMARY KEY\"),\n\n    NORMAL(\"Normal\", \"INDEX\"),\n\n    UNIQUE(\"Unique\", \"UNIQUE INDEX\"),\n\n    FULLTEXT(\"Fulltext\", \"FULLTEXT INDEX\"),\n\n    SPATIAL(\"Spatial\", \"SPATIAL INDEX\");\n\n    public String getName() {\n        return name;\n    }\n\n    private String name;\n\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    private String keyword;\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    private IndexType indexType;\n\n    MysqlIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n\n    public static MysqlIndexTypeEnum getByType(String type) {\n        for (MysqlIndexTypeEnum value : MysqlIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(keyword).append(\" \");\n\n        script.append(buildIndexName(tableIndex)).append(\" \");\n\n        script.append(buildIndexColumn(tableIndex)).append(\" \");\n\n        script.append(buildIndexComment(tableIndex)).append(\" \");\n\n        return script.toString();\n    }\n\n    private String buildIndexComment(TableIndex tableIndex) {\n        if(StringUtils.isBlank(tableIndex.getComment())){\n            return \"\";\n        }else {\n            return StringUtils.join(\"COMMENT '\",tableIndex.getComment(),\"'\");\n        }\n\n    }\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if(StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"`\").append(column.getColumnName()).append(\"`\");\n                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {\n                    script.append(\" \").append(column.getAscOrDesc());\n                }\n                script.append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        if(this.equals(PRIMARY_KEY)){\n            return \"\";\n        }else {\n            return \"`\"+tableIndex.getName()+\"`\";\n        }\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex),\",\\n\", \"ADD \", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\"ADD \", buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (MysqlIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {\n            return StringUtils.join(\"DROP PRIMARY KEY\");\n        }\n        return StringUtils.join(\"DROP INDEX `\", tableIndex.getOldName(),\"`\");\n    }\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(MysqlIndexTypeEnum.values()).stream().map(MysqlIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/GeometryValueHandler.java",
    "content": "//package ai.chat2db.plugin.mysql.value;\n//\n//import ai.chat2db.spi.ValueHandler;\n//import org.locationtech.jts.geom.Geometry;\n//import org.locationtech.jts.io.WKBReader;\n//\n//import java.io.InputStream;\n//import java.io.ByteArrayOutputStream;\n//import java.sql.ResultSet;\n//import java.sql.SQLException;\n//\n//public class GeometryValueHandler implements ValueHandler {\n//    @Override\n//    public String getString(ResultSet rs, int index, boolean limitSize) throws SQLException {\n//        try {\n//            InputStream inputStream = rs.getBinaryStream(index);\n//            Geometry dbGeometry = null;\n//            if (inputStream != null) {\n//\n//                //convert the stream to a byte[] array\n//                //so it can be passed to the WKBReader\n//                byte[] buffer = new byte[255];\n//\n//                int bytesRead = 0;\n//                ByteArrayOutputStream baos = new ByteArrayOutputStream();\n//                while ((bytesRead = inputStream.read(buffer)) != -1) {\n//                    baos.write(buffer, 0, bytesRead);\n//                }\n//\n//                byte[] geometryAsBytes = baos.toByteArray();\n//\n//                if (geometryAsBytes.length < 5) {\n//                    throw new Exception(\"Invalid geometry inputStream - less than five bytes\");\n//                }\n//\n//                //first four bytes of the geometry are the SRID,\n//                //followed by the actual WKB.  Determine the SRID\n//                //here\n//                byte[] sridBytes = new byte[4];\n//                System.arraycopy(geometryAsBytes, 0, sridBytes, 0, 4);\n//                boolean bigEndian = (geometryAsBytes[4] == 0x00);\n//\n//                int srid = 0;\n//                if (bigEndian) {\n//                    for (int i = 0; i < sridBytes.length; i++) {\n//                        srid = (srid << 8) + (sridBytes[i] & 0xff);\n//                    }\n//                } else {\n//                    for (int i = 0; i < sridBytes.length; i++) {\n//                        srid += (sridBytes[i] & 0xff) << (8 * i);\n//                    }\n//                }\n//\n//                //use the JTS WKBReader for WKB parsing\n//                WKBReader wkbReader = new WKBReader();\n//\n//                //copy the byte array, removing the first four\n//                //SRID bytes\n//                byte[] wkb = new byte[geometryAsBytes.length - 4];\n//                System.arraycopy(geometryAsBytes, 4, wkb, 0, wkb.length);\n//                dbGeometry = wkbReader.read(wkb);\n//                dbGeometry.setSRID(srid);\n//            }\n//            return dbGeometry.toString();\n//        } catch (Exception e) {\n//            return rs.getString(index);\n//        }\n//    }\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/MysqlValueProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value;\n\nimport ai.chat2db.plugin.mysql.value.factory.MysqlValueProcessorFactory;\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Objects;\nimport java.util.Set;\n\n/**\n * @author: zgq\n * @date: 2024年05月24日 21:02\n * <br>\n *  TODO:\n *      attribute: [zerofill] example tinyint[5] zerofill 34->00034\n */\npublic class MysqlValueProcessor extends DefaultValueProcessor {\n    public static final Set<String> FUNCTION_SET = Set.of(\"now()\", \"default\");\n    private static final Logger log = LoggerFactory.getLogger(MysqlValueProcessor.class);\n\n\n    @Override\n    public String getJdbcValue(JDBCDataValue dataValue) {\n        Object value = dataValue.getObject();\n        if (Objects.isNull(value)) {\n            // mysql -> example: [date]->0000-00-00\n            String stringValue = dataValue.getStringValue();\n            if (Objects.nonNull(stringValue)) {\n                return stringValue;\n            }\n            return null;\n        }\n        if (value instanceof String emptyStr) {\n            if (StringUtils.isBlank(emptyStr)) {\n                return emptyStr;\n            }\n        }\n        return convertJDBCValueByType(dataValue);\n    }\n\n\n    @Override\n    public String getJdbcSqlValueString(JDBCDataValue dataValue) {\n        Object value = dataValue.getObject();\n        if (Objects.isNull(value)) {\n            // mysql -> example: [date]->0000-00-00\n            String stringValue = dataValue.getStringValue();\n            if (Objects.nonNull(stringValue)) {\n                return EasyStringUtils.escapeAndQuoteString(stringValue);\n            }\n            return \"NULL\";\n        }\n        if (value instanceof String stringValue) {\n            if (StringUtils.isBlank(stringValue)) {\n                return EasyStringUtils.quoteString(stringValue);\n            }\n        }\n        return convertJDBCValueStrByType(dataValue);\n    }\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        if (FUNCTION_SET.contains(dataValue.getValue().toLowerCase())) {\n            return dataValue.getValue();\n        }\n        try {\n            DefaultValueProcessor valueProcessor = MysqlValueProcessorFactory.getValueProcessor(dataValue.getDateTypeName());\n            if (Objects.nonNull(valueProcessor)) {\n                return valueProcessor.convertSQLValueByType(dataValue);\n            }\n        } catch (Exception e) {\n            log.warn(\"convertSQLValueByType error\", e);\n            return super.convertSQLValueByType(dataValue);\n        }\n        return super.convertSQLValueByType(dataValue);\n    }\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        String type = dataValue.getType();\n        try {\n            DefaultValueProcessor valueProcessor = MysqlValueProcessorFactory.getValueProcessor(type);\n            if (Objects.nonNull(valueProcessor)) {\n                return valueProcessor.convertJDBCValueByType(dataValue);\n            }\n        } catch (Exception e) {\n            log.warn(\"convertJDBCValueByType error\", e);\n            return super.convertJDBCValueByType(dataValue);\n        }\n        return super.convertJDBCValueByType(dataValue);\n\n    }\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        String type = dataValue.getType();\n        DefaultValueProcessor valueProcessor;\n        try {\n            valueProcessor = MysqlValueProcessorFactory.getValueProcessor(type);\n            if (Objects.nonNull(valueProcessor)) {\n                return valueProcessor.convertJDBCValueStrByType(dataValue);\n            }\n        } catch (Exception e) {\n            log.warn(\"convertJDBCValueStrByType error\", e);\n            return super.convertJDBCValueStrByType(dataValue);\n        }\n        return super.convertJDBCValueStrByType(dataValue);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/factory/MysqlValueProcessorFactory.java",
    "content": "package ai.chat2db.plugin.mysql.value.factory;\n\nimport ai.chat2db.plugin.mysql.type.MysqlColumnTypeEnum;\nimport ai.chat2db.plugin.mysql.value.sub.*;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\n\nimport java.util.Map;\n\n/**\n * @author: zgq\n * @date: 2024年06月03日 23:16\n */\npublic class MysqlValueProcessorFactory {\n\n    private static final Map<String, DefaultValueProcessor> PROCESSOR_MAP;\n\n    static {\n        MysqlGeometryProcessor mysqlGeometryProcessor = new MysqlGeometryProcessor();\n        MysqlVarBinaryProcessor mysqlVarBinaryProcessor = new MysqlVarBinaryProcessor();\n        MysqlTimestampProcessor mysqlTimestampProcessor = new MysqlTimestampProcessor();\n        MysqlTextProcessor mysqlTextProcessor = new MysqlTextProcessor();\n        PROCESSOR_MAP = Map.ofEntries(\n                //text\n                Map.entry(MysqlColumnTypeEnum.TEXT.name(), mysqlTextProcessor),\n                Map.entry(MysqlColumnTypeEnum.TINYTEXT.name(), mysqlTextProcessor),\n                Map.entry(MysqlColumnTypeEnum.MEDIUMTEXT.name(), mysqlTextProcessor),\n                Map.entry(MysqlColumnTypeEnum.LONGTEXT.name(), mysqlTextProcessor),\n                // geometry\n                Map.entry(MysqlColumnTypeEnum.GEOMETRY.name(), mysqlGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.POINT.name(), mysqlGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.LINESTRING.name(), mysqlGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.POLYGON.name(), mysqlGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.MULTIPOINT.name(), mysqlGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.MULTILINESTRING.name(), mysqlGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.MULTIPOLYGON.name(), mysqlGeometryProcessor),\n                Map.entry(MysqlColumnTypeEnum.GEOMETRYCOLLECTION.name(), mysqlGeometryProcessor),\n                // binary\n                Map.entry(MysqlColumnTypeEnum.VARBINARY.name(), mysqlVarBinaryProcessor),\n                Map.entry(MysqlColumnTypeEnum.BLOB.name(), mysqlVarBinaryProcessor),\n                Map.entry(MysqlColumnTypeEnum.LONGBLOB.name(), mysqlVarBinaryProcessor),\n                Map.entry(MysqlColumnTypeEnum.TINYBLOB.name(), mysqlVarBinaryProcessor),\n                Map.entry(MysqlColumnTypeEnum.MEDIUMBLOB.name(), mysqlVarBinaryProcessor),\n                // timestamp\n                Map.entry(MysqlColumnTypeEnum.TIMESTAMP.name(), mysqlTimestampProcessor),\n                Map.entry(MysqlColumnTypeEnum.DATETIME.name(), mysqlTimestampProcessor),\n                //others\n                Map.entry(MysqlColumnTypeEnum.YEAR.name(), new MysqlYearProcessor()),\n                Map.entry(MysqlColumnTypeEnum.BIT.name(), new MysqlBitProcessor()),\n                Map.entry(MysqlColumnTypeEnum.DECIMAL.name(), new MysqlDecimalProcessor()),\n                Map.entry(MysqlColumnTypeEnum.BINARY.name(), new MysqlBinaryProcessor())\n        );\n    }\n\n    public static DefaultValueProcessor getValueProcessor(String type) {\n        return PROCESSOR_MAP.get(type);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/sub/MysqlBinaryProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value.sub;\n\nimport ai.chat2db.plugin.mysql.value.template.MysqlDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月03日 19:43\n */\npublic class MysqlBinaryProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        String value = dataValue.getValue();\n        if (value.startsWith(\"0x\")) {\n            return value;\n        }\n        return MysqlDmlValueTemplate.wrapHex(dataValue.getBlobHexString());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        byte[] bytes = dataValue.getBytes();\n        if (bytes.length == 1) {\n            if (bytes[0] >= 32 && bytes[0] <= 126) {\n                return new String(bytes);\n            }\n        }\n        return MysqlDmlValueTemplate.wrapHex(dataValue.getBlobHexString());\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return MysqlDmlValueTemplate.wrapHex(dataValue.getBlobHexString());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/sub/MysqlBitProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value.sub;\n\nimport ai.chat2db.plugin.mysql.value.template.MysqlDmlValueTemplate;\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 13:08\n */\npublic class MysqlBitProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return getString(dataValue.getValue());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return getValue(dataValue, s -> s);\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return getValue(dataValue, this::wrap);\n    }\n\n    private String getValue(JDBCDataValue dataValue, Function<String, String> function) {\n        int precision = dataValue.getPrecision();\n        byte[] bytes = dataValue.getBytes();\n        if (precision == 1) {\n            //bit(1) [1 -> true] [0 -> false]\n            if (bytes.length == 1 && (bytes[0] == 0 || bytes[0] == 1)) {\n                return String.valueOf(dataValue.getBoolean());\n            }\n            // tinyint(1)\n            return String.valueOf(dataValue.getInt());\n        }\n        //bit(m) m: 2~64\n        return function.apply(EasyStringUtils.getBitString(bytes, precision));\n    }\n\n    public String getString(String value) {\n\n        if (Objects.equals(\"true\", value.toLowerCase())) {\n            return \"1\";\n        }\n        if (Objects.equals(\"false\", value.toLowerCase())) {\n            return \"0\";\n        }\n        if (StringUtils.isBlank(value)) {\n            return \"NULL\";\n        }\n        return MysqlDmlValueTemplate.wrapBit(value);\n    }\n\n    private String wrap(String value) {\n        return MysqlDmlValueTemplate.wrapBit(value);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/sub/MysqlDecimalProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value.sub;\n\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 18:01\n */\npublic class MysqlDecimalProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return dataValue.getValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getBigDecimalString();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return dataValue.getBigDecimalString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/sub/MysqlGeometryProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value.sub;\n\nimport ai.chat2db.plugin.mysql.value.template.MysqlDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.WKBReader;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 12:42\n */\npublic class MysqlGeometryProcessor extends DefaultValueProcessor {\n\n\n    private static final Logger log = LoggerFactory.getLogger(MysqlGeometryProcessor.class);\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return MysqlDmlValueTemplate.wrapGeometry(dataValue.getValue());\n    }\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        try {\n            InputStream inputStream = dataValue.getBinaryStream();\n            Geometry dbGeometry = null;\n            if (inputStream != null) {\n\n                //convert the stream to a byte[] array\n                //so it can be passed to the WKBReader\n                byte[] buffer = new byte[255];\n\n                int bytesRead = 0;\n                ByteArrayOutputStream baos = new ByteArrayOutputStream();\n                while ((bytesRead = inputStream.read(buffer)) != -1) {\n                    baos.write(buffer, 0, bytesRead);\n                }\n\n                byte[] geometryAsBytes = baos.toByteArray();\n\n                if (geometryAsBytes.length < 5) {\n                    throw new Exception(\"Invalid geometry inputStream - less than five bytes\");\n                }\n\n                //first four bytes of the geometry are the SRID,\n                //followed by the actual WKB.  Determine the SRID\n                //here\n                byte[] sridBytes = new byte[4];\n                System.arraycopy(geometryAsBytes, 0, sridBytes, 0, 4);\n                boolean bigEndian = (geometryAsBytes[4] == 0x00);\n\n                int srid = 0;\n                if (bigEndian) {\n                    for (int i = 0; i < sridBytes.length; i++) {\n                        srid = (srid << 8) + (sridBytes[i] & 0xff);\n                    }\n                } else {\n                    for (int i = 0; i < sridBytes.length; i++) {\n                        srid += (sridBytes[i] & 0xff) << (8 * i);\n                    }\n                }\n\n                //use the JTS WKBReader for WKB parsing\n                WKBReader wkbReader = new WKBReader();\n\n                //copy the byte array, removing the first four\n                //SRID bytes\n                byte[] wkb = new byte[geometryAsBytes.length - 4];\n                System.arraycopy(geometryAsBytes, 4, wkb, 0, wkb.length);\n                dbGeometry = wkbReader.read(wkb);\n                dbGeometry.setSRID(srid);\n            }\n            return dbGeometry != null ? dbGeometry.toString() : null;\n        } catch (Exception e) {\n            log.warn(\"Error converting database geometry\", e);\n            return dataValue.getStringValue();\n        }\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return MysqlDmlValueTemplate.wrapGeometry(convertJDBCValueByType(dataValue));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/sub/MysqlTextProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value.sub;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * @author: zgq\n * @date: 2024年06月05日 0:11\n */\n@Slf4j\npublic class MysqlTextProcessor extends DefaultValueProcessor {\n\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return EasyStringUtils.escapeAndQuoteString(dataValue.getValue());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getClobString();\n    }\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return EasyStringUtils.escapeAndQuoteString(dataValue.getClobString());\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/sub/MysqlTimestampProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value.sub;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 18:26\n */\npublic class MysqlTimestampProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return EasyStringUtils.quoteString(dataValue.getValue());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getStringValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return EasyStringUtils.quoteString(dataValue.getStringValue());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/sub/MysqlVarBinaryProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value.sub;\n\nimport ai.chat2db.plugin.mysql.value.template.MysqlDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * @author: zgq\n * @date: 2024年06月03日 20:48\n */\n@Slf4j\npublic class MysqlVarBinaryProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        String value = dataValue.getValue();\n        if (value.startsWith(\"0x\")) {\n            return value;\n        }\n        return MysqlDmlValueTemplate.wrapHex(dataValue.getBlobHexString());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getBlobString();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return MysqlDmlValueTemplate.wrapHex(dataValue.getBlobHexString());\n    }\n\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/sub/MysqlYearProcessor.java",
    "content": "package ai.chat2db.plugin.mysql.value.sub;\n\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * 功能描述\n *\n * @author: zgq\n * @date: 2024年06月01日 12:57\n */\npublic class MysqlYearProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return dataValue.getValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return new String(dataValue.getBytes());\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return new String(dataValue.getBytes());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/value/template/MysqlDmlValueTemplate.java",
    "content": "package ai.chat2db.plugin.mysql.value.template;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 13:31\n */\npublic class MysqlDmlValueTemplate {\n\n    public static final String GEOMETRY_TEMPLATE = \"ST_GeomFromText('%s')\";\n    public static final String BIT_TEMPLATE = \"b'%s'\";\n    public static final String HEX_TEMPLATE = \"0x%s\";\n\n\n    public static String wrapGeometry(String value) {\n        return String.format(GEOMETRY_TEMPLATE, value);\n    }\n\n    public static String wrapBit(String value) {\n        return String.format(BIT_TEMPLATE, value);\n    }\n\n    public static String wrapHex(String value) {\n        return String.format(HEX_TEMPLATE, value);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-mysql/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.mysql.MysqlPlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-mysql</artifactId>\n            <version>2.0.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n    <artifactId>chat2db-oceanbase</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/src/main/java/ai/chat2db/plugin/oceanbase/OceanBaseDBManage.java",
    "content": "package ai.chat2db.plugin.oceanbase;\n\nimport ai.chat2db.plugin.mysql.MysqlDBManage;\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.SQLExecutor;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\npublic class OceanBaseDBManage extends MysqlDBManage implements DBManage {\n\n    private static String PROCEDURE_SQL = \"SELECT COUNT(*) FROM information_schema.routines \" +\n            \"WHERE routine_type='PROCEDURE' AND routine_schema='%s' AND routine_name='%s';\";\n\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            boolean isCreateOrReplace = procedureBody.trim().toUpperCase().startsWith(\"CREATE OR REPLACE \");\n\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, schemaName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String checkProcedureSQL = String.format(PROCEDURE_SQL, schemaName.toUpperCase(), procedure.getProcedureName().toUpperCase());\n            String finalProcedureBody = procedureBody;\n            SQLExecutor.getInstance().execute(connection, checkProcedureSQL, resultSet -> {\n                if (resultSet.next()) {\n                    int count = resultSet.getInt(1);\n                    if (count >= 1) {\n                        if (isCreateOrReplace) {\n                            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet2 -> {});\n                        } else {\n                            throw new SQLException(\"Procedure with the same name already exists.\");\n                        }\n                    }\n                }\n            });\n            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet -> {});\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/src/main/java/ai/chat2db/plugin/oceanbase/OceanBaseMetaData.java",
    "content": "package ai.chat2db.plugin.oceanbase;\n\nimport ai.chat2db.plugin.mysql.MysqlMetaData;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\n\npublic class OceanBaseMetaData extends MysqlMetaData implements MetaData {\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/src/main/java/ai/chat2db/plugin/oceanbase/OceanBasePlugin.java",
    "content": "package ai.chat2db.plugin.oceanbase;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class OceanBasePlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"oceanbase.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new OceanBaseMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new OceanBaseDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/src/main/java/ai/chat2db/plugin/oceanbase/builder/OceanBaseSqlBuilder.java",
    "content": "//package ai.chat2db.plugin.oceanbase.builder;\n//\n//import ai.chat2db.plugin.oceanbase.type.OceanBaseColumnTypeEnum;\n//import ai.chat2db.plugin.oceanbase.type.OceanBaseIndexTypeEnum;\n//import ai.chat2db.spi.SqlBuilder;\n//import ai.chat2db.spi.jdbc.DefaultSqlBuilder;\n//import ai.chat2db.spi.model.*;\n//import org.apache.commons.lang3.StringUtils;\n//\n//public class OceanBaseSqlBuilder extends DefaultSqlBuilder implements SqlBuilder {\n//\n//    @Override\n//    public String buildCreateTableSql(Table table) {\n//        StringBuilder script = new StringBuilder();\n//        script.append(\"CREATE TABLE \");\n//        if(StringUtils.isNotBlank(table.getDatabaseName())) {\n//            script.append(\"`\").append(table.getName()).append(\"`\").append(\".\");\n//        }\n//        script.append(\"`\").append(table.getName()).append(\"`\").append(\" (\").append(\"\\n\");\n//\n//        // append column\n//        for (TableColumn column : table.getColumnList()) {\n//            if(StringUtils.isBlank(column.getName())|| StringUtils.isBlank(column.getColumnType())){\n//                continue;\n//            }\n//            OceanBaseColumnTypeEnum typeEnum = OceanBaseColumnTypeEnum.getByType(column.getColumnType());\n//            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n//        }\n//\n//        // append primary key and index\n//        for (TableIndex tableIndex : table.getIndexList()) {\n//            if(StringUtils.isBlank(tableIndex.getName())|| StringUtils.isBlank(tableIndex.getType())){\n//                continue;\n//            }\n//            OceanBaseIndexTypeEnum mysqlIndexTypeEnum = OceanBaseIndexTypeEnum.getByType(tableIndex.getType());\n//            script.append(\"\\t\").append(\"\").append(mysqlIndexTypeEnum.buildIndexScript(tableIndex)).append(\",\\n\");\n//        }\n//\n//        script = new StringBuilder(script.substring(0, script.length() - 2));\n//        script.append(\"\\n)\");\n//\n//\n//        if (StringUtils.isNotBlank(table.getEngine())) {\n//            script.append(\" ENGINE=\").append(table.getEngine());\n//        }\n//\n//        if (StringUtils.isNotBlank(table.getCharset())) {\n//            script.append(\" DEFAULT CHARACTER SET=\").append(table.getCharset());\n//        }\n//\n//        if (StringUtils.isNotBlank(table.getCollate())) {\n//            script.append(\" COLLATE=\").append(table.getCollate());\n//        }\n//\n//        if (table.getIncrementValue() != null) {\n//            script.append(\" AUTO_INCREMENT=\").append(table.getIncrementValue());\n//        }\n//\n//        if (StringUtils.isNotBlank(table.getComment())) {\n//            script.append(\" COMMENT='\").append(table.getComment()).append(\"'\");\n//        }\n//\n//        if (StringUtils.isNotBlank(table.getPartition())) {\n//            script.append(\" \\n\").append(table.getPartition());\n//        }\n//        script.append(\";\");\n//\n//        return script.toString();\n//    }\n//\n//    @Override\n//    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n//        StringBuilder script = new StringBuilder();\n//        script.append(\"ALTER TABLE \");\n//        if(StringUtils.isNotBlank(oldTable.getDatabaseName())) {\n//            script.append(\"`\").append(oldTable.getDatabaseName()).append(\"`\").append(\".\");\n//        }\n//        script.append(\"`\").append(oldTable.getName()).append(\"`\").append(\"\\n\");\n//        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n//            script.append(\"\\t\").append(\"RENAME TO \").append(\"`\").append(newTable.getName()).append(\"`\").append(\",\\n\");\n//        }\n//        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {\n//            script.append(\"\\t\").append(\"COMMENT=\").append(\"'\").append(newTable.getComment()).append(\"'\").append(\",\\n\");\n//        }\n//        if (oldTable.getIncrementValue() != newTable.getIncrementValue()) {\n//            script.append(\"\\t\").append(\"AUTO_INCREMENT=\").append(newTable.getIncrementValue()).append(\",\\n\");\n//        }\n//\n//        // append modify column\n//        for (TableColumn tableColumn : newTable.getColumnList()) {\n//            if (StringUtils.isNotBlank(tableColumn.getEditStatus()) &&  StringUtils.isNotBlank(tableColumn.getColumnType())&& StringUtils.isNotBlank(tableColumn.getName())){\n//                OceanBaseColumnTypeEnum typeEnum = OceanBaseColumnTypeEnum.getByType(tableColumn.getColumnType());\n//                script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\",\\n\");\n//            }\n//        }\n//\n//        // append modify index\n//        for (TableIndex tableIndex : newTable.getIndexList()) {\n//            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n//                OceanBaseIndexTypeEnum mysqlIndexTypeEnum = OceanBaseIndexTypeEnum.getByType(tableIndex.getType());\n//                script.append(\"\\t\").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(\",\\n\");\n//            }\n//        }\n//        if(script.length()>2) {\n//            script = new StringBuilder(script.substring(0, script.length() - 2));\n//            script.append(\";\");\n//        }\n//\n//        return script.toString();\n//    }\n//\n//\n//\n//    @Override\n//    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n//        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);\n//        sqlBuilder.append(sql);\n//        if (offset == 0) {\n//            sqlBuilder.append(\"\\n LIMIT \");\n//            sqlBuilder.append(pageSize);\n//        } else {\n//            sqlBuilder.append(\"\\n LIMIT \");\n//            sqlBuilder.append(offset);\n//            sqlBuilder.append(\",\");\n//            sqlBuilder.append(pageSize);\n//        }\n//        return sqlBuilder.toString();\n//    }\n//\n//\n//\n//\n//    @Override\n//    public String buildCreateDatabaseSql(Database database) {\n//        StringBuilder sqlBuilder = new StringBuilder();\n//        sqlBuilder.append(\"CREATE DATABASE `\"+database.getName()+\"`\");\n//        if (StringUtils.isNotBlank(database.getCharset())) {\n//            sqlBuilder.append(\" DEFAULT CHARACTER SET=\").append(database.getCharset());\n//        }\n//        if (StringUtils.isNotBlank(database.getCollation())) {\n//            sqlBuilder.append(\" COLLATE=\").append(database.getCollation());\n//        }\n//        return sqlBuilder.toString();\n//    }\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/src/main/java/ai/chat2db/plugin/oceanbase/oceanbase.json",
    "content": "{\n  \"dbType\": \"OCEANBASE\",\n  \"supportDatabase\": true,\n  \"supportSchema\": false,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:oceanbase://localhost:2883/\",\n      \"custom\": false,\n      \"defaultDriver\": true,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/oceanbase-client-2.4.2.jar\"\n      ],\n      \"jdbcDriver\": \"oceanbase-client-2.4.2.jar\",\n      \"jdbcDriverClass\": \"com.oceanbase.jdbc.Driver\"\n    }\n  ],\n  \"name\": \"OceanBase\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/src/main/java/ai/chat2db/plugin/oceanbase/type/OceanBaseColumnTypeEnum.java",
    "content": "//package ai.chat2db.plugin.oceanbase.type;\n//\n//import ai.chat2db.spi.ColumnBuilder;\n//import ai.chat2db.spi.enums.EditStatus;\n//import ai.chat2db.spi.model.ColumnType;\n//import ai.chat2db.spi.model.TableColumn;\n//import com.google.common.collect.Maps;\n//import org.apache.commons.lang3.StringUtils;\n//\n//import java.util.Arrays;\n//import java.util.List;\n//import java.util.Map;\n//\n//public enum OceanBaseColumnTypeEnum implements ColumnBuilder {\n//\n//    BIT(\"BIT\", true, false, true, false, false, false, true, true, false, false),\n//\n//    TINYINT(\"TINYINT\", false, false, true, true, false, false, true, true, false, false),\n//\n//    TINYINT_UNSIGNED(\"TINYINT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n//\n//    SMALLINT(\"SMALLINT\", false, false, true, true, false, false, true, true, false, false),\n//\n//    SMALLINT_UNSIGNED(\"SMALLINT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n//\n//    MEDIUMINT(\"MEDIUMINT\", false, false, true, true, false, false, true, true, false, false),\n//\n//    MEDIUMINT_UNSIGNED(\"MEDIUMINT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n//\n//    INT(\"INT\", false, false, true, true, false, false, true, true, false, false),\n//\n//\n//    INT_UNSIGNED(\"INT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n//\n//    BIGINT(\"BIGINT\", false, false, true, true, false, false, true, true, false, false),\n//\n//\n//    BIGINT_UNSIGNED(\"BIGINT UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n//\n//\n//    DECIMAL(\"DECIMAL\", true, true, true, false, false, false, true, true, false, false),\n//\n//    DECIMAL_UNSIGNED(\"DECIMAL UNSIGNED\", true, true, true, false, false, false, true, true, false, false),\n//\n//\n//    FLOAT(\"FLOAT\", true, true, true, false, false, false, true, true, false, false),\n//\n//    FLOAT_UNSIGNED(\"FLOAT UNSIGNED\", true, true, true, false, false, false, true, true, false, false),\n//\n//    DOUBLE(\"DOUBLE\", true, true, true, false, false, false, true, true, false, false),\n//\n//    DOUBLE_UNSIGNED(\"DOUBLE UNSIGNED\", true, true, true, false, false, false, true, true, false, false),\n//    DATE(\"DATE\", false, false, true, false, false, false, true, true, false, false),\n//    DATETIME(\"DATETIME\", true, false, true, false, false, false, true, true, true, false),\n//\n//    TIMESTAMP(\"TIMESTAMP\", true, false, true, false, false, false, true, true, true, false),\n//    TIME(\"TIME\", true, false, true, false, false, false, true, true, false, false),\n//    YEAR(\"YEAR\", false, false, true, false, false, false, true, true, false, false),\n//    CHAR(\"CHAR\", true, false, true, false, true, true, true, true, false, false),\n//\n//    VARCHAR(\"VARCHAR\", true, false, true, false, true, true, true, true, false, false),\n//\n//    BINARY(\"BINARY\", true, false, true, false, false, false, true, true, false, false),\n//\n//    VARBINARY(\"VARBINARY\", true, false, true, false, false, false, true, true, false, false),\n//\n//    TINYBLOB(\"TINYBLOB\", false, false, true, false, false, false, true, false, false, false),\n//\n//    BLOB(\"BLOB\", false, false, true, false, false, false, true, false, false, false),\n//\n//    MEDIUMBLOB(\"MEDIUMBLOB\", false, false, true, false, false, false, true, false, false, false),\n//\n//    LONGBLOB(\"LONGBLOB\", false, false, true, false, false, false, true, false, false, false),\n//\n//    TINYTEXT(\"TINYTEXT\", false, false, true, false, true, true, true, false, false, false),\n//\n//    TEXT(\"TEXT\", false, false, true, false, true, true, true, false, false, false),\n//\n//    MEDIUMTEXT(\"MEDIUMTEXT\", false, false, true, false, true, true, true, false, false, false),\n//\n//    LONGTEXT(\"LONGTEXT\", false, false, true, false, true, true, true, false, false, false),\n//\n//\n//    ENUM(\"ENUM\", false, false, true, false, true, true, true, true, true, true),\n//\n//\n//    BOOL(\"BOOL\", false, false, true, true, false, false, true, true, false, false),\n//\n//    INTEGER(\"INTEGER\", false, false, true, true, false, false, true, true, false, false),\n//\n//    INTEGER_UNSIGNED(\"INTEGER UNSIGNED\", false, false, true, true, false, false, true, true, false, false),\n//\n//    REAL(\"REAL\", true, true, true, false, false, false, true, true, false, false),\n//\n//    SET(\"SET\", false, false, true, false, true, true, true, true, true, true),\n//\n//\n//    GEOMETRY(\"GEOMETRY\", false, false, true, false, false, false, true, false, false, false),\n//\n//    POINT(\"POINT\", false, false, true, false, false, false, true, false, false, false),\n//\n//    LINESTRING(\"LINESTRING\", false, false, true, false, false, false, true, false, false, false),\n//\n//    POLYGON(\"POLYGON\", false, false, true, false, false, false, true, false, false, false),\n//\n//    MULTIPOINT(\"MULTIPOINT\", false, false, true, false, false, false, true, false, false, false),\n//\n//    MULTILINESTRING(\"MULTILINESTRING\", false, false, true, false, false, false, true, false, false, false),\n//\n//    MULTIPOLYGON(\"MULTIPOLYGON\", false, false, true, false, false, false, true, false, false, false),\n//\n//    GEOMETRYCOLLECTION(\"GEOMETRYCOLLECTION\", false, false, true, false, false, false, true, false, false, false),\n//\n//    JSON(\"JSON\", false, false, true, false, false, false, true, false, false, false);\n//\n//    private ColumnType columnType;\n//\n//    public static OceanBaseColumnTypeEnum getByType(String dataType) {\n//        return COLUMN_TYPE_MAP.get(dataType.toUpperCase());\n//    }\n//\n//    public ColumnType getColumnType() {\n//        return columnType;\n//    }\n//\n//\n//    OceanBaseColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportValue) {\n//        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent,supportValue,false);\n//    }\n//\n//    private static Map<String, OceanBaseColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n//\n//    static {\n//        for (OceanBaseColumnTypeEnum value : OceanBaseColumnTypeEnum.values()) {\n//            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n//        }\n//    }\n//\n//\n//    @Override\n//    public String buildCreateColumnSql(TableColumn column) {\n//        OceanBaseColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n//        if (type == null) {\n//            return \"\";\n//        }\n//        StringBuilder script = new StringBuilder();\n//\n//        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n//\n//        script.append(buildDataType(column, type)).append(\" \");\n//\n//        script.append(buildCharset(column,type)).append(\" \");\n//\n//        script.append(buildCollation(column,type)).append(\" \");\n//\n//        script.append(buildNullable(column,type)).append(\" \");\n//\n//        script.append(buildDefaultValue(column,type)).append(\" \");\n//\n//        script.append(buildExt(column,type)).append(\" \");\n//\n//        script.append(buildAutoIncrement(column,type)).append(\" \");\n//\n//        script.append(buildComment(column,type)).append(\" \");\n//\n//        return script.toString();\n//    }\n//\n//    private String buildCharset(TableColumn column, OceanBaseColumnTypeEnum type) {\n//        if(!type.getColumnType().isSupportCharset() || StringUtils.isEmpty(column.getCharSetName())){\n//            return \"\";\n//        }\n//        return StringUtils.join(\"CHARACTER SET \",column.getCharSetName());\n//    }\n//\n//    private String buildCollation(TableColumn column, OceanBaseColumnTypeEnum type) {\n//        if(!type.getColumnType().isSupportCollation() || StringUtils.isEmpty(column.getCollationName())){\n//            return \"\";\n//        }\n//        return StringUtils.join(\"COLLATE \",column.getCollationName());\n//    }\n//\n//    @Override\n//    public String buildModifyColumn(TableColumn tableColumn) {\n//\n//        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n//            return StringUtils.join(\"DROP COLUMN `\", tableColumn.getName() + \"`\");\n//        }\n//        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n//            return StringUtils.join(\"ADD COLUMN \", buildCreateColumnSql(tableColumn));\n//        }\n//        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n//            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n//                return StringUtils.join(\"CHANGE COLUMN `\", tableColumn.getOldName(), \"` \", buildCreateColumnSql(tableColumn));\n//            } else {\n//                return StringUtils.join(\"MODIFY COLUMN \", buildCreateColumnSql(tableColumn));\n//            }\n//        }\n//        return \"\";\n//    }\n//\n//    private String buildAutoIncrement(TableColumn column, OceanBaseColumnTypeEnum type) {\n//        if(!type.getColumnType().isSupportAutoIncrement()){\n//            return \"\";\n//        }\n//        if (column.getAutoIncrement() != null && column.getAutoIncrement()) {\n//            return \"AUTO_INCREMENT\";\n//        }\n//        return \"\";\n//    }\n//\n//    private String buildComment(TableColumn column, OceanBaseColumnTypeEnum type) {\n//        if(!type.columnType.isSupportComments() || StringUtils.isEmpty(column.getComment())){\n//            return \"\";\n//        }\n//        return StringUtils.join(\"COMMENT '\",column.getComment(),\"'\");\n//    }\n//\n//    private String buildExt(TableColumn column, OceanBaseColumnTypeEnum type) {\n//        if(!type.columnType.isSupportExtent() || StringUtils.isEmpty(column.getExtent())){\n//            return \"\";\n//        }\n//        return column.getComment();\n//    }\n//\n//    private String buildDefaultValue(TableColumn column, OceanBaseColumnTypeEnum type) {\n//        if(!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())){\n//            return \"\";\n//        }\n//\n//        if(\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())){\n//            return StringUtils.join(\"DEFAULT ''\");\n//        }\n//\n//        if(\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())){\n//            return StringUtils.join(\"DEFAULT NULL\");\n//        }\n//\n//        if(Arrays.asList(CHAR,VARCHAR,BINARY,VARBINARY, SET,ENUM).contains(type)){\n//            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n//        }\n//\n//        if(Arrays.asList(DATE,TIME,YEAR).contains(type)){\n//            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n//        }\n//\n//        if(Arrays.asList(DATETIME,TIMESTAMP).contains(type)){\n//            if(\"CURRENT_TIMESTAMP\".equalsIgnoreCase(column.getDefaultValue().trim())){\n//                return StringUtils.join(\"DEFAULT \",column.getDefaultValue());\n//            }\n//            return StringUtils.join(\"DEFAULT '\",column.getDefaultValue(),\"'\");\n//        }\n//\n//        return StringUtils.join(\"DEFAULT \",column.getDefaultValue());\n//    }\n//\n//    private String buildNullable(TableColumn column, OceanBaseColumnTypeEnum type) {\n//        if(!type.getColumnType().isSupportNullable()){\n//            return \"\";\n//        }\n//        if (column.getNullable()!=null && 1==column.getNullable()) {\n//            return \"NULL\";\n//        } else {\n//            return \"NOT NULL\";\n//        }\n//    }\n//\n//    private String buildDataType(TableColumn column, OceanBaseColumnTypeEnum type) {\n//        String columnType = type.columnType.getTypeName();\n//        if (Arrays.asList(BINARY, VARBINARY, VARCHAR, CHAR).contains(type)) {\n//            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n//        }\n//\n//        if (BIT.equals(type)) {\n//            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n//        }\n//\n//        if (Arrays.asList(TIME, DATETIME, TIMESTAMP).contains(type)) {\n//            if (column.getColumnSize() == null || column.getColumnSize() == 0) {\n//                return columnType;\n//            } else {\n//                return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n//            }\n//        }\n//\n//\n//        if (Arrays.asList(DECIMAL, FLOAT, DOUBLE).contains(type)) {\n//            if (column.getColumnSize() == null || column.getDecimalDigits() == null) {\n//                return columnType;\n//            }\n//            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n//                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \")\");\n//            }\n//            if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n//                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n//            }\n//        }\n//\n//        if (Arrays.asList(DECIMAL_UNSIGNED, FLOAT_UNSIGNED, DECIMAL_UNSIGNED).contains(type)) {\n//            if (column.getColumnSize() == null || column.getDecimalDigits() == null) {\n//                return columnType;\n//            }\n//            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n//                return unsignedDataType(columnType, \"(\" + column.getColumnSize() + \")\");\n//            }\n//            if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n//                return unsignedDataType(columnType, \"(\" + column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n//            }\n//        }\n//\n//        if(Arrays.asList(SET,ENUM).contains(type)){\n//            if(!StringUtils.isEmpty( column.getValue())){\n//                return StringUtils.join(columnType,\"(\",column.getValue(),\")\");\n//            }\n//            //List<String> enumList = column.\n//        }\n//\n//        return columnType;\n//    }\n//\n//    private String unsignedDataType(String dataTypeName, String middle) {\n//        String[] split = dataTypeName.split(\" \");\n//        if (split.length == 2) {\n//            return StringUtils.join(split[0], middle, split[1]);\n//        }\n//        return StringUtils.join(dataTypeName, middle);\n//    }\n//\n//    public static List<ColumnType> getTypes(){\n//       return Arrays.stream(OceanBaseColumnTypeEnum.values()).map(columnTypeEnum ->\n//                columnTypeEnum.getColumnType()\n//        ).toList();\n//    }\n//\n//\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/src/main/java/ai/chat2db/plugin/oceanbase/type/OceanBaseIndexTypeEnum.java",
    "content": "//package ai.chat2db.plugin.oceanbase.type;\n//\n//import ai.chat2db.spi.enums.EditStatus;\n//import ai.chat2db.spi.model.IndexType;\n//import ai.chat2db.spi.model.TableIndex;\n//import ai.chat2db.spi.model.TableIndexColumn;\n//import org.apache.commons.lang3.StringUtils;\n//\n//import java.util.Arrays;\n//import java.util.List;\n//\n//public enum OceanBaseIndexTypeEnum {\n//\n//    PRIMARY_KEY(\"Primary\", \"PRIMARY KEY\"),\n//\n//    NORMAL(\"Normal\", \"INDEX\"),\n//\n//    UNIQUE(\"Unique\", \"UNIQUE INDEX\"),\n//\n//    FULLTEXT(\"Fulltext\", \"FULLTEXT INDEX\"),\n//\n//    SPATIAL(\"Spatial\", \"SPATIAL INDEX\");\n//\n//    public String getName() {\n//        return name;\n//    }\n//\n//    private String name;\n//\n//\n//    public String getKeyword() {\n//        return keyword;\n//    }\n//\n//    private String keyword;\n//\n//    public IndexType getIndexType() {\n//        return indexType;\n//    }\n//\n//    public void setIndexType(IndexType indexType) {\n//        this.indexType = indexType;\n//    }\n//\n//    private IndexType indexType;\n//\n//    OceanBaseIndexTypeEnum(String name, String keyword) {\n//        this.name = name;\n//        this.keyword = keyword;\n//        this.indexType = new IndexType(name);\n//    }\n//\n//\n//    public static OceanBaseIndexTypeEnum getByType(String type) {\n//        for (OceanBaseIndexTypeEnum value : OceanBaseIndexTypeEnum.values()) {\n//            if (value.name.equalsIgnoreCase(type)) {\n//                return value;\n//            }\n//        }\n//        return null;\n//    }\n//\n//    public String buildIndexScript(TableIndex tableIndex) {\n//        StringBuilder script = new StringBuilder();\n//\n//        script.append(keyword).append(\" \");\n//\n//        script.append(buildIndexName(tableIndex)).append(\" \");\n//\n//        script.append(buildIndexColumn(tableIndex)).append(\" \");\n//\n//        script.append(buildIndexComment(tableIndex)).append(\" \");\n//\n//        return script.toString();\n//    }\n//\n//    private String buildIndexComment(TableIndex tableIndex) {\n//        if(StringUtils.isBlank(tableIndex.getComment())){\n//            return \"\";\n//        }else {\n//            return StringUtils.join(\"COMMENT '\",tableIndex.getComment(),\"'\");\n//        }\n//\n//    }\n//\n//    private String buildIndexColumn(TableIndex tableIndex) {\n//        StringBuilder script = new StringBuilder();\n//        script.append(\"(\");\n//        for (TableIndexColumn column : tableIndex.getColumnList()) {\n//            if(StringUtils.isNotBlank(column.getColumnName())) {\n//                script.append(\"`\").append(column.getColumnName()).append(\"`\");\n//                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {\n//                    script.append(\" \").append(column.getAscOrDesc());\n//                }\n//                script.append(\",\");\n//            }\n//        }\n//        script.deleteCharAt(script.length() - 1);\n//        script.append(\")\");\n//        return script.toString();\n//    }\n//\n//    private String buildIndexName(TableIndex tableIndex) {\n//        if(this.equals(PRIMARY_KEY)){\n//            return \"\";\n//        }else {\n//            return \"`\"+tableIndex.getName()+\"`\";\n//        }\n//    }\n//\n//    public String buildModifyIndex(TableIndex tableIndex) {\n//        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n//            return buildDropIndex(tableIndex);\n//        }\n//        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n//            return StringUtils.join(buildDropIndex(tableIndex),\",\\n\", \"ADD \", buildIndexScript(tableIndex));\n//        }\n//        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n//            return StringUtils.join(\"ADD \", buildIndexScript(tableIndex));\n//        }\n//        return \"\";\n//    }\n//\n//    private String buildDropIndex(TableIndex tableIndex) {\n//        if (OceanBaseIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {\n//            return StringUtils.join(\"DROP PRIMARY KEY\");\n//        }\n//        return StringUtils.join(\"DROP INDEX `\", tableIndex.getOldName(),\"`\");\n//    }\n//    public static List<IndexType> getIndexTypes() {\n//        return Arrays.asList(OceanBaseIndexTypeEnum.values()).stream().map(OceanBaseIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n//    }\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oceanbase/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.oceanbase.OceanBasePlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>chat2db-oracle</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleDBManage.java",
    "content": "package ai.chat2db.plugin.oracle;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.SqlUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.List;\n\n@Slf4j\npublic class OracleDBManage extends DefaultDBManage implements DBManage {\n\n    private static String PROCEDURE_SQL = \"SELECT COUNT(*) FROM ALL_OBJECTS \" +\n            \"WHERE OWNER = '%s' AND OBJECT_NAME = '%s' AND OBJECT_TYPE = 'PROCEDURE'\";\n\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        exportTables(connection, databaseName, schemaName, asyncContext);\n        exportViews(connection, asyncContext, schemaName);\n        exportProcedures(connection, schemaName, asyncContext);\n        exportFunctions(connection, schemaName, asyncContext);\n//        exportTriggers(connection, schemaName, asyncContext);\n    }\n\n    private void exportTables(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getTables(null, schemaName, null, new String[]{\"TABLE\", \"SYSTEM TABLE\"})) {\n            while (resultSet.next()) {\n                String tableName = resultSet.getString(\"TABLE_NAME\");\n                exportTable(connection, databaseName, schemaName, tableName, asyncContext);\n            }\n        }\n    }\n\n\n    public void exportTable(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {\n        String tableDDL = Chat2DBContext.getMetaData().tableDDL(connection, databaseName, schemaName, tableName);\n        String sqlBuilder = \"DROP TABLE \" + SqlUtils.quoteObjectName(tableName) + \";\" + tableDDL + \"\\n\";\n        asyncContext.write(sqlBuilder);\n        if (asyncContext.isContainsData()) {\n            exportTableData(connection, databaseName, schemaName, tableName, asyncContext);\n        }\n\n    }\n\n\n    private void exportViews(Connection connection, AsyncContext asyncContext, String schemaName) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getTables(null, schemaName, null, new String[]{\"VIEW\"})) {\n            while (resultSet.next()) {\n                String viewName = resultSet.getString(\"TABLE_NAME\");\n                exportView(connection, asyncContext, schemaName, viewName);\n            }\n        }\n    }\n\n    private void exportView(Connection connection, AsyncContext asyncContext, String schemaName, String viewName) {\n        Table view = Chat2DBContext.getMetaData().view(connection, null, schemaName, viewName);\n        asyncContext.write(view.getDdl() + \";\" + \"\\n\");\n    }\n\n    private void exportProcedures(Connection connection, String schemaName, AsyncContext asyncContext) {\n        List<Procedure> procedures = Chat2DBContext.getMetaData().procedures(connection, null, schemaName);\n        if (CollectionUtils.isNotEmpty(procedures)) {\n            for (Procedure procedure : procedures) {\n                String procedureName = procedure.getProcedureName();\n                exportProcedure(connection, schemaName, procedureName, asyncContext);\n            }\n        }\n\n    }\n\n    private void exportProcedure(Connection connection, String schemaName, String procedureName, AsyncContext asyncContext) {\n        Procedure procedure = Chat2DBContext.getMetaData().procedure(connection, null, schemaName, procedureName);\n        asyncContext.write(procedure.getProcedureBody() + \"\\n\");\n\n    }\n\n    private void exportTriggers(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT TRIGGER_NAME FROM all_triggers where OWNER='%s'\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String triggerName = resultSet.getString(\"TRIGGER_NAME\");\n                exportTrigger(connection, schemaName, triggerName, asyncContext);\n            }\n        }\n    }\n\n    private void exportTrigger(Connection connection, String schemaName, String triggerName, AsyncContext asyncContext) {\n        Trigger trigger = Chat2DBContext.getMetaData().trigger(connection, null, schemaName, triggerName);\n        asyncContext.write(trigger.getTriggerBody() + \";\" + \"\\n\");\n\n    }\n\n    private void exportFunctions(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getFunctions(null, schemaName, null)) {\n            while (resultSet.next()) {\n                String functionName = resultSet.getString(\"FUNCTION_NAME\");\n                exportFunction(connection, schemaName, functionName, asyncContext);\n            }\n        }\n    }\n\n    private void exportFunction(Connection connection, String schemaName, String functionName, AsyncContext asyncContext) {\n        Function function = Chat2DBContext.getMetaData().function(connection, null, schemaName, functionName);\n        asyncContext.write(function.getFunctionBody() + \"\\n\");\n    }\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            boolean isCreateOrReplace = procedureBody.trim().toUpperCase().startsWith(\"CREATE OR REPLACE \");\n\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, schemaName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String checkProcedureSQL = String.format(PROCEDURE_SQL, schemaName.toUpperCase(), procedure.getProcedureName().toUpperCase());\n            String finalProcedureBody = procedureBody;\n            SQLExecutor.getInstance().execute(connection, checkProcedureSQL, resultSet -> {\n                if (resultSet.next()) {\n                    int count = resultSet.getInt(1);\n                    if (count >= 1) {\n                        if (isCreateOrReplace) {\n                            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet2 -> {});\n                        } else {\n                            throw new SQLException(\"Procedure with the same name already exists.\");\n                        }\n                    }\n                }\n            });\n\n            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet -> {});\n\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n        if (ObjectUtils.anyNull(connectInfo) || StringUtils.isEmpty(connectInfo.getSchemaName())) {\n            return;\n        }\n        String schemaName = connectInfo.getSchemaName();\n        try {\n            SQLExecutor.getInstance().execute(connection, \"ALTER SESSION SET CURRENT_SCHEMA = \\\"\" + schemaName + \"\\\"\");\n        } catch (SQLException e) {\n            log.error(\"connectDatabase error\", e);\n        }\n    }\n\n    @Override\n    public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName, boolean copyData) throws SQLException {\n        String sql = \"\";\n        if (copyData) {\n            sql = \"CREATE TABLE \" + SqlUtils.quoteObjectName(newTableName) + \" AS SELECT * FROM \" + SqlUtils.quoteObjectName(tableName);\n        } else {\n            sql = \"CREATE TABLE \" + SqlUtils.quoteObjectName(newTableName) + \" AS SELECT * FROM \" + SqlUtils.quoteObjectName(tableName) + \" WHERE 1=0\";\n        }\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"DROP TABLE \" + SqlUtils.quoteObjectName(tableName);\n        SQLExecutor.getInstance().execute(connection, sql, (resultSet) -> null);\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleMetaData.java",
    "content": "package ai.chat2db.plugin.oracle;\n\nimport ai.chat2db.plugin.oracle.builder.OracleSqlBuilder;\nimport ai.chat2db.plugin.oracle.type.OracleColumnTypeEnum;\nimport ai.chat2db.plugin.oracle.type.OracleDefaultValueEnum;\nimport ai.chat2db.plugin.oracle.type.OracleIndexTypeEnum;\nimport ai.chat2db.plugin.oracle.value.OracleValueProcessor;\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.SortUtils;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Lists;\nimport jakarta.validation.constraints.NotEmpty;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.Reader;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n@Slf4j\npublic class OracleMetaData extends DefaultMetaService implements MetaData {\n\n    private static final String TABLE_DDL_SQL = \"select dbms_metadata.get_ddl('TABLE','%s','%s') as sql from dual\";\n    private static final String TABLE_COMMENT_SQL = \"select owner, table_name, comments from ALL_TAB_COMMENTS where OWNER = '%s'  and TABLE_NAME = '%s'\";\n    private static final String TABLE_COLUMN_COMMENT_SQL = \"\"\"\n                                                           SELECT owner, table_name, column_name, comments\n                                                           FROM all_col_comments\n                                                           WHERE  owner = '%s' and table_name = '%s' and comments is not null\"\"\";\n\n    private List<String> systemSchemas = Arrays.asList(\"ANONYMOUS\", \"APEX_030200\", \"APEX_PUBLIC_USER\", \"APPQOSSYS\", \"BI\", \"CTXSYS\", \"DBSNMP\", \"DIP\", \"EXFSYS\", \"FLOWS_FILES\", \"HR\", \"IX\", \"MDDATA\", \"MDSYS\", \"MGMT_VIEW\", \"OE\", \"OLAPSYS\", \"ORACLE_OCM\", \"ORDDATA\", \"ORDPLUGINS\", \"ORDSYS\", \"OUTLN\", \"OWBSYS\", \"OWBSYS_AUDIT\", \"PM\", \"SCOTT\", \"SH\", \"SI_INFORMTN_SCHEMA\", \"SPATIAL_CSW_ADMIN_USR\", \"SPATIAL_WFS_ADMIN_USR\", \"SYS\", \"SYSMAN\", \"SYSTEM\", \"WMSYS\", \"XDB\", \"XS$NULL\");\n\n    private static final String PROCEDURE_LIST_DDL = \"\"\"\n                                                     SELECT OBJECT_NAME, OBJECT_TYPE\n                                                     FROM ALL_OBJECTS\n                                                     WHERE OBJECT_TYPE IN ('PROCEDURE')\n                                                       AND OWNER = '%s'\"\"\";\n    private static final String TABLE_INDEX_DDL_SQL = \"\"\"\n                                                      SELECT DBMS_METADATA.GET_DDL('INDEX', index_name, table_owner) AS ddl,\n                                                      index_name AS INDEX_NAME\n                                                      FROM all_indexes\n                                                      WHERE table_owner = '%s' AND table_name = '%s'\"\"\";\n    private static final String PU_INDEX_NAME_SQL = \"\"\"\n                                                    SELECT DISTINCT AC.INDEX_NAME\n                                                    FROM ALL_CONSTRAINTS AC\n                                                    WHERE  AC.OWNER = '%s' AND AC.TABLE_NAME = '%s'\n                                                      AND AC.CONSTRAINT_TYPE IN ('P', 'U')\"\"\";\n\n    @Override\n    public List<Procedure> procedures(Connection connection, String databaseName, String schemaName) {\n        String sql = String.format(PROCEDURE_LIST_DDL, schemaName);\n        ArrayList<Procedure> procedures = new ArrayList<>();\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Procedure procedure = new Procedure();\n                procedure.setProcedureName(resultSet.getString(\"object_name\"));\n                procedures.add(procedure);\n            }\n        });\n        return procedures;\n    }\n\n    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);\n        return SortUtils.sortSchema(schemas, systemSchemas);\n    }\n\n    @Override\n    public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {\n        // TODO: only_read user can not get ddl\n        String sql = String.format(TABLE_DDL_SQL, tableName, schemaName);\n        String tableCommentSql = String.format(TABLE_COMMENT_SQL, schemaName, tableName);\n        String tableColumnCommentSql = String.format(TABLE_COLUMN_COMMENT_SQL, schemaName, tableName);\n        String tableIndexSql = String.format(TABLE_INDEX_DDL_SQL, schemaName, tableName);\n        String PUIndexSql = String.format(PU_INDEX_NAME_SQL, schemaName, tableName);\n        StringBuilder ddlBuilder = new StringBuilder();\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            try {\n                if (resultSet.next()) {\n                    ddlBuilder.append(resultSet.getString(\"sql\")).append(\";\");\n                }\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            }\n        });\n        SQLExecutor.getInstance().execute(connection, tableCommentSql, resultSet -> {\n            if (resultSet.next()) {\n                String tableComment = resultSet.getString(\"comments\");\n                if (StringUtils.isNotBlank(tableComment)) {\n                    ddlBuilder.append(\"\\nCOMMENT ON TABLE \").append(SqlUtils.quoteObjectName(tableName)).append(\" IS \")\n                            .append(EasyStringUtils.escapeAndQuoteString(tableComment)).append(\";\");\n                }\n            }\n        });\n        SQLExecutor.getInstance().execute(connection, tableColumnCommentSql, resultSet -> {\n            while (resultSet.next()) {\n                String columnName = resultSet.getString(\"column_name\");\n                String columnComment = resultSet.getString(\"comments\");\n                if (StringUtils.isNotBlank(columnComment)) {\n                    ddlBuilder.append(\"\\nCOMMENT ON COLUMN \")\n                            .append(SqlUtils.quoteObjectName(tableName)).append(\".\")\n                            .append(SqlUtils.quoteObjectName(columnName)).append(\" IS \")\n                            .append(EasyStringUtils.escapeAndQuoteString(columnComment)).append(\";\");\n                }\n            }\n        });\n        List<String> indexNames = SQLExecutor.getInstance().execute(connection, PUIndexSql, resultSet -> {\n            List<String> PUIndexNames = new ArrayList<>();\n            while (resultSet.next()) {\n                String indexName = resultSet.getString(\"index_name\");\n                if (StringUtils.isNotBlank(indexName)) {\n                    PUIndexNames.add(indexName);\n                }\n            }\n            return PUIndexNames;\n        });\n        SQLExecutor.getInstance().execute(connection, tableIndexSql, resultSet -> {\n            while (resultSet.next()) {\n                String indexName = resultSet.getString(\"INDEX_NAME\");\n                if (CollectionUtils.isNotEmpty(indexNames) && indexNames.contains(indexName)) {\n                    continue;\n                }\n                String ddl = resultSet.getString(\"ddl\");\n                if (StringUtils.isNotBlank(ddl)) {\n                    ddlBuilder.append(\"\\n\").append(ddl).append(\";\");\n                }\n            }\n        });\n        return ddlBuilder.toString();\n\n    }\n\n    private static String SELECT_TABLE_SQL = \"SELECT A.OWNER, A.TABLE_NAME, B.COMMENTS \" +\n            \"FROM ALL_TABLES A LEFT JOIN ALL_TAB_COMMENTS B ON  A.OWNER = B.OWNER  AND A.TABLE_NAME = B.TABLE_NAME\\n\" +\n            \"where A.OWNER = '%s' \";\n\n    @Override\n    public List<Table> tables(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = String.format(SELECT_TABLE_SQL, schemaName);\n        if (StringUtils.isNotBlank(tableName)) {\n            sql = sql + \" and A.TABLE_NAME = '\" + tableName + \"'\";\n        }\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            List<Table> tables = new ArrayList<>();\n            while (resultSet.next()) {\n                Table table = new Table();\n                table.setDatabaseName(databaseName);\n                table.setSchemaName(schemaName);\n                table.setName(resultSet.getString(\"TABLE_NAME\"));\n                table.setComment(resultSet.getString(\"COMMENTS\"));\n                tables.add(table);\n            }\n            return tables;\n        });\n    }\n\n    private static String SELECT_TAB_COLS = \"SELECT atc.column_id , atc.column_name as COLUMN_NAME, atc.data_type as DATA_TYPE , atc.data_length as DATA_LENGTH , atc.data_type_mod , atc.nullable ,  atc.data_default as DATA_DEFAULT,  acc.comments ,  atc.DATA_PRECISION ,  atc.DATA_SCALE , atc.CHAR_USED  FROM  all_tab_columns atc, all_col_comments acc WHERE atc.owner = acc.owner AND atc.table_name = acc.table_name AND atc.column_name = acc.column_name AND atc.owner = '%s'  AND atc.table_name = '%s'  order by atc.column_id\";\n\n    @Override\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {\n        List<TableColumn> tableColumns = super.columns(connection, databaseName, schemaName, tableName);\n        if (CollectionUtils.isNotEmpty(tableColumns)) {\n            Map<String, TableColumn> tableColumnMap = getTableColumns(connection, databaseName, schemaName, tableName);\n            for (TableColumn tableColumn : tableColumns) {\n                tableColumn.setColumnType(SqlUtils.removeDigits(tableColumn.getColumnType()));\n                TableColumn column = tableColumnMap.get(tableColumn.getName());\n                if (column != null) {\n                    tableColumn.setUnit(column.getUnit());\n                    tableColumn.setComment(column.getComment());\n                    tableColumn.setDefaultValue(column.getDefaultValue());\n                    tableColumn.setOrdinalPosition(column.getOrdinalPosition());\n                    tableColumn.setNullable(column.getNullable());\n                }\n            }\n        }\n        return tableColumns;\n    }\n\n    private Map<String, TableColumn> getTableColumns(Connection connection, String databaseName, String schemaName, String tableName) {\n        Map<String, TableColumn> tableColumns = new HashMap<>();\n        String sql = String.format(SELECT_TAB_COLS, schemaName, tableName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                TableColumn tableColumn = new TableColumn();\n                tableColumn.setTableName(tableName);\n                tableColumn.setSchemaName(schemaName);\n                try {\n                    //\n                    // Fields of the LONG type cannot be retrieved using getObject. They need to be accessed using getCharacterStream, and must be read first in the sequence.\n                    Reader reader = resultSet.getCharacterStream(\"DATA_DEFAULT\");\n                    if (reader != null) {\n                        StringBuilder sb = new StringBuilder();\n                        int charValue;\n                        while ((charValue = reader.read()) != -1) {\n                            sb.append((char) charValue);\n                        }\n                        tableColumn.setDefaultValue(sb.toString());\n                    }\n                } catch (Exception e) {\n                    log.error(\"getDefaultValue error\", e);\n                }\n                tableColumn.setName(resultSet.getString(\"COLUMN_NAME\"));\n                String dataType = resultSet.getString(\"DATA_TYPE\");\n                if (dataType.contains(\"(\")) {\n                    dataType = dataType.substring(0, dataType.indexOf(\"(\")).trim();\n                }\n                tableColumn.setColumnType(dataType);\n                Integer dataPrecision = resultSet.getInt(\"DATA_PRECISION\");\n                if (resultSet.getString(\"DATA_PRECISION\") != null) {\n                    tableColumn.setColumnSize(dataPrecision);\n                } else {\n                    tableColumn.setColumnSize(resultSet.getInt(\"DATA_LENGTH\"));\n                }\n//                Object dataDefault = resultSet.getObject(7);\n//                if(dataDefault!=null) {\n//                    tableColumn.setDefaultValue(dataDefault.toString());\n//                }\n\n\n                tableColumn.setComment(resultSet.getString(\"COMMENTS\"));\n                tableColumn.setNullable(\"Y\".equalsIgnoreCase(resultSet.getString(\"NULLABLE\")) ? 1 : 0);\n                tableColumn.setOrdinalPosition(resultSet.getInt(\"COLUMN_ID\"));\n                tableColumn.setDecimalDigits(resultSet.getInt(\"DATA_SCALE\"));\n                String charUsed = resultSet.getString(\"CHAR_USED\");\n                if (\"B\".equalsIgnoreCase(charUsed)) {\n                    tableColumn.setUnit(\"BYTE\");\n                } else if (\"C\".equalsIgnoreCase(charUsed)) {\n                    tableColumn.setUnit(\"CHAR\");\n                }\n                tableColumns.put(tableColumn.getName(), tableColumn);\n            }\n            return tableColumns;\n        });\n\n    }\n\n    private static String ROUTINES_SQL\n            = \"SELECT LINE, TEXT \"\n            + \"FROM ALL_SOURCE \"\n            + \"WHERE TYPE = '%s' AND OWNER = '%s' AND NAME = '%s'\"\n            + \"ORDER BY LINE\";\n\n    @Override\n    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,\n                             String functionName) {\n        String sql = String.format(ROUTINES_SQL, \"FUNCTION\", schemaName, functionName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Function function = new Function();\n            function.setDatabaseName(databaseName);\n            function.setSchemaName(schemaName);\n            function.setFunctionName(functionName);\n            StringBuilder bodyBuilder = new StringBuilder(\"CREATE OR REPLACE \");\n            while (resultSet.next()) {\n                bodyBuilder.append(resultSet.getString(\"TEXT\")).append(\"\\n\");\n            }\n            String functionBody = bodyBuilder.toString().trim();\n            if (!functionBody.endsWith(\"/\")) {\n                functionBody += \"\\n/\";\n            }\n            function.setFunctionBody(functionBody);\n            return function;\n\n        });\n\n    }\n\n    private static String TRIGGER_SQL_LIST\n            = \"SELECT TRIGGER_NAME \"\n            + \"FROM ALL_TRIGGERS WHERE OWNER = '%s'\";\n\n    private static String SELECT_PK_SQL = \"select  acc.CONSTRAINT_NAME from  all_cons_columns acc,  all_constraints ac  where  acc.constraint_name = ac.constraint_name  and acc.owner = ac.owner  and acc.owner = '%s'  and ac.constraint_type = 'P'  and ac.table_name = '%s' \";\n\n    private static String SELECT_TABLE_INDEX = \"SELECT ai.index_name AS Key_name, aic.column_name AS Column_name, ai.index_type AS Index_type, ai.uniqueness AS Unique_name, aic.COLUMN_POSITION as Seq_in_index, aic.descend AS Collation, ex.COLUMN_EXPRESSION as COLUMN_EXPRESSION FROM all_ind_columns aic JOIN all_indexes ai ON aic.table_owner = ai.table_owner and aic.table_name = ai.table_name and aic.index_name = ai.index_name LEFT JOIN ALL_IND_EXPRESSIONS ex ON aic.table_owner = ex.table_owner and aic.table_name = ex.table_name and aic.index_name = ex.index_name where ai.table_owner = '%s' AND ai.table_name = '%s' \";\n\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        String pkSql = String.format(SELECT_PK_SQL, schemaName, tableName);\n        Set<String> pkSet = new HashSet<>();\n        SQLExecutor.getInstance().execute(connection, pkSql, resultSet -> {\n                                              while (resultSet.next()) {\n                                                  pkSet.add(resultSet.getString(\"CONSTRAINT_NAME\"));\n                                              }\n                                              return null;\n                                          }\n        );\n\n        String sql = String.format(SELECT_TABLE_INDEX, schemaName, tableName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            LinkedHashMap<String, TableIndex> map = new LinkedHashMap();\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"Key_name\");\n                TableIndex tableIndex = map.get(keyName);\n                if (tableIndex != null) {\n                    List<TableIndexColumn> columnList = tableIndex.getColumnList();\n                    columnList.add(getTableIndexColumn(resultSet));\n                    columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition))\n                            .collect(Collectors.toList());\n                    tableIndex.setColumnList(columnList);\n                } else {\n                    TableIndex index = new TableIndex();\n                    index.setDatabaseName(databaseName);\n                    index.setSchemaName(schemaName);\n                    index.setTableName(tableName);\n                    index.setName(keyName);\n                    index.setUnique(\"unique\".equalsIgnoreCase(resultSet.getString(\"Unique_name\")));\n                    index.setType(resultSet.getString(\"Index_type\"));\n                    List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n                    tableIndexColumns.add(getTableIndexColumn(resultSet));\n                    index.setColumnList(tableIndexColumns);\n                    if (index.getUnique()) {\n                        index.setType(OracleIndexTypeEnum.UNIQUE.getName());\n                    } else if (\"NORMAL\".equalsIgnoreCase(index.getType())) {\n                        index.setType(OracleIndexTypeEnum.NORMAL.getName());\n                    } else if (\"BITMAP\".equalsIgnoreCase(index.getType())) {\n                        index.setType(OracleIndexTypeEnum.BITMAP.getName());\n                    } else if (StringUtils.isNotBlank(index.getType()) && index.getType().toUpperCase().contains(\"NORMAL\")) {\n                        index.setType(OracleIndexTypeEnum.NORMAL.getName());\n                    }\n                    if (pkSet.contains(keyName)) {\n                        index.setType(OracleIndexTypeEnum.PRIMARY_KEY.getName());\n                    }\n                    map.put(keyName, index);\n                }\n            }\n            return map.values().stream().collect(Collectors.toList());\n        });\n\n    }\n\n    private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException {\n        TableIndexColumn tableIndexColumn = new TableIndexColumn();\n        tableIndexColumn.setColumnName(resultSet.getString(\"Column_name\"));\n        String expression = resultSet.getString(\"COLUMN_EXPRESSION\");\n        if (!StringUtils.isBlank(expression)) {\n            tableIndexColumn.setColumnName(expression.replace(\"\\\"\", \"\"));\n        }\n        tableIndexColumn.setOrdinalPosition(resultSet.getShort(\"Seq_in_index\"));\n        tableIndexColumn.setCollation(resultSet.getString(\"Collation\"));\n        tableIndexColumn.setAscOrDesc(resultSet.getString(\"Collation\"));\n        return tableIndexColumn;\n    }\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        List<Trigger> triggers = new ArrayList<>();\n        return SQLExecutor.getInstance().execute(connection, String.format(TRIGGER_SQL_LIST, schemaName),\n                                                 resultSet -> {\n                                                     while (resultSet.next()) {\n                                                         String triggerName = resultSet.getString(\"TRIGGER_NAME\");\n                                                         Trigger trigger = new Trigger();\n                                                         trigger.setTriggerName(triggerName == null ? \"\" : triggerName.trim());\n                                                         trigger.setSchemaName(schemaName);\n                                                         trigger.setDatabaseName(databaseName);\n                                                         triggers.add(trigger);\n                                                     }\n                                                     return triggers;\n                                                 });\n    }\n\n    private static final String TRIGGER_DDL_SQL = \"SELECT DBMS_METADATA.GET_DDL('TRIGGER', '%s', '%s') as ddl FROM DUAL\";\n\n    @Override\n    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           String triggerName) {\n        String sql = String.format(TRIGGER_DDL_SQL, triggerName, schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Trigger trigger = new Trigger();\n            trigger.setDatabaseName(databaseName);\n            trigger.setSchemaName(schemaName);\n            trigger.setTriggerName(triggerName);\n            while (resultSet.next()) {\n                trigger.setTriggerBody(resultSet.getString(\"ddl\"));\n            }\n            return trigger;\n        });\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n                               String procedureName) {\n        String sql = String.format(ROUTINES_SQL, \"PROCEDURE\", schemaName, procedureName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Procedure procedure = new Procedure();\n            procedure.setDatabaseName(databaseName);\n            procedure.setSchemaName(schemaName);\n            procedure.setProcedureName(procedureName);\n            StringBuilder bodyBuilder = new StringBuilder(\"CREATE OR REPLACE \");\n            while (resultSet.next()) {\n                bodyBuilder.append(resultSet.getString(\"TEXT\")).append(\"\\n\");\n            }\n            String procedureBody = bodyBuilder.toString().trim(); // 去掉最后的空白字符\n            if (!procedureBody.endsWith(\"/\")) {\n                procedureBody += \"\\n/\";\n            }\n            procedure.setProcedureBody(procedureBody);\n            return procedure;\n        });\n    }\n\n\n    private static String VIEW_DDL_SQL = \"SELECT VIEW_NAME, TEXT FROM ALL_VIEWS WHERE OWNER = '%s' AND VIEW_NAME = '%s'\";\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_DDL_SQL, schemaName, viewName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Table table = new Table();\n            table.setDatabaseName(databaseName);\n            table.setSchemaName(schemaName);\n            table.setName(viewName);\n            if (resultSet.next()) {\n                table.setDdl(\"CREATE OR REPLACE VIEW \" + viewName + \" AS \" + resultSet.getString(\"TEXT\"));\n            }\n            return table;\n        });\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new OracleSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(OracleColumnTypeEnum.getTypes())\n                .charsets(Lists.newArrayList())\n                .collations(Lists.newArrayList())\n                .indexTypes(OracleIndexTypeEnum.getIndexTypes())\n                .defaultValues(OracleDefaultValueEnum.getDefaultValues())\n                .build();\n    }\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"\\\"\" + name + \"\\\"\").collect(Collectors.joining(\".\"));\n    }\n\n\n    @Override\n    public List<String> getSystemSchemas() {\n        return systemSchemas;\n    }\n\n    /**\n     * @return\n     */\n    @Override\n    public ValueProcessor getValueProcessor() {\n        return new OracleValueProcessor();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OraclePlugin.java",
    "content": "package ai.chat2db.plugin.oracle;\n\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class OraclePlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"oracle.json\", DBConfig.class);\n\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new OracleMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new OracleDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/builder/OracleSqlBuilder.java",
    "content": "package ai.chat2db.plugin.oracle.builder;\n\nimport ai.chat2db.plugin.oracle.type.OracleColumnTypeEnum;\nimport ai.chat2db.plugin.oracle.type.OracleIndexTypeEnum;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.util.SqlUtils;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class OracleSqlBuilder extends DefaultSqlBuilder {\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"CREATE TABLE \").append(\"\\\"\").append(table.getSchemaName()).append(\"\\\".\\\"\").append(table.getName()).append(\"\\\" (\").append(\"\\n\");\n\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            OracleColumnTypeEnum typeEnum = OracleColumnTypeEnum.getByType(column.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n);\");\n\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {\n                continue;\n            }\n            OracleIndexTypeEnum oracleColumnTypeEnum = OracleIndexTypeEnum.getByType(tableIndex.getType());\n            if(oracleColumnTypeEnum == null){\n                continue;\n            }\n            script.append(\"\\n\").append(\"\").append(oracleColumnTypeEnum.buildIndexScript(tableIndex)).append(\";\");\n        }\n\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType()) || StringUtils.isBlank(column.getComment())) {\n                continue;\n            }\n            script.append(\"\\n\").append(buildComment(column)).append(\";\");\n        }\n\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\"\\n\").append(buildTableComment(table)).append(\";\");\n        }\n\n\n        return script.toString();\n    }\n\n    private String buildTableComment(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"COMMENT ON TABLE \").append(\"\\\"\").append(table.getSchemaName()).append(\"\\\".\\\"\").append(table.getName()).append(\"\\\" IS '\").append(table.getComment()).append(\"'\");\n        return script.toString();\n    }\n\n    private String buildComment(TableColumn column) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"COMMENT ON COLUMN \").append(\"\\\"\").append(column.getSchemaName()).append(\"\\\".\\\"\").append(column.getTableName()).append(\"\\\".\\\"\").append(column.getName()).append(\"\\\" IS '\").append(column.getComment()).append(\"'\");\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(oldTable.getSchemaName()).append(\"\\\".\\\"\").append(oldTable.getName()).append(\"\\\"\");\n            script.append(\" \").append(\"RENAME TO \").append(\"\\\"\").append(newTable.getName()).append(\"\\\"\").append(\";\\n\");\n        }\n        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {\n            script.append(\"\").append(buildTableComment(newTable)).append(\";\\n\");\n        }\n\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if (StringUtils.isNotBlank(tableColumn.getEditStatus())) {\n                OracleColumnTypeEnum typeEnum = OracleColumnTypeEnum.getByType(tableColumn.getColumnType());\n                if(typeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\";\\n\");\n                if (StringUtils.isNotBlank(tableColumn.getComment())) {\n                    script.append(\"\\n\").append(buildComment(tableColumn)).append(\";\\n\");\n                }\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                OracleIndexTypeEnum oracleIndexTypeEnum = OracleIndexTypeEnum.getByType(tableIndex.getType());\n                if(oracleIndexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(oracleIndexTypeEnum.buildModifyIndex(tableIndex)).append(\";\\n\");\n            }\n        }\n        if (script.length() > 2) {\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            script.append(\";\");\n        }\n\n        return script.toString();\n    }\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        int startRow = offset;\n        int endRow = offset + pageSize;\n        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120);\n        if (startRow > 0) {\n            sqlBuilder.append(\"SELECT * FROM ( \");\n        }\n        if (endRow > 0) {\n            sqlBuilder.append(\" SELECT TMP_PAGE.*, ROWNUM CAHT2DB_AUTO_ROW_ID FROM ( \");\n        }\n        sqlBuilder.append(\"\\n\");\n        sqlBuilder.append(sql);\n        sqlBuilder.append(\"\\n\");\n        if (endRow > 0) {\n            sqlBuilder.append(\" ) TMP_PAGE WHERE ROWNUM <= \");\n            sqlBuilder.append(endRow);\n        }\n        if (startRow > 0) {\n            sqlBuilder.append(\" ) WHERE CAHT2DB_AUTO_ROW_ID > \");\n            sqlBuilder.append(startRow);\n        }\n        return sqlBuilder.toString();\n    }\n\n//    @Override\n//    public String buildCreateSchemaSql(Schema schema){\n//        StringBuilder sqlBuilder = new StringBuilder();\n//        sqlBuilder.append(\"CREATE SCHEMA \\\"\"+schema.getName()+\"\\\"\");\n//        if(StringUtils.isNotBlank(schema.getOwner())){\n//            sqlBuilder.append(\" AUTHORIZATION \").append(schema.getOwner());\n//        }\n//        if(StringUtils.isNotBlank(schema.getComment())){\n//            sqlBuilder.append(\"; COMMENT ON SCHEMA \\\"\").append(schema.getName()).append(\"\\\" IS '\").append(schema.getComment()).append(\"';\");\n//        }\n//        return sqlBuilder.toString();\n//    }\n\n\n    @Override\n    protected void buildTableName(String databaseName, String schemaName, String tableName, StringBuilder script) {\n        if (StringUtils.isNotBlank(databaseName)) {\n            script.append(SqlUtils.quoteObjectName(databaseName)).append('.');\n        }\n        script.append(SqlUtils.quoteObjectName(tableName));\n    }\n\n    /**\n     * @param columnList\n     * @param script\n     */\n    @Override\n    protected void buildColumns(List<String> columnList, StringBuilder script) {\n        if (CollectionUtils.isNotEmpty(columnList)) {\n            script.append(\" (\")\n                    .append(columnList.stream().map(SqlUtils::quoteObjectName).collect(Collectors.joining(\",\")))\n                    .append(\") \");\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/oracle.json",
    "content": "{\n  \"dbType\": \"ORACLE\",\n  \"supportDatabase\": false,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:oracle:thin:@localhost:1521:XE\",\n      \"custom\": false,\n      \"defaultDriver\": true,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/ojdbc11-21.5.0.0.jar\",\n        \"https://cdn.chat2db-ai.com/lib/orai18n-21.5.0.0.jar\",\n        \"https://cdn.chat2db-ai.com/lib/xmlparserv2-21.5.0.0.jar\",\n        \"https://cdn.chat2db-ai.com/lib/xdb-21.5.0.0.jar\"\n      ],\n      \"jdbcDriver\": \"ojdbc11-21.5.0.0.jar,orai18n-21.5.0.0.jar,xmlparserv2-21.5.0.0.jar,xdb-21.5.0.0.jar\",\n      \"jdbcDriverClass\": \"oracle.jdbc.driver.OracleDriver\"\n    }\n  ],\n  \"name\": \"Oracle\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.oracle.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum OracleColumnTypeEnum implements ColumnBuilder {\n    //JSON(\"JSON\", false, false, true, false, false, false, true, false, false, false)\n\n    BFILE(\"BFILE\", false, false, true, false, false, false, true, true, false, false),\n\n    BINARY_DOUBLE(\"BINARY_DOUBLE\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BINARY_FLOAT(\"BINARY_FLOAT\", false, false, true, false, false, false, true, true, false, false),\n\n\n    BLOB(\"BLOB\", false, false, true, false, false, false, true, true, false, false),\n\n\n    CHAR(\"CHAR\", true, false, true, false, false, false, true, true, false, true),\n\n    CHAR_VARYING(\"CHAR VARYING\", true, false, true, false, false, false, true, true, false, true),\n\n    CHARACTER(\"CHARACTER\", true, false, true, false, false, false, true, true, false, true),\n\n    CHARACTER_VARYING(\"CHARACTER VARYING\", true, false, true, false, false, false, true, true, false, true),\n\n    CLOB(\"CLOB\", false, false, true, false, false, false, true, true, false, false),\n\n    DATE(\"DATE\", false, false, true, false, false, false, true, true, false, false),\n\n    DECIMAL(\"DECIMAL\", true, true, true, false, false, false, true, true, false, false),\n\n    DOUBLE_PRECISION(\"DOUBLE PRECISION\", false, false, true, false, false, false, true, true, false, false),\n\n\n    FLOAT(\"FLOAT\", true, false, true, false, false, false, true, true, false, false),\n\n    INT(\"INT\", false, false, true, false, false, false, true, true, false, false),\n\n    INTEGER(\"INTEGER\", false, false, true, false, false, false, true, true, false, false),\n\n    LONG(\"LONG\", false, false, true, false, false, false, true, true, false, false),\n\n    LONG_RAW(\"LONG RAW\", false, false, true, false, false, false, true, true, false, false),\n\n\n    LONG_VARCHAR(\"LONG VARCHAR\", false, false, true, false, false, false, true, true, false, false),\n\n    NATIONAL_CHAR(\"NATIONAL CHAR\", true, false, true, false, false, false, true, true, false, true),\n\n\n    NATIONAL_CHAR_VARYING(\"NATIONAL CHAR VARYING\", true, false, true, false, false, false, true, true, false, true),\n\n\n    NATIONAL_CHARACTER(\"NATIONAL CHARACTER\", true, false, true, false, false, false, true, true, false, true),\n\n\n    NATIONAL_CHARACTER_VARYING(\"NATIONAL CHARACTER VARYING\", true, false, true, false, false, false, true, true, false, true),\n\n    NCHAR(\"NCHAR\", true, false, true, false, false, false, true, true, false, false),\n\n    NCHAR_VARYING(\"NCHAR VARYING\", true, false, true, false, false, false, true, true, false, false),\n\n    NCLOB(\"NCLOB\", false, false, true, false, false, false, true, true, false, false),\n\n    NUMBER(\"NUMBER\", true, true, true, false, false, false, true, true, false, false),\n\n\n    NVARCHAR2(\"NVARCHAR2\", true, false, true, false, false, false, true, true, false, true),\n\n    RAW(\"RAW\", true, false, true, false, false, false, true, true, false, false),\n\n    REAL(\"REAL\", false, false, true, false, false, false, true, true, false, false),\n\n    ROWID(\"ROWID\", false, false, true, false, false, false, true, true, false, false),\n\n\n    SMALLINT(\"SMALLINT\", false, false, true, false, false, false, true, true, false, false),\n\n    TIMESTAMP(\"TIMESTAMP\", false, true, true, false, false, false, true, true, false, false),\n\n    TIMESTAMP_WITH_LOCAL_TIME_ZONE(\"TIMESTAMP WITH LOCAL TIME ZONE\", false, true, true, false, false, false, true, true, false, false),\n\n\n    TIMESTAMP_WITH_TIME_ZONE(\"TIMESTAMP WITH TIME ZONE\", false, true, true, false, false, false, true, true, false, false),\n\n\n    INTERVAL_YEAR_TO_MONTH(\"INTERVAL YEAR TO MONTH\", true, false, true, false, false, false, true, true, false, false),\n\n    INTERVAL_DAY_TO_SECOND(\"INTERVAL DAY TO SECOND\", true, true, true, false, false, false, true, true, false, false),\n\n    UROWID(\"UROWID\", true, false, true, false, false, false, true, true, false, false),\n\n    VARCHAR(\"VARCHAR\", true, false, true, false, false, false, true, true, false, true),\n\n    VARCHAR2(\"VARCHAR2\", true, false, true, false, false, false, true, true, false, true),\n\n    XMLTYPE(\"XMLTYPE\", false, false, true, false, false, false, true, true, false, false),\n\n    ;\n    private ColumnType columnType;\n\n    public static OracleColumnTypeEnum getByType(String dataType) {\n        return COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n    }\n\n    private static Map<String, OracleColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (OracleColumnTypeEnum value : OracleColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n\n    OracleColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportUnit) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, false, supportUnit);\n    }\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        OracleColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"\\\"\").append(column.getName()).append(\"\\\"\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        script.append(buildNullable(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n\n    private String buildNullable(TableColumn column, OracleColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportNullable()) {\n            return \"\";\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"NULL\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDefaultValue(TableColumn column, OracleColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {\n            return \"\";\n        }\n\n        if (\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if (\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildDataType(TableColumn column, OracleColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(CHAR, CHAR_VARYING, CHARACTER, CHARACTER_VARYING,\n                NVARCHAR2, VARCHAR, VARCHAR2, NATIONAL_CHAR,\n                NATIONAL_CHAR_VARYING, NATIONAL_CHARACTER,\n                NATIONAL_CHARACTER_VARYING, NCHAR, NCHAR_VARYING).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            script.append(columnType);\n            if (column.getColumnSize() != null && StringUtils.isEmpty(column.getUnit())) {\n                script.append(\"(\").append(column.getColumnSize()).append(\")\");\n            } else if (column.getColumnSize() != null && !StringUtils.isEmpty(column.getUnit())) {\n                script.append(\"(\").append(column.getColumnSize()).append(\" \").append(column.getUnit()).append(\")\");\n            }\n            return script.toString();\n        }\n\n        if (Arrays.asList(DECIMAL, FLOAT, NUMBER, UROWID, RAW).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            script.append(columnType);\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\")\");\n            } else if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\",\").append(column.getDecimalDigits()).append(\")\");\n            }\n            return script.toString();\n        }\n        if (Arrays.asList(TIMESTAMP).contains(type)) {\n            int decimalDigits = column.getDecimalDigits() != null ? column.getDecimalDigits() : 6;\n            String valueTemplate = \"TIMESTAMP(%s)\";\n            return String.format(valueTemplate, decimalDigits);\n        }\n        if (Arrays.asList(TIMESTAMP_WITH_TIME_ZONE).contains(type)) {\n            int decimalDigits = column.getDecimalDigits() != null ? column.getDecimalDigits() : 6;\n            String valueTemplate = \"TIMESTAMP(%s) WITH TIME ZONE\";\n            return String.format(valueTemplate, decimalDigits);\n        }\n        if (Arrays.asList(TIMESTAMP_WITH_LOCAL_TIME_ZONE).contains(type)) {\n            int decimalDigits = column.getDecimalDigits() != null ? column.getDecimalDigits() : 6;\n            String valueTemplate = \"TIMESTAMP(%s) WITH LOCAL TIME ZONE\";\n            return String.format(valueTemplate, decimalDigits);\n        }\n        if (Arrays.asList(INTERVAL_DAY_TO_SECOND).contains(type)) {\n            int columnSize = column.getColumnSize() != null ? column.getColumnSize() : 2;\n            int decimalDigits = column.getDecimalDigits() != null ? column.getDecimalDigits() : 6;\n            String valueTemplate = \"INTERVAL DAY(%s) TO SECOND(%s)\";\n            return String.format(valueTemplate, columnSize, decimalDigits);\n        }\n        if (Arrays.asList(INTERVAL_YEAR_TO_MONTH).contains(type)) {\n            int columnSize = column.getColumnSize() != null ? column.getColumnSize() : 2;\n            String valueTemplate = \"INTERVAL YEAR(%s) TO MONTH\";\n            return String.format(valueTemplate, columnSize);\n        }\n\n\n        return columnType;\n    }\n\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"DROP COLUMN \").append(\"\\\"\").append(tableColumn.getName()).append(\"\\\"\");\n            return script.toString();\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"ADD (\").append(buildCreateColumnSql(tableColumn)).append(\")\");\n            return script.toString();\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n            script.append(\" \").append(\"MODIFY (\").append(buildModifyColumnSql(tableColumn, tableColumn.getOldColumn())).append(\") \\n\");\n\n            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n                script.append(\";\");\n                script.append(\"ALTER TABLE \").append(\"\\\"\").append(tableColumn.getSchemaName()).append(\"\\\".\\\"\").append(tableColumn.getTableName()).append(\"\\\"\");\n                script.append(\" \").append(\"RENAME COLUMN \").append(\"\\\"\").append(tableColumn.getOldName()).append(\"\\\"\").append(\" TO \").append(\"\\\"\").append(tableColumn.getName()).append(\"\\\"\");\n\n            }\n            return script.toString();\n\n        }\n        return \"\";\n    }\n\n    public String buildModifyColumnSql(TableColumn column, TableColumn oldColumn) {\n        OracleColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"\\\"\").append(column.getName()).append(\"\\\"\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        if (oldColumn.getNullable() != column.getNullable()) {\n            script.append(buildNullable(column, type)).append(\" \");\n        }\n\n        return script.toString();\n    }\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(OracleColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleDefaultValueEnum.java",
    "content": "package ai.chat2db.plugin.oracle.type;\n\nimport ai.chat2db.spi.model.DefaultValue;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum OracleDefaultValueEnum {\n\n    EMPTY_STRING(\"EMPTY_STRING\"),\n    NULL(\"NULL\"),\n    ;\n    private DefaultValue defaultValue;\n\n    OracleDefaultValueEnum(String defaultValue) {\n        this.defaultValue = new DefaultValue(defaultValue);\n    }\n\n\n    public DefaultValue getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static List<DefaultValue> getDefaultValues() {\n        return Arrays.stream(OracleDefaultValueEnum.values()).map(OracleDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.oracle.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum OracleIndexTypeEnum {\n\n    PRIMARY_KEY(\"Primary\", \"PRIMARY KEY\"),\n\n    NORMAL(\"Normal\", \"INDEX\"),\n\n    UNIQUE(\"Unique\", \"UNIQUE INDEX\"),\n\n    BITMAP(\"BITMAP\", \"BITMAP INDEX\");\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    private IndexType indexType;\n\n\n    public String getName() {\n        return name;\n    }\n\n    private String name;\n\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    private String keyword;\n\n    OracleIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n\n    public static OracleIndexTypeEnum getByType(String type) {\n        for (OracleIndexTypeEnum value : OracleIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        if (PRIMARY_KEY.equals(this)) {\n            script.append(\"ALTER TABLE \\\"\").append(tableIndex.getSchemaName()).append(\"\\\".\\\"\").append(tableIndex.getTableName()).append(\"\\\" ADD PRIMARY KEY \").append(buildIndexColumn(tableIndex));\n        } else {\n            if (UNIQUE.equals(this)) {\n                script.append(\"CREATE UNIQUE INDEX \");\n            } else {\n                script.append(\"CREATE INDEX \");\n            }\n            script.append(buildIndexName(tableIndex)).append(\" ON \\\"\").append(tableIndex.getSchemaName()).append(\"\\\".\\\"\").append(tableIndex.getTableName()).append(\"\\\" \").append(buildIndexColumn(tableIndex));\n        }\n        return script.toString();\n    }\n\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"\\\"\").append(column.getColumnName()).append(\"\\\"\");\n                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {\n                    script.append(\" \").append(column.getAscOrDesc());\n                }\n                script.append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        return \"\\\"\" + tableIndex.getSchemaName() + \"\\\".\" + \"\\\"\" + tableIndex.getName() + \"\\\"\";\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex), \";\\n\", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (OracleIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {\n            String tableName = \"\\\"\" + tableIndex.getSchemaName() + \"\\\".\" + \"\\\"\" + tableIndex.getTableName() + \"\\\"\";\n            return StringUtils.join(\"ALTER TABLE \",tableName,\" DROP PRIMARY KEY\");\n        }\n        StringBuilder script = new StringBuilder();\n        script.append(\"DROP INDEX \");\n        script.append(buildIndexName(tableIndex));\n\n        return script.toString();\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(OracleIndexTypeEnum.values()).stream().map(OracleIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/OracleValueProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value;\n\nimport ai.chat2db.plugin.oracle.type.OracleColumnTypeEnum;\nimport ai.chat2db.plugin.oracle.value.factory.OracleValueProcessorFactory;\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Objects;\n\n/**\n * @author: zgq\n * @date: 2024年06月03日 22:30\n */\npublic class OracleValueProcessor extends DefaultValueProcessor {\n\n\n    private static final Logger log = LoggerFactory.getLogger(OracleValueProcessor.class);\n\n    @Override\n    public String getJdbcValue(JDBCDataValue dataValue) {\n        if (OracleColumnTypeEnum.LONG_RAW.getColumnType().getTypeName().equalsIgnoreCase(dataValue.getType())) {\n            return convertJDBCValueByType(dataValue);\n        }\n        Object value = dataValue.getObject();\n        if (Objects.isNull(value)) {\n            return null;\n        }\n        if (value instanceof String emptyStr) {\n            if (StringUtils.isBlank(emptyStr)) {\n                return emptyStr;\n            }\n        }\n        return convertJDBCValueByType(dataValue);\n    }\n\n\n    @Override\n    public String getJdbcSqlValueString(JDBCDataValue dataValue) {\n        if (OracleColumnTypeEnum.LONG_RAW.getColumnType().getTypeName().equalsIgnoreCase(dataValue.getType())) {\n            return convertJDBCValueStrByType(dataValue);\n        }\n        Object value = dataValue.getObject();\n        if (Objects.isNull(value)) {\n            return \"NULL\";\n        }\n        if (value instanceof String stringValue) {\n            if (StringUtils.isBlank(stringValue)) {\n                return EasyStringUtils.quoteString(stringValue);\n            }\n        }\n        return convertJDBCValueStrByType(dataValue);\n    }\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        try {\n            DefaultValueProcessor valueProcessor = OracleValueProcessorFactory.getValueProcessor(dataValue.getDateTypeName());\n            if (Objects.nonNull(valueProcessor)) {\n                return valueProcessor.convertSQLValueByType(dataValue);\n            }\n        } catch (Exception e) {\n            log.warn(\"convertSQLValueByType error\", e);\n            return super.convertSQLValueByType(dataValue);\n        }\n        return super.convertSQLValueByType(dataValue);\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        String type = dataValue.getType();\n        try {\n            DefaultValueProcessor valueProcessor = OracleValueProcessorFactory.getValueProcessor(type);\n            if (Objects.nonNull(valueProcessor)) {\n                return valueProcessor.convertJDBCValueByType(dataValue);\n            }\n        } catch (Exception e) {\n            log.warn(\"convertJDBCValueByType error\", e);\n            return super.convertJDBCValueByType(dataValue);\n        }\n        return super.convertJDBCValueByType(dataValue);\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        String type = dataValue.getType();\n        try {\n            DefaultValueProcessor valueProcessor = OracleValueProcessorFactory.getValueProcessor(type);\n            if (Objects.nonNull(valueProcessor)) {\n                return valueProcessor.convertJDBCValueStrByType(dataValue);\n            }\n        } catch (Exception e) {\n            log.warn(\"convertJDBCValueStrByType error\", e);\n            return super.convertJDBCValueStrByType(dataValue);\n        }\n        return super.convertJDBCValueStrByType(dataValue);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/factory/OracleValueProcessorFactory.java",
    "content": "package ai.chat2db.plugin.oracle.value.factory;\n\nimport ai.chat2db.plugin.oracle.type.OracleColumnTypeEnum;\nimport ai.chat2db.plugin.oracle.value.sub.*;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\n\nimport java.util.Map;\n\n/**\n * @author: zgq\n * @date: 2024年06月03日 23:21\n */  // TODO: 1.空间数据类型  2.动态类型数据\npublic class OracleValueProcessorFactory {\n\n    private static final Map<String, DefaultValueProcessor> PROCESSOR_MAP;\n\n    static {\n        OracleClobProcessor oracleClobProcessor = new OracleClobProcessor();\n        OracleTimeStampProcessor oracleTimeStampProcessor = new OracleTimeStampProcessor();\n        OracleBlobProcessor oracleBlobProcessor = new OracleBlobProcessor();\n        OracleRawValueProcessor oracleRawValueProcessor = new OracleRawValueProcessor();\n        PROCESSOR_MAP = Map.ofEntries(\n                //clob\n                Map.entry(OracleColumnTypeEnum.CLOB.name(), oracleClobProcessor),\n                Map.entry(OracleColumnTypeEnum.NCLOB.name(), oracleClobProcessor),\n                Map.entry(OracleColumnTypeEnum.LONG.name(), oracleClobProcessor),\n                //date\n                Map.entry(OracleColumnTypeEnum.DATE.name(), new OracleDateProcessor()),\n                //timestamp\n                Map.entry(OracleColumnTypeEnum.TIMESTAMP.name(), oracleTimeStampProcessor),\n                Map.entry(OracleColumnTypeEnum.TIMESTAMP_WITH_LOCAL_TIME_ZONE.getColumnType().getTypeName(), new OracleTimeStampLTZProcessor()),\n                Map.entry(OracleColumnTypeEnum.TIMESTAMP_WITH_TIME_ZONE.getColumnType().getTypeName(), new OracleTimeStampTZProcessor()),\n                //INTERVAL\n                Map.entry(\"INTERVALDS\", new OracleIntervalDSProcessor()),\n                Map.entry(\"INTERVALYM\", new OracleIntervalYMProcessor()),\n                //number\n                Map.entry(OracleColumnTypeEnum.NUMBER.name(), new OracleNumberProcessor()),\n                //blob\n                Map.entry(OracleColumnTypeEnum.BLOB.name(), oracleBlobProcessor),\n                //raw\n                Map.entry(OracleColumnTypeEnum.RAW.name(), oracleRawValueProcessor),\n                //long raw\n                Map.entry(OracleColumnTypeEnum.LONG_RAW.getColumnType().getTypeName(), new OracleLongRawProcessor()),\n                //xml\n                Map.entry(\"SYS.XMLTYPE\", new OracleXmlValueProcessor()),\n                Map.entry(\"SYS.ANYDATA\", new OracleAnyDataProcessor())\n        );\n\n    }\n\n    public static DefaultValueProcessor getValueProcessor(String type) {\n        return PROCESSOR_MAP.get(type);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleAnyDataProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年07月07日 10:42\n */\npublic class OracleAnyDataProcessor extends DefaultValueProcessor {\n    /**\n     * @param dataValue\n     * @return\n     */\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return dataValue.getValue();\n    }\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n//        byte[] bytes = dataValue.getBytes();\n//        int length = bytes.length;\n//        String rawString = new String(bytes);\n//\n//        // Filter printable characters\n//        StringBuilder printableString = new StringBuilder();\n//        for (char c : rawString.toCharArray()) {\n//            if (c >= 32 && c <= 126) { // ASCII printable characters range\n//                printableString.append(c);\n//            }\n//        }\n\n        return \"SYS.ANYDATA\";\n    }\n\n\n    /**\n     * @param dataValue\n     * @return\n     */\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return \"SYS.ANYDATA\";\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleBlobProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * @author: zgq\n * @date: 2024年06月05日 20:06\n */\n@Slf4j\npublic class OracleBlobProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        String value = dataValue.getValue();\n        if (value.startsWith(\"0x\")) {\n            // 0xabcd\n            return EasyStringUtils.quoteString(value.substring(2));\n        } else {\n            //example: hello,world\n            for (int i = 0; i < value.length(); i++) {\n                char c = value.charAt(i);\n                boolean isDigit = (c >= '0' && c <= '9');\n                boolean isUpperCaseHex = (c >= 'A' && c <= 'F');\n                boolean isLowerCaseHex = (c >= 'a' && c <= 'f');\n                if (!isDigit && !isUpperCaseHex && !isLowerCaseHex) {\n                    return EasyStringUtils.quoteString(dataValue.getBlobHexString());\n                }\n            }\n            // example: abcd1234\n            return EasyStringUtils.quoteString(value);\n        }\n    }\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getBlobString();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return EasyStringUtils.quoteString(dataValue.getBlobHexString());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleClobProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 17:06\n */\npublic class OracleClobProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return EasyStringUtils.escapeAndQuoteString(dataValue.getValue());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getClobString();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return EasyStringUtils.escapeAndQuoteString(dataValue.getClobString());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleDateProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.plugin.oracle.value.template.OracleDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 16:33\n */\npublic class OracleDateProcessor extends DefaultValueProcessor {\n\n    /**\n     * @param dataValue\n     * @return\n     */\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return OracleDmlValueTemplate.wrapDate(dataValue.getValue());\n    }\n\n    /**\n     * @param dataValue\n     * @return\n     */\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getStringValue();\n\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return OracleDmlValueTemplate.wrapDate(dataValue.getStringValue());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleIntervalDSProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.plugin.oracle.value.template.OracleDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月05日 18:56\n */\npublic class OracleIntervalDSProcessor extends DefaultValueProcessor {\n\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return OracleDmlValueTemplate.wrapIntervalDayToSecond(dataValue.getValue(), dataValue.getPrecision(), dataValue.getScale());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getStringValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return OracleDmlValueTemplate.wrapIntervalDayToSecond(convertJDBCValueByType(dataValue), dataValue.getPrecision(), dataValue.getScale());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleIntervalYMProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.plugin.oracle.value.template.OracleDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * 功能描述\n *\n * @author: zgq\n * @date: 2024年06月05日 18:58\n */\npublic class OracleIntervalYMProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return OracleDmlValueTemplate.wrapIntervalYearToMonth(dataValue.getValue(), dataValue.getPrecision());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getStringValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return OracleDmlValueTemplate.wrapIntervalYearToMonth(dataValue.getStringValue(), dataValue.getPrecision());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleLongRawProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\nimport java.util.Objects;\n\n/**\n * @author: zgq\n * @date: 2024年07月07日 16:58\n */\npublic class OracleLongRawProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        String value = dataValue.getValue();\n        if (value.startsWith(\"0x\")) {\n            // 0xabcd\n            return EasyStringUtils.quoteString(value.substring(2));\n        } else {\n            //example: hello,world\n            // TODO: Need to optimize recognition of hexadecimal strings\n            for (int i = 0; i < value.length(); i++) {\n                char c = value.charAt(i);\n                boolean isDigit = (c >= '0' && c <= '9');\n                boolean isUpperCaseHex = (c >= 'A' && c <= 'F');\n                boolean isLowerCaseHex = (c >= 'a' && c <= 'f');\n                if (!isDigit && !isUpperCaseHex && !isLowerCaseHex) {\n                    return EasyStringUtils.quoteString(dataValue.getBlobHexString());\n                }\n            }\n            // example: abcd1234\n            return EasyStringUtils.quoteString(value);\n        }\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getBinaryDataString();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        String blobHexString = dataValue.getBlobHexString();\n        if (Objects.isNull(blobHexString)) {\n            return \"NULL\";\n        }\n        return EasyStringUtils.quoteString(blobHexString);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleNumberProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * 功能描述\n *\n * @author: zgq\n * @date: 2024年06月05日 20:00\n */\npublic class OracleNumberProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return dataValue.getValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getBigDecimalString();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return dataValue.getBigDecimalString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleRawValueProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月28日 下午1:59\n */\npublic class OracleRawValueProcessor extends DefaultValueProcessor {\n\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        String value = dataValue.getValue();\n        if (value.startsWith(\"0x\")) {\n            // 0xabcd\n            return EasyStringUtils.quoteString(value.substring(2));\n        } else {\n            //example: hello,world\n            for (int i = 0; i < value.length(); i++) {\n                char c = value.charAt(i);\n                boolean isDigit = (c >= '0' && c <= '9');\n                boolean isUpperCaseHex = (c >= 'A' && c <= 'F');\n                boolean isLowerCaseHex = (c >= 'a' && c <= 'f');\n                if (!isDigit && !isUpperCaseHex && !isLowerCaseHex) {\n                    return EasyStringUtils.quoteString(dataValue.getBlobHexString());\n                }\n            }\n            // example: abcd1234\n            return EasyStringUtils.quoteString(value);\n        }\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getBinaryDataString();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return EasyStringUtils.quoteString(dataValue.getBlobHexString());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleTimeStampLTZProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.plugin.oracle.value.template.OracleDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\nimport java.sql.Timestamp;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\n\n/**\n * @author: zgq\n * @date: 2024年07月05日 下午4:19\n */\npublic class OracleTimeStampLTZProcessor extends DefaultValueProcessor {\n\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return wrap(dataValue.getValue(), dataValue.getScale());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        Timestamp timestamp = dataValue.getTimestamp();\n        int scale = dataValue.getScale();\n        LocalDateTime localDateTime = timestamp.toLocalDateTime();\n        StringBuilder templateBuilder = new StringBuilder(\"yyyy-MM-dd HH:mm:ss\");\n        if (scale != 0) {\n            templateBuilder.append(\".\");\n            templateBuilder.append(\"S\".repeat(scale));\n        }\n        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(templateBuilder.toString());\n        return localDateTime.format(formatter);\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        Timestamp timestamp = dataValue.getTimestamp();\n        int scale = dataValue.getScale();\n        // 将 Timestamp 转换为 Instant 对象\n        Instant instant = timestamp.toInstant();\n        // 将 Instant 转换为 UTC 时区的 ZonedDateTime\n        ZonedDateTime utcZonedDateTime = instant.atZone(ZoneId.of(\"UTC\"));\n        StringBuilder templateBuilder = new StringBuilder(\"yyyy-MM-dd HH:mm:ss\");\n        if (scale != 0) {\n            templateBuilder.append(\".\");\n            templateBuilder.append(\"S\".repeat(scale));\n        }\n        // 定义日期时间格式化器\n        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(templateBuilder.toString());\n        // 格式化 UTC 时区的 ZonedDateTime\n        String formattedUtcTime = utcZonedDateTime.format(formatter);\n        return wrap(formattedUtcTime, scale);\n    }\n\n    private String wrap(String value, int scale) {\n        if (scale == 0) {\n            return OracleDmlValueTemplate.wrapDate(value);\n        }\n        return OracleDmlValueTemplate.wrapTimestamp(value, scale);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleTimeStampProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.plugin.oracle.value.template.OracleDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\nimport java.sql.Timestamp;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\n\n/**\n * @author: zgq\n * @date: 2024年06月05日 16:20\n */\npublic class OracleTimeStampProcessor extends DefaultValueProcessor {\n\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return wrap(dataValue.getValue(), dataValue.getScale());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        Timestamp timestamp = dataValue.getTimestamp();\n        int scale = dataValue.getScale();\n        LocalDateTime localDateTime = timestamp.toLocalDateTime();\n        StringBuilder templateBuilder = new StringBuilder(\"yyyy-MM-dd HH:mm:ss\");\n        if (scale != 0) {\n            templateBuilder.append(\".\");\n            templateBuilder.append(\"S\".repeat(scale));\n        }\n        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(templateBuilder.toString());\n        return localDateTime.format(formatter);\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return wrap(convertJDBCValueByType(dataValue), dataValue.getScale());\n    }\n\n    private String wrap(String value, int scale) {\n        if (scale == 0) {\n            return OracleDmlValueTemplate.wrapDate(value);\n        }\n        return OracleDmlValueTemplate.wrapTimestamp(value, scale);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleTimeStampTZProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.plugin.oracle.value.template.OracleDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * 功能描述\n *\n * @author: zgq\n * @date: 2024年06月05日 17:32\n */\npublic class OracleTimeStampTZProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return wrap(dataValue.getValue(), dataValue.getScale());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        String timeStampString = dataValue.getStringValue();\n        int scale = dataValue.getScale();\n        int lastSpaceIndex = timeStampString.lastIndexOf(\" \");\n        int lastDotIndex = timeStampString.indexOf(\".\");\n        int nanosLength = lastSpaceIndex - lastDotIndex - 1;\n        if (scale == 0) {\n            return timeStampString.substring(0, lastDotIndex) + timeStampString.substring(lastDotIndex + 2);\n        } else if (nanosLength < scale) {\n            // 计算需要补充的零的数量\n            int zerosToAdd = scale - nanosLength;\n            StringBuilder sb = new StringBuilder(timeStampString);\n            for (int i = 0; i < zerosToAdd; i++) {\n                sb.insert(lastSpaceIndex, '0');\n            }\n            return sb.toString();\n\n        }\n        return timeStampString;\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return wrap(convertJDBCValueByType(dataValue), dataValue.getScale());\n    }\n\n    private String wrap(String value, int scale) {\n        if (scale == 0) {\n            return OracleDmlValueTemplate.wrapTimestampTzWithOutNanos(value);\n        }\n        return OracleDmlValueTemplate.wrapTimestampTz(value, scale);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/sub/OracleXmlValueProcessor.java",
    "content": "package ai.chat2db.plugin.oracle.value.sub;\n\nimport ai.chat2db.plugin.oracle.value.template.OracleDmlValueTemplate;\nimport ai.chat2db.spi.jdbc.DefaultValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\n/**\n * @author: zgq\n * @date: 2024年06月21日 12:55\n */\npublic class OracleXmlValueProcessor extends DefaultValueProcessor {\n\n    @Override\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return OracleDmlValueTemplate.wrapXml(dataValue.getValue());\n    }\n\n\n    @Override\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getStringValue();\n    }\n\n\n    @Override\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        return OracleDmlValueTemplate.wrapXml(dataValue.getString());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/value/template/OracleDmlValueTemplate.java",
    "content": "package ai.chat2db.plugin.oracle.value.template;\n\n/**\n * @author: zgq\n * @date: 2024年06月01日 13:35\n */\npublic class OracleDmlValueTemplate {\n\n    public static final String DATE_TEMPLATE = \"TO_DATE('%s', 'SYYYY-MM-DD HH24:MI:SS')\";\n\n    public static final String TIMESTAMP_TEMPLATE = \"TO_TIMESTAMP('%s', 'SYYYY-MM-DD HH24:MI:SS.FF%d')\";\n\n    public static final String TIMESTAMP_TZ_TEMPLATE = \"TO_TIMESTAMP_TZ('%s', 'SYYYY-MM-DD HH24:MI:SS.FF%d TZR')\";\n    public static final String TIMESTAMP_TZ_WITHOUT_NANOS_TEMPLATE = \"TO_TIMESTAMP_TZ('%s', 'SYYYY-MM-DD HH24:MI:SS TZR')\";\n\n    public static final String INTERVAL_YEAR_TO_MONTH_TEMPLATE = \"INTERVAL '%s' YEAR(%d) TO MONTH\";\n    public static final String INTERVAL_DAY_TO_SECOND_TEMPLATE = \"INTERVAL '%s' DAY(%d) TO SECOND(%d)\";\n\n    public static final String XML_TEMPLATE = \"XMLType('%s')\";\n\n\n    public static String wrapDate(String date) {\n        return String.format(DATE_TEMPLATE, date);\n    }\n\n    public static String wrapTimestamp(String timestamp, int scale) {\n        return String.format(TIMESTAMP_TEMPLATE, timestamp, scale);\n    }\n\n    public static String wrapTimestampTz(String timestamp, int scale) {\n        return String.format(TIMESTAMP_TZ_TEMPLATE, timestamp, scale);\n    }\n\n    public static String wrapTimestampTzWithOutNanos(String timestamp) {\n        return String.format(TIMESTAMP_TZ_WITHOUT_NANOS_TEMPLATE, timestamp);\n    }\n\n    public static String wrapIntervalYearToMonth(String year, int precision) {\n        return String.format(INTERVAL_YEAR_TO_MONTH_TEMPLATE, year, precision);\n    }\n\n    public static String wrapIntervalDayToSecond(String day, int precision, int scale) {\n        return String.format(INTERVAL_DAY_TO_SECOND_TEMPLATE, day, precision, scale);\n    }\n\n    public static String wrapXml(String xml) {\n        return String.format(XML_TEMPLATE, xml);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-oracle/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.oracle.OraclePlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>chat2db-postgresql</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/PostgreSQLDBManage.java",
    "content": "package ai.chat2db.plugin.postgresql;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.model.Function;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static ai.chat2db.plugin.postgresql.consts.SQLConst.ENUM_TYPE_DDL_SQL;\n\npublic class PostgreSQLDBManage extends DefaultDBManage implements DBManage {\n\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        exportTypes(connection, asyncContext);\n        exportTables(connection, databaseName, schemaName, asyncContext);\n        exportViews(connection, schemaName, asyncContext);\n        exportFunctions(connection, schemaName, asyncContext);\n        exportTriggers(connection, asyncContext);\n    }\n\n    private void exportTypes(Connection connection, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.createStatement().executeQuery(ENUM_TYPE_DDL_SQL)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"ddl\")).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportTables(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, null,\n                new String[]{\"TABLE\", \"SYSTEM TABLE\", \"PARTITIONED TABLE\"})) {\n            ArrayList<String> tableNames = new ArrayList<>();\n            while (resultSet.next()) {\n                String tableName = resultSet.getString(\"TABLE_NAME\");\n                tableNames.add(tableName);\n            }\n            for (String tableName : tableNames) {\n                exportTable(connection, databaseName, schemaName, tableName, asyncContext);\n            }\n\n        }\n    }\n\n    public void exportTable(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"select pg_get_tabledef('%s','%s',true,'COMMENTS') as ddl;\", schemaName, tableName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"\\n\").append(\"DROP TABLE IF EXISTS \").append(tableName).append(\";\").append(\"\\n\")\n                        .append(resultSet.getString(\"ddl\")).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n                if (asyncContext.isContainsData()) {\n                    exportTableData(connection, databaseName, schemaName, tableName, asyncContext);\n                }\n            }\n        }\n    }\n\n\n    private void exportViews(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n\n        String sql = String.format(\"SELECT table_name, view_definition FROM information_schema.views WHERE table_schema = '%s'\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                String viewName = resultSet.getString(\"table_name\");\n                String viewDefinition = resultSet.getString(\"view_definition\");\n                sqlBuilder.append(\"CREATE OR REPLACE VIEW \").append(viewName).append(\" AS \").append(viewDefinition).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportFunctions(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT proname, pg_get_functiondef(oid) AS function_definition FROM pg_proc \" +\n                \"WHERE pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = '%s')\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                String functionName = resultSet.getString(\"proname\");\n                String functionDefinition = resultSet.getString(\"function_definition\");\n                sqlBuilder.append(\"DROP FUNCTION IF EXISTS \").append(schemaName).append(\".\").append(functionName).append(\";\\n\");\n                sqlBuilder.append(functionDefinition).append(\";\\n\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportTriggers(Connection connection, AsyncContext asyncContext) throws SQLException {\n        String sql = \"SELECT pg_get_triggerdef(oid) AS trigger_definition FROM pg_trigger\";\n        try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"trigger_definition\")).append(\";\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        try {\n            ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n            if (!StringUtils.isEmpty(connectInfo.getSchemaName())) {\n                SQLExecutor.getInstance().execute(connection, \"SET search_path TO \\\"\" + connectInfo.getSchemaName() + \"\\\"\");\n            }\n        } catch (Exception e) {\n\n        }\n    }\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            boolean isCreateOrReplace = procedureBody.trim().toUpperCase().startsWith(\"CREATE OR REPLACE \");\n            String parameterSignature = extractParameterSignature(procedureBody);\n\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, schemaName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String dropSql = \"DROP PROCEDURE IF EXISTS \" + procedureNewName + parameterSignature;\n            SQLExecutor.getInstance().execute(connection, dropSql, resultSet -> {});\n            SQLExecutor.getInstance().execute(connection, procedureBody, resultSet -> {});\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n    }\n\n    @Override\n    public Connection getConnection(ConnectInfo connectInfo) {\n        String url = connectInfo.getUrl();\n        String database = connectInfo.getDatabaseName();\n        if (database != null && !database.isEmpty()) {\n            url = replaceDatabaseInJdbcUrl(url, database);\n        }\n        connectInfo.setUrl(url);\n\n        return super.getConnection(connectInfo);\n    }\n\n\n    public String replaceDatabaseInJdbcUrl(String url, String newDatabase) {\n        // First split the string at the \"?\" character and process the query parameters\n        String[] urlAndParams = url.split(\"\\\\?\");\n        String urlWithoutParams = urlAndParams[0];\n\n        // Split string at \"/\" character in URL\n        String[] parts=new String[4];\n        String[] splitParts = urlWithoutParams.split(\"/\");\n        for (int i = 0; i < splitParts.length; i++) {\n            parts[i] = splitParts[i];\n        }\n\n        // Take the last part, the database name, and replace it with the new database name\n        parts[parts.length - 1] = newDatabase;\n\n        // Reassemble the modified parts into a URL\n        String newUrlWithoutParams = String.join(\"/\", parts);\n\n        // If query parameters exist, add them again\n        String newUrl = urlAndParams.length > 1 ? newUrlWithoutParams + \"?\" + urlAndParams[1] : newUrlWithoutParams;\n\n        return newUrl;\n    }\n\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"DROP TABLE \" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName, boolean copyData) throws SQLException {\n        String sql = \"\";\n        if (copyData) {\n            sql = \"CREATE TABLE \" + newTableName + \" AS TABLE \" + tableName + \" WITH DATA\";\n        } else {\n            sql = \"CREATE TABLE \" + newTableName + \" AS TABLE \" + tableName + \" WITH NO DATA\";\n        }\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void deleteProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) {\n        String procedureBody = procedure.getProcedureBody();\n        String parameterSignature = extractParameterSignature(procedureBody);\n        String procedureNewName = getSchemaOrProcedureName(procedureBody, schemaName, procedure);\n        String sql = \"DROP PROCEDURE \" + procedureNewName + parameterSignature;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> {});\n    }\n\n    @Override\n    public void deleteFunction(Connection connection, String databaseName, String schemaName, Function function) {\n        String functionBody = function.getFunctionBody();\n        String parameterSignature = extractParameterSignature(functionBody);\n        String functionNewName = getSchemaOrFunctionName(functionBody, schemaName, function);\n        String sql = \"DROP FUNCTION\" + functionNewName + parameterSignature;\n        SQLExecutor.getInstance().execute(connection,sql,resultSet -> {});\n    }\n\n    private String extractParameterSignature(String input) {\n        int depth = 0, start = -1;\n        for (int i = 0; i < input.length(); i++) {\n            char c = input.charAt(i);\n            if (c == '(') {\n                if (depth++ == 0) start = i;\n            } else if (c == ')' && --depth == 0 && start != -1) {\n                return \"(\" + input.substring(start + 1, i) + \")\";\n            }\n        }\n        if (depth == 0) {\n            return \"\";\n        }\n        return null;\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n\n    private static String getSchemaOrFunctionName(String functionBody, String schemaName, Function function) {\n        if (functionBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return function.getFunctionName();\n        } else {\n            return schemaName + \".\" + function.getFunctionName();\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/PostgreSQLMetaData.java",
    "content": "package ai.chat2db.plugin.postgresql;\n\nimport ai.chat2db.plugin.postgresql.builder.PostgreSQLSqlBuilder;\nimport ai.chat2db.plugin.postgresql.type.*;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport com.google.common.collect.Lists;\nimport jakarta.validation.constraints.NotEmpty;\nimport lombok.SneakyThrows;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static ai.chat2db.plugin.postgresql.consts.SequenceCommonConst.*;\nimport static ai.chat2db.plugin.postgresql.consts.SQLConst.*;\nimport static ai.chat2db.server.tools.base.constant.SymbolConstant.*;\nimport static ai.chat2db.spi.util.SortUtils.sortDatabase;\n\npublic class PostgreSQLMetaData extends DefaultMetaService implements MetaData {\n\n    private static final String SELECT_KEY_INDEX = \"SELECT ccu.table_schema AS Foreign_schema_name, ccu.table_name AS Foreign_table_name, ccu.column_name AS Foreign_column_name, constraint_type AS Constraint_type, tc.CONSTRAINT_NAME AS Key_name, tc.TABLE_NAME, kcu.Column_name, tc.is_deferrable, tc.initially_deferred FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE tc.TABLE_SCHEMA = '%s'  AND tc.TABLE_NAME = '%s';\";\n\n\n    private List<String> systemDatabases = Arrays.asList(\"postgres\");\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        List<Database> list = SQLExecutor.getInstance().execute(connection, \"SELECT datname FROM pg_database;\", resultSet -> {\n            List<Database> databases = new ArrayList<>();\n            try {\n                while (resultSet.next()) {\n                    String dbName = resultSet.getString(\"datname\");\n                    if (\"template0\".equals(dbName) || \"template1\".equals(dbName)) {\n                        continue;\n                    }\n                    Database database = new Database();\n                    database.setName(dbName);\n                    databases.add(database);\n                }\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            }\n            return databases;\n        });\n        return sortDatabase(list, systemDatabases, connection);\n    }\n\n    private List<String> systemSchemas = Arrays.asList(\"pg_toast\", \"pg_temp_1\", \"pg_toast_temp_1\", \"pg_catalog\", \"information_schema\");\n\n/*    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        List<Schema> schemas = SQLExecutor.getInstance().execute(connection,\n                                                                 \"SELECT catalog_name, schema_name FROM information_schema.schemata;\", resultSet -> {\n                    List<Schema> databases = new ArrayList<>();\n                    while (resultSet.next()) {\n                        Schema schema = new Schema();\n                        String name = resultSet.getString(\"schema_name\");\n                        String catalogName = resultSet.getString(\"catalog_name\");\n                        schema.setName(name);\n                        schema.setDatabaseName(catalogName);\n                        databases.add(schema);\n                    }\n                    return databases;\n                });\n        return SortUtils.sortSchema(schemas, systemSchemas);\n    }*/\n\n\n    private static final String SELECT_TABLE_INDEX = \"SELECT tmp.INDISPRIMARY AS Index_primary, tmp.TABLE_SCHEM, tmp.TABLE_NAME, tmp.NON_UNIQUE, tmp.INDEX_QUALIFIER, tmp.INDEX_NAME AS Key_name, tmp.indisclustered, tmp.ORDINAL_POSITION AS Seq_in_index, TRIM ( BOTH '\\\"' FROM pg_get_indexdef ( tmp.CI_OID, tmp.ORDINAL_POSITION, FALSE ) ) AS Column_name,CASE  tmp.AM_NAME   WHEN 'btree' THEN CASE   tmp.I_INDOPTION [ tmp.ORDINAL_POSITION - 1 ] & 1 :: SMALLINT   WHEN 1 THEN  'D' ELSE'A'  END ELSE NULL  END AS Collation, tmp.CARDINALITY, tmp.PAGES, tmp.FILTER_CONDITION , tmp.AM_NAME AS Index_method, tmp.DESCRIPTION AS Index_comment FROM ( SELECT  n.nspname AS TABLE_SCHEM,  ct.relname AS TABLE_NAME,  NOT i.indisunique AS NON_UNIQUE, NULL AS INDEX_QUALIFIER,  ci.relname AS INDEX_NAME,i.INDISPRIMARY , i.indisclustered ,  ( information_schema._pg_expandarray ( i.indkey ) ).n AS ORDINAL_POSITION,  ci.reltuples AS CARDINALITY,   ci.relpages AS PAGES,  pg_get_expr ( i.indpred, i.indrelid ) AS FILTER_CONDITION,  ci.OID AS CI_OID, i.indoption AS I_INDOPTION,  am.amname AS AM_NAME , d.description  FROM   pg_class ct   JOIN pg_namespace n ON ( ct.relnamespace = n.OID )   JOIN pg_index i ON ( ct.OID = i.indrelid )   JOIN pg_class ci ON ( ci.OID = i.indexrelid )  JOIN pg_am am ON ( ci.relam = am.OID )      left outer join pg_description d on i.indexrelid = d.objoid  WHERE  n.nspname = '%s'   AND ct.relname = '%s'   ) AS tmp ;\";\n    private static String ROUTINES_SQL = \"SELECT p.proname, p.prokind, pg_catalog.pg_get_functiondef(p.oid) as \\\"code\\\" FROM pg_catalog.pg_proc p where p.prokind = '%s' and p.proname='%s'\";\n    private static String TRIGGER_SQL\n            = \"SELECT n.nspname AS \\\"schema\\\", c.relname AS \\\"table_name\\\", t.tgname AS \\\"trigger_name\\\", t.tgenabled AS \"\n            + \"\\\"enabled\\\", pg_get_triggerdef(t.oid) AS \\\"trigger_body\\\" FROM pg_trigger t JOIN pg_class c ON c.oid = t\"\n            + \".tgrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = '%s' AND t.tgname ='%s';\";\n    private static String TRIGGER_SQL_LIST\n            = \"SELECT n.nspname AS \\\"schema\\\", c.relname AS \\\"table_name\\\", t.tgname AS \\\"trigger_name\\\", t.tgenabled AS \"\n            + \"\\\"enabled\\\", pg_get_triggerdef(t.oid) AS \\\"trigger_body\\\" FROM pg_trigger t JOIN pg_class c ON c.oid = t\"\n            + \".tgrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = '%s';\";\n    private static String VIEW_SQL\n            = \"SELECT schemaname, viewname, definition FROM pg_views WHERE schemaname = '%s' AND viewname = '%s';\";\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        List<Trigger> triggers = new ArrayList<>();\n        String sql = String.format(TRIGGER_SQL_LIST, schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Trigger trigger = new Trigger();\n                trigger.setTriggerName(resultSet.getString(\"trigger_name\"));\n                trigger.setSchemaName(schemaName);\n                trigger.setDatabaseName(databaseName);\n                triggers.add(trigger);\n            }\n            return triggers;\n        });\n    }\n\n    @Override\n    public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {\n        SQLExecutor.getInstance().execute(connection, String.format(DROP_TYPE_SQL, schemaName, \"tabledefs\"), resultSet -> null);\n        SQLExecutor.getInstance().execute(connection, TABLE_DEF_FUNCTION_SQL, resultSet -> null);\n        String ddlSql = String.format(\"select * from pg_get_tabledef('%s','%s',false,'COMMENTS') as ddl;\", schemaName, tableName);\n        return SQLExecutor.getInstance().execute(connection, ddlSql, resultSet -> {\n            if (resultSet.next()) {\n                return resultSet.getString(\"ddl\");\n            }\n            return null;\n        });\n    }\n\n\n    @Override\n    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,\n                             String functionName) {\n\n        String sql = String.format(ROUTINES_SQL, \"f\", functionName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Function function = new Function();\n            function.setDatabaseName(databaseName);\n            function.setSchemaName(schemaName);\n            function.setFunctionName(functionName);\n            if (resultSet.next()) {\n                function.setFunctionBody(resultSet.getString(\"code\"));\n            }\n            return function;\n        });\n\n    }\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_SQL, schemaName, viewName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Table table = new Table();\n            table.setDatabaseName(databaseName);\n            table.setSchemaName(schemaName);\n            table.setName(viewName);\n            if (resultSet.next()) {\n                table.setDdl(resultSet.getString(\"definition\"));\n            }\n            return table;\n        });\n    }\n\n    @Override\n    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           String triggerName) {\n\n        String sql = String.format(TRIGGER_SQL, schemaName, triggerName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Trigger trigger = new Trigger();\n            trigger.setDatabaseName(databaseName);\n            trigger.setSchemaName(schemaName);\n            trigger.setTriggerName(triggerName);\n            if (resultSet.next()) {\n                trigger.setTriggerBody(resultSet.getString(\"trigger_body\"));\n            }\n\n            return trigger;\n        });\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n                               String procedureName) {\n        String sql = String.format(ROUTINES_SQL, \"p\", procedureName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Procedure procedure = new Procedure();\n            procedure.setDatabaseName(databaseName);\n            procedure.setSchemaName(schemaName);\n            procedure.setProcedureName(procedureName);\n            if (resultSet.next()) {\n                procedure.setProcedureBody(resultSet.getString(\"code\"));\n            }\n            return procedure;\n        });\n    }\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n\n        String constraintSql = String.format(SELECT_KEY_INDEX, schemaName, tableName);\n        Map<String, String> constraintMap = new HashMap();\n        LinkedHashMap<String, TableIndex> foreignMap = new LinkedHashMap();\n        SQLExecutor.getInstance().execute(connection, constraintSql, resultSet -> {\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"Key_name\");\n                String constraintType = resultSet.getString(\"Constraint_type\");\n                constraintMap.put(keyName, constraintType);\n                if (StringUtils.equalsIgnoreCase(constraintType, PostgreSQLIndexTypeEnum.FOREIGN.getKeyword())) {\n                    TableIndex tableIndex = foreignMap.get(keyName);\n                    String columnName = resultSet.getString(\"Column_name\");\n                    if (tableIndex == null) {\n                        tableIndex = new TableIndex();\n                        tableIndex.setDatabaseName(databaseName);\n                        tableIndex.setSchemaName(schemaName);\n                        tableIndex.setTableName(tableName);\n                        tableIndex.setName(keyName);\n                        tableIndex.setForeignSchemaName(resultSet.getString(\"Foreign_schema_name\"));\n                        tableIndex.setForeignTableName(resultSet.getString(\"Foreign_table_name\"));\n                        tableIndex.setForeignColumnNamelist(Lists.newArrayList(columnName));\n                        tableIndex.setType(PostgreSQLIndexTypeEnum.FOREIGN.getName());\n                        foreignMap.put(keyName, tableIndex);\n                    } else {\n                        tableIndex.getForeignColumnNamelist().add(columnName);\n                    }\n                }\n            }\n            return null;\n        });\n\n        String sql = String.format(SELECT_TABLE_INDEX, schemaName, tableName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            LinkedHashMap<String, TableIndex> map = new LinkedHashMap(foreignMap);\n\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"Key_name\");\n                TableIndex tableIndex = map.get(keyName);\n                if (tableIndex != null) {\n                    List<TableIndexColumn> columnList = tableIndex.getColumnList();\n                    if (columnList == null) {\n                        columnList = new ArrayList<>();\n                        tableIndex.setColumnList(columnList);\n                    }\n                    columnList.add(getTableIndexColumn(resultSet));\n                    columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition))\n                            .collect(Collectors.toList());\n                    tableIndex.setColumnList(columnList);\n                } else {\n                    TableIndex index = new TableIndex();\n                    index.setDatabaseName(databaseName);\n                    index.setSchemaName(schemaName);\n                    index.setTableName(tableName);\n                    index.setName(keyName);\n                    index.setUnique(!StringUtils.equals(\"t\", resultSet.getString(\"NON_UNIQUE\")));\n                    index.setMethod(resultSet.getString(\"Index_method\"));\n                    index.setComment(resultSet.getString(\"Index_comment\"));\n                    List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n                    tableIndexColumns.add(getTableIndexColumn(resultSet));\n                    index.setColumnList(tableIndexColumns);\n                    String constraintType = constraintMap.get(keyName);\n                    if (StringUtils.equals(\"t\", resultSet.getString(\"Index_primary\"))) {\n                        index.setType(PostgreSQLIndexTypeEnum.PRIMARY.getName());\n                    } else if (StringUtils.equalsIgnoreCase(constraintType, PostgreSQLIndexTypeEnum.UNIQUE.getName())) {\n                        index.setType(PostgreSQLIndexTypeEnum.UNIQUE.getName());\n                    } else {\n                        index.setType(PostgreSQLIndexTypeEnum.NORMAL.getName());\n                    }\n                    map.put(keyName, index);\n                }\n            }\n            return map.values().stream().collect(Collectors.toList());\n        });\n\n    }\n\n    @Override\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {\n        List<TableColumn> columnList = super.columns(connection, databaseName, schemaName, tableName);\n\n        EasyCollectionUtils.stream(columnList).forEach(v -> {\n            if (StringUtils.equalsIgnoreCase(v.getColumnType(), \"bpchar\")) {\n                v.setColumnType(PostgreSQLColumnTypeEnum.CHAR.getColumnType().getTypeName().toUpperCase());\n            } else {\n                v.setColumnType(v.getColumnType().toUpperCase());\n            }\n        });\n        return columnList;\n    }\n\n    private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException {\n        TableIndexColumn tableIndexColumn = new TableIndexColumn();\n        tableIndexColumn.setColumnName(resultSet.getString(\"Column_name\"));\n        tableIndexColumn.setOrdinalPosition(resultSet.getShort(\"Seq_in_index\"));\n        tableIndexColumn.setCollation(resultSet.getString(\"Collation\"));\n        tableIndexColumn.setAscOrDesc(resultSet.getString(\"Collation\"));\n        return tableIndexColumn;\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new PostgreSQLSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(PostgreSQLColumnTypeEnum.getTypes())\n                .charsets(PostgreSQLCharsetEnum.getCharsets())\n                .collations(PostgreSQLCollationEnum.getCollations())\n                .indexTypes(PostgreSQLIndexTypeEnum.getIndexTypes())\n                .defaultValues(PostgreSQLDefaultValueEnum.getDefaultValues())\n                .build();\n    }\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"\\\"\" + name + \"\\\"\").collect(Collectors.joining(\".\"));\n    }\n\n    @Override\n    public List<String> getSystemDatabases() {\n        return systemDatabases;\n    }\n\n    @Override\n    public List<String> getSystemSchemas() {\n        return systemSchemas;\n    }\n\n    @Override\n    @SneakyThrows\n    public String sequenceDDL(Connection connection, @NotEmpty String databaseName, String schemaName,\n                              @NotEmpty String sequenceName) {\n        DatabaseMetaData metaData = connection.getMetaData();\n        double databaseProductVersion = Double.parseDouble(metaData.getDatabaseProductVersion());\n        String[] args = new String[]{sequenceName, schemaName};\n        return SQLExecutor.getInstance().preExecute(connection, EXPORT_SEQUENCE_DDL_SQL, args, resultSet -> {\n                    StringBuilder stringBuilder = new StringBuilder();\n                    if (resultSet.next()) {\n                        String nspname = resultSet.getString(\"nspname\");\n                        String relname = resultSet.getString(\"relname\");\n                        String typname = getConversionType(resultSet.getString(\"typname\"));\n                        String seqcache = resultSet.getString(\"seqcache\");\n                        String rolname = resultSet.getString(\"rolname\");\n                        String comment = resultSet.getString(\"comment\");\n                        String seqstart = resultSet.getString(\"seqstart\");\n                        String seqincrement = resultSet.getString(\"seqincrement\");\n                        String seqmax = resultSet.getString(\"seqmax\");\n                        String seqmin = resultSet.getString(\"seqmin\");\n                        Boolean seqcycle = resultSet.getBoolean(\"seqcycle\");\n\n                        stringBuilder.append(CREATE_SEQUENCE).append(getMetaDataName(nspname, relname)).append(NEW_LINE);\n\n                        if (Double.compare(databaseProductVersion, 10.0) >= 0) {\n                            stringBuilder.append(AS).append(typname).append(NEW_LINE);\n                        }\n\n                        Optional.ofNullable(seqstart).ifPresent(v -> stringBuilder.append(START_WITH).append(v).append(NEW_LINE));\n\n                        Optional.ofNullable(seqincrement).ifPresent(v -> stringBuilder.append(INCREMENT_BY).append(v).append(NEW_LINE));\n\n                        Optional.ofNullable(seqmin).ifPresent(v -> stringBuilder.append(MINVALUE).append(v).append(NEW_LINE));\n\n                        Optional.ofNullable(seqmax).ifPresent(v -> stringBuilder.append(MAXVALUE).append(v).append(NEW_LINE));\n\n                        Optional.ofNullable(seqcache).ifPresent(v -> stringBuilder.append(CACHE).append(v).append(NEW_LINE));\n\n                        Optional.ofNullable(seqcycle).ifPresent(v -> {\n                            if (Boolean.TRUE.equals(seqcycle)) {\n                                stringBuilder.append(CYCLE).append(NEW_LINE);\n                            }\n                        });\n\n                        stringBuilder.append(SEMICOLON).append(BLANK_LINE);\n\n                        Optional.ofNullable(comment).ifPresent(v -> stringBuilder.append(COMMENT_ON_SEQUENCE)\n                                .append(getMetaDataName(nspname, relname))\n                                .append(IS).append(SQUOT).append(v).append(SQUOT).append(SEMICOLON).append(BLANK_LINE));\n\n                        Optional.ofNullable(rolname).ifPresent(v -> stringBuilder.append(ALTER_SEQUENCE)\n                                .append(getMetaDataName(nspname, relname))\n                                .append(OWNER_TO).append(getMetaDataName(v)).append(SEMICOLON));\n                    }\n                    return stringBuilder.toString();\n                });\n    }\n\n    @Override\n    public List<SimpleSequence> sequences(Connection connection, String databaseName, String schemaName) {\n        List<SimpleSequence> simpleSequences = new ArrayList<>();\n        String[] args = new String[]{schemaName};\n        return SQLExecutor.getInstance().preExecute(connection, EXPORT_SEQUENCES_SQL, args, resultSet -> {\n                    while (resultSet.next()) {\n                        String relname = resultSet.getString(\"relname\");\n                        String comment = resultSet.getString(\"comment\");\n                        simpleSequences.add(SimpleSequence.builder()\n                                .name(relname)\n                                .comment(comment)\n                                .build());\n                    }\n                    return simpleSequences;\n                });\n    }\n\n    @Override\n    public Sequence sequences(Connection connection, @NotEmpty String databaseName, String schemaName, String sequenceName) {\n        String[] args = new String[]{sequenceName, schemaName};\n        return SQLExecutor.getInstance().preExecute(connection, EXPORT_SEQUENCE_DDL_SQL, args, resultSet -> {\n            if (resultSet.next()) {\n                return Sequence.builder()\n                        .nspname(resultSet.getString(\"nspname\"))\n                        .relname(resultSet.getString(\"relname\"))\n                        .typname(getConversionType(resultSet.getString(\"typname\")))\n                        .seqcache(resultSet.getString(\"seqcache\"))\n                        .rolname(resultSet.getString(\"rolname\"))\n                        .comment(resultSet.getString(\"comment\"))\n                        .seqstart(resultSet.getString(\"seqstart\"))\n                        .seqincrement(resultSet.getString(\"seqincrement\"))\n                        .seqmax(resultSet.getString(\"seqmax\"))\n                        .seqmin(resultSet.getString(\"seqmin\"))\n                        .seqcycle(resultSet.getBoolean(\"seqcycle\"))\n                        .build();\n            }\n            return null;\n        });\n    }\n\n    @Override\n    public List<String> usernames(Connection connection) {\n        List<String> usernames = new ArrayList<>();\n        return SQLExecutor.getInstance().preExecute(connection, EXPORT_USERS_SQL, null, resultSet -> {\n            while (resultSet.next()) {\n                String username = resultSet.getString(\"username\");\n                usernames.add(username);\n            }\n            return usernames;\n        });\n    }\n\n    private String getConversionType(String typname) {\n        switch (typname) {\n            case \"int2\" -> typname = \"SMALLINT\";\n            case \"int8\" -> typname = \"BIGINT\";\n            default -> typname = \"INTEGER\";\n        }\n        return typname;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/PostgreSQLPlugin.java",
    "content": "package ai.chat2db.plugin.postgresql;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class PostgreSQLPlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"pg.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new PostgreSQLMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new PostgreSQLDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/builder/PostgreSQLSqlBuilder.java",
    "content": "package ai.chat2db.plugin.postgresql.builder;\n\nimport ai.chat2db.plugin.postgresql.type.PostgreSQLColumnTypeEnum;\nimport ai.chat2db.plugin.postgresql.type.PostgreSQLIndexTypeEnum;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport cn.hutool.core.util.BooleanUtil;\nimport lombok.SneakyThrows;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport static ai.chat2db.plugin.postgresql.consts.SequenceCommonConst.*;\nimport static ai.chat2db.server.tools.base.constant.SymbolConstant.*;\n\n\npublic class PostgreSQLSqlBuilder extends DefaultSqlBuilder {\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"CREATE TABLE \");\n        script.append(\"\\\"\").append(table.getName()).append(\"\\\"\").append(\" (\").append(\" \").append(\"\\n\");\n        // append column\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            PostgreSQLColumnTypeEnum typeEnum = PostgreSQLColumnTypeEnum.getByType(column.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n        Map<Boolean, List<TableIndex>> tableIndexMap = table.getIndexList().stream()\n                .collect(Collectors.partitioningBy(v -> PostgreSQLIndexTypeEnum.NORMAL.getName().equals(v.getType())));\n        // append constraint key\n        List<TableIndex> constraintList = tableIndexMap.get(Boolean.FALSE);\n        if (CollectionUtils.isNotEmpty(constraintList)) {\n            for (TableIndex index : constraintList) {\n                if (StringUtils.isBlank(index.getName()) || StringUtils.isBlank(index.getType())) {\n                    continue;\n                }\n                PostgreSQLIndexTypeEnum indexTypeEnum = PostgreSQLIndexTypeEnum.getByType(index.getType());\n                if(indexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(\"\").append(indexTypeEnum.buildIndexScript(index));\n                script.append(\",\\n\");\n            }\n\n        }\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n)\").append(\";\");\n\n        // append index\n        List<TableIndex> tableIndexList = tableIndexMap.get(Boolean.TRUE);\n        for (TableIndex tableIndex : tableIndexList) {\n            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {\n                continue;\n            }\n            script.append(\"\\n\");\n            PostgreSQLIndexTypeEnum indexTypeEnum = PostgreSQLIndexTypeEnum.getByType(tableIndex.getType());\n            if(indexTypeEnum == null){\n                continue;\n            }\n            script.append(\"\").append(indexTypeEnum.buildIndexScript(tableIndex)).append(\";\");\n        }\n\n        // append comment\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\"\\n\");\n            script.append(\"COMMENT ON TABLE\").append(\" \").append(\"\\\"\").append(table.getName()).append(\"\\\" IS '\")\n                    .append(table.getComment()).append(\"';\\n\");\n        }\n        List<TableColumn> tableColumnList = table.getColumnList().stream().filter(v -> StringUtils.isNotBlank(v.getComment())).toList();\n        for (TableColumn tableColumn : tableColumnList) {\n            PostgreSQLColumnTypeEnum typeEnum = PostgreSQLColumnTypeEnum.getByType(tableColumn.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            script.append(typeEnum.buildComment(tableColumn, typeEnum)).append(\"\\n\");\n            ;\n        }\n        List<TableIndex> indexList = table.getIndexList().stream().filter(v -> StringUtils.isNotBlank(v.getComment())).toList();\n        for (TableIndex index : indexList) {\n            PostgreSQLIndexTypeEnum indexEnum = PostgreSQLIndexTypeEnum.getByType(index.getType());\n            if(indexEnum == null){\n                continue;\n            }\n            script.append(indexEnum.buildIndexComment(index)).append(\"\\n\");\n            ;\n        }\n\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(oldTable.getName()).append(\"\\\"\");\n            script.append(\"\\t\").append(\"RENAME TO \").append(\"\\\"\").append(newTable.getName()).append(\"\\\"\").append(\";\\n\");\n\n        }\n        newTable.setColumnList(newTable.getColumnList().stream().filter(v -> StringUtils.isNotBlank(v.getEditStatus())).toList());\n        newTable.setIndexList(newTable.getIndexList().stream().filter(v -> StringUtils.isNotBlank(v.getEditStatus())).toList());\n\n        //update name\n        List<TableColumn> columnNameList = newTable.getColumnList().stream().filter(v ->\n                v.getOldName() != null && !StringUtils.equals(v.getOldName(), v.getName())).toList();\n        for (TableColumn tableColumn : columnNameList) {\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(newTable.getName()).append(\"\\\" \").append(\"RENAME COLUMN \\\"\")\n                    .append(tableColumn.getOldName()).append(\"\\\" TO \\\"\").append(tableColumn.getName()).append(\"\\\";\\n\");\n        }\n\n        Map<Boolean, List<TableIndex>> tableIndexMap = newTable.getIndexList().stream()\n                .collect(Collectors.partitioningBy(v -> PostgreSQLIndexTypeEnum.NORMAL.getName().equals(v.getType())));\n        StringBuilder scriptModify = new StringBuilder();\n        Boolean modify = false;\n        scriptModify.append(\"ALTER TABLE \").append(\"\\\"\").append(newTable.getName()).append(\"\\\" \\n\");\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            PostgreSQLColumnTypeEnum typeEnum = PostgreSQLColumnTypeEnum.getByType(tableColumn.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            scriptModify.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\",\\n\");\n            modify = true;\n\n        }\n\n        // append modify constraint\n        for (TableIndex tableIndex : tableIndexMap.get(Boolean.FALSE)) {\n            if (StringUtils.isNotBlank(tableIndex.getType())) {\n                PostgreSQLIndexTypeEnum indexTypeEnum = PostgreSQLIndexTypeEnum.getByType(tableIndex.getType());\n                if(indexTypeEnum == null){\n                    continue;\n                }\n                scriptModify.append(\"\\t\").append(indexTypeEnum.buildModifyIndex(tableIndex)).append(\",\\n\");\n                modify = true;\n            }\n        }\n\n        if (BooleanUtils.isTrue(modify)) {\n            script.append(scriptModify);\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            script.append(\";\\n\");\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : tableIndexMap.get(Boolean.TRUE)) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                PostgreSQLIndexTypeEnum indexTypeEnum = PostgreSQLIndexTypeEnum.getByType(tableIndex.getType());\n                if(indexTypeEnum == null){\n                    continue;\n                }\n                script.append(indexTypeEnum.buildModifyIndex(tableIndex)).append(\";\\n\");\n            }\n        }\n\n        // append comment\n        if (!StringUtils.equals(oldTable.getComment(), newTable.getComment())) {\n            script.append(\"\\n\");\n            script.append(\"COMMENT ON TABLE\").append(\" \").append(\"\\\"\").append(newTable.getName()).append(\"\\\" IS '\")\n                    .append(newTable.getComment()).append(\"';\\n\");\n        }\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            PostgreSQLColumnTypeEnum typeEnum = PostgreSQLColumnTypeEnum.getByType(tableColumn.getColumnType());\n            if(typeEnum ==null){\n                continue;\n            }\n            script.append(typeEnum.buildComment(tableColumn, typeEnum)).append(\"\\n\");\n            ;\n        }\n        List<TableIndex> indexList = newTable.getIndexList().stream().filter(v -> StringUtils.isNotBlank(v.getComment())).toList();\n        for (TableIndex index : indexList) {\n            PostgreSQLIndexTypeEnum indexEnum = PostgreSQLIndexTypeEnum.getByType(index.getType());\n            if(indexEnum == null){\n                continue;\n            }\n            script.append(indexEnum.buildIndexComment(index)).append(\"\\n\");\n        }\n\n        return script.toString();\n    }\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        StringBuilder sqlStr = new StringBuilder(sql.length() + 17);\n        sqlStr.append(sql);\n        if (offset == 0) {\n            sqlStr.append(\" LIMIT \");\n            sqlStr.append(pageSize);\n        } else {\n            sqlStr.append(\" LIMIT \");\n            sqlStr.append(pageSize);\n            sqlStr.append(\" OFFSET \");\n            sqlStr.append(offset);\n        }\n        return sqlStr.toString();\n    }\n\n    @Override\n    public String buildCreateDatabaseSql(Database database) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE DATABASE \\\"\"+database.getName()+\"\\\"\");\n        sqlBuilder.append(\"\\nWITH \");\n        if(StringUtils.isNotBlank(database.getCharset())){\n            sqlBuilder.append(\"\\n LC_CTYPE = '\").append(database.getCharset()).append(\"' \");\n        }\n        if(StringUtils.isNotBlank(database.getCollation())){\n            sqlBuilder.append(\"\\n LC_COLLATE = '\").append(database.getCollation()).append(\"' \");\n        }\n\n        if(StringUtils.isNotBlank(database.getComment())){\n            sqlBuilder.append(\"; COMMENT ON DATABASE \\\"\").append(database.getName()).append(\"\\\" IS '\").append(database.getComment()).append(\"';\");\n        }\n        return sqlBuilder.toString();\n    }\n\n\n    @Override\n    public String buildCreateSchemaSql(Schema schema){\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE SCHEMA \\\"\"+schema.getName()+\"\\\"\");\n        if(StringUtils.isNotBlank(schema.getOwner())){\n            sqlBuilder.append(\" AUTHORIZATION \").append(schema.getOwner());\n        }\n        if(StringUtils.isNotBlank(schema.getComment())){\n            sqlBuilder.append(\"; COMMENT ON SCHEMA \\\"\").append(schema.getName()).append(\"\\\" IS '\").append(schema.getComment()).append(\"';\");\n        }\n        return sqlBuilder.toString();\n    }\n\n    @Override\n    @SneakyThrows\n    public String buildCreateSequenceSql(Sequence sequence) {\n\n        double databaseProductVersion = Double.parseDouble(Chat2DBContext.getConnection().getMetaData().getDatabaseProductVersion());\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(CREATE_SEQUENCE).append(getMetaDataName(sequence.getNspname(), sequence.getRelname())).append(\"\\n \");\n        if (Double.compare(databaseProductVersion, 10.0) >= 0) {\n            sqlBuilder.append(AS).append(sequence.getTypname()).append(\"\\n \");\n        }\n        Optional.ofNullable(sequence.getSeqstart()).ifPresent(v -> sqlBuilder.append(START_WITH).append(v).append(\"\\n \"));\n\n        Optional.ofNullable(sequence.getSeqincrement()).ifPresent(v -> sqlBuilder.append(INCREMENT_BY).append(v).append(\"\\n \"));\n\n        Optional.ofNullable(sequence.getSeqmin()).ifPresent(v -> sqlBuilder.append(MINVALUE).append(v).append(\"\\n \"));\n\n        Optional.ofNullable(sequence.getSeqmax()).ifPresent(v -> sqlBuilder.append(MAXVALUE).append(v).append(\"\\n \"));\n\n        Optional.ofNullable(sequence.getSeqcache()).ifPresent(v -> sqlBuilder.append(CACHE).append(v).append(\"\\n \"));\n\n        Optional.ofNullable(sequence.getSeqcycle()).ifPresent(v -> {\n            if (Boolean.TRUE.equals(sequence.getSeqcycle())) {\n                sqlBuilder.append(CYCLE).append(\"\\n \");\n            }\n        });\n\n        sqlBuilder.append(SEMICOLON).append(\"\\n \").append(\"\\n \");\n\n        Optional.ofNullable(sequence.getComment()).ifPresent(v -> sqlBuilder.append(COMMENT_ON_SEQUENCE)\n                .append(getMetaDataName(sequence.getNspname(), sequence.getRelname()))\n                .append(IS).append(SQUOT).append(v).append(SQUOT).append(SEMICOLON).append(\"\\n \").append(\"\\n \"));\n\n        Optional.ofNullable(sequence.getRolname()).ifPresent(v -> sqlBuilder.append(ALTER_SEQUENCE)\n                .append(getMetaDataName(sequence.getNspname(), sequence.getRelname()))\n                .append(OWNER_TO).append(getMetaDataName(v)).append(SEMICOLON));\n        return sqlBuilder.toString();\n    }\n\n    @Override\n    public String buildModifySequenceSql(Sequence oldSequence, Sequence newSequence) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        if (!StringUtils.equalsIgnoreCase(oldSequence.getRelname(), newSequence.getRelname())) {\n            sqlBuilder.append(ALTER_SEQUENCE).append(getMetaDataName(oldSequence.getNspname(), oldSequence.getRelname())).append(RENAME_TO).append(getMetaDataName(newSequence.getRelname())).append(SEMICOLON).append(BLANK_LINE);\n        }\n        if (!StringUtils.equals(oldSequence.getComment(), newSequence.getComment())) {\n            sqlBuilder.append(COMMENT_ON_SEQUENCE).append(getMetaDataName(newSequence.getNspname(), newSequence.getRelname())).append(IS).append(SQUOT).append(newSequence.getComment()).append(SQUOT).append(SEMICOLON).append(BLANK_LINE);\n        }\n        if (!StringUtils.equals(oldSequence.getSeqcache(), newSequence.getSeqcache())) {\n            sqlBuilder.append(ALTER_SEQUENCE).append(getMetaDataName(newSequence.getNspname(), newSequence.getRelname())).append(CACHE).append(getMetaDataName(newSequence.getSeqcache())).append(SEMICOLON).append(BLANK_LINE);\n        }\n        if (BooleanUtil.xor(oldSequence.getSeqcycle(), newSequence.getSeqcycle())) {\n            if (Boolean.TRUE.equals(newSequence.getSeqcycle())) {\n                sqlBuilder.append(ALTER_SEQUENCE).append(getMetaDataName(newSequence.getNspname(), newSequence.getRelname())).append(CYCLE).append(BLANK_LINE);\n            } else {\n                sqlBuilder.append(ALTER_SEQUENCE).append(getMetaDataName(newSequence.getNspname(), newSequence.getRelname())).append(NO_CYCLE).append(BLANK_LINE);\n            }\n        }\n\n        if (!StringUtils.equals(oldSequence.getSeqstart(), newSequence.getSeqstart()) ||\n                !StringUtils.equals(oldSequence.getSeqincrement(), newSequence.getSeqincrement()) ||\n                !StringUtils.equals(oldSequence.getSeqmax(), newSequence.getSeqmax()) ||\n                !StringUtils.equals(oldSequence.getSeqmin(), newSequence.getSeqmin())) {\n            sqlBuilder.append(ALTER_SEQUENCE);\n            if (!StringUtils.equals(oldSequence.getSeqstart(), newSequence.getSeqstart())) {\n                sqlBuilder.append(getMetaDataName(newSequence.getNspname(), newSequence.getRelname())).append(RESTART_WITH).append(newSequence.getSeqstart());\n            }\n            if (!StringUtils.equals(oldSequence.getSeqincrement(), newSequence.getSeqincrement())) {\n                sqlBuilder.append(INCREMENT_BY).append(newSequence.getSeqincrement());\n            }\n            if (!StringUtils.equals(oldSequence.getSeqmax(), newSequence.getSeqmax())) {\n                sqlBuilder.append(MAXVALUE).append(newSequence.getSeqmax());\n            }\n            if (!StringUtils.equals(oldSequence.getSeqmin(), newSequence.getSeqmin())) {\n                sqlBuilder.append(MINVALUE).append(newSequence.getSeqmin());\n            }\n            sqlBuilder.append(SEMICOLON).append(BLANK_LINE);\n        }\n\n        if (!StringUtils.equals(oldSequence.getTypname(), newSequence.getTypname())) {\n            sqlBuilder.append(ALTER_SEQUENCE).append(getMetaDataName(newSequence.getNspname(), newSequence.getRelname())).append(AS).append(newSequence.getTypname()).append(SEMICOLON).append(BLANK_LINE);\n        }\n        if (!StringUtils.equals(oldSequence.getRolname(), newSequence.getRolname())) {\n            sqlBuilder.append(ALTER_SEQUENCE).append(getMetaDataName(newSequence.getNspname(), newSequence.getRelname())).append(OWNER_TO).append(getMetaDataName(newSequence.getRolname())).append(SEMICOLON).append(BLANK_LINE);\n        }\n        return sqlBuilder.toString();\n    }\n\n    private String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(StringUtils::isNotBlank).map(name -> DOUBLE_SQUOT + name + DOUBLE_SQUOT).collect(Collectors.joining(DOT));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/consts/SQLConst.java",
    "content": "package ai.chat2db.plugin.postgresql.consts;\n\npublic class SQLConst {\n    public static String TABLE_DEF_FUNCTION_SQL =\n            \"\"\"\n            CREATE TYPE tabledefs AS ENUM ('PKEY_INTERNAL','PKEY_EXTERNAL','FKEYS_INTERNAL', 'FKEYS_EXTERNAL', 'COMMENTS', 'FKEYS_NONE', 'INCLUDE_TRIGGERS', 'NO_TRIGGERS');\n            CREATE OR REPLACE FUNCTION pg_get_coldef(\n              in_schema text,\n              in_table  text,\n              in_column text,\n              oldway    boolean default False\n            )\n            RETURNS text\n            LANGUAGE plpgsql VOLATILE\n            AS\n            $$\n            DECLARE\n            v_coldef     text;\n            v_dt1        text;\n            v_dt2        text;\n            v_dt3        text;\n            v_nullable   boolean;\n            v_position   int;\n            v_identity   text;\n            v_generated  text;\n            v_hasdflt    boolean;\n            v_dfltexpr   text;\n                          \n            BEGIN\n              IF oldway THEN\n                SELECT pg_catalog.format_type(a.atttypid, a.atttypmod) INTO v_coldef FROM pg_namespace n, pg_class c, pg_attribute a, pg_type t\n                WHERE n.nspname = in_schema AND n.oid = c.relnamespace AND c.relname = in_table AND a.attname = in_column and a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid ORDER BY a.attnum;\n                -- RAISE NOTICE 'DEBUG: oldway=%',v_coldef;\n              ELSE\n\n                SELECT CASE WHEN a.atttypid = ANY ('{int,int8,int2}'::regtype[]) AND EXISTS (SELECT FROM pg_attrdef ad WHERE ad.adrelid = a.attrelid AND ad.adnum   = a.attnum AND\n            \t  pg_get_expr(ad.adbin, ad.adrelid) = 'nextval(''' || (pg_get_serial_sequence (a.attrelid::regclass::text, a.attname))::regclass || '''::regclass)') THEN CASE a.atttypid\n            \t  WHEN 'int'::regtype  THEN 'serial' WHEN 'int8'::regtype THEN 'bigserial' WHEN 'int2'::regtype THEN 'smallserial' END ELSE format_type(a.atttypid, a.atttypmod) END AS data_type \n            \t  INTO v_coldef FROM pg_namespace n, pg_class c, pg_attribute a, pg_type t\n            \t  WHERE n.nspname = in_schema AND n.oid = c.relnamespace AND c.relname = in_table AND a.attname = in_column and a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid ORDER BY a.attnum;\n                   \n                          \n             \n              END IF;\n              RETURN v_coldef;\n            END;\n            $$;\n                          \n            -- SELECT * FROM pg_get_tabledef('sample', 'address', false);\n            DROP FUNCTION IF EXISTS pg_get_tabledef(character varying,character varying,boolean,tabledefs[]);\n            CREATE OR REPLACE FUNCTION pg_get_tabledef(\n              in_schema varchar,\n              in_table varchar,\n              _verbose boolean,\n              VARIADIC arr tabledefs[] DEFAULT '{}':: tabledefs[]\n            )\n            RETURNS text\n            LANGUAGE plpgsql VOLATILE\n            AS\n            $$\n              DECLARE\n                v_qualified text := '';\n                v_table_ddl text;\n                v_table_oid int;\n                v_colrec record;\n                v_constraintrec record;\n                v_trigrec       record;\n                v_indexrec record;\n                v_rec           record;\n                v_constraint_name text;\n                v_constraint_def  text;\n                v_pkey_def        text := '';\n                v_fkey_def        text := '';\n                v_fkey_defs       text := '';\n                v_trigger text := '';\n                v_partition_key text := '';\n                v_partbound text;\n                v_parent text;\n                v_parent_schema text;\n                v_persist text;\n                v_temp  text := '';\n                v_temp2 text;\n                v_relopts text;\n                v_tablespace text;\n                v_pgversion int;\n                bSerial boolean;\n                bPartition boolean;\n                bInheritance boolean;\n                bRelispartition boolean;\n                constraintarr text[] := '{}';\n                constraintelement text;\n                bSkip boolean;\n            \t  bVerbose boolean := False;\n            \t  v_cnt1   integer;\n            \t  v_cnt2   integer;\n            \t  search_path_old text := '';\n            \t  search_path_new text := '';\n            \t  v_partial    boolean;\n            \t  v_pos        integer;\n                          \n\n              \tpkcnt            int := 0;\n              \tfkcnt            int := 0;\n            \t  trigcnt          int := 0;\n            \t  cmtcnt           int := 0;\n                pktype           tabledefs := 'PKEY_INTERNAL';\n                fktype           tabledefs := 'FKEYS_INTERNAL';\n                trigtype         tabledefs := 'NO_TRIGGERS';\n                arglen           integer;\n              \tvargs            text;\n            \t  avarg            tabledefs;\n                          \n\n                v_ret            text;\n                v_diag1          text;\n                v_diag2          text;\n                v_diag3          text;\n                v_diag4          text;\n                v_diag5          text;\n                v_diag6          text;\n            \t\n              BEGIN\n                SET client_min_messages = 'notice';\n                IF _verbose THEN bVerbose = True; END IF;\n               \n\n            \t\n                arglen := array_length($4, 1);\n                IF arglen IS NULL THEN\n                    -- nothing to do, so assume defaults\n                    NULL;\n                ELSE\n\n                    IF bVerbose THEN RAISE NOTICE 'arguments=%', $4; END IF;\n                    FOREACH avarg IN ARRAY $4 LOOP\n                        IF bVerbose THEN RAISE NOTICE 'arg=%', avarg; END IF;\n                        IF avarg = 'FKEYS_INTERNAL' OR avarg = 'FKEYS_EXTERNAL' OR avarg = 'FKEYS_NONE' THEN\n                            fkcnt = fkcnt + 1;\n                            fktype = avarg;\n                        ELSEIF avarg = 'INCLUDE_TRIGGERS' OR avarg = 'NO_TRIGGERS' THEN\n                            trigcnt = trigcnt + 1;\n                            trigtype = avarg;\n                        ELSEIF avarg = 'PKEY_EXTERNAL' THEN\n                            pkcnt = pkcnt + 1;\n                            pktype = avarg;\t\t\t\t               \n                        ELSEIF avarg = 'COMMENTS' THEN\n                            cmtcnt = cmtcnt + 1;\n                           \n                        END IF;\n                    END LOOP;\n                    IF fkcnt > 1 THEN\n              \t        RAISE WARNING 'Only one foreign key option can be provided. You provided %', fkcnt;\n            \t          RETURN '';\n                    ELSEIF trigcnt > 1 THEN\n                        RAISE WARNING 'Only one trigger option can be provided. You provided %', trigcnt;\n                        RETURN '';\n                    ELSEIF pkcnt > 1 THEN\n                        RAISE WARNING 'Only one pkey option can be provided. You provided %', pkcnt;\n                        RETURN '';\t\t\t\n                    ELSEIF cmtcnt > 1 THEN\n                        RAISE WARNING 'Only one comments option can be provided. You provided %', cmtcnt;\n                        RETURN '';\t\t\t\n                       \n                    END IF;\t\t   \t\t  \n                END IF;\n                          \n                SELECT c.oid, (select setting from pg_settings where name = 'server_version_num') INTO v_table_oid, v_pgversion FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n                WHERE c.relkind in ('r','p') AND c.relname = in_table AND n.nspname = in_schema;\n                          \n                SELECT setting INTO search_path_old FROM pg_settings WHERE name = 'search_path';\n                          \n                SELECT REPLACE(REPLACE(setting, '\"$user\"', '$user'), '$user', '\"$user\"') INTO search_path_old\n                FROM pg_settings\n                WHERE name = 'search_path';\n\n                EXECUTE 'SET search_path = \"public\"';\n                SELECT setting INTO search_path_new FROM pg_settings WHERE name = 'search_path';\n              \n                IF (v_table_oid IS NULL) THEN\n                  RAISE EXCEPTION 'table does not exist';\n                END IF;\n                          \n                  \n                SELECT tablespace INTO v_temp FROM pg_tables WHERE schemaname = in_schema and tablename = in_table and tablespace IS NOT NULL;\n                IF v_temp IS NULL THEN\n                  v_tablespace := 'TABLESPACE pg_default';\n                ELSE\n                  v_tablespace := 'TABLESPACE ' || v_temp;\n                END IF;\n               \n\n                WITH relopts AS (SELECT unnest(c.reloptions) relopts FROM pg_class c, pg_namespace n WHERE n.nspname = in_schema and n.oid = c.relnamespace and c.relname = in_table)\n                SELECT string_agg(r.relopts, ', ') as relopts INTO v_temp from relopts r;\n                IF v_temp IS NULL THEN\n                  v_relopts := '';\n                ELSE\n                  v_relopts := ' WITH (' || v_temp || ')';\n                END IF;\n               \n                \n                v_partbound := '';\n                bPartition := False;\n                bInheritance := False;\n                IF v_pgversion < 100000 THEN\n\n                  SELECT c2.relname parent, c2.relnamespace::regnamespace INTO v_parent, v_parent_schema from pg_class c1, pg_namespace n, pg_inherits i, pg_class c2\n                  WHERE n.nspname = in_schema and n.oid = c1.relnamespace and c1.relname = in_table and c1.oid = i.inhrelid and i.inhparent = c2.oid and c1.relkind = 'r';     \n                  IF (v_parent IS NOT NULL) THEN\n                    bPartition   := True;\n                    bInheritance := True;\n                  END IF;\n                ELSE\n\n                  SELECT c2.relname parent, c1.relispartition, pg_get_expr(c1.relpartbound, c1.oid, true), c2.relnamespace::regnamespace INTO v_parent, bRelispartition, v_partbound, v_parent_schema from pg_class c1, pg_namespace n, pg_inherits i, pg_class c2\n                  WHERE n.nspname = in_schema and n.oid = c1.relnamespace and c1.relname = in_table and c1.oid = i.inhrelid and i.inhparent = c2.oid and c1.relkind = 'r';\n                  IF (v_parent IS NOT NULL) THEN\n                    bPartition   := True;\n                    IF bRelispartition THEN\n                      bInheritance := False;\n                    ELSE\n                      bInheritance := True;\n                    END IF;\n                  END IF;\n                END IF;\n                IF bPartition THEN\n\n            \t\t  SELECT count(*) INTO v_cnt1 FROM information_schema.tables t WHERE EXISTS (SELECT REGEXP_MATCHES(s.table_name, '([A-Z]+)','g') FROM information_schema.tables s\n            \t\t  WHERE t.table_schema=s.table_schema AND t.table_name=s.table_name AND t.table_schema = in_schema AND t.table_name = in_table AND t.table_type = 'BASE TABLE');      \t\t \n            \t\t \n             \n                  SELECT COUNT(*) INTO v_cnt2 FROM pg_get_keywords() WHERE word = in_table AND catcode = 'R';\n            \t\t \n                  IF bInheritance THEN\n                    IF v_cnt1 > 0 OR v_cnt2 > 0 THEN\n                      v_table_ddl := 'CREATE TABLE ' || in_schema || '.\"' || in_table || '\"( '|| E'\\\\n';       \n                    ELSE\n                      v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || '( '|| E'\\\\n';               \n                    END IF;\n                          \n               \n                  ELSE\n                    IF v_relopts <> '' THEN\n                      IF v_cnt1 > 0 OR v_cnt2 > 0 THEN\n                        v_table_ddl := 'CREATE TABLE ' || in_schema || '.\"' || in_table || '\" PARTITION OF ' || in_schema || '.' || v_parent || ' ' || v_partbound || v_relopts || ' ' || v_tablespace || '; ' || E'\\\\n';\n            \t\t\t\t  ELSE\n            \t\t\t\t    v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || ' PARTITION OF ' || in_schema || '.' || v_parent || ' ' || v_partbound || v_relopts || ' ' || v_tablespace || '; ' || E'\\\\n';\n            \t\t\t\t  END IF;\n                    ELSE\n                      IF v_cnt1 > 0 OR v_cnt2 > 0 THEN\n                        v_table_ddl := 'CREATE TABLE ' || in_schema || '.\"' || in_table || '\" PARTITION OF ' || in_schema || '.' || v_parent || ' ' || v_partbound || ' ' || v_tablespace || '; ' || E'\\\\n';\n            \t\t\t\t  ELSE\n            \t\t\t\t    v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || ' PARTITION OF ' || in_schema || '.' || v_parent || ' ' || v_partbound || ' ' || v_tablespace || '; ' || E'\\\\n';\n            \t\t\t\t  END IF;\n                    END IF;\n\n                  END IF;\n                END IF;\n            \t  IF bVerbose THEN RAISE NOTICE '(1)tabledef so far: %', v_table_ddl; END IF;\n                          \n                IF NOT bPartition THEN\n\n                  select c.relpersistence into v_persist from pg_class c, pg_namespace n where n.nspname = in_schema and n.oid = c.relnamespace and c.relname = in_table and c.relkind = 'r';\n                  IF v_persist = 'u' THEN\n                    v_temp := 'UNLOGGED';\n                  ELSIF v_persist = 't' THEN\n                    v_temp := 'TEMPORARY';\n                  ELSE\n                    v_temp := '';\n                  END IF;\n                END IF;\n               \n\n                IF NOT bPartition THEN\n\n                  SELECT count(*) INTO v_cnt1 FROM information_schema.tables t WHERE EXISTS (SELECT REGEXP_MATCHES(s.table_name, '([A-Z]+)','g') FROM information_schema.tables s\n                  WHERE t.table_schema=s.table_schema AND t.table_name=s.table_name AND t.table_schema = in_schema AND t.table_name = in_table AND t.table_type = 'BASE TABLE');        \n                  IF v_cnt1 > 0 THEN\n                    v_table_ddl := 'CREATE ' || v_temp || ' TABLE ' || in_schema || '.\"' || in_table || '\" (' || E'\\\\n';\n                  ELSE\n                    v_table_ddl := 'CREATE ' || v_temp || ' TABLE ' || in_schema || '.' || in_table || ' (' || E'\\\\n';\n                  END IF;\n                END IF;\n\n                IF NOT bPartition THEN\n                  FOR v_colrec IN\n                    SELECT c.column_name, c.data_type, c.udt_name, c.udt_schema, c.character_maximum_length, c.is_nullable, c.column_default, c.numeric_precision, c.numeric_scale, c.is_identity, c.identity_generation, c.is_generated, c.generation_expression       \n                    FROM information_schema.columns c WHERE (table_schema, table_name) = (in_schema, in_table) ORDER BY ordinal_position\n                  LOOP\n                     IF bVerbose THEN RAISE NOTICE '(col loop) name=%  type=%  udt_name=%  default=%  is_generated=%  gen_expr=%', v_colrec.column_name, v_colrec.data_type, v_colrec.udt_name, v_colrec.column_default, v_colrec.is_generated, v_colrec.generation_expression; END IF; \n                   \n                     SELECT CASE WHEN pg_get_serial_sequence(quote_ident(in_schema) || '.' || quote_ident(in_table), v_colrec.column_name) IS NOT NULL THEN True ELSE False END into bSerial;\n                     IF bVerbose THEN\n\n                       SELECT pg_get_serial_sequence(quote_ident(in_schema) || '.' || quote_ident(in_table), v_colrec.column_name) into v_temp;\n                       IF v_temp IS NULL THEN v_temp = 'NA'; END IF;\n                       SELECT pg_get_coldef(in_schema, in_table,v_colrec.column_name) INTO v_diag1;\n                       RAISE NOTICE 'DEBUG table: %  Column: %  datatype: %  Serial=%  serialval=%  coldef=%', v_qualified, v_colrec.column_name, v_colrec.data_type, bSerial, v_temp, v_diag1;\n                       RAISE NOTICE 'DEBUG tabledef: %', v_table_ddl;\n                     END IF;\n                    \n              \n                     SELECT COUNT(*) INTO v_cnt1 FROM information_schema.columns t WHERE EXISTS (SELECT REGEXP_MATCHES(s.column_name, '([A-Z]+)','g') FROM information_schema.columns s\n                     WHERE t.table_schema=s.table_schema and t.table_name=s.table_name and t.column_name=s.column_name AND t.table_schema = quote_ident(in_schema) AND column_name = v_colrec.column_name);        \n                          \n                     SELECT COUNT(*) INTO v_cnt2 FROM pg_get_keywords() WHERE word = v_colrec.column_name AND catcode = 'R';\n                    \n                     IF v_cnt1 > 0 OR v_cnt2 > 0 THEN\n                       v_table_ddl := v_table_ddl || '  \"' || v_colrec.column_name || '\" ';\n                     ELSE\n                       v_table_ddl := v_table_ddl || '  ' || v_colrec.column_name || ' ';\n                     END IF;\n                   \n                     IF v_colrec.is_generated = 'ALWAYS' and v_colrec.generation_expression IS NOT NULL THEN\n              \n                         v_temp = v_colrec.data_type || ' GENERATED ALWAYS AS (' || v_colrec.generation_expression || ') STORED ';\n                     ELSEIF v_colrec.udt_name in ('geometry', 'box2d', 'box2df', 'box3d', 'geography', 'geometry_dump', 'gidx', 'spheroid', 'valid_detail') THEN\n            \t\t         v_temp = v_colrec.udt_name;\n            \t\t     ELSEIF v_colrec.data_type = 'USER-DEFINED' THEN\n            \t\t         v_temp = v_colrec.udt_schema || '.' || v_colrec.udt_name;\n            \t\t     ELSEIF v_colrec.data_type = 'ARRAY' THEN\n\n            \t\t         v_temp = pg_get_coldef(in_schema, in_table,v_colrec.column_name);\n\n            \t\t     ELSEIF pg_get_serial_sequence(quote_ident(in_schema) || '.' || quote_ident(in_table), v_colrec.column_name) IS NOT NULL THEN\n            \t\t         -- Issue#8 fix: handle serial. Note: NOT NULL is implied so no need to declare it explicitly\n            \t\t         v_temp = pg_get_coldef(in_schema, in_table,v_colrec.column_name);\n            \t\t     ELSE\n            \t\t         v_temp = v_colrec.data_type;\n                     END IF;\n             \n                          \n\n            \t\t     IF v_colrec.is_identity = 'YES' THEN\n            \t\t         IF v_colrec.identity_generation = 'ALWAYS' THEN\n            \t\t             v_temp = v_temp || ' GENERATED ALWAYS AS IDENTITY NOT NULL';\n            \t\t         ELSE\n            \t\t             v_temp = v_temp || ' GENERATED BY DEFAULT AS IDENTITY NOT NULL';\n            \t\t         END IF;\n                     ELSEIF v_colrec.character_maximum_length IS NOT NULL THEN\n                         v_temp = v_temp || ('(' || v_colrec.character_maximum_length || ')');\n                     ELSEIF v_colrec.numeric_precision > 0 AND v_colrec.numeric_scale > 0 THEN\n                         v_temp = v_temp || '(' || v_colrec.numeric_precision || ',' || v_colrec.numeric_scale || ')';\n                     END IF;\n                          \n\n                     IF bSerial THEN\n                         v_temp = v_temp || ' NOT NULL';\n                     ELSEIF v_colrec.is_nullable = 'NO' THEN\n                         v_temp = v_temp || ' NOT NULL';\n                     ELSEIF v_colrec.is_nullable = 'YES' THEN\n                         v_temp = v_temp || ' NULL';\n                     END IF;\n                          \n\n                     IF v_colrec.column_default IS NOT null AND NOT bSerial THEN\n\n                         v_temp = v_temp || (' DEFAULT ' || v_colrec.column_default);\n                     END IF;\n                     v_temp = v_temp || ',' || E'\\\\n';\n\n                     v_table_ddl := v_table_ddl || v_temp;\n\n                          \n                  END LOOP;\n                END IF;\n                IF bVerbose THEN RAISE NOTICE '(2)tabledef so far: %', v_table_ddl; END IF;\n                   \n                IF v_pgversion < 110000 THEN\n                  FOR v_constraintrec IN\n                    SELECT con.conname as constraint_name, con.contype as constraint_type,\n                      CASE\n                        WHEN con.contype = 'p' THEN 1 -- primary key constraint\n                        WHEN con.contype = 'u' THEN 2 -- unique constraint\n                        WHEN con.contype = 'f' THEN 3 -- foreign key constraint\n                        WHEN con.contype = 'c' THEN 4\n                        ELSE 5\n                      END as type_rank,\n                      pg_get_constraintdef(con.oid) as constraint_definition\n                    FROM pg_catalog.pg_constraint con JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace\n                    WHERE nsp.nspname = in_schema AND rel.relname = in_table ORDER BY type_rank\n                  LOOP\n                    v_constraint_name := v_constraintrec.constraint_name;\n                    v_constraint_def  := v_constraintrec.constraint_definition;\n                    IF v_constraintrec.type_rank = 1 THEN\n                        IF pkcnt = 0 OR pktype = 'PKEY_INTERNAL' THEN\n\n                            v_constraint_name := v_constraintrec.constraint_name;\n                            v_constraint_def  := v_constraintrec.constraint_definition;\n                            v_table_ddl := v_table_ddl || '  ' -- note: two char spacer to start, to indent the column\n                              || 'CONSTRAINT' || ' '\n                              || v_constraint_name || ' '\n                              || v_constraint_def\n                              || ',' || E'\\\\n';\n                        ELSE\n\n                          SELECT 'ALTER TABLE ONLY ' || in_schema || '.' || c.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' INTO v_pkey_def\n                          FROM pg_catalog.pg_constraint r, pg_class c, pg_namespace n where r.conrelid = c.oid and  r.contype = 'p' and n.oid = r.connamespace and n.nspname = in_schema AND c.relname = in_table and r.conname = v_constraint_name;            \n                        END IF;\n                        IF bPartition THEN\n                          continue;\n                        END IF;\n                    ELSIF v_constraintrec.type_rank = 3 THEN\n\n                        IF fktype = 'FKEYS_NONE' THEN\n\n                            continue;\n                        ELSIF fkcnt = 0 OR fktype = 'FKEYS_INTERNAL' THEN\n\n                            v_table_ddl := v_table_ddl || '  ' -- note: two char spacer to start, to indent the column\n                              || 'CONSTRAINT' || ' '\n                              || v_constraint_name || ' '\n                              || v_constraint_def\n                              || ',' || E'\\\\n';               \n                        ELSE\n\n                            SELECT 'ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' INTO v_fkey_def\n              \t\t\t        FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and  r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table;\n                            v_fkey_defs = v_fkey_defs || v_fkey_def || E'\\\\n';\n                        END IF;\n                    ELSE\n\n                        v_table_ddl := v_table_ddl || '  ' -- note: two char spacer to start, to indent the column\n                          || 'CONSTRAINT' || ' '\n                          || v_constraint_name || ' '\n                          || v_constraint_def\n                          || ',' || E'\\\\n';           \n                    END IF;\n                    if bVerbose THEN RAISE NOTICE 'DEBUG4: constraint name=% constraint_def=%', v_constraint_name,v_constraint_def; END IF;\n                    constraintarr := constraintarr || v_constraintrec.constraint_name:: text;\n             \n                  END LOOP;\n                ELSE\n                  FOR v_constraintrec IN\n                    SELECT con.conname as constraint_name, con.contype as constraint_type,\n                      CASE\n                        WHEN con.contype = 'p' THEN 1 -- primary key constraint\n                        WHEN con.contype = 'u' THEN 2 -- unique constraint\n                        WHEN con.contype = 'f' THEN 3 -- foreign key constraint\n                        WHEN con.contype = 'c' THEN 4\n                        ELSE 5\n                      END as type_rank,\n                      pg_get_constraintdef(con.oid) as constraint_definition\n                    FROM pg_catalog.pg_constraint con JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace\n                    WHERE nsp.nspname = in_schema AND rel.relname = in_table\n                          --Issue#13 added this condition:\n                          AND con.conparentid = 0\n                          ORDER BY type_rank\n                  LOOP\n                    v_constraint_name := v_constraintrec.constraint_name;\n                    v_constraint_def  := v_constraintrec.constraint_definition;\n                    IF v_constraintrec.type_rank = 1 THEN\n                        IF pkcnt = 0 OR pktype = 'PKEY_INTERNAL' THEN\n                            -- internal def\n                            v_constraint_name := v_constraintrec.constraint_name;\n                            v_constraint_def  := v_constraintrec.constraint_definition;\n                            v_table_ddl := v_table_ddl || '  ' -- note: two char spacer to start, to indent the column\n                              || 'CONSTRAINT' || ' '\n                              || v_constraint_name || ' '\n                              || v_constraint_def\n                              || ',' || E'\\\\n';\n                        ELSE\n                          SELECT 'ALTER TABLE ONLY ' || in_schema || '.' || c.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' INTO v_pkey_def\n                          FROM pg_catalog.pg_constraint r, pg_class c, pg_namespace n where r.conrelid = c.oid and  r.contype = 'p' and n.oid = r.connamespace and n.nspname = in_schema AND c.relname = in_table;             \n                        END IF;\n                        IF bPartition THEN\n                          continue;\n                        END IF;\n                    ELSIF v_constraintrec.type_rank = 3 THEN\n\n                        IF fktype = 'FKEYS_NONE' THEN\n                            -- skip\n                            continue;           \n                        ELSIF fkcnt = 0 OR fktype = 'FKEYS_INTERNAL' THEN\n                            -- internal def\n                            v_table_ddl := v_table_ddl || '  ' -- note: two char spacer to start, to indent the column\n                              || 'CONSTRAINT' || ' '\n                              || v_constraint_name || ' '\n                              || v_constraint_def\n                              || ',' || E'\\\\n';               \n                        ELSE\n\n                            SELECT 'ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' INTO v_fkey_def\n              \t\t\t        FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and  r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table and\n              \t\t\t        r.conname = v_constraint_name and r.conparentid = 0;\n                            v_fkey_defs = v_fkey_defs || v_fkey_def || E'\\\\n';\n                        END IF;\n                    ELSE\n\n                        v_table_ddl := v_table_ddl || '  ' -- note: two char spacer to start, to indent the column\n                          || 'CONSTRAINT' || ' '\n                          || v_constraint_name || ' '\n                          || v_constraint_def\n                          || ',' || E'\\\\n';           \n                    END IF;\n                    if bVerbose THEN RAISE NOTICE 'DEBUG4: constraint name=% constraint_def=%', v_constraint_name,v_constraint_def; END IF;\n                    constraintarr := constraintarr || v_constraintrec.constraint_name:: text;\n             \n                   END LOOP;\n                END IF;     \n            \t\n\n                select substring(v_table_ddl, length(v_table_ddl) - 1, 1) INTO v_temp;\n                IF v_temp = ',' THEN\n                    v_table_ddl = substr(v_table_ddl, 0, length(v_table_ddl) - 1) || E'\\\\n';\n                END IF;\n                IF bVerbose THEN RAISE NOTICE '(3)tabledef so far: %', trim(v_table_ddl); END IF;\n                          \n\n                IF bVerbose THEN RAISE NOTICE '(4)tabledef so far: %', v_table_ddl; END IF;\n                          \n\n                IF bPartition and bInheritance THEN\n\n                  IF v_parent_schema = '' OR v_parent_schema IS NULL THEN v_parent_schema = in_schema; END IF;\n                  v_table_ddl := v_table_ddl || ') INHERITS (' || v_parent_schema || '.' || v_parent || ') ' || E'\\\\n' || v_relopts || ' ' || v_tablespace || ';' || E'\\\\n';\n                END IF;\n                          \n                IF v_pgversion >= 100000 AND NOT bPartition and NOT bInheritance THEN\n                  SELECT pg_get_partkeydef(c1.oid) as partition_key INTO v_partition_key FROM pg_class c1 JOIN pg_namespace n ON (n.oid = c1.relnamespace) LEFT JOIN pg_partitioned_table p ON (c1.oid = p.partrelid)\n                  WHERE n.nspname = in_schema and n.oid = c1.relnamespace and c1.relname = in_table and c1.relkind = 'p';\n                          \n                  IF v_partition_key IS NOT NULL AND v_partition_key <> '' THEN\n                    v_table_ddl := v_table_ddl || ') PARTITION BY ' || v_partition_key || ';' || E'\\\\n'; \n                  ELSEIF v_relopts <> '' THEN\n                    v_table_ddl := v_table_ddl || ') ' || v_relopts || ' ' || v_tablespace || ';' || E'\\\\n'; \n                  ELSE\n\n                    v_table_ddl := v_table_ddl || ') ' || v_tablespace || ';' || E'\\\\n';   \n                  END IF; \n                END IF;\n                          \n                IF bVerbose THEN RAISE NOTICE '(5)tabledef so far: %', v_table_ddl; END IF;\n                                        \n                IF v_pkey_def <> '' THEN\n                    v_table_ddl := v_table_ddl || v_pkey_def || E'\\\\n';   \n                END IF;\n              \n\n                IF v_fkey_defs <> '' THEN\n            \t         v_table_ddl := v_table_ddl || v_fkey_defs || E'\\\\n';   \n                END IF;\n              \n                IF bVerbose THEN RAISE NOTICE '(6)tabledef so far: %', v_table_ddl; END IF;\n              \n                FOR v_indexrec IN\n                  SELECT indexdef, COALESCE(tablespace, 'pg_default') as tablespace, indexname FROM pg_indexes WHERE (schemaname, tablename) = (in_schema, in_table)\n                LOOP\n\n                  bSkip = False;\n                  FOREACH constraintelement IN ARRAY constraintarr\n                  LOOP\n                     IF constraintelement = v_indexrec.indexname THEN\n                         -- RAISE NOTICE 'DEBUG7: skipping index, %', v_indexrec.indexname;\n                         bSkip = True;\n                         EXIT;\n                     END IF;\n                  END LOOP;  \n                  if bSkip THEN CONTINUE; END IF;\n                 \n                  v_indexrec.indexdef := REPLACE(v_indexrec.indexdef, 'CREATE INDEX', 'CREATE INDEX IF NOT EXISTS');\n                  v_indexrec.indexdef := REPLACE(v_indexrec.indexdef, 'CREATE UNIQUE INDEX', 'CREATE UNIQUE INDEX IF NOT EXISTS');\n                  IF v_partition_key IS NOT NULL AND v_partition_key <> '' THEN\n                      v_table_ddl := v_table_ddl || v_indexrec.indexdef || ';' || E'\\\\n';\n                  ELSE\n            \t\t\t\t\tselect CASE WHEN i.indpred IS NOT NULL THEN True ELSE False END INTO v_partial\n            \t\t\t\t\tFROM pg_index i JOIN pg_class c1 ON (i.indexrelid = c1.oid) JOIN pg_class c2 ON (i.indrelid = c2.oid)\n            \t\t\t\t\tWHERE c1.relnamespace::regnamespace::text = in_schema AND c2.relnamespace::regnamespace::text = in_schema AND c2.relname = in_table AND c1.relname = v_indexrec.indexname;\n                      IF v_partial THEN\n                          -- Put tablespace def before WHERE CLAUSE\n                          v_temp = v_indexrec.indexdef;\n                          v_pos = POSITION(' WHERE ' IN v_temp);\n                          v_temp2 = SUBSTRING(v_temp, v_pos);\n                          v_temp  = SUBSTRING(v_temp, 1, v_pos);\n                          v_table_ddl := v_table_ddl || v_temp || ' TABLESPACE ' || v_indexrec.tablespace || v_temp2 || ';' || E'\\\\n';             \n                      ELSE\n                          v_table_ddl := v_table_ddl || v_indexrec.indexdef || ' TABLESPACE ' || v_indexrec.tablespace || ';' || E'\\\\n';\n                      END IF;\n                  END IF;\n                 \n                END LOOP;\n                IF bVerbose THEN RAISE NOTICE '(7)tabledef so far: %', v_table_ddl; END IF;\n                          \n                -- Issue#20: added logic for table and column comments\n                IF  cmtcnt > 0 THEN\n                    FOR v_rec IN\n                      SELECT c.relname, 'COMMENT ON ' || CASE WHEN c.relkind in ('r','p') AND a.attname IS NULL THEN 'TABLE ' WHEN c.relkind in ('r','p') AND a.attname IS NOT NULL THEN 'COLUMN ' WHEN c.relkind = 'f' THEN 'FOREIGN TABLE '\n                             WHEN c.relkind = 'm' THEN 'MATERIALIZED VIEW ' WHEN c.relkind = 'v' THEN 'VIEW ' WHEN c.relkind = 'i' THEN 'INDEX ' WHEN c.relkind = 'S' THEN 'SEQUENCE ' ELSE 'XX' END || n.nspname || '.' ||\n                             CASE WHEN c.relkind in ('r','p') AND a.attname IS NOT NULL THEN quote_ident(c.relname) || '.' || a.attname ELSE quote_ident(c.relname) END || ' IS '   || quote_literal(d.description) || ';' as ddl\n            \t   \t    FROM pg_class c JOIN pg_namespace n ON (n.oid = c.relnamespace) LEFT JOIN pg_description d ON (c.oid = d.objoid) LEFT JOIN pg_attribute a ON (c.oid = a.attrelid AND a.attnum > 0 and a.attnum = d.objsubid)\n            \t   \t    WHERE d.description IS NOT NULL AND n.nspname = in_schema AND c.relname = in_table ORDER BY 2 desc, ddl\n                    LOOP\n                        --RAISE NOTICE 'comments:%', v_rec.ddl;\n                        v_table_ddl = v_table_ddl || v_rec.ddl || E'\\\\n';\n                    END LOOP;  \n                END IF;\n                IF bVerbose THEN RAISE NOTICE '(8)tabledef so far: %', v_table_ddl; END IF;\n            \t\n                IF trigtype = 'INCLUDE_TRIGGERS' THEN\n            \t    -- Issue#14: handle multiple triggers for a table\n                  FOR v_trigrec IN\n                      select pg_get_triggerdef(t.oid, True) || ';' as triggerdef FROM pg_trigger t, pg_class c, pg_namespace n\n                      WHERE n.nspname = in_schema and n.oid = c.relnamespace and c.relname = in_table and c.relkind = 'r' and t.tgrelid = c.oid and NOT t.tgisinternal\n                  LOOP\n                      v_table_ddl := v_table_ddl || v_trigrec.triggerdef;\n                      v_table_ddl := v_table_ddl || E'\\\\n';         \n                      IF bVerbose THEN RAISE NOTICE 'triggerdef = %', v_trigrec.triggerdef; END IF;\n                  END LOOP;       \t   \n                END IF;\n             \n                IF bVerbose THEN RAISE NOTICE '(9)tabledef so far: %', v_table_ddl; END IF;\n                v_table_ddl := v_table_ddl || E'\\\\n';\n                IF bVerbose THEN RAISE NOTICE '(10)tabledef so far: %', v_table_ddl; END IF;\n                IF search_path_old = '' THEN\n                  SELECT set_config('search_path', '', false) into v_temp;\n                ELSE\n                  EXECUTE 'SET search_path = ' || search_path_old;\n                END IF;\n                          \n                RETURN v_table_ddl;\n            \t\n                EXCEPTION\n                WHEN others THEN\n                BEGIN\n                  GET STACKED DIAGNOSTICS v_diag1 = MESSAGE_TEXT, v_diag2 = PG_EXCEPTION_DETAIL, v_diag3 = PG_EXCEPTION_HINT, v_diag4 = RETURNED_SQLSTATE, v_diag5 = PG_CONTEXT, v_diag6 = PG_EXCEPTION_CONTEXT;\n                  v_ret := 'line=' || v_diag6 || '. '|| v_diag4 || '. ' || v_diag1;\n                  RAISE EXCEPTION '%', v_ret;\n                   RETURN '';\n                END;\n                          \n              END;\n            $$;\"\"\".indent(1);\n\n    public static final String DROP_TYPE_SQL = \"DROP TYPE IF EXISTS %s.%s CASCADE;\";\n\n    public static final String ENUM_TYPE_DDL_SQL = \"\"\"\n                                                   SELECT 'CREATE TYPE \"' || n.nspname || '\".\"' || t.typname || '\" AS ENUM (' ||\n                                                       string_agg(quote_literal(e.enumlabel), ', ') || ');' AS ddl\n                                                   FROM pg_type t\n                                                   JOIN pg_enum e ON t.oid = e.enumtypid\n                                                   JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n                                                   WHERE t.typtype = 'e'\n                                                   GROUP BY n.nspname, t.typname;\"\"\";\n\n    public static final String EXPORT_SEQUENCE_DDL_SQL = \"\"\"\n            SELECT n.nspname,\n                   c.relname,\n                   t.typname,\n                   a.rolname,\n                   obj_description(c.oid, 'pg_class') AS comment,\n                   s.seqstart,\n                   s.seqincrement,\n                   s.seqmax,\n                   s.seqmin,\n                   s.seqcycle,\n                   s.seqcache\n            FROM pg_sequence s\n                     JOIN\n                 pg_class c ON c.oid = s.seqrelid\n                     JOIN\n                 pg_namespace n ON n.oid = c.relnamespace\n                     JOIN\n                 pg_roles a ON a.oid = c.relowner\n                     JOIN\n                 pg_type t ON s.seqtypid = t.oid\n            WHERE c.relname = ?\n              AND n.nspname = ?;\n            \"\"\";\n\n    public static final String EXPORT_SEQUENCES_SQL = \"\"\"\n            SELECT c.relname, obj_description(c.oid, 'pg_class') AS comment\n            FROM pg_sequence s\n                     JOIN\n                 pg_class c ON c.oid = s.seqrelid\n                     JOIN\n                 pg_namespace n ON n.oid = c.relnamespace\n            WHERE n.nspname = ?;\n            \"\"\";\n\n\tpublic static final String EXPORT_USERS_SQL = \"\"\"\n\t\t\tSELECT rolname AS username\n\t\t\tFROM pg_roles\n\t\t\tORDER BY rolname;\n\t\t\t\"\"\";\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/consts/SequenceCommonConst.java",
    "content": "package ai.chat2db.plugin.postgresql.consts;\n\n\n/**\n * Sequence operation common constants\n *\n * @author Sylphy\n */\npublic class SequenceCommonConst {\n    private SequenceCommonConst() {\n        throw new IllegalStateException(\"Utility class\");\n    }\n\n    public static final String CREATE_SEQUENCE = \"CREATE SEQUENCE \";\n    public static final String START_WITH = \"\\tSTART WITH \";\n    public static final String INCREMENT_BY = \"\\tINCREMENT BY \";\n    public static final String MAXVALUE = \"\\tMAXVALUE \";\n    public static final String MINVALUE = \"\\tMINVALUE \";\n    public static final String CYCLE = \"\\tCYCLE \";\n    public static final String NO_CYCLE = \"\\tNO CYCLE \";\n    public static final String CACHE = \"\\tCACHE \";\n    public static final String RESTART_WITH = \"\\tRESTART WITH \";\n    public static final String ALTER_SEQUENCE = \"ALTER SEQUENCE \";\n    public static final String COMMENT_ON_SEQUENCE = \"COMMENT ON SEQUENCE \";\n    public static final String RENAME_TO = \" RENAME TO \";\n    public static final String OWNER_TO = \" OWNER TO \";\n    public static final String OWNED_BY = \" OWNED BY \";\n    public static final String IS = \" IS \";\n    public static final String AS = \" AS \";\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/pg.json",
    "content": "{\n  \"dbType\": \"POSTGRESQL\",\n  \"supportDatabase\": true,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:postgresql://localhost:5432/postgres\",\n      \"custom\": false,\n      \"defaultDriver\": true,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/postgresql-42.5.1.jar\"\n      ],\n      \"jdbcDriver\": \"postgresql-42.5.1.jar\",\n      \"jdbcDriverClass\": \"org.postgresql.Driver\"\n    }\n  ],\n  \"name\": \"PostgreSQL\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/type/PostgreSQLCharsetEnum.java",
    "content": "package ai.chat2db.plugin.postgresql.type;\n\nimport ai.chat2db.spi.model.Charset;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum PostgreSQLCharsetEnum {\n    BIG5(\"BIG5\",null),\n    EUC_CN(\"EUC_CN\",null),\n    EUC_JP(\"EUC_JP\",null),\n    EUC_JIS_2004(\"EUC_JIS_2004\",null),\n    EUC_KR(\"EUC_KR\",null),\n    EUC_TW(\"EUC_TW\",null),\n    GB18030(\"GB18030\",null),\n    GBK(\"GBK\",null),\n    ISO_8859_5(\"ISO_8859_5\",null),\n    ISO_8859_6(\"ISO_8859_6\",null),\n    ISO_8859_7(\"ISO_8859_7\",null),\n    ISO_8859_8(\"ISO_8859_8\",null),\n    JOHAB(\"JOHAB\",null),\n    KOI8R(\"KOI8R\",null),\n    KOI8U(\"KOI8U\",null),\n    LATIN1(\"LATIN1\",null),\n    LATIN2(\"LATIN2\",null),\n    LATIN3(\"LATIN3\",null),\n    LATIN4(\"LATIN4\",null),\n    LATIN5(\"LATIN5\",null),\n    LATIN6(\"LATIN6\",null),\n    LATIN7(\"LATIN7\",null),\n    LATIN8(\"LATIN8\",null),\n    LATIN9(\"LATIN9\",null),\n    LATIN10(\"LATIN10\",null),\n    MULE_INTERNAL(\"MULE_INTERNAL\",null),\n    SJIS(\"SJIS\",null),\n    SHIFT_JIS_2004(\"SHIFT_JIS_2004\",null),\n    SQL_ASCII(\"SQL_ASCII\",null),\n    UHC(\"UHC\",null),\n    UTF8(\"UTF8\",null),\n    WIN866(\"WIN866\",null),\n    WIN874(\"WIN874\",null),\n    WIN1250(\"WIN1250\",null),\n    WIN1251(\"WIN1251\",null),\n    WIN1252(\"WIN1252\",null),\n    WIN1253(\"WIN1253\",null),\n    WIN1254(\"WIN1254\",null),\n    WIN1255(\"WIN1255\",null),\n    WIN1256(\"WIN1256\",null),\n    WIN1257(\"WIN1257\",null),\n    WIN1258(\"WIN1258\",null),\n\n    ;\n\n    private Charset charset;\n\n    PostgreSQLCharsetEnum(String charsetName, String defaultCollationName) {\n        this.charset = new Charset(charsetName, defaultCollationName);\n    }\n\n    public static List<Charset> getCharsets() {\n        return Arrays.stream(PostgreSQLCharsetEnum.values()).map(PostgreSQLCharsetEnum::getCharset).collect(java.util.stream.Collectors.toList());\n    }\n\n    public Charset getCharset() {\n        return charset;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/type/PostgreSQLCollationEnum.java",
    "content": "package ai.chat2db.plugin.postgresql.type;\n\nimport ai.chat2db.spi.model.Collation;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum PostgreSQLCollationEnum {\n\n    COLLATION1(\"default\"),\n    COLLATION2(\"C\"),\n    COLLATION3(\"POSIX\"),\n    COLLATION4(\"ucs_basic\"),\n    COLLATION5(\"unicode\"),\n    COLLATION6(\"und-x-icu\"),\n    COLLATION7(\"af-x-icu\"),\n    COLLATION8(\"af-NA-x-icu\"),\n    COLLATION9(\"af-ZA-x-icu\"),\n    COLLATION10(\"agq-x-icu\"),\n    COLLATION11(\"agq-CM-x-icu\"),\n    COLLATION12(\"ak-x-icu\"),\n    COLLATION13(\"ak-GH-x-icu\"),\n    COLLATION14(\"am-x-icu\"),\n    COLLATION15(\"am-ET-x-icu\"),\n    COLLATION16(\"ar-x-icu\"),\n    COLLATION17(\"ar-001-x-icu\"),\n    COLLATION18(\"ar-AE-x-icu\"),\n    COLLATION19(\"ar-BH-x-icu\"),\n    COLLATION20(\"ar-DJ-x-icu\"),\n    COLLATION21(\"ar-DZ-x-icu\"),\n    COLLATION22(\"ar-EG-x-icu\"),\n    COLLATION23(\"ar-EH-x-icu\"),\n    COLLATION24(\"ar-ER-x-icu\"),\n    COLLATION25(\"ar-IL-x-icu\"),\n    COLLATION26(\"ar-IQ-x-icu\"),\n    COLLATION27(\"ar-JO-x-icu\"),\n    COLLATION28(\"ar-KM-x-icu\"),\n    COLLATION29(\"ar-KW-x-icu\"),\n    COLLATION30(\"ar-LB-x-icu\"),\n    COLLATION31(\"ar-LY-x-icu\"),\n    COLLATION32(\"ar-MA-x-icu\"),\n    COLLATION33(\"ar-MR-x-icu\"),\n    COLLATION34(\"ar-OM-x-icu\"),\n    COLLATION35(\"ar-PS-x-icu\"),\n    COLLATION36(\"ar-QA-x-icu\"),\n    COLLATION37(\"ar-SA-x-icu\"),\n    COLLATION38(\"ar-SD-x-icu\"),\n    COLLATION39(\"ar-SO-x-icu\"),\n    COLLATION40(\"ar-SS-x-icu\"),\n    COLLATION41(\"ar-SY-x-icu\"),\n    COLLATION42(\"ar-TD-x-icu\"),\n    COLLATION43(\"ar-TN-x-icu\"),\n    COLLATION44(\"ar-YE-x-icu\"),\n    COLLATION45(\"as-x-icu\"),\n    COLLATION46(\"as-IN-x-icu\"),\n    COLLATION47(\"asa-x-icu\"),\n    COLLATION48(\"asa-TZ-x-icu\"),\n    COLLATION49(\"ast-x-icu\"),\n    COLLATION50(\"ast-ES-x-icu\"),\n    COLLATION51(\"az-x-icu\"),\n    COLLATION52(\"az-Cyrl-x-icu\"),\n    COLLATION53(\"az-Cyrl-AZ-x-icu\"),\n    COLLATION54(\"az-Latn-x-icu\"),\n    COLLATION55(\"az-Latn-AZ-x-icu\"),\n    COLLATION56(\"bas-x-icu\"),\n    COLLATION57(\"bas-CM-x-icu\"),\n    COLLATION58(\"be-x-icu\"),\n    COLLATION59(\"be-BY-x-icu\"),\n    COLLATION60(\"bem-x-icu\"),\n    COLLATION61(\"bem-ZM-x-icu\"),\n    COLLATION62(\"bez-x-icu\"),\n    COLLATION63(\"bez-TZ-x-icu\"),\n    COLLATION64(\"bg-x-icu\"),\n    COLLATION65(\"bg-BG-x-icu\"),\n    COLLATION66(\"bm-x-icu\"),\n    COLLATION67(\"bm-ML-x-icu\"),\n    COLLATION68(\"bn-x-icu\"),\n    COLLATION69(\"bn-BD-x-icu\"),\n    COLLATION70(\"bn-IN-x-icu\"),\n    COLLATION71(\"bo-x-icu\"),\n    COLLATION72(\"bo-CN-x-icu\"),\n    COLLATION73(\"bo-IN-x-icu\"),\n    COLLATION74(\"br-x-icu\"),\n    COLLATION75(\"br-FR-x-icu\"),\n    COLLATION76(\"brx-x-icu\"),\n    COLLATION77(\"brx-IN-x-icu\"),\n    COLLATION78(\"bs-x-icu\"),\n    COLLATION79(\"bs-Cyrl-x-icu\"),\n    COLLATION80(\"bs-Cyrl-BA-x-icu\"),\n    COLLATION81(\"bs-Latn-x-icu\"),\n    COLLATION82(\"bs-Latn-BA-x-icu\"),\n    COLLATION83(\"ca-x-icu\"),\n    COLLATION84(\"ca-AD-x-icu\"),\n    COLLATION85(\"ca-ES-x-icu\"),\n    COLLATION86(\"ca-FR-x-icu\"),\n    COLLATION87(\"ca-IT-x-icu\"),\n    COLLATION88(\"ccp-x-icu\"),\n    COLLATION89(\"ccp-BD-x-icu\"),\n    COLLATION90(\"ccp-IN-x-icu\"),\n    COLLATION91(\"ce-x-icu\"),\n    COLLATION92(\"ce-RU-x-icu\"),\n    COLLATION93(\"ceb-x-icu\"),\n    COLLATION94(\"ceb-PH-x-icu\"),\n    COLLATION95(\"cgg-x-icu\"),\n    COLLATION96(\"cgg-UG-x-icu\"),\n    COLLATION97(\"chr-x-icu\"),\n    COLLATION98(\"chr-US-x-icu\"),\n    COLLATION99(\"ckb-x-icu\"),\n    COLLATION100(\"ckb-IQ-x-icu\"),\n    COLLATION101(\"ckb-IR-x-icu\"),\n    COLLATION102(\"cs-x-icu\"),\n    COLLATION103(\"cs-CZ-x-icu\"),\n    COLLATION104(\"cy-x-icu\"),\n    COLLATION105(\"cy-GB-x-icu\"),\n    COLLATION106(\"da-x-icu\"),\n    COLLATION107(\"da-DK-x-icu\"),\n    COLLATION108(\"da-GL-x-icu\"),\n    COLLATION109(\"dav-x-icu\"),\n    COLLATION110(\"dav-KE-x-icu\"),\n    COLLATION111(\"de-x-icu\"),\n    COLLATION112(\"de-AT-x-icu\"),\n    COLLATION113(\"de-BE-x-icu\"),\n    COLLATION114(\"de-CH-x-icu\"),\n    COLLATION115(\"de-DE-x-icu\"),\n    COLLATION116(\"de-IT-x-icu\"),\n    COLLATION117(\"id-x-icu\"),\n    COLLATION118(\"de-LI-x-icu\"),\n    COLLATION119(\"de-LU-x-icu\"),\n    COLLATION120(\"dje-x-icu\"),\n    COLLATION121(\"dje-NE-x-icu\"),\n    COLLATION122(\"dsb-x-icu\"),\n    COLLATION123(\"dsb-DE-x-icu\"),\n    COLLATION124(\"dua-x-icu\"),\n    COLLATION125(\"dua-CM-x-icu\"),\n    COLLATION126(\"dyo-x-icu\"),\n    COLLATION127(\"dyo-SN-x-icu\"),\n    COLLATION128(\"dz-x-icu\"),\n    COLLATION129(\"dz-BT-x-icu\"),\n    COLLATION130(\"ebu-x-icu\"),\n    COLLATION131(\"ebu-KE-x-icu\"),\n    COLLATION132(\"ee-x-icu\"),\n    COLLATION133(\"ee-GH-x-icu\"),\n    COLLATION134(\"ee-TG-x-icu\"),\n    COLLATION135(\"el-x-icu\"),\n    COLLATION136(\"el-CY-x-icu\"),\n    COLLATION137(\"el-GR-x-icu\"),\n    COLLATION138(\"en-x-icu\"),\n    COLLATION139(\"en-001-x-icu\"),\n    COLLATION140(\"en-150-x-icu\"),\n    COLLATION141(\"en-AE-x-icu\"),\n    COLLATION142(\"en-AG-x-icu\"),\n    COLLATION143(\"en-AI-x-icu\"),\n    COLLATION144(\"en-AS-x-icu\"),\n    COLLATION145(\"en-AT-x-icu\"),\n    COLLATION146(\"en-AU-x-icu\"),\n    COLLATION147(\"en-BB-x-icu\"),\n    COLLATION148(\"en-BE-x-icu\"),\n    COLLATION149(\"en-BI-x-icu\"),\n    COLLATION150(\"en-BM-x-icu\"),\n    COLLATION151(\"en-BS-x-icu\"),\n    COLLATION152(\"en-BW-x-icu\"),\n    COLLATION153(\"en-BZ-x-icu\"),\n    COLLATION154(\"en-CA-x-icu\"),\n    COLLATION155(\"en-CC-x-icu\"),\n    COLLATION156(\"en-CH-x-icu\"),\n    COLLATION157(\"en-CK-x-icu\"),\n    COLLATION158(\"en-CM-x-icu\"),\n    COLLATION159(\"en-CX-x-icu\"),\n    COLLATION160(\"en-CY-x-icu\"),\n    COLLATION161(\"en-DE-x-icu\"),\n    COLLATION162(\"en-DG-x-icu\"),\n    COLLATION163(\"en-DK-x-icu\"),\n    COLLATION164(\"en-DM-x-icu\"),\n    COLLATION165(\"en-ER-x-icu\"),\n    COLLATION166(\"en-FI-x-icu\"),\n    COLLATION167(\"en-FJ-x-icu\"),\n    COLLATION168(\"en-FK-x-icu\"),\n    COLLATION169(\"en-FM-x-icu\"),\n    COLLATION170(\"en-GB-x-icu\"),\n    COLLATION171(\"en-GD-x-icu\"),\n    COLLATION172(\"en-GG-x-icu\"),\n    COLLATION173(\"en-GH-x-icu\"),\n    COLLATION174(\"en-GI-x-icu\"),\n    COLLATION175(\"en-GM-x-icu\"),\n    COLLATION176(\"en-GU-x-icu\"),\n    COLLATION177(\"en-GY-x-icu\"),\n    COLLATION178(\"en-HK-x-icu\"),\n    COLLATION179(\"en-IE-x-icu\"),\n    COLLATION180(\"en-IL-x-icu\"),\n    COLLATION181(\"en-IM-x-icu\"),\n    COLLATION182(\"en-IN-x-icu\"),\n    COLLATION183(\"en-IO-x-icu\"),\n    COLLATION184(\"en-JE-x-icu\"),\n    COLLATION185(\"en-JM-x-icu\"),\n    COLLATION186(\"en-KE-x-icu\"),\n    COLLATION187(\"en-KI-x-icu\"),\n    COLLATION188(\"en-KN-x-icu\"),\n    COLLATION189(\"en-KY-x-icu\"),\n    COLLATION190(\"en-LC-x-icu\"),\n    COLLATION191(\"en-LR-x-icu\"),\n    COLLATION192(\"en-LS-x-icu\"),\n    COLLATION193(\"en-MG-x-icu\"),\n    COLLATION194(\"en-MH-x-icu\"),\n    COLLATION195(\"en-MO-x-icu\"),\n    COLLATION196(\"en-MP-x-icu\"),\n    COLLATION197(\"en-MS-x-icu\"),\n    COLLATION198(\"en-MT-x-icu\"),\n    COLLATION199(\"en-MU-x-icu\"),\n    COLLATION200(\"en-MW-x-icu\"),\n    COLLATION201(\"en-MY-x-icu\"),\n    COLLATION202(\"en-NA-x-icu\"),\n    COLLATION203(\"en-NF-x-icu\"),\n    COLLATION204(\"en-NG-x-icu\"),\n    COLLATION205(\"en-NL-x-icu\"),\n    COLLATION206(\"en-NR-x-icu\"),\n    COLLATION207(\"en-NU-x-icu\"),\n    COLLATION208(\"en-NZ-x-icu\"),\n    COLLATION209(\"en-PG-x-icu\"),\n    COLLATION210(\"en-PH-x-icu\"),\n    COLLATION211(\"en-PK-x-icu\"),\n    COLLATION212(\"en-PN-x-icu\"),\n    COLLATION213(\"en-PR-x-icu\"),\n    COLLATION214(\"en-PW-x-icu\"),\n    COLLATION215(\"en-RW-x-icu\"),\n    COLLATION216(\"en-SB-x-icu\"),\n    COLLATION217(\"en-SC-x-icu\"),\n    COLLATION218(\"en-SD-x-icu\"),\n    COLLATION219(\"en-SE-x-icu\"),\n    COLLATION220(\"en-SG-x-icu\"),\n    COLLATION221(\"en-SH-x-icu\"),\n    COLLATION222(\"en-SI-x-icu\"),\n    COLLATION223(\"en-SL-x-icu\"),\n    COLLATION224(\"en-SS-x-icu\"),\n    COLLATION225(\"en-SX-x-icu\"),\n    COLLATION226(\"en-SZ-x-icu\"),\n    COLLATION227(\"en-TC-x-icu\"),\n    COLLATION228(\"en-TK-x-icu\"),\n    COLLATION229(\"en-TO-x-icu\"),\n    COLLATION230(\"en-TT-x-icu\"),\n    COLLATION231(\"en-TV-x-icu\"),\n    COLLATION232(\"en-TZ-x-icu\"),\n    COLLATION233(\"en-UG-x-icu\"),\n    COLLATION234(\"en-UM-x-icu\"),\n    COLLATION235(\"en-US-x-icu\"),\n    COLLATION236(\"en-US-u-va-posix-x-icu\"),\n    COLLATION237(\"en-VC-x-icu\"),\n    COLLATION238(\"en-VG-x-icu\"),\n    COLLATION239(\"en-VI-x-icu\"),\n    COLLATION240(\"en-VU-x-icu\"),\n    COLLATION241(\"en-WS-x-icu\"),\n    COLLATION242(\"en-ZA-x-icu\"),\n    COLLATION243(\"en-ZM-x-icu\"),\n    COLLATION244(\"en-ZW-x-icu\"),\n    COLLATION245(\"eo-x-icu\"),\n    COLLATION246(\"eo-001-x-icu\"),\n    COLLATION247(\"es-x-icu\"),\n    COLLATION248(\"es-419-x-icu\"),\n    COLLATION249(\"es-AR-x-icu\"),\n    COLLATION250(\"es-BO-x-icu\"),\n    COLLATION251(\"es-BR-x-icu\"),\n    COLLATION252(\"es-BZ-x-icu\"),\n    COLLATION253(\"es-CL-x-icu\"),\n    COLLATION254(\"es-CO-x-icu\"),\n    COLLATION255(\"es-CR-x-icu\"),\n    COLLATION256(\"es-CU-x-icu\"),\n    COLLATION257(\"es-DO-x-icu\"),\n    COLLATION258(\"es-EA-x-icu\"),\n    COLLATION259(\"es-EC-x-icu\"),\n    COLLATION260(\"es-ES-x-icu\"),\n    COLLATION261(\"es-GQ-x-icu\"),\n    COLLATION262(\"es-GT-x-icu\"),\n    COLLATION263(\"es-HN-x-icu\"),\n    COLLATION264(\"es-IC-x-icu\"),\n    COLLATION265(\"es-MX-x-icu\"),\n    COLLATION266(\"es-NI-x-icu\"),\n    COLLATION267(\"es-PA-x-icu\"),\n    COLLATION268(\"es-PE-x-icu\"),\n    COLLATION269(\"es-PH-x-icu\"),\n    COLLATION270(\"es-PR-x-icu\"),\n    COLLATION271(\"es-PY-x-icu\"),\n    COLLATION272(\"es-SV-x-icu\"),\n    COLLATION273(\"es-US-x-icu\"),\n    COLLATION274(\"es-UY-x-icu\"),\n    COLLATION275(\"es-VE-x-icu\"),\n    COLLATION276(\"et-x-icu\"),\n    COLLATION277(\"et-EE-x-icu\"),\n    COLLATION278(\"eu-x-icu\"),\n    COLLATION279(\"eu-ES-x-icu\"),\n    COLLATION280(\"ewo-x-icu\"),\n    COLLATION281(\"ewo-CM-x-icu\"),\n    COLLATION282(\"fa-x-icu\"),\n    COLLATION283(\"fa-AF-x-icu\"),\n    COLLATION284(\"fa-IR-x-icu\"),\n    COLLATION285(\"ff-x-icu\"),\n    COLLATION286(\"ff-Adlm-x-icu\"),\n    COLLATION287(\"ff-Adlm-BF-x-icu\"),\n    COLLATION288(\"ff-Adlm-CM-x-icu\"),\n    COLLATION289(\"ff-Adlm-GH-x-icu\"),\n    COLLATION290(\"ff-Adlm-GM-x-icu\"),\n    COLLATION291(\"ff-Adlm-GN-x-icu\"),\n    COLLATION292(\"ff-Adlm-GW-x-icu\"),\n    COLLATION293(\"ff-Adlm-LR-x-icu\"),\n    COLLATION294(\"ff-Adlm-MR-x-icu\"),\n    COLLATION295(\"ff-Adlm-NE-x-icu\"),\n    COLLATION296(\"ff-Adlm-NG-x-icu\"),\n    COLLATION297(\"ff-Adlm-SL-x-icu\"),\n    COLLATION298(\"ff-Adlm-SN-x-icu\"),\n    COLLATION299(\"ff-Latn-x-icu\"),\n    COLLATION300(\"ff-Latn-BF-x-icu\"),\n    COLLATION301(\"ff-Latn-CM-x-icu\"),\n    COLLATION302(\"ff-Latn-GH-x-icu\"),\n    COLLATION303(\"ff-Latn-GM-x-icu\"),\n    COLLATION304(\"ff-Latn-GN-x-icu\"),\n    COLLATION305(\"ff-Latn-GW-x-icu\"),\n    COLLATION306(\"ff-Latn-LR-x-icu\"),\n    COLLATION307(\"ff-Latn-MR-x-icu\"),\n    COLLATION308(\"ff-Latn-NE-x-icu\"),\n    COLLATION309(\"ff-Latn-NG-x-icu\"),\n    COLLATION310(\"ff-Latn-SL-x-icu\"),\n    COLLATION311(\"ff-Latn-SN-x-icu\"),\n    COLLATION312(\"fi-x-icu\"),\n    COLLATION313(\"fi-FI-x-icu\"),\n    COLLATION314(\"fil-x-icu\"),\n    COLLATION315(\"fil-PH-x-icu\"),\n    COLLATION316(\"fo-x-icu\"),\n    COLLATION317(\"fo-DK-x-icu\"),\n    COLLATION318(\"fo-FO-x-icu\"),\n    COLLATION319(\"fr-x-icu\"),\n    COLLATION320(\"fr-BE-x-icu\"),\n    COLLATION321(\"fr-BF-x-icu\"),\n    COLLATION322(\"fr-BI-x-icu\"),\n    COLLATION323(\"fr-BJ-x-icu\"),\n    COLLATION324(\"fr-BL-x-icu\"),\n    COLLATION325(\"fr-CA-x-icu\"),\n    COLLATION326(\"fr-CD-x-icu\"),\n    COLLATION327(\"fr-CF-x-icu\"),\n    COLLATION328(\"fr-CG-x-icu\"),\n    COLLATION329(\"fr-CH-x-icu\"),\n    COLLATION330(\"fr-CI-x-icu\"),\n    COLLATION331(\"fr-CM-x-icu\"),\n    COLLATION332(\"fr-DJ-x-icu\"),\n    COLLATION333(\"fr-DZ-x-icu\"),\n    COLLATION334(\"fr-FR-x-icu\"),\n    COLLATION335(\"fr-GA-x-icu\"),\n    COLLATION336(\"fr-GF-x-icu\"),\n    COLLATION337(\"fr-GN-x-icu\"),\n    COLLATION338(\"fr-GP-x-icu\"),\n    COLLATION339(\"fr-GQ-x-icu\"),\n    COLLATION340(\"fr-HT-x-icu\"),\n    COLLATION341(\"fr-KM-x-icu\"),\n    COLLATION342(\"fr-LU-x-icu\"),\n    COLLATION343(\"fr-MA-x-icu\"),\n    COLLATION344(\"fr-MC-x-icu\"),\n    COLLATION345(\"fr-MF-x-icu\"),\n    COLLATION346(\"fr-MG-x-icu\"),\n    COLLATION347(\"fr-ML-x-icu\"),\n    COLLATION348(\"fr-MQ-x-icu\"),\n    COLLATION349(\"fr-MR-x-icu\"),\n    COLLATION350(\"fr-MU-x-icu\"),\n    COLLATION351(\"fr-NC-x-icu\"),\n    COLLATION352(\"fr-NE-x-icu\"),\n    COLLATION353(\"fr-PF-x-icu\"),\n    COLLATION354(\"fr-PM-x-icu\"),\n    COLLATION355(\"fr-RE-x-icu\"),\n    COLLATION356(\"fr-RW-x-icu\"),\n    COLLATION357(\"fr-SC-x-icu\"),\n    COLLATION358(\"fr-SN-x-icu\"),\n    COLLATION359(\"fr-SY-x-icu\"),\n    COLLATION360(\"fr-TD-x-icu\"),\n    COLLATION361(\"fr-TG-x-icu\"),\n    COLLATION362(\"fr-TN-x-icu\"),\n    COLLATION363(\"fr-VU-x-icu\"),\n    COLLATION364(\"fr-WF-x-icu\"),\n    COLLATION365(\"fr-YT-x-icu\"),\n    COLLATION366(\"fur-x-icu\"),\n    COLLATION367(\"fur-IT-x-icu\"),\n    COLLATION368(\"fy-x-icu\"),\n    COLLATION369(\"fy-NL-x-icu\"),\n    COLLATION370(\"ga-x-icu\"),\n    COLLATION371(\"ga-GB-x-icu\"),\n    COLLATION372(\"ga-IE-x-icu\"),\n    COLLATION373(\"gd-x-icu\"),\n    COLLATION374(\"gd-GB-x-icu\"),\n    COLLATION375(\"gl-x-icu\"),\n    COLLATION376(\"gl-ES-x-icu\"),\n    COLLATION377(\"gsw-x-icu\"),\n    COLLATION378(\"gsw-CH-x-icu\"),\n    COLLATION379(\"gsw-FR-x-icu\"),\n    COLLATION380(\"gsw-LI-x-icu\"),\n    COLLATION381(\"gu-x-icu\"),\n    COLLATION382(\"gu-IN-x-icu\"),\n    COLLATION383(\"guz-x-icu\"),\n    COLLATION384(\"guz-KE-x-icu\"),\n    COLLATION385(\"gv-x-icu\"),\n    COLLATION386(\"gv-IM-x-icu\"),\n    COLLATION387(\"ha-x-icu\"),\n    COLLATION388(\"ha-GH-x-icu\"),\n    COLLATION389(\"ha-NE-x-icu\"),\n    COLLATION390(\"ha-NG-x-icu\"),\n    COLLATION391(\"haw-x-icu\"),\n    COLLATION392(\"haw-US-x-icu\"),\n    COLLATION393(\"he-x-icu\"),\n    COLLATION394(\"he-IL-x-icu\"),\n    COLLATION395(\"hi-x-icu\"),\n    COLLATION396(\"hi-IN-x-icu\"),\n    COLLATION397(\"hr-x-icu\"),\n    COLLATION398(\"hr-BA-x-icu\"),\n    COLLATION399(\"hr-HR-x-icu\"),\n    COLLATION400(\"hsb-x-icu\"),\n    COLLATION401(\"hsb-DE-x-icu\"),\n    COLLATION402(\"hu-x-icu\"),\n    COLLATION403(\"hu-HU-x-icu\"),\n    COLLATION404(\"hy-x-icu\"),\n    COLLATION405(\"hy-AM-x-icu\"),\n    COLLATION406(\"ia-x-icu\"),\n    COLLATION407(\"ia-001-x-icu\"),\n    COLLATION408(\"id-ID-x-icu\"),\n    COLLATION409(\"ig-x-icu\"),\n    COLLATION410(\"ig-NG-x-icu\"),\n    COLLATION411(\"ii-x-icu\"),\n    COLLATION412(\"ii-CN-x-icu\"),\n    COLLATION413(\"is-x-icu\"),\n    COLLATION414(\"is-IS-x-icu\"),\n    COLLATION415(\"it-x-icu\"),\n    COLLATION416(\"it-CH-x-icu\"),\n    COLLATION417(\"it-IT-x-icu\"),\n    COLLATION418(\"it-SM-x-icu\"),\n    COLLATION419(\"it-VA-x-icu\"),\n    COLLATION420(\"ja-x-icu\"),\n    COLLATION421(\"ja-JP-x-icu\"),\n    COLLATION422(\"jgo-x-icu\"),\n    COLLATION423(\"jgo-CM-x-icu\"),\n    COLLATION424(\"jmc-x-icu\"),\n    COLLATION425(\"jmc-TZ-x-icu\"),\n    COLLATION426(\"jv-x-icu\"),\n    COLLATION427(\"jv-ID-x-icu\"),\n    COLLATION428(\"ka-x-icu\"),\n    COLLATION429(\"ka-GE-x-icu\"),\n    COLLATION430(\"kab-x-icu\"),\n    COLLATION431(\"kab-DZ-x-icu\"),\n    COLLATION432(\"kam-x-icu\"),\n    COLLATION433(\"kam-KE-x-icu\"),\n    COLLATION434(\"kde-x-icu\"),\n    COLLATION435(\"kde-TZ-x-icu\"),\n    COLLATION436(\"kea-x-icu\"),\n    COLLATION437(\"kea-CV-x-icu\"),\n    COLLATION438(\"khq-x-icu\"),\n    COLLATION439(\"khq-ML-x-icu\"),\n    COLLATION440(\"ki-x-icu\"),\n    COLLATION441(\"ki-KE-x-icu\"),\n    COLLATION442(\"kk-x-icu\"),\n    COLLATION443(\"kk-KZ-x-icu\"),\n    COLLATION444(\"kkj-x-icu\"),\n    COLLATION445(\"kkj-CM-x-icu\"),\n    COLLATION446(\"kl-x-icu\"),\n    COLLATION447(\"kl-GL-x-icu\"),\n    COLLATION448(\"kln-x-icu\"),\n    COLLATION449(\"kln-KE-x-icu\"),\n    COLLATION450(\"km-x-icu\"),\n    COLLATION451(\"km-KH-x-icu\"),\n    COLLATION452(\"kn-x-icu\"),\n    COLLATION453(\"kn-IN-x-icu\"),\n    COLLATION454(\"ko-x-icu\"),\n    COLLATION455(\"ko-KP-x-icu\"),\n    COLLATION456(\"ko-KR-x-icu\"),\n    COLLATION457(\"kok-x-icu\"),\n    COLLATION458(\"kok-IN-x-icu\"),\n    COLLATION459(\"ks-x-icu\"),\n    COLLATION460(\"ks-Arab-x-icu\"),\n    COLLATION461(\"ks-Arab-IN-x-icu\"),\n    COLLATION462(\"ksb-x-icu\"),\n    COLLATION463(\"ksb-TZ-x-icu\"),\n    COLLATION464(\"ksf-x-icu\"),\n    COLLATION465(\"ksf-CM-x-icu\"),\n    COLLATION466(\"ksh-x-icu\"),\n    COLLATION467(\"ksh-DE-x-icu\"),\n    COLLATION468(\"ku-x-icu\"),\n    COLLATION469(\"ku-TR-x-icu\"),\n    COLLATION470(\"kw-x-icu\"),\n    COLLATION471(\"kw-GB-x-icu\"),\n    COLLATION472(\"ky-x-icu\"),\n    COLLATION473(\"ky-KG-x-icu\"),\n    COLLATION474(\"lag-x-icu\"),\n    COLLATION475(\"lag-TZ-x-icu\"),\n    COLLATION476(\"lb-x-icu\"),\n    COLLATION477(\"lb-LU-x-icu\"),\n    COLLATION478(\"lg-x-icu\"),\n    COLLATION479(\"lg-UG-x-icu\"),\n    COLLATION480(\"lkt-x-icu\"),\n    COLLATION481(\"lkt-US-x-icu\"),\n    COLLATION482(\"ln-x-icu\"),\n    COLLATION483(\"ln-AO-x-icu\"),\n    COLLATION484(\"ln-CD-x-icu\"),\n    COLLATION485(\"ln-CF-x-icu\"),\n    COLLATION486(\"ln-CG-x-icu\"),\n    COLLATION487(\"lo-x-icu\"),\n    COLLATION488(\"lo-LA-x-icu\"),\n    COLLATION489(\"lrc-x-icu\"),\n    COLLATION490(\"lrc-IQ-x-icu\"),\n    COLLATION491(\"lrc-IR-x-icu\"),\n    COLLATION492(\"lt-x-icu\"),\n    COLLATION493(\"lt-LT-x-icu\"),\n    COLLATION494(\"lu-x-icu\"),\n    COLLATION495(\"lu-CD-x-icu\"),\n    COLLATION496(\"luo-x-icu\"),\n    COLLATION497(\"luo-KE-x-icu\"),\n    COLLATION498(\"luy-x-icu\"),\n    COLLATION499(\"luy-KE-x-icu\"),\n    COLLATION500(\"lv-x-icu\"),\n    COLLATION501(\"lv-LV-x-icu\"),\n    COLLATION502(\"mai-x-icu\"),\n    COLLATION503(\"mai-IN-x-icu\"),\n    COLLATION504(\"mas-x-icu\"),\n    COLLATION505(\"mas-KE-x-icu\"),\n    COLLATION506(\"mas-TZ-x-icu\"),\n    COLLATION507(\"mer-x-icu\"),\n    COLLATION508(\"mer-KE-x-icu\"),\n    COLLATION509(\"mfe-x-icu\"),\n    COLLATION510(\"mfe-MU-x-icu\"),\n    COLLATION511(\"mg-x-icu\"),\n    COLLATION512(\"mg-MG-x-icu\"),\n    COLLATION513(\"mgh-x-icu\"),\n    COLLATION514(\"mgh-MZ-x-icu\"),\n    COLLATION515(\"mgo-x-icu\"),\n    COLLATION516(\"mgo-CM-x-icu\"),\n    COLLATION517(\"mi-x-icu\"),\n    COLLATION518(\"mi-NZ-x-icu\"),\n    COLLATION519(\"mk-x-icu\"),\n    COLLATION520(\"mk-MK-x-icu\"),\n    COLLATION521(\"ml-x-icu\"),\n    COLLATION522(\"ml-IN-x-icu\"),\n    COLLATION523(\"mn-x-icu\"),\n    COLLATION524(\"mn-MN-x-icu\"),\n    COLLATION525(\"mni-x-icu\"),\n    COLLATION526(\"mni-Beng-x-icu\"),\n    COLLATION527(\"mni-Beng-IN-x-icu\"),\n    COLLATION528(\"mr-x-icu\"),\n    COLLATION529(\"mr-IN-x-icu\"),\n    COLLATION530(\"ms-x-icu\"),\n    COLLATION531(\"ms-BN-x-icu\"),\n    COLLATION532(\"ms-ID-x-icu\"),\n    COLLATION533(\"ms-MY-x-icu\"),\n    COLLATION534(\"ms-SG-x-icu\"),\n    COLLATION535(\"mt-x-icu\"),\n    COLLATION536(\"mt-MT-x-icu\"),\n    COLLATION537(\"mua-x-icu\"),\n    COLLATION538(\"mua-CM-x-icu\"),\n    COLLATION539(\"my-x-icu\"),\n    COLLATION540(\"my-MM-x-icu\"),\n    COLLATION541(\"mzn-x-icu\"),\n    COLLATION542(\"mzn-IR-x-icu\"),\n    COLLATION543(\"naq-x-icu\"),\n    COLLATION544(\"naq-NA-x-icu\"),\n    COLLATION545(\"nb-x-icu\"),\n    COLLATION546(\"nb-NO-x-icu\"),\n    COLLATION547(\"nb-SJ-x-icu\"),\n    COLLATION548(\"nd-x-icu\"),\n    COLLATION549(\"nd-ZW-x-icu\"),\n    COLLATION550(\"nds-x-icu\"),\n    COLLATION551(\"nds-DE-x-icu\"),\n    COLLATION552(\"nds-NL-x-icu\"),\n    COLLATION553(\"ne-x-icu\"),\n    COLLATION554(\"ne-IN-x-icu\"),\n    COLLATION555(\"ne-NP-x-icu\"),\n    COLLATION556(\"nl-x-icu\"),\n    COLLATION557(\"nl-AW-x-icu\"),\n    COLLATION558(\"nl-BE-x-icu\"),\n    COLLATION559(\"nl-BQ-x-icu\"),\n    COLLATION560(\"nl-CW-x-icu\"),\n    COLLATION561(\"nl-NL-x-icu\"),\n    COLLATION562(\"nl-SR-x-icu\"),\n    COLLATION563(\"nl-SX-x-icu\"),\n    COLLATION564(\"nmg-x-icu\"),\n    COLLATION565(\"nmg-CM-x-icu\"),\n    COLLATION566(\"nn-x-icu\"),\n    COLLATION567(\"nn-NO-x-icu\"),\n    COLLATION568(\"nnh-x-icu\"),\n    COLLATION569(\"nnh-CM-x-icu\"),\n    COLLATION570(\"nus-x-icu\"),\n    COLLATION571(\"nus-SS-x-icu\"),\n    COLLATION572(\"nyn-x-icu\"),\n    COLLATION573(\"nyn-UG-x-icu\"),\n    COLLATION574(\"om-x-icu\"),\n    COLLATION575(\"om-ET-x-icu\"),\n    COLLATION576(\"om-KE-x-icu\"),\n    COLLATION577(\"or-x-icu\"),\n    COLLATION578(\"or-IN-x-icu\"),\n    COLLATION579(\"os-x-icu\"),\n    COLLATION580(\"os-GE-x-icu\"),\n    COLLATION581(\"os-RU-x-icu\"),\n    COLLATION582(\"pa-x-icu\"),\n    COLLATION583(\"pa-Arab-x-icu\"),\n    COLLATION584(\"pa-Arab-PK-x-icu\"),\n    COLLATION585(\"pa-Guru-x-icu\"),\n    COLLATION586(\"pa-Guru-IN-x-icu\"),\n    COLLATION587(\"pcm-x-icu\"),\n    COLLATION588(\"pcm-NG-x-icu\"),\n    COLLATION589(\"pl-x-icu\"),\n    COLLATION590(\"pl-PL-x-icu\"),\n    COLLATION591(\"ps-x-icu\"),\n    COLLATION592(\"ps-AF-x-icu\"),\n    COLLATION593(\"ps-PK-x-icu\"),\n    COLLATION594(\"pt-x-icu\"),\n    COLLATION595(\"pt-AO-x-icu\"),\n    COLLATION596(\"pt-BR-x-icu\"),\n    COLLATION597(\"pt-CH-x-icu\"),\n    COLLATION598(\"pt-CV-x-icu\"),\n    COLLATION599(\"pt-GQ-x-icu\"),\n    COLLATION600(\"pt-GW-x-icu\"),\n    COLLATION601(\"pt-LU-x-icu\"),\n    COLLATION602(\"pt-MO-x-icu\"),\n    COLLATION603(\"pt-MZ-x-icu\"),\n    COLLATION604(\"pt-PT-x-icu\"),\n    COLLATION605(\"pt-ST-x-icu\"),\n    COLLATION606(\"pt-TL-x-icu\"),\n    COLLATION607(\"qu-x-icu\"),\n    COLLATION608(\"qu-BO-x-icu\"),\n    COLLATION609(\"qu-EC-x-icu\"),\n    COLLATION610(\"qu-PE-x-icu\"),\n    COLLATION611(\"rm-x-icu\"),\n    COLLATION612(\"rm-CH-x-icu\"),\n    COLLATION613(\"rn-x-icu\"),\n    COLLATION614(\"rn-BI-x-icu\"),\n    COLLATION615(\"ro-x-icu\"),\n    COLLATION616(\"ro-MD-x-icu\"),\n    COLLATION617(\"ro-RO-x-icu\"),\n    COLLATION618(\"rof-x-icu\"),\n    COLLATION619(\"rof-TZ-x-icu\"),\n    COLLATION620(\"ru-x-icu\"),\n    COLLATION621(\"ru-BY-x-icu\"),\n    COLLATION622(\"ru-KG-x-icu\"),\n    COLLATION623(\"ru-KZ-x-icu\"),\n    COLLATION624(\"ru-MD-x-icu\"),\n    COLLATION625(\"ru-RU-x-icu\"),\n    COLLATION626(\"ru-UA-x-icu\"),\n    COLLATION627(\"rw-x-icu\"),\n    COLLATION628(\"rw-RW-x-icu\"),\n    COLLATION629(\"rwk-x-icu\"),\n    COLLATION630(\"rwk-TZ-x-icu\"),\n    COLLATION631(\"sah-x-icu\"),\n    COLLATION632(\"sah-RU-x-icu\"),\n    COLLATION633(\"saq-x-icu\"),\n    COLLATION634(\"saq-KE-x-icu\"),\n    COLLATION635(\"sat-x-icu\"),\n    COLLATION636(\"sat-Olck-x-icu\"),\n    COLLATION637(\"sat-Olck-IN-x-icu\"),\n    COLLATION638(\"sbp-x-icu\"),\n    COLLATION639(\"sbp-TZ-x-icu\"),\n    COLLATION640(\"sd-x-icu\"),\n    COLLATION641(\"sd-Arab-x-icu\"),\n    COLLATION642(\"sd-Arab-PK-x-icu\"),\n    COLLATION643(\"sd-Deva-x-icu\"),\n    COLLATION644(\"sd-Deva-IN-x-icu\"),\n    COLLATION645(\"se-x-icu\"),\n    COLLATION646(\"se-FI-x-icu\"),\n    COLLATION647(\"se-NO-x-icu\"),\n    COLLATION648(\"se-SE-x-icu\"),\n    COLLATION649(\"seh-x-icu\"),\n    COLLATION650(\"seh-MZ-x-icu\"),\n    COLLATION651(\"ses-x-icu\"),\n    COLLATION652(\"ses-ML-x-icu\"),\n    COLLATION653(\"sg-x-icu\"),\n    COLLATION654(\"sg-CF-x-icu\"),\n    COLLATION655(\"shi-x-icu\"),\n    COLLATION656(\"shi-Latn-x-icu\"),\n    COLLATION657(\"shi-Latn-MA-x-icu\"),\n    COLLATION658(\"shi-Tfng-x-icu\"),\n    COLLATION659(\"shi-Tfng-MA-x-icu\"),\n    COLLATION660(\"si-x-icu\"),\n    COLLATION661(\"si-LK-x-icu\"),\n    COLLATION662(\"sk-x-icu\"),\n    COLLATION663(\"sk-SK-x-icu\"),\n    COLLATION664(\"sl-x-icu\"),\n    COLLATION665(\"sl-SI-x-icu\"),\n    COLLATION666(\"smn-x-icu\"),\n    COLLATION667(\"smn-FI-x-icu\"),\n    COLLATION668(\"sn-x-icu\"),\n    COLLATION669(\"sn-ZW-x-icu\"),\n    COLLATION670(\"so-x-icu\"),\n    COLLATION671(\"so-DJ-x-icu\"),\n    COLLATION672(\"so-ET-x-icu\"),\n    COLLATION673(\"so-KE-x-icu\"),\n    COLLATION674(\"so-SO-x-icu\"),\n    COLLATION675(\"sq-x-icu\"),\n    COLLATION676(\"sq-AL-x-icu\"),\n    COLLATION677(\"sq-MK-x-icu\"),\n    COLLATION678(\"sq-XK-x-icu\"),\n    COLLATION679(\"sr-x-icu\"),\n    COLLATION680(\"sr-Cyrl-x-icu\"),\n    COLLATION681(\"sr-Cyrl-BA-x-icu\"),\n    COLLATION682(\"sr-Cyrl-ME-x-icu\"),\n    COLLATION683(\"sr-Cyrl-RS-x-icu\"),\n    COLLATION684(\"sr-Cyrl-XK-x-icu\"),\n    COLLATION685(\"sr-Latn-x-icu\"),\n    COLLATION686(\"sr-Latn-BA-x-icu\"),\n    COLLATION687(\"sr-Latn-ME-x-icu\"),\n    COLLATION688(\"sr-Latn-RS-x-icu\"),\n    COLLATION689(\"sr-Latn-XK-x-icu\"),\n    COLLATION690(\"su-x-icu\"),\n    COLLATION691(\"su-Latn-x-icu\"),\n    COLLATION692(\"su-Latn-ID-x-icu\"),\n    COLLATION693(\"sv-x-icu\"),\n    COLLATION694(\"sv-AX-x-icu\"),\n    COLLATION695(\"sv-FI-x-icu\"),\n    COLLATION696(\"sv-SE-x-icu\"),\n    COLLATION697(\"sw-x-icu\"),\n    COLLATION698(\"sw-CD-x-icu\"),\n    COLLATION699(\"sw-KE-x-icu\"),\n    COLLATION700(\"sw-TZ-x-icu\"),\n    COLLATION701(\"sw-UG-x-icu\"),\n    COLLATION702(\"ta-x-icu\"),\n    COLLATION703(\"ta-IN-x-icu\"),\n    COLLATION704(\"ta-LK-x-icu\"),\n    COLLATION705(\"ta-MY-x-icu\"),\n    COLLATION706(\"ta-SG-x-icu\"),\n    COLLATION707(\"te-x-icu\"),\n    COLLATION708(\"te-IN-x-icu\"),\n    COLLATION709(\"teo-x-icu\"),\n    COLLATION710(\"teo-KE-x-icu\"),\n    COLLATION711(\"teo-UG-x-icu\"),\n    COLLATION712(\"tg-x-icu\"),\n    COLLATION713(\"tg-TJ-x-icu\"),\n    COLLATION714(\"th-x-icu\"),\n    COLLATION715(\"th-TH-x-icu\"),\n    COLLATION716(\"ti-x-icu\"),\n    COLLATION717(\"ti-ER-x-icu\"),\n    COLLATION718(\"ti-ET-x-icu\"),\n    COLLATION719(\"tk-x-icu\"),\n    COLLATION720(\"tk-TM-x-icu\"),\n    COLLATION721(\"to-x-icu\"),\n    COLLATION722(\"to-TO-x-icu\"),\n    COLLATION723(\"tr-x-icu\"),\n    COLLATION724(\"tr-CY-x-icu\"),\n    COLLATION725(\"tr-TR-x-icu\"),\n    COLLATION726(\"tt-x-icu\"),\n    COLLATION727(\"tt-RU-x-icu\"),\n    COLLATION728(\"twq-x-icu\"),\n    COLLATION729(\"twq-NE-x-icu\"),\n    COLLATION730(\"tzm-x-icu\"),\n    COLLATION731(\"tzm-MA-x-icu\"),\n    COLLATION732(\"ug-x-icu\"),\n    COLLATION733(\"ug-CN-x-icu\"),\n    COLLATION734(\"uk-x-icu\"),\n    COLLATION735(\"uk-UA-x-icu\"),\n    COLLATION736(\"ur-x-icu\"),\n    COLLATION737(\"ur-IN-x-icu\"),\n    COLLATION738(\"ur-PK-x-icu\"),\n    COLLATION739(\"uz-x-icu\"),\n    COLLATION740(\"uz-Arab-x-icu\"),\n    COLLATION741(\"uz-Arab-AF-x-icu\"),\n    COLLATION742(\"uz-Cyrl-x-icu\"),\n    COLLATION743(\"uz-Cyrl-UZ-x-icu\"),\n    COLLATION744(\"uz-Latn-x-icu\"),\n    COLLATION745(\"uz-Latn-UZ-x-icu\"),\n    COLLATION746(\"vai-x-icu\"),\n    COLLATION747(\"vai-Latn-x-icu\"),\n    COLLATION748(\"vai-Latn-LR-x-icu\"),\n    COLLATION749(\"vai-Vaii-x-icu\"),\n    COLLATION750(\"vai-Vaii-LR-x-icu\"),\n    COLLATION751(\"vi-x-icu\"),\n    COLLATION752(\"vi-VN-x-icu\"),\n    COLLATION753(\"vun-x-icu\"),\n    COLLATION754(\"vun-TZ-x-icu\"),\n    COLLATION755(\"wae-x-icu\"),\n    COLLATION756(\"wae-CH-x-icu\"),\n    COLLATION757(\"wo-x-icu\"),\n    COLLATION758(\"wo-SN-x-icu\"),\n    COLLATION759(\"xh-x-icu\"),\n    COLLATION760(\"xh-ZA-x-icu\"),\n    COLLATION761(\"xog-x-icu\"),\n    COLLATION762(\"xog-UG-x-icu\"),\n    COLLATION763(\"yav-x-icu\"),\n    COLLATION764(\"yav-CM-x-icu\"),\n    COLLATION765(\"yi-x-icu\"),\n    COLLATION766(\"yi-001-x-icu\"),\n    COLLATION767(\"yo-x-icu\"),\n    COLLATION768(\"yo-BJ-x-icu\"),\n    COLLATION769(\"yo-NG-x-icu\"),\n    COLLATION770(\"yue-x-icu\"),\n    COLLATION771(\"yue-Hans-x-icu\"),\n    COLLATION772(\"yue-Hans-CN-x-icu\"),\n    COLLATION773(\"yue-Hant-x-icu\"),\n    COLLATION774(\"yue-Hant-HK-x-icu\"),\n    COLLATION775(\"zgh-x-icu\"),\n    COLLATION776(\"zgh-MA-x-icu\"),\n    COLLATION777(\"zh-x-icu\"),\n    COLLATION778(\"zh-Hans-x-icu\"),\n    COLLATION779(\"zh-Hans-CN-x-icu\"),\n    COLLATION780(\"zh-Hans-HK-x-icu\"),\n    COLLATION781(\"zh-Hans-MO-x-icu\"),\n    COLLATION782(\"zh-Hans-SG-x-icu\"),\n    COLLATION783(\"zh-Hant-x-icu\"),\n    COLLATION784(\"zh-Hant-HK-x-icu\"),\n    COLLATION785(\"zh-Hant-MO-x-icu\"),\n    COLLATION786(\"zh-Hant-TW-x-icu\"),\n    COLLATION787(\"zu-x-icu\"),\n    COLLATION788(\"zu-ZA-x-icu\"),\n    COLLATION789(\"aa\"),\n    COLLATION790(\"aa-DJ\"),\n    COLLATION791(\"aa_DJ\"),\n    COLLATION792(\"aa-ER\"),\n    COLLATION793(\"aa_ER\"),\n    COLLATION794(\"aa-ET\"),\n    COLLATION795(\"aa_ET\"),\n    COLLATION796(\"af\"),\n    COLLATION797(\"af-NA\"),\n    COLLATION798(\"af_NA\"),\n    COLLATION799(\"af-ZA\"),\n    COLLATION800(\"af_ZA\"),\n    COLLATION801(\"agq\"),\n    COLLATION802(\"agq-CM\"),\n    COLLATION803(\"agq_CM\"),\n    COLLATION804(\"ak\"),\n    COLLATION805(\"ak-GH\"),\n    COLLATION806(\"ak_GH\"),\n    COLLATION807(\"am\"),\n    COLLATION808(\"am-ET\"),\n    COLLATION809(\"am_ET\"),\n    COLLATION810(\"ar\"),\n    COLLATION811(\"ar-001\"),\n    COLLATION812(\"ar_001\"),\n    COLLATION813(\"ar-AE\"),\n    COLLATION814(\"ar_AE\"),\n    COLLATION815(\"ar-BH\"),\n    COLLATION816(\"ar_BH\"),\n    COLLATION817(\"ar-DJ\"),\n    COLLATION818(\"ar_DJ\"),\n    COLLATION819(\"ar-DZ\"),\n    COLLATION820(\"ar_DZ\"),\n    COLLATION821(\"ar-EG\"),\n    COLLATION822(\"ar_EG\"),\n    COLLATION823(\"ar-ER\"),\n    COLLATION824(\"ar_ER\"),\n    COLLATION825(\"ar-IL\"),\n    COLLATION826(\"ar_IL\"),\n    COLLATION827(\"ar-IQ\"),\n    COLLATION828(\"ar_IQ\"),\n    COLLATION829(\"ar-JO\"),\n    COLLATION830(\"ar_JO\"),\n    COLLATION831(\"ar-KM\"),\n    COLLATION832(\"ar_KM\"),\n    COLLATION833(\"ar-KW\"),\n    COLLATION834(\"ar_KW\"),\n    COLLATION835(\"ar-LB\"),\n    COLLATION836(\"ar_LB\"),\n    COLLATION837(\"ar-LY\"),\n    COLLATION838(\"ar_LY\"),\n    COLLATION839(\"ar-MA\"),\n    COLLATION840(\"ar_MA\"),\n    COLLATION841(\"ar-MR\"),\n    COLLATION842(\"ar_MR\"),\n    COLLATION843(\"ar-OM\"),\n    COLLATION844(\"ar_OM\"),\n    COLLATION845(\"ar-PS\"),\n    COLLATION846(\"ar_PS\"),\n    COLLATION847(\"ar-QA\"),\n    COLLATION848(\"ar_QA\"),\n    COLLATION849(\"ar-SA\"),\n    COLLATION850(\"ar_SA\"),\n    COLLATION851(\"ar-SD\"),\n    COLLATION852(\"ar_SD\"),\n    COLLATION853(\"ar-SO\"),\n    COLLATION854(\"ar_SO\"),\n    COLLATION855(\"ar-SS\"),\n    COLLATION856(\"ar_SS\"),\n    COLLATION857(\"ar-SY\"),\n    COLLATION858(\"ar_SY\"),\n    COLLATION859(\"ar-TD\"),\n    COLLATION860(\"ar_TD\"),\n    COLLATION861(\"ar-TN\"),\n    COLLATION862(\"ar_TN\"),\n    COLLATION863(\"ar-YE\"),\n    COLLATION864(\"ar_YE\"),\n    COLLATION865(\"arn\"),\n    COLLATION866(\"arn-CL\"),\n    COLLATION867(\"arn_CL\"),\n    COLLATION868(\"as\"),\n    COLLATION869(\"as-IN\"),\n    COLLATION870(\"as_IN\"),\n    COLLATION871(\"asa\"),\n    COLLATION872(\"asa-TZ\"),\n    COLLATION873(\"asa_TZ\"),\n    COLLATION874(\"ast\"),\n    COLLATION875(\"ast-ES\"),\n    COLLATION876(\"ast_ES\"),\n    COLLATION877(\"az\"),\n    COLLATION878(\"az-Cyrl\"),\n    COLLATION879(\"az_Cyrl\"),\n    COLLATION880(\"az-Cyrl-AZ\"),\n    COLLATION881(\"az_Cyrl_AZ\"),\n    COLLATION882(\"az-Latn\"),\n    COLLATION883(\"az_Latn\"),\n    COLLATION884(\"az-Latn-AZ\"),\n    COLLATION885(\"az_Latn_AZ\"),\n    COLLATION886(\"ba\"),\n    COLLATION887(\"ba-RU\"),\n    COLLATION888(\"ba_RU\"),\n    COLLATION889(\"bas\"),\n    COLLATION890(\"bas-CM\"),\n    COLLATION891(\"bas_CM\"),\n    COLLATION892(\"be\"),\n    COLLATION893(\"be-BY\"),\n    COLLATION894(\"be_BY\"),\n    COLLATION895(\"bem\"),\n    COLLATION896(\"bem-ZM\"),\n    COLLATION897(\"bem_ZM\"),\n    COLLATION898(\"bez\"),\n    COLLATION899(\"bez-TZ\"),\n    COLLATION900(\"bez_TZ\"),\n    COLLATION901(\"bg\"),\n    COLLATION902(\"bg-BG\"),\n    COLLATION903(\"bg_BG\"),\n    COLLATION904(\"bin\"),\n    COLLATION905(\"bin-NG\"),\n    COLLATION906(\"bin_NG\"),\n    COLLATION907(\"bm\"),\n    COLLATION908(\"bm-Latn\"),\n    COLLATION909(\"bm_Latn\"),\n    COLLATION910(\"bm-Latn-ML\"),\n    COLLATION911(\"bm_Latn_ML\"),\n    COLLATION912(\"bn\"),\n    COLLATION913(\"bn-BD\"),\n    COLLATION914(\"bn_BD\"),\n    COLLATION915(\"bn-IN\"),\n    COLLATION916(\"bn_IN\"),\n    COLLATION917(\"bo\"),\n    COLLATION918(\"bo-CN\"),\n    COLLATION919(\"bo_CN\"),\n    COLLATION920(\"bo-IN\"),\n    COLLATION921(\"bo_IN\"),\n    COLLATION922(\"br\"),\n    COLLATION923(\"br-FR\"),\n    COLLATION924(\"br_FR\"),\n    COLLATION925(\"brx\"),\n    COLLATION926(\"brx-IN\"),\n    COLLATION927(\"brx_IN\"),\n    COLLATION928(\"bs\"),\n    COLLATION929(\"bs-Cyrl\"),\n    COLLATION930(\"bs_Cyrl\"),\n    COLLATION931(\"bs-Cyrl-BA\"),\n    COLLATION932(\"bs_Cyrl_BA\"),\n    COLLATION933(\"bs-Latn\"),\n    COLLATION934(\"bs_Latn\"),\n    COLLATION935(\"bs-Latn-BA\"),\n    COLLATION936(\"bs_Latn_BA\"),\n    COLLATION937(\"byn\"),\n    COLLATION938(\"byn-ER\"),\n    COLLATION939(\"byn_ER\"),\n    COLLATION940(\"ca\"),\n    COLLATION941(\"ca-AD\"),\n    COLLATION942(\"ca_AD\"),\n    COLLATION943(\"ca-ES\"),\n    COLLATION944(\"ca_ES\"),\n    COLLATION945(\"ca-ES-valencia\"),\n    COLLATION946(\"ca_ES_valencia\"),\n    COLLATION947(\"ca-FR\"),\n    COLLATION948(\"ca_FR\"),\n    COLLATION949(\"ca-IT\"),\n    COLLATION950(\"ca_IT\"),\n    COLLATION951(\"ccp\"),\n    COLLATION952(\"ccp-Cakm\"),\n    COLLATION953(\"ccp_Cakm\"),\n    COLLATION954(\"ccp-Cakm-BD\"),\n    COLLATION955(\"ccp_Cakm_BD\"),\n    COLLATION956(\"ccp-Cakm-IN\"),\n    COLLATION957(\"ccp_Cakm_IN\"),\n    COLLATION958(\"ce\"),\n    COLLATION959(\"ce-RU\"),\n    COLLATION960(\"ce_RU\"),\n    COLLATION961(\"ceb\"),\n    COLLATION962(\"ceb-Latn\"),\n    COLLATION963(\"ceb_Latn\"),\n    COLLATION964(\"ceb-Latn-PH\"),\n    COLLATION965(\"ceb_Latn_PH\"),\n    COLLATION966(\"cgg\"),\n    COLLATION967(\"cgg-UG\"),\n    COLLATION968(\"cgg_UG\"),\n    COLLATION969(\"chr\"),\n    COLLATION970(\"chr-Cher\"),\n    COLLATION971(\"chr_Cher\"),\n    COLLATION972(\"chr-Cher-US\"),\n    COLLATION973(\"chr_Cher_US\"),\n    COLLATION974(\"co\"),\n    COLLATION975(\"co-FR\"),\n    COLLATION976(\"co_FR\"),\n    COLLATION977(\"cs\"),\n    COLLATION978(\"cs-CZ\"),\n    COLLATION979(\"cs_CZ\"),\n    COLLATION980(\"cu\"),\n    COLLATION981(\"cu-RU\"),\n    COLLATION982(\"cu_RU\"),\n    COLLATION983(\"cy\"),\n    COLLATION984(\"cy-GB\"),\n    COLLATION985(\"cy_GB\"),\n    COLLATION986(\"da\"),\n    COLLATION987(\"da-DK\"),\n    COLLATION988(\"da_DK\"),\n    COLLATION989(\"da-GL\"),\n    COLLATION990(\"da_GL\"),\n    COLLATION991(\"dav\"),\n    COLLATION992(\"dav-KE\"),\n    COLLATION993(\"dav_KE\"),\n    COLLATION994(\"de\"),\n    COLLATION995(\"de-AT\"),\n    COLLATION996(\"de_AT\"),\n    COLLATION997(\"de-BE\"),\n    COLLATION998(\"de_BE\"),\n    COLLATION999(\"de-CH\"),\n    COLLATION1000(\"de_CH\"),\n    COLLATION1001(\"de-DE\"),\n    COLLATION1002(\"de_DE\"),\n    COLLATION1003(\"de-DE_phoneb\"),\n    COLLATION1004(\"de_DE_phoneb\"),\n    COLLATION1005(\"de-IT\"),\n    COLLATION1006(\"de_IT\"),\n    COLLATION1007(\"de-LI\"),\n    COLLATION1008(\"de_LI\"),\n    COLLATION1009(\"de-LU\"),\n    COLLATION1010(\"de_LU\"),\n    COLLATION1011(\"dje\"),\n    COLLATION1012(\"dje-NE\"),\n    COLLATION1013(\"dje_NE\"),\n    COLLATION1014(\"dsb\"),\n    COLLATION1015(\"dsb-DE\"),\n    COLLATION1016(\"dsb_DE\"),\n    COLLATION1017(\"dua\"),\n    COLLATION1018(\"dua-CM\"),\n    COLLATION1019(\"dua_CM\"),\n    COLLATION1020(\"dv\"),\n    COLLATION1021(\"dv-MV\"),\n    COLLATION1022(\"dv_MV\"),\n    COLLATION1023(\"dyo\"),\n    COLLATION1024(\"dyo-SN\"),\n    COLLATION1025(\"dyo_SN\"),\n    COLLATION1026(\"dz\"),\n    COLLATION1027(\"dz-BT\"),\n    COLLATION1028(\"dz_BT\"),\n    COLLATION1029(\"ebu\"),\n    COLLATION1030(\"ebu-KE\"),\n    COLLATION1031(\"ebu_KE\"),\n    COLLATION1032(\"ee\"),\n    COLLATION1033(\"ee-GH\"),\n    COLLATION1034(\"ee_GH\"),\n    COLLATION1035(\"ee-TG\"),\n    COLLATION1036(\"ee_TG\"),\n    COLLATION1037(\"el\"),\n    COLLATION1038(\"el-CY\"),\n    COLLATION1039(\"el_CY\"),\n    COLLATION1040(\"el-GR\"),\n    COLLATION1041(\"el_GR\"),\n    COLLATION1042(\"en\"),\n    COLLATION1043(\"en-001\"),\n    COLLATION1044(\"en_001\"),\n    COLLATION1045(\"en-029\"),\n    COLLATION1046(\"en_029\"),\n    COLLATION1047(\"en-150\"),\n    COLLATION1048(\"en_150\"),\n    COLLATION1049(\"en-AE\"),\n    COLLATION1050(\"en_AE\"),\n    COLLATION1051(\"en-AG\"),\n    COLLATION1052(\"en_AG\"),\n    COLLATION1053(\"en-AI\"),\n    COLLATION1054(\"en_AI\"),\n    COLLATION1055(\"en-AS\"),\n    COLLATION1056(\"en_AS\"),\n    COLLATION1057(\"en-AT\"),\n    COLLATION1058(\"en_AT\"),\n    COLLATION1059(\"en-AU\"),\n    COLLATION1060(\"en_AU\"),\n    COLLATION1061(\"en-BB\"),\n    COLLATION1062(\"en_BB\"),\n    COLLATION1063(\"en-BE\"),\n    COLLATION1064(\"en_BE\"),\n    COLLATION1065(\"en-BI\"),\n    COLLATION1066(\"en_BI\"),\n    COLLATION1067(\"en-BM\"),\n    COLLATION1068(\"en_BM\"),\n    COLLATION1069(\"en-BS\"),\n    COLLATION1070(\"en_BS\"),\n    COLLATION1071(\"en-BW\"),\n    COLLATION1072(\"en_BW\"),\n    COLLATION1073(\"en-BZ\"),\n    COLLATION1074(\"en_BZ\"),\n    COLLATION1075(\"en-CA\"),\n    COLLATION1076(\"en_CA\"),\n    COLLATION1077(\"en-CC\"),\n    COLLATION1078(\"en_CC\"),\n    COLLATION1079(\"en-CH\"),\n    COLLATION1080(\"en_CH\"),\n    COLLATION1081(\"en-CK\"),\n    COLLATION1082(\"en_CK\"),\n    COLLATION1083(\"en-CM\"),\n    COLLATION1084(\"en_CM\"),\n    COLLATION1085(\"en-CX\"),\n    COLLATION1086(\"en_CX\"),\n    COLLATION1087(\"en-CY\"),\n    COLLATION1088(\"en_CY\"),\n    COLLATION1089(\"en-DE\"),\n    COLLATION1090(\"en_DE\"),\n    COLLATION1091(\"en-DK\"),\n    COLLATION1092(\"en_DK\"),\n    COLLATION1093(\"en-DM\"),\n    COLLATION1094(\"en_DM\"),\n    COLLATION1095(\"en-ER\"),\n    COLLATION1096(\"en_ER\"),\n    COLLATION1097(\"en-FI\"),\n    COLLATION1098(\"en_FI\"),\n    COLLATION1099(\"en-FJ\"),\n    COLLATION1100(\"en_FJ\"),\n    COLLATION1101(\"en-FK\"),\n    COLLATION1102(\"en_FK\"),\n    COLLATION1103(\"en-FM\"),\n    COLLATION1104(\"en_FM\"),\n    COLLATION1105(\"en-GB\"),\n    COLLATION1106(\"en_GB\"),\n    COLLATION1107(\"en-GD\"),\n    COLLATION1108(\"en_GD\"),\n    COLLATION1109(\"en-GG\"),\n    COLLATION1110(\"en_GG\"),\n    COLLATION1111(\"en-GH\"),\n    COLLATION1112(\"en_GH\"),\n    COLLATION1113(\"en-GI\"),\n    COLLATION1114(\"en_GI\"),\n    COLLATION1115(\"en-GM\"),\n    COLLATION1116(\"en_GM\"),\n    COLLATION1117(\"en-GU\"),\n    COLLATION1118(\"en_GU\"),\n    COLLATION1119(\"en-GY\"),\n    COLLATION1120(\"en_GY\"),\n    COLLATION1121(\"en-HK\"),\n    COLLATION1122(\"en_HK\"),\n    COLLATION1123(\"en-ID\"),\n    COLLATION1124(\"en_ID\"),\n    COLLATION1125(\"en-IE\"),\n    COLLATION1126(\"en_IE\"),\n    COLLATION1127(\"en-IL\"),\n    COLLATION1128(\"en_IL\"),\n    COLLATION1129(\"en-IM\"),\n    COLLATION1130(\"en_IM\"),\n    COLLATION1131(\"en-IN\"),\n    COLLATION1132(\"en_IN\"),\n    COLLATION1133(\"en-IO\"),\n    COLLATION1134(\"en_IO\"),\n    COLLATION1135(\"en-JE\"),\n    COLLATION1136(\"en_JE\"),\n    COLLATION1137(\"en-JM\"),\n    COLLATION1138(\"en_JM\"),\n    COLLATION1139(\"en-KE\"),\n    COLLATION1140(\"en_KE\"),\n    COLLATION1141(\"en-KI\"),\n    COLLATION1142(\"en_KI\"),\n    COLLATION1143(\"en-KN\"),\n    COLLATION1144(\"en_KN\"),\n    COLLATION1145(\"en-KY\"),\n    COLLATION1146(\"en_KY\"),\n    COLLATION1147(\"en-LC\"),\n    COLLATION1148(\"en_LC\"),\n    COLLATION1149(\"en-LR\"),\n    COLLATION1150(\"en_LR\"),\n    COLLATION1151(\"en-LS\"),\n    COLLATION1152(\"en_LS\"),\n    COLLATION1153(\"en-MG\"),\n    COLLATION1154(\"en_MG\"),\n    COLLATION1155(\"en-MH\"),\n    COLLATION1156(\"en_MH\"),\n    COLLATION1157(\"en-MO\"),\n    COLLATION1158(\"en_MO\"),\n    COLLATION1159(\"en-MP\"),\n    COLLATION1160(\"en_MP\"),\n    COLLATION1161(\"en-MS\"),\n    COLLATION1162(\"en_MS\"),\n    COLLATION1163(\"en-MT\"),\n    COLLATION1164(\"en_MT\"),\n    COLLATION1165(\"en-MU\"),\n    COLLATION1166(\"en_MU\"),\n    COLLATION1167(\"en-MW\"),\n    COLLATION1168(\"en_MW\"),\n    COLLATION1169(\"en-MY\"),\n    COLLATION1170(\"en_MY\"),\n    COLLATION1171(\"en-NA\"),\n    COLLATION1172(\"en_NA\"),\n    COLLATION1173(\"en-NF\"),\n    COLLATION1174(\"en_NF\"),\n    COLLATION1175(\"en-NG\"),\n    COLLATION1176(\"en_NG\"),\n    COLLATION1177(\"en-NL\"),\n    COLLATION1178(\"en_NL\"),\n    COLLATION1179(\"en-NR\"),\n    COLLATION1180(\"en_NR\"),\n    COLLATION1181(\"en-NU\"),\n    COLLATION1182(\"en_NU\"),\n    COLLATION1183(\"en-NZ\"),\n    COLLATION1184(\"en_NZ\"),\n    COLLATION1185(\"en-PG\"),\n    COLLATION1186(\"en_PG\"),\n    COLLATION1187(\"en-PH\"),\n    COLLATION1188(\"en_PH\"),\n    COLLATION1189(\"en-PK\"),\n    COLLATION1190(\"en_PK\"),\n    COLLATION1191(\"en-PN\"),\n    COLLATION1192(\"en_PN\"),\n    COLLATION1193(\"en-PR\"),\n    COLLATION1194(\"en_PR\"),\n    COLLATION1195(\"en-PW\"),\n    COLLATION1196(\"en_PW\"),\n    COLLATION1197(\"en-RW\"),\n    COLLATION1198(\"en_RW\"),\n    COLLATION1199(\"en-SB\"),\n    COLLATION1200(\"en_SB\"),\n    COLLATION1201(\"en-SC\"),\n    COLLATION1202(\"en_SC\"),\n    COLLATION1203(\"en-SD\"),\n    COLLATION1204(\"en_SD\"),\n    COLLATION1205(\"en-SE\"),\n    COLLATION1206(\"en_SE\"),\n    COLLATION1207(\"en-SG\"),\n    COLLATION1208(\"en_SG\"),\n    COLLATION1209(\"en-SH\"),\n    COLLATION1210(\"en_SH\"),\n    COLLATION1211(\"en-SI\"),\n    COLLATION1212(\"en_SI\"),\n    COLLATION1213(\"en-SL\"),\n    COLLATION1214(\"en_SL\"),\n    COLLATION1215(\"en-SS\"),\n    COLLATION1216(\"en_SS\"),\n    COLLATION1217(\"en-SX\"),\n    COLLATION1218(\"en_SX\"),\n    COLLATION1219(\"en-SZ\"),\n    COLLATION1220(\"en_SZ\"),\n    COLLATION1221(\"en-TC\"),\n    COLLATION1222(\"en_TC\"),\n    COLLATION1223(\"en-TK\"),\n    COLLATION1224(\"en_TK\"),\n    COLLATION1225(\"en-TO\"),\n    COLLATION1226(\"en_TO\"),\n    COLLATION1227(\"en-TT\"),\n    COLLATION1228(\"en_TT\"),\n    COLLATION1229(\"en-TV\"),\n    COLLATION1230(\"en_TV\"),\n    COLLATION1231(\"en-TZ\"),\n    COLLATION1232(\"en_TZ\"),\n    COLLATION1233(\"en-UG\"),\n    COLLATION1234(\"en_UG\"),\n    COLLATION1235(\"en-UM\"),\n    COLLATION1236(\"en_UM\"),\n    COLLATION1237(\"en-US\"),\n    COLLATION1238(\"en_US\"),\n    COLLATION1239(\"en-VC\"),\n    COLLATION1240(\"en_VC\"),\n    COLLATION1241(\"en-VG\"),\n    COLLATION1242(\"en_VG\"),\n    COLLATION1243(\"en-VI\"),\n    COLLATION1244(\"en_VI\"),\n    COLLATION1245(\"en-VU\"),\n    COLLATION1246(\"en_VU\"),\n    COLLATION1247(\"en-WS\"),\n    COLLATION1248(\"en_WS\"),\n    COLLATION1249(\"en-ZA\"),\n    COLLATION1250(\"en_ZA\"),\n    COLLATION1251(\"en-ZM\"),\n    COLLATION1252(\"en_ZM\"),\n    COLLATION1253(\"en-ZW\"),\n    COLLATION1254(\"en_ZW\"),\n    COLLATION1255(\"eo\"),\n    COLLATION1256(\"eo-001\"),\n    COLLATION1257(\"eo_001\"),\n    COLLATION1258(\"es\"),\n    COLLATION1259(\"es-419\"),\n    COLLATION1260(\"es_419\"),\n    COLLATION1261(\"es-AR\"),\n    COLLATION1262(\"es_AR\"),\n    COLLATION1263(\"es-BO\"),\n    COLLATION1264(\"es_BO\"),\n    COLLATION1265(\"es-BR\"),\n    COLLATION1266(\"es_BR\"),\n    COLLATION1267(\"es-BZ\"),\n    COLLATION1268(\"es_BZ\"),\n    COLLATION1269(\"es-CL\"),\n    COLLATION1270(\"es_CL\"),\n    COLLATION1271(\"es-CO\"),\n    COLLATION1272(\"es_CO\"),\n    COLLATION1273(\"es-CR\"),\n    COLLATION1274(\"es_CR\"),\n    COLLATION1275(\"es-CU\"),\n    COLLATION1276(\"es_CU\"),\n    COLLATION1277(\"es-DO\"),\n    COLLATION1278(\"es_DO\"),\n    COLLATION1279(\"es-EC\"),\n    COLLATION1280(\"es_EC\"),\n    COLLATION1281(\"es-ES\"),\n    COLLATION1282(\"es_ES\"),\n    COLLATION1283(\"es-ES_tradnl\"),\n    COLLATION1284(\"es_ES_tradnl\"),\n    COLLATION1285(\"es-GQ\"),\n    COLLATION1286(\"es_GQ\"),\n    COLLATION1287(\"es-GT\"),\n    COLLATION1288(\"es_GT\"),\n    COLLATION1289(\"es-HN\"),\n    COLLATION1290(\"es_HN\"),\n    COLLATION1291(\"es-MX\"),\n    COLLATION1292(\"es_MX\"),\n    COLLATION1293(\"es-NI\"),\n    COLLATION1294(\"es_NI\"),\n    COLLATION1295(\"es-PA\"),\n    COLLATION1296(\"es_PA\"),\n    COLLATION1297(\"es-PE\"),\n    COLLATION1298(\"es_PE\"),\n    COLLATION1299(\"es-PH\"),\n    COLLATION1300(\"es_PH\"),\n    COLLATION1301(\"es-PR\"),\n    COLLATION1302(\"es_PR\"),\n    COLLATION1303(\"es-PY\"),\n    COLLATION1304(\"es_PY\"),\n    COLLATION1305(\"es-SV\"),\n    COLLATION1306(\"es_SV\"),\n    COLLATION1307(\"es-US\"),\n    COLLATION1308(\"es_US\"),\n    COLLATION1309(\"es-UY\"),\n    COLLATION1310(\"es_UY\"),\n    COLLATION1311(\"es-VE\"),\n    COLLATION1312(\"es_VE\"),\n    COLLATION1313(\"et\"),\n    COLLATION1314(\"et-EE\"),\n    COLLATION1315(\"et_EE\"),\n    COLLATION1316(\"eu\"),\n    COLLATION1317(\"eu-ES\"),\n    COLLATION1318(\"eu_ES\"),\n    COLLATION1319(\"ewo\"),\n    COLLATION1320(\"ewo-CM\"),\n    COLLATION1321(\"ewo_CM\"),\n    COLLATION1322(\"fa\"),\n    COLLATION1323(\"fa-IR\"),\n    COLLATION1324(\"fa_IR\"),\n    COLLATION1325(\"ff\"),\n    COLLATION1326(\"ff-Latn\"),\n    COLLATION1327(\"ff_Latn\"),\n    COLLATION1328(\"ff-Latn-BF\"),\n    COLLATION1329(\"ff_Latn_BF\"),\n    COLLATION1330(\"ff-Latn-CM\"),\n    COLLATION1331(\"ff_Latn_CM\"),\n    COLLATION1332(\"ff-Latn-GH\"),\n    COLLATION1333(\"ff_Latn_GH\"),\n    COLLATION1334(\"ff-Latn-GM\"),\n    COLLATION1335(\"ff_Latn_GM\"),\n    COLLATION1336(\"ff-Latn-GN\"),\n    COLLATION1337(\"ff_Latn_GN\"),\n    COLLATION1338(\"ff-Latn-GW\"),\n    COLLATION1339(\"ff_Latn_GW\"),\n    COLLATION1340(\"ff-Latn-LR\"),\n    COLLATION1341(\"ff_Latn_LR\"),\n    COLLATION1342(\"ff-Latn-MR\"),\n    COLLATION1343(\"ff_Latn_MR\"),\n    COLLATION1344(\"ff-Latn-NE\"),\n    COLLATION1345(\"ff_Latn_NE\"),\n    COLLATION1346(\"ff-Latn-NG\"),\n    COLLATION1347(\"ff_Latn_NG\"),\n    COLLATION1348(\"ff-Latn-SL\"),\n    COLLATION1349(\"ff_Latn_SL\"),\n    COLLATION1350(\"ff-Latn-SN\"),\n    COLLATION1351(\"ff_Latn_SN\"),\n    COLLATION1352(\"fi\"),\n    COLLATION1353(\"fi-FI\"),\n    COLLATION1354(\"fi_FI\"),\n    COLLATION1355(\"fil\"),\n    COLLATION1356(\"fil-PH\"),\n    COLLATION1357(\"fil_PH\"),\n    COLLATION1358(\"fo\"),\n    COLLATION1359(\"fo-DK\"),\n    COLLATION1360(\"fo_DK\"),\n    COLLATION1361(\"fo-FO\"),\n    COLLATION1362(\"fo_FO\"),\n    COLLATION1363(\"fr\"),\n    COLLATION1364(\"fr-029\"),\n    COLLATION1365(\"fr_029\"),\n    COLLATION1366(\"fr-BE\"),\n    COLLATION1367(\"fr_BE\"),\n    COLLATION1368(\"fr-BF\"),\n    COLLATION1369(\"fr_BF\"),\n    COLLATION1370(\"fr-BI\"),\n    COLLATION1371(\"fr_BI\"),\n    COLLATION1372(\"fr-BJ\"),\n    COLLATION1373(\"fr_BJ\"),\n    COLLATION1374(\"fr-BL\"),\n    COLLATION1375(\"fr_BL\"),\n    COLLATION1376(\"fr-CA\"),\n    COLLATION1377(\"fr_CA\"),\n    COLLATION1378(\"fr-CD\"),\n    COLLATION1379(\"fr_CD\"),\n    COLLATION1380(\"fr-CF\"),\n    COLLATION1381(\"fr_CF\"),\n    COLLATION1382(\"fr-CG\"),\n    COLLATION1383(\"fr_CG\"),\n    COLLATION1384(\"fr-CH\"),\n    COLLATION1385(\"fr_CH\"),\n    COLLATION1386(\"fr-CI\"),\n    COLLATION1387(\"fr_CI\"),\n    COLLATION1388(\"fr-CM\"),\n    COLLATION1389(\"fr_CM\"),\n    COLLATION1390(\"fr-DJ\"),\n    COLLATION1391(\"fr_DJ\"),\n    COLLATION1392(\"fr-DZ\"),\n    COLLATION1393(\"fr_DZ\"),\n    COLLATION1394(\"fr-FR\"),\n    COLLATION1395(\"fr_FR\"),\n    COLLATION1396(\"fr-GA\"),\n    COLLATION1397(\"fr_GA\"),\n    COLLATION1398(\"fr-GF\"),\n    COLLATION1399(\"fr_GF\"),\n    COLLATION1400(\"fr-GN\"),\n    COLLATION1401(\"fr_GN\"),\n    COLLATION1402(\"fr-GP\"),\n    COLLATION1403(\"fr_GP\"),\n    COLLATION1404(\"fr-GQ\"),\n    COLLATION1405(\"fr_GQ\"),\n    COLLATION1406(\"fr-HT\"),\n    COLLATION1407(\"fr_HT\"),\n    COLLATION1408(\"fr-KM\"),\n    COLLATION1409(\"fr_KM\"),\n    COLLATION1410(\"fr-LU\"),\n    COLLATION1411(\"fr_LU\"),\n    COLLATION1412(\"fr-MA\"),\n    COLLATION1413(\"fr_MA\"),\n    COLLATION1414(\"fr-MC\"),\n    COLLATION1415(\"fr_MC\"),\n    COLLATION1416(\"fr-MF\"),\n    COLLATION1417(\"fr_MF\"),\n    COLLATION1418(\"fr-MG\"),\n    COLLATION1419(\"fr_MG\"),\n    COLLATION1420(\"fr-ML\"),\n    COLLATION1421(\"fr_ML\"),\n    COLLATION1422(\"fr-MQ\"),\n    COLLATION1423(\"fr_MQ\"),\n    COLLATION1424(\"fr-MR\"),\n    COLLATION1425(\"fr_MR\"),\n    COLLATION1426(\"fr-MU\"),\n    COLLATION1427(\"fr_MU\"),\n    COLLATION1428(\"fr-NC\"),\n    COLLATION1429(\"fr_NC\"),\n    COLLATION1430(\"fr-NE\"),\n    COLLATION1431(\"fr_NE\"),\n    COLLATION1432(\"fr-PF\"),\n    COLLATION1433(\"fr_PF\"),\n    COLLATION1434(\"fr-PM\"),\n    COLLATION1435(\"fr_PM\"),\n    COLLATION1436(\"fr-RE\"),\n    COLLATION1437(\"fr_RE\"),\n    COLLATION1438(\"fr-RW\"),\n    COLLATION1439(\"fr_RW\"),\n    COLLATION1440(\"fr-SC\"),\n    COLLATION1441(\"fr_SC\"),\n    COLLATION1442(\"fr-SN\"),\n    COLLATION1443(\"fr_SN\"),\n    COLLATION1444(\"fr-SY\"),\n    COLLATION1445(\"fr_SY\"),\n    COLLATION1446(\"fr-TD\"),\n    COLLATION1447(\"fr_TD\"),\n    COLLATION1448(\"fr-TG\"),\n    COLLATION1449(\"fr_TG\"),\n    COLLATION1450(\"fr-TN\"),\n    COLLATION1451(\"fr_TN\"),\n    COLLATION1452(\"fr-VU\"),\n    COLLATION1453(\"fr_VU\"),\n    COLLATION1454(\"fr-WF\"),\n    COLLATION1455(\"fr_WF\"),\n    COLLATION1456(\"fr-YT\"),\n    COLLATION1457(\"fr_YT\"),\n    COLLATION1458(\"fur\"),\n    COLLATION1459(\"fur-IT\"),\n    COLLATION1460(\"fur_IT\"),\n    COLLATION1461(\"fy\"),\n    COLLATION1462(\"fy-NL\"),\n    COLLATION1463(\"fy_NL\"),\n    COLLATION1464(\"ga\"),\n    COLLATION1465(\"ga-IE\"),\n    COLLATION1466(\"ga_IE\"),\n    COLLATION1467(\"gd\"),\n    COLLATION1468(\"gd-GB\"),\n    COLLATION1469(\"gd_GB\"),\n    COLLATION1470(\"gl\"),\n    COLLATION1471(\"gl-ES\"),\n    COLLATION1472(\"gl_ES\"),\n    COLLATION1473(\"gn\"),\n    COLLATION1474(\"gn-PY\"),\n    COLLATION1475(\"gn_PY\"),\n    COLLATION1476(\"gsw\"),\n    COLLATION1477(\"gsw-CH\"),\n    COLLATION1478(\"gsw_CH\"),\n    COLLATION1479(\"gsw-FR\"),\n    COLLATION1480(\"gsw_FR\"),\n    COLLATION1481(\"gsw-LI\"),\n    COLLATION1482(\"gsw_LI\"),\n    COLLATION1483(\"gu\"),\n    COLLATION1484(\"gu-IN\"),\n    COLLATION1485(\"gu_IN\"),\n    COLLATION1486(\"guz\"),\n    COLLATION1487(\"guz-KE\"),\n    COLLATION1488(\"guz_KE\"),\n    COLLATION1489(\"gv\"),\n    COLLATION1490(\"gv-IM\"),\n    COLLATION1491(\"gv_IM\"),\n    COLLATION1492(\"ha\"),\n    COLLATION1493(\"ha-Latn\"),\n    COLLATION1494(\"ha_Latn\"),\n    COLLATION1495(\"ha-Latn-GH\"),\n    COLLATION1496(\"ha_Latn_GH\"),\n    COLLATION1497(\"ha-Latn-NE\"),\n    COLLATION1498(\"ha_Latn_NE\"),\n    COLLATION1499(\"ha-Latn-NG\"),\n    COLLATION1500(\"ha_Latn_NG\"),\n    COLLATION1501(\"haw\"),\n    COLLATION1502(\"haw-US\"),\n    COLLATION1503(\"haw_US\"),\n    COLLATION1504(\"he\"),\n    COLLATION1505(\"he-IL\"),\n    COLLATION1506(\"he_IL\"),\n    COLLATION1507(\"hi\"),\n    COLLATION1508(\"hi-IN\"),\n    COLLATION1509(\"hi_IN\"),\n    COLLATION1510(\"hr\"),\n    COLLATION1511(\"hr-BA\"),\n    COLLATION1512(\"hr_BA\"),\n    COLLATION1513(\"hr-HR\"),\n    COLLATION1514(\"hr_HR\"),\n    COLLATION1515(\"hsb\"),\n    COLLATION1516(\"hsb-DE\"),\n    COLLATION1517(\"hsb_DE\"),\n    COLLATION1518(\"hu\"),\n    COLLATION1519(\"hu-HU\"),\n    COLLATION1520(\"hu_HU\"),\n    COLLATION1521(\"hu-HU_technl\"),\n    COLLATION1522(\"hu_HU_technl\"),\n    COLLATION1523(\"hy\"),\n    COLLATION1524(\"hy-AM\"),\n    COLLATION1525(\"hy_AM\"),\n    COLLATION1526(\"ia\"),\n    COLLATION1527(\"ia-001\"),\n    COLLATION1528(\"ia_001\"),\n    COLLATION1529(\"ibb\"),\n    COLLATION1530(\"ibb-NG\"),\n    COLLATION1531(\"ibb_NG\"),\n    COLLATION1532(\"id\"),\n    COLLATION1533(\"id-ID\"),\n    COLLATION1534(\"id_ID\"),\n    COLLATION1535(\"ig\"),\n    COLLATION1536(\"ig-NG\"),\n    COLLATION1537(\"ig_NG\"),\n    COLLATION1538(\"ii\"),\n    COLLATION1539(\"ii-CN\"),\n    COLLATION1540(\"ii_CN\"),\n    COLLATION1541(\"is\"),\n    COLLATION1542(\"is-IS\"),\n    COLLATION1543(\"is_IS\"),\n    COLLATION1544(\"it\"),\n    COLLATION1545(\"it-CH\"),\n    COLLATION1546(\"it_CH\"),\n    COLLATION1547(\"it-IT\"),\n    COLLATION1548(\"it_IT\"),\n    COLLATION1549(\"it-SM\"),\n    COLLATION1550(\"it_SM\"),\n    COLLATION1551(\"it-VA\"),\n    COLLATION1552(\"it_VA\"),\n    COLLATION1553(\"iu\"),\n    COLLATION1554(\"iu-Cans\"),\n    COLLATION1555(\"iu_Cans\"),\n    COLLATION1556(\"iu-Cans-CA\"),\n    COLLATION1557(\"iu_Cans_CA\"),\n    COLLATION1558(\"iu-Latn\"),\n    COLLATION1559(\"iu_Latn\"),\n    COLLATION1560(\"iu-Latn-CA\"),\n    COLLATION1561(\"iu_Latn_CA\"),\n    COLLATION1562(\"jgo\"),\n    COLLATION1563(\"jgo-CM\"),\n    COLLATION1564(\"jgo_CM\"),\n    COLLATION1565(\"jmc\"),\n    COLLATION1566(\"jmc-TZ\"),\n    COLLATION1567(\"jmc_TZ\"),\n    COLLATION1568(\"jv\"),\n    COLLATION1569(\"jv-Java\"),\n    COLLATION1570(\"jv_Java\"),\n    COLLATION1571(\"jv-Java-ID\"),\n    COLLATION1572(\"jv_Java_ID\"),\n    COLLATION1573(\"jv-Latn\"),\n    COLLATION1574(\"jv_Latn\"),\n    COLLATION1575(\"jv-Latn-ID\"),\n    COLLATION1576(\"jv_Latn_ID\"),\n    COLLATION1577(\"ka\"),\n    COLLATION1578(\"ka-GE\"),\n    COLLATION1579(\"ka_GE\"),\n    COLLATION1580(\"ka-GE_modern\"),\n    COLLATION1581(\"ka_GE_modern\"),\n    COLLATION1582(\"kab\"),\n    COLLATION1583(\"kab-DZ\"),\n    COLLATION1584(\"kab_DZ\"),\n    COLLATION1585(\"kam\"),\n    COLLATION1586(\"kam-KE\"),\n    COLLATION1587(\"kam_KE\"),\n    COLLATION1588(\"kde\"),\n    COLLATION1589(\"kde-TZ\"),\n    COLLATION1590(\"kde_TZ\"),\n    COLLATION1591(\"kea\"),\n    COLLATION1592(\"kea-CV\"),\n    COLLATION1593(\"kea_CV\"),\n    COLLATION1594(\"khq\"),\n    COLLATION1595(\"khq-ML\"),\n    COLLATION1596(\"khq_ML\"),\n    COLLATION1597(\"ki\"),\n    COLLATION1598(\"ki-KE\"),\n    COLLATION1599(\"ki_KE\"),\n    COLLATION1600(\"kk\"),\n    COLLATION1601(\"kk-KZ\"),\n    COLLATION1602(\"kk_KZ\"),\n    COLLATION1603(\"kkj\"),\n    COLLATION1604(\"kkj-CM\"),\n    COLLATION1605(\"kkj_CM\"),\n    COLLATION1606(\"kl\"),\n    COLLATION1607(\"kl-GL\"),\n    COLLATION1608(\"kl_GL\"),\n    COLLATION1609(\"kln\"),\n    COLLATION1610(\"kln-KE\"),\n    COLLATION1611(\"kln_KE\"),\n    COLLATION1612(\"km\"),\n    COLLATION1613(\"km-KH\"),\n    COLLATION1614(\"km_KH\"),\n    COLLATION1615(\"kn\"),\n    COLLATION1616(\"kn-IN\"),\n    COLLATION1617(\"kn_IN\"),\n    COLLATION1618(\"ko-KP\"),\n    COLLATION1619(\"ko_KP\"),\n    COLLATION1620(\"kok\"),\n    COLLATION1621(\"kok-IN\"),\n    COLLATION1622(\"kok_IN\"),\n    COLLATION1623(\"kr\"),\n    COLLATION1624(\"kr-Latn\"),\n    COLLATION1625(\"kr_Latn\"),\n    COLLATION1626(\"kr-Latn-NG\"),\n    COLLATION1627(\"kr_Latn_NG\"),\n    COLLATION1628(\"ks\"),\n    COLLATION1629(\"ks-Arab\"),\n    COLLATION1630(\"ks_Arab\"),\n    COLLATION1631(\"ks-Arab-IN\"),\n    COLLATION1632(\"ks_Arab_IN\"),\n    COLLATION1633(\"ks-Deva\"),\n    COLLATION1634(\"ks_Deva\"),\n    COLLATION1635(\"ks-Deva-IN\"),\n    COLLATION1636(\"ks_Deva_IN\"),\n    COLLATION1637(\"ksb\"),\n    COLLATION1638(\"ksb-TZ\"),\n    COLLATION1639(\"ksb_TZ\"),\n    COLLATION1640(\"ksf\"),\n    COLLATION1641(\"ksf-CM\"),\n    COLLATION1642(\"ksf_CM\"),\n    COLLATION1643(\"ksh\"),\n    COLLATION1644(\"ksh-DE\"),\n    COLLATION1645(\"ksh_DE\"),\n    COLLATION1646(\"ku\"),\n    COLLATION1647(\"ku-Arab\"),\n    COLLATION1648(\"ku_Arab\"),\n    COLLATION1649(\"ku-Arab-IQ\"),\n    COLLATION1650(\"ku_Arab_IQ\"),\n    COLLATION1651(\"ku-Arab-IR\"),\n    COLLATION1652(\"ku_Arab_IR\"),\n    COLLATION1653(\"kw\"),\n    COLLATION1654(\"kw-GB\"),\n    COLLATION1655(\"kw_GB\"),\n    COLLATION1656(\"ky\"),\n    COLLATION1657(\"ky-KG\"),\n    COLLATION1658(\"ky_KG\"),\n    COLLATION1659(\"la\"),\n    COLLATION1660(\"la-001\"),\n    COLLATION1661(\"la_001\"),\n    COLLATION1662(\"lag\"),\n    COLLATION1663(\"lag-TZ\"),\n    COLLATION1664(\"lag_TZ\"),\n    COLLATION1665(\"lb\"),\n    COLLATION1666(\"lb-LU\"),\n    COLLATION1667(\"lb_LU\"),\n    COLLATION1668(\"lg\"),\n    COLLATION1669(\"lg-UG\"),\n    COLLATION1670(\"lg_UG\"),\n    COLLATION1671(\"lkt\"),\n    COLLATION1672(\"lkt-US\"),\n    COLLATION1673(\"lkt_US\"),\n    COLLATION1674(\"ln\"),\n    COLLATION1675(\"ln-AO\"),\n    COLLATION1676(\"ln_AO\"),\n    COLLATION1677(\"ln-CD\"),\n    COLLATION1678(\"ln_CD\"),\n    COLLATION1679(\"ln-CF\"),\n    COLLATION1680(\"ln_CF\"),\n    COLLATION1681(\"ln-CG\"),\n    COLLATION1682(\"ln_CG\"),\n    COLLATION1683(\"lo\"),\n    COLLATION1684(\"lo-LA\"),\n    COLLATION1685(\"lo_LA\"),\n    COLLATION1686(\"lrc\"),\n    COLLATION1687(\"lrc-IQ\"),\n    COLLATION1688(\"lrc_IQ\"),\n    COLLATION1689(\"lrc-IR\"),\n    COLLATION1690(\"lrc_IR\"),\n    COLLATION1691(\"lt\"),\n    COLLATION1692(\"lt-LT\"),\n    COLLATION1693(\"lt_LT\"),\n    COLLATION1694(\"lu\"),\n    COLLATION1695(\"lu-CD\"),\n    COLLATION1696(\"lu_CD\"),\n    COLLATION1697(\"luo\"),\n    COLLATION1698(\"luo-KE\"),\n    COLLATION1699(\"luo_KE\"),\n    COLLATION1700(\"luy\"),\n    COLLATION1701(\"luy-KE\"),\n    COLLATION1702(\"luy_KE\"),\n    COLLATION1703(\"lv\"),\n    COLLATION1704(\"lv-LV\"),\n    COLLATION1705(\"lv_LV\"),\n    COLLATION1706(\"mas\"),\n    COLLATION1707(\"mas-KE\"),\n    COLLATION1708(\"mas_KE\"),\n    COLLATION1709(\"mas-TZ\"),\n    COLLATION1710(\"mas_TZ\"),\n    COLLATION1711(\"mer\"),\n    COLLATION1712(\"mer-KE\"),\n    COLLATION1713(\"mer_KE\"),\n    COLLATION1714(\"mfe\"),\n    COLLATION1715(\"mfe-MU\"),\n    COLLATION1716(\"mfe_MU\"),\n    COLLATION1717(\"mg\"),\n    COLLATION1718(\"mg-MG\"),\n    COLLATION1719(\"mg_MG\"),\n    COLLATION1720(\"mgh\"),\n    COLLATION1721(\"mgh-MZ\"),\n    COLLATION1722(\"mgh_MZ\"),\n    COLLATION1723(\"mgo\"),\n    COLLATION1724(\"mgo-CM\"),\n    COLLATION1725(\"mgo_CM\"),\n    COLLATION1726(\"mi\"),\n    COLLATION1727(\"mi-NZ\"),\n    COLLATION1728(\"mi_NZ\"),\n    COLLATION1729(\"mk\"),\n    COLLATION1730(\"mk-MK\"),\n    COLLATION1731(\"mk_MK\"),\n    COLLATION1732(\"ml\"),\n    COLLATION1733(\"ml-IN\"),\n    COLLATION1734(\"ml_IN\"),\n    COLLATION1735(\"mn\"),\n    COLLATION1736(\"mn-Cyrl\"),\n    COLLATION1737(\"mn_Cyrl\"),\n    COLLATION1738(\"mn-MN\"),\n    COLLATION1739(\"mn_MN\"),\n    COLLATION1740(\"mn-Mong\"),\n    COLLATION1741(\"mn_Mong\"),\n    COLLATION1742(\"mn-Mong-CN\"),\n    COLLATION1743(\"mn_Mong_CN\"),\n    COLLATION1744(\"mn-Mong-MN\"),\n    COLLATION1745(\"mn_Mong_MN\"),\n    COLLATION1746(\"mni\"),\n    COLLATION1747(\"mni-IN\"),\n    COLLATION1748(\"mni_IN\"),\n    COLLATION1749(\"moh\"),\n    COLLATION1750(\"moh-CA\"),\n    COLLATION1751(\"moh_CA\"),\n    COLLATION1752(\"mr\"),\n    COLLATION1753(\"mr-IN\"),\n    COLLATION1754(\"mr_IN\"),\n    COLLATION1755(\"ms\"),\n    COLLATION1756(\"ms-BN\"),\n    COLLATION1757(\"ms_BN\"),\n    COLLATION1758(\"ms-MY\"),\n    COLLATION1759(\"ms_MY\"),\n    COLLATION1760(\"ms-SG\"),\n    COLLATION1761(\"ms_SG\"),\n    COLLATION1762(\"mt\"),\n    COLLATION1763(\"mt-MT\"),\n    COLLATION1764(\"mt_MT\"),\n    COLLATION1765(\"mua\"),\n    COLLATION1766(\"mua-CM\"),\n    COLLATION1767(\"mua_CM\"),\n    COLLATION1768(\"my\"),\n    COLLATION1769(\"my-MM\"),\n    COLLATION1770(\"my_MM\"),\n    COLLATION1771(\"mzn\"),\n    COLLATION1772(\"mzn-IR\"),\n    COLLATION1773(\"mzn_IR\"),\n    COLLATION1774(\"naq\"),\n    COLLATION1775(\"naq-NA\"),\n    COLLATION1776(\"naq_NA\"),\n    COLLATION1777(\"nb\"),\n    COLLATION1778(\"nb-NO\"),\n    COLLATION1779(\"nb_NO\"),\n    COLLATION1780(\"nb-SJ\"),\n    COLLATION1781(\"nb_SJ\"),\n    COLLATION1782(\"nd\"),\n    COLLATION1783(\"nd-ZW\"),\n    COLLATION1784(\"nd_ZW\"),\n    COLLATION1785(\"nds\"),\n    COLLATION1786(\"nds-DE\"),\n    COLLATION1787(\"nds_DE\"),\n    COLLATION1788(\"nds-NL\"),\n    COLLATION1789(\"nds_NL\"),\n    COLLATION1790(\"ne\"),\n    COLLATION1791(\"ne-IN\"),\n    COLLATION1792(\"ne_IN\"),\n    COLLATION1793(\"ne-NP\"),\n    COLLATION1794(\"ne_NP\"),\n    COLLATION1795(\"nl\"),\n    COLLATION1796(\"nl-AW\"),\n    COLLATION1797(\"nl_AW\"),\n    COLLATION1798(\"nl-BE\"),\n    COLLATION1799(\"nl_BE\"),\n    COLLATION1800(\"nl-BQ\"),\n    COLLATION1801(\"nl_BQ\"),\n    COLLATION1802(\"nl-CW\"),\n    COLLATION1803(\"nl_CW\"),\n    COLLATION1804(\"nl-NL\"),\n    COLLATION1805(\"nl_NL\"),\n    COLLATION1806(\"nl-SR\"),\n    COLLATION1807(\"nl_SR\"),\n    COLLATION1808(\"nl-SX\"),\n    COLLATION1809(\"nl_SX\"),\n    COLLATION1810(\"nmg\"),\n    COLLATION1811(\"nmg-CM\"),\n    COLLATION1812(\"nmg_CM\"),\n    COLLATION1813(\"nn\"),\n    COLLATION1814(\"nn-NO\"),\n    COLLATION1815(\"nn_NO\"),\n    COLLATION1816(\"nnh\"),\n    COLLATION1817(\"nnh-CM\"),\n    COLLATION1818(\"nnh_CM\"),\n    COLLATION1819(\"no\"),\n    COLLATION1820(\"nqo\"),\n    COLLATION1821(\"nqo-GN\"),\n    COLLATION1822(\"nqo_GN\"),\n    COLLATION1823(\"nr\"),\n    COLLATION1824(\"nr-ZA\"),\n    COLLATION1825(\"nr_ZA\"),\n    COLLATION1826(\"nso\"),\n    COLLATION1827(\"nso-ZA\"),\n    COLLATION1828(\"nso_ZA\"),\n    COLLATION1829(\"nus\"),\n    COLLATION1830(\"nus-SS\"),\n    COLLATION1831(\"nus_SS\"),\n    COLLATION1832(\"nyn\"),\n    COLLATION1833(\"nyn-UG\"),\n    COLLATION1834(\"nyn_UG\"),\n    COLLATION1835(\"oc\"),\n    COLLATION1836(\"oc-FR\"),\n    COLLATION1837(\"oc_FR\"),\n    COLLATION1838(\"om\"),\n    COLLATION1839(\"om-ET\"),\n    COLLATION1840(\"om_ET\"),\n    COLLATION1841(\"om-KE\"),\n    COLLATION1842(\"om_KE\"),\n    COLLATION1843(\"or\"),\n    COLLATION1844(\"or-IN\"),\n    COLLATION1845(\"or_IN\"),\n    COLLATION1846(\"os\"),\n    COLLATION1847(\"os-GE\"),\n    COLLATION1848(\"os_GE\"),\n    COLLATION1849(\"os-RU\"),\n    COLLATION1850(\"os_RU\"),\n    COLLATION1851(\"pa\"),\n    COLLATION1852(\"pa-Arab\"),\n    COLLATION1853(\"pa_Arab\"),\n    COLLATION1854(\"pa-Arab-PK\"),\n    COLLATION1855(\"pa_Arab_PK\"),\n    COLLATION1856(\"pa-Guru\"),\n    COLLATION1857(\"pa_Guru\"),\n    COLLATION1858(\"pa-IN\"),\n    COLLATION1859(\"pa_IN\"),\n    COLLATION1860(\"pap\"),\n    COLLATION1861(\"pap-029\"),\n    COLLATION1862(\"pap_029\"),\n    COLLATION1863(\"pl\"),\n    COLLATION1864(\"pl-PL\"),\n    COLLATION1865(\"pl_PL\"),\n    COLLATION1866(\"prg\"),\n    COLLATION1867(\"prg-001\"),\n    COLLATION1868(\"prg_001\"),\n    COLLATION1869(\"prs\"),\n    COLLATION1870(\"prs-AF\"),\n    COLLATION1871(\"prs_AF\"),\n    COLLATION1872(\"ps\"),\n    COLLATION1873(\"ps-AF\"),\n    COLLATION1874(\"ps_AF\"),\n    COLLATION1875(\"ps-PK\"),\n    COLLATION1876(\"ps_PK\"),\n    COLLATION1877(\"pt\"),\n    COLLATION1878(\"pt-AO\"),\n    COLLATION1879(\"pt_AO\"),\n    COLLATION1880(\"pt-BR\"),\n    COLLATION1881(\"pt_BR\"),\n    COLLATION1882(\"pt-CH\"),\n    COLLATION1883(\"pt_CH\"),\n    COLLATION1884(\"pt-CV\"),\n    COLLATION1885(\"pt_CV\"),\n    COLLATION1886(\"pt-GQ\"),\n    COLLATION1887(\"pt_GQ\"),\n    COLLATION1888(\"pt-GW\"),\n    COLLATION1889(\"pt_GW\"),\n    COLLATION1890(\"pt-LU\"),\n    COLLATION1891(\"pt_LU\"),\n    COLLATION1892(\"pt-MO\"),\n    COLLATION1893(\"pt_MO\"),\n    COLLATION1894(\"pt-MZ\"),\n    COLLATION1895(\"pt_MZ\"),\n    COLLATION1896(\"pt-PT\"),\n    COLLATION1897(\"pt_PT\"),\n    COLLATION1898(\"pt-ST\"),\n    COLLATION1899(\"pt_ST\"),\n    COLLATION1900(\"pt-TL\"),\n    COLLATION1901(\"pt_TL\"),\n    COLLATION1902(\"quc\"),\n    COLLATION1903(\"quc-Latn\"),\n    COLLATION1904(\"quc_Latn\"),\n    COLLATION1905(\"quc-Latn-GT\"),\n    COLLATION1906(\"quc_Latn_GT\"),\n    COLLATION1907(\"quz\"),\n    COLLATION1908(\"quz-BO\"),\n    COLLATION1909(\"quz_BO\"),\n    COLLATION1910(\"quz-EC\"),\n    COLLATION1911(\"quz_EC\"),\n    COLLATION1912(\"quz-PE\"),\n    COLLATION1913(\"quz_PE\"),\n    COLLATION1914(\"rm\"),\n    COLLATION1915(\"rm-CH\"),\n    COLLATION1916(\"rm_CH\"),\n    COLLATION1917(\"rn\"),\n    COLLATION1918(\"rn-BI\"),\n    COLLATION1919(\"rn_BI\"),\n    COLLATION1920(\"ro\"),\n    COLLATION1921(\"ro-MD\"),\n    COLLATION1922(\"ro_MD\"),\n    COLLATION1923(\"ro-RO\"),\n    COLLATION1924(\"ro_RO\"),\n    COLLATION1925(\"rof\"),\n    COLLATION1926(\"rof-TZ\"),\n    COLLATION1927(\"rof_TZ\"),\n    COLLATION1928(\"ru\"),\n    COLLATION1929(\"ru-BY\"),\n    COLLATION1930(\"ru_BY\"),\n    COLLATION1931(\"ru-KG\"),\n    COLLATION1932(\"ru_KG\"),\n    COLLATION1933(\"ru-KZ\"),\n    COLLATION1934(\"ru_KZ\"),\n    COLLATION1935(\"ru-MD\"),\n    COLLATION1936(\"ru_MD\"),\n    COLLATION1937(\"ru-RU\"),\n    COLLATION1938(\"ru_RU\"),\n    COLLATION1939(\"ru-UA\"),\n    COLLATION1940(\"ru_UA\"),\n    COLLATION1941(\"rw\"),\n    COLLATION1942(\"rw-RW\"),\n    COLLATION1943(\"rw_RW\"),\n    COLLATION1944(\"rwk\"),\n    COLLATION1945(\"rwk-TZ\"),\n    COLLATION1946(\"rwk_TZ\"),\n    COLLATION1947(\"sa\"),\n    COLLATION1948(\"sa-IN\"),\n    COLLATION1949(\"sa_IN\"),\n    COLLATION1950(\"sah\"),\n    COLLATION1951(\"sah-RU\"),\n    COLLATION1952(\"sah_RU\"),\n    COLLATION1953(\"saq\"),\n    COLLATION1954(\"saq-KE\"),\n    COLLATION1955(\"saq_KE\"),\n    COLLATION1956(\"sbp\"),\n    COLLATION1957(\"sbp-TZ\"),\n    COLLATION1958(\"sbp_TZ\"),\n    COLLATION1959(\"sd\"),\n    COLLATION1960(\"sd-Arab\"),\n    COLLATION1961(\"sd_Arab\"),\n    COLLATION1962(\"sd-Arab-PK\"),\n    COLLATION1963(\"sd_Arab_PK\"),\n    COLLATION1964(\"sd-Deva\"),\n    COLLATION1965(\"sd_Deva\"),\n    COLLATION1966(\"sd-Deva-IN\"),\n    COLLATION1967(\"sd_Deva_IN\"),\n    COLLATION1968(\"se\"),\n    COLLATION1969(\"se-FI\"),\n    COLLATION1970(\"se_FI\"),\n    COLLATION1971(\"se-NO\"),\n    COLLATION1972(\"se_NO\"),\n    COLLATION1973(\"se-SE\"),\n    COLLATION1974(\"se_SE\"),\n    COLLATION1975(\"seh\"),\n    COLLATION1976(\"seh-MZ\"),\n    COLLATION1977(\"seh_MZ\"),\n    COLLATION1978(\"ses\"),\n    COLLATION1979(\"ses-ML\"),\n    COLLATION1980(\"ses_ML\"),\n    COLLATION1981(\"sg\"),\n    COLLATION1982(\"sg-CF\"),\n    COLLATION1983(\"sg_CF\"),\n    COLLATION1984(\"shi\"),\n    COLLATION1985(\"shi-Latn\"),\n    COLLATION1986(\"shi_Latn\"),\n    COLLATION1987(\"shi-Latn-MA\"),\n    COLLATION1988(\"shi_Latn_MA\"),\n    COLLATION1989(\"shi-Tfng\"),\n    COLLATION1990(\"shi_Tfng\"),\n    COLLATION1991(\"shi-Tfng-MA\"),\n    COLLATION1992(\"shi_Tfng_MA\"),\n    COLLATION1993(\"si\"),\n    COLLATION1994(\"si-LK\"),\n    COLLATION1995(\"si_LK\"),\n    COLLATION1996(\"sk\"),\n    COLLATION1997(\"sk-SK\"),\n    COLLATION1998(\"sk_SK\"),\n    COLLATION1999(\"sl\"),\n    COLLATION2000(\"sl-SI\"),\n    COLLATION2001(\"sl_SI\"),\n    COLLATION2002(\"sma\"),\n    COLLATION2003(\"sma-NO\"),\n    COLLATION2004(\"sma_NO\"),\n    COLLATION2005(\"sma-SE\"),\n    COLLATION2006(\"sma_SE\"),\n    COLLATION2007(\"smj\"),\n    COLLATION2008(\"smj-NO\"),\n    COLLATION2009(\"smj_NO\"),\n    COLLATION2010(\"smj-SE\"),\n    COLLATION2011(\"smj_SE\"),\n    COLLATION2012(\"smn\"),\n    COLLATION2013(\"smn-FI\"),\n    COLLATION2014(\"smn_FI\"),\n    COLLATION2015(\"sms\"),\n    COLLATION2016(\"sms-FI\"),\n    COLLATION2017(\"sms_FI\"),\n    COLLATION2018(\"sn\"),\n    COLLATION2019(\"sn-Latn\"),\n    COLLATION2020(\"sn_Latn\"),\n    COLLATION2021(\"sn-Latn-ZW\"),\n    COLLATION2022(\"sn_Latn_ZW\"),\n    COLLATION2023(\"so\"),\n    COLLATION2024(\"so-DJ\"),\n    COLLATION2025(\"so_DJ\"),\n    COLLATION2026(\"so-ET\"),\n    COLLATION2027(\"so_ET\"),\n    COLLATION2028(\"so-KE\"),\n    COLLATION2029(\"so_KE\"),\n    COLLATION2030(\"so-SO\"),\n    COLLATION2031(\"so_SO\"),\n    COLLATION2032(\"sq\"),\n    COLLATION2033(\"sq-AL\"),\n    COLLATION2034(\"sq_AL\"),\n    COLLATION2035(\"sq-MK\"),\n    COLLATION2036(\"sq_MK\"),\n    COLLATION2037(\"sq-XK\"),\n    COLLATION2038(\"sq_XK\"),\n    COLLATION2039(\"sr\"),\n    COLLATION2040(\"sr-Cyrl\"),\n    COLLATION2041(\"sr_Cyrl\"),\n    COLLATION2042(\"sr-Cyrl-BA\"),\n    COLLATION2043(\"sr_Cyrl_BA\"),\n    COLLATION2044(\"sr-Cyrl-ME\"),\n    COLLATION2045(\"sr_Cyrl_ME\"),\n    COLLATION2046(\"sr-Cyrl-RS\"),\n    COLLATION2047(\"sr_Cyrl_RS\"),\n    COLLATION2048(\"sr-Cyrl-XK\"),\n    COLLATION2049(\"sr_Cyrl_XK\"),\n    COLLATION2050(\"sr-Latn\"),\n    COLLATION2051(\"sr_Latn\"),\n    COLLATION2052(\"sr-Latn-BA\"),\n    COLLATION2053(\"sr_Latn_BA\"),\n    COLLATION2054(\"sr-Latn-ME\"),\n    COLLATION2055(\"sr_Latn_ME\"),\n    COLLATION2056(\"sr-Latn-RS\"),\n    COLLATION2057(\"sr_Latn_RS\"),\n    COLLATION2058(\"sr-Latn-XK\"),\n    COLLATION2059(\"sr_Latn_XK\"),\n    COLLATION2060(\"ss\"),\n    COLLATION2061(\"ss-SZ\"),\n    COLLATION2062(\"ss_SZ\"),\n    COLLATION2063(\"ss-ZA\"),\n    COLLATION2064(\"ss_ZA\"),\n    COLLATION2065(\"ssy\"),\n    COLLATION2066(\"ssy-ER\"),\n    COLLATION2067(\"ssy_ER\"),\n    COLLATION2068(\"st\"),\n    COLLATION2069(\"st-LS\"),\n    COLLATION2070(\"st_LS\"),\n    COLLATION2071(\"st-ZA\"),\n    COLLATION2072(\"st_ZA\"),\n    COLLATION2073(\"sv\"),\n    COLLATION2074(\"sv-AX\"),\n    COLLATION2075(\"sv_AX\"),\n    COLLATION2076(\"sv-FI\"),\n    COLLATION2077(\"sv_FI\"),\n    COLLATION2078(\"sv-SE\"),\n    COLLATION2079(\"sv_SE\"),\n    COLLATION2080(\"sw\"),\n    COLLATION2081(\"sw-CD\"),\n    COLLATION2082(\"sw_CD\"),\n    COLLATION2083(\"sw-KE\"),\n    COLLATION2084(\"sw_KE\"),\n    COLLATION2085(\"sw-TZ\"),\n    COLLATION2086(\"sw_TZ\"),\n    COLLATION2087(\"sw-UG\"),\n    COLLATION2088(\"sw_UG\"),\n    COLLATION2089(\"syr\"),\n    COLLATION2090(\"syr-SY\"),\n    COLLATION2091(\"syr_SY\"),\n    COLLATION2092(\"ta\"),\n    COLLATION2093(\"ta-IN\"),\n    COLLATION2094(\"ta_IN\"),\n    COLLATION2095(\"ta-LK\"),\n    COLLATION2096(\"ta_LK\"),\n    COLLATION2097(\"ta-MY\"),\n    COLLATION2098(\"ta_MY\"),\n    COLLATION2099(\"ta-SG\"),\n    COLLATION2100(\"ta_SG\"),\n    COLLATION2101(\"te\"),\n    COLLATION2102(\"te-IN\"),\n    COLLATION2103(\"te_IN\"),\n    COLLATION2104(\"teo\"),\n    COLLATION2105(\"teo-KE\"),\n    COLLATION2106(\"teo_KE\"),\n    COLLATION2107(\"teo-UG\"),\n    COLLATION2108(\"teo_UG\"),\n    COLLATION2109(\"tg\"),\n    COLLATION2110(\"tg-Cyrl\"),\n    COLLATION2111(\"tg_Cyrl\"),\n    COLLATION2112(\"tg-Cyrl-TJ\"),\n    COLLATION2113(\"tg_Cyrl_TJ\"),\n    COLLATION2114(\"th\"),\n    COLLATION2115(\"th-TH\"),\n    COLLATION2116(\"th_TH\"),\n    COLLATION2117(\"ti\"),\n    COLLATION2118(\"ti-ER\"),\n    COLLATION2119(\"ti_ER\"),\n    COLLATION2120(\"ti-ET\"),\n    COLLATION2121(\"ti_ET\"),\n    COLLATION2122(\"tig\"),\n    COLLATION2123(\"tig-ER\"),\n    COLLATION2124(\"tig_ER\"),\n    COLLATION2125(\"tk\"),\n    COLLATION2126(\"tk-TM\"),\n    COLLATION2127(\"tk_TM\"),\n    COLLATION2128(\"tn\"),\n    COLLATION2129(\"tn-BW\"),\n    COLLATION2130(\"tn_BW\"),\n    COLLATION2131(\"tn-ZA\"),\n    COLLATION2132(\"tn_ZA\"),\n    COLLATION2133(\"to\"),\n    COLLATION2134(\"to-TO\"),\n    COLLATION2135(\"to_TO\"),\n    COLLATION2136(\"tr\"),\n    COLLATION2137(\"tr-CY\"),\n    COLLATION2138(\"tr_CY\"),\n    COLLATION2139(\"tr-TR\"),\n    COLLATION2140(\"tr_TR\"),\n    COLLATION2141(\"ts\"),\n    COLLATION2142(\"ts-ZA\"),\n    COLLATION2143(\"ts_ZA\"),\n    COLLATION2144(\"tt\"),\n    COLLATION2145(\"tt-RU\"),\n    COLLATION2146(\"tt_RU\"),\n    COLLATION2147(\"twq\"),\n    COLLATION2148(\"twq-NE\"),\n    COLLATION2149(\"twq_NE\"),\n    COLLATION2150(\"tzm\"),\n    COLLATION2151(\"tzm-Arab\"),\n    COLLATION2152(\"tzm_Arab\"),\n    COLLATION2153(\"tzm-Arab-MA\"),\n    COLLATION2154(\"tzm_Arab_MA\"),\n    COLLATION2155(\"tzm-Latn\"),\n    COLLATION2156(\"tzm_Latn\"),\n    COLLATION2157(\"tzm-Latn-DZ\"),\n    COLLATION2158(\"tzm_Latn_DZ\"),\n    COLLATION2159(\"tzm-Latn-MA\"),\n    COLLATION2160(\"tzm_Latn_MA\"),\n    COLLATION2161(\"tzm-Tfng\"),\n    COLLATION2162(\"tzm_Tfng\"),\n    COLLATION2163(\"tzm-Tfng-MA\"),\n    COLLATION2164(\"tzm_Tfng_MA\"),\n    COLLATION2165(\"ug\"),\n    COLLATION2166(\"ug-CN\"),\n    COLLATION2167(\"ug_CN\"),\n    COLLATION2168(\"uk\"),\n    COLLATION2169(\"uk-UA\"),\n    COLLATION2170(\"uk_UA\"),\n    COLLATION2171(\"ur\"),\n    COLLATION2172(\"ur-IN\"),\n    COLLATION2173(\"ur_IN\"),\n    COLLATION2174(\"ur-PK\"),\n    COLLATION2175(\"ur_PK\"),\n    COLLATION2176(\"uz\"),\n    COLLATION2177(\"uz-Arab\"),\n    COLLATION2178(\"uz_Arab\"),\n    COLLATION2179(\"uz-Arab-AF\"),\n    COLLATION2180(\"uz_Arab_AF\"),\n    COLLATION2181(\"uz-Cyrl\"),\n    COLLATION2182(\"uz_Cyrl\"),\n    COLLATION2183(\"uz-Cyrl-UZ\"),\n    COLLATION2184(\"uz_Cyrl_UZ\"),\n    COLLATION2185(\"uz-Latn\"),\n    COLLATION2186(\"uz_Latn\"),\n    COLLATION2187(\"uz-Latn-UZ\"),\n    COLLATION2188(\"uz_Latn_UZ\"),\n    COLLATION2189(\"vai\"),\n    COLLATION2190(\"vai-Latn\"),\n    COLLATION2191(\"vai_Latn\"),\n    COLLATION2192(\"vai-Latn-LR\"),\n    COLLATION2193(\"vai_Latn_LR\"),\n    COLLATION2194(\"vai-Vaii\"),\n    COLLATION2195(\"vai_Vaii\"),\n    COLLATION2196(\"vai-Vaii-LR\"),\n    COLLATION2197(\"vai_Vaii_LR\"),\n    COLLATION2198(\"ve\"),\n    COLLATION2199(\"ve-ZA\"),\n    COLLATION2200(\"ve_ZA\"),\n    COLLATION2201(\"vi\"),\n    COLLATION2202(\"vi-VN\"),\n    COLLATION2203(\"vi_VN\"),\n    COLLATION2204(\"vo\"),\n    COLLATION2205(\"vo-001\"),\n    COLLATION2206(\"vo_001\"),\n    COLLATION2207(\"vun\"),\n    COLLATION2208(\"vun-TZ\"),\n    COLLATION2209(\"vun_TZ\"),\n    COLLATION2210(\"wae\"),\n    COLLATION2211(\"wae-CH\"),\n    COLLATION2212(\"wae_CH\"),\n    COLLATION2213(\"wal\"),\n    COLLATION2214(\"wal-ET\"),\n    COLLATION2215(\"wal_ET\"),\n    COLLATION2216(\"wo\"),\n    COLLATION2217(\"wo-SN\"),\n    COLLATION2218(\"wo_SN\"),\n    COLLATION2219(\"x-IV_mathan\"),\n    COLLATION2220(\"x_IV_mathan\"),\n    COLLATION2221(\"xh\"),\n    COLLATION2222(\"xh-ZA\"),\n    COLLATION2223(\"xh_ZA\"),\n    COLLATION2224(\"xog\"),\n    COLLATION2225(\"xog-UG\"),\n    COLLATION2226(\"xog_UG\"),\n    COLLATION2227(\"yav\"),\n    COLLATION2228(\"yav-CM\"),\n    COLLATION2229(\"yav_CM\"),\n    COLLATION2230(\"yi\"),\n    COLLATION2231(\"yi-001\"),\n    COLLATION2232(\"yi_001\"),\n    COLLATION2233(\"yo\"),\n    COLLATION2234(\"yo-BJ\"),\n    COLLATION2235(\"yo_BJ\"),\n    COLLATION2236(\"yo-NG\"),\n    COLLATION2237(\"yo_NG\"),\n    COLLATION2238(\"zgh\"),\n    COLLATION2239(\"zgh-Tfng\"),\n    COLLATION2240(\"zgh_Tfng\"),\n    COLLATION2241(\"zgh-Tfng-MA\"),\n    COLLATION2242(\"zgh_Tfng_MA\"),\n    COLLATION2243(\"zu\"),\n    COLLATION2244(\"zu-ZA\"),\n    COLLATION2245(\"zu_ZA\"),\n\n\n            ;\n    private Collation collation;\n\n    PostgreSQLCollationEnum(String collationName) {\n        this.collation = new Collation(collationName);\n    }\n\n    public static List<Collation> getCollations() {\n        return Arrays.asList(PostgreSQLCollationEnum.values()).stream().map(PostgreSQLCollationEnum::getCollation).collect(java.util.stream.Collectors.toList());\n    }\n\n    public Collation getCollation() {\n        return collation;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/type/PostgreSQLColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.postgresql.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum PostgreSQLColumnTypeEnum implements ColumnBuilder {\n\n    BIGSERIAL(\"BIGSERIAL\", false, false, true, false, false, false, true, true, false, false),\n    BIT(\"BIT\", true, false, true, false, false, false, true, true, false, false),\n    BOOL(\"BOOL\", false, false, true, false, false, false, true, true, false, false),\n    BOX(\"BOX\", false, false, true, false, false, false, true, true, false, false),\n    BYTEA(\"BYTEA\", false, false, true, false, false, false, true, true, false, false),\n    CHAR(\"CHAR\", true, false, true, false, false, true, true, true, false, false),\n    CIDR(\"CIDR\", false, false, true, false, false, false, true, true, false, false),\n    CIRCLE(\"CIRCLE\", false, false, true, false, false, false, true, true, false, false),\n    DATE(\"DATE\", false, false, true, false, false, false, true, true, false, false),\n    DECIMAL(\"DECIMAL\", true, true, true, false, false, false, true, true, false, false),\n    FLOAT4(\"FLOAT4\", false, false, true, false, false, false, true, true, false, false),\n    FLOAT8(\"FLOAT8\", false, false, true, false, false, false, true, true, false, false),\n    INET(\"INET\", false, false, true, false, false, false, true, true, false, false),\n    INT2(\"INT2\", false, false, true, false, false, false, true, true, false, false),\n    INT4(\"INT4\", false, false, true, false, false, false, true, true, false, false),\n    INT8(\"INT8\", false, false, true, false, false, false, true, true, false, false),\n    INTERVAL(\"INTERVAL\", false, false, true, false, false, false, true, true, false, false),\n    JSON(\"JSON\", false, false, true, false, false, false, true, true, false, false),\n    JSONB(\"JSONB\", false, false, true, false, false, false, true, true, false, false),\n    LINE(\"LINE\", false, false, true, false, false, false, true, true, false, false),\n    LSEG(\"LSEG\", false, false, true, false, false, false, true, true, false, false),\n    MACADDR(\"MACADDR\", false, false, true, false, false, false, true, true, false, false),\n    MONEY(\"MONEY\", false, false, true, false, false, false, true, true, false, false),\n    NUMERIC(\"NUMERIC\", true, true, true, false, false, false, true, true, false, false),\n    PATH(\"PATH\", false, false, true, false, false, false, true, true, false, false),\n    POINT(\"POINT\", false, false, true, false, false, false, true, true, false, false),\n    POLYGON(\"POLYGON\", false, false, true, false, false, false, true, true, false, false),\n    SERIAL(\"SERIAL\", false, false, true, false, false, false, true, true, false, false),\n    SERIAL2(\"SERIAL2\", false, false, true, false, false, false, true, true, false, false),\n    SERIAL4(\"SERIAL4\", false, false, true, false, false, false, true, true, false, false),\n    SERIAL8(\"SERIAL8\", false, false, true, false, false, false, true, true, false, false),\n    SMALLSERIAL(\"SMALLSERIAL\", false, false, true, false, false, false, true, true, false, false),\n    TEXT(\"TEXT\", false, false, true, false, false, true, true, true, false, false),\n    TIME(\"TIME\", true, false, true, false, false, false, true, true, false, false),\n    TIMESTAMP(\"TIMESTAMP\", true, false, true, false, false, false, true, true, false, false),\n    TIMESTAMPTZ(\"TIMESTAMPTZ\", true, false, true, false, false, false, true, true, false, false),\n    TIMETZ(\"TIMETZ\", true, false, true, false, false, false, true, true, false, false),\n    TSQUERY(\"TSQUERY\", false, false, true, false, false, false, true, true, false, false),\n    TSVECTOR(\"TSVECTOR\", false, false, true, false, false, false, true, true, false, false),\n    TXID_SNAPSHOT(\"TXID_SNAPSHOT\", false, false, true, false, false, false, true, true, false, false),\n    UUID(\"UUID\", false, false, true, false, false, false, true, true, false, false),\n    VARBIT(\"VARBIT\", true, false, true, false, false, false, true, true, false, false),\n    VARCHAR(\"VARCHAR\", true, false, true, false, false, true, true, true, false, false),\n    XML(\"XML\", false, false, true, false, false, false, true, true, false, false),\n\n    ;\n\n    private static Map<String, PostgreSQLColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (PostgreSQLColumnTypeEnum value : PostgreSQLColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n    private ColumnType columnType;\n\n\n    PostgreSQLColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportValue) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, supportValue, false);\n    }\n\n    public static PostgreSQLColumnTypeEnum getByType(String dataType) {\n        return COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n    }\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(PostgreSQLColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        PostgreSQLColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"\\\"\").append(column.getName()).append(\"\\\"\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n\n        script.append(buildCollation(column, type)).append(\" \");\n\n        script.append(buildNullable(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n    private String buildCollation(TableColumn column, PostgreSQLColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportCollation() || StringUtils.isEmpty(column.getCollationName())) {\n            return \"\";\n        }\n        return StringUtils.join(\"\\\"\", column.getCollationName(), \"\\\"\");\n    }\n\n    @Override\n    public String buildModifyColumn(TableColumn column) {\n\n        if (EditStatus.DELETE.name().equals(column.getEditStatus())) {\n            return StringUtils.join(\"DROP COLUMN \\\"\", column.getName() + \"\\\"\");\n        }\n        if (EditStatus.ADD.name().equals(column.getEditStatus())) {\n            return StringUtils.join(\"ADD COLUMN \", buildCreateColumnSql(column));\n        }\n        if (EditStatus.MODIFY.name().equals(column.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER COLUMN \\\"\").append(column.getName()).append(\"\\\" TYPE \").append(buildDataType(column, this)).append(\",\\n\");\n            if (column.getNullable() != null && 1 == column.getNullable()) {\n                script.append(\"\\t\").append(\"ALTER COLUMN \\\"\").append(column.getName()).append(\"\\\" DROP NOT NULL ,\\n\");\n            } else {\n                script.append(\"\\t\").append(\"ALTER COLUMN \\\"\").append(column.getName()).append(\"\\\" SET NOT NULL ,\\n\");\n\n            }\n            String defaultValue = buildDefaultValue(column, this);\n            if (StringUtils.isNotBlank(defaultValue)) {\n                script.append(\"ALTER COLUMN \\\"\").append(column.getName()).append(\"\\\" SET \").append(defaultValue).append(\",\\n\");\n            }\n            script = new StringBuilder(script.substring(0, script.length() - 2));\n            return script.toString();\n        }\n        return \"\";\n    }\n\n    public String buildComment(TableColumn column, PostgreSQLColumnTypeEnum type) {\n        if (!this.columnType.isSupportComments() || column.getComment() == null\n                || EditStatus.DELETE.name().equals(column.getEditStatus())) {\n            return \"\";\n        }\n        return StringUtils.join(\"COMMENT ON COLUMN\", \" \\\"\", column.getTableName(),\n                \"\\\".\\\"\", column.getName(), \"\\\" IS '\", column.getComment(), \"';\");\n    }\n\n    private String buildDefaultValue(TableColumn column, PostgreSQLColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {\n            return \"\";\n        }\n\n        if(\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())){\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if(\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())){\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        if (Arrays.asList(CHAR, VARCHAR).contains(type)) {\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        if (Arrays.asList(TIMESTAMP, TIME, TIMETZ, TIMESTAMPTZ, DATE).contains(type)) {\n            if (\"CURRENT_TIMESTAMP\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n                return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n            }\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildNullable(TableColumn column, PostgreSQLColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportNullable()) {\n            return \"\";\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"NULL\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDataType(TableColumn column, PostgreSQLColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(VARCHAR, CHAR).contains(type)) {\n            if (column.getColumnSize() == null ) {\n                return columnType;\n            }\n            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n        }\n\n        if (Arrays.asList(VARBIT, BIT).contains(type)) {\n            if (column.getColumnSize() == null ) {\n                return columnType;\n            }\n            return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n        }\n\n        if (Arrays.asList(TIME, TIMETZ, TIMESTAMPTZ, TIMESTAMP).contains(type)) {\n            if (column.getColumnSize() == null || column.getColumnSize() == 0) {\n                return columnType;\n            } else {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize(), \")\");\n            }\n        }\n\n        if (Arrays.asList(DECIMAL, NUMERIC).contains(type)) {\n            if (column.getColumnSize() == null && column.getDecimalDigits() == null) {\n                return columnType;\n            }\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \")\");\n            } else {\n                return StringUtils.join(columnType, \"(\", column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n            }\n        }\n        return columnType;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/type/PostgreSQLDefaultValueEnum.java",
    "content": "package ai.chat2db.plugin.postgresql.type;\n\nimport ai.chat2db.spi.model.DefaultValue;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum PostgreSQLDefaultValueEnum {\n    EMPTY_STRING(\"EMPTY_STRING\"),\n    NULL(\"NULL\"),\n    ;\n    private DefaultValue defaultValue;\n\n    PostgreSQLDefaultValueEnum(String defaultValue) {\n        this.defaultValue = new DefaultValue(defaultValue);\n    }\n\n\n    public DefaultValue getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static List<DefaultValue> getDefaultValues() {\n        return Arrays.stream(PostgreSQLDefaultValueEnum.values()).map(PostgreSQLDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/type/PostgreSQLIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.postgresql.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum PostgreSQLIndexTypeEnum {\n\n    PRIMARY(\"Primary\", \"PRIMARY KEY\"),\n\n    FOREIGN(\"Foreign\", \"FOREIGN KEY\"),\n\n    NORMAL(\"Normal\", \"INDEX\"),\n\n    UNIQUE(\"Unique\", \"UNIQUE\"),\n    ;\n\n    private String name;\n    private String keyword;\n\n    private IndexType indexType;\n\n\n    PostgreSQLIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType =new IndexType(name);\n    }\n\n    public static PostgreSQLIndexTypeEnum getByType(String type) {\n        for (PostgreSQLIndexTypeEnum value : PostgreSQLIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(PostgreSQLIndexTypeEnum.values()).stream().map(PostgreSQLIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        if (NORMAL.equals(this)) {\n            script.append(\"CREATE\").append(\" \");\n            script.append(buildIndexUnique(tableIndex)).append(\" \");\n            script.append(buildIndexConcurrently(tableIndex)).append(\" \");\n            script.append(buildIndexName(tableIndex)).append(\" \");\n            script.append(\"ON \").append(\"\\\"\").append(tableIndex.getTableName()).append(\"\\\"\").append(\" \");\n            script.append(buildIndexMethod(tableIndex)).append(\" \");\n            script.append(buildIndexColumn(tableIndex));\n        } else {\n            script.append(\"CONSTRAINT\").append(\" \");\n            script.append(buildIndexName(tableIndex)).append(\" \");\n            script.append(keyword).append(\" \");\n            script.append(buildIndexColumn(tableIndex));\n            script.append(buildForeignColum(tableIndex));\n        }\n        return script.toString();\n    }\n\n    private String buildForeignColum(TableIndex tableIndex) {\n        if (FOREIGN.equals(this)) {\n            StringBuilder script = new StringBuilder();\n            script.append(\" REFERENCES \");\n            if (StringUtils.isNotBlank(tableIndex.getForeignSchemaName())) {\n                script.append(tableIndex.getForeignSchemaName()).append(\".\");\n            }\n            if (StringUtils.isNotBlank(tableIndex.getForeignTableName())) {\n                script.append(tableIndex.getForeignTableName()).append(\" \");\n            }\n            if (CollectionUtils.isNotEmpty(tableIndex.getForeignColumnNamelist())) {\n                script.append(\"(\");\n                for (String column : tableIndex.getForeignColumnNamelist()) {\n                    if (StringUtils.isNotBlank(column)) {\n                        script.append(\"\\\"\").append(column).append(\"\\\"\").append(\",\");\n                    }\n                }\n                script.deleteCharAt(script.length() - 1);\n                script.append(\")\");\n            }\n            return script.toString();\n        }\n        return \"\";\n    }\n\n    private String buildIndexMethod(TableIndex tableIndex) {\n        if (StringUtils.isNotBlank(tableIndex.getMethod())) {\n            return \"USING \" + tableIndex.getMethod();\n        } else {\n            return \"\";\n        }\n    }\n\n    private String buildIndexConcurrently(TableIndex tableIndex) {\n        if (BooleanUtils.isTrue(tableIndex.getConcurrently())) {\n            return \"CONCURRENTLY\";\n        } else {\n            return \"\";\n        }\n    }\n\n    private String buildIndexUnique(TableIndex tableIndex) {\n        if (BooleanUtils.isTrue(tableIndex.getUnique())) {\n            return \"UNIQUE \" + keyword;\n        } else {\n            return keyword;\n        }\n    }\n\n    public String buildIndexComment(TableIndex tableIndex) {\n        if (StringUtils.isBlank(tableIndex.getComment()) || EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return \"\";\n        } else if (NORMAL.equals(this)) {\n            return StringUtils.join(\"COMMENT ON INDEX\", \" \",\n                    \"\\\"\", tableIndex.getName(), \"\\\" IS '\", tableIndex.getComment(), \"';\");\n        } else {\n            return StringUtils.join(\"COMMENT ON CONSTRAINT\", \" \\\"\", tableIndex.getName(), \"\\\" ON \\\"\", tableIndex.getSchemaName(),\n                    \"\\\".\\\"\", tableIndex.getTableName(), \"\\\" IS '\", tableIndex.getComment(), \"';\");\n        }\n    }\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"\\\"\").append(column.getColumnName()).append(\"\\\"\").append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        return \"\\\"\" + tableIndex.getName() + \"\\\"\";\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        boolean isNormal = NORMAL.equals(this);\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex), isNormal ? \";\\n\" : \",\\n\\tADD \", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(isNormal ? \"\" : \"ADD \", buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (NORMAL.equals(this)) {\n            return StringUtils.join(\"DROP INDEX \\\"\", tableIndex.getOldName(), \"\\\"\");\n        }\n        return StringUtils.join(\"DROP CONSTRAINT \\\"\", tableIndex.getOldName(), \"\\\"\");\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.postgresql.PostgreSQLPlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-presto/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n\n    <artifactId>chat2db-presto</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-presto/src/main/java/ai/chat2db/plugin/presto/PrestoDBManage.java",
    "content": "package ai.chat2db.plugin.presto;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\n\npublic class PrestoDBManage extends DefaultDBManage implements DBManage {\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-presto/src/main/java/ai/chat2db/plugin/presto/PrestoMetaData.java",
    "content": "package ai.chat2db.plugin.presto;\n\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\n\npublic class PrestoMetaData extends DefaultMetaService implements MetaData {\n    public String tableDDL(String databaseName, String schemaName,String tableName) {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-presto/src/main/java/ai/chat2db/plugin/presto/PrestoPlugin.java",
    "content": "package ai.chat2db.plugin.presto;\n\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class PrestoPlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"presto.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new PrestoMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new PrestoDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-presto/src/main/java/ai/chat2db/plugin/presto/presto.json",
    "content": "{\n  \"dbType\": \"PRESTO\",\n  \"supportDatabase\": true,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:presto://localhost:8080/\",\n      \"custom\": false,\n      \"defaultDriver\": true,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/presto-jdbc-0.245.1.jar\"\n      ],\n      \"jdbcDriver\": \"presto-jdbc-0.245.1.jar\",\n      \"jdbcDriverClass\": \"com.facebook.presto.jdbc.PrestoDriver\"\n    }\n  ],\n  \"name\": \"Presto\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-presto/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.presto.PrestoPlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <artifactId>chat2db-sqlite</artifactId>\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteDBManage.java",
    "content": "package ai.chat2db.plugin.sqlite;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\npublic class SqliteDBManage extends DefaultDBManage implements DBManage {\n\n    @Override\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        exportTables(connection, databaseName, schemaName,asyncContext);\n        exportViews(connection, databaseName, asyncContext);\n        exportTriggers(connection, asyncContext);\n    }\n\n    private void exportTables(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, null, null, new String[]{\"TABLE\", \"SYSTEM TABLE\"})) {\n            while (resultSet.next()) {\n                exportTable(connection, databaseName,schemaName, resultSet.getString(\"TABLE_NAME\"), asyncContext);\n            }\n        }\n    }\n\n\n    public void exportTable(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT sql FROM sqlite_master WHERE type='table' AND name='%s'\", tableName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP TABLE IF EXISTS \").append(format(tableName)).append(\";\").append(\"\\n\")\n                        .append(resultSet.getString(\"sql\")).append(\";\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n                if (asyncContext.isContainsData()) {\n                    exportTableData(connection, databaseName,schemaName, tableName, asyncContext);\n                }\n            }\n        }\n    }\n\n    private String format(String tableName) {\n        return \"\\\"\"+tableName+\"\\\"\";\n    }\n\n    private void exportViews(Connection connection, String databaseName, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, null, null, new String[]{\"VIEW\"})) {\n            while (resultSet.next()) {\n                exportView(connection, resultSet.getString(\"TABLE_NAME\"), asyncContext);\n            }\n        }\n    }\n\n    private void exportView(Connection connection, String viewName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT * FROM sqlite_master WHERE type = 'view' and name='%s';\", viewName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP VIEW IF EXISTS \").append(format(viewName)).append(\";\").append(\"\\n\")\n                        .append(resultSet.getString(\"sql\")).append(\";\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportTriggers(Connection connection, AsyncContext asyncContext) throws SQLException {\n        String sql = \"SELECT * FROM sqlite_master WHERE type = 'trigger';\";\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String triggerName = resultSet.getString(\"name\");\n                exportTrigger(connection, triggerName, asyncContext);\n            }\n        }\n    }\n\n    private void exportTrigger(Connection connection, String triggerName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT * FROM sqlite_master WHERE type = 'trigger' and name='%s';\", triggerName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"sql\")).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteMetaData.java",
    "content": "package ai.chat2db.plugin.sqlite;\n\nimport ai.chat2db.plugin.sqlite.builder.SqliteBuilder;\nimport ai.chat2db.plugin.sqlite.type.SqliteCollationEnum;\nimport ai.chat2db.plugin.sqlite.type.SqliteColumnTypeEnum;\nimport ai.chat2db.plugin.sqlite.type.SqliteDefaultValueEnum;\nimport ai.chat2db.plugin.sqlite.type.SqliteIndexTypeEnum;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport com.google.common.collect.Lists;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class SqliteMetaData extends DefaultMetaService implements MetaData {\n    private static  String  VIEW_DDL_SQL=\"SELECT * FROM sqlite_master WHERE type = 'view' and name='%s';\";\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        Table view = new Table();\n        String sql = String.format(VIEW_DDL_SQL,viewName);\n        SQLExecutor.getInstance().execute(connection, sql, resultSet->{\n            if (resultSet.next()) {\n                view.setDatabaseName(databaseName);\n                view.setDdl(resultSet.getString(\"sql\"));\n            }\n        });\n        return view;\n    }\n\n    private static final String TRIGGER_LIST_SQL = \"SELECT * FROM sqlite_master WHERE type = 'trigger';\";\n    private static  String TRIGGER_DDL_SQL = \"SELECT * FROM sqlite_master WHERE type = 'trigger' and name='%s';\";\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        List<Trigger> triggers = new ArrayList<>();\n        return SQLExecutor.getInstance().execute(connection, TRIGGER_LIST_SQL, resultSet -> {\n            while (resultSet.next()) {\n                Trigger trigger = new Trigger();\n                String triggerName = resultSet.getString(\"name\");\n                trigger.setTriggerName(triggerName);\n                trigger.setDatabaseName(databaseName);\n                triggers.add(trigger);\n            }\n            return triggers;\n        });\n    }\n\n    @Override\n    public Trigger trigger(Connection connection, String databaseName, String schemaName, String triggerName) {\n        Trigger trigger = new Trigger();\n        String sql = String.format(TRIGGER_DDL_SQL, triggerName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                trigger.setTriggerName(triggerName);\n                trigger.setDatabaseName(databaseName);\n                trigger.setTriggerBody(resultSet.getString(\"sql\"));\n            }\n            return trigger;\n        });\n    }\n\n    @Override\n    public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"SELECT sql FROM sqlite_master WHERE type='table' AND name='\" + tableName + \"'\";\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            try {\n                if (resultSet.next()) {\n                    return resultSet.getString(\"sql\");\n                }\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            }\n            return null;\n        });\n    }\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        return Lists.newArrayList(Database.builder().name(\"main\").build());\n    }\n\n    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        return Lists.newArrayList();\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new SqliteBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(SqliteColumnTypeEnum.getTypes())\n                .charsets(null)\n                .collations(SqliteCollationEnum.getCollations())\n                .indexTypes(SqliteIndexTypeEnum.getIndexTypes())\n                .defaultValues(SqliteDefaultValueEnum.getDefaultValues())\n                .build();\n    }\n\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"\\\"\" + name + \"\\\"\").collect(Collectors.joining(\".\"));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqlitePlugin.java",
    "content": "package ai.chat2db.plugin.sqlite;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class SqlitePlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"sqlite.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new SqliteMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new SqliteDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/builder/SqliteBuilder.java",
    "content": "package ai.chat2db.plugin.sqlite.builder;\n\nimport ai.chat2db.plugin.sqlite.type.SqliteColumnTypeEnum;\nimport ai.chat2db.plugin.sqlite.type.SqliteIndexTypeEnum;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport org.apache.commons.lang3.StringUtils;\n\n\npublic class SqliteBuilder extends DefaultSqlBuilder {\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"CREATE TABLE \");\n        script.append(\"\\\"\").append(table.getDatabaseName()).append(\"\\\".\\\"\").append(table.getName()).append(\"\\\"\").append(\" (\").append(\"\\n\");\n\n        // append column\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            SqliteColumnTypeEnum typeEnum = SqliteColumnTypeEnum.getByType(column.getColumnType());\n            if(typeEnum == null){\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if(SqliteIndexTypeEnum.PRIMARY_KEY.getName().equals( tableIndex.getType())) {\n                SqliteIndexTypeEnum sqliteIndexTypeEnum = SqliteIndexTypeEnum.getByType(tableIndex.getType());\n                if(sqliteIndexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(sqliteIndexTypeEnum.buildIndexScript(tableIndex)).append(\",\\n\");\n            }\n        }\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n);\");\n\n        // append primary key and index\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {\n                continue;\n            }\n            if(!SqliteIndexTypeEnum.PRIMARY_KEY.getName().equals( tableIndex.getType())) {\n                SqliteIndexTypeEnum sqliteIndexTypeEnum = SqliteIndexTypeEnum.getByType(tableIndex.getType());\n                if(sqliteIndexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\n\").append(\"CREATE \").append(sqliteIndexTypeEnum.buildIndexScript(tableIndex)).append(\";\\n\");\n            }\n        }\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(\"ALTER TABLE \").append(\"\\\"\").append(oldTable.getDatabaseName()).append(\"\\\".\\\"\").append(oldTable.getName()).append(\"\\\"\").append(\"\\n\");\n            script.append(\"\\t\").append(\"RENAME TO \").append(\"\\\"\").append(newTable.getName()).append(\"\\\"\").append(\";\\n\");\n        }\n\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if (StringUtils.isNotBlank(tableColumn.getEditStatus()) && StringUtils.isNotBlank(tableColumn.getColumnType()) && StringUtils.isNotBlank(tableColumn.getName())) {\n                script.append(\"ALTER TABLE \").append(\"\\\"\").append(newTable.getDatabaseName()).append(\"\\\".\\\"\").append(newTable.getName()).append(\"\\\"\").append(\"\\n\");\n                SqliteColumnTypeEnum typeEnum = SqliteColumnTypeEnum.getByType(tableColumn.getColumnType());\n                if(typeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(typeEnum.buildModifyColumn(tableColumn)).append(\";\\n\");\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                // script.append(\"ALTER TABLE \").append(\"\\\"\").append(newTable.getDatabaseName()).append(\"\\\".\\\"\").append(newTable.getName()).append(\"\\\"\").append(\"\\n\");\n                SqliteIndexTypeEnum sqliteIndexTypeEnum = SqliteIndexTypeEnum.getByType(tableIndex.getType());\n                if(sqliteIndexTypeEnum == null){\n                    continue;\n                }\n                script.append(\"\\t\").append(sqliteIndexTypeEnum.buildModifyIndex(tableIndex)).append(\";\\n\");\n            }\n        }\n\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\";\");\n\n        return script.toString();\n    }\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        return \"select * from(\" + sql + \") t LIMIT \" + pageSize + \" OFFSET \" + offset + \"\";\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/sqlite.json",
    "content": "{\n  \"dbType\": \"SQLITE\",\n  \"supportDatabase\": true,\n  \"supportSchema\": false,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:sqlite:identifier.sqlite\",\n      \"custom\": false,\n      \"defaultDriver\": true,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/sqlite-jdbc-3.39.3.0.jar\"\n      ],\n      \"jdbcDriver\": \"sqlite-jdbc-3.39.3.0.jar\",\n      \"jdbcDriverClass\": \"org.sqlite.JDBC\"\n    }\n  ],\n  \"name\": \"SQLite\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/type/SqliteCharsetEnum.java",
    "content": "//package ai.chat2db.plugin.sqlite.type;\n//\n//import ai.chat2db.spi.model.Charset;\n//\n//import java.util.Arrays;\n//import java.util.List;\n//\n//public enum SqliteCharsetEnum {\n//\n//    UTF8(\"utf8\", \"utf8_general_ci\"),\n//    BIG5(\"big5\", \"big5_chinese_ci\"),\n//    DEC8(\"dec8\", \"dec8_swedish_ci\"),\n//    CP850(\"cp850\", \"cp850_general_ci\"),\n//    HP8(\"hp8\", \"hp8_english_ci\"),\n//    KOI8R(\"koi8r\", \"koi8r_general_ci\"),\n//    LATIN1(\"latin1\", \"latin1_swedish_ci\"),\n//    LATIN2(\"latin2\", \"latin2_general_ci\"),\n//    SWE7(\"swe7\", \"swe7_swedish_ci\"),\n//    ASCII(\"ascii\", \"ascii_general_ci\"),\n//    UJIS(\"ujis\", \"ujis_japanese_ci\"),\n//    SJIS(\"sjis\", \"sjis_japanese_ci\"),\n//    HEBREW(\"hebrew\", \"hebrew_general_ci\"),\n//    TIS620(\"tis620\", \"tis620_thai_ci\"),\n//    EUCKR(\"euckr\", \"euckr_korean_ci\"),\n//    KOI8U(\"koi8u\", \"koi8u_general_ci\"),\n//    GB2312(\"gb2312\", \"gb2312_chinese_ci\"),\n//    GREEK(\"greek\", \"greek_general_ci\"),\n//    CP1250(\"cp1250\", \"cp1250_general_ci\"),\n//    GBK(\"gbk\", \"gbk_chinese_ci\"),\n//    LATIN5(\"latin5\", \"latin5_turkish_ci\"),\n//    ARMSCII8(\"armscii8\", \"armscii8_general_ci\"),\n//    UCS2(\"ucs2\", \"ucs2_general_ci\"),\n//    CP866(\"cp866\", \"cp866_general_ci\"),\n//    KEYBCS2(\"keybcs2\", \"keybcs2_general_ci\"),\n//    MACCE(\"macce\", \"macce_general_ci\"),\n//    MACROMAN(\"macroman\", \"macroman_general_ci\"),\n//    CP852(\"cp852\", \"cp852_general_ci\"),\n//    LATIN7(\"latin7\", \"latin7_general_ci\"),\n//    UTF8MB4(\"utf8mb4\", \"utf8mb4_general_ci\"),\n//    CP1251(\"cp1251\", \"cp1251_general_ci\"),\n//    UTF16(\"utf16\", \"utf16_general_ci\"),\n//    UTF16LE(\"utf16le\", \"utf16le_general_ci\"),\n//    CP1256(\"cp1256\", \"cp1256_general_ci\"),\n//    CP1257(\"cp1257\", \"cp1257_general_ci\"),\n//    UTF32(\"utf32\", \"utf32_general_ci\"),\n//    BINARY(\"binary\", \"binary\"),\n//    GEOSTD8(\"geostd8\", \"geostd8_general_ci\"),\n//    CP932(\"cp932\", \"cp932_japanese_ci\"),\n//    EUCJPMS(\"eucjpms\", \"eucjpms_japanese_ci\"),\n//    GB18030(\"gb18030\", \"gb18030_chinese_ci\");\n//    private Charset charset;\n//\n//    SqliteCharsetEnum(String charsetName, String defaultCollationName) {\n//        this.charset = new Charset(charsetName, defaultCollationName);\n//    }\n//\n//\n//    public Charset getCharset() {\n//        return charset;\n//    }\n//\n//    public static List<Charset> getCharsets() {\n//        return Arrays.stream(SqliteCharsetEnum.values()).map(SqliteCharsetEnum::getCharset).collect(java.util.stream.Collectors.toList());\n//    }\n//\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/type/SqliteCollationEnum.java",
    "content": "package ai.chat2db.plugin.sqlite.type;\n\nimport ai.chat2db.spi.model.Collation;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum SqliteCollationEnum {\n\n    BINARY(\"BINARY\"),\n\n    NOCASE(\"NOCASE\"),\n\n    RTRIM(\"RTRIM\"),\n    ;\n    private Collation collation;\n\n    SqliteCollationEnum(String collationName) {\n        this.collation = new Collation(collationName);\n    }\n\n    public Collation getCollation() {\n        return collation;\n    }\n\n\n    public static List<Collation> getCollations() {\n        return Arrays.asList(SqliteCollationEnum.values()).stream().map(SqliteCollationEnum::getCollation).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/type/SqliteColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.sqlite.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum SqliteColumnTypeEnum implements ColumnBuilder {\n\n\n    INTEGER(\"INTEGER\", true, false, true, false, false, true, false, false, false, false),\n\n    REAL(\"REAL\", true, false, true, false, false, true, false, false, false, false),\n\n    BLOB(\"BLOB\", true, false, true, false, false, true, false, false, false, false),\n\n\n    TEXT(\"TEXT\", true, false, true, false, false, true, false, false, false, false),\n\n    ;\n    private ColumnType columnType;\n\n    public static SqliteColumnTypeEnum getByType(String dataType) {\n        return COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n\n    SqliteColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportValue) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, supportValue, false);\n    }\n\n    private static Map<String, SqliteColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (SqliteColumnTypeEnum value : SqliteColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        SqliteColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"\\\"\").append(column.getName()).append(\"\\\"\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildCharset(column, type)).append(\" \");\n\n        script.append(buildCollation(column, type)).append(\" \");\n\n        script.append(buildNullable(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        script.append(buildExt(column, type)).append(\" \");\n\n        script.append(buildAutoIncrement(column, type)).append(\" \");\n\n//        script.append(buildComment(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n    private String buildCharset(TableColumn column, SqliteColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportCharset() || StringUtils.isEmpty(column.getCharSetName())) {\n            return \"\";\n        }\n        return StringUtils.join(\"CHARACTER SET \", column.getCharSetName());\n    }\n\n    private String buildCollation(TableColumn column, SqliteColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportCollation() || StringUtils.isEmpty(column.getCollationName())) {\n            return \"\";\n        }\n        return StringUtils.join(\"COLLATE \", column.getCollationName());\n    }\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n\n//        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n//            return StringUtils.join(\"DROP COLUMN \\\"\", tableColumn.getName() + \"\\\"\");\n//        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\"ADD \", buildCreateColumnSql(tableColumn));\n        }\n//        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n//            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n//                return StringUtils.join(\"CHANGE COLUMN \\\"\", tableColumn.getOldName(), \"\\\" \", buildCreateColumnSql(tableColumn));\n//            } else {\n//                return StringUtils.join(\"MODIFY COLUMN \", buildCreateColumnSql(tableColumn));\n//            }\n//        }\n        return \"\";\n    }\n\n    private String buildAutoIncrement(TableColumn column, SqliteColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportAutoIncrement()) {\n            return \"\";\n        }\n        if (column.getAutoIncrement() != null && column.getAutoIncrement()) {\n            return \"AUTO_INCREMENT\";\n        }\n        return \"\";\n    }\n\n\n    private String buildExt(TableColumn column, SqliteColumnTypeEnum type) {\n        if (!type.columnType.isSupportExtent() || StringUtils.isEmpty(column.getExtent())) {\n            return \"\";\n        }\n        return column.getComment();\n    }\n\n    private String buildDefaultValue(TableColumn column, SqliteColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {\n            return \"\";\n        }\n\n        if (\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if (\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildNullable(TableColumn column, SqliteColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportNullable()) {\n            return \"\";\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"NULL\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDataType(TableColumn column, SqliteColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n\n        if (column.getColumnSize() == null || column.getDecimalDigits() == null) {\n            return columnType;\n        }\n        if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n            return StringUtils.join(columnType, \"(\", column.getColumnSize() + \")\");\n        }\n        if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n            return StringUtils.join(columnType, \"(\", column.getColumnSize() + \",\" + column.getDecimalDigits() + \")\");\n        }\n        return columnType;\n    }\n\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(SqliteColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/type/SqliteDefaultValueEnum.java",
    "content": "package ai.chat2db.plugin.sqlite.type;\n\nimport ai.chat2db.spi.model.DefaultValue;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum SqliteDefaultValueEnum {\n    EMPTY_STRING(\"EMPTY_STRING\"),\n    NULL(\"NULL\"),\n    ;\n    private DefaultValue defaultValue;\n\n    SqliteDefaultValueEnum(String defaultValue) {\n        this.defaultValue = new DefaultValue(defaultValue);\n    }\n\n\n    public DefaultValue getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static List<DefaultValue> getDefaultValues() {\n        return Arrays.stream(SqliteDefaultValueEnum.values()).map(SqliteDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/type/SqliteIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.sqlite.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.Collation;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum SqliteIndexTypeEnum {\n\n    PRIMARY_KEY(\"Primary\", \"PRIMARY KEY\"),\n\n    NORMAL(\"Normal\", \"INDEX\"),\n\n    UNIQUE(\"Unique\", \"UNIQUE INDEX\");\n\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    private IndexType indexType;\n\n    public String getName() {\n        return name;\n    }\n\n    private String name;\n\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    private String keyword;\n\n    SqliteIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n\n    public static SqliteIndexTypeEnum getByType(String type) {\n        for (SqliteIndexTypeEnum value : SqliteIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        if (this.equals(PRIMARY_KEY)) {\n            return buildPrimaryKeyScript(tableIndex);\n        } else {\n            StringBuilder script = new StringBuilder();\n\n            script.append(keyword).append(\" \");\n\n            script.append(buildIndexName(tableIndex)).append(\" ON \").append(tableIndex.getTableName()).append(\" \");\n\n            script.append(buildIndexColumn(tableIndex)).append(\" \");\n            return script.toString();\n        }\n\n\n    }\n\n    private String buildPrimaryKeyScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"CONSTRAINT \").append(buildIndexName(tableIndex)).append(\" \").append(keyword).append(\" \").append(buildIndexColumn(tableIndex));\n        return script.toString();\n    }\n\n    private String buildIndexComment(TableIndex tableIndex) {\n        if (StringUtils.isBlank(tableIndex.getComment())) {\n            return \"\";\n        } else {\n            return StringUtils.join(\"COMMENT '\", tableIndex.getComment(), \"'\");\n        }\n\n    }\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"\\\"\").append(column.getColumnName()).append(\"\\\"\").append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        if (this.equals(PRIMARY_KEY)) {\n            return tableIndex.getTableName()+\"_pk\";\n        } else {\n            return \"\\\"\" + tableIndex.getName() + \"\\\"\";\n        }\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex), \",\\n\", \"ADD \", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\"CREATE \", buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (SqliteIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {\n            return StringUtils.join(\"DROP PRIMARY KEY\");\n        }\n        return StringUtils.join(\"DROP INDEX \\\"\", tableIndex.getOldName(), \"\\\"\");\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(SqliteIndexTypeEnum.values()).stream().map(SqliteIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.sqlite.SqlitePlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>chat2db-sqlserver</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n<!--        <dependency>-->\n<!--            <groupId>com.microsoft.sqlserver</groupId>-->\n<!--            <artifactId>mssql-jdbc</artifactId>-->\n<!--            <version>11.2.1.jre17</version>-->\n<!--            <scope>test</scope>-->\n<!--        </dependency>-->\n    </dependencies>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerCommandExecutor.java",
    "content": "package ai.chat2db.plugin.sqlserver;\n\nimport ai.chat2db.spi.model.Command;\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\n\npublic class SqlServerCommandExecutor extends SQLExecutor {\n\n    /**\n     * Execute command\n     */\n    @Override\n    public List<ExecuteResult> execute(Command command) {\n        String sql = command.getScript();\n        command.setScript(removeSpecialGO(sql));\n        return super.execute(command);\n    }\n\n\n    private String removeSpecialGO(String sql) {\n        if (StringUtils.isBlank(sql)) {\n            return null;\n        }\n        sql = sql.replaceAll(\"(?mi)^[ \\\\t]*go[ \\\\t]*$\", \";\");\n        return sql;\n    }\n\n    /**\n     * Execute command\n     */\n    @Override\n    public ExecuteResult executeUpdate(String sql, Connection connection, int n) throws SQLException {\n        sql = removeSpecialGO(sql);\n        return super.executeUpdate(sql, connection, n);\n    }\n\n\n    /**\n     *\n     */\n    public ExecuteResult execute(final String sql, Connection connection, boolean limitRowSize, Integer offset,\n                                 Integer count)\n            throws SQLException {\n        return super.execute(removeSpecialGO(sql), connection, limitRowSize, offset, count);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerDBManage.java",
    "content": "package ai.chat2db.plugin.sqlserver;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.ResultSetUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class SqlServerDBManage extends DefaultDBManage implements DBManage {\n    private String tableDDLFunction\n            = \"CREATE FUNCTION tableSchema.ufn_GetCreateTableScript( @schema_name NVARCHAR(128), @table_name NVARCHAR\"\n            + \"(128)) RETURNS NVARCHAR(MAX) AS BEGIN DECLARE @CreateTableScript NVARCHAR(MAX); DECLARE @IndexScripts \"\n            + \"NVARCHAR(MAX) = ''; DECLARE @ColumnDescriptions NVARCHAR(MAX) = N''; SELECT @CreateTableScript = CONCAT( \"\n            + \"'CREATE TABLE [', s.name, '].[' , t.name, '] (', STUFF( ( SELECT ', [' + c.name + '] ' + tp.name + CASE \"\n            + \"WHEN tp.name IN ('varchar', 'nvarchar', 'char', 'nchar') THEN '(' + IIF(c.max_length = -1, 'MAX', CAST(c\"\n            + \".max_length AS NVARCHAR(10))) + ')' WHEN tp.name IN ('decimal', 'numeric') THEN '(' + CAST(c.precision AS \"\n            + \"NVARCHAR(10)) + ', ' + CAST(c.scale AS NVARCHAR(10)) + ')' ELSE '' END + ' ' + CASE WHEN c.is_nullable = 1\"\n            + \" THEN 'NULL' ELSE 'NOT NULL' END FROM sys.columns c JOIN sys.types tp ON c.user_type_id = tp.user_type_id \"\n            + \"WHERE c.object_id = t.object_id FOR XML PATH(''), TYPE ).value('/', 'nvarchar(max)'), 1, 1, ''), ');' ) \"\n            + \"FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE t.name = @table_name AND s.name = \"\n            + \"@schema_name; SELECT @IndexScripts = @IndexScripts + 'CREATE ' + CASE WHEN i.is_unique = 1 THEN 'UNIQUE ' \"\n            + \"ELSE '' END + i.type_desc + ' INDEX [' + i.name + '] ON [' + s.name + '].[' + t.name + '] (' + STUFF( ( \"\n            + \"SELECT ', [' + c.name + ']' + CASE WHEN ic.is_descending_key = 1 THEN ' DESC' ELSE ' ASC' END FROM sys\"\n            + \".index_columns ic JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id WHERE ic\"\n            + \".object_id = i.object_id AND ic.index_id = i.index_id ORDER BY ic.key_ordinal FOR XML PATH('') ), 1, 1, \"\n            + \"'') + ')' + CASE WHEN i.has_filter = 1 THEN ' WHERE ' + i.filter_definition ELSE '' END + ';' + CHAR(13) +\"\n            + \" CHAR(10) FROM sys.indexes i JOIN sys.tables t ON i.object_id = t.object_id JOIN sys.schemas s ON t\"\n            + \".schema_id = s.schema_id WHERE i.type > 0 AND t.name = @table_name AND s.name \"\n            + \"= @schema_name; SELECT @ColumnDescriptions += 'EXEC sp_addextendedproperty @name=N''MS_Description'', \"\n            + \"@value=N''' + CAST(p.value AS NVARCHAR(MAX)) + ''', @level0type=N''SCHEMA'', @level0name=N''' + \"\n            + \"@schema_name + ''', @level1type=N''TABLE'', @level1name=N''' + @table_name + ''', @level2type=N''COLUMN'',\"\n            + \" @level2name=N''' + c.name + ''';' + CHAR(13) + CHAR(10) FROM sys.extended_properties p JOIN sys.columns c\"\n            + \" ON p.major_id = c.object_id AND p.minor_id = c.column_id JOIN sys.tables t ON c.object_id = t.object_id \"\n            + \"JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE p.class = 1 AND t.name = @table_name AND s.name = \"\n            + \"@schema_name; SET @CreateTableScript = @CreateTableScript + CHAR(13) + CHAR(10) + @IndexScripts + CHAR(13)\"\n            + \" + CHAR(10)+ @ColumnDescriptions+ CHAR(10); RETURN @CreateTableScript; END\";\n\n    private static String TRIGGER_SQL_LIST\n            = \"SELECT OBJECT_NAME(parent_obj) AS TableName, name AS triggerName, OBJECT_DEFINITION(id) AS \"\n            + \"triggerDefinition, CASE WHEN status & 1 = 1 THEN 'Enabled' ELSE 'Disabled' END AS Status FROM sysobjects \"\n            + \"WHERE xtype = 'TR' \";\n\n    private static String PROCEDURE_SQL = \"SELECT COUNT(*) AS ProcedureCount\\n\" +\n            \"FROM sys.procedures\\n\" +\n            \"WHERE [name] = N'%s'\\n\" +\n            \"AND schema_id = SCHEMA_ID(N'%s');\";\n\n\n    @Override\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        exportTables(connection, databaseName, schemaName, asyncContext);\n        exportViews(connection, databaseName, schemaName, asyncContext);\n        exportFunctions(connection, schemaName, asyncContext);\n        exportProcedures(connection, schemaName, asyncContext);\n        exportTriggers(connection, asyncContext);\n    }\n\n    private void exportTables(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = \"SELECT name FROM SysObjects Where XType='U'\";\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String tableName = resultSet.getString(\"name\");\n                exportTable(connection, databaseName, schemaName, tableName, asyncContext);\n            }\n        }\n    }\n\n\n    public void exportTable(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {\n        try {\n            SQLExecutor.getInstance().execute(connection, tableDDLFunction.replace(\"tableSchema\", schemaName),\n                                              resultSet -> null);\n        } catch (Exception e) {\n            //log.error(\"Failed to create function\", e);\n        }\n        String sql = String.format(\"SELECT %s.ufn_GetCreateTableScript('%s', '%s') as ddl\", schemaName, schemaName, tableName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP TABLE IF EXISTS \").append(tableName).append(\";\").append(\"\\n\")\n                        .append(resultSet.getString(\"ddl\")).append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n                if (asyncContext.isContainsData()) {\n                    exportTableData(connection, databaseName, schemaName, tableName, asyncContext);\n                } else {\n                    asyncContext.write(\"go \\n\");\n                }\n            }\n        }\n    }\n\n\n    public void exportTableData(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) {\n        SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();\n        String tableQuerySql = sqlBuilder.buildTableQuerySql(databaseName, schemaName, tableName);\n        SQLExecutor.getInstance().execute(connection, tableQuerySql, 1000, resultSet -> {\n            ResultSetMetaData metaData = resultSet.getMetaData();\n            List<String> columnList = ResultSetUtils.getRsHeader(resultSet);\n            List<String> valueList = new ArrayList<>();\n            while (resultSet.next()) {\n                for (int i = 1; i <= metaData.getColumnCount(); i++) {\n                    ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n                    JDBCDataValue jdbcDataValue = new JDBCDataValue(resultSet, metaData, i, false);\n                    String valueString = valueProcessor.getJdbcSqlValueString(jdbcDataValue);\n                    valueList.add(valueString);\n                }\n                String insertSql = sqlBuilder.buildSingleInsertSql(databaseName, schemaName, tableName, columnList, valueList);\n                asyncContext.write(insertSql+\";\");\n                valueList.clear();\n            }\n            asyncContext.write(\"go \\n\");\n        });\n    }\n\n    private void exportViews(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT TABLE_NAME, VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS \" +\n                                           \"WHERE TABLE_SCHEMA = '%s' AND TABLE_CATALOG = '%s'; \", schemaName, databaseName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(\"DROP VIEW IF EXISTS \").append(resultSet.getString(\"TABLE_NAME\")).append(\";\\n\").append(\"go\").append(\"\\n\")\n                        .append(resultSet.getString(\"VIEW_DEFINITION\")).append(\";\").append(\"\\n\")\n                        .append(\"go\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n\n        }\n    }\n\n    private void exportFunctions(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT name FROM sys.objects WHERE type = 'FN' and SCHEMA_ID = SCHEMA_ID('%s')\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String functionName = resultSet.getString(\"name\");\n                exportFunction(connection, functionName, schemaName, asyncContext);\n            }\n        }\n    }\n\n    private void exportFunction(Connection connection, String functionName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT OBJECT_DEFINITION(OBJECT_ID('%s.%s')) as ddl\", schemaName, functionName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"ddl\")\n                                          .replace(\"CREATE   FUNCTION\", \"CREATE OR ALTER FUNCTION\"))\n                        .append(\"\\n\").append(\"go\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n\n            }\n        }\n    }\n\n    private void exportProcedures(Connection connection, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT name FROM sys.procedures WHERE SCHEMA_ID = SCHEMA_ID('%s')\", schemaName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            while (resultSet.next()) {\n                String procedureName = resultSet.getString(\"name\");\n\n                exportProcedure(connection, procedureName, schemaName, asyncContext);\n            }\n        }\n    }\n\n    private void exportProcedure(Connection connection, String procedureName, String schemaName, AsyncContext asyncContext) throws SQLException {\n        String sql = String.format(\"SELECT definition FROM sys.sql_modules  WHERE object_id = (OBJECT_ID('%s.%s'));\", schemaName, procedureName);\n        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {\n            if (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"definition\")\n                                          .replace(\"CREATE   PROCEDURE\", \"CREATE OR ALTER PROCEDURE\"))\n                        .append(\"\\n\").append(\"go\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n\n            }\n        }\n    }\n\n    private void exportTriggers(Connection connection, AsyncContext asyncContext) throws SQLException {\n        try (ResultSet resultSet = connection.createStatement().executeQuery(TRIGGER_SQL_LIST)) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder.append(resultSet.getString(\"triggerDefinition\")\n                                          .replace(\"CREATE   TRIGGER\", \"CREATE OR ALTER TRIGGER\"))\n                        .append(\"\\n\").append(\"go\").append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        try {\n            connection.setAutoCommit(false);\n            String procedureBody = procedure.getProcedureBody();\n            boolean isCreateOrAlter = procedureBody.trim().toUpperCase().startsWith(\"CREATE OR ALTER \");\n\n            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith(\"CREATE\")) {\n                throw new IllegalArgumentException(\"No CREATE statement found.\");\n            }\n\n            String procedureNewName = getSchemaOrProcedureName(procedureBody, schemaName, procedure);\n            if (!procedureNewName.equals(procedure.getProcedureName())) {\n                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);\n            }\n            String checkProcedureSQL = String.format(PROCEDURE_SQL, procedure.getProcedureName().toUpperCase(), schemaName.toUpperCase());\n            String finalProcedureBody = procedureBody;\n            SQLExecutor.getInstance().execute(connection, checkProcedureSQL, resultSet -> {\n                if (resultSet.next()) {\n                    int count = resultSet.getInt(1);\n                    if (count >= 1) {\n                        if (isCreateOrAlter) {\n                            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet2 -> {});\n                        } else {\n                            throw new SQLException(\"Procedure with the same name already exists.\");\n                        }\n                    }\n                }\n            });\n            SQLExecutor.getInstance().execute(connection, finalProcedureBody, resultSet -> {});\n        } catch (Exception e) {\n            connection.rollback();\n            throw new RuntimeException(e);\n        } finally {\n            connection.setAutoCommit(true);\n        }\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n        try {\n            SQLExecutor.getInstance().execute(connection, \"use [\" + database + \"];\");\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName,boolean copyData) throws SQLException {\n        String sql = \"\";\n        if(copyData){\n            sql = \"SELECT * INTO \" + newTableName + \" FROM \" + tableName;\n        }else {\n            sql = \"SELECT * INTO \" + newTableName + \" FROM \" + tableName + \" WHERE 1=0\";\n        }\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerMetaData.java",
    "content": "package ai.chat2db.plugin.sqlserver;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport ai.chat2db.plugin.sqlserver.builder.SqlServerSqlBuilder;\nimport ai.chat2db.plugin.sqlserver.type.SqlServerColumnTypeEnum;\nimport ai.chat2db.plugin.sqlserver.type.SqlServerDefaultValueEnum;\nimport ai.chat2db.plugin.sqlserver.type.SqlServerIndexTypeEnum;\nimport ai.chat2db.spi.CommandExecutor;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.SortUtils;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Lists;\nimport jakarta.validation.constraints.NotEmpty;\nimport org.apache.commons.lang3.StringUtils;\n\nimport static ai.chat2db.spi.util.SortUtils.sortDatabase;\n\npublic class SqlServerMetaData extends DefaultMetaService implements MetaData {\n\n\n    private List<String> systemDatabases = Arrays.asList(\"master\", \"model\", \"msdb\", \"tempdb\");\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        List<Database> databases = SQLExecutor.getInstance().databases(connection);\n        return sortDatabase(databases, systemDatabases, connection);\n    }\n\n    private List<String> systemSchemas = Arrays.asList(\"guest\", \"INFORMATION_SCHEMA\", \"sys\", \"db_owner\",\n            \"db_accessadmin\", \"db_securityadmin\", \"db_ddladmin\", \"db_backupoperator\", \"db_datareader\", \"db_datawriter\",\n            \"db_denydatareader\", \"db_denydatawriter\");\n\n    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);\n        return SortUtils.sortSchema(schemas, systemSchemas);\n    }\n\n    private String functionSQL\n            = \"CREATE FUNCTION tableSchema.ufn_GetCreateTableScript( @schema_name NVARCHAR(128), @table_name NVARCHAR\"\n            + \"(128)) RETURNS NVARCHAR(MAX) AS BEGIN DECLARE @CreateTableScript NVARCHAR(MAX); DECLARE @IndexScripts \"\n            + \"NVARCHAR(MAX) = ''; DECLARE @ColumnDescriptions NVARCHAR(MAX) = N''; SELECT @CreateTableScript = CONCAT( \"\n            + \"'CREATE TABLE [', s.name, '].[' , t.name, '] (', STUFF( ( SELECT ', [' + c.name + '] ' + tp.name + CASE \"\n            + \"WHEN tp.name IN ('varchar', 'nvarchar', 'char', 'nchar') THEN '(' + IIF(c.max_length = -1, 'MAX', CAST(c\"\n            + \".max_length AS NVARCHAR(10))) + ')' WHEN tp.name IN ('decimal', 'numeric') THEN '(' + CAST(c.precision AS \"\n            + \"NVARCHAR(10)) + ', ' + CAST(c.scale AS NVARCHAR(10)) + ')' ELSE '' END + ' ' + CASE WHEN c.is_nullable = 1\"\n            + \" THEN 'NULL' ELSE 'NOT NULL' END FROM sys.columns c JOIN sys.types tp ON c.user_type_id = tp.user_type_id \"\n            + \"WHERE c.object_id = t.object_id FOR XML PATH(''), TYPE ).value('/', 'nvarchar(max)'), 1, 1, ''), ');' ) \"\n            + \"FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE t.name = @table_name AND s.name = \"\n            + \"@schema_name; SELECT @IndexScripts = @IndexScripts + 'CREATE ' + CASE WHEN i.is_unique = 1 THEN 'UNIQUE ' \"\n            + \"ELSE '' END + i.type_desc + ' INDEX [' + i.name + '] ON [' + s.name + '].[' + t.name + '] (' + STUFF( ( \"\n            + \"SELECT ', [' + c.name + ']' + CASE WHEN ic.is_descending_key = 1 THEN ' DESC' ELSE ' ASC' END FROM sys\"\n            + \".index_columns ic JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id WHERE ic\"\n            + \".object_id = i.object_id AND ic.index_id = i.index_id ORDER BY ic.key_ordinal FOR XML PATH('') ), 1, 1, \"\n            + \"'') + ')' + CASE WHEN i.has_filter = 1 THEN ' WHERE ' + i.filter_definition ELSE '' END + ';' + CHAR(13) +\"\n            + \" CHAR(10) FROM sys.indexes i JOIN sys.tables t ON i.object_id = t.object_id JOIN sys.schemas s ON t\"\n            + \".schema_id = s.schema_id WHERE i.type > 0 AND t.name = @table_name AND s.name \"\n            + \"= @schema_name; SELECT @ColumnDescriptions += 'EXEC sp_addextendedproperty @name=N''MS_Description'', \"\n            + \"@value=N''' + CAST(p.value AS NVARCHAR(MAX)) + ''', @level0type=N''SCHEMA'', @level0name=N''' + \"\n            + \"@schema_name + ''', @level1type=N''TABLE'', @level1name=N''' + @table_name + ''', @level2type=N''COLUMN'',\"\n            + \" @level2name=N''' + c.name + ''';' + CHAR(13) + CHAR(10) FROM sys.extended_properties p JOIN sys.columns c\"\n            + \" ON p.major_id = c.object_id AND p.minor_id = c.column_id JOIN sys.tables t ON c.object_id = t.object_id \"\n            + \"JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE p.class = 1 AND t.name = @table_name AND s.name = \"\n            + \"@schema_name; SET @CreateTableScript = @CreateTableScript + CHAR(13) + CHAR(10) + @IndexScripts + CHAR(13)\"\n            + \" + CHAR(10)+ @ColumnDescriptions+ CHAR(10); RETURN @CreateTableScript; END\";\n\n    @Override\n    public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {\n        try {\n            SQLExecutor.getInstance().execute(connection, functionSQL.replace(\"tableSchema\", schemaName),\n                    resultSet -> null);\n        } catch (Exception e) {\n            //log.error(\"Failed to create function\", e);\n        }\n\n        String ddlSql = \"SELECT \" + schemaName + \".ufn_GetCreateTableScript('\" + schemaName + \"', '\" + tableName\n                + \"') AS sql\";\n        return SQLExecutor.getInstance().execute(connection, ddlSql, resultSet -> {\n            if (resultSet.next()) {\n                return resultSet.getString(\"sql\");\n            }\n            return null;\n        });\n    }\n\n    private static String SELECT_TABLES_SQL = \"SELECT t.name AS TableName, mm.value as comment FROM sys.tables t LEFT JOIN(SELECT * from sys.extended_properties ep where ep.minor_id = 0 AND ep.name = 'MS_Description') mm ON t.object_id = mm.major_id WHERE t.schema_id= SCHEMA_ID('%s')\";\n\n    @Override\n    public List<Table> tables(Connection connection, String databaseName, String schemaName, String tableName) {\n        List<Table> tables = new ArrayList<>();\n        String sql = String.format(SELECT_TABLES_SQL, schemaName);\n        if (StringUtils.isNotBlank(tableName)) {\n            sql += \" AND t.name = '\" + tableName + \"'\";\n        }else {\n            sql += \" ORDER BY t.name\";\n        }\n\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Table table = new Table();\n                table.setDatabaseName(databaseName);\n                table.setSchemaName(schemaName);\n                table.setName(resultSet.getString(\"TableName\"));\n                table.setComment(resultSet.getString(\"comment\"));\n                tables.add(table);\n            }\n            return tables;\n        });\n    }\n\n    private static final String SELECT_TABLE_COLUMNS = \"SELECT c.name as COLUMN_NAME , c.is_nullable as IS_NULLABLE ,c.column_id as ORDINAL_POSITION,c.max_length as COLUMN_SIZE, c.scale as NUMERIC_SCALE, c.collation_name as COLLATION_NAME, ty.name as DATA_TYPE ,t.name, def.definition as COLUMN_DEFAULT, ep.value as COLUMN_COMMENT from sys.columns c LEFT JOIN sys.tables t on c.object_id=t.object_id LEFT JOIN sys.types ty ON c.user_type_id = ty.user_type_id LEFT JOIN sys.default_constraints def ON c.default_object_id = def.object_id LEFT JOIN sys.extended_properties ep ON t.object_id = ep.major_id AND c.column_id = ep.minor_id WHERE t.name ='%s' and t.schema_id=SCHEMA_ID('%s');\";\n\n    @Override\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = String.format(SELECT_TABLE_COLUMNS, tableName, schemaName);\n        List<TableColumn> tableColumns = new ArrayList<>();\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                TableColumn column = new TableColumn();\n                column.setDatabaseName(databaseName);\n                column.setTableName(tableName);\n                column.setSchemaName(schemaName);\n                column.setOldName(resultSet.getString(\"COLUMN_NAME\"));\n                column.setName(resultSet.getString(\"COLUMN_NAME\"));\n                //column.setColumnType(resultSet.getString(\"COLUMN_TYPE\"));\n                String dataType = resultSet.getString(\"DATA_TYPE\").toUpperCase();\n                column.setColumnType(SqlUtils.removeDigits(dataType));\n\n                //column.setDataType(resultSet.getInt(\"DATA_TYPE\"));\n                column.setDefaultValue(resultSet.getString(\"COLUMN_DEFAULT\"));\n                //column.setAutoIncrement(resultSet.getString(\"EXTRA\").contains(\"auto_increment\"));\n                column.setComment(resultSet.getString(\"COLUMN_COMMENT\"));\n                // column.setPrimaryKey(\"PRI\".equalsIgnoreCase(resultSet.getString(\"COLUMN_KEY\")));\n                column.setNullable(resultSet.getInt(\"IS_NULLABLE\"));\n                column.setOrdinalPosition(resultSet.getInt(\"ORDINAL_POSITION\"));\n                column.setDecimalDigits(resultSet.getInt(\"NUMERIC_SCALE\"));\n                // column.setCharSetName(resultSet.getString(\"CHARACTER_SET_NAME\"));\n                column.setCollationName(resultSet.getString(\"COLLATION_NAME\"));\n                column.setColumnSize(resultSet.getInt(\"COLUMN_SIZE\"));\n                //setColumnSize(column, resultSet.getString(\"COLUMN_TYPE\"));\n                tableColumns.add(column);\n            }\n            return tableColumns;\n        });\n    }\n\n    private static String ROUTINES_SQL\n            = \"SELECT type_desc, OBJECT_NAME(object_id) AS FunctionName, OBJECT_DEFINITION(object_id) AS \"\n            + \"definition FROM sys.objects WHERE type_desc IN(%s) and name = '%s' ;\";\n\n\n    private static String OBJECT_SQL\n            = \"SELECT name FROM sys.objects WHERE type = '%s' and SCHEMA_ID = SCHEMA_ID('%s') order by name;\";\n\n    @Override\n    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,\n                             String functionName) {\n\n        String sql = String.format(ROUTINES_SQL, \"'SQL_SCALAR_FUNCTION', 'SQL_TABLE_VALUED_FUNCTION'\", functionName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Function function = new Function();\n            function.setDatabaseName(databaseName);\n            function.setSchemaName(schemaName);\n            function.setFunctionName(functionName);\n            if (resultSet.next()) {\n                function.setFunctionBody(resultSet.getString(\"definition\"));\n            }\n            return function;\n        });\n    }\n\n    @Override\n    public List<Function> functions(Connection connection, String databaseName, String schemaName) {\n        List<Function> functions = new ArrayList<>();\n        String sql = String.format(OBJECT_SQL, \"FN\", schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Function function = new Function();\n                function.setDatabaseName(databaseName);\n                function.setSchemaName(schemaName);\n                function.setFunctionName(resultSet.getString(\"name\"));\n                functions.add(function);\n            }\n            return functions;\n        });\n    }\n\n    private Function removeVersion(Function function) {\n        String fullFunctionName = function.getFunctionName();\n        if (!StringUtils.isEmpty(fullFunctionName)) {\n            String[] parts = fullFunctionName.split(\";\");\n            String functionName = parts[0];\n            function.setFunctionName(functionName);\n        }\n        return function;\n    }\n\n    @Override\n    public List<Procedure> procedures(Connection connection, String databaseName, String schemaName) {\n        List<Procedure> procedures = new ArrayList<>();\n        String sql = String.format(OBJECT_SQL, \"P\", schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            while (resultSet.next()) {\n                Procedure procedure = new Procedure();\n                procedure.setDatabaseName(databaseName);\n                procedure.setSchemaName(schemaName);\n                procedure.setProcedureName(resultSet.getString(\"name\"));\n                procedures.add(procedure);\n            }\n            return procedures;\n        });\n    }\n\n    private Procedure removeVersion(Procedure procedure) {\n        String fullProcedureName = procedure.getProcedureName();\n        if (!StringUtils.isEmpty(fullProcedureName)) {\n            String[] parts = fullProcedureName.split(\";\");\n            String procedureName = parts[0];\n            procedure.setProcedureName(procedureName);\n        }\n        return procedure;\n    }\n\n    private static String TRIGGER_SQL\n            = \"SELECT OBJECT_NAME(parent_obj) AS TableName, name AS triggerName, OBJECT_DEFINITION(id) AS \"\n            + \"triggerDefinition, CASE WHEN status & 1 = 1 THEN 'Enabled' ELSE 'Disabled' END AS Status FROM sysobjects \"\n            + \"WHERE xtype = 'TR' and name = '%s';\";\n\n    private static String TRIGGER_SQL_LIST\n            = \"SELECT OBJECT_NAME(parent_obj) AS TableName, name AS triggerName, OBJECT_DEFINITION(id) AS \"\n            + \"triggerDefinition, CASE WHEN status & 1 = 1 THEN 'Enabled' ELSE 'Disabled' END AS Status FROM sysobjects \"\n            + \"WHERE xtype = 'TR' order by name\";\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        List<Trigger> triggers = new ArrayList<>();\n        return SQLExecutor.getInstance().execute(connection, TRIGGER_SQL_LIST, resultSet -> {\n            while (resultSet.next()) {\n                Trigger trigger = new Trigger();\n                trigger.setTriggerName(resultSet.getString(\"triggerName\"));\n                trigger.setSchemaName(schemaName);\n                trigger.setDatabaseName(databaseName);\n                triggers.add(trigger);\n            }\n            return triggers;\n        });\n    }\n\n    @Override\n    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,\n                           String triggerName) {\n\n        String sql = String.format(TRIGGER_SQL, triggerName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Trigger trigger = new Trigger();\n            trigger.setDatabaseName(databaseName);\n            trigger.setSchemaName(schemaName);\n            trigger.setTriggerName(triggerName);\n            if (resultSet.next()) {\n                trigger.setTriggerBody(resultSet.getString(\"triggerDefinition\"));\n            }\n            return trigger;\n        });\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n                               String procedureName) {\n        String sql = String.format(ROUTINES_SQL, \"'SQL_STORED_PROCEDURE'\", procedureName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n                    Procedure procedure = new Procedure();\n                    procedure.setDatabaseName(databaseName);\n                    procedure.setSchemaName(schemaName);\n                    procedure.setProcedureName(procedureName);\n                    if (resultSet.next()) {\n                        procedure.setProcedureBody(resultSet.getString(\"definition\"));\n                    }\n                    return procedure;\n                }\n        );\n    }\n\n    private static String VIEW_SQL\n            = \"SELECT TABLE_SCHEMA, TABLE_NAME, VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = '%s' \"\n            + \"AND TABLE_NAME = '%s';\";\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        String sql = String.format(VIEW_SQL, schemaName, viewName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            Table table = new Table();\n            table.setDatabaseName(databaseName);\n            table.setSchemaName(schemaName);\n            table.setName(viewName);\n            if (resultSet.next()) {\n                table.setDdl(resultSet.getString(\"VIEW_DEFINITION\"));\n            }\n            return table;\n        });\n    }\n\n    private static final String INDEX_SQL = \"SELECT ic.key_ordinal AS COLUMN_POSITION, ic.is_descending_key as DESCEND , ind.name AS INDEX_NAME, ind.is_unique AS IS_UNIQUE, col.name AS COLUMN_NAME, ind.type_desc AS INDEX_TYPE, ind.is_primary_key AS IS_PRIMARY FROM sys.indexes ind INNER JOIN sys.index_columns ic ON ind.object_id = ic.object_id and ind.index_id = ic.index_id and ic.key_ordinal>0 INNER JOIN sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id INNER JOIN sys.tables t ON ind.object_id = t.object_id LEFT JOIN sys.key_constraints kc ON ind.object_id = kc.parent_object_id AND ind.index_id = kc.unique_index_id WHERE t.name = '%s' and t.schema_id= SCHEMA_ID('%s') ORDER BY t.name, ind.name, ind.index_id, ic.index_column_id\";\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = String.format(INDEX_SQL, tableName,schemaName);\n        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {\n            LinkedHashMap<String, TableIndex> map = new LinkedHashMap();\n            while (resultSet.next()) {\n                String keyName = resultSet.getString(\"INDEX_NAME\");\n                TableIndex tableIndex = map.get(keyName);\n                if (tableIndex != null) {\n                    List<TableIndexColumn> columnList = tableIndex.getColumnList();\n                    columnList.add(getTableIndexColumn(resultSet));\n                    columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition))\n                            .collect(Collectors.toList());\n                    tableIndex.setColumnList(columnList);\n                } else {\n                    TableIndex index = new TableIndex();\n                    index.setDatabaseName(databaseName);\n                    index.setSchemaName(schemaName);\n                    index.setTableName(tableName);\n                    index.setName(keyName);\n                    int isunique = resultSet.getInt(\"IS_UNIQUE\");\n                    if(isunique ==1){\n                        index.setUnique(true);\n                    }else {\n                        index.setUnique(false);\n                    }\n                    List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n                    tableIndexColumns.add(getTableIndexColumn(resultSet));\n                    index.setColumnList(tableIndexColumns);\n                    String indexType = resultSet.getString(\"INDEX_TYPE\");\n                    if (resultSet.getBoolean(\"IS_PRIMARY\")) {\n                        index.setType(SqlServerIndexTypeEnum.PRIMARY_KEY.getName());\n                    }else if(\"CLUSTERED\".equalsIgnoreCase(indexType)){\n                        if(index.getUnique()){\n                            index.setType(SqlServerIndexTypeEnum.UNIQUE_CLUSTERED.getName());\n                        }else {\n                            index.setType(SqlServerIndexTypeEnum.CLUSTERED.getName());\n                        }\n                    }else if(\"NONCLUSTERED\".equalsIgnoreCase(indexType)){\n                        if(index.getUnique()){\n                            index.setType(SqlServerIndexTypeEnum.UNIQUE_NONCLUSTERED.getName());\n                        }else {\n                            index.setType(SqlServerIndexTypeEnum.NONCLUSTERED.getName());\n                        }\n                    }else {\n                        index.setType(indexType);\n                    }\n                    map.put(keyName, index);\n                }\n            }\n            return map.values().stream().collect(Collectors.toList());\n        });\n\n    }\n\n    private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException {\n        TableIndexColumn tableIndexColumn = new TableIndexColumn();\n        tableIndexColumn.setColumnName(resultSet.getString(\"COLUMN_NAME\"));\n        tableIndexColumn.setOrdinalPosition(resultSet.getShort(\"COLUMN_POSITION\"));\n        int collation = resultSet.getInt(\"DESCEND\");\n        if (collation == 1) {\n            tableIndexColumn.setAscOrDesc(\"ASC\");\n        } else {\n            tableIndexColumn.setAscOrDesc(\"DESC\");\n        }\n        return tableIndexColumn;\n    }\n\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new SqlServerSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return TableMeta.builder()\n                .columnTypes(SqlServerColumnTypeEnum.getTypes())\n                .charsets(null)\n                .collations(null)\n                .indexTypes(SqlServerIndexTypeEnum.getIndexTypes())\n                .defaultValues(SqlServerDefaultValueEnum.getDefaultValues())\n                .build();\n    }\n\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> \"[\" + name + \"]\").collect(Collectors.joining(\".\"));\n    }\n\n    @Override\n    public CommandExecutor getCommandExecutor() {\n        return new SqlServerCommandExecutor();\n    }\n\n    @Override\n    public List<String> getSystemDatabases() {\n        return systemDatabases;\n    }\n\n    @Override\n    public List<String> getSystemSchemas() {\n        return systemSchemas;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerPlugin.java",
    "content": "package ai.chat2db.plugin.sqlserver;\n\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class SqlServerPlugin implements Plugin {\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(this.getClass(),\"sqlserver.json\", DBConfig.class);\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new SqlServerMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new SqlServerDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/builder/SqlServerSqlBuilder.java",
    "content": "package ai.chat2db.plugin.sqlserver.builder;\n\nimport ai.chat2db.plugin.sqlserver.type.SqlServerColumnTypeEnum;\nimport ai.chat2db.plugin.sqlserver.type.SqlServerIndexTypeEnum;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport org.apache.commons.lang3.StringUtils;\n\npublic class SqlServerSqlBuilder extends DefaultSqlBuilder {\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"CREATE TABLE \").append(\"[\").append(table.getSchemaName()).append(\"].[\").append(table.getName()).append(\"] (\").append(\"\\n\");\n\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {\n                continue;\n            }\n            SqlServerColumnTypeEnum typeEnum = SqlServerColumnTypeEnum.getByType(column.getColumnType());\n            if (typeEnum == null) {\n                continue;\n            }\n            script.append(\"\\t\").append(typeEnum.buildCreateColumnSql(column)).append(\",\\n\");\n        }\n\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n)\\ngo\\n\");\n\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {\n                continue;\n            }\n            SqlServerIndexTypeEnum sqlServerIndexTypeEnum = SqlServerIndexTypeEnum.getByType(tableIndex.getType());\n            if (sqlServerIndexTypeEnum == null) {\n                continue;\n            }\n            script.append(\"\\n\").append(sqlServerIndexTypeEnum.buildIndexScript(tableIndex));\n            if (StringUtils.isNotBlank(tableIndex.getComment())) {\n                script.append(\"\\n\").append(buildIndexComment(tableIndex));\n            }\n        }\n\n        for (TableColumn column : table.getColumnList()) {\n            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType()) || StringUtils.isBlank(column.getComment())) {\n                continue;\n            }\n            script.append(\"\\n\").append(buildColumnComment(column));\n        }\n\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\"\\n\").append(buildTableComment(table));\n        }\n\n\n        return script.toString();\n    }\n\n    private static String INDEX_COMMENT_SCRIPT = \"exec sp_addextendedproperty 'MS_Description','%s','SCHEMA','%s','TABLE','%s','INDEX','%s' \\ngo\";\n\n\n    private String buildIndexComment(TableIndex tableIndex) {\n        return String.format(INDEX_COMMENT_SCRIPT, tableIndex.getComment(), tableIndex.getSchemaName(), tableIndex.getTableName(), tableIndex.getName());\n    }\n\n    private static String TABLE_COMMENT_SCRIPT = \"exec sp_addextendedproperty 'MS_Description','%s','SCHEMA','%s','TABLE','%s' \\ngo\";\n\n\n    private String buildTableComment(Table table) {\n        return String.format(TABLE_COMMENT_SCRIPT, table.getComment(), table.getSchemaName(), table.getName());\n    }\n\n    private static String COLUMN_COMMENT_SCRIPT = \"exec sp_addextendedproperty 'MS_Description','%s','SCHEMA','%s','TABLE','%s','COLUMN','%s' \\ngo\";\n\n    private String buildColumnComment(TableColumn column) {\n        return String.format(COLUMN_COMMENT_SCRIPT, column.getComment(), column.getSchemaName(), column.getTableName(), column.getName());\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n\n        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {\n            script.append(buildRenameTable(oldTable, newTable));\n        }\n        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {\n            if (oldTable.getComment() == null) {\n                script.append(\"\\n\").append(buildTableComment(newTable));\n            } else {\n                script.append(\"\\n\").append(buildUpdateTableComment(newTable));\n            }\n        }\n\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if (StringUtils.isNotBlank(tableColumn.getEditStatus())) {\n                SqlServerColumnTypeEnum typeEnum = SqlServerColumnTypeEnum.getByType(tableColumn.getColumnType());\n                if (typeEnum == null) {\n                    continue;\n                }\n                script.append(typeEnum.buildModifyColumn(tableColumn)).append(\"\\n\");\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {\n                SqlServerIndexTypeEnum mysqlIndexTypeEnum = SqlServerIndexTypeEnum.getByType(tableIndex.getType());\n                if (mysqlIndexTypeEnum == null) {\n                    continue;\n                }\n                script.append(\"\\t\").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(\"\\n\");\n                if (StringUtils.isNotBlank(tableIndex.getComment())) {\n                    script.append(\"\\n\").append(buildIndexComment(tableIndex)).append(\"\\ngo\");\n                }\n            }\n        }\n\n        return script.toString();\n    }\n\n    private static String UPDATE_TABLE_COMMENT_SCRIPT = \"exec sp_updateextendedproperty 'MS_Description','%s','SCHEMA','%s','TABLE','%s' \\ngo\";\n\n    private String buildUpdateTableComment(Table newTable) {\n        return String.format(UPDATE_TABLE_COMMENT_SCRIPT, newTable.getComment(), newTable.getSchemaName(), newTable.getName());\n    }\n\n    private static String RENAME_TABLE_SCRIPT = \"exec sp_rename '%s','%s','OBJECT' \\ngo\";\n\n    private String buildRenameTable(Table oldTable, Table newTable) {\n        return String.format(RENAME_TABLE_SCRIPT, oldTable.getName(), newTable.getName());\n    }\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        String version = Chat2DBContext.getDbVersion();\n        if (StringUtils.isNotBlank(version)) {\n            String[] versions = version.split(\"\\\\.\");\n            if (versions.length > 0 && Integer.parseInt(versions[0]) >= 11) {\n                StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);\n                sqlBuilder.append(sql);\n                if(!sql.toLowerCase().contains(\"order by\")){\n                    sqlBuilder.append(\"\\n ORDER BY (SELECT NULL)\");\n                }\n                sqlBuilder.append(\"\\n OFFSET \");\n                sqlBuilder.append(offset);\n                sqlBuilder.append(\" ROWS \");\n                sqlBuilder.append(\" FETCH NEXT \");\n                sqlBuilder.append(pageSize);\n                sqlBuilder.append(\" ROWS ONLY\");\n                return sqlBuilder.toString();\n            }\n        }\n        return \"\";\n    }\n\n\n    @Override\n    public String buildCreateDatabaseSql(Database database) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE DATABASE [\" + database.getName() + \"]\");\n        if (StringUtils.isNotBlank(database.getCollation())) {\n            sqlBuilder.append(\" COLLATE \").append(database.getCollation());\n        }\n        sqlBuilder.append(\"\\ngo\\n\");\n        if (StringUtils.isNotBlank(database.getComment())) {\n            sqlBuilder.append(\"exec [\" + database.getName() + \"].sys. sp_addextendedproperty 'MS_Description','\")\n                    .append(database.getComment()).append(\"'\").append(\"\\ngo\\n\");\n        }\n        return sqlBuilder.toString();\n    }\n\n\n    @Override\n    public String buildCreateSchemaSql(Schema schema) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE SCHEMA [\" + schema.getName() + \"] \\ngo\\n\");\n        if (StringUtils.isNotBlank(schema.getComment())) {\n            sqlBuilder.append(\"exec sp_addextendedproperty 'MS_Description','\")\n                    .append(schema.getComment()).append(\"'\").append(\",'SCHEMA'\")\n                    .append(\",'\").append(schema.getName()).append(\"'\").append(\"\\ngo\\n\");\n        }\n        return sqlBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/sqlserver.json",
    "content": "{\n  \"dbType\": \"SQLSERVER\",\n  \"supportDatabase\": true,\n  \"supportSchema\": true,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:sqlserver://localhost:1433;database=master\",\n      \"custom\": false,\n      \"defaultDriver\": true,\n      \"downloadJdbcDriverUrls\": [\n        \"https://cdn.chat2db-ai.com/lib/mssql-jdbc-11.2.1.jre17.jar\"\n      ],\n      \"jdbcDriver\": \"mssql-jdbc-11.2.1.jre17.jar\",\n      \"jdbcDriverClass\": \"com.microsoft.sqlserver.jdbc.SQLServerDriver\",\n      \"extendInfo\": [\n        {\n          \"key\": \"encrypt\",\n          \"value\": \"false\",\n          \"required\": false\n        },\n        {\n          \"key\": \"trustServerCertificate\",\n          \"value\": \"true\",\n          \"required\": false\n        },\n        {\n          \"key\": \"integratedSecurity\",\n          \"value\": \"false\",\n          \"required\": false\n        },\n        {\n          \"key\": \"Trusted_Connection\",\n          \"value\": \"yes\",\n          \"required\": false\n        }\n      ]\n    }\n  ],\n  \"name\": \"SQLServer\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/type/SqlServerColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.sqlserver.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\npublic enum SqlServerColumnTypeEnum implements ColumnBuilder {\n    //JSON(\"JSON\", false, false, true, false, false, false, true, false, false, false)\n\n    BIGINT(\"BIGINT\", false, false, true, false, false, false, true, true),\n\n    BINARY(\"BINARY\", false, false, true, false, false, false, true, true),\n\n    BIT(\"BIT\", false, false, true, false, false, false, true, true),\n\n    CHAR(\"CHAR\", true, false, true, false, false, true, true, true),\n\n    DATE(\"DATE\", false, false, true, false, false, false, true, true),\n\n    DATETIME(\"DATETIME\", false, false, true, false, false, false, true, true),\n\n    DATETIME2(\"DATETIME2\", true, false, true, false, false, false, true, true),\n\n\n    DATETIMEOFFSET(\"DATETIMEOFFSET\", true, false, true, false, false, false, true, true),\n\n\n    DECIMAL(\"DECIMAL\", true, true, true, false, false, false, true, true),\n\n\n    FLOAT(\"FLOAT\", true, false, true, false, false, false, true, true),\n\n\n    GEOGRAPHY(\"GEOGRAPHY\", false, false, true, false, false, false, true, true),\n\n    GEOMETRY(\"GEOMETRY\", false, false, true, false, false, false, true, true),\n\n    HIERARCHYID(\"HIERARCHYID\", false, false, true, false, false, false, true, true),\n\n    IMAGE(\"IMAGE\", false, false, true, false, false, false, true, true),\n\n    INT(\"INT\", false, false, true, false, false, false, true, true),\n\n\n    MONEY(\"MONEY\", false, false, true, false, false, false, true, true),\n\n    NCHAR(\"NCHAR\", true, false, true, false, false, true, true, true),\n\n    NTEXT(\"NTEXT\", false, false, true, false, false, false, true, true),\n\n    NUMERIC(\"NUMERIC\", true, true, true, false, false, false, true, true),\n\n    NVARCHAR(\"NVARCHAR\", true, false, true, false, false, true, true, true),\n\n    NVARCHAR_MAX(\"NVARCHAR(MAX)\", false, false, true, false, false, true, true, true),\n\n\n    REAL(\"REAL\", false, false, true, false, false, false, true, true),\n\n    SMALLDATETIME(\"SMALLDATETIME\", false, false, true, false, false, false, true, true),\n\n    SMALLINT(\"SMALLINT\", false, false, true, false, false, false, true, true),\n\n    SMALLMONEY(\"SMALLMONEY\", false, false, true, false, false, false, true, true),\n\n    SQL_VARIANT(\"SQL_VARIANT\", false, false, true, false, false, false, true, true),\n\n    SYSNAME(\"SYSNAME\", false, false, true, false, false, false, true, true),\n\n    TEXT(\"TEXT\", false, false, true, false, false, true, true, true),\n\n    TIME(\"TIME\", true, false, true, false, false, false, true, true),\n\n    TIMESTAMP(\"TIMESTAMP\", false, false, true, false, false, false, true, true),\n\n\n    TINYINT(\"TINYINT\", false, false, true, false, false, false, true, true),\n\n    UNIQUEIDENTIFIER(\"UNIQUEIDENTIFIER\", false, false, true, false, false, false, true, true),\n\n\n    VARBINARY(\"VARBINARY\", true, false, true, false, false, false, true, true),\n\n    VARBINARY_MAX(\"VARBINARY(MAX)\", false, false, true, false, false, false, true, true),\n\n    VARCHAR(\"VARCHAR\", true, false, true, false, false, true, true, true),\n\n    VARCHAR_MAX(\"VARCHAR(MAX)\", false, false, true, false, false, true, true, true),\n\n    XML(\"XML\", false, false, true, false, false, false, true, true),\n\n\n    OTHER(\"OTHER\", false, false, true, false, false, false, true, true),\n    ;\n    private ColumnType columnType;\n\n    public static SqlServerColumnTypeEnum getByType(String dataType) {\n        SqlServerColumnTypeEnum typeEnum = COLUMN_TYPE_MAP.get(SqlUtils.removeDigits(dataType.toUpperCase()));\n        if (typeEnum == null) {\n            return OTHER;\n        }\n        return typeEnum;\n    }\n\n    private static Map<String, SqlServerColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();\n\n    static {\n        for (SqlServerColumnTypeEnum value : SqlServerColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n\n    SqlServerColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue) {\n        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, false, false, false);\n    }\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        SqlServerColumnTypeEnum type = this;\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"[\").append(column.getName()).append(\"]\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildSparse(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        script.append(buildNullable(column, type)).append(\" \");\n\n        script.append(buildCollation(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n    public String buildUpdateColumnSql(TableColumn column) {\n        SqlServerColumnTypeEnum type = this;\n\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"[\").append(column.getName()).append(\"]\").append(\" \");\n\n        script.append(buildDataType(column, type)).append(\" \");\n\n        script.append(buildNullable(column, type)).append(\" \\ngo\\n\");\n\n        if (StringUtils.isNotBlank(column.getDefaultValue()) && column.getOldColumn().getDefaultValue() != null && !StringUtils.equalsIgnoreCase(column.getDefaultValue(), column.getOldColumn().getDefaultValue())) {\n            script.append(\"ALTER TABLE \").append(\"[\").append(column.getSchemaName()).append(\"].[\").append(column.getTableName()).append(\"]\");\n            script.append(\" \").append(\"DROP CONSTRAINT \").append(\"[\").append(column.getDefaultConstraintName()).append(\"]\");\n\n            script.append(\"ALTER TABLE \").append(\"[\").append(column.getSchemaName()).append(\"].[\").append(column.getTableName()).append(\"]\");\n            script.append(\" \").append(\"ADD \").append(buildDefaultValue(column, type)).append(\" for \").append(column.getName()).append(\" \\ngo\\n\");\n        }\n\n        if (StringUtils.isNotBlank(column.getDefaultValue()) && column.getOldColumn().getDefaultValue() == null) {\n            script.append(\"ALTER TABLE \").append(\"[\").append(column.getSchemaName()).append(\"].[\").append(column.getTableName()).append(\"]\");\n            script.append(\" \").append(\"ADD \").append(buildDefaultValue(column, type)).append(\" for \").append(column.getName()).append(\" \\ngo\\n\");\n        }\n\n\n        if (!Objects.equals(column.getSparse(), column.getOldColumn().getSparse())) {\n            script.append(\"ALTER TABLE \").append(\"[\").append(column.getSchemaName()).append(\"].[\").append(column.getTableName()).append(\"]\");\n            script.append(\" \").append(\"ALTER COLUMN \").append(\"[\").append(column.getName()).append(\"]\").append(\" add \").append(\"SPARSE\").append(\" \\ngo\\n\");\n        }\n\n        if (!Objects.equals(column.getCollationName(), column.getOldColumn().getCollationName())) {\n            script.append(\"ALTER TABLE \").append(\"[\").append(column.getSchemaName()).append(\"].[\").append(column.getTableName()).append(\"]\");\n            script.append(\" \").append(\"ALTER COLUMN \").append(\"[\").append(column.getName()).append(\"]\").append(\" \").append(\"COLLATE \").append(column.getCollationName()).append(\" \\ngo\\n\");\n        }\n        return script.toString();\n    }\n\n    private String buildSparse(TableColumn column, SqlServerColumnTypeEnum type) {\n        if (Boolean.TRUE.equals(column.getSparse())) {\n            return \"SPARSE\";\n        } else {\n            return \"\";\n        }\n    }\n\n    private String buildCollation(TableColumn column, SqlServerColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportCollation() || StringUtils.isEmpty(column.getCollationName())) {\n            return \"\";\n        }\n        return StringUtils.join(\"COLLATE \", column.getCollationName());\n    }\n\n\n    private String buildNullable(TableColumn column, SqlServerColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportNullable()) {\n            return \"\";\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"NULL\";\n        } else {\n            return \"NOT NULL\";\n        }\n    }\n\n    private String buildDefaultValue(TableColumn column, SqlServerColumnTypeEnum type) {\n        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {\n            return \"\";\n        }\n\n        if (\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if (\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildDataType(TableColumn column, SqlServerColumnTypeEnum type) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(CHAR, NCHAR, NVARCHAR, VARBINARY, VARCHAR).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            script.append(columnType);\n            if (column.getColumnSize() != null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\")\");\n            }\n\n            return script.toString();\n        }\n\n        if (Arrays.asList(DECIMAL, FLOAT, TIMESTAMP, TIME, DATETIME2, DATETIMEOFFSET, FLOAT, NUMERIC).contains(type)) {\n            StringBuilder script = new StringBuilder();\n            script.append(columnType);\n            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\")\");\n            } else if (column.getColumnSize() != null && column.getDecimalDigits() != null) {\n                script.append(\"(\").append(column.getColumnSize()).append(\",\").append(column.getDecimalDigits()).append(\")\");\n            }\n            return script.toString();\n        }\n\n        if (Arrays.asList().contains(type)) {\n            StringBuilder script = new StringBuilder();\n            if (column.getColumnSize() == null) {\n                script.append(columnType);\n            } else {\n                String[] split = columnType.split(\"TIMESTAMP\");\n                script.append(\"TIMESTAMP\").append(\"(\").append(column.getColumnSize()).append(\")\").append(split[1]);\n            }\n            return script.toString();\n        }\n        if(OTHER.equals(columnType)){\n            return column.getColumnType();\n        }\n\n        return columnType;\n    }\n\n    private static String RENAME_COLUMN_SCRIPT = \"exec sp_rename '%s.%s','%s','COLUMN' \\ngo\";\n\n    private String renameColumn(TableColumn tableColumn) {\n        return String.format(RENAME_COLUMN_SCRIPT, tableColumn.getTableName(), tableColumn.getOldName(), tableColumn.getName());\n    }\n\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            if (StringUtils.isNotBlank(tableColumn.getDefaultConstraintName())) {\n                script.append(\"ALTER TABLE \").append(\"[\").append(tableColumn.getSchemaName()).append(\"].[\").append(tableColumn.getTableName()).append(\"]\");\n                script.append(\" \").append(\"DROP CONSTRAINT \").append(\"[\").append(tableColumn.getDefaultConstraintName()).append(\"]\");\n                script.append(\"\\ngo\\n\");\n            }\n            script.append(\"ALTER TABLE \").append(\"[\").append(tableColumn.getSchemaName()).append(\"].[\").append(tableColumn.getTableName()).append(\"]\");\n            script.append(\" \").append(\"DROP COLUMN \").append(\"[\").append(tableColumn.getName()).append(\"]\");\n            script.append(\"\\ngo\\n\");\n            return script.toString();\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n            script.append(\"ALTER TABLE \").append(\"[\").append(tableColumn.getSchemaName()).append(\"].[\").append(tableColumn.getTableName()).append(\"]\");\n            script.append(\" \").append(\"ADD \").append(buildCreateColumnSql(tableColumn)).append(\" \\ngo\\n\");\n\n\n            if (StringUtils.isNotBlank(tableColumn.getComment())) {\n                script.append(\"\\n\").append(buildModifyColumnComment(tableColumn));\n            }\n            return script.toString();\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            StringBuilder script = new StringBuilder();\n\n            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {\n                script.append(renameColumn(tableColumn));\n                script.append(\"\\n\");\n            }\n            script.append(\"ALTER TABLE \").append(\"[\").append(tableColumn.getSchemaName()).append(\"].[\").append(tableColumn.getTableName()).append(\"]\");\n            script.append(\" \").append(\"ALTER COLUMN \").append(buildUpdateColumnSql(tableColumn)).append(\" \\n\");\n\n            if (!Objects.equals(tableColumn.getComment(), tableColumn.getOldColumn().getComment())) {\n                script.append(\"\\n\").append(buildModifyColumnComment(tableColumn));\n            }\n\n            return script.toString();\n\n        }\n        return \"\";\n    }\n\n    private static String COLUMN_MODIFY_COMMENT_SCRIPT = \"IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description',\\n\" +\n            \"'SCHEMA', N'%s',\\n\" +\n            \"'TABLE', N'%s',\\n\" +\n            \"'COLUMN', N'%s')) > 0)\\n\" +\n            \"  EXEC sp_updateextendedproperty\\n\" +\n            \"'MS_Description', N'%s',\\n\" +\n            \"'SCHEMA', N'%s',\\n\" +\n            \"'TABLE', N'%s',\\n\" +\n            \"'COLUMN', N'%s'\\n\" +\n            \"ELSE\\n\" +\n            \"  EXEC sp_addextendedproperty\\n\" +\n            \"'MS_Description', N'%s',\\n\" +\n            \"'SCHEMA', N'%s',\\n\" +\n            \"'TABLE', N'%s',\\n\" +\n            \"'COLUMN', N'%s'\\n go\";\n\n    private String buildModifyColumnComment(TableColumn tableColumn) {\n        return String.format(COLUMN_MODIFY_COMMENT_SCRIPT, tableColumn.getSchemaName(), tableColumn.getTableName(),\n                tableColumn.getName(), tableColumn.getComment(), tableColumn.getSchemaName(), tableColumn.getTableName(), tableColumn.getName(),\n                tableColumn.getComment(), tableColumn.getSchemaName(), tableColumn.getTableName(), tableColumn.getName());\n    }\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(SqlServerColumnTypeEnum.values()).map(columnTypeEnum ->\n                columnTypeEnum.getColumnType()\n        ).toList();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/type/SqlServerDefaultValueEnum.java",
    "content": "package ai.chat2db.plugin.sqlserver.type;\n\nimport ai.chat2db.spi.model.DefaultValue;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum SqlServerDefaultValueEnum {\n    EMPTY_STRING(\"EMPTY_STRING\"),\n    NULL(\"NULL\"),\n    ;\n    private DefaultValue defaultValue;\n\n    SqlServerDefaultValueEnum(String defaultValue) {\n        this.defaultValue = new DefaultValue(defaultValue);\n    }\n\n\n    public DefaultValue getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static List<DefaultValue> getDefaultValues() {\n        return Arrays.stream(SqlServerDefaultValueEnum.values()).map(SqlServerDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/type/SqlServerIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.sqlserver.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum SqlServerIndexTypeEnum {\n\n    PRIMARY_KEY(\"Primary\", \"PRIMARY KEY\"),\n\n//    NORMAL(\"Normal\", \"INDEX\"),\n//\n//    UNIQUE(\"Unique\", \"UNIQUE INDEX\"),\n\n\n    UNIQUE_CLUSTERED(\"UNIQUE CLUSTERED\", \"UNIQUE CLUSTERED INDEX\"),\n\n    CLUSTERED(\"CLUSTERED\", \"CLUSTERED INDEX\"),\n\n\n    NONCLUSTERED(\"NONCLUSTERED\", \"NONCLUSTERED INDEX\"),\n\n    UNIQUE_NONCLUSTERED(\"UNIQUE NONCLUSTERED\", \"UNIQUE NONCLUSTERED INDEX\"),\n\n    SPATIAL(\"SPATIAL\", \"SPATIAL INDEX\"),\n\n    XML(\"XML\", \"XML INDEX\");\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    private IndexType indexType;\n\n\n    public String getName() {\n        return name;\n    }\n\n    private String name;\n\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    private String keyword;\n\n    SqlServerIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n\n    public static SqlServerIndexTypeEnum getByType(String type) {\n        for (SqlServerIndexTypeEnum value : SqlServerIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    //ALTER TABLE [dbo].[Employees] ADD CONSTRAINT [PK__Employee__7AD04FF164ABF7C7] PRIMARY KEY CLUSTERED ([FirstName])\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        if (PRIMARY_KEY.equals(this)) {\n            script.append(\"ALTER TABLE [\").append(tableIndex.getSchemaName()).append(\"].[\").append(tableIndex.getTableName()).append(\"] ADD CONSTRAINT \").append(buildIndexName(tableIndex)).append(\" \").append(keyword).append(\" \").append(buildIndexColumn(tableIndex));\n        } else {\n            script.append(\"CREATE \").append(keyword).append(\" \");\n            script.append(buildIndexName(tableIndex)).append(\"\\n ON [\").append(tableIndex.getSchemaName()).append(\"].[\").append(tableIndex.getTableName()).append(\"] \").append(buildIndexColumn(tableIndex));\n        }\n        script.append(\"\\ngo\");\n        return script.toString();\n    }\n\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"[\").append(column.getColumnName()).append(\"]\");\n                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {\n                    script.append(\" \").append(column.getAscOrDesc());\n                }\n                script.append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        return \"[\" + tableIndex.getName() + \"]\";\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return buildDropIndex(tableIndex);\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildDropIndex(tableIndex), \"\\n\", buildIndexScript(tableIndex));\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n\n    private String buildDropIndex(TableIndex tableIndex) {\n        if (SqlServerIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {\n            return StringUtils.join(\"ALTER TABLE [\", tableIndex.getSchemaName(), \"].[\", tableIndex.getTableName(), \"] DROP CONSTRAINT \", buildIndexName(tableIndex),\"\\ngo\");\n        }\n        StringBuilder script = new StringBuilder();\n        script.append(\"DROP INDEX \");\n        script.append(buildIndexName(tableIndex));\n        script.append(\" ON [\").append(tableIndex.getSchemaName()).append(\"].[\").append(tableIndex.getTableName()).append(\"] \\ngo\");\n\n        return script.toString();\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(SqlServerIndexTypeEnum.values()).stream().map(SqlServerIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());\n    }}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.sqlserver.SqlServerPlugin"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-plugins</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n    <artifactId>chat2db-timeplus</artifactId>\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/java</directory>\n                <includes>\n                    <!--The properties configuration file will be placed together with the compiled class file-->\n                    <include>**/*.json</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n            </resource>\n        </resources>\n    </build>\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/java/ai/chat2db/plugin/timeplus/TimeplusDBManage.java",
    "content": "package ai.chat2db.plugin.timeplus;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.jdbc.DefaultDBManage;\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport java.sql.*;\nimport java.util.Objects;\nimport org.apache.commons.lang3.StringUtils;\n\npublic class TimeplusDBManage extends DefaultDBManage implements DBManage {\n\n    @Override\n    public void exportDatabase(\n        Connection connection,\n        String databaseName,\n        String schemaName,\n        AsyncContext asyncContext\n    ) throws SQLException {\n        exportTablesOrViewsOrDictionaries(\n            connection,\n            databaseName,\n            schemaName,\n            asyncContext\n        );\n        exportFunctions(connection, asyncContext);\n    }\n\n    private void exportFunctions(\n        Connection connection,\n        AsyncContext asyncContext\n    ) throws SQLException {\n        String sql =\n            \"SELECT name,create_query from system.functions where origin='ExecutableUserDefined'\";\n        try (\n            ResultSet resultSet = connection.createStatement().executeQuery(sql)\n        ) {\n            while (resultSet.next()) {\n                StringBuilder sqlBuilder = new StringBuilder();\n                sqlBuilder\n                    .append(\"DROP FUNCTION IF EXISTS \")\n                    .append(resultSet.getString(\"name\"))\n                    .append(\";\")\n                    .append(\"\\n\")\n                    .append(resultSet.getString(\"create_query\"))\n                    .append(\";\")\n                    .append(\"\\n\");\n                asyncContext.write(sqlBuilder.toString());\n            }\n        }\n    }\n\n    private void exportTablesOrViewsOrDictionaries(\n        Connection connection,\n        String databaseName,\n        String schemaName,\n        AsyncContext asyncContext\n    ) throws SQLException {\n        String sql = String.format(\n            \"SELECT create_table_query, has_own_data,engine,name from system.`tables` WHERE `database`='%s'\",\n            databaseName\n        );\n        try (\n            Statement statement = connection.createStatement();\n            ResultSet resultSet = statement.executeQuery(sql)\n        ) {\n            while (resultSet.next()) {\n                String ddl = resultSet.getString(\"create_table_query\");\n                boolean dataFlag = resultSet.getInt(\"has_own_data\") == 1;\n                String tableType = resultSet.getString(\"engine\");\n                String tableOrViewName = resultSet.getString(\"name\");\n                if (Objects.equals(\"View\", tableType)) {\n                    StringBuilder sqlBuilder = new StringBuilder();\n                    sqlBuilder\n                        .append(\"DROP VIEW IF EXISTS \")\n                        .append(databaseName)\n                        .append(\".\")\n                        .append(tableOrViewName)\n                        .append(\";\")\n                        .append(\"\\n\")\n                        .append(ddl)\n                        .append(\";\")\n                        .append(\"\\n\");\n                    asyncContext.write(sqlBuilder.toString());\n                } else if (Objects.equals(\"Dictionary\", tableType)) {\n                    StringBuilder sqlBuilder = new StringBuilder();\n                    sqlBuilder\n                        .append(\"DROP DICTIONARY IF EXISTS \")\n                        .append(databaseName)\n                        .append(\".\")\n                        .append(tableOrViewName)\n                        .append(\";\")\n                        .append(\"\\n\")\n                        .append(ddl)\n                        .append(\";\")\n                        .append(\"\\n\");\n                    asyncContext.write(sqlBuilder.toString());\n                } else {\n                    StringBuilder sqlBuilder = new StringBuilder();\n                    sqlBuilder\n                        .append(\"DROP STREAM IF EXISTS \")\n                        .append(databaseName)\n                        .append(\".\")\n                        .append(tableOrViewName)\n                        .append(\";\")\n                        .append(\"\\n\")\n                        .append(ddl)\n                        .append(\";\")\n                        .append(\"\\n\");\n                    asyncContext.write(sqlBuilder.toString());\n                    if (asyncContext.isContainsData() && dataFlag) {\n                        exportTableData(\n                            connection,\n                            databaseName,\n                            schemaName,\n                            tableOrViewName,\n                            asyncContext\n                        );\n                    }\n                }\n            }\n        }\n    }\n\n    @Override\n    public Connection getConnection(ConnectInfo connectInfo) {\n        String url = setDatabaseInJdbcUrl(connectInfo);\n        connectInfo.setUrl(url);\n\n        return super.getConnection(connectInfo);\n    }\n\n    private String setDatabaseInJdbcUrl(ConnectInfo connectInfo) {\n        String databaseName; //sometimes it's null, sometimes not. Need special handling\n        String url = connectInfo.getUrl();\n        if (\n            StringUtils.isBlank(\n                (databaseName = connectInfo.getDatabaseName())\n            ) &&\n            StringUtils.isBlank((databaseName = connectInfo.getSchemaName()))\n        ) {\n            return url;\n        }\n\n        String connectAddress =\n            connectInfo.getHost() + \":\" + connectInfo.getPort();\n        String[] addressSplit = url.split(connectAddress);\n        String connectParams = addressSplit[1];\n        if (connectParams.startsWith(\"/\")) {\n            // Remove / from connection parameters\n            connectParams = connectParams.substring(1);\n        }\n        if (connectParams.equals(databaseName)) {\n            return url;\n        }\n        // Add database name\n        String rv =\n            addressSplit[0] +\n            connectAddress +\n            \"/\" +\n            databaseName +\n            connectParams;\n        return rv;\n    }\n\n    @Override\n    public void dropTable(\n        Connection connection,\n        String databaseName,\n        String schemaName,\n        String tableName\n    ) {\n        String sql = \"DROP STREAM IF EXISTS \" + databaseName + \".\" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void copyTable(\n        Connection connection,\n        String databaseName,\n        String schemaName,\n        String tableName,\n        String newTableName,\n        boolean copyData\n    ) throws SQLException {\n        String sql = \"CREATE STREAM \" + newTableName + \" AS \" + tableName + \"\";\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n        if (copyData) {\n            sql = \"INSERT INTO \" +\n            newTableName +\n            \" SELECT * FROM table(\" +\n            tableName +\n            \")\";\n            SQLExecutor.getInstance()\n                .execute(connection, sql, resultSet -> null);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/java/ai/chat2db/plugin/timeplus/TimeplusMetaData.java",
    "content": "package ai.chat2db.plugin.timeplus;\n\nimport static ai.chat2db.spi.util.SortUtils.sortDatabase;\n\nimport ai.chat2db.plugin.timeplus.builder.TimeplusSqlBuilder;\nimport ai.chat2db.plugin.timeplus.type.TimeplusColumnTypeEnum;\nimport ai.chat2db.plugin.timeplus.type.TimeplusEngineTypeEnum;\nimport ai.chat2db.plugin.timeplus.type.TimeplusIndexTypeEnum;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultMetaService;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport jakarta.validation.constraints.NotEmpty;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport org.apache.commons.lang3.StringUtils;\n\npublic class TimeplusMetaData extends DefaultMetaService implements MetaData {\n\n    private static String ROUTINES_SQL = \"\";\n    private static String TRIGGER_SQL = \"\";\n    private static String TRIGGER_SQL_LIST = \"\";\n    private static String SELECT_TABLE_COLUMNS =\n        \"select * from `system`.columns where table ='%s' and database='%s';\";\n    private static String VIEW_SQL =\n        \"SELECT create_table_query from system.`tables` WHERE `database`='%s' and name='%s'\";\n    private List<String> systemDatabases = Arrays.asList(\n        \"information_schema\",\n        \"system\"\n    );\n    public static final String FUNCTION_SQL =\n        \"SELECT name,create_query as ddl from system.functions where origin='ExecutableUserDefined'\";\n\n    public static String format(String tableName) {\n        return \"`\" + tableName + \"`\";\n    }\n\n    @Override\n    public List<Function> functions(\n        Connection connection,\n        String databaseName,\n        String schemaName\n    ) {\n        return SQLExecutor.getInstance()\n            .execute(connection, FUNCTION_SQL, resultSet -> {\n                List<Function> functions = new ArrayList<>();\n                while (resultSet.next()) {\n                    Function function = new Function();\n                    function.setFunctionName(resultSet.getString(\"name\"));\n                    functions.add(function);\n                }\n                return functions;\n            });\n    }\n\n    @Override\n    public List<Database> databases(Connection connection) {\n        List<Database> list = SQLExecutor.getInstance()\n            .execute(\n                connection,\n                \"SELECT name FROM system.databases;\",\n                resultSet -> {\n                    List<Database> databases = new ArrayList<>();\n                    try {\n                        while (resultSet.next()) {\n                            String dbName = resultSet.getString(\"name\");\n                            Database database = new Database();\n                            database.setName(dbName);\n                            databases.add(database);\n                        }\n                    } catch (SQLException e) {\n                        throw new RuntimeException(e);\n                    }\n                    return databases;\n                }\n            );\n        return sortDatabase(list, systemDatabases, connection);\n    }\n\n    @Override\n    public String tableDDL(\n        Connection connection,\n        @NotEmpty String databaseName,\n        String schemaName,\n        @NotEmpty String tableName\n    ) {\n        String sql =\n            \"SHOW CREATE \" + format(schemaName) + \".\" + format(tableName);\n        return SQLExecutor.getInstance()\n            .execute(connection, sql, resultSet -> {\n                if (resultSet.next()) {\n                    return resultSet.getString(\"statement\");\n                }\n                return null;\n            });\n    }\n\n    @Override\n    public Function function(\n        Connection connection,\n        @NotEmpty String databaseName,\n        String schemaName,\n        String functionName\n    ) {\n        return SQLExecutor.getInstance()\n            .execute(connection, FUNCTION_SQL, resultSet -> {\n                Function function = new Function();\n                function.setDatabaseName(databaseName);\n                function.setSchemaName(schemaName);\n                function.setFunctionName(functionName);\n                if (resultSet.next()) {\n                    /*                function.setSpecificName(resultSet.getString(\"SPECIFIC_NAME\"));\n                function.setRemarks(resultSet.getString(\"ROUTINE_COMMENT\"));*/\n                    function.setFunctionBody(resultSet.getString(\"ddl\"));\n                }\n                return function;\n            });\n    }\n\n    @Override\n    public List<Trigger> triggers(\n        Connection connection,\n        String databaseName,\n        String schemaName\n    ) {\n        List<Trigger> triggers = new ArrayList<>();\n        return triggers;\n    }\n\n    @Override\n    public Trigger trigger(\n        Connection connection,\n        @NotEmpty String databaseName,\n        String schemaName,\n        String triggerName\n    ) {\n        String sql = String.format(TRIGGER_SQL, databaseName, triggerName);\n        return SQLExecutor.getInstance()\n            .execute(connection, sql, resultSet -> {\n                Trigger trigger = new Trigger();\n                trigger.setDatabaseName(databaseName);\n                trigger.setSchemaName(schemaName);\n                trigger.setTriggerName(triggerName);\n                if (resultSet.next()) {\n                    trigger.setTriggerBody(\n                        resultSet.getString(\"ACTION_STATEMENT\")\n                    );\n                }\n                return trigger;\n            });\n    }\n\n    @Override\n    public Procedure procedure(\n        Connection connection,\n        @NotEmpty String databaseName,\n        String schemaName,\n        String procedureName\n    ) {\n        String sql = String.format(\n            ROUTINES_SQL,\n            \"PROCEDURE\",\n            schemaName,\n            procedureName\n        );\n        return SQLExecutor.getInstance()\n            .execute(connection, sql, resultSet -> {\n                Procedure procedure = new Procedure();\n                procedure.setDatabaseName(databaseName);\n                procedure.setSchemaName(schemaName);\n                procedure.setProcedureName(procedureName);\n                if (resultSet.next()) {\n                    procedure.setSpecificName(\n                        resultSet.getString(\"SPECIFIC_NAME\")\n                    );\n                    procedure.setRemarks(\n                        resultSet.getString(\"ROUTINE_COMMENT\")\n                    );\n                    procedure.setProcedureBody(\n                        resultSet.getString(\"ROUTINE_DEFINITION\")\n                    );\n                }\n                return procedure;\n            });\n    }\n\n    @Override\n    public List<TableColumn> columns(\n        Connection connection,\n        String databaseName,\n        String schemaName,\n        String tableName\n    ) {\n        final String db = \"default\";\n        String sql = String.format(SELECT_TABLE_COLUMNS, tableName, db);\n        List<TableColumn> tableColumns = new ArrayList<>();\n\n        return SQLExecutor.getInstance()\n            .execute(connection, sql, resultSet -> {\n                while (resultSet.next()) {\n                    TableColumn column = new TableColumn();\n                    column.setDatabaseName(db);\n                    column.setTableName(tableName);\n                    column.setOldName(resultSet.getString(\"name\"));\n                    column.setName(resultSet.getString(\"name\"));\n                    String dataType = resultSet.getString(\"type\");\n                    if (dataType.startsWith(\"nullable(\")) {\n                        dataType = dataType.substring(9, dataType.length() - 1);\n                        column.setNullable(1);\n                    }\n                    column.setColumnType(dataType);\n                    column.setDefaultValue(\n                        resultSet.getString(\"default_expression\")\n                    );\n                    //                column.setAutoIncrement(resultSet.getString(\"EXTRA\").contains(\"auto_increment\"));\n                    column.setComment(resultSet.getString(\"comment\"));\n                    column.setOrdinalPosition(resultSet.getInt(\"position\"));\n                    column.setDecimalDigits(resultSet.getInt(\"numeric_scale\"));\n                    /*column.setCharSetName(resultSet.getString(\"CHARACTER_SET_NAME\"));\n                column.setCollationName(resultSet.getString(\"COLLATION_NAME\"));*/\n                    setColumnSize(column, dataType);\n                    tableColumns.add(column);\n                }\n                return tableColumns;\n            });\n    }\n\n    private void setColumnSize(TableColumn column, String columnType) {\n        try {\n            if (columnType.contains(\"(\")) {\n                String size = columnType.substring(\n                    columnType.indexOf(\"(\") + 1,\n                    columnType.indexOf(\")\")\n                ); //\"size\" can be a number or \"3, 'UTC'\" with timezone for datetime objects\n                if (\n                    \"SET\".equalsIgnoreCase(column.getColumnType()) ||\n                    \"ENUM\".equalsIgnoreCase(column.getColumnType())\n                ) {\n                    column.setValue(size);\n                } else {\n                    if (size.contains(\",\")) {\n                        String[] sizes = size.split(\",\");\n                        if (StringUtils.isNotBlank(sizes[0])) {\n                            column.setColumnSize(Integer.parseInt(sizes[0]));\n                        }\n                        if (StringUtils.isNotBlank(sizes[1])) {\n                            //can be \" 'UTC'\"\n                            if (sizes[1].contains(\"'\") == false) {\n                                column.setDecimalDigits(\n                                    Integer.parseInt(sizes[1])\n                                );\n                            }\n                        }\n                    } else {\n                        column.setColumnSize(Integer.parseInt(size));\n                    }\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public Table view(\n        Connection connection,\n        String databaseName,\n        String schemaName,\n        String viewName\n    ) {\n        final String db = \"default\";\n        String sql = String.format(VIEW_SQL, db, viewName);\n        return SQLExecutor.getInstance()\n            .execute(connection, sql, resultSet -> {\n                Table table = new Table();\n                table.setDatabaseName(db);\n                table.setSchemaName(schemaName);\n                table.setName(viewName);\n                if (resultSet.next()) {\n                    table.setDdl(resultSet.getString(\"create_table_query\"));\n                }\n                return table;\n            });\n    }\n\n    @Override\n    public List<TableIndex> indexes(\n        Connection connection,\n        String databaseName,\n        String schemaName,\n        String tableName\n    ) {\n        List<TableIndex> rv = new ArrayList<>();\n        return rv;\n    }\n\n    private List<TableIndexColumn> getTableIndexColumn(ResultSet resultSet)\n        throws SQLException {\n        List<TableIndexColumn> tableIndexColumns = new ArrayList<>();\n        return tableIndexColumns;\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new TimeplusSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(\n        String databaseName,\n        String schemaName,\n        String tableName\n    ) {\n        return TableMeta.builder()\n            .columnTypes(TimeplusColumnTypeEnum.getTypes())\n            .engineTypes(TimeplusEngineTypeEnum.getTypes())\n            //.indexTypes(TimeplusIndexTypeEnum.getIndexTypes())\n            .build();\n    }\n\n    @Override\n    public String getMetaDataName(String... names) {\n        //avoid default.default.abc\n        String rv = Arrays.stream(names)\n            .filter(name -> StringUtils.isNotBlank(name))\n            .map(name -> \"`\" + name + \"`\")\n            .collect(Collectors.joining(\".\"));\n        rv = rv.replaceFirst(\"`default`.`default`\", \"`default`\");\n        return rv;\n    }\n\n    @Override\n    public List<String> getSystemDatabases() {\n        return systemDatabases;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/java/ai/chat2db/plugin/timeplus/TimeplusPlugin.java",
    "content": "package ai.chat2db.plugin.timeplus;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.FileUtils;\n\npublic class TimeplusPlugin implements Plugin {\n\n    @Override\n    public DBConfig getDBConfig() {\n        return FileUtils.readJsonValue(\n            this.getClass(),\n            \"timeplus.json\",\n            DBConfig.class\n        );\n    }\n\n    @Override\n    public MetaData getMetaData() {\n        return new TimeplusMetaData();\n    }\n\n    @Override\n    public DBManage getDBManage() {\n        return new TimeplusDBManage();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/java/ai/chat2db/plugin/timeplus/builder/TimeplusSqlBuilder.java",
    "content": "package ai.chat2db.plugin.timeplus.builder;\n\nimport ai.chat2db.plugin.timeplus.type.TimeplusColumnTypeEnum;\nimport ai.chat2db.plugin.timeplus.type.TimeplusIndexTypeEnum;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.jdbc.DefaultSqlBuilder;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport org.apache.commons.lang3.StringUtils;\n\npublic class TimeplusSqlBuilder extends DefaultSqlBuilder {\n\n    @Override\n    public String buildCreateTableSql(Table table) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"CREATE STREAM \");\n        if (StringUtils.isNotBlank(table.getDatabaseName())) {\n            script\n                .append(\"`\")\n                .append(table.getDatabaseName())\n                .append(\"`\")\n                .append(\".\");\n        }\n        script\n            .append(\"`\")\n            .append(table.getName())\n            .append(\"`\")\n            .append(\" (\")\n            .append(\"\\n\");\n\n        // append column\n        for (TableColumn column : table.getColumnList()) {\n            if (\n                StringUtils.isBlank(column.getName()) ||\n                StringUtils.isBlank(column.getColumnType())\n            ) {\n                continue;\n            }\n            TimeplusColumnTypeEnum typeEnum = TimeplusColumnTypeEnum.getByType(\n                column.getColumnType()\n            );\n            if (typeEnum != null) {\n                continue;\n            }\n            script\n                .append(\"\\t\")\n                .append(typeEnum.buildCreateColumnSql(column))\n                .append(\",\\n\");\n        }\n\n        // append index\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if (\n                StringUtils.isBlank(tableIndex.getName()) ||\n                StringUtils.isBlank(tableIndex.getType())\n            ) {\n                continue;\n            }\n            TimeplusIndexTypeEnum mysqlIndexTypeEnum =\n                TimeplusIndexTypeEnum.getByType(tableIndex.getType());\n            if (!TimeplusIndexTypeEnum.PRIMARY.equals(mysqlIndexTypeEnum)) {\n                script\n                    .append(\"\\t\")\n                    .append(\"\")\n                    .append(mysqlIndexTypeEnum.buildIndexScript(tableIndex))\n                    .append(\",\\n\");\n            }\n        }\n\n        script = new StringBuilder(script.substring(0, script.length() - 2));\n        script.append(\"\\n)\");\n\n        if (StringUtils.isNotBlank(table.getEngine())) {\n            script.append(\" ENGINE=\").append(table.getEngine()).append(\"\\n\");\n        }\n        // append primary key\n        for (TableIndex tableIndex : table.getIndexList()) {\n            if (\n                StringUtils.isBlank(tableIndex.getName()) ||\n                StringUtils.isBlank(tableIndex.getType())\n            ) {\n                continue;\n            }\n            TimeplusIndexTypeEnum mysqlIndexTypeEnum =\n                TimeplusIndexTypeEnum.getByType(tableIndex.getType());\n            if (TimeplusIndexTypeEnum.PRIMARY.equals(mysqlIndexTypeEnum)) {\n                script\n                    .append(\"\\t\")\n                    .append(\"\")\n                    .append(mysqlIndexTypeEnum.buildIndexScript(tableIndex))\n                    .append(\"\\n\");\n            }\n        }\n\n        if (StringUtils.isNotBlank(table.getComment())) {\n            script.append(\" COMMENT '\").append(table.getComment()).append(\"'\");\n        }\n\n        script.append(\";\");\n\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"ALTER STREAM \");\n        if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {\n            script\n                .append(\"`\")\n                .append(oldTable.getDatabaseName())\n                .append(\"`\")\n                .append(\".\");\n        }\n        script.append(\"`\").append(oldTable.getName()).append(\"`\").append(\"\\n\");\n\n        if (\n            !StringUtils.equalsIgnoreCase(\n                oldTable.getComment(),\n                newTable.getComment()\n            )\n        ) {\n            script\n                .append(\"\\t\")\n                .append(\"MODIFY COMMENT\")\n                .append(\"'\")\n                .append(newTable.getComment())\n                .append(\"'\")\n                .append(\",\\n\");\n        }\n\n        // append modify column\n        for (TableColumn tableColumn : newTable.getColumnList()) {\n            if (\n                StringUtils.isNotBlank(tableColumn.getEditStatus()) &&\n                StringUtils.isNotBlank(tableColumn.getColumnType()) &&\n                StringUtils.isNotBlank(tableColumn.getName())\n            ) {\n                TimeplusColumnTypeEnum typeEnum =\n                    TimeplusColumnTypeEnum.getByType(\n                        tableColumn.getColumnType()\n                    );\n                if (typeEnum == null) {\n                    continue;\n                }\n                script\n                    .append(\"\\t\")\n                    .append(typeEnum.buildModifyColumn(tableColumn))\n                    .append(\",\\n\");\n            }\n        }\n\n        // append modify index\n        for (TableIndex tableIndex : newTable.getIndexList()) {\n            if (\n                StringUtils.isNotBlank(tableIndex.getEditStatus()) &&\n                StringUtils.isNotBlank(tableIndex.getType())\n            ) {\n                TimeplusIndexTypeEnum clickHouseIndexTypeEnum =\n                    TimeplusIndexTypeEnum.getByType(tableIndex.getType());\n                if (clickHouseIndexTypeEnum == null) {\n                    continue;\n                }\n                script\n                    .append(\"\\t\")\n                    .append(\n                        clickHouseIndexTypeEnum.buildModifyIndex(tableIndex)\n                    )\n                    .append(\",\\n\");\n            }\n        }\n\n        if (script.length() > 2) {\n            script = new StringBuilder(\n                script.substring(0, script.length() - 2)\n            );\n            script.append(\";\");\n        }\n\n        return script.toString();\n    }\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);\n        sqlBuilder.append(sql);\n        if (offset == 0) {\n            sqlBuilder.append(\"\\n LIMIT \");\n            sqlBuilder.append(pageSize);\n        } else {\n            sqlBuilder.append(\"\\n LIMIT \");\n            sqlBuilder.append(offset);\n            sqlBuilder.append(\",\");\n            sqlBuilder.append(pageSize);\n        }\n        return sqlBuilder.toString();\n    }\n\n    @Override\n    public String buildCreateDatabaseSql(Database database) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"CREATE DATABASE `\" + database.getName() + \"`\");\n        if (StringUtils.isNotBlank(database.getComment())) {\n            sqlBuilder\n                .append(\";ALTER DATABASE \")\n                .append(database.getName())\n                .append(\" COMMENT '\")\n                .append(database.getComment())\n                .append(\"';\");\n        }\n        return sqlBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/java/ai/chat2db/plugin/timeplus/timeplus.json",
    "content": "{\n  \"dbType\": \"TIMEPLUS\",\n  \"supportDatabase\": true,\n  \"supportSchema\": false,\n  \"driverConfigList\": [\n    {\n      \"url\": \"jdbc:timeplus://localhost:7587\",\n      \"defaultDriver\": true,\n      \"custom\": false,\n      \"downloadJdbcDriverUrls\": [\n        \"https://timeplus.io/downloads/timeplus-native-jdbc-shaded-2.0.5.jar\"\n      ],\n      \"jdbcDriver\": \"timeplus-native-jdbc-shaded-2.0.5.jar\",\n      \"jdbcDriverClass\": \"com.timeplus.jdbc.TimeplusDriver\"\n    }\n  ],\n  \"name\": \"Timeplus\"\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/java/ai/chat2db/plugin/timeplus/type/TimeplusColumnTypeEnum.java",
    "content": "package ai.chat2db.plugin.timeplus.type;\n\nimport ai.chat2db.spi.ColumnBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.ColumnType;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Maps;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport org.apache.commons.lang3.StringUtils;\n\npublic enum TimeplusColumnTypeEnum implements ColumnBuilder {\n    String(\n        \"string\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Int8(\n        \"int8\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Int16(\n        \"int16\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Int32(\n        \"int32\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Int64(\n        \"int64\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Int128(\n        \"int128\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Int256(\n        \"int256\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    UInt8(\n        \"uint8\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    UInt16(\n        \"uint16\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    UInt32(\n        \"uint32\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    UInt64(\n        \"uint64\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    UInt128(\n        \"uint128\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    UInt256(\n        \"uint256\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Float32(\n        \"float32\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Float64(\n        \"float64\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Decimal(\n        \"decimal\",\n        true,\n        true,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Boolean(\n        \"bool\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    FixedString(\n        \"fixed_string\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    UUID(\n        \"uuid\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Date(\n        \"date\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    DATE32(\n        \"date32\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    DateTime(\n        \"datetime\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    DateTime64(\n        \"datetime64\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Enum8(\n        \"enum8\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Enum16(\n        \"enum16\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Array(\n        \"array\",\n        false,\n        false,\n        false,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    JSON(\n        \"json\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Nested(\n        \"nested\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Map(\"map\", true, true, true, false, false, false, true, true, false, false),\n    IPv4(\n        \"ipv4\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    IPv6(\n        \"ipv6\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Point(\n        \"point\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Ring(\n        \"ring\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    Polygon(\n        \"polygon\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    MultiPolygon(\n        \"multi_polygon\",\n        false,\n        false,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    AggregateFunction(\n        \"aggregate_function\",\n        true,\n        true,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    ),\n    SimpleAggregateFunction(\n        \"simple_aggregate_function\",\n        true,\n        true,\n        true,\n        false,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false\n    );\n\n    private static Map<String, TimeplusColumnTypeEnum> COLUMN_TYPE_MAP =\n        Maps.newHashMap();\n\n    static {\n        for (TimeplusColumnTypeEnum value : TimeplusColumnTypeEnum.values()) {\n            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);\n        }\n    }\n\n    private ColumnType columnType;\n\n    TimeplusColumnTypeEnum(\n        String dataTypeName,\n        boolean supportLength,\n        boolean supportScale,\n        boolean supportNullable,\n        boolean supportAutoIncrement,\n        boolean supportCharset,\n        boolean supportCollation,\n        boolean supportComments,\n        boolean supportDefaultValue,\n        boolean supportExtent,\n        boolean supportValue\n    ) {\n        this.columnType = new ColumnType(\n            dataTypeName,\n            supportLength,\n            supportScale,\n            supportNullable,\n            supportAutoIncrement,\n            supportCharset,\n            supportCollation,\n            supportComments,\n            supportDefaultValue,\n            supportExtent,\n            supportValue,\n            false\n        );\n    }\n\n    public static TimeplusColumnTypeEnum getByType(String dataType) {\n        return COLUMN_TYPE_MAP.get(\n            SqlUtils.removeDigits(dataType.toUpperCase())\n        );\n    }\n\n    public static List<ColumnType> getTypes() {\n        return Arrays.stream(TimeplusColumnTypeEnum.values())\n            .map(columnTypeEnum -> columnTypeEnum.getColumnType())\n            .toList();\n    }\n\n    public ColumnType getColumnType() {\n        return columnType;\n    }\n\n    @Override\n    public String buildCreateColumnSql(TableColumn column) {\n        TimeplusColumnTypeEnum type = COLUMN_TYPE_MAP.get(\n            column.getColumnType()\n        );\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n\n        script.append(buildNullableAndDataType(column, type)).append(\" \");\n\n        script.append(buildDefaultValue(column, type)).append(\" \");\n\n        script.append(buildComment(column, type)).append(\" \");\n\n        return script.toString();\n    }\n\n    @Override\n    public String buildModifyColumn(TableColumn tableColumn) {\n        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\n                \"DROP COLUMN `\",\n                tableColumn.getName() + \"`\"\n            );\n        }\n        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {\n            return StringUtils.join(\n                \"ADD COLUMN \",\n                buildCreateColumnSql(tableColumn)\n            );\n        }\n        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {\n            String modifyColumn = \"\";\n            if (\n                !StringUtils.equalsIgnoreCase(\n                    tableColumn.getOldName(),\n                    tableColumn.getName()\n                )\n            ) {\n                modifyColumn = StringUtils.join(\n                    \"RENAME COLUMN `\",\n                    tableColumn.getOldName(),\n                    \"` TO `\",\n                    tableColumn.getName(),\n                    \"`, \",\n                    buildCreateColumnSql(tableColumn)\n                );\n            }\n            return StringUtils.join(\n                modifyColumn,\n                \"MODIFY COLUMN \",\n                buildCreateColumnSql(tableColumn)\n            );\n        }\n        return \"\";\n    }\n\n    private String buildComment(\n        TableColumn column,\n        TimeplusColumnTypeEnum type\n    ) {\n        if (\n            !type.columnType.isSupportComments() ||\n            StringUtils.isEmpty(column.getComment())\n        ) {\n            return \"\";\n        }\n        return StringUtils.join(\"COMMENT '\", column.getComment(), \"'\");\n    }\n\n    private String buildDefaultValue(\n        TableColumn column,\n        TimeplusColumnTypeEnum type\n    ) {\n        if (\n            !type.getColumnType().isSupportDefaultValue() ||\n            StringUtils.isEmpty(column.getDefaultValue())\n        ) {\n            return \"\";\n        }\n\n        if (\"EMPTY_STRING\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT ''\");\n        }\n\n        if (\"NULL\".equalsIgnoreCase(column.getDefaultValue().trim())) {\n            return StringUtils.join(\"DEFAULT NULL\");\n        }\n\n        if (Arrays.asList(Enum8, Enum16).contains(type)) {\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        if (Arrays.asList(Date).contains(type)) {\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        if (Arrays.asList(DateTime).contains(type)) {\n            if (\n                \"CURRENT_TIMESTAMP\".equalsIgnoreCase(\n                        column.getDefaultValue().trim()\n                    )\n            ) {\n                return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n            }\n            return StringUtils.join(\"DEFAULT '\", column.getDefaultValue(), \"'\");\n        }\n\n        return StringUtils.join(\"DEFAULT \", column.getDefaultValue());\n    }\n\n    private String buildNullableAndDataType(\n        TableColumn column,\n        TimeplusColumnTypeEnum type\n    ) {\n        StringBuilder script = new StringBuilder();\n        script.append(buildDataType(column, type));\n\n        if (!type.getColumnType().isSupportNullable()) {\n            return script.toString();\n        }\n        if (column.getNullable() != null && 1 == column.getNullable()) {\n            return \"Nullable(\" + script.append(\")\").toString();\n        } else {\n            return script.toString();\n        }\n    }\n\n    private String buildDataType(\n        TableColumn column,\n        TimeplusColumnTypeEnum type\n    ) {\n        String columnType = type.columnType.getTypeName();\n        if (Arrays.asList(FixedString).contains(type)) {\n            return StringUtils.join(\n                columnType,\n                \"(\",\n                column.getColumnSize(),\n                \")\"\n            );\n        }\n\n        if (Arrays.asList(Decimal).contains(type)) {\n            if (\n                column.getColumnSize() == null ||\n                column.getDecimalDigits() == null\n            ) {\n                return columnType;\n            }\n            if (\n                column.getColumnSize() != null &&\n                column.getDecimalDigits() == null\n            ) {\n                return StringUtils.join(\n                    columnType,\n                    \"(\",\n                    column.getColumnSize() + \")\"\n                );\n            }\n            if (\n                column.getColumnSize() != null &&\n                column.getDecimalDigits() != null\n            ) {\n                return StringUtils.join(\n                    columnType,\n                    \"(\",\n                    column.getColumnSize() +\n                    \",\" +\n                    column.getDecimalDigits() +\n                    \")\"\n                );\n            }\n        }\n\n        return columnType;\n    }\n\n    public String buildColumn(TableColumn column) {\n        TimeplusColumnTypeEnum type = COLUMN_TYPE_MAP.get(\n            column.getColumnType()\n        );\n        if (type == null) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"`\").append(column.getName()).append(\"`\").append(\" \");\n        script.append(buildDataType(column, type)).append(\" \");\n        if (StringUtils.isNoneBlank(column.getComment())) {\n            script\n                .append(\"COMMENT\")\n                .append(\" \")\n                .append(\"'\")\n                .append(column.getComment())\n                .append(\"'\")\n                .append(\" \");\n        }\n        return script.toString();\n    }\n\n    private String unsignedDataType(String dataTypeName, String middle) {\n        String[] split = dataTypeName.split(\" \");\n        if (split.length == 2) {\n            return StringUtils.join(split[0], middle, split[1]);\n        }\n        return StringUtils.join(dataTypeName, middle);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/java/ai/chat2db/plugin/timeplus/type/TimeplusEngineTypeEnum.java",
    "content": "package ai.chat2db.plugin.timeplus.type;\n\nimport ai.chat2db.spi.model.EngineType;\nimport com.google.common.collect.Maps;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic enum TimeplusEngineTypeEnum {\n    Stream(\"Stream\", true, true, true, false, true, true, true, true),\n    ExternalStream(\n        \"ExternalStream\",\n        false,\n        false,\n        false,\n        false,\n        true,\n        false,\n        false,\n        false\n    ),\n    ExternalTable(\n        \"ExternalTable\",\n        false,\n        false,\n        false,\n        false,\n        true,\n        false,\n        false,\n        false\n    ),\n    MutableStream(\n        \"MutableStream\",\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true\n    ),\n    View(\"View\", false, false, false, false, false, false, false, false),\n    MaterializedView(\n        \"MaterializedView\",\n        true,\n        false,\n        false,\n        false,\n        true,\n        false,\n        false,\n        false\n    ),\n    Random(\"Random\", false, false, false, false, true, false, false, false),\n    Dictionary(\n        \"Dictionary\",\n        false,\n        false,\n        false,\n        false,\n        false,\n        false,\n        false,\n        false\n    ),\n    S3(\"S3\", false, true, false, false, true, false, false, false),\n    MergeTree(\"MergeTree\", true, true, true, false, true, true, true, false),\n    ReplicatedReplacingMergeTree(\n        \"ReplicatedReplacingMergeTree\",\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true\n    ),\n    Memory(\"Memory\", false, false, false, false, true, true, false, false),\n    ReplacingMergeTree(\n        \"ReplacingMergeTree\",\n        true,\n        true,\n        true,\n        false,\n        true,\n        true,\n        true,\n        false\n    ),\n    ReplicatedAggregatingMergeTree(\n        \"ReplicatedAggregatingMergeTree\",\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true\n    ),\n    ReplicatedMergeTree(\n        \"ReplicatedMergeTree\",\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true\n    ),\n    ReplicatedCollapsingMergeTree(\n        \"ReplicatedCollapsingMergeTree\",\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true,\n        true\n    ),\n    File(\"File\", false, false, false, false, true, false, false, false),\n    SummingMergeTree(\n        \"SummingMergeTree\",\n        true,\n        true,\n        true,\n        false,\n        true,\n        true,\n        true,\n        false\n    ),\n    CollapsingMergeTree(\n        \"CollapsingMergeTree\",\n        true,\n        true,\n        true,\n        false,\n        true,\n        true,\n        true,\n        false\n    ),\n    Merge(\"Merge\", false, false, false, false, false, false, false, false),\n    AggregatingMergeTree(\n        \"AggregatingMergeTree\",\n        true,\n        true,\n        true,\n        false,\n        true,\n        true,\n        true,\n        false\n    ),\n    Null(\"Null\", false, false, false, false, false, true, false, false),\n\n    Log(\"Log\", false, false, false, false, true, false, false, false);\n\n    private static Map<String, TimeplusEngineTypeEnum> ENGINE_TYPE_MAP =\n        Maps.newHashMap();\n\n    static {\n        for (TimeplusEngineTypeEnum value : TimeplusEngineTypeEnum.values()) {\n            ENGINE_TYPE_MAP.put(value.getEngineType().getName(), value);\n        }\n    }\n\n    private EngineType engineType;\n\n    TimeplusEngineTypeEnum(\n        String name,\n        boolean supportTTL,\n        boolean supportSortOrder,\n        boolean supportSkippingIndices,\n        boolean supportDeduplication,\n        boolean supportSettings,\n        boolean supportParallelInsert,\n        boolean supportProjections,\n        boolean supportReplication\n    ) {\n        this.engineType = new EngineType(\n            name,\n            supportTTL,\n            supportSortOrder,\n            supportSkippingIndices,\n            supportDeduplication,\n            supportSettings,\n            supportParallelInsert,\n            supportProjections,\n            supportReplication\n        );\n    }\n\n    public static TimeplusEngineTypeEnum getByType(String dataType) {\n        return ENGINE_TYPE_MAP.get(dataType.toUpperCase());\n    }\n\n    public static List<EngineType> getTypes() {\n        return Arrays.stream(TimeplusEngineTypeEnum.values())\n            .map(engineTypeEnum -> engineTypeEnum.getEngineType())\n            .toList();\n    }\n\n    public EngineType getEngineType() {\n        return engineType;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/java/ai/chat2db/plugin/timeplus/type/TimeplusIndexTypeEnum.java",
    "content": "package ai.chat2db.plugin.timeplus.type;\n\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.IndexType;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport java.util.Arrays;\nimport java.util.List;\nimport org.apache.commons.lang3.StringUtils;\n\npublic enum TimeplusIndexTypeEnum {\n    PRIMARY(\"Primary\", \"PRIMARY KEY\"),\n    MINMAX(\"MINMAX\", \"INDEX\"),\n    SET(\"SET\", \"INDEX\"),\n    BLOOM_FILTER(\"BLOOM_FILTER\", \"INDEX\"),\n    TOKENBF_V1(\"TOKENBF_V1\", \"INDEX\"),\n    NGRAMBF_V1(\"NGRAMBF_V1\", \"INDEX\"),\n    INVERTED(\"INVERTED\", \"INDEX\"),\n    HYPOTHESIS(\"HYPOTHESIS\", \"INDEX\"),\n    ANNOY(\"ANNOY\", \"INDEX\"),\n    USEARCH(\"USEARCH\", \"INDEX\");\n\n    private String name;\n    private String keyword;\n    private IndexType indexType;\n\n    TimeplusIndexTypeEnum(String name, String keyword) {\n        this.name = name;\n        this.keyword = keyword;\n        this.indexType = new IndexType(name);\n    }\n\n    public static TimeplusIndexTypeEnum getByType(String type) {\n        for (TimeplusIndexTypeEnum value : TimeplusIndexTypeEnum.values()) {\n            if (value.name.equalsIgnoreCase(type)) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    public static List<IndexType> getIndexTypes() {\n        return Arrays.asList(TimeplusIndexTypeEnum.values())\n            .stream()\n            .map(TimeplusIndexTypeEnum::getIndexType)\n            .collect(java.util.stream.Collectors.toList());\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    public IndexType getIndexType() {\n        return indexType;\n    }\n\n    public void setIndexType(IndexType indexType) {\n        this.indexType = indexType;\n    }\n\n    public String buildIndexScript(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(keyword).append(\" \");\n        script.append(buildIndexName(tableIndex)).append(\" \");\n        script.append(buildIndexColumn(tableIndex)).append(\" \");\n        script.append(buildIndexType(tableIndex)).append(\" \");\n        return script.toString();\n    }\n\n    private String buildIndexType(TableIndex tableIndex) {\n        if (this.equals(PRIMARY)) {\n            return \"\";\n        } else {\n            return \"TYPE \" + name;\n        }\n    }\n\n    private String buildIndexColumn(TableIndex tableIndex) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"(\");\n        for (TableIndexColumn column : tableIndex.getColumnList()) {\n            if (StringUtils.isNotBlank(column.getColumnName())) {\n                script.append(\"`\").append(column.getColumnName()).append(\"`\");\n                script.append(\",\");\n            }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    private String buildIndexName(TableIndex tableIndex) {\n        if (this.equals(PRIMARY)) {\n            return \"\";\n        } else {\n            return \"`\" + tableIndex.getName() + \"`\";\n        }\n    }\n\n    public String buildModifyIndex(TableIndex tableIndex) {\n        if (this.equals(PRIMARY)) {\n            return \"\";\n        }\n        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\n                \"DROP INDEX `\",\n                tableIndex.getOldName(),\n                \"`\"\n            );\n        }\n        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\n                \"DROP INDEX `\",\n                tableIndex.getOldName(),\n                \"`,\\n ADD \",\n                buildIndexScript(tableIndex)\n            );\n        }\n        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {\n            return StringUtils.join(\"ADD \", buildIndexScript(tableIndex));\n        }\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/chat2db-timeplus/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin",
    "content": "ai.chat2db.plugin.timeplus.TimeplusPlugin\n"
  },
  {
    "path": "chat2db-server/chat2db-plugins/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-parent</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-plugins</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>chat2db-mysql</module>\n        <module>chat2db-sqlserver</module>\n        <module>chat2db-postgresql</module>\n        <module>chat2db-oracle</module>\n        <module>chat2db-sqlite</module>\n        <module>chat2db-h2</module>\n        <module>chat2db-clickhouse</module>\n        <module>chat2db-oceanbase</module>\n        <module>chat2db-db2</module>\n        <module>chat2db-mariadb</module>\n        <module>chat2db-dm</module>\n        <module>chat2db-mongodb</module>\n        <module>chat2db-presto</module>\n        <module>chat2db-hive</module>\n        <module>chat2db-kingbase</module>\n        <module>chat2db-timeplus</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/README.md",
    "content": "## ali-dbhub-server-domain-core\n写核心处理逻辑\n## ali-dbhub-server-domain-data\n连接各种花里胡哨数据库连接"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-domain</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>chat2db-server-domain-api</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-tools-base</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/chart/ChartCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.chart;\n\nimport java.time.LocalDateTime;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ChartCreateParam.java, v 0.1 June 9, 2023 15:38 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class ChartCreateParam {\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * chart name\n     */\n    private String name;\n\n    /**\n     * chart information\n     */\n    private String schema;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * schemaName\n     */\n    private String schemaName;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * Whether it has been deleted, y means deleted, n means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/chart/ChartListQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.chart;\n\nimport java.util.List;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.NonNull;\n\n/**\n * query\n *\n * @author Jiaju Zhuang\n */\n@Data\n@NoArgsConstructor\npublic class ChartListQueryParam {\n\n    /**\n     * primary key\n     */\n    @NonNull\n    private List<Long> idList;\n\n    /**\n     * user id\n     */\n    @NonNull\n    private Long userId;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/chart/ChartPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.chart;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version UserSavedDdlPageQueryParam.java, v 0.1 September 25, 2022 14:05 moji Exp $\n * @date 2022/09/25\n */\n@Data\npublic class ChartPageQueryParam extends PageQueryParam {\n\n    /**\n     * Report ID\n     */\n    private Long dashboardId;\n\n    /**\n     * search keyword\n     */\n    private String searchKey;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/chart/ChartQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.chart;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.NonNull;\n\n/**\n * query\n *\n * @author Jiaju Zhuang\n */\n@Data\n@NoArgsConstructor\npublic class ChartQueryParam {\n\n    /**\n     * primary key\n     */\n    @NonNull\n    private Long id;\n\n    /**\n     * user id\n     */\n    @NonNull\n    private Long userId;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/chart/ChartUpdateParam.java",
    "content": "package ai.chat2db.server.domain.api.chart;\n\nimport java.time.LocalDateTime;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ChartUpdateParam.java, v 0.1 June 9, 2023 15:39 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class ChartUpdateParam {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * chart name\n     */\n    private String name;\n\n    /**\n     * chart information\n     */\n    private String schema;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/AccessObjectTypeEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * Access Object Type\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum AccessObjectTypeEnum implements BaseEnum<String> {\n    /**\n     * TEAM\n     */\n    TEAM(\"TEAM\"),\n\n    /**\n     * USER\n     */\n    USER(\"USER\"),\n\n    ;\n\n    final String description;\n\n    AccessObjectTypeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/AiSqlSourceEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\n\nimport lombok.Getter;\n\n/**\n * AI model type selected by AI SQL\n *\n * @author moji\n */\n@Getter\npublic enum AiSqlSourceEnum implements BaseEnum<String> {\n    /**\n     * OPENAI\n     */\n    OPENAI( \"OPENAI\"),\n\n    /**\n     * RESTAI\n     */\n    RESTAI(\"RESTAI\"),\n\n    /**\n     * AZURE OPENAI\n     */\n    AZUREAI(\"AZURE OPENAI\"),\n\n    /**\n     * CHAT2DB OPENAI\n     */\n    CHAT2DBAI(\"CHAT2DB OPENAI\"),\n\n    /**\n     * CLAUDE AI\n     */\n    CLAUDEAI(\"CLAUDE AI\"),\n\n    /**\n     * WENXIN AI\n     */\n    WENXINAI(\"WENXIN AI\"),\n\n    /**\n     * BAICHUAN AI\n     */\n    BAICHUANAI(\"BAICHUAN AI\"),\n\n    /**\n     * ZHIPU AI\n     */\n    ZHIPUAI(\"ZHIPU AI\"),\n\n    /**\n     * TONGYIQIANWEN AI\n     */\n    TONGYIQIANWENAI(\"TONGYIQIANWEN AI\"),\n\n    /**\n     * FAST CHAT AI\n     */\n    FASTCHATAI(\"FAST CHAT AI\"),\n\n    ;\n\n    final String description;\n\n\n    AiSqlSourceEnum(String description) {\n        this.description = description;\n    }\n\n    /**\n     * Get enum by name\n     *\n     * @param name\n     * @return\n     */\n    public static AiSqlSourceEnum getByName(String name) {\n        for (AiSqlSourceEnum dbTypeEnum : AiSqlSourceEnum.values()) {\n            if (dbTypeEnum.name().equals(name)) {\n                return dbTypeEnum;\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/DataSourceKindEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * Data Source Kind\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum DataSourceKindEnum implements BaseEnum<String> {\n    /**\n     * PRIVATE\n     */\n    PRIVATE(\"PRIVATE\"),\n\n    /**\n     * SHARED\n     */\n    SHARED(\"SHARED\"),\n\n    ;\n\n    final String description;\n\n    DataSourceKindEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/DeletedTypeEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\npublic enum DeletedTypeEnum {\n\n    Y,N\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/EnvironmentEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * Environment\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum EnvironmentEnum implements BaseEnum<Long> {\n    /**\n     * RELEASE\n     */\n    RELEASE(1L, \"RELEASE\"),\n\n    /**\n     * TEST\n     */\n    TEST(2L, \"TEST\"),\n\n    ;\n    final Long code;\n    final String description;\n\n    EnvironmentEnum(Long code, String description) {\n        this.code = code;\n        this.description = description;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportFileSuffix.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport lombok.Getter;\n\n/**\n * ExportFileType\n *\n * @author lzy\n **/\n@Getter\npublic enum ExportFileSuffix {\n\n    //word\n    WORD(\".docx\"),\n    //excel\n    EXCEL(\".xlsx\"),\n    XLS(\".xls\"),\n    //markdown\n    MARKDOWN(\".md\"),\n    //html\n    HTML(\".html\"),\n    //pdf\n    PDF(\".pdf\"),\n\n    SQL(\".sql\"),\n\n    JSON(\".json\"),\n\n    CSV(\".csv\"),\n\n    ZIP(\".zip\");\n\n    private String suffix;\n\n    ExportFileSuffix(String suffix) {\n        this.suffix = suffix;\n    }\n\n    public void setSuffix(String suffix) {\n        this.suffix = suffix;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportSizeEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * How much data is currently needed at the beginning\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum ExportSizeEnum implements BaseEnum<String> {\n    /**\n     * CURRENT_PAGE\n     */\n    CURRENT_PAGE(\"CURRENT_PAGE\"),\n\n    /**\n     * ALL\n     */\n    ALL(\"ALL\"),\n\n    ;\n\n    final String description;\n\n    ExportSizeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportTypeEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\nimport javax.swing.text.html.HTML;\n\n/**\n * export type\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum ExportTypeEnum implements BaseEnum<String> {\n    /**\n     * CSV\n     */\n    CSV(\"CSV\"),\n\n    /**\n     * INSERT\n     */\n    INSERT(\"INSERT\"),\n\n    /**\n     * WORD\n     */\n    WORD(\"WORD\"),\n\n    /**\n     * EXCEL\n     */\n    EXCEL(\"EXCEL\"),\n\n    /**\n     * HTML\n     */\n    HTML(\"HTML\"),\n\n    /**\n     * MARKDOWN\n     */\n    MARKDOWN(\"MARKDOWN\"),\n\n    /**\n     * PDF\n     */\n    PDF(\"PDF\"),\n\n    JSON(\"JSON\"),\n\n    SQL(\"SQL\");\n\n    final String description;\n\n    ExportTypeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/OperationStatusEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\n\nimport lombok.Getter;\n\n/**\n * state\n *\n * @author Shi Yi\n */\n@Getter\npublic enum OperationStatusEnum implements BaseEnum<String> {\n    /**\n     * draft\n     */\n    DRAFT(\"草稿\"),\n\n    /**\n     * Published\n     */\n    RELEASE(\"已发布\"),\n\n    ;\n\n    final String description;\n\n    OperationStatusEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/RoleCodeEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * role code\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum RoleCodeEnum implements BaseEnum<String> {\n    /**\n     * DESKTOP\n     */\n    DESKTOP(\"DESKTOP\", 1L, \"_desktop_default_user_name\", \"_desktop_default_user_name\"),\n\n    /**\n     * ADMIN\n     */\n    ADMIN(\"ADMIN\", 2L, System.getenv().getOrDefault(\"ADMIN_NAME\",\"chat2db\"),\n            System.getenv().getOrDefault(\"ADMIN_PASSWORD\",\"chat2db\")),\n\n    /**\n     * USER\n     */\n    USER(\"USER\", null, null, null),\n\n    ;\n    final String description;\n    final Long defaultUserId;\n    final String userName;\n    final String password;\n\n    RoleCodeEnum(String description, Long defaultUserId, String userName, String password) {\n        this.description = description;\n        this.defaultUserId = defaultUserId;\n        this.userName = userName;\n        this.password = password;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/TableVectorEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * table vector status\n *\n * @author moji\n */\n@Getter\npublic enum TableVectorEnum implements BaseEnum<String> {\n    /**\n     * SAVED\n     */\n    SAVED( \"SAVED\"),\n\n\n    ;\n\n    final String description;\n\n\n    TableVectorEnum(String description) {\n        this.description = description;\n    }\n\n    /**\n     * Get enum by name\n     *\n     * @param name\n     * @return\n     */\n    public static TableVectorEnum getByName(String name) {\n        for (TableVectorEnum dbTypeEnum : TableVectorEnum.values()) {\n            if (dbTypeEnum.name().equals(name)) {\n                return dbTypeEnum;\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/TaskStatusEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\npublic enum TaskStatusEnum {\n\n    INIT, PROCESSING, FINISH, ERROR\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/TaskTypeEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\npublic enum TaskTypeEnum {\n\n    /**\n     * download table data\n     */\n    DOWNLOAD_TABLE_DATA,\n\n    /**\n     * upload table data\n     */\n    UPLOAD_TABLE_DATA,\n\n    /**\n     * download table structure\n     */\n    DOWNLOAD_TABLE_STRUCTURE,\n\n    /**\n     * upload table structure\n     */\n    UPLOAD_TABLE_STRUCTURE,\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ValidStatusEnum.java",
    "content": "package ai.chat2db.server.domain.api.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * Is it a valid enumeration\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum ValidStatusEnum implements BaseEnum<String> {\n    /**\n     * VALID\n     */\n    VALID(\"VALID\"),\n\n    /**\n     * INVALID\n     */\n    INVALID(\"INVALID\"),\n\n    ;\n    final String description;\n\n    ValidStatusEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/AIConfig.java",
    "content": "\npackage ai.chat2db.server.domain.api.model;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : SystemConfigRequest.java\n */\n@Data\npublic class AIConfig {\n\n    /**\n     * APIKEY\n     */\n    private String apiKey = \"\";\n\n    /**\n     * SECRETKEY\n     */\n    private String secretKey = \"\";\n\n    /**\n     * APIHOST\n     */\n    private String apiHost = \"\";\n\n    /**\n     * api http proxy host\n     */\n    private String httpProxyHost = \"\";\n\n    /**\n     * api http proxy port\n     */\n    private String httpProxyPort = \"\";\n\n    /**\n     * @see AiSqlSourceEnum\n     */\n    @NotNull\n    private String aiSqlSource = \"\";\n\n    /**\n     * return data stream\n     * Optional, default value is TRUE\n     */\n    private Boolean stream = Boolean.TRUE;\n\n    /**\n     * deployed model, default gpt-3.5-turbo\n     */\n    private String model = \"\";\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/Chart.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.util.Date;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version Chart.java, v 0.1 June 9, 2023 15:37 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class Chart {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Chart name\n     */\n    private String name;\n\n    /**\n     * Chart description\n     */\n    private String description;\n\n    /**\n     * schema\n     */\n    private String schema;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Data source name\n     */\n    private String dataSourceName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * Whether it has been deleted, y means deleted, n means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/ChatGptConfig.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ChatGptConfig.java, v 0.1 May 9, 2023 13:47 moji Exp $\n * @date 2023/05/09\n */\n@Data\npublic class ChatGptConfig {\n    /**\n     * chat2db APIKEY\n     */\n    private String chat2dbApiKey;\n\n    /**\n     * chat2db APIHOST\n     */\n    private String chat2dbApiHost;\n\n    /**\n     * OpenAi APIKEY\n     */\n    private String apiKey;\n\n    /**\n     * OpenAi APIHOST\n     */\n    private String apiHost;\n\n    /**\n     * HTTP proxy host\n     */\n    private String httpProxyHost;\n\n    /**\n     * HTTP proxy Port\n     */\n    private String httpProxyPort;\n\n    /**\n     * AI type\n     * @see AiSqlSourceEnum\n     */\n    private String aiSqlSource;\n\n    /**\n     * Custom AI interface\n     */\n    private String restAiUrl;\n\n    /**\n     * Whether the Rest interface streams output\n     * Optional, default value is TRUE\n     */\n    private Boolean restAiStream = Boolean.TRUE;\n\n    /**\n     * Get Azure OpenAI key credential from the Azure Portal\n     */\n    private String azureApiKey;\n\n    /**\n     * Get Azure OpenAI endpoint from the Azure Portal\n     */\n    private String azureEndpoint;\n\n    /**\n     * deploymentId of the deployed model, default gpt-3.5-turbo\n     */\n    private String azureDeploymentId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/Config.java",
    "content": "\npackage ai.chat2db.server.domain.api.model;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : Config.java\n */\n@Data\npublic class Config implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = 8377899386569086415L;\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Configuration item code\n     */\n    private String code;\n\n    /**\n     * Configuration item content\n     */\n    private String content;\n\n\n    /**\n     * Configuration summary\n     */\n    private String summary;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/Dashboard.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version Dashboard.java, v 0.1 June 9, 2023 15:32 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class Dashboard {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Dashboard name\n     */\n    private String name;\n\n    /**\n     * Dashboard description\n     */\n    private String description;\n\n    /**\n     * Dashboard layout information\n     */\n    private String schema;\n\n    /**\n     * Whether it has been deleted, y means deleted, n means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * Chart ID list\n     */\n    private List<Long> chartIds;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/DataSource.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.time.LocalDateTime;\nimport java.util.LinkedHashMap;\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\nimport lombok.Data;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * @author moji\n * @version DataSourceDTO.java, v 0.1 September 23, 2022 15:39 moji Exp $\n * @date 2022/09/23\n */\n@Data\npublic class DataSource {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * user name\n     */\n    private String userName;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * environment type\n     */\n    private String envType;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n\n    /**\n     * environment id\n     */\n    private Long environmentId;\n\n    /**\n     * environment\n     */\n    private Environment environment;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n\n    /**\n     * Connection Type\n     *\n     * @see ai.chat2db.server.domain.api.enums.DataSourceKindEnum\n     */\n    private String kind;\n\n\n    /**\n     * Service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n\n\n    private boolean supportDatabase;\n\n    private boolean supportSchema;\n\n    public LinkedHashMap<String, Object> getExtendMap() {\n        if (ObjectUtils.isEmpty(extendInfo)) {\n            return new LinkedHashMap<>();\n        }\n        LinkedHashMap<String, Object> map = new LinkedHashMap<>();\n        for (KeyValue keyValue : extendInfo) {\n            map.put(keyValue.getKey(), keyValue.getValue());\n        }\n        return map;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/DataSourceAccess.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessSelector;\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * DataSource Access\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceAccess implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * creation time\n     */\n    @NotNull\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    @NotNull\n    private LocalDateTime gmtModified;\n\n    /**\n     * Creator user id\n     */\n    private Long createUserId;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * data source\n     */\n    @NotNull\n    private DataSource dataSource;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    @NotNull\n    private String accessObjectType;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    @NotNull\n    private Long accessObjectId;\n\n    /**\n     * Authorization object\n     * @see DataSourceAccessSelector#setAccessObject(Boolean)\n     */\n    @NotNull\n    private DataSourceAccessObject accessObject;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/DataSourceAccessObject.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * DataSource Access Object\n * It could be a user or a team\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceAccessObject implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    private Long id;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    private String type;\n\n    /**\n     * The name of the code that belongs to the authorization type, such as user account, team code\n     */\n    private String code;\n\n    /**\n     * Code that belongs to the authorization type, such as user name, team name\n     */\n    private String name;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/Environment.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Environment\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Environment implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * environment name\n     */\n    private String name;\n\n    /**\n     * environment abbreviation\n     */\n    private String shortName;\n\n    /**\n     * color\n     */\n    private String color;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/IndexInfo.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n/**\n * Index export information\n *\n * @author lzy\n */\n@Data\n@Accessors(chain = true)\npublic class IndexInfo {\n    /**\n     * Index name\n     */\n    private String name;\n    /**\n     * Field\n     */\n    private String columnName;\n    /**\n     * Index type\n     */\n    private String indexType;\n    /**\n     * Index method\n     */\n    private String indexMethod;\n    /**\n     * Comment\n     */\n    private String comment;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/Operation.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.time.LocalDateTime;\n\nimport lombok.Data;\n\n/**\n * <p>\n * My save list\n * </p>\n *\n * @author ali-dbhub\n * @since 2022-09-18\n */\n@Data\npublic class Operation {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Data source name\n     */\n    private String dataSourceName;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located\n     */\n    private String schemaName;\n    \n    /**\n     * save name\n     */\n    private String name;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * ddl statement status: DRAFT/RELEASE\n     */\n    private String status;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/OperationLog.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.time.LocalDateTime;\n\nimport lombok.Data;\n\n/**\n * <p>\n * My execution record\n * </p>\n *\n * @author ali-dbhub\n * @since 2022-09-18\n */\n@Data\npublic class OperationLog {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * data source\n     */\n    private String dataSourceName;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * status\n     */\n    private String status;\n\n    /**\n     * Number of operation lines\n     */\n    private Long operationRows;\n\n    /**\n     * Length of use\n     */\n    private Long useTime;\n\n    /**\n     * Extended Information\n     */\n    private String extendInfo;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/TableParameter.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n/**\n * TableParameter\n *\n * @author lzy\n **/\n@Data\n@Accessors(chain = true)\npublic class TableParameter {\n    /**\n     * serial number\n     **/\n    private String no;\n    /**\n     * Field name\n     **/\n    private String fieldName;\n    /**\n     * type of data\n     **/\n    private String columnType;\n    /**\n     * length\n     **/\n    private String length;\n    /**\n     * not null\n     **/\n    private String isNullAble;\n    /**\n     * default value\n     **/\n    private String columnDefault;\n    /**\n     * Decimal places\n     **/\n    private String decimalPlaces;\n    /**\n     * Remark\n     **/\n    private String columnComment;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/Task.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n@Data\npublic class Task implements Serializable {\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * table_name\n     */\n    private String tableName;\n\n    /**\n     * Whether it has been deleted, y means deleted, n means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * task type, such as: DOWNLOAD_DATA, UPLOAD_TABLE_DATA, DOWNLOAD_TABLE_STRUCTURE, UPLOAD_TABLE_STRUCTURE,\n     */\n    private String taskType;\n\n    /**\n     * task status\n     */\n    private String taskStatus;\n\n    /**\n     * task progress\n     */\n    private String taskProgress;\n\n    /**\n     * task name\n     */\n    private String taskName;\n\n    /**\n     * download url\n     */\n    private String downloadUrl;\n\n    /**\n     * task content\n     */\n    private byte[] content;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/Team.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Team\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Team implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * team coding\n     */\n    @NotNull\n    private String code;\n\n    /**\n     * Team Name\n     */\n    @NotNull\n    private String name;\n\n    /**\n     * Team status\n     *\n     * @see ai.chat2db.server.domain.api.enums.ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n\n    /**\n     * Team description\n     */\n    private String description;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * Modifier user\n     */\n    private User modifiedUser;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/TeamUser.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Team user\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TeamUser implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * team id\n     */\n    @NotNull\n    private Long teamId;\n\n    /**\n     * team\n     */\n    @NotNull\n    private Team team;\n\n    /**\n     * user id\n     */\n    @NotNull\n    private Long userId;\n\n    /**\n     * user\n     */\n    @NotNull\n    private User user;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/model/User.java",
    "content": "package ai.chat2db.server.domain.api.model;\n\nimport java.util.Date;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * User Info\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class User {\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * username\n     */\n    @NotNull\n    private String userName;\n\n    /**\n     * password\n     */\n    @NotNull\n    private String password;\n\n    /**\n     * Nick name\n     */\n    @NotNull\n    private String nickName;\n\n    /**\n     * email\n     */\n    @NotNull\n    private String email;\n\n    /**\n     * role coding\n     *\n     * @see RoleCodeEnum\n     */\n    private String roleCode;\n\n    /**\n     * user status\n     *\n     * @see ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * Modifier user\n     */\n    private User modifiedUser;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/ConsoleCloseParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Console shutdown parameters\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ConsoleCloseParam {\n\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * The id of the console, ensuring global uniqueness\n     */\n    @NotNull\n    private Long consoleId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/ConsoleConnectParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConsoleConnectParam.java, v 0.1 October 30, 2022 15:53 moji Exp $\n * @date 2022/10/30\n */\n@Data\npublic class ConsoleConnectParam {\n\n    /**\n     * Data source id\n     */\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n    /**\n     * console id\n     */\n    private Long consoleId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/ConsoleCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Console creation parameters\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ConsoleCreateParam {\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * The id of the console, ensuring global uniqueness\n     * Make sure not to duplicate it, in which case the previous connection will be discarded and recreated\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     * Corresponding connection database name\n     * Databases that support multiple databases will call use xx; to switch to the database.\n     */\n    @NotNull\n    private String databaseName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/DlCountParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * total number\n *\n * @author Shi Yi\n */\n@Data\npublic class DlCountParam {\n\n    /**\n     * sql statement\n     */\n    @NotNull\n    private String sql;\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    @NotNull\n    private String databaseName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/DlExecuteParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DataSourceExecuteParam.java, v 0.1 October 14, 2022 13:53 moji Exp $\n * @date 2022/10/14\n */\n@Data\npublic class DlExecuteParam {\n\n    /**\n     * sql statement\n     */\n    @NotNull\n    private String sql;\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    @NotNull\n    private String databaseName;\n\n\n    private String tableName;\n\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * Page coding\n     * Only the select statement has\n     */\n    private Integer pageNo;\n\n    /**\n     * Paging Size\n     * Only the select statement has\n     */\n    private Integer pageSize;\n\n    /**\n     * Return all data\n     * Only the select statement has\n     */\n    private Boolean pageSizeAll;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/DmlSqlCopyParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DmlSqlCopyParam extends TableQueryParam{\n\n    private String type;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/DropParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Delete table structure\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DropParam {\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Corresponding connection database name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * Name\n     */\n    private String name;\n\n    /**\n     * schema\n     */\n    private String schema;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/EnvironmentPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport lombok.Data;\n\n/**\n * environment\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class EnvironmentPageQueryParam extends PageQueryParam {\n\n    /**\n     * search keyword\n     */\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/GroupByParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n\n@Data\npublic class GroupByParam {\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n\n    /**\n     * origin sql\n     */\n    private String originSql;\n\n\n    /**\n     * sort field\n     */\n    private List<String> groupByList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/MetaDataQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class MetaDataQueryParam {\n\n    @NotNull\n    private Long dataSourceId;\n\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/OrderByParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport ai.chat2db.spi.model.OrderBy;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n\n@Data\npublic class OrderByParam {\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n\n    /**\n     * origin sql\n     */\n    private String originSql;\n\n\n    /**\n     * sort field\n     */\n    private List<OrderBy> orderByList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/PinTableParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class PinTableParam {\n\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located\n     */\n    private String schemaName;\n\n    /**\n     * tableName\n     */\n    private String tableName;\n\n    /**\n     * pin userId\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/SchemaOperationParam.java",
    "content": "\npackage ai.chat2db.server.domain.api.param;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author jipengfei\n * @version : SchemaOperationParam.java\n */\n@Data\n@AllArgsConstructor\n@Builder\n@NoArgsConstructor\npublic class SchemaOperationParam {\n    String databaseName;\n    String schemaName;\n    String newSchemaName;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/SchemaQueryParam.java",
    "content": "\npackage ai.chat2db.server.domain.api.param;\n\nimport java.sql.Connection;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author jipengfei\n * @version : SchemaQueryParam.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SchemaQueryParam {\n\n    @NotNull\n    private Long dataSourceId;\n\n    private String dataBaseName;\n\n\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n\n    /**\n     * Can be null, if null, use the default connection\n     */\n    private Connection connection;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/SelectResultOperation.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class SelectResultOperation {\n\n    private String type;\n\n    private List<String> dataList;\n\n    private List<String> oldDataList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/SequencePageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Pagination query sequence information\n *\n * @author Sylphy\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SequencePageQueryParam extends PageQueryParam {\n    private static final long serialVersionUID = 1364512325486354343L;\n\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Corresponding connection database name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * Sequence Name\n     */\n    private String sequenceName;\n\n\n    /**\n     * schema\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/SequenceQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\n\nimport ai.chat2db.server.tools.base.wrapper.param.QueryParam;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serial;\n\n/**\n * Sequence query param\n *\n * @author Sylphy\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SequenceQueryParam extends QueryParam {\n    @Serial\n    private static final long serialVersionUID = -6918238998725081254L;\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Corresponding connection database name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * Sequence Name\n     */\n    private String sequenceName;\n\n    /**\n     * Space name\n     */\n    private String schemaName;\n\n    private boolean refresh;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/ShowCreateSequenceParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Query Sequence creation statement\n *\n * @author Sylphy\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ShowCreateSequenceParam {\n/**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Corresponding connection database name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * Sequence Name\n     */\n    private String sequenceName;\n\n    /**\n     * The schema to which the sequence belongs\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/ShowCreateTableParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Query table creation statement\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ShowCreateTableParam {\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Corresponding connection database name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n\n    /**\n     * The schema to which the table belongs\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/SqlAnalyseParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Sql parsing parameters\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SqlAnalyseParam {\n\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * The SQL that needs to be parsed may be a complex SQL\n     */\n    private String sql;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/SystemConfigParam.java",
    "content": "\npackage ai.chat2db.server.domain.api.param;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author jipengfei\n * @version : SystemConfigParam.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SystemConfigParam implements Serializable {\n    @Serial\n    private static final long serialVersionUID = 7969235263543844658L;\n\n    /**\n     * Configuration item code\n     */\n    private String code;\n\n    /**\n     * Configuration item content\n     */\n    private String content;\n\n    /**\n     * Configuration summary\n     */\n    private String summary;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/TablePageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Pagination query table information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TablePageQueryParam extends PageQueryParam {\n    private static final long serialVersionUID = 8054519332890887747L;\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Corresponding connection database name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n\n\n    /**\n     * schema\n     */\n    private String schemaName;\n\n\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n\n\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/TableQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport java.io.Serial;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.tools.base.wrapper.param.QueryParam;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Query table information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableQueryParam extends QueryParam {\n    @Serial\n    private static final long serialVersionUID = -8918610899872508804L;\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Corresponding connection database name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n\n    /**\n     * Space name\n     */\n    private String schemaName;\n\n    private boolean refresh;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/TableSelector.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * table structure selector\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableSelector {\n    /**\n     * column list\n     */\n    private Boolean columnList;\n\n    /**\n     * index list\n     */\n    private Boolean indexList;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/TableVectorParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableVectorParam {\n\n    /**\n     * api key\n     */\n    @NotNull\n    private String apiKey;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * database name\n     */\n    private String database;\n\n    /**\n     * schema name\n     */\n    private String schema;\n\n    /**\n     * Vector saved state\n     */\n    private String status;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/TaskCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\npublic class TaskCreateParam implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * table_name\n     */\n    private String tableName;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * task progress\n     */\n    private String taskProgress;\n\n    /**\n     * task name\n     */\n    private String taskName;\n\n    /**\n     * task type, such as: DOWNLOAD_DATA, UPLOAD_TABLE_DATA, DOWNLOAD_TABLE_STRUCTURE, UPLOAD_TABLE_STRUCTURE,\n     */\n    private String taskType;\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/TaskPageParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n@Data\npublic class TaskPageParam  extends PageQueryParam implements Serializable {\n\n\n    private Long userId;\n\n    private List<String> taskType;\n\n    private String taskStatus;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/TaskUpdateParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\npublic class TaskUpdateParam implements Serializable {\n\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * task id\n     */\n    private Long id;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * task type, such as: DOWNLOAD_DATA, UPLOAD_TABLE_DATA, DOWNLOAD_TABLE_STRUCTURE, UPLOAD_TABLE_STRUCTURE,\n     */\n    private String taskStatus;\n\n    /**\n     * task progress\n     */\n    private String taskProgress;\n\n    /**\n     * task name\n     */\n    private String taskName;\n\n    /**\n     * task description\n     */\n    private String downloadUrl;\n\n    /**\n     * task content\n     */\n    private byte[] content;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/TypeQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TypeQueryParam {\n\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/UpdateSelectResultParam.java",
    "content": "package ai.chat2db.server.domain.api.param;\n\nimport ai.chat2db.spi.model.Header;\nimport ai.chat2db.spi.model.ResultOperation;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class UpdateSelectResultParam {\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n\n    /**\n     * List of display headers\n     */\n    @NotEmpty\n    private List<Header> headerList;\n\n\n    /**\n     * List of modified data\n     */\n    @NotEmpty\n    private List<ResultOperation> operations;\n\n\n    /**\n     * Table Name\n     */\n    @NotEmpty\n    private String tableName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/dashboard/DashboardCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.dashboard;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DashboardSaveParam.java, v 0.1 June 9, 2023 15:29 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class DashboardCreateParam {\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Report name\n     */\n    private String name;\n\n    /**\n     * description\n     */\n    private String description;\n\n    /**\n     * Report layout information\n     */\n    private String schema;\n\n    /**\n     * Whether it has been deleted, 'Y' means deleted, 'N' means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * Chart ID list\n     */\n    private List<Long> chartIds;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/dashboard/DashboardPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.dashboard;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version UserSavedDdlPageQueryParam.java, v 0.1 September 25, 2022 14:05 moji Exp $\n * @date 2022/09/25\n */\n@Data\npublic class DashboardPageQueryParam extends PageQueryParam {\n\n    /**\n     * search keyword\n     */\n    private String searchKey;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/dashboard/DashboardQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.dashboard;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.NonNull;\n\n/**\n * query\n *\n * @author Jiaju Zhuang\n */\n@Data\n@NoArgsConstructor\npublic class DashboardQueryParam {\n\n    /**\n     * primary key\n     */\n    @NonNull\n    private Long id;\n\n    /**\n     * user id\n     */\n    @NonNull\n    private Long userId;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/dashboard/DashboardSelector.java",
    "content": "package ai.chat2db.server.domain.api.param.dashboard;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * selectro\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DashboardSelector {\n\n    /**\n     * Chart ID list\n     */\n    private Boolean chartIds;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/dashboard/DashboardUpdateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.dashboard;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DashboardSaveParam.java, v 0.1 June 9, 2023 15:29 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class DashboardUpdateParam {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Report name\n     */\n    private String name;\n\n    /**\n     * description\n     */\n    private String description;\n\n    /**\n     * Report layout information\n     */\n    private String schema;\n\n    /**\n     * Whether it has been deleted, y means deleted, n means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * Chart ID list\n     */\n    private List<Long> chartIds;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DataSourceCloseParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * data source closed\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceCloseParam {\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DataSourceCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DataSourceCreateParam.java, v 0.1 September 23, 2022 15:23 moji Exp $\n * @date 2022/09/23\n */\n@Data\npublic class DataSourceCreateParam {\n\n    /**\n     * Alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * userName\n     */\n    private String userName;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * environment type\n     */\n    private String envType;\n\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n\n    /**\n     * Connection Type\n     *\n     * @see ai.chat2db.server.domain.api.enums.DataSourceKindEnum\n     */\n    private String kind;\n\n    /**\n     * environment id\n     */\n    @NotNull\n    private Long environmentId;\n\n    /**\n     * service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DataSourcePageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport ai.chat2db.server.tools.base.wrapper.param.OrderBy;\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport lombok.Data;\nimport lombok.Getter;\n\n/**\n * @author moji\n * @version DataSourcePageQueryParam.java, v 0.1 September 23, 2022 15:27 moji Exp $\n * @date 2022/09/23\n */\n@Data\npublic class DataSourcePageQueryParam extends PageQueryParam {\n\n    /**\n     * search keyword\n     */\n    private String searchKey;\n\n    /**\n     * Connection Type\n     *\n     * @see ai.chat2db.server.domain.api.enums.DataSourceKindEnum\n     */\n    private String kind;\n\n    @Getter\n    public enum OrderCondition implements ai.chat2db.server.tools.base.wrapper.param.OrderCondition {\n        ID_DESC(OrderBy.desc(\"id\")),\n        ;\n\n        final OrderBy orderBy;\n\n        OrderCondition(OrderBy orderBy) {\n            this.orderBy = orderBy;\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DataSourcePreConnectParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCreateRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourcePreConnectParam {\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    @NotNull\n    private String url;\n\n    /**\n     * Connect users\n     */\n    private String user;\n\n    /**\n     * password\n     */\n    @NotNull\n    private String password;\n\n    /**\n     * Connection Type\n     */\n    @NotNull\n    private String type;\n\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DataSourceSelector.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author moji\n * @version DataSourceSelector.java, v 0.1 September 23, 2022 15:28 moji Exp $\n * @date 2022/09/23\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceSelector {\n\n    /**\n     * environment id\n     */\n    private Boolean environment;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DataSourceTestParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Data source test parameters\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceTestParam {\n\n    /**\n     * Database type\n     *\n     * @see DbTypeEnum\n     */\n    @NotNull\n    private String dbType;\n\n    /**\n     * Request connection\n     */\n    @NotNull\n    private String url;\n\n    /**\n     * userName\n     */\n    private String username;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DataSourceUpdateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DataSourceCreateParam.java, v 0.1 September 23, 2022 15:23 moji Exp $\n * @date 2022/09/23\n */\n@Data\npublic class DataSourceUpdateParam {\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * Alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * userName\n     */\n    private String userName;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * environment type\n     */\n    private String envType;\n\n    /**\n     * environment id\n     */\n    private Integer environmentId;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n\n    /**\n     * service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DatabaseCreateParam.java",
    "content": "\npackage ai.chat2db.server.domain.api.param.datasource;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author jipengfei\n * @version : DatabaseOperationParam.java\n */\n@Data\n@AllArgsConstructor\n@Builder\n@NoArgsConstructor\npublic class DatabaseCreateParam {\n\n    private Long dataSourceId;\n\n    private String name;\n\n    private String newName;\n\n    private String comment;\n\n    private String charset;\n\n    private String collation;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DatabaseExportDataParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n/**\n * @author: zgq\n * @date: 2024年03月24日 13:17\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class DatabaseExportDataParam {\n    private Long dataSourceId;\n    private String databaseName;\n    private String schemaName;\n    private String exportType;\n    private List<String> tableNames;\n    private String sqyType;\n    private Boolean containsHeader;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DatabaseExportParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author: zgq\n * @date: 2024年02月27日 22:08\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@Builder\npublic class DatabaseExportParam {\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    private String schemaName;\n\n    private Boolean containData;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DatabaseQueryAllParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource;\n\nimport java.sql.Connection;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Display database information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DatabaseQueryAllParam {\n    /**\n     * Corresponding source id stored in the database\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n\n    /**\n     * Can be null, if null, use the default connection\n     */\n    private Connection connection;\n\n    /**\n     * Can be null, if null, use the default dbType\n     */\n    private String dbType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/access/DataSourceAccessBatchCreatParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource.access;\n\nimport java.util.List;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Data Source Access\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class DataSourceAccessBatchCreatParam extends PageQueryParam {\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * DataSource Access Object\n     */\n    @NotNull\n    @NotEmpty\n    private List<DataSourceAccessObjectParam> accessObjectList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/access/DataSourceAccessComprehensivePageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource.access;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport lombok.Data;\n\n/**\n * Data Source Access\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class DataSourceAccessComprehensivePageQueryParam extends PageQueryParam {\n    /**\n     * Data source id\n     */\n    private Long dataSourceId;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    private String accessObjectType;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    private Long accessObjectId;\n\n    /**\n     * Query keywords for users or teams\n     */\n    private String userOrTeamSearchKey;\n\n    /**\n     * Query keywords for data source\n     */\n    private String dataSourceSearchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/access/DataSourceAccessCreatParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource.access;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Data Source Access\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceAccessCreatParam  {\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    @NotNull\n    private String accessObjectType;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    @NotNull\n    private Long accessObjectId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/access/DataSourceAccessObjectParam.java",
    "content": "\npackage ai.chat2db.server.domain.api.param.datasource.access;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * DataSource Access Object\n * It could be a user or a team\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceAccessObjectParam implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    private Long id;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    private String type;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/access/DataSourceAccessPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource.access;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Data Source Access\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class DataSourceAccessPageQueryParam extends PageQueryParam {\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    @NotNull\n    private String accessObjectType;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    @NotNull\n    private Long accessObjectId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/access/DataSourceAccessSelector.java",
    "content": "package ai.chat2db.server.domain.api.param.datasource.access;\n\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * slecetor\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceAccessSelector {\n\n    /**\n     * Authorization object\n     */\n    private Boolean accessObject;\n\n    /**\n     * data source\n     */\n    private Boolean dataSource;\n\n    /**\n     * data source\n     */\n    private DataSourceSelector dataSourceSelector;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/message/MessageCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.message;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author Juechen\n * @version : MessageCreateParam.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class MessageCreateParam {\n\n    /**\n     * 平台类型\n     * @see ai.chat2db.server.domain.core.enums.ExternalNotificationTypeEnum\n     */\n    private String platformType;\n\n    /**\n     * 服务URL\n     */\n    private String serviceUrl;\n\n    /**\n     * 密钥\n     */\n    private String secretKey;\n\n    /**\n     * 消息模版\n     */\n    private String textTemplate;\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/operation/OperationLogCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.operation;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version UserExecutedDdlCreateParam.java, v 0.1 September 25, 2022 11:08 moji Exp $\n * @date 2022/09/25\n */\n@Data\npublic class OperationLogCreateParam {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n\n    /**\n     * state\n     */\n    private String status;\n\n    /**\n     * Number of operation lines\n     */\n    private Long operationRows;\n\n    /**\n     * Length of use\n     */\n    private Long useTime;\n\n    /**\n     * Extended Information\n     */\n    private String extendInfo;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/operation/OperationLogPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.operation;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version UserExecutedDdlPageQueryParam.java, v 0.1 September 25, 2022 14:05 moji Exp $\n * @date 2022/09/25\n */\n@Data\npublic class OperationLogPageQueryParam extends PageQueryParam {\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * search keyword\n     */\n    private String searchKey;\n\n    /**\n     * Data source id\n     */\n    private Long dataSourceId;\n\n    /**\n     * database name\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/operation/OperationPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.operation;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version UserSavedDdlPageQueryParam.java, v 0.1 September 25, 2022 14:05 moji Exp $\n * @date 2022/09/25\n */\n@Data\npublic class OperationPageQueryParam extends PageQueryParam {\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n    /**\n     * ddl statement status: DRAFT/RELEASE\n     */\n    private String status;\n\n    /**\n     * search keyword\n     */\n    private String searchKey;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * orderBy modify time desc\n     */\n    private Boolean orderByDesc;\n\n    /**\n     * orderBy create time desc\n     */\n    private Boolean orderByCreateDesc;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/operation/OperationQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.operation;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.NonNull;\n\n/**\n * query\n *\n * @author Jiaju Zhuang\n */\n@Data\n@NoArgsConstructor\npublic class OperationQueryParam {\n\n    /**\n     * primary key\n     */\n    @NonNull\n    private Long id;\n\n    /**\n     * user id\n     */\n    @NonNull\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/operation/OperationSavedParam.java",
    "content": "package ai.chat2db.server.domain.api.param.operation;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version UserSavedDdlCreateParam.java, v 0.1 September 25, 2022 15:40 moji Exp $\n * @date 2022/09/25\n */\n@Data\npublic class OperationSavedParam {\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located\n     */\n    private String schemaName;\n\n    /**\n     * save name\n     */\n    private String name;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * ddl statement status: DRAFT/RELEASE\n     */\n    private String status;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/operation/OperationUpdateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.operation;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version UserSavedDdlCreateParam.java, v 0.1 September 25, 2022 15:40 moji Exp $\n * @date 2022/09/25\n */\n@Data\npublic class OperationUpdateParam {\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * databaseName\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located\n     */\n    private String schemaName;\n\n    /**\n     * save name\n     */\n    private String name;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * ddl statement status: DRAFT/RELEASE\n     */\n    private String status;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/team/TeamCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.team;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamCreateParam {\n    /**\n     * team coding\n     */\n    @NotNull\n    private String code;\n\n    /**\n     * Team Name\n     */\n    @NotNull\n    private String name;\n\n    /**\n     * Team status\n     *\n     * @see ai.chat2db.server.domain.api.enums.ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n\n\n    /**\n     * role coding\n     *\n     * @see ai.chat2db.server.domain.api.enums.RoleCodeEnum\n     */\n    @NotNull\n    private String roleCode;\n\n    /**\n     * Team description\n     */\n    private String description;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/team/TeamPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.team;\n\nimport ai.chat2db.server.tools.base.wrapper.param.OrderBy;\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport lombok.Data;\nimport lombok.Getter;\n\n/**\n * page query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamPageQueryParam extends PageQueryParam {\n\n    /**\n     * searchKey\n     */\n    private String searchKey;\n\n    @Getter\n    public enum OrderCondition implements ai.chat2db.server.tools.base.wrapper.param.OrderCondition {\n        ID_DESC(OrderBy.desc(\"id\")),\n        ;\n\n        final OrderBy orderBy;\n\n        OrderCondition(OrderBy orderBy) {\n            this.orderBy = orderBy;\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/team/TeamSelector.java",
    "content": "package ai.chat2db.server.domain.api.param.team;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * select\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TeamSelector {\n    /**\n     * Modifier user\n     */\n    private Boolean modifiedUser;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/team/TeamUpdateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.team;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * update\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamUpdateParam {\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * Team Name\n     */\n    private String name;\n\n    /**\n     * Team status\n     *\n     * @see ai.chat2db.server.domain.api.enums.ValidStatusEnum\n     */\n    private String status;\n\n    /**\n     * role coding\n     *\n     * @see ai.chat2db.server.domain.api.enums.RoleCodeEnum\n     */\n    private String roleCode;\n\n    /**\n     * Team description\n     */\n    private String description;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/team/user/TeamUserComprehensivePageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.team.user;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport lombok.Data;\n\n/**\n * Team User\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamUserComprehensivePageQueryParam extends PageQueryParam {\n\n    /**\n     * team id\n     */\n    private Long teamId;\n\n    /**\n     * user id\n     */\n    private Long userId;\n    \n\n    /**\n     * Query keywords for team\n     */\n    private String teamSearchKey;\n\n    /**\n     * Query keywords for user\n     */\n    private String userSearchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/team/user/TeamUserCreatParam.java",
    "content": "package ai.chat2db.server.domain.api.param.team.user;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Team User\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TeamUserCreatParam {\n    /**\n     * team id\n     */\n    @NotNull\n    private Long teamId;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/team/user/TeamUserPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.team.user;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Team User\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamUserPageQueryParam extends PageQueryParam {\n\n    /**\n     * team id\n     */\n    @NotNull\n    private Long teamId;\n\n    /**\n     * user id\n     */\n    @NotNull\n    private Long userId;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/team/user/TeamUserSelector.java",
    "content": "package ai.chat2db.server.domain.api.param.team.user;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * select\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TeamUserSelector {\n    /**\n     * Team\n     */\n    private Boolean team;\n\n    /**\n     * User\n     */\n    private Boolean user;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/user/UserCreateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.user;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class UserCreateParam {\n    /**\n     * userName\n     */\n    @NotNull\n    private String userName;\n\n    /**\n     * password\n     */\n    @NotNull\n    private String password;\n\n    /**\n     * Nick name\n     */\n    @NotNull\n    private String nickName;\n\n    /**\n     * Mail\n     */\n    @NotNull\n    private String email;\n\n    /**\n     * role coding\n     *\n     * @see RoleCodeEnum\n     */\n    @NotNull\n    private String roleCode;\n\n    /**\n     * user status\n     *\n     * @see ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/user/UserPageQueryParam.java",
    "content": "package ai.chat2db.server.domain.api.param.user;\n\nimport ai.chat2db.server.tools.base.wrapper.param.OrderBy;\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * * page query\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserPageQueryParam extends PageQueryParam {\n\n    /**\n     * searchKey\n     */\n    private String searchKey;\n\n    @Getter\n    public enum OrderCondition implements ai.chat2db.server.tools.base.wrapper.param.OrderCondition {\n        ID_DESC(OrderBy.desc(\"id\")),\n        ;\n\n        final OrderBy orderBy;\n\n        OrderCondition(OrderBy orderBy) {\n            this.orderBy = orderBy;\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/user/UserSelector.java",
    "content": "package ai.chat2db.server.domain.api.param.user;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * select\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserSelector {\n    /**\n     * Modifier user\n     */\n    private Boolean modifiedUser;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/user/UserUpdateParam.java",
    "content": "package ai.chat2db.server.domain.api.param.user;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class UserUpdateParam {\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * password\n     */\n    @NotNull\n    private String password;\n\n    /**\n     * Nick name\n     */\n    @NotNull\n    private String nickName;\n\n    /**\n     * Mail\n     */\n    @NotNull\n    private String email;\n\n\n    /**\n     * role coding\n     *\n     * @see RoleCodeEnum\n     */\n    private String roleCode;\n\n    /**\n     * user status\n     *\n     * @see ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/ChartService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.chart.ChartCreateParam;\nimport ai.chat2db.server.domain.api.chart.ChartListQueryParam;\nimport ai.chat2db.server.domain.api.chart.ChartQueryParam;\nimport ai.chat2db.server.domain.api.chart.ChartUpdateParam;\nimport ai.chat2db.server.domain.api.model.Chart;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\n\n/**\n * @author moji\n * @version ChartService.java, v 0.1 June 9, 2023 15:28 moji Exp $\n * @date 2023/06/09\n */\npublic interface ChartService {\n    /**\n     * Create report\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> createWithPermission(ChartCreateParam param);\n\n    /**\n     * Update report\n     *\n     * @param param\n     * @return\n     */\n    ActionResult updateWithPermission(ChartUpdateParam param);\n\n    /**\n     * Query based on id\n     *\n     * @param id\n     * @return\n     */\n    DataResult<Chart> find(@NotNull Long id);\n\n    /**\n     * Query a piece of data\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Chart> queryExistent(@NotNull ChartQueryParam param);\n\n    /**\n     * Query a piece of data\n     *\n     * @param id\n     * @return\n     */\n    DataResult<Chart> queryExistent(@NotNull Long id);\n\n    /**\n     * Query multiple pieces of data\n     *\n     * @param param\n     * @return\n     */\n    ListResult<Chart> listQuery(@NotNull ChartListQueryParam param);\n\n    /**\n     * Query chart list by ID\n     *\n     * @param ids\n     * @return\n     */\n    ListResult<Chart> queryByIds(@NotEmpty List<Long> ids);\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    ActionResult deleteWithPermission(@NotNull Long id);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/ConfigService.java",
    "content": "\npackage ai.chat2db.server.domain.api.service;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.SystemConfigParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\n\n/**\n * @author jipengfei\n * @version : SystemConfigService.java\n */\npublic interface ConfigService {\n\n    /**\n     * Create configuration\n     *\n     * @param param\n     * @return\n     */\n    ActionResult create(SystemConfigParam param);\n\n    /**\n     * Change setting\n     *\n     * @param param\n     * @return\n     */\n    ActionResult update(SystemConfigParam param);\n\n    /**\n     * insert or update\n     * @param param\n     * @return\n     */\n    ActionResult createOrUpdate(SystemConfigParam param);\n\n    /**\n     * Query based on code\n     *\n     * @param code\n     * @return\n     */\n    DataResult<Config> find(@NotNull String code);\n\n    /**\n     * delete\n     *\n     * @param code\n     * @return\n     */\n    ActionResult delete(@NotNull String code);\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/ConsoleService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.param.ConsoleCloseParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\n\n/**\n * Data source management services\n *\n * @author moji\n * @version DataSourceCoreService.java, v 0.1 September 23, 2022 15:22 moji Exp $\n * @date 2022/09/23\n */\npublic interface ConsoleService {\n\n    /**\n     * Create console link\n     *\n     * @param param\n     * @return\n     */\n    ActionResult createConsole(ConsoleConnectParam param);\n\n    /**\n     * close connection\n     *\n     * @param param\n     * @return\n     */\n    ActionResult closeConsole(ConsoleCloseParam param);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/DashboardService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.model.Dashboard;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardCreateParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardPageQueryParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardQueryParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardUpdateParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport jakarta.validation.constraints.NotNull;\n\n/**\n * @author moji\n * @version DashboardService.java, v 0.1 June 9, 2023 15:28 moji Exp $\n * @date 2023/06/09\n */\npublic interface DashboardService {\n\n    /**\n     * Save report\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> createWithPermission(DashboardCreateParam param);\n\n    /**\n     * Update report\n     *\n     * @param param\n     * @return\n     */\n    ActionResult updateWithPermission(DashboardUpdateParam param);\n\n    /**\n     * Query based on id\n     *\n     * @param id\n     * @return\n     */\n    DataResult<Dashboard> find(@NotNull Long id);\n\n    /**\n     * Query a piece of data\n     *\n     * @param param\n     * @param selector\n     * @return\n     */\n    DataResult<Dashboard> queryExistent(@NotNull DashboardQueryParam param);\n\n    /**\n     * Query a piece of data\n     *\n     * @param id\n     * @return\n     */\n    DataResult<Dashboard> queryExistent(@NotNull Long id);\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    ActionResult deleteWithPermission(@NotNull Long id);\n\n    /**\n     * Query report list\n     *\n     * @param param\n     * @return\n     */\n    PageResult<Dashboard> queryPage(DashboardPageQueryParam param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/DataSourceAccessBusinessService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport jakarta.validation.constraints.NotNull;\n\n/**\n * Data Source Access\n *\n * @author Jiaju Zhuang\n */\npublic interface DataSourceAccessBusinessService {\n    /**\n     * delete\n     *\n     * @param dataSource\n     * @return\n     */\n    ActionResult checkPermission(@NotNull DataSource dataSource);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/DataSourceAccessService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.model.DataSourceAccess;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessComprehensivePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessPageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessSelector;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport jakarta.validation.constraints.NotNull;\n\n/**\n * Data Source Access\n *\n * @author Jiaju Zhuang\n */\npublic interface DataSourceAccessService {\n\n    /**\n     * Comprehensive Paging Query Data\n     *\n     * @param param\n     * @param selector\n     * @return\n     */\n    PageResult<DataSourceAccess> pageQuery(DataSourceAccessPageQueryParam param, DataSourceAccessSelector selector);\n\n    /**\n     * Paging Query Data\n     *\n     * @param param\n     * @param selector\n     * @return\n     */\n    PageResult<DataSourceAccess> comprehensivePageQuery(DataSourceAccessComprehensivePageQueryParam param,\n        DataSourceAccessSelector selector);\n\n\n    /**\n     * Batch Create\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> create(DataSourceAccessCreatParam param);\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    ActionResult delete(@NotNull Long id);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/DataSourceService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceUpdateParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.exception.PermissionDeniedBusinessException;\nimport ai.chat2db.spi.model.Database;\nimport jakarta.validation.constraints.NotNull;\n\n/**\n * Data source management services\n *\n * @author moji\n * @version DataSourceCoreService.java, v 0.1 September 23, 2022 15:22 moji Exp $\n * @date 2022/09/23\n */\npublic interface DataSourceService {\n\n    /**\n     * Create data source connection\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> createWithPermission(DataSourceCreateParam param);\n\n    /**\n     * Update data source connection\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> updateWithPermission(DataSourceUpdateParam param);\n\n    /**\n     * Delete data source connection\n     *\n     * @param id\n     * @return\n     */\n    ActionResult deleteWithPermission(@NotNull Long id);\n\n    /**\n     * Query data source connection details based on id\n     *\n     * @param id\n     * @return\n     */\n    DataResult<DataSource> queryById(@NotNull Long id);\n\n    /**\n     * Query data source connection details based on id\n     *\n     * @param id\n     * @return\n     * @throws ai.chat2db.server.tools.common.exception.DataNotFoundException\n     */\n    DataResult<DataSource> queryExistent(@NotNull Long id, DataSourceSelector selector);\n\n    /**\n     * clone connection\n     *\n     * @param id\n     * @return\n     */\n    DataResult<Long> copyByIdWithPermission(@NotNull Long id);\n\n    /**\n     * Paginated query data source list\n     *\n     * @param param\n     * @param selector\n     * @return\n     */\n    PageResult<DataSource> queryPage(DataSourcePageQueryParam param, DataSourceSelector selector);\n\n    /**\n     * Paginated query data source list\n     * Need to determine permissions\n     *\n     * @param param\n     * @param selector\n     * @return\n     * @throws PermissionDeniedBusinessException\n     */\n    PageResult<DataSource> queryPageWithPermission(DataSourcePageQueryParam param, DataSourceSelector selector);\n\n    /**\n     * Query data source by ID list\n     *\n     * @param ids\n     * @return\n     * @deprecated Use {@link #listQuery(List, DataSourceSelector)}\n     */\n    ListResult<DataSource> queryByIds(List<Long> ids);\n\n    /**\n     * Query data source by ID list\n     *\n     * @param idList\n     * @return\n     */\n    ListResult<DataSource> listQuery(List<Long> idList, DataSourceSelector selector);\n\n    /**\n     * Data source connection test\n     *\n     * @param param\n     * @return\n     */\n    ActionResult preConnect(DataSourcePreConnectParam param);\n\n    /**\n     * Connect to data source\n     *\n     * @param id\n     * @return\n     */\n    ListResult<Database> connect(Long id);\n\n    /**\n     * Close data source connection\n     *\n     * @param id\n     * @return\n     */\n    ActionResult close(Long id);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/DatabaseService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseQueryAllParam;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\n\nimport java.sql.SQLException;\n\n/**\n * Data source management services\n *\n * @author moji\n * @version DataSourceCoreService.java, v 0.1 September 23, 2022 15:22 moji Exp $\n * @date 2022/09/23\n */\npublic interface DatabaseService {\n\n    /**\n     * Query all databases under the data source\n     *\n     * @param param\n     * @return\n     */\n    ListResult<Database> queryAll(DatabaseQueryAllParam param);\n\n    /**\n     * Query the schema under a database\n     * @param param\n     * @return\n     */\n    ListResult<Schema> querySchema(SchemaQueryParam param);\n\n    /**\n     * query Database and Schema\n     * @param param\n     * @return\n     */\n    DataResult<MetaSchema> queryDatabaseSchema(MetaDataQueryParam param);\n\n\n\n    /**\n     * Delete database\n     *\n     * @param param\n     * @return\n     */\n    ActionResult deleteDatabase(DatabaseCreateParam param);\n\n    /**\n     * create database\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Sql> createDatabase(Database param);\n\n    /**\n     * Modify database\n     *\n     * @return\n     */\n    ActionResult modifyDatabase( DatabaseCreateParam param) ;\n\n    /**\n     * Delete schema\n     *\n     * @param param\n     * @return\n     */\n    ActionResult deleteSchema(SchemaOperationParam param) ;\n\n    /**\n     * Create schema\n     *\n     * @param schema\n     * @return\n     */\n    DataResult<Sql> createSchema(Schema schema);\n\n    /**\n     * Modify schema\n     *\n     * @param request\n     * @return\n     */\n    ActionResult modifySchema( SchemaOperationParam request);\n\n    /**\n     * Export database\n     *\n     * @param param\n     * @return\n     */\n    String exportDatabase(DatabaseExportParam param) throws SQLException;\n\n    /**\n     * Query the user under a database\n     *\n     * @return User list\n     */\n    ListResult<String> getUsernameList();\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/DlTemplateService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.param.DlCountParam;\nimport ai.chat2db.server.domain.api.param.DlExecuteParam;\nimport ai.chat2db.server.domain.api.param.OrderByParam;\nimport ai.chat2db.server.domain.api.param.UpdateSelectResultParam;\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.domain.api.param.GroupByParam;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\n\n/**\n * Data source management services\n *\n * @author moji\n * @version DataSourceCoreService.java, v 0.1 September 23, 2022 15:22 moji Exp $\n * @date 2022/09/23\n */\npublic interface DlTemplateService {\n\n    /**\n     * data source execution dl\n     *\n     * @param param\n     * @return\n     */\n    ListResult<ExecuteResult> execute(DlExecuteParam param);\n\n\n    /**\n     *\n     * @param param\n     * @return\n     */\n    ListResult<ExecuteResult> executeSelectTable(DlExecuteParam param);\n\n\n    /**\n     * Data source execution update\n     *\n     * @param param\n     * @return\n     */\n    DataResult<ExecuteResult> executeUpdate(DlExecuteParam param);\n\n    /**\n     * Execute statistics sql\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> count(DlCountParam param);\n\n    /**\n     * Update query results\n     * @param param\n     * @return\n     */\n    DataResult<String> updateSelectResult(UpdateSelectResultParam param);\n\n    /**\n     *\n     * @param param\n     * @return\n     */\n    DataResult<String> getGroupBySql(GroupByParam param);\n\n    /**\n     *\n     * @param param\n     * @return\n     */\n    DataResult<String> getOrderBySql(OrderByParam param);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/EnvironmentService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Environment;\nimport ai.chat2db.server.domain.api.param.EnvironmentPageQueryParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\n\n/**\n * environment\n *\n * @author Jiaju Zhuang\n */\npublic interface EnvironmentService {\n\n    /**\n     * List Query Data\n     *\n     * @param idList\n     * @return\n     */\n    ListResult<Environment> listQuery(List<Long> idList);\n\n    /**\n     * Paging Query Data\n     *\n     * @param param\n     * @return\n     */\n    PageResult<Environment> pageQuery(EnvironmentPageQueryParam param);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/FunctionService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Function;\nimport jakarta.validation.constraints.NotEmpty;\n\n/**\n * author jipengfei\n * date 2021/9/23 15:22\n */\npublic interface FunctionService {\n\n    /**\n     * Querying all functions under a schema.\n     *\n     * @param databaseName\n     * @return\n     */\n    ListResult<Function> functions(@NotEmpty String databaseName, String schemaName);\n\n    /**\n     * Querying function information.\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param functionName\n     * @return\n     */\n    DataResult<Function> detail(String databaseName, String schemaName, String functionName);\n\n    /**\n     * Delete function.\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param function\n     * @return\n     */\n    ActionResult delete(String databaseName, String schemaName, Function function);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/JdbcDriverService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.spi.config.DBConfig;\n\npublic interface JdbcDriverService {\n\n    /**\n     * Query the driver list of the current DB\n     *\n     * @param dbType\n     * @return\n     */\n    DataResult<DBConfig> getDrivers(String dbType);\n\n    /**\n     * Upload the driver\n     *\n     * @param dbType\n     * @param jdbcDriverClass\n     * @param jdbcDriver\n     * @return\n     */\n    ActionResult upload(String dbType, String jdbcDriverClass, String jdbcDriver);\n\n    /**\n     * Upload the driver\n     *\n     * @param dbType\n     * @return\n     */\n    ActionResult download(String dbType);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/OperationLogService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.param.operation.OperationLogPageQueryParam;\nimport ai.chat2db.server.domain.api.model.OperationLog;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\n\n/**\n * User executes ddl\n *\n * @author moji\n * @version UserExecutedDdlCoreService.java, v 0.1 September 23, 2022 17:35 moji Exp $\n * @date 2022/09/23\n */\npublic interface OperationLogService {\n\n    /**\n     * Create ddl record executed by user\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> create(OperationLogCreateParam param);\n\n    /**\n     * Query the ddl records executed by the user\n     *\n     * @param param\n     * @return\n     */\n    PageResult<OperationLog> queryPage(OperationLogPageQueryParam param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/OperationService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.model.Operation;\nimport ai.chat2db.server.domain.api.param.operation.OperationPageQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationSavedParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationUpdateParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport jakarta.validation.constraints.NotNull;\n\n/**\n * user save ddl\n *\n * @author moji\n * @version UserSavedDdlCoreService.java, v 0.1 September 23, 2022 17:35 moji Exp $\n * @date 2022/09/23\n */\npublic interface OperationService {\n\n    /**\n     * Save user's ddl\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> createWithPermission(OperationSavedParam param);\n\n    /**\n     * Update user's ddl\n     *\n     * @param param\n     * @return\n     */\n    ActionResult updateWithPermission(OperationUpdateParam param);\n\n    /**\n     * Query based on id\n     *\n     * @param id\n     * @return\n     */\n    DataResult<Operation> find(@NotNull Long id);\n\n    /**\n     * Query based on id\n     *\n     * @param id\n     * @return\n     */\n    DataResult<Operation> queryExistent(@NotNull Long id);\n    /**\n     * Query a piece of data\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Operation> queryExistent(@NotNull OperationQueryParam param);\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    ActionResult deleteWithPermission(@NotNull Long id);\n\n    /**\n     * Query the ddl records executed by the user\n     *\n     * @param param\n     * @return\n     */\n    PageResult<Operation> queryPage(OperationPageQueryParam param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/PinService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.param.PinTableParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\n\nimport java.util.List;\n\npublic interface PinService {\n\n    /**\n     * User pin table\n     * @param param\n     * @return\n     */\n    ActionResult pinTable(PinTableParam param);\n\n\n    /**\n     * Delete pin table\n     * @param param\n     * @return\n     */\n    ActionResult deletePinTable(PinTableParam param);\n\n\n    /**\n     * Query user pin tables\n     * @param param\n     * @return\n     */\n    ListResult<String> queryPinTables(PinTableParam param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/ProcedureService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Procedure;\nimport jakarta.validation.constraints.NotEmpty;\n\nimport java.sql.SQLException;\n\npublic interface ProcedureService {\n\n    /**\n     * Querying all procedures under a schema.\n     *\n     * @param databaseName\n     * @return\n     */\n    ListResult<Procedure> procedures(@NotEmpty String databaseName, String schemaName);\n\n    /**\n     * Querying procedure information.\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param procedureName\n     * @return\n     */\n    DataResult<Procedure> detail(String databaseName, String schemaName, String procedureName);\n\n    /**\n     * Update procedure.\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param procedure\n     * @return\n     */\n    ActionResult update(String databaseName, String schemaName, Procedure procedure) throws SQLException;\n\n    /**\n     * Delete procedure.\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param procedure\n     * @return\n     */\n    ActionResult delete(String databaseName, String schemaName, Procedure procedure);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/SequenceService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.param.DropParam;\nimport ai.chat2db.server.domain.api.param.SequencePageQueryParam;\nimport ai.chat2db.server.domain.api.param.SequenceQueryParam;\nimport ai.chat2db.server.domain.api.param.ShowCreateSequenceParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Sequence;\nimport ai.chat2db.spi.model.SimpleSequence;\nimport ai.chat2db.spi.model.Sql;\n\n/**\n * Sequence source management services\n *\n * @author Sylphy\n */\npublic interface SequenceService {\n    DataResult<String> showCreateSequence(ShowCreateSequenceParam request);\n\n    ListResult<SimpleSequence> pageQuery(SequencePageQueryParam request);\n\n    ListResult<Sql> buildSql(Sequence oldSequence, Sequence newSequence);\n\n    ActionResult drop(DropParam dropParam);\n\n    DataResult<Sequence> query(SequenceQueryParam queryParam);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/TableService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\n\n/**\n * Data source management services\n *\n * @author moji\n * @version DataSourceCoreService.java, v 0.1 September 23, 2022 15:22 moji Exp $\n * @date 2022/09/23\n */\npublic interface TableService {\n\n    /**\n     * Query table information\n     *\n     * @param param\n     * @return\n     */\n    DataResult<String> showCreateTable(ShowCreateTableParam param);\n\n    /**\n     * Delete table\n     *\n     * @param param\n     * @return\n     */\n    ActionResult drop(DropParam param);\n\n    /**\n     * Example of creating a table structure\n     *\n     * @param dbType\n     * @return\n     */\n    DataResult<String> createTableExample(String dbType);\n\n    /**\n     * Example of modifying table structure\n     *\n     * @param dbType\n     * @return\n     */\n    DataResult<String> alterTableExample(String dbType);\n\n    /**\n     * Query table information\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Table> query(TableQueryParam param, TableSelector selector);\n\n    /**\n     * build sql\n     *\n     * @param oldTable\n     * @param newTable\n     * @return\n     */\n    ListResult<Sql> buildSql(Table oldTable, Table newTable);\n\n    /**\n     * Pagination query table information\n     *\n     * @param param\n     * @return\n     */\n    PageResult<Table> pageQuery(TablePageQueryParam param, TableSelector selector);\n\n\n    /**\n     * Query table information\n     * @param param\n     * @return\n     */\n    ListResult<SimpleTable> queryTables(TablePageQueryParam param);\n\n    /**\n     * Fields included in the query table\n     *\n     * @param param\n     * @return\n     */\n    List<TableColumn> queryColumns(TableQueryParam param);\n\n    /**\n     * Query table index\n     *\n     * @param param\n     * @return\n     */\n    List<TableIndex> queryIndexes(TableQueryParam param);\n\n    /**\n     *\n     * @param param\n     *\n     * @return\n     */\n    List<Type> queryTypes(TypeQueryParam param);\n\n    /**\n     *\n     * @param param\n     * @return\n     */\n    TableMeta queryTableMeta(TypeQueryParam param);\n\n    /**\n     * save table vector\n     *\n     * @param param\n     * @return\n     */\n    ActionResult saveTableVector(TableVectorParam param);\n\n    /**\n     * check if table vector saved status\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Boolean> checkTableVector(TableVectorParam param);\n\n\n    /**\n     * Get dml template sql\n     * @param param table query param\n     * @return sql\n     */\n    DataResult<String> copyDmlSql(DmlSqlCopyParam param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/TaskService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.model.Task;\nimport ai.chat2db.server.domain.api.param.TaskCreateParam;\nimport ai.chat2db.server.domain.api.param.TaskPageParam;\nimport ai.chat2db.server.domain.api.param.TaskUpdateParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\n\npublic interface TaskService {\n\n    /**\n     * create task\n     *\n     * @param param task param\n     * @return task id\n     */\n    DataResult<Long> create(TaskCreateParam param);\n\n    /**\n     * update task status\n     *\n     * @param param task param\n     * @return action result\n     */\n    ActionResult updateStatus(TaskUpdateParam param);\n\n\n    /**\n     * get task list\n     *\n     * @param param task id\n     * @return task\n     */\n    PageResult<Task> page(TaskPageParam param);\n\n    /**\n     * get task\n     *\n     * @param id task id\n     * @return task\n     */\n    DataResult<Task> get(Long id);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/TeamService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Team;\nimport ai.chat2db.server.domain.api.param.team.TeamCreateParam;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.TeamSelector;\nimport ai.chat2db.server.domain.api.param.team.TeamUpdateParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport jakarta.validation.constraints.NotNull;\n\n/**\n * team\n *\n * @author Jiaju Zhuang\n */\npublic interface TeamService {\n\n    /**\n     * Pagination query\n     *\n     * @param param\n     * @param selector\n     * @return\n     */\n    PageResult<Team> pageQuery(TeamPageQueryParam param, TeamSelector selector);\n\n    /**\n     * List Query Data\n     *\n     * @param idList\n     * @return\n     */\n    ListResult<Team> listQuery(List<Long> idList);\n\n    /**\n     * Create\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> create(TeamCreateParam param);\n\n    /**\n     * update\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> update(TeamUpdateParam param);\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    ActionResult delete(@NotNull Long id);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/TeamUserService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.model.TeamUser;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserComprehensivePageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserCreatParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserSelector;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport jakarta.validation.constraints.NotNull;\n\n/**\n * team user\n *\n * @author Jiaju Zhuang\n */\npublic interface TeamUserService {\n\n    /**\n     * Comprehensive Paging Query Data\n     *\n     * @param param\n     * @param selector\n     * @return\n     */\n    PageResult<TeamUser> pageQuery(TeamUserPageQueryParam param, TeamUserSelector selector);\n\n    /**\n     * Comprehensive Paging Query Data\n     *\n     * @param param\n     * @param selector\n     * @return\n     */\n    PageResult<TeamUser> comprehensivePageQuery(TeamUserComprehensivePageQueryParam param, TeamUserSelector selector);\n\n    /**\n     * Create\n     *\n     * @param param\n     * @return\n     */\n    DataResult<Long> create(TeamUserCreatParam param);\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    ActionResult delete(@NotNull Long id);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/TriggerService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Trigger;\nimport jakarta.validation.constraints.NotEmpty;\n\npublic interface TriggerService {\n\n    /**\n     * Querying all triggers under a schema.\n     *\n     * @param databaseName\n     * @return\n     */\n    ListResult<Trigger> triggers(@NotEmpty String databaseName, String schemaName);\n\n    /**\n     * Querying trigger information.\n     * @param databaseName\n     * @param schemaName\n     * @param triggerName\n     * @return\n     */\n    DataResult<Trigger> detail(String databaseName, String schemaName, String triggerName);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/UserService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.param.user.UserCreateParam;\nimport ai.chat2db.server.domain.api.param.user.UserSelector;\nimport ai.chat2db.server.domain.api.param.user.UserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.user.UserUpdateParam;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\n\n/**\n * User service\n *\n * @author Jiaju Zhuang\n */\npublic interface UserService {\n\n    /**\n     * Query user information\n     *\n     * @param id\n     * @return\n     */\n    DataResult<User> query(Long id);\n\n    /**\n     * gen\n     * @param userName\n     * @return\n     */\n    DataResult<User> query(String userName);\n\n    /**\n     * List Query Data\n     *\n     * @param idList\n     * @return\n     */\n    ListResult<User> listQuery(List<Long> idList);\n\n    /**\n     * Query user information\n     *\n     * @param param\n     * @return\n     */\n    PageResult<User> pageQuery(UserPageQueryParam param, UserSelector selector);\n\n    /**\n     * Update user information\n     * @param user\n     * @return\n     */\n    DataResult<Long> update(UserUpdateParam user);\n\n    /**\n     * delete users\n     * @param id\n     * @return\n     */\n   ActionResult delete(Long id);\n\n    /**\n     * Create a user\n     * @param user\n     * @return\n     */\n    DataResult<Long> create(UserCreateParam user);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/ViewService.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Table;\nimport jakarta.validation.constraints.NotEmpty;\n\n/**\n * author jipengfei\n * date 2021/9/23 15:22\n */\npublic interface ViewService {\n\n    /**\n     * Querying all views under a schema.\n     *\n     * @param databaseName\n     * @return\n     */\n    ListResult<Table> views(@NotEmpty String databaseName, String schemaName);\n\n\n    /**\n     * Querying the details of a view.\n     *\n     * @param databaseName\n     * @return\n     */\n    DataResult<Table> detail(@NotEmpty String databaseName, String schemaName,String tableName);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/WebhookSender.java",
    "content": "package ai.chat2db.server.domain.api.service;\n\nimport ai.chat2db.server.domain.api.param.message.MessageCreateParam;\n\n/**\n * @author Juechen\n * @version : WebhookSender.java\n */\npublic interface WebhookSender {\n\n    void sendMessage(MessageCreateParam param);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-domain</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>chat2db-server-domain-core</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-repository</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-tools-common</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.ehcache</groupId>\n            <artifactId>ehcache</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>javax.cache</groupId>\n            <artifactId>cache-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-mysql</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-clickhouse</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-db2</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-dm</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-h2</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-hive</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-kingbase</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-mariadb</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-mongodb</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-oceanbase</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-oracle</artifactId>\n            <version>${revision}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-postgresql</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-presto</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-sqlite</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-sqlserver</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-timeplus</artifactId>\n            <version>${revision}</version>\n        </dependency>\n        <dependency>\n            <groupId>commons-codec</groupId>\n            <artifactId>commons-codec</artifactId>\n            <version>1.11</version>\n        </dependency>\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>okhttp</artifactId>\n            <version>4.9.1</version>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/cache/CacheKey.java",
    "content": "package ai.chat2db.server.domain.core.cache;\n\nimport org.springframework.util.StringUtils;\n\npublic class CacheKey {\n\n    public static String getLoginUserKey(Long userId) {\n        return \"login_user_\" + userId;\n    }\n\n    public static String getDataSourceKey(Long dataSourceId) {\n        return \"schemas_datasourceId_\" + dataSourceId;\n    }\n\n    public static String getDataBasesKey(Long dataSourceId) {\n        return \"databases_datasourceId_\" + dataSourceId;\n    }\n\n    public static String getSchemasKey(Long dataSourceId, String databaseName) {\n\n        return \"databases_datasourceId_\" + dataSourceId + \"_databaseName_\" + databaseName;\n    }\n\n    public static String getTableKey(Long dataSourceId, String databaseName, String schemaName) {\n        StringBuffer stringBuffer = new StringBuffer(\"tables_dataSourceId_\" + dataSourceId);\n        if (!StringUtils.isEmpty(databaseName)) {\n            stringBuffer.append(\"_databaseName_\" + databaseName);\n        }\n        if (!StringUtils.isEmpty(schemaName)) {\n            stringBuffer.append(\"_schemaName_\" + schemaName);\n        }\n        return stringBuffer.toString();\n    }\n\n    public static String getColumnKey(Long dataSourceId, String databaseName, String schemaName,String tableName) {\n        StringBuffer stringBuffer = new StringBuffer(\"columns_dataSourceId_\" + dataSourceId);\n        if (!StringUtils.isEmpty(databaseName)) {\n            stringBuffer.append(\"_databaseName_\" + databaseName);\n        }\n        if (!StringUtils.isEmpty(schemaName)) {\n            stringBuffer.append(\"_schemaName_\" + schemaName);\n        }\n        stringBuffer.append(\"_tableName_\"+tableName);\n        return stringBuffer.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/cache/CacheManage.java",
    "content": "package ai.chat2db.server.domain.core.cache;\n\nimport java.io.File;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport com.alibaba.fastjson2.JSON;\n\nimport org.ehcache.Cache;\nimport org.ehcache.CacheManager;\nimport org.ehcache.config.builders.CacheConfigurationBuilder;\nimport org.ehcache.config.builders.CacheManagerBuilder;\nimport org.ehcache.config.builders.ResourcePoolsBuilder;\nimport org.ehcache.config.units.EntryUnit;\nimport org.ehcache.config.units.MemoryUnit;\nimport org.springframework.util.StringUtils;\n\npublic class CacheManage {\n    private static final String PATH = System.getProperty(\"user.home\") + File.separator + \".chat2db\"\n        + File.separator\n        + \"cache\" + File.separator + \"chat2db-ehcache-data_\" +System.getProperty(\"spring.profiles.active\");\n\n    private static final String CACHE = \"meta_cache\";\n    private static CacheManager cacheManager;\n\n    static {\n        cacheManager = CacheManagerBuilder.newCacheManagerBuilder()\n            .with(CacheManagerBuilder.persistence(PATH)) // Make sure this path exists and has write permissions\n            .withCache(CACHE, CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class,\n                ResourcePoolsBuilder.newResourcePoolsBuilder()\n                    .heap(1000, EntryUnit.ENTRIES)\n                    .disk(20, MemoryUnit.GB, true))) // Disk persistence is set to true\n            .build(true);\n    }\n\n    public static <T> T get(String key, Class<T> clazz) {\n        Cache<String, String> myCache = cacheManager.getCache(CACHE, String.class, String.class);\n        String value = myCache.get(key);\n        if (!StringUtils.isEmpty(value)) {\n            return JSON.parseObject(value, clazz);\n        }\n        return null;\n    }\n\n    public static <T> List<T> getList(String key, Class<T> clazz) {\n        Cache<String, String> myCache = cacheManager.getCache(CACHE, String.class, String.class);\n        String value = myCache.get(key);\n        if (!StringUtils.isEmpty(value)) {\n            return JSON.parseArray(value, clazz);\n        }\n        return null;\n    }\n\n    public static <T> T get(String key, Class<T> clazz, Function<Object, Boolean> refresh,\n        Function<Object, T> function) {\n        T t;\n        if (refresh.apply(key)) {\n            t = function.apply(key);\n            put(key, t);\n        } else {\n            t = get(key, clazz);\n            if (t == null) {\n                t = function.apply(key);\n                put(key, t);\n            }\n        }\n        return t;\n    }\n\n    public static <T> List<T> getList(String key, Class<T> clazz, Function<Object, Boolean> refresh,\n        Function<Object, List<T>> function) {\n        List<T> t;\n        if (refresh.apply(key)) {\n            t = function.apply(key);\n            put(key, t);\n        } else {\n            t = getList(key, clazz);\n            if (t == null) {\n                t = function.apply(key);\n                put(key, t);\n            }\n        }\n        return t;\n    }\n\n    public static void put(String s, Object value) {\n        Cache<String, String> myCache = cacheManager.getCache(CACHE, String.class, String.class);\n        myCache.put(s, JSON.toJSONString(value));\n    }\n\n    public static void close() {\n        cacheManager.close();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/cache/MemoryCacheManage.java",
    "content": "package ai.chat2db.server.domain.core.cache;\n\nimport java.io.Serializable;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Supplier;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.Weigher;\nimport org.apache.commons.lang3.SerializationUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.cache.support.NullValue;\n\n/**\n * It will only be stored in memory\n *\n * @author Jiaju Zhuang\n */\npublic class MemoryCacheManage {\n\n    private static final byte[] NULL_BYTES = SerializationUtils.serialize((NullValue)NullValue.INSTANCE);\n    private static final String SYNCHRONIZED_PREFIX = \"MemoryCache:\";\n\n    private static final Cache<String, byte[]> CACHE = CacheBuilder.newBuilder()\n        // 5M\n        .maximumWeight(5 * 1024 * 1024)\n        .weigher((Weigher<String, byte[]>)(key, value) -> value.length)\n        .expireAfterAccess(10, TimeUnit.MINUTES)\n        .build();\n\n    /**\n     * Retrieve a value from the cache, and if not, query it\n     * The timeout is fixed at 10 minutes\n     *\n     * @param key\n     * @param queryData\n     * @param <T>\n     * @return\n     */\n    public static <T extends Serializable> T computeIfAbsent(String key, Supplier<T> queryData) {\n        if (key == null) {\n            return null;\n        }\n        T data = get(key);\n        if (data != null) {\n            return data;\n        }\n        String lockKey = SYNCHRONIZED_PREFIX + key;\n        synchronized (lockKey.intern()) {\n            data = get(key);\n            if (data != null) {\n                return data;\n            }\n\n            T value = queryData.get();\n            put(key, value);\n            return value;\n        }\n    }\n\n    /**\n     * Get a data from cache\n     *\n     * @param key\n     * @param <T>\n     * @return\n     */\n    public static <T> T get(String key) {\n        if (StringUtils.isBlank(key)) {\n            return null;\n        }\n        byte[] bytes = CACHE.getIfPresent(key);\n        if (bytes == null) {\n            return null;\n        }\n        T data = SerializationUtils.deserialize(bytes);\n        if (NullValue.INSTANCE.equals(data)) {\n            return null;\n        }\n        return data;\n    }\n\n    /**\n     * Put a data from cache\n     * The timeout is fixed at 10 minutes\n     *\n     * @param key\n     * @param value\n     */\n    public static void put(String key, Serializable value) {\n        if (key == null) {\n            return;\n        }\n        if (value == null) {\n            CACHE.put(key, NULL_BYTES);\n        } else {\n            CACHE.put(key, SerializationUtils.serialize(value));\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/ChartConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Chart;\nimport ai.chat2db.server.domain.api.chart.ChartCreateParam;\nimport ai.chat2db.server.domain.api.chart.ChartUpdateParam;\nimport ai.chat2db.server.domain.repository.entity.ChartDO;\n\nimport org.mapstruct.Mapper;\n\n/**\n * @author moji\n * @version ChartConverter.java, v 0.1 June 9, 2023 17:13 moji Exp $\n * @date 2023/06/09\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class ChartConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    public abstract ChartDO param2do(ChartCreateParam param);\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    public abstract ChartDO updateParam2do(ChartUpdateParam param);\n\n    /**\n     * Model conversion\n     *\n     * @param chartDO\n     * @return\n     */\n    public abstract Chart do2model(ChartDO chartDO);\n\n    /**\n     * Model conversion\n     *\n     * @param chartDOS\n     * @return\n     */\n    public abstract List<Chart> do2model(List<ChartDO> chartDOS);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/CommandConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport ai.chat2db.server.domain.api.param.DlExecuteParam;\nimport ai.chat2db.spi.model.Command;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n@Mapper(componentModel = \"spring\")\npublic abstract class CommandConverter {\n\n    @Mappings({\n            @Mapping(target = \"script\", source = \"sql\")\n    })\n    public abstract Command param2model(DlExecuteParam param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/ConfigConverter.java",
    "content": "\npackage ai.chat2db.server.domain.core.converter;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.SystemConfigParam;\nimport ai.chat2db.server.domain.repository.entity.SystemConfigDO;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.mapstruct.Mapper;\n\n/**\n * @author jipengfei\n * @version : ConfigConverter.java\n */\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class ConfigConverter {\n\n    public abstract SystemConfigDO param2do(SystemConfigParam param);\n\n    public abstract Config do2model(SystemConfigDO systemConfigDO);\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/DashboardConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Dashboard;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardCreateParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardUpdateParam;\nimport ai.chat2db.server.domain.repository.entity.DashboardDO;\n\nimport org.mapstruct.Mapper;\n\n/**\n * @author moji\n * @version ChartConverter.java, v 0.1 June 9, 2023 17:13 moji Exp $\n * @date 2023/06/09\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class DashboardConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    public abstract DashboardDO param2do(DashboardCreateParam param);\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    public abstract DashboardDO updateParam2do(DashboardUpdateParam param);\n\n    /**\n     * Model conversion\n     *\n     * @param chartDO\n     * @return\n     */\n    public abstract Dashboard do2model(DashboardDO chartDO);\n\n    /**\n     * Model conversion\n     *\n     * @param chartDOS\n     * @return\n     */\n    public abstract List<Dashboard> do2model(List<DashboardDO> chartDOS);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/DataSourceAccessConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.DataSourceAccess;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessCreatParam;\nimport ai.chat2db.server.domain.repository.entity.DataSourceAccessDO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class DataSourceAccessConverter {\n\n    /**\n     * convert\n     *\n     * @param data\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"accessObject.id\", source = \"accessObjectId\"),\n        @Mapping(target = \"accessObject.type\", source = \"accessObjectType\"),\n        @Mapping(target = \"dataSource.id\", source = \"dataSourceId\"),\n    })\n    public abstract DataSourceAccess do2dto(DataSourceAccessDO data);\n\n    /**\n     * convert\n     *\n     * @param dataSourceId\n     * @param accessObjectId\n     * @param accessObjectType\n     * @param userId\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"createUserId\", source = \"userId\"),\n        @Mapping(target = \"modifiedUserId\", source = \"userId\"),\n    })\n    public abstract DataSourceAccessDO param2do(Long dataSourceId, Long accessObjectId, String accessObjectType,\n        Long userId);\n\n    /**\n     * convert\n     *\n     * @param param\n\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"createUserId\", source = \"userId\"),\n        @Mapping(target = \"modifiedUserId\", source = \"userId\"),\n    })\n    public abstract DataSourceAccessDO param2do(DataSourceAccessCreatParam param, Long userId);\n\n\n    /**\n     * convert\n     *\n     * @param list\n     * @return\n     */\n    public abstract List<DataSourceAccess> do2dto(List<DataSourceAccessDO> list);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/DataSourceConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.param.ConsoleCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceTestParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceUpdateParam;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.core.util.DesUtil;\nimport ai.chat2db.server.domain.repository.entity.DataSourceDO;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.MappingTarget;\nimport org.mapstruct.Mappings;\nimport org.springframework.context.annotation.Lazy;\n\n/**\n * @author moji\n * @version DataSourceCoreConverter.java, v 0.1 September 23, 2022 15:53 moji Exp $\n * @date 2022/09/23\n */\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class DataSourceConverter {\n\n    @Resource\n    @Lazy\n    private DataSourceService dataSourceService;\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n\n    @Mapping(target = \"password\", expression = \"java(encryptString(param))\")\n    @Mapping(target = \"ssh\",\n        expression = \"java(param.getSsh()==null?null: com.alibaba.fastjson2.JSON.toJSONString(param.getSsh()))\")\n    @Mapping(target = \"ssl\",\n        expression = \"java(param.getSsl()==null?null: com.alibaba.fastjson2.JSON.toJSONString(param.getSsl()))\")\n    @Mapping(target = \"extendInfo\",\n        expression = \"java(param.getExtendInfo()==null?null: com.alibaba.fastjson2.JSON.toJSONString(param\"\n            + \".getExtendInfo()))\")\n    @Mapping(target = \"driverConfig\",\n        expression = \"java(param.getDriverConfig()==null?null: com.alibaba.fastjson2.JSON.toJSONString(param\"\n            + \".getDriverConfig()))\")\n    public abstract DataSourceDO param2do(DataSourceCreateParam param);\n\n    /**\n     * encrypt\n     *\n     * @param param\n     * @return\n     */\n    protected String encryptString(DataSourceCreateParam param) {\n        String encryptStr = param.getPassword();\n        if (StringUtils.isNotBlank(encryptStr)) {\n            try {\n                DesUtil desUtil = new DesUtil(DesUtil.DES_KEY);\n                encryptStr = desUtil.encrypt(param.getPassword(), \"CBC\");\n            } catch (Exception exception) {\n                // do nothing\n                log.error(\"encrypt error\", exception);\n            }\n        }\n        return encryptStr;\n    }\n\n    /**\n     * encrypt\n     *\n     * @param param\n     * @return\n     */\n        protected String encryptString(DataSourceUpdateParam param) {\n            String encryptStr = param.getPassword();\n            try {\n                DesUtil desUtil = new DesUtil(DesUtil.DES_KEY);\n                encryptStr = desUtil.encrypt(param.getPassword(), \"CBC\");\n            } catch (Exception exception) {\n                // do nothing\n                log.error(\"encrypt error\", exception);\n            }\n            return encryptStr;\n        }\n\n    /**\n     * decrypt\n     *\n     * @param param\n     * @return\n     */\n    protected String decryptString(DataSourceDO param) {\n        String decryptStr = param.getPassword();\n        try {\n            DesUtil desUtil = new DesUtil(DesUtil.DES_KEY);\n            decryptStr = desUtil.decrypt(param.getPassword(), \"CBC\");\n        } catch (Exception exception) {\n            // do nothing\n            log.error(\"encrypt error\", exception);\n        }\n        return decryptStr;\n    }\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"password\", expression = \"java(encryptString(param))\"),\n        @Mapping(target = \"ssh\",\n            expression = \"java(param.getSsh()==null?null: com.alibaba.fastjson2.JSON.toJSONString(param.getSsh()))\"),\n        @Mapping(target = \"ssl\",\n            expression = \"java(param.getSsl()==null?null: com.alibaba.fastjson2.JSON.toJSONString(param.getSsl()))\"),\n        @Mapping(target = \"extendInfo\",\n            expression = \"java(param.getExtendInfo()==null?null: com.alibaba.fastjson2.JSON.toJSONString(param\"\n                + \".getExtendInfo()))\"),\n        @Mapping(target = \"driverConfig\",\n            expression = \"java(param.getDriverConfig()==null?null: com.alibaba.fastjson2.JSON.toJSONString(param\"\n                + \".getDriverConfig()))\")\n    })\n    public abstract DataSourceDO param2do(DataSourceUpdateParam param);\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    public abstract ConsoleCreateParam param2consoleParam(ConsoleConnectParam param);\n\n    /**\n     * Parameter conversion\n     *\n     * @param dataSourcePreConnectParam\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"type\", target = \"dbType\"),\n        @Mapping(source = \"user\", target = \"username\")\n    })\n    public abstract DataSourceTestParam param2param(\n        DataSourcePreConnectParam dataSourcePreConnectParam);\n\n    /**\n     * Model conversion\n     *\n     * @param dataSourceDO\n     * @return\n     */\n\n    @Mapping(target = \"password\", expression = \"java(decryptString(dataSourceDO))\")\n    @Mapping(target = \"ssh\",\n        expression = \"java(com.alibaba.fastjson2.JSON.parseObject(dataSourceDO.getSsh(),ai.chat2db.spi\"\n            + \".model.SSHInfo.class))\")\n    @Mapping(target = \"ssl\",\n        expression =\n            \"java(com.alibaba.fastjson2.JSON.parseObject(dataSourceDO.getSsl(),ai.chat2db.spi\"\n                + \".model.SSLInfo\"\n                + \".class))\")\n    @Mapping(target = \"driverConfig\",\n        expression =\n            \"java(com.alibaba.fastjson2.JSON.parseObject(dataSourceDO.getDriverConfig(),ai.chat2db.spi.config\"\n                + \".DriverConfig\"\n                + \".class))\")\n    @Mapping(target = \"extendInfo\",\n        expression = \"java(com.alibaba.fastjson2.JSON.parseArray(dataSourceDO.getExtendInfo(),ai.chat2db.spi.model\"\n            + \".KeyValue.class))\")\n    @Mapping(target = \"environment.id\", source = \"environmentId\")\n    public abstract DataSource do2dto(DataSourceDO dataSourceDO);\n\n    /**\n     * Model conversion\n     *\n     * @param dataSourceDOList\n     * @return\n     */\n    public abstract List<DataSource> do2dto(List<DataSourceDO> dataSourceDOList);\n\n    /**\n     * Fill in detailed information\n     *\n     * @param list\n     */\n    public void fillDetail(List<DataSource> list) {\n        fillDetail(list, null);\n    }\n\n    /**\n     * Fill in detailed information\n     *\n     * @param list\n     */\n    public void fillDetail(List<DataSource> list, DataSourceSelector selector) {\n        if (CollectionUtils.isEmpty(list)) {\n            return;\n        }\n        List<Long> idList = EasyCollectionUtils.toList(list, DataSource::getId);\n        List<DataSource> queryList = dataSourceService.listQuery(idList, selector).getData();\n        Map<Long, DataSource> queryMap = EasyCollectionUtils.toIdentityMap(queryList, DataSource::getId);\n        for (DataSource data : list) {\n            if (data == null || data.getId() == null) {\n                continue;\n            }\n            DataSource query = queryMap.get(data.getId());\n            add(data, query);\n        }\n    }\n\n    @Mappings({\n        @Mapping(target = \"id\", ignore = true),\n    })\n    public abstract void add(@MappingTarget DataSource target, DataSource source);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/DriverConfigConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport ai.chat2db.server.domain.repository.entity.JdbcDriverDO;\nimport ai.chat2db.spi.config.DriverConfig;\nimport lombok.extern.slf4j.Slf4j;\nimport org.mapstruct.Mapper;\n\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class DriverConfigConverter {\n    public abstract DriverConfig do2Config(JdbcDriverDO driverDO);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/EnvironmentConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport ai.chat2db.server.domain.api.model.Environment;\nimport ai.chat2db.server.domain.api.service.EnvironmentService;\nimport ai.chat2db.server.domain.repository.entity.EnvironmentDO;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.MappingTarget;\nimport org.mapstruct.Mappings;\nimport org.springframework.context.annotation.Lazy;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class EnvironmentConverter {\n\n    @Resource\n    @Lazy\n    private EnvironmentService environmentService;\n\n    /**\n     * convert\n     *\n     * @param list\n     * @return\n     */\n    public abstract List<Environment> do2dto(List<EnvironmentDO> list);\n\n    /**\n     * Fill in detailed information\n     *\n     * @param list\n     */\n    public void fillDetail(List<Environment> list) {\n        if (CollectionUtils.isEmpty(list)) {\n            return;\n        }\n        List<Long> idList = EasyCollectionUtils.toList(list, Environment::getId);\n        List<Environment> queryList = environmentService.listQuery(idList).getData();\n        Map<Long, Environment> queryMap = EasyCollectionUtils.toIdentityMap(queryList, Environment::getId);\n        for (Environment data : list) {\n            if (data == null || data.getId() == null) {\n                continue;\n            }\n            Environment query = queryMap.get(data.getId());\n            add(data, query);\n        }\n    }\n\n    @Mappings({\n        @Mapping(target = \"id\", ignore = true),\n    })\n    public abstract void add(@MappingTarget Environment target, Environment source);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/OperationConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Operation;\nimport ai.chat2db.server.domain.api.param.operation.OperationSavedParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationUpdateParam;\nimport ai.chat2db.server.domain.repository.entity.OperationSavedDO;\n\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * @author moji\n * @version UserSavedDdlCoreConverter.java, v 0.1 September 25, 2022 15:50 moji Exp $\n * @date 2022/09/25\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class OperationConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"schemaName\", target = \"dbSchemaName\")\n    })\n    public abstract OperationSavedDO param2do(OperationSavedParam param);\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"schemaName\", target = \"dbSchemaName\")\n    })\n    public abstract OperationSavedDO param2do(OperationUpdateParam param);\n\n    /**\n     * Model conversion\n     *\n     * @param userSavedDdlDO\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"dbSchemaName\", target = \"schemaName\")\n    })\n    public abstract Operation do2dto(OperationSavedDO userSavedDdlDO);\n\n    /**\n     * Model conversion\n     *\n     * @param userSavedDdlDOS\n     * @return\n     */\n    public abstract List<Operation> do2dto(List<OperationSavedDO> userSavedDdlDOS);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/OperationLogConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.OperationLog;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;\nimport ai.chat2db.server.domain.repository.entity.OperationLogDO;\n\nimport org.mapstruct.Mapper;\n\n/**\n * @author moji\n * @version UserExecutedDdlCoreConverter.java, v 0.1 September 25, 2022 14:08 moji Exp $\n * @date 2022/09/25\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class OperationLogConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param param\n     * @return\n     */\n    public abstract OperationLogDO param2do(OperationLogCreateParam param);\n\n    /**\n     * Model conversion\n     *\n     * @param userExecutedDdlDO\n     * @return\n     */\n    public abstract OperationLog do2dto(OperationLogDO userExecutedDdlDO);\n\n    /**\n     * Model conversion\n     *\n     * @param userExecutedDdlDOS\n     * @return\n     */\n    public abstract List<OperationLog> do2dto(List<OperationLogDO> userExecutedDdlDOS);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/PinTableConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport ai.chat2db.server.domain.api.param.PinTableParam;\nimport ai.chat2db.server.domain.api.param.TablePageQueryParam;\nimport ai.chat2db.server.domain.repository.entity.PinTableDO;\nimport org.mapstruct.Mapper;\n\n@Mapper(componentModel = \"spring\")\npublic abstract class PinTableConverter {\n\n    /**\n     *\n     * @param param\n     * @return\n     */\n    public abstract PinTableDO param2do(PinTableParam param);\n\n\n\n    public abstract PinTableParam toPinTableParam (TablePageQueryParam param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/TableConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport ai.chat2db.server.domain.api.param.TableVectorParam;\nimport ai.chat2db.server.domain.repository.entity.TableVectorMappingDO;\nimport org.mapstruct.Mapper;\n\n@Mapper(componentModel = \"spring\")\npublic abstract class TableConverter {\n\n    /**\n     * TableVectorParam to TableVectorMappingDO\n     *\n     * @param param\n     * @return\n     */\n    public abstract TableVectorMappingDO toTableVectorMappingDO(TableVectorParam param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/TaskConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport ai.chat2db.server.domain.api.model.Task;\nimport ai.chat2db.server.domain.api.param.TaskCreateParam;\nimport ai.chat2db.server.domain.repository.entity.TaskDO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.mapstruct.Mapper;\n\nimport java.util.List;\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class TaskConverter {\n\n    public abstract TaskDO todo(TaskCreateParam param);\n\n\n    public abstract Task toModel(TaskDO param);\n\n\n    public abstract List<Task> toModel(List<TaskDO> param);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/TeamConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport ai.chat2db.server.domain.api.model.Team;\nimport ai.chat2db.server.domain.api.param.team.TeamCreateParam;\nimport ai.chat2db.server.domain.api.param.team.TeamUpdateParam;\nimport ai.chat2db.server.domain.api.service.TeamService;\nimport ai.chat2db.server.domain.repository.entity.TeamDO;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.MappingTarget;\nimport org.mapstruct.Mappings;\nimport org.springframework.context.annotation.Lazy;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class TeamConverter {\n\n    @Resource\n    @Lazy\n    private TeamService teamService;\n\n    /**\n     * convert\n     *\n     * @param list\n     * @return\n     */\n    public abstract List<Team> do2dto(List<TeamDO> list);\n\n    /**\n     * convert\n     *\n     * @param data\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"modifiedUser.id\", source = \"modifiedUserId\"),\n    })\n    public abstract Team do2dto(TeamDO data);\n\n    /**\n     * convert\n     *\n     * @param param\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"createUserId\", source = \"userId\"),\n        @Mapping(target = \"modifiedUserId\", source = \"userId\"),\n    })\n    public abstract TeamDO param2do(TeamCreateParam param, Long userId);\n\n    /**\n     * convert\n     *\n     * @param param\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"modifiedUserId\", source = \"userId\"),\n    })\n    public abstract TeamDO param2do(TeamUpdateParam param, Long userId);\n\n    /**\n     * Fill in detailed information\n     *\n     * @param list\n     */\n    public void fillDetail(List<Team> list) {\n        if (CollectionUtils.isEmpty(list)) {\n            return;\n        }\n        List<Long> idList = EasyCollectionUtils.toList(list, Team::getId);\n        List<Team> queryList = teamService.listQuery(idList).getData();\n        Map<Long, Team> queryMap = EasyCollectionUtils.toIdentityMap(queryList, Team::getId);\n        for (Team data : list) {\n            if (data == null || data.getId() == null) {\n                continue;\n            }\n            Team query = queryMap.get(data.getId());\n            add(data, query);\n        }\n    }\n\n    @Mappings({\n        @Mapping(target = \"id\", ignore = true),\n    })\n    public abstract void add(@MappingTarget Team target, Team source);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/TeamUserConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport ai.chat2db.server.domain.api.model.Environment;\nimport ai.chat2db.server.domain.api.model.TeamUser;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserCreatParam;\nimport ai.chat2db.server.domain.api.service.EnvironmentService;\nimport ai.chat2db.server.domain.repository.entity.TeamUserDO;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.MappingTarget;\nimport org.mapstruct.Mappings;\nimport org.springframework.context.annotation.Lazy;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class TeamUserConverter {\n\n    @Resource\n    @Lazy\n    private EnvironmentService environmentService;\n\n    /**\n     * convert\n     *\n     * @param data\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"team.id\", source = \"teamId\"),\n        @Mapping(target = \"user.id\", source = \"userId\"),\n    })\n    public abstract TeamUser do2dto(TeamUserDO data);\n\n    /**\n     * convert\n     *\n     * @param list\n     * @return\n     */\n    public abstract List<TeamUser> do2dto(List<TeamUserDO> list);\n\n    /**\n     * convert\n     *\n     * @param param\n\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"createUserId\", source = \"userId\"),\n        @Mapping(target = \"modifiedUserId\", source = \"userId\"),\n    })\n    public abstract TeamUserDO param2do(TeamUserCreatParam param, Long userId);\n\n\n    /**\n     * Fill in detailed information\n     *\n     * @param list\n     */\n    public void fillDetail(List<Environment> list) {\n        if (CollectionUtils.isEmpty(list)) {\n            return;\n        }\n        List<Long> idList = EasyCollectionUtils.toList(list, Environment::getId);\n        List<Environment> queryList = environmentService.listQuery(idList).getData();\n        Map<Long, Environment> queryMap = EasyCollectionUtils.toIdentityMap(queryList, Environment::getId);\n        for (Environment data : list) {\n            if (data == null || data.getId() == null) {\n                continue;\n            }\n            Environment query = queryMap.get(data.getId());\n            add(data, query);\n        }\n    }\n\n    @Mappings({\n        @Mapping(target = \"id\", ignore = true),\n    })\n    public abstract void add(@MappingTarget Environment target, Environment source);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/UserConverter.java",
    "content": "package ai.chat2db.server.domain.core.converter;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.param.user.UserCreateParam;\nimport ai.chat2db.server.domain.api.param.user.UserUpdateParam;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.domain.repository.entity.DbhubUserDO;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport jakarta.annotation.Resource;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.MappingTarget;\nimport org.mapstruct.Mappings;\nimport org.springframework.context.annotation.Lazy;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class UserConverter {\n\n    @Resource\n    @Lazy\n    private UserService userService;\n\n    /**\n     * Convert\n     *\n     * @param data\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"modifiedUser.id\", source = \"modifiedUserId\"),\n    })\n    public abstract User do2dto(DbhubUserDO data);\n\n    /**\n     * Convert\n     *\n     * @param datas\n     * @return\n     */\n    public abstract List<User> do2dto(List<DbhubUserDO> datas);\n\n    /**\n     *\n     * @param user\n     * @return\n     */\n    public abstract DbhubUserDO dto2do(User user);\n\n    /**\n     *\n     * @param user\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"createUserId\", source = \"userId\"),\n        @Mapping(target = \"modifiedUserId\", source = \"userId\"),\n    })\n    public abstract DbhubUserDO param2do(UserCreateParam user, Long userId);\n\n    /**\n     *\n     * @param user\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"modifiedUserId\", source = \"userId\"),\n    })\n    public abstract DbhubUserDO param2do(UserUpdateParam user, Long userId);\n\n    /**\n     * Fill in detailed information\n     *\n     * @param list\n     */\n    public void fillDetail(List<User> list) {\n        if (CollectionUtils.isEmpty(list)) {\n            return;\n        }\n        List<Long> idList = EasyCollectionUtils.toList(list, User::getId);\n        List<User> queryList = userService.listQuery(idList).getData();\n        Map<Long, User> queryMap = EasyCollectionUtils.toIdentityMap(queryList, User::getId);\n        for (User data : list) {\n            if (data == null || data.getId() == null) {\n                continue;\n            }\n            User query = queryMap.get(data.getId());\n            add(data, query);\n        }\n    }\n\n    @Mappings({\n        @Mapping(target = \"id\", ignore = true),\n    })\n    public abstract void add(@MappingTarget User target, User source);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/enums/ExternalNotificationTypeEnum.java",
    "content": "package ai.chat2db.server.domain.core.enums;\n\nimport ai.chat2db.server.domain.api.service.WebhookSender;\nimport ai.chat2db.server.domain.core.notification.DingTalkWebhookSender;\nimport ai.chat2db.server.domain.core.notification.LarkWebhookSender;\nimport ai.chat2db.server.domain.core.notification.WeComWebhookSender;\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * @author Juechen\n * @version : ExternalNotificationTypeEnum.java\n */\n@Getter\npublic enum ExternalNotificationTypeEnum implements BaseEnum<String> {\n\n    /**\n     * 企业微信\n     */\n    WECOM(\"WeCom\", WeComWebhookSender.class),\n\n    /**\n     * 钉钉\n     */\n    DINGTALK(\"DingTalk\", DingTalkWebhookSender.class),\n\n    /**\n     * 飞书\n     */\n    LARK(\"Lark\", LarkWebhookSender.class),\n\n    ;\n\n    final String description;\n\n    final Class<? extends WebhookSender> webhookSender;\n\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n    public static WebhookSender getWebhookSender(String platformType) {\n        String lowerCasePlatformType = platformType.toLowerCase();\n        switch (lowerCasePlatformType) {\n            case \"wecom\":\n                return new WeComWebhookSender();\n            case \"dingtalk\":\n                return new DingTalkWebhookSender();\n            case \"lark\":\n                return new LarkWebhookSender();\n            default:\n                return null;\n        }\n    }\n\n    /**\n     * Get enum by name\n     *\n     * @param name\n     * @return\n     */\n    public static ExternalNotificationTypeEnum getByName(String name) {\n        for (ExternalNotificationTypeEnum dbTypeEnum : ExternalNotificationTypeEnum.values()) {\n            if (dbTypeEnum.name().equalsIgnoreCase(name)) {\n                return dbTypeEnum;\n            }\n        }\n        return null;\n    }\n\n    ExternalNotificationTypeEnum(String description, Class<? extends WebhookSender> webhookSender) {\n        this.description = description;\n        this.webhookSender = webhookSender;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/ChartServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.chart.ChartCreateParam;\nimport ai.chat2db.server.domain.api.chart.ChartListQueryParam;\nimport ai.chat2db.server.domain.api.chart.ChartQueryParam;\nimport ai.chat2db.server.domain.api.chart.ChartUpdateParam;\nimport ai.chat2db.server.domain.api.model.Chart;\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.service.ChartService;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.core.converter.ChartConverter;\nimport ai.chat2db.server.domain.core.util.PermissionUtils;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.ChartDO;\nimport ai.chat2db.server.domain.repository.entity.DashboardChartRelationDO;\nimport ai.chat2db.server.domain.repository.mapper.ChartMapper;\nimport ai.chat2db.server.domain.repository.mapper.DashboardChartRelationMapper;\nimport ai.chat2db.server.tools.base.enums.YesOrNoEnum;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.exception.DataNotFoundException;\nimport ai.chat2db.server.tools.common.model.EasyLambdaQueryWrapper;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.google.common.collect.Lists;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * @author moji\n * @version ChartServiceImpl.java, v 0.1 June 9, 2023 16:06 moji Exp $\n * @date 2023/06/09\n */\n@Service\npublic class ChartServiceImpl implements ChartService {\n\n    @Autowired\n    private DataSourceService dataSourceService;\n\n\n    private DashboardChartRelationMapper getDashboardMapper() {\n        return Dbutils.getMapper(DashboardChartRelationMapper.class);\n    }\n\n\n    @Autowired\n    private ChartConverter chartConverter;\n\n    @Override\n    public DataResult<Long> createWithPermission(ChartCreateParam param) {\n        param.setGmtCreate(LocalDateTime.now());\n        param.setGmtModified(LocalDateTime.now());\n        param.setDeleted(YesOrNoEnum.NO.getLetter());\n        param.setUserId(ContextUtils.getUserId());\n        ChartDO chartDO = chartConverter.param2do(param);\n        getMapper().insert(chartDO);\n        return DataResult.of(chartDO.getId());\n    }\n\n    @Override\n    public ActionResult updateWithPermission(ChartUpdateParam param) {\n        Chart data = queryExistent(param.getId()).getData();\n        PermissionUtils.checkOperationPermission(data.getUserId());\n\n        param.setGmtModified(LocalDateTime.now());\n        ChartDO chartDO = chartConverter.updateParam2do(param);\n        getMapper().updateById(chartDO);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<Chart> find(Long id) {\n        ChartDO chartDO = getMapper().selectById(id);\n        if (YesOrNoEnum.YES.getLetter().equals(chartDO.getDeleted())) {\n            return DataResult.empty();\n        }\n        Chart chart = chartConverter.do2model(chartDO);\n        setDataSourceInfo(Lists.newArrayList(chart));\n        return DataResult.of(chart);\n    }\n\n    @Override\n    public DataResult<Chart> queryExistent(ChartQueryParam param) {\n        EasyLambdaQueryWrapper<ChartDO> queryWrapper = new EasyLambdaQueryWrapper<>();\n        queryWrapper\n            .eq(ChartDO::getDeleted, YesOrNoEnum.NO.getLetter())\n            .eqWhenPresent(ChartDO::getId, param.getId())\n            .eqWhenPresent(ChartDO::getUserId, param.getUserId());\n        IPage<ChartDO> page = getMapper().selectPage(new Page<>(1, 1), queryWrapper);\n        if (CollectionUtils.isEmpty(page.getRecords())) {\n            throw new DataNotFoundException();\n        }\n        Chart data = chartConverter.do2model(page.getRecords().get(0));\n        setDataSourceInfo(Lists.newArrayList(data));\n        return DataResult.of(data);\n    }\n\n    @Override\n    public DataResult<Chart> queryExistent(Long id) {\n        DataResult<Chart> dataResult = find(id);\n        if (dataResult.getData() == null) {\n            throw new DataNotFoundException();\n        }\n        return dataResult;\n    }\n\n    @Override\n    public ListResult<Chart> listQuery(ChartListQueryParam param) {\n        EasyLambdaQueryWrapper<ChartDO> queryWrapper = new EasyLambdaQueryWrapper<>();\n        queryWrapper\n            .eq(ChartDO::getDeleted, YesOrNoEnum.NO.getLetter())\n            .inWhenPresent(ChartDO::getId, param.getIdList())\n            .eqWhenPresent(ChartDO::getUserId, param.getUserId());\n        List<ChartDO> queryList = getMapper().selectList(queryWrapper);\n        List<Chart> list = chartConverter.do2model(queryList);\n        setDataSourceInfo(list);\n        return ListResult.of(list);\n    }\n\n    @Override\n    public ActionResult deleteWithPermission(Long id) {\n        Chart data = queryExistent(id).getData();\n        PermissionUtils.checkOperationPermission(data.getUserId());\n\n        ChartDO chartDO = new ChartDO();\n        chartDO.setId(id);\n        chartDO.setDeleted(YesOrNoEnum.YES.getLetter());\n        getMapper().updateById(chartDO);\n        LambdaQueryWrapper<DashboardChartRelationDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(DashboardChartRelationDO::getChartId, id);\n        List<DashboardChartRelationDO> relationDO = getDashboardMapper().selectList(queryWrapper);\n        List<Long> relationIds = relationDO.stream().map(DashboardChartRelationDO::getId).toList();\n        if (CollectionUtils.isNotEmpty(relationIds)) {\n            getDashboardMapper().deleteBatchIds(relationIds);\n        }\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ListResult<Chart> queryByIds(List<Long> ids) {\n        if (CollectionUtils.isEmpty(ids)) {\n            return ListResult.empty();\n        }\n        List<ChartDO> chartDOS = getMapper().selectBatchIds(ids);\n        List<Chart> charts = chartConverter.do2model(chartDOS);\n        List<Chart> result = charts.stream().filter(o -> YesOrNoEnum.NO.getLetter().equals(o.getDeleted())).toList();\n        setDataSourceInfo(result);\n        return ListResult.of(result);\n    }\n\n    /**\n     * Backfill data source information\n     *\n     * @param result\n     */\n    private void setDataSourceInfo(List<Chart> result) {\n        List<Long> dataSourceIds = result.stream().map(Chart::getDataSourceId).toList();\n        ListResult<DataSource> dataSourceListResult = dataSourceService.queryByIds(dataSourceIds);\n        Map<Long, DataSource> dataSourceMap = dataSourceListResult.getData().stream().collect(\n            Collectors.toMap(DataSource::getId, Function.identity(), (a, b) -> a));\n        result.forEach(o -> {\n            if (dataSourceMap.containsKey(o.getDataSourceId())) {\n                o.setDataSourceName(dataSourceMap.get(o.getDataSourceId()).getAlias());\n            }\n        });\n    }\n\n    private ChartMapper getMapper() {\n        return Dbutils.getMapper(ChartMapper.class);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/ConfigServiceImpl.java",
    "content": "\npackage ai.chat2db.server.domain.core.impl;\n\nimport java.time.LocalDateTime;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.SystemConfigParam;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.domain.core.converter.ConfigConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.SystemConfigDO;\nimport ai.chat2db.server.domain.repository.mapper.SystemConfigMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\n\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author jipengfei\n * @version : ConfigServiceImpl.java\n */\n@Service\npublic class ConfigServiceImpl implements ConfigService {\n\n    private SystemConfigMapper getMapper() {\n        return Dbutils.getMapper(SystemConfigMapper.class);\n    }\n\n    @Autowired\n    private ConfigConverter configConverter;\n\n    @Override\n    public ActionResult create(SystemConfigParam param) {\n        SystemConfigDO systemConfigDO = configConverter.param2do(param);\n        systemConfigDO.setGmtCreate(LocalDateTime.now());\n        systemConfigDO.setGmtModified(LocalDateTime.now());\n        getMapper().insert(systemConfigDO);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ActionResult update(SystemConfigParam param) {\n        SystemConfigDO systemConfigDO = configConverter.param2do(param);\n        UpdateWrapper<SystemConfigDO> updateWrapper = new UpdateWrapper<>();\n        updateWrapper.eq(\"code\", param.getCode());\n        getMapper().update(systemConfigDO, updateWrapper);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ActionResult createOrUpdate(SystemConfigParam param) {\n        SystemConfigDO systemConfigDO = getMapper().selectOne(\n            new UpdateWrapper<SystemConfigDO>().eq(\"code\", param.getCode()));\n        if (systemConfigDO == null) {\n            return create(param);\n        } else {\n            return update(param);\n        }\n    }\n\n    @Override\n    public DataResult<Config> find(String code) {\n        SystemConfigDO systemConfigDO = getMapper().selectOne(\n            new UpdateWrapper<SystemConfigDO>().eq(\"code\", code));\n        return DataResult.of(configConverter.do2model(systemConfigDO));\n    }\n\n    @Override\n    public ActionResult delete(String code) {\n        getMapper().delete(new UpdateWrapper<SystemConfigDO>().eq(\"code\", code));\n        return ActionResult.isSuccess();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/ConsoleServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.service.ConsoleService;\nimport ai.chat2db.server.domain.api.param.ConsoleCloseParam;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\n\nimport org.springframework.stereotype.Service;\n\n/**\n * @author moji\n * @version DataSourceCoreServiceImpl.java, v 0.1 September 23, 2022 15:51 moji Exp $\n * @date 2022/09/23\n */\n@Service\npublic class ConsoleServiceImpl implements ConsoleService {\n    @Override\n    public ActionResult createConsole(ConsoleConnectParam param) {\n        Chat2DBContext.getDBManage().connectDatabase(Chat2DBContext.getConnection(),param.getDatabaseName());\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ActionResult closeConsole(ConsoleCloseParam param) {\n        return ActionResult.isSuccess();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DashboardServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Objects;\n\nimport ai.chat2db.server.domain.api.model.Dashboard;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardCreateParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardPageQueryParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardQueryParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardUpdateParam;\nimport ai.chat2db.server.domain.api.service.DashboardService;\nimport ai.chat2db.server.domain.core.converter.DashboardConverter;\nimport ai.chat2db.server.domain.core.util.PermissionUtils;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.DashboardChartRelationDO;\nimport ai.chat2db.server.domain.repository.entity.DashboardDO;\nimport ai.chat2db.server.domain.repository.mapper.DashboardChartRelationMapper;\nimport ai.chat2db.server.domain.repository.mapper.DashboardMapper;\nimport ai.chat2db.server.tools.base.enums.YesOrNoEnum;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.exception.DataNotFoundException;\nimport ai.chat2db.server.tools.common.model.EasyLambdaQueryWrapper;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.EasySqlUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author moji\n * @version DashboardServiceImpl.java, v 0.1 June 9, 2023 16:06 moji Exp $\n * @date 2023/06/09\n */\n@Service\npublic class DashboardServiceImpl implements DashboardService {\n\n\n    private DashboardMapper getMapper() {\n        return Dbutils.getMapper(DashboardMapper.class);\n    }\n    private DashboardChartRelationMapper getMapper1() {\n        return Dbutils.getMapper(DashboardChartRelationMapper.class);\n    }\n\n\n    @Autowired\n    private DashboardConverter dashboardConverter;\n\n    @Override\n    public DataResult<Long> createWithPermission(DashboardCreateParam param) {\n        param.setGmtCreate(LocalDateTime.now());\n        param.setGmtModified(LocalDateTime.now());\n        param.setDeleted(YesOrNoEnum.NO.getLetter());\n        param.setUserId(ContextUtils.getUserId());\n        DashboardDO dashboardDO = dashboardConverter.param2do(param);\n        getMapper().insert(dashboardDO);\n        insertDashboardRelation(dashboardDO.getId(), param.getChartIds());\n        return DataResult.of(dashboardDO.getId());\n    }\n\n    @Override\n    public ActionResult updateWithPermission(DashboardUpdateParam param) {\n        Dashboard dashboardData = queryExistent(param.getId()).getData();\n        PermissionUtils.checkOperationPermission(dashboardData.getUserId());\n\n        param.setGmtModified(LocalDateTime.now());\n        DashboardDO dashboardDO = dashboardConverter.updateParam2do(param);\n        getMapper().updateById(dashboardDO);\n        if (CollectionUtils.isEmpty(param.getChartIds())) {\n            return ActionResult.isSuccess();\n        }\n        deleteDashboardRelation(dashboardDO.getId());\n        insertDashboardRelation(dashboardDO.getId(), param.getChartIds());\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<Dashboard> find(Long id) {\n        DashboardDO dashboardDO = getMapper().selectById(id);\n        if (YesOrNoEnum.YES.getLetter().equals(dashboardDO.getDeleted())) {\n            return DataResult.empty();\n        }\n        Dashboard dashboard = dashboardConverter.do2model(dashboardDO);\n        LambdaQueryWrapper<DashboardChartRelationDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(DashboardChartRelationDO::getDashboardId, id);\n        List<DashboardChartRelationDO> relationDO = getMapper1().selectList(queryWrapper);\n        List<Long> chartIds = relationDO.stream().map(DashboardChartRelationDO::getChartId).toList();\n        dashboard.setChartIds(chartIds);\n        return DataResult.of(dashboard);\n    }\n\n    @Override\n    public DataResult<Dashboard> queryExistent(DashboardQueryParam param) {\n        EasyLambdaQueryWrapper<DashboardDO> queryWrapper = new EasyLambdaQueryWrapper<>();\n        queryWrapper\n            .eq(DashboardDO::getDeleted, YesOrNoEnum.NO.getLetter())\n            .eqWhenPresent(DashboardDO::getId, param.getId())\n            .eqWhenPresent(DashboardDO::getUserId, param.getUserId());\n        IPage<DashboardDO> page = getMapper().selectPage(new Page<>(1, 1), queryWrapper);\n        if (CollectionUtils.isEmpty(page.getRecords())) {\n            throw new DataNotFoundException();\n        }\n        Dashboard dashboardData = dashboardConverter.do2model(page.getRecords().get(0));\n        LambdaQueryWrapper<DashboardChartRelationDO> dashboardChartRelationQueryWrapper = new LambdaQueryWrapper<>();\n        dashboardChartRelationQueryWrapper.eq(DashboardChartRelationDO::getDashboardId, param.getId());\n        List<DashboardChartRelationDO> relationDO = getMapper1().selectList(\n            dashboardChartRelationQueryWrapper);\n        List<Long> chartIds = relationDO.stream().map(DashboardChartRelationDO::getChartId).toList();\n        dashboardData.setChartIds(chartIds);\n        return DataResult.of(dashboardData);\n    }\n\n    @Override\n    public DataResult<Dashboard> queryExistent(Long id) {\n        DataResult<Dashboard> dataResult = find(id);\n        if (dataResult.getData() == null) {\n            throw new DataNotFoundException();\n        }\n        return dataResult;\n    }\n\n    @Override\n    public ActionResult deleteWithPermission(Long id) {\n        Dashboard data = queryExistent(id).getData();\n        PermissionUtils.checkOperationPermission(data.getUserId());\n\n        DashboardDO dashboardDO = new DashboardDO();\n        dashboardDO.setId(id);\n        dashboardDO.setDeleted(YesOrNoEnum.YES.getLetter());\n        getMapper().updateById(dashboardDO);\n        deleteDashboardRelation(id);\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * delete dashboard relation\n     *\n     * @param id\n     */\n    private void deleteDashboardRelation(Long id) {\n        LambdaQueryWrapper<DashboardChartRelationDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(DashboardChartRelationDO::getDashboardId, id);\n        List<DashboardChartRelationDO> relationDO = getMapper1().selectList(queryWrapper);\n        List<Long> relationIds = relationDO.stream().map(DashboardChartRelationDO::getId).toList();\n        if (CollectionUtils.isNotEmpty(relationIds)) {\n            getMapper1().deleteBatchIds(relationIds);\n        }\n    }\n\n    /**\n     * insert dashboard relation\n     *\n     * @param dashboardId\n     * @param chartIds\n     */\n    private void insertDashboardRelation(Long dashboardId, List<Long> chartIds) {\n        if (Objects.isNull(dashboardId) || CollectionUtils.isEmpty(chartIds)) {\n            return;\n        }\n        chartIds.forEach(chartId -> {\n            DashboardChartRelationDO relationDO = new DashboardChartRelationDO();\n            relationDO.setGmtCreate(LocalDateTime.now());\n            relationDO.setGmtModified(LocalDateTime.now());\n            relationDO.setDashboardId(dashboardId);\n            relationDO.setChartId(chartId);\n            getMapper1().insert(relationDO);\n        });\n    }\n\n    @Override\n    public PageResult<Dashboard> queryPage(DashboardPageQueryParam param) {\n        EasyLambdaQueryWrapper<DashboardDO> queryWrapper = new EasyLambdaQueryWrapper<>();\n        queryWrapper\n            .eq(DashboardDO::getDeleted, YesOrNoEnum.NO.getLetter())\n            .likeWhenPresent(DashboardDO::getName, EasySqlUtils.buildLikeRightFuzzy(param.getSearchKey()))\n            .eqWhenPresent(DashboardDO::getUserId, param.getUserId());\n        Integer start = param.getPageNo();\n        Integer offset = param.getPageSize();\n        Page<DashboardDO> page = new Page<>(start, offset);\n        IPage<DashboardDO> iPage = getMapper().selectPage(page, queryWrapper);\n        List<Dashboard> dashboards = dashboardConverter.do2model(iPage.getRecords());\n        return PageResult.of(dashboards, iPage.getTotal(), param);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DataSourceAccessBusinessServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessPageQueryParam;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessBusinessService;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceAccessCustomMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.exception.PermissionDeniedBusinessException;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\n/**\n * Data Source Access\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Service\npublic class DataSourceAccessBusinessServiceImpl implements DataSourceAccessBusinessService {\n\n    @Resource\n    private DataSourceAccessService dataSourceAccessService;\n    private DataSourceAccessCustomMapper getMapper() {\n        return Dbutils.getMapper(DataSourceAccessCustomMapper.class);\n    }\n    @Override\n    public ActionResult checkPermission(@NotNull DataSource dataSource) {\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        // private\n        if (DataSourceKindEnum.PRIVATE.getCode().equals(dataSource.getKind())) {\n            if (loginUser.getId().equals(dataSource.getUserId())) {\n                return ActionResult.isSuccess();\n            } else {\n                throw new PermissionDeniedBusinessException();\n            }\n        }\n\n        // Administrators can edit anything\n        if (loginUser.getAdmin()) {\n            return ActionResult.isSuccess();\n        }\n\n        // Verify if user have permission\n        DataSourceAccessPageQueryParam dataSourceAccessPageQueryParam = new DataSourceAccessPageQueryParam();\n        dataSourceAccessPageQueryParam.setDataSourceId(dataSource.getId());\n        dataSourceAccessPageQueryParam.setAccessObjectType(AccessObjectTypeEnum.USER.getCode());\n        dataSourceAccessPageQueryParam.setAccessObjectId(loginUser.getId());\n        dataSourceAccessPageQueryParam.queryOne();\n        if (dataSourceAccessService.pageQuery(dataSourceAccessPageQueryParam, null).hasData()) {\n            return ActionResult.isSuccess();\n        }\n\n        // Verify if the team has permission\n        if (getMapper().checkTeamPermission(dataSource.getId(), loginUser.getId()) != null) {\n            return ActionResult.isSuccess();\n\n        }\n        throw new PermissionDeniedBusinessException();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DataSourceAccessServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.model.DataSourceAccess;\nimport ai.chat2db.server.domain.api.model.DataSourceAccessObject;\nimport ai.chat2db.server.domain.api.model.Team;\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessComprehensivePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessPageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessSelector;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessService;\nimport ai.chat2db.server.domain.api.service.TeamService;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.domain.core.converter.DataSourceAccessConverter;\nimport ai.chat2db.server.domain.core.converter.DataSourceConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.DataSourceAccessDO;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceAccessCustomMapper;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceAccessMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.google.common.collect.Lists;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.springframework.stereotype.Service;\n\n/**\n * Data Source Access\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Service\npublic class DataSourceAccessServiceImpl implements DataSourceAccessService {\n\n\n    private DataSourceAccessCustomMapper getMapper() {\n        return Dbutils.getMapper(DataSourceAccessCustomMapper.class);\n    }\n\n    private DataSourceAccessMapper getAccessMapper() {\n        return Dbutils.getMapper(DataSourceAccessMapper.class);\n    }\n\n    @Resource\n    private DataSourceAccessConverter dataSourceAccessConverter;\n    @Resource\n    private DataSourceConverter dataSourceConverter;\n    @Resource\n    private UserService userService;\n    @Resource\n    private TeamService teamService;\n\n    @Override\n    public PageResult<DataSourceAccess> pageQuery(DataSourceAccessPageQueryParam param, DataSourceAccessSelector selector) {\n        LambdaQueryWrapper<DataSourceAccessDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(DataSourceAccessDO::getDataSourceId, param.getDataSourceId())\n            .eq(DataSourceAccessDO::getAccessObjectType, param.getAccessObjectType())\n            .eq(DataSourceAccessDO::getAccessObjectId, param.getAccessObjectId())\n        ;\n\n        Page<DataSourceAccessDO> page = new Page<>(param.getPageNo(), param.getPageSize());\n        page.setSearchCount(param.getEnableReturnCount());\n        IPage<DataSourceAccessDO> iPage = getAccessMapper().selectPage(page, queryWrapper);\n\n        List<DataSourceAccess> list = dataSourceAccessConverter.do2dto(iPage.getRecords());\n\n        fillData(list, selector);\n\n        return PageResult.of(list, iPage.getTotal(), param);\n    }\n\n    @Override\n    public PageResult<DataSourceAccess> comprehensivePageQuery(DataSourceAccessComprehensivePageQueryParam param,\n        DataSourceAccessSelector selector) {\n        Page<DataSourceAccessDO> page = new Page<>(param.getPageNo(), param.getPageSize());\n        page.setSearchCount(param.getEnableReturnCount());\n        IPage<DataSourceAccessDO> iPage = getMapper().comprehensivePageQuery(page,\n            param.getDataSourceId(), param.getAccessObjectType(), param.getAccessObjectId(),\n            param.getUserOrTeamSearchKey(),\n            param.getDataSourceSearchKey());\n\n        List<DataSourceAccess> list = dataSourceAccessConverter.do2dto(iPage.getRecords());\n\n        fillData(list, selector);\n\n        return PageResult.of(list, iPage.getTotal(), param);\n    }\n\n    @Override\n    public DataResult<Long> create(DataSourceAccessCreatParam param) {\n        DataSourceAccessDO data = dataSourceAccessConverter.param2do(param, ContextUtils.getUserId());\n\n        getAccessMapper().insert(data);\n        return DataResult.of(data.getId());\n    }\n\n    @Override\n    public ActionResult delete(Long id) {\n        getAccessMapper().deleteById(id);\n        return ActionResult.isSuccess();\n    }\n\n    private void fillData(List<DataSourceAccess> list, DataSourceAccessSelector selector) {\n        if (CollectionUtils.isEmpty(list) || selector == null) {\n            return;\n        }\n\n        fillAccessObject(list, selector);\n\n        fillDataSource(list, selector);\n    }\n\n    private void fillDataSource(List<DataSourceAccess> list, DataSourceAccessSelector selector) {\n        if (BooleanUtils.isNotTrue(selector.getDataSource())) {\n            return;\n        }\n        dataSourceConverter.fillDetail(EasyCollectionUtils.toList(list, DataSourceAccess::getDataSource),\n            selector.getDataSourceSelector());\n    }\n\n    private void fillAccessObject(List<DataSourceAccess> list, DataSourceAccessSelector selector) {\n        if (BooleanUtils.isNotTrue(selector.getAccessObject())) {\n            return;\n        }\n        List<Long> userIdList = Lists.newArrayList();\n        List<Long> teamIdList = Lists.newArrayList();\n        for (DataSourceAccess data : list) {\n            if (AccessObjectTypeEnum.TEAM.getCode().equals(data.getAccessObjectType())) {\n                teamIdList.add(data.getAccessObjectId());\n            } else if (AccessObjectTypeEnum.USER.getCode().equals(data.getAccessObjectType())) {\n                userIdList.add(data.getAccessObjectId());\n            }\n        }\n        List<User> userList = userService.listQuery(userIdList).getData();\n        Map<Long, User> userMap = EasyCollectionUtils.toIdentityMap(userList, User::getId);\n        List<Team> teamList = teamService.listQuery(teamIdList).getData();\n        Map<Long, Team> teamMap = EasyCollectionUtils.toIdentityMap(teamList, Team::getId);\n        for (DataSourceAccess data : list) {\n            DataSourceAccessObject dataSourceAccessObject = data.getAccessObject();\n            if (dataSourceAccessObject == null) {\n                continue;\n            }\n            if (AccessObjectTypeEnum.TEAM.getCode().equals(data.getAccessObjectType())) {\n                Team team = teamMap.get(data.getAccessObjectId());\n                if (team == null) {\n                    continue;\n                }\n                dataSourceAccessObject.setCode(team.getCode());\n                dataSourceAccessObject.setName(team.getName());\n            } else if (AccessObjectTypeEnum.USER.getCode().equals(data.getAccessObjectType())) {\n                User user = userMap.get(data.getAccessObjectId());\n                if (user == null) {\n                    continue;\n                }\n                dataSourceAccessObject.setCode(user.getUserName());\n                dataSourceAccessObject.setName(user.getNickName());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DataSourceServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.sql.Connection;\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCloseParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceTestParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceUpdateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseQueryAllParam;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.DatabaseService;\nimport ai.chat2db.server.domain.core.converter.DataSourceConverter;\nimport ai.chat2db.server.domain.core.converter.EnvironmentConverter;\nimport ai.chat2db.server.domain.core.util.PermissionUtils;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.DataSourceAccessDO;\nimport ai.chat2db.server.domain.repository.entity.DataSourceDO;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceAccessMapper;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceCustomMapper;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.exception.DataNotFoundException;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.tools.common.exception.PermissionDeniedBusinessException;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport ai.chat2db.server.tools.common.util.EasyEnumUtils;\nimport ai.chat2db.server.tools.common.util.EasySqlUtils;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.DataSourceConnect;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.IDriverManager;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.JdbcUtils;\nimport cn.hutool.core.date.DateUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.google.common.collect.Lists;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author moji\n * @version DataSourceCoreServiceImpl.java, v 0.1 September 23, 2022 15:51 moji Exp $\n * @date 2022/09/23\n */\n@Slf4j\n@Service\npublic class DataSourceServiceImpl implements DataSourceService {\n\n\n    private DataSourceMapper getMapper() {\n        return Dbutils.getMapper(DataSourceMapper.class);\n    }\n\n    @Autowired\n    private DataSourceConverter dataSourceConverter;\n\n    @Autowired\n    private DatabaseService databaseService;\n\n\n    private DataSourceCustomMapper getCustomMapper() {\n        return Dbutils.getMapper(DataSourceCustomMapper.class);\n    }\n    @Resource\n    private EnvironmentConverter environmentConverter;\n    private DataSourceAccessMapper getAccessMapper() {\n        return Dbutils.getMapper(DataSourceAccessMapper.class);\n    }\n\n    @Override\n    public DataResult<Long> createWithPermission(DataSourceCreateParam param) {\n        DataSourceKindEnum dataSourceKind = EasyEnumUtils.getEnum(DataSourceKindEnum.class, param.getKind());\n        if (dataSourceKind == null) {\n            throw new ParamBusinessException(\"kind\");\n        }\n        if (dataSourceKind == DataSourceKindEnum.SHARED && !ContextUtils.getLoginUser().getAdmin()) {\n            throw new PermissionDeniedBusinessException();\n        }\n        JdbcUtils.removePropertySameAsDefault(param.getDriverConfig());\n        DataSourceDO dataSourceDO = dataSourceConverter.param2do(param);\n        dataSourceDO.setGmtCreate(DateUtil.date());\n        dataSourceDO.setGmtModified(DateUtil.date());\n        dataSourceDO.setUserId(ContextUtils.getUserId());\n        //dataSourceDO.setExtendInfo(null);\n\n        getMapper().insert(dataSourceDO);\n        preWarmingData(dataSourceDO.getId());\n        return DataResult.of(dataSourceDO.getId());\n    }\n\n    private void preWarmingData(Long dataSourceId) {\n        DataResult<DataSource> dataResult = queryById(dataSourceId);\n        if (dataResult.success() && dataResult.getData() != null) {\n            DataSource dataSource = dataResult.getData();\n            DriverConfig driverConfig = dataSource.getDriverConfig();\n            if (driverConfig == null || StringUtils.isBlank(driverConfig.getJdbcDriver())) {\n                return;\n            }\n            try (Connection connection = IDriverManager.getConnection(dataSource.getUrl(), dataSource.getUserName(),\n                    dataSource.getPassword(), dataSource.getDriverConfig(), dataSource.getExtendMap())) {\n                DatabaseQueryAllParam databaseQueryAllParam = new DatabaseQueryAllParam();\n                databaseQueryAllParam.setDataSourceId(dataSourceId);\n                databaseQueryAllParam.setConnection(connection);\n                databaseQueryAllParam.setDbType(dataSource.getType());\n                databaseQueryAllParam.setRefresh(true);\n                databaseService.queryAll(databaseQueryAllParam);\n            } catch (Exception e) {\n                log.error(\"preWarmingData error\", e);\n            }\n        }\n    }\n\n    @Override\n    public DataResult<Long> updateWithPermission(DataSourceUpdateParam param) {\n        DataSource dataSource = queryExistent(param.getId(), null).getData();\n        PermissionUtils.checkOperationPermission(dataSource.getUserId());\n\n        JdbcUtils.removePropertySameAsDefault(param.getDriverConfig());\n        DataSourceDO dataSourceDO = dataSourceConverter.param2do(param);\n        dataSourceDO.setGmtModified(DateUtil.date());\n        getMapper().updateById(dataSourceDO);\n        return DataResult.of(dataSourceDO.getId());\n    }\n\n    @Override\n    public ActionResult deleteWithPermission(Long id) {\n\n        DataSource dataSource = queryExistent(id, null).getData();\n        PermissionUtils.checkOperationPermission(dataSource.getUserId());\n\n        getMapper().deleteById(id);\n\n        LambdaQueryWrapper<DataSourceAccessDO> dataSourceAccessQueryWrapper = new LambdaQueryWrapper<>();\n        dataSourceAccessQueryWrapper.eq(DataSourceAccessDO::getDataSourceId, id)\n        ;\n        getAccessMapper().delete(dataSourceAccessQueryWrapper);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<DataSource> queryById(Long id) {\n        DataSourceDO dataSourceDO = getMapper().selectById(id);\n        return DataResult.of(dataSourceConverter.do2dto(dataSourceDO));\n    }\n\n    @Override\n    public DataResult<DataSource> queryExistent(Long id, DataSourceSelector selector) {\n        DataResult<DataSource> dataResult = queryById(id);\n        if (dataResult.getData() == null) {\n            throw new DataNotFoundException();\n        }\n\n        fillData(Lists.newArrayList(dataResult.getData()), selector);\n\n        return dataResult;\n    }\n\n    @Override\n    public DataResult<Long> copyByIdWithPermission(Long id) {\n        DataSource dataSource = queryExistent(id, null).getData();\n        PermissionUtils.checkOperationPermission(dataSource.getUserId());\n\n        DataSourceDO dataSourceDO = getMapper().selectById(id);\n        dataSourceDO.setId(null);\n        String alias = dataSourceDO.getAlias() + \"Copy\";\n        dataSourceDO.setAlias(alias);\n        dataSourceDO.setGmtCreate(DateUtil.date());\n        dataSourceDO.setGmtModified(DateUtil.date());\n        getMapper().insert(dataSourceDO);\n        return DataResult.of(dataSourceDO.getId());\n    }\n\n    @Override\n    public PageResult<DataSource> queryPage(DataSourcePageQueryParam param, DataSourceSelector selector) {\n        LambdaQueryWrapper<DataSourceDO> queryWrapper = new LambdaQueryWrapper<>();\n        if (StringUtils.isNotBlank(param.getSearchKey())) {\n            queryWrapper.and(wrapper -> wrapper.like(DataSourceDO::getAlias, \"%\" + param.getSearchKey() + \"%\")\n                    .or()\n                    .like(DataSourceDO::getUrl, \"%\" + param.getSearchKey() + \"%\"));\n        }\n        Integer start = param.getPageNo();\n        Integer offset = param.getPageSize();\n        Page<DataSourceDO> page = new Page<>(start, offset);\n        IPage<DataSourceDO> iPage = getMapper().selectPage(page, queryWrapper);\n        List<DataSource> dataSources = dataSourceConverter.do2dto(iPage.getRecords());\n\n        fillData(dataSources, selector);\n\n        return PageResult.of(dataSources, iPage.getTotal(), param);\n    }\n\n    @Override\n    public PageResult<DataSource> queryPageWithPermission(DataSourcePageQueryParam param, DataSourceSelector selector) {\n        LoginUser loginUser = ContextUtils.getLoginUser();\n\n        IPage<DataSourceDO> iPage = getCustomMapper().selectPageWithPermission(\n                new Page<>(param.getPageNo(), param.getPageSize()),\n                BooleanUtils.isTrue(loginUser.getAdmin()), loginUser.getId(), param.getSearchKey(), param.getKind(),\n                EasySqlUtils.orderBy(param.getOrderByList()));\n\n        List<DataSource> dataSources = dataSourceConverter.do2dto(iPage.getRecords());\n\n        fillData(dataSources, selector);\n\n        return PageResult.of(dataSources, iPage.getTotal(), param);\n\n    }\n\n    @Override\n    public ListResult<DataSource> queryByIds(List<Long> ids) {\n        return listQuery(ids, null);\n    }\n\n    @Override\n    public ListResult<DataSource> listQuery(List<Long> idList, DataSourceSelector selector) {\n        if (CollectionUtils.isEmpty(idList)) {\n            return ListResult.empty();\n        }\n        List<DataSourceDO> dataList = getMapper().selectBatchIds(idList);\n        List<DataSource> list = dataSourceConverter.do2dto(dataList);\n\n        fillData(list, selector);\n        return ListResult.of(list);\n    }\n\n    @Override\n    public ActionResult preConnect(DataSourcePreConnectParam param) {\n        DataSourceTestParam testParam\n                = dataSourceConverter.param2param(param);\n        DriverConfig driverConfig = testParam.getDriverConfig();\n        if (driverConfig == null || !driverConfig.notEmpty()) {\n            driverConfig = Chat2DBContext.getDefaultDriverConfig(param.getType());\n        }\n        DataSourceConnect dataSourceConnect = JdbcUtils.testConnect(testParam.getUrl(), testParam.getHost(),\n                testParam.getPort(),\n                testParam.getUsername(), testParam.getPassword(), testParam.getDbType(),\n                driverConfig, param.getSsh(), KeyValue.toMap(param.getExtendInfo()));\n        if (BooleanUtils.isNotTrue(dataSourceConnect.getSuccess())) {\n            return ActionResult.fail(dataSourceConnect.getMessage(), dataSourceConnect.getDescription(),\n                    dataSourceConnect.getErrorDetail());\n        }\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ListResult<Database> connect(Long id) {\n        DatabaseQueryAllParam queryAllParam = new DatabaseQueryAllParam();\n        queryAllParam.setDataSourceId(id);\n        List<Database> databases = Chat2DBContext.getMetaData().databases(Chat2DBContext.getConnection());\n        return ListResult.of(databases);\n    }\n\n    @Override\n    public ActionResult close(Long id) {\n        DataSourceCloseParam closeParam = new DataSourceCloseParam();\n        closeParam.setDataSourceId(id);\n        return ActionResult.isSuccess();\n    }\n\n    private void fillData(List<DataSource> list, DataSourceSelector selector) {\n        if (CollectionUtils.isEmpty(list) || selector == null) {\n            return;\n        }\n\n        fillEnvironment(list, selector);\n\n        fillSupportDatabase(list);\n    }\n\n    private void fillSupportDatabase(List<DataSource> list) {\n\n        if(CollectionUtils.isEmpty(list)) {\n            return;\n        }\n        for (DataSource dataSource:list) {\n            String type = dataSource.getType();\n            if(StringUtils.isNotBlank(type)) {\n                DBConfig config = Chat2DBContext.getDBConfig(type);\n                if(config != null) {\n                    dataSource.setSupportDatabase(config.isSupportDatabase());\n                    dataSource.setSupportSchema(config.isSupportSchema());\n                }\n            }\n        }\n    }\n\n\n    private void fillEnvironment(List<DataSource> list, DataSourceSelector selector) {\n        if (BooleanUtils.isNotTrue(selector.getEnvironment())) {\n            return;\n        }\n        environmentConverter.fillDetail(EasyCollectionUtils.toList(list, DataSource::getEnvironment));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DatabaseServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CountDownLatch;\n\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseQueryAllParam;\nimport ai.chat2db.server.domain.api.param.MetaDataQueryParam;\nimport ai.chat2db.server.domain.api.param.SchemaOperationParam;\nimport ai.chat2db.server.domain.api.param.SchemaQueryParam;\nimport ai.chat2db.server.domain.api.service.DatabaseService;\nimport ai.chat2db.server.domain.core.cache.CacheManage;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport cn.hutool.core.thread.ThreadUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport static ai.chat2db.server.domain.core.cache.CacheKey.getDataBasesKey;\nimport static ai.chat2db.server.domain.core.cache.CacheKey.getDataSourceKey;\nimport static ai.chat2db.server.domain.core.cache.CacheKey.getSchemasKey;\n\n/**\n * @author moji\n * @version DataSourceCoreServiceImpl.java, v 0.1 September 23, 2022 15:51 moji Exp $\n * @date 2022/09/23\n */\n@Slf4j\n@Service\npublic class DatabaseServiceImpl implements DatabaseService {\n\n    @Override\n    public ListResult<Database> queryAll(DatabaseQueryAllParam param) {\n        List<Database> databases = CacheManage.getList(getDataBasesKey(param.getDataSourceId()), Database.class,\n                (key) -> param.isRefresh(),\n                (key) -> getDatabases(param.getDbType(), param.getConnection() == null ? Chat2DBContext.getConnection()\n                        : param.getConnection())\n        );\n        return ListResult.of(databases);\n    }\n\n    private List<Database> getDatabases(String dbType, Connection connection) {\n        return Chat2DBContext.getMetaData(dbType).databases(connection);\n    }\n\n    @Override\n    public ListResult<Schema> querySchema(SchemaQueryParam param) {\n        List<Schema> schemas = CacheManage.getList(getSchemasKey(param.getDataSourceId(), param.getDataBaseName()),\n                Schema.class,\n                (key) -> param.isRefresh(), (key) -> {\n                    Connection connection = param.getConnection() == null ? Chat2DBContext.getConnection()\n                            : param.getConnection();\n                    return getSchemaList(param.getDataBaseName(), connection);\n                });\n        return ListResult.of(schemas);\n    }\n\n\n    private List<Schema> getSchemaList(String databaseName, Connection connection) {\n        MetaData metaData = Chat2DBContext.getMetaData();\n        List<Schema> schemas = metaData.schemas(connection, databaseName);\n        sortSchema(schemas, connection);\n        return schemas;\n    }\n\n    private void sortSchema(List<Schema> schemas, Connection connection) {\n        if (CollectionUtils.isEmpty(schemas)) {\n            return;\n        }\n        String ulr = null;\n        try {\n            ulr = connection.getMetaData().getURL();\n        } catch (SQLException e) {\n            log.error(\"get url error\", e);\n        }\n        // If the database name contains the name of the current database, the current database is placed in the first place\n        int targetIndex = -1;\n        for (int i = 0; i < schemas.size(); i++) {\n            String schema = schemas.get(i).getName();\n            if (StringUtils.isNotBlank(ulr) && schema!=null && ulr.contains(schema)) {\n                targetIndex = i;\n                break;\n            }\n        }\n        if (targetIndex != -1 && targetIndex != 0) {\n            Collections.swap(schemas, targetIndex, 0);\n        }\n    }\n\n    @Override\n    public DataResult<MetaSchema> queryDatabaseSchema(MetaDataQueryParam param) {\n        MetaSchema metaSchema = new MetaSchema();\n        MetaData metaData = Chat2DBContext.getMetaData();\n        MetaSchema ms = CacheManage.get(getDataSourceKey(param.getDataSourceId()), MetaSchema.class,\n                (key) -> param.isRefresh(), (key) -> {\n                    Connection connection = Chat2DBContext.getConnection();\n                    List<Database> databases = metaData.databases(connection);\n                    if (!CollectionUtils.isEmpty(databases)) {\n                        CountDownLatch countDownLatch = ThreadUtil.newCountDownLatch(databases.size());\n                        for (Database database : databases) {\n                            ThreadUtil.execute(() -> {\n                                try {\n                                    database.setSchemas(metaData.schemas(connection, database.getName()));\n                                } catch (Exception e) {\n                                    log.error(\"queryDatabaseSchema error\", e);\n                                } finally{\n                                    countDownLatch.countDown();\n                                }\n                            });\n                        }\n                        try {\n                            countDownLatch.await();\n                        } catch (InterruptedException e) {\n                            log.error(\"queryDatabaseSchema error\", e);\n                        }\n                        metaSchema.setDatabases(databases);\n\n                    } else {\n                        List<Schema> schemas = metaData.schemas(connection, null);\n                        metaSchema.setSchemas(schemas);\n                    }\n                    return metaSchema;\n                });\n\n        return DataResult.of(ms);\n    }\n\n    @Override\n    public ActionResult deleteDatabase(DatabaseCreateParam param) {\n        Chat2DBContext.getDBManage().dropDatabase(Chat2DBContext.getConnection(), param.getName());\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<Sql> createDatabase(Database database) {\n        String sql = Chat2DBContext.getSqlBuilder().buildCreateDatabaseSql(database);\n        return DataResult.of(Sql.builder().sql(sql).build());\n    }\n\n    @Override\n    public ActionResult modifyDatabase(DatabaseCreateParam param) {\n        Chat2DBContext.getDBManage().modifyDatabase(Chat2DBContext.getConnection(), param.getName(),\n                param.getNewName());\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ActionResult deleteSchema(SchemaOperationParam param) {\n        Chat2DBContext.getDBManage().dropSchema(Chat2DBContext.getConnection(), param.getDatabaseName(),\n                param.getSchemaName());\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<Sql> createSchema(Schema schema) {\n        String sql = Chat2DBContext.getSqlBuilder().buildCreateSchemaSql(schema);\n        return DataResult.of(Sql.builder().sql(sql).build());\n    }\n\n    @Override\n    public ActionResult modifySchema(SchemaOperationParam param) {\n        Chat2DBContext.getDBManage().modifySchema(Chat2DBContext.getConnection(), param.getDatabaseName(),\n                param.getSchemaName(),\n                param.getNewSchemaName());\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public String exportDatabase(DatabaseExportParam param) throws SQLException {\n        AsyncCall call = new AsyncCall() {\n\n            @Override\n            public void update(Map<String, Object> map) {\n\n            }\n        };\n\n        AsyncContext asyncContext = new AsyncContext(call, ContextUtils.queryContext(), null, param.getContainData());\n        Chat2DBContext.getDBManage().exportDatabase(Chat2DBContext.getConnection(),\n                                                          param.getDatabaseName(),\n                                                          param.getSchemaName(), asyncContext);\n\n        return \"exportDatabase success\";\n    }\n\n    @Override\n    public ListResult<String> getUsernameList(){\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        List<String> usernames = metaSchema.usernames(Chat2DBContext.getConnection());\n        return ListResult.of(usernames);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DlTemplateServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.domain.api.service.OperationLogService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.domain.core.converter.CommandConverter;\nimport ai.chat2db.server.domain.core.util.MetaNameUtils;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport ai.chat2db.spi.CommandExecutor;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.util.JdbcUtils;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.PagerUtils;\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * @author moji\n * @version DataSourceCoreServiceImpl.java, v 0.1 September 23, 2022 15:51 moji Exp $\n * @date 2022/09/23\n */\n@Slf4j\n@Service\npublic class DlTemplateServiceImpl implements DlTemplateService {\n\n    @Autowired\n    private OperationLogService operationLogService;\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private CommandConverter commandConverter;\n\n    @Override\n    public ListResult<ExecuteResult> execute(DlExecuteParam param) {\n        CommandExecutor executor = Chat2DBContext.getMetaData().getCommandExecutor();\n        Command command = commandConverter.param2model(param);\n        List<ExecuteResult> results = executor.execute(command);\n        return reBuildHeader(results,param.getSchemaName(),param.getDatabaseName());\n    }\n\n    private ListResult<ExecuteResult> reBuildHeader(List<ExecuteResult> results,String schemaName,String databaseName){\n        ListResult<ExecuteResult> listResult = ListResult.of(results);\n        for (ExecuteResult executeResult : results) {\n            List<Header> headers = executeResult.getHeaderList();\n            if (executeResult.getSuccess() && executeResult.isCanEdit() && CollectionUtils.isNotEmpty(headers)) {\n                headers = setColumnInfo(headers, executeResult.getTableName(), schemaName, databaseName);\n                executeResult.setHeaderList(headers);\n            }\n            if (!executeResult.getSuccess()) {\n                listResult.setSuccess(false);\n                listResult.errorCode(executeResult.getDescription());\n                listResult.setErrorMessage(executeResult.getMessage());\n            }\n            addOperationLog(executeResult);\n        }\n        return listResult;\n    }\n\n    @Override\n    public ListResult<ExecuteResult> executeSelectTable(DlExecuteParam param) {\n        Command command = commandConverter.param2model(param);\n        List<ExecuteResult> results = Chat2DBContext.getMetaData().getCommandExecutor().executeSelectTable(command);\n        return reBuildHeader(results,param.getSchemaName(),param.getDatabaseName());\n    }\n\n    @Override\n    public DataResult<ExecuteResult> executeUpdate(DlExecuteParam param) {\n        CommandExecutor executor = Chat2DBContext.getMetaData().getCommandExecutor();\n        DataResult<ExecuteResult> dataResult = new DataResult<>();\n        dataResult.setSuccess(true);\n        //RemoveSpecialGO(param);\n        DbType dbType =\n                JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());\n        List<String> sqlList = SqlUtils.parse(param.getSql(), dbType,true);\n        Connection connection = Chat2DBContext.getConnection();\n        try {\n//            connection.setAutoCommit(false);\n            for (String originalSql : sqlList) {\n                ExecuteResult executeResult = executor.executeUpdate(originalSql, connection, 1);\n                dataResult.setData(executeResult);\n                addOperationLog(executeResult);\n            }\n//            connection.commit();\n        } catch (Exception e) {\n            log.error(\"executeUpdate error\", e);\n            dataResult.setSuccess(false);\n            dataResult.setErrorCode(\"connection error\");\n            dataResult.setErrorMessage(e.getMessage());\n        }\n        return dataResult;\n    }\n\n\n\n\n    @Override\n    public DataResult<Long> count(DlCountParam param) {\n        if (StringUtils.isBlank(param.getSql())) {\n            return DataResult.of(0L);\n        }\n        DbType dbType =\n                JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());\n        String sql = param.getSql();\n        // Parse sql pagination\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);\n        if (!(sqlStatement instanceof SQLSelectStatement)) {\n            throw new BusinessException(\"dataSource.sqlAnalysisError\");\n        }\n        sql = PagerUtils.count(sql, dbType);\n        ExecuteResult executeResult;\n        try {\n            executeResult = Chat2DBContext.getMetaData().getCommandExecutor().execute(sql, Chat2DBContext.getConnection(), true, null, null);\n        } catch (SQLException e) {\n            log.warn(\"Execute sql: {} exception\", sql, e);\n            executeResult = ExecuteResult.builder()\n                    .sql(sql)\n                    .success(Boolean.FALSE)\n                    .message(e.getMessage())\n                    .build();\n        }\n\n        List<List<String>> dataList = executeResult.getDataList();\n        if (CollectionUtils.isEmpty(dataList)) {\n            return DataResult.of(0L);\n        }\n        String count = EasyCollectionUtils.stream(executeResult.getDataList())\n                .findFirst()\n                .orElse(Collections.emptyList())\n                .stream()\n                .findFirst()\n                .orElse(\"0\");\n        return DataResult.of(Long.valueOf(count));\n    }\n\n    @Override\n    public DataResult<String> updateSelectResult(UpdateSelectResultParam param) {\n        SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();\n        QueryResult queryResult = new QueryResult();\n        BeanUtils.copyProperties(param, queryResult);\n        String sql = sqlBuilder.buildSqlByQuery(queryResult);\n        return DataResult.of(sql);\n    }\n\n    @Override\n    public DataResult<String> getOrderBySql(OrderByParam param) {\n        SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();\n        String orderSql = sqlBuilder.buildOrderBySql(param.getOriginSql(), param.getOrderByList());\n        return DataResult.of(orderSql);\n    }\n\n    \n    /**\n     * The method getGroupBySql constructs a GROUP BY SQL query string from the provided parameters.\n     *\n     * @param param - a GroupByParam object containing the original SQL query and the list of columns to group by\n     * @return DataResult<String> - a DataResult object containing the constructed GROUP BY SQL query string\n    */\n   @Override\n    public DataResult<String> getGroupBySql(GroupByParam param) {\n        // - Retrieve the SqlBuilder instance from Chat2DBContext\n        SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();\n        // Build the GROUP BY SQL using the provided parameters\n        String groupSql = sqlBuilder.buildGroupBySql(param.getOriginSql(), param.getGroupByList());\n        // Return the built SQL as a DataResult\n        return DataResult.of(groupSql);\n    }\n    private List<Header> setColumnInfo(List<Header> headers, String tableName, String schemaName, String databaseName) {\n        try {\n            TableQueryParam tableQueryParam = new TableQueryParam();\n            tableQueryParam.setTableName(MetaNameUtils.getMetaName(tableName));\n            tableQueryParam.setSchemaName(schemaName);\n            tableQueryParam.setDatabaseName(databaseName);\n            tableQueryParam.setRefresh(true);\n            List<TableColumn> columns = tableService.queryColumns(tableQueryParam);\n            if (CollectionUtils.isEmpty(columns)) {\n                return headers;\n            }\n            Map<String, TableColumn> columnMap = columns.stream().collect(\n                    Collectors.toMap(TableColumn::getName, tableColumn -> tableColumn));\n            List<TableIndex> tableIndices = tableService.queryIndexes(tableQueryParam);\n            if (!CollectionUtils.isEmpty(tableIndices)) {\n                for (TableIndex tableIndex : tableIndices) {\n                    if (\"PRIMARY\".equalsIgnoreCase(tableIndex.getType())) {\n                        List<TableIndexColumn> columnList = tableIndex.getColumnList();\n                        if (!CollectionUtils.isEmpty(columnList)) {\n                            for (TableIndexColumn tableIndexColumn : columnList) {\n                                TableColumn tableColumn = columnMap.get(tableIndexColumn.getColumnName());\n                                if (tableColumn != null) {\n                                    tableColumn.setPrimaryKey(true);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            for (Header header : headers) {\n                TableColumn tableColumn = columnMap.get(header.getName());\n                if (tableColumn != null) {\n                    header.setPrimaryKey(tableColumn.getPrimaryKey());\n                    header.setComment(tableColumn.getComment());\n                    header.setDefaultValue(tableColumn.getDefaultValue());\n                    header.setNullable(tableColumn.getNullable());\n                    header.setColumnSize(tableColumn.getColumnSize());\n                    header.setDecimalDigits(tableColumn.getDecimalDigits());\n                }\n            }\n\n        } catch (Exception e) {\n            log.error(\"setColumnInfo error:\", e);\n        }\n        return headers;\n    }\n\n\n    private void addOperationLog(ExecuteResult executeResult) {\n        if (executeResult == null) {\n            return;\n        }\n        try {\n            ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n            OperationLogCreateParam createParam = new OperationLogCreateParam();\n            createParam.setDdl(executeResult.getSql());\n            createParam.setStatus(executeResult.getSuccess() ? \"success\" : \"fail\");\n            createParam.setDatabaseName(connectInfo.getDatabaseName());\n            createParam.setDataSourceId(connectInfo.getDataSourceId());\n            createParam.setSchemaName(connectInfo.getSchemaName());\n            createParam.setUseTime(executeResult.getDuration());\n            createParam.setType(connectInfo.getDbType());\n            createParam.setOperationRows(\n                    executeResult.getUpdateCount() != null ? Long.valueOf(executeResult.getUpdateCount()) : null);\n            operationLogService.create(createParam);\n        } catch (Exception e) {\n            log.error(\"addOperationLog error:\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/EnvironmentServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Environment;\nimport ai.chat2db.server.domain.api.param.EnvironmentPageQueryParam;\nimport ai.chat2db.server.domain.api.service.EnvironmentService;\nimport ai.chat2db.server.domain.core.converter.EnvironmentConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.EnvironmentDO;\nimport ai.chat2db.server.domain.repository.mapper.EnvironmentMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Service;\n\n/**\n * environment\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Service\npublic class EnvironmentServiceImpl implements EnvironmentService {\n\n\n\n    private EnvironmentMapper getMapper() {\n        return Dbutils.getMapper(EnvironmentMapper.class);\n    }\n    @Resource\n    private EnvironmentConverter environmentConverter;\n\n    @Override\n    public ListResult<Environment> listQuery(List<Long> idList) {\n        if (CollectionUtils.isEmpty(idList)) {\n            return ListResult.empty();\n        }\n        LambdaQueryWrapper<EnvironmentDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.in(EnvironmentDO::getId, idList);\n        List<EnvironmentDO> dataList = getMapper().selectList(queryWrapper);\n        List<Environment> list = environmentConverter.do2dto(dataList);\n        return ListResult.of(list);\n    }\n\n    @Override\n    public PageResult<Environment> pageQuery(EnvironmentPageQueryParam param) {\n        LambdaQueryWrapper<EnvironmentDO> queryWrapper = new LambdaQueryWrapper<>();\n        if (StringUtils.isNotBlank(param.getSearchKey())) {\n            queryWrapper.and(wrapper -> wrapper.like(EnvironmentDO::getName, \"%\" + param.getSearchKey() + \"%\")\n                .or()\n                .like(EnvironmentDO::getShortName, \"%\" + param.getSearchKey() + \"%\"));\n        }\n        IPage<EnvironmentDO> iPage = getMapper().selectPage(new Page<>(param.getPageNo(), param.getPageSize()),\n            queryWrapper);\n        List<Environment> dataList = environmentConverter.do2dto(iPage.getRecords());\n        return PageResult.of(dataList, iPage.getTotal(), param);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/FunctionServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.service.FunctionService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Function;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class FunctionServiceImpl implements FunctionService {\n    @Override\n    public ListResult<Function> functions(String databaseName, String schemaName) {\n        return ListResult.of(Chat2DBContext.getMetaData().functions(Chat2DBContext.getConnection(),databaseName, schemaName));\n    }\n\n    @Override\n    public DataResult<Function> detail(String databaseName, String schemaName, String functionName) {\n        return DataResult.of(Chat2DBContext.getMetaData().function(Chat2DBContext.getConnection(), databaseName, schemaName, functionName));\n    }\n\n    @Override\n    public ActionResult delete(String databaseName, String schemaName, Function function) {\n        Chat2DBContext.getDBManage().deleteFunction(Chat2DBContext.getConnection(), databaseName, schemaName, function);\n        return ActionResult.isSuccess();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/JdbcDriverServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.service.JdbcDriverService;\nimport ai.chat2db.server.domain.core.converter.DriverConfigConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.JdbcDriverDO;\nimport ai.chat2db.server.domain.repository.mapper.EnvironmentMapper;\nimport ai.chat2db.server.domain.repository.mapper.JdbcDriverMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.IDriverManager;\nimport ai.chat2db.spi.util.JdbcJarUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.google.common.collect.Lists;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport static ai.chat2db.spi.util.JdbcUtils.setDriverDefaultProperty;\n\n\n@Slf4j\n@Service\npublic class JdbcDriverServiceImpl implements JdbcDriverService {\n\n    @Autowired\n    private DriverConfigConverter driverConfigConverter;\n\n    private JdbcDriverMapper getMapper() {\n        return Dbutils.getMapper(JdbcDriverMapper.class);\n    }\n\n    @Override\n    public DataResult<DBConfig> getDrivers(String dbType) {\n        Map<String, DriverConfig> driverConfigMap = new LinkedHashMap<>();\n        LambdaQueryWrapper<JdbcDriverDO> query = new LambdaQueryWrapper<JdbcDriverDO>();\n        query.eq(JdbcDriverDO::getDbType, dbType);\n        List<JdbcDriverDO> driverDOS = getMapper().selectList(query);\n        List<DriverConfig> driverConfigs = Lists.newArrayList();\n        if (!CollectionUtils.isEmpty(driverDOS)) {\n            driverConfigs = driverDOS.stream().map(driverConfigConverter::do2Config).collect(Collectors.toList());\n        }\n\n        DBConfig dbConfig = Chat2DBContext.PLUGIN_MAP.get(dbType).getDBConfig();\n        List<DriverConfig> driverConfigList = dbConfig.getDriverConfigList();\n        if (CollectionUtils.isNotEmpty(driverConfigList)) {\n            driverConfigs.addAll(driverConfigList);\n        }\n\n        for (DriverConfig driverConfig : driverConfigs) {\n            boolean flag = driverExists(driverConfig);\n            if (flag && driverConfigMap.get(driverConfig.getJdbcDriver()) == null) {\n                driverConfigMap.put(driverConfig.getJdbcDriver(), driverConfig);\n                //TODO :Temporary solution, need to be optimized later\n                //setDriverDefaultProperty(driverConfig);\n            } else {\n                log.warn(\"Driver file not found: {}\", driverConfig.getJdbcDriver());\n            }\n        }\n        dbConfig.setDriverConfigList(driverConfigMap.isEmpty() ? null : Lists.newArrayList(driverConfigMap.values()));\n        return DataResult.of(dbConfig);\n    }\n\n\n    private boolean driverExists(DriverConfig driverConfig) {\n        boolean flag = true;\n        String[] jarPaths = driverConfig.getJdbcDriver().split(\",\");\n        for (String jarPath : jarPaths) {\n            File file = new File(JdbcJarUtils.PATH + jarPath);\n            if (!file.exists()) {\n                flag = false;\n                break;\n            }\n        }\n        return flag;\n    }\n\n    @Override\n    public ActionResult upload(String dbType, String jdbcDriverClass, String localPath) {\n        JdbcDriverDO driverDO = new JdbcDriverDO();\n        driverDO.setJdbcDriverClass(jdbcDriverClass);\n        driverDO.setDbType(dbType);\n        driverDO.setJdbcDriver(localPath);\n        DriverConfig driverConfig = driverConfigConverter.do2Config(driverDO);\n        try {\n            IDriverManager.getClassLoader(driverConfig);\n        } catch (Exception e) {\n            throw new RuntimeException(\"Driver error,please check the driver file\", e);\n        }\n        getMapper().insert(driverDO);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ActionResult download(String dbType) {\n        DBConfig dbConfig = Chat2DBContext.PLUGIN_MAP.get(dbType).getDBConfig();\n        List<DriverConfig> driverConfigList = dbConfig.getDriverConfigList();\n        for (DriverConfig driverConfig : driverConfigList) {\n            List<String> downloadJdbcDriverUrls = driverConfig.getDownloadJdbcDriverUrls();\n            for (String downloadJdbcDriverUrl : downloadJdbcDriverUrls) {\n                try {\n                    JdbcJarUtils.download(downloadJdbcDriverUrl);\n                } catch (IOException e) {\n                    throw new RuntimeException(e);\n                }\n            }\n        }\n        return ActionResult.isSuccess();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/OperationLogServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.time.LocalDateTime;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.model.OperationLog;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogPageQueryParam;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.OperationLogService;\nimport ai.chat2db.server.domain.core.converter.OperationLogConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.OperationLogDO;\nimport ai.chat2db.server.domain.repository.mapper.OperationLogMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.EasyLambdaQueryWrapper;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.EasySqlUtils;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author moji\n * @version UserExecutedDdlCoreServiceImpl.java, v 0.1 September 25, 2022 14:07 moji Exp $\n * @date 2022/09/25\n */\n@Service\npublic class OperationLogServiceImpl implements OperationLogService {\n\n\n    private OperationLogMapper getMapper() {\n        return Dbutils.getMapper(OperationLogMapper.class);\n    }\n\n    @Autowired\n    private OperationLogConverter operationLogConverter;\n\n    @Autowired\n    private DataSourceService dataSourceService;\n\n    @Override\n    public DataResult<Long> create(OperationLogCreateParam param) {\n        OperationLogDO userExecutedDdlDO = operationLogConverter.param2do(param);\n        userExecutedDdlDO.setGmtCreate(LocalDateTime.now());\n        userExecutedDdlDO.setGmtModified(LocalDateTime.now());\n        userExecutedDdlDO.setUserId(ContextUtils.getUserId());\n        getMapper().insert(userExecutedDdlDO);\n        return DataResult.of(userExecutedDdlDO.getId());\n    }\n\n    @Override\n    public PageResult<OperationLog> queryPage(OperationLogPageQueryParam param) {\n        EasyLambdaQueryWrapper<OperationLogDO> queryWrapper = new EasyLambdaQueryWrapper<>();\n        queryWrapper.likeWhenPresent(OperationLogDO::getDdl, EasySqlUtils.buildLikeRightFuzzy(param.getSearchKey()))\n                .eqWhenPresent(OperationLogDO::getUserId, param.getUserId())\n                .eqWhenPresent(OperationLogDO::getDataSourceId, param.getDataSourceId())\n                .eqWhenPresent(OperationLogDO::getDatabaseName, param.getDatabaseName())\n                .eqWhenPresent(OperationLogDO::getSchemaName, param.getSchemaName())\n        ;\n        Integer start = param.getPageNo();\n        Integer offset = param.getPageSize();\n        Page<OperationLogDO> page = new Page<>(start, offset);\n        page.setOptimizeCountSql(false);\n        page.setOrders(Arrays.asList(OrderItem.desc(\"gmt_create\")));\n        IPage<OperationLogDO> executedDdlDOIPage = getMapper().selectPage(page, queryWrapper);\n        List<OperationLog> executedDdlDTOS = operationLogConverter.do2dto(executedDdlDOIPage.getRecords());\n        if (CollectionUtils.isEmpty(executedDdlDTOS)) {\n            return PageResult.empty(param.getPageNo(), param.getPageSize());\n        }\n        List<Long> dataSourceIds = executedDdlDTOS.stream().map(OperationLog::getDataSourceId).toList();\n        ListResult<DataSource> dataSourceListResult = dataSourceService.queryByIds(dataSourceIds);\n        Map<Long, DataSource> dataSourceMap = dataSourceListResult.getData().stream().collect(\n            Collectors.toMap(DataSource::getId, Function.identity(), (a, b) -> a));\n        executedDdlDTOS.stream().forEach(executeDdl -> {\n            if (dataSourceMap.containsKey(executeDdl.getDataSourceId())) {\n                executeDdl.setDataSourceName(dataSourceMap.get(executeDdl.getDataSourceId()).getAlias());\n            }\n        });\n        return PageResult.of(executedDdlDTOS, executedDdlDOIPage.getTotal(), param);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/OperationServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.model.Operation;\nimport ai.chat2db.server.domain.api.param.operation.OperationPageQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationSavedParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationUpdateParam;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.OperationService;\nimport ai.chat2db.server.domain.core.converter.OperationConverter;\nimport ai.chat2db.server.domain.core.util.PermissionUtils;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.OperationSavedDO;\nimport ai.chat2db.server.domain.repository.mapper.OperationLogMapper;\nimport ai.chat2db.server.domain.repository.mapper.OperationSavedMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.exception.DataNotFoundException;\nimport ai.chat2db.server.tools.common.model.EasyLambdaQueryWrapper;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author moji\n * @version UserSavedDdlCoreServiceImpl.java, v 0.1 September 25, 2022 15:50 moji Exp $\n * @date 2022/09/25\n */\n@Service\npublic class OperationServiceImpl implements OperationService {\n\n\n    private OperationSavedMapper getMapper() {\n        return Dbutils.getMapper(OperationSavedMapper.class);\n    }\n\n    @Autowired\n    private OperationConverter operationConverter;\n\n    @Autowired\n    private DataSourceService dataSourceService;\n\n    @Override\n    public DataResult<Long> createWithPermission(OperationSavedParam param) {\n        OperationSavedDO userSavedDdlDO = operationConverter.param2do(param);\n        userSavedDdlDO.setGmtCreate(LocalDateTime.now());\n        userSavedDdlDO.setGmtModified(LocalDateTime.now());\n        userSavedDdlDO.setUserId(ContextUtils.getUserId());\n        getMapper().insert(userSavedDdlDO);\n        return DataResult.of(userSavedDdlDO.getId());\n    }\n\n    @Override\n    public ActionResult updateWithPermission(OperationUpdateParam param) {\n        Operation data = queryExistent(param.getId()).getData();\n        PermissionUtils.checkOperationPermission(data.getUserId());\n\n        OperationSavedDO userSavedDdlDO = operationConverter.param2do(param);\n        userSavedDdlDO.setGmtModified(LocalDateTime.now());\n        getMapper().updateById(userSavedDdlDO);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<Operation> find(Long id) {\n        OperationSavedDO operationSavedDO = getMapper().selectById(id);\n        List<Long> dataSourceIds = Lists.newArrayList(operationSavedDO.getDataSourceId());\n        Map<Long, DataSource> dataSourceMap = getDataSourceInfo(dataSourceIds);\n        Operation operation = operationConverter.do2dto(operationSavedDO);\n        operation.setDataSourceName(dataSourceMap.containsKey(operation.getDataSourceId()) ? dataSourceMap.get(\n            operation.getDataSourceId()).getAlias() : null);\n        return DataResult.of(operation);\n    }\n\n    @Override\n    public DataResult<Operation> queryExistent(Long id) {\n        DataResult<Operation> dataResult = find(id);\n        if (dataResult.getData() == null) {\n            throw new DataNotFoundException();\n        }\n        return dataResult;\n    }\n\n    @Override\n    public DataResult<Operation> queryExistent(OperationQueryParam param) {\n        EasyLambdaQueryWrapper<OperationSavedDO> queryWrapper = new EasyLambdaQueryWrapper<>();\n        queryWrapper.eqWhenPresent(OperationSavedDO::getId, param.getId())\n            .eqWhenPresent(OperationSavedDO::getUserId, param.getUserId());\n        IPage<OperationSavedDO> page = getMapper().selectPage(new Page<>(1, 1), queryWrapper);\n        if (CollectionUtils.isEmpty(page.getRecords())) {\n            throw new DataNotFoundException();\n        }\n        return DataResult.of(operationConverter.do2dto(page.getRecords().get(0)));\n    }\n\n    @Override\n    public ActionResult deleteWithPermission(Long id) {\n        Operation data = queryExistent(id).getData();\n        PermissionUtils.checkOperationPermission(data.getUserId());\n\n        getMapper().deleteById(id);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public PageResult<Operation> queryPage(OperationPageQueryParam param) {\n        QueryWrapper<OperationSavedDO> queryWrapper = new QueryWrapper<>();\n        if (StringUtils.isNotBlank(param.getSearchKey())) {\n            queryWrapper.like(\"name\", param.getSearchKey());\n        }\n        if (Objects.nonNull(param.getDataSourceId())) {\n            queryWrapper.eq(\"data_source_id\", param.getDataSourceId());\n        }\n        if (StringUtils.isNotBlank(param.getDatabaseName())) {\n            queryWrapper.eq(\"database_name\", param.getDatabaseName());\n        }\n        if (StringUtils.isNotBlank(param.getStatus())) {\n            queryWrapper.eq(\"status\", param.getStatus());\n        }\n        if (StringUtils.isNotBlank(param.getTabOpened())) {\n            queryWrapper.eq(\"tab_opened\", param.getTabOpened());\n        }\n        if (StringUtils.isNotBlank(param.getOperationType())) {\n            queryWrapper.eq(\"operation_type\", param.getOperationType());\n        }\n        if (param.getUserId() != null) {\n            queryWrapper.eq(\"user_id\", param.getUserId());\n        }\n        Integer start = param.getPageNo();\n        Integer offset = param.getPageSize();\n        Page<OperationSavedDO> page = new Page<>(start, offset);\n        page.setOptimizeCountSql(false);\n        if (Objects.nonNull(param.getOrderByDesc()) && param.getOrderByDesc()) {\n            queryWrapper.orderByDesc(\"gmt_modified\");\n        }\n        if (Objects.nonNull(param.getOrderByCreateDesc()) && param.getOrderByCreateDesc()) {\n            queryWrapper.orderByDesc(\"gmt_create\");\n        }\n        IPage<OperationSavedDO> iPage = getMapper().selectPage(page, queryWrapper);\n        List<Operation> userSavedDdlDOS = operationConverter.do2dto(iPage.getRecords());\n        if (CollectionUtils.isEmpty(userSavedDdlDOS)) {\n            return PageResult.empty(param.getPageNo(), param.getPageSize());\n        }\n        List<Long> dataSourceIds = userSavedDdlDOS.stream().map(Operation::getDataSourceId).toList();\n        Map<Long, DataSource> dataSourceMap = getDataSourceInfo(dataSourceIds);\n        userSavedDdlDOS.forEach(userSavedDdl -> userSavedDdl.setDataSourceName(\n            dataSourceMap.containsKey(userSavedDdl.getDataSourceId()) ? dataSourceMap.get(\n                userSavedDdl.getDataSourceId()).getAlias() : null));\n        return PageResult.of(userSavedDdlDOS, iPage.getTotal(), param);\n    }\n\n    /**\n     * Query data source information\n     *\n     * @param dataSourceIds\n     * @return\n     */\n    private Map<Long, DataSource> getDataSourceInfo(List<Long> dataSourceIds) {\n        if (CollectionUtils.isEmpty(dataSourceIds)) {\n            return Maps.newHashMap();\n        }\n        ListResult<DataSource> dataSourceListResult = dataSourceService.queryByIds(dataSourceIds);\n        Map<Long, DataSource> dataSourceMap = dataSourceListResult.getData().stream().collect(\n            Collectors.toMap(DataSource::getId, Function.identity(), (a, b) -> a));\n        return dataSourceMap;\n    }\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/PinServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.param.PinTableParam;\nimport ai.chat2db.server.domain.api.service.PinService;\nimport ai.chat2db.server.domain.core.converter.PinTableConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.PinTableDO;\nimport ai.chat2db.server.domain.repository.mapper.PinTableMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@Service\npublic class PinServiceImpl implements PinService {\n\n    @Autowired\n    private PinTableConverter pinTableConverter;\n\n    private PinTableMapper getMapper() {\n        return Dbutils.getMapper(PinTableMapper.class);\n    }\n\n    @Override\n    public ActionResult pinTable(PinTableParam param) {\n        PinTableDO entity = pinTableConverter.param2do(param);\n        entity.setUserId(ContextUtils.getUserId());\n        getMapper().insert(entity);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ActionResult deletePinTable(PinTableParam param) {\n        param.setUserId(ContextUtils.getUserId());\n        LambdaUpdateWrapper<PinTableDO> updateWrapper = new LambdaUpdateWrapper<>();\n        updateWrapper.eq(PinTableDO::getUserId, param.getUserId());\n        updateWrapper.eq(PinTableDO::getDataSourceId, param.getDataSourceId());\n        if (StringUtils.isNotBlank(param.getDatabaseName())) {\n            updateWrapper.eq(PinTableDO::getDatabaseName, param.getDatabaseName());\n        }\n        if (StringUtils.isNotBlank(param.getSchemaName())) {\n            updateWrapper.eq(PinTableDO::getSchemaName, param.getSchemaName());\n        }\n        if (StringUtils.isNotBlank(param.getTableName())) {\n            updateWrapper.eq(PinTableDO::getTableName, param.getTableName());\n        }\n        getMapper().delete(updateWrapper);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ListResult<String> queryPinTables(PinTableParam param) {\n        List<String> result = new ArrayList<>();\n        LambdaQueryWrapper<PinTableDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(PinTableDO::getUserId, param.getUserId());\n        queryWrapper.eq(PinTableDO::getDataSourceId, param.getDataSourceId());\n        if (StringUtils.isNotBlank(param.getDatabaseName())) {\n            queryWrapper.eq(PinTableDO::getDatabaseName, param.getDatabaseName());\n        }\n        if (StringUtils.isNotBlank(param.getSchemaName())) {\n            queryWrapper.eq(PinTableDO::getSchemaName, param.getSchemaName());\n        }\n        if (StringUtils.isNotBlank(param.getTableName())) {\n            queryWrapper.eq(PinTableDO::getTableName, param.getTableName());\n        }\n        queryWrapper.orderByDesc(PinTableDO::getGmtModified);\n        List<PinTableDO> list = getMapper().selectList(queryWrapper);\n        if (!CollectionUtils.isEmpty(list)) {\n            result = list.stream().map(pinTableDO -> pinTableDO.getTableName()).collect(Collectors.toList());\n        }\n        return ListResult.of(result);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/ProcedureServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.service.ProcedureService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport org.springframework.stereotype.Service;\n\nimport java.sql.SQLException;\n\n@Service\npublic class ProcedureServiceImpl implements ProcedureService {\n\n    @Override\n    public ListResult<Procedure> procedures(String databaseName, String schemaName) {\n        return ListResult.of(Chat2DBContext.getMetaData().procedures(Chat2DBContext.getConnection(),databaseName, schemaName));\n    }\n\n    @Override\n    public DataResult<Procedure> detail(String databaseName, String schemaName, String procedureName) {\n        return DataResult.of(Chat2DBContext.getMetaData().procedure(Chat2DBContext.getConnection(), databaseName, schemaName, procedureName));\n    }\n    @Override\n    public ActionResult update(String databaseName, String schemaName, Procedure procedure) throws SQLException {\n        Chat2DBContext.getDBManage().updateProcedure(Chat2DBContext.getConnection(), databaseName, schemaName, procedure);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public ActionResult delete(String databaseName, String schemaName, Procedure procedure) {\n        Chat2DBContext.getDBManage().deleteProcedure(Chat2DBContext.getConnection(), databaseName, schemaName, procedure);\n        return ActionResult.isSuccess();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/SequenceServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\n\nimport ai.chat2db.server.domain.api.param.DropParam;\nimport ai.chat2db.server.domain.api.param.SequencePageQueryParam;\nimport ai.chat2db.server.domain.api.param.SequenceQueryParam;\nimport ai.chat2db.server.domain.api.param.ShowCreateSequenceParam;\nimport ai.chat2db.server.domain.api.service.SequenceService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport cn.hutool.core.util.ObjectUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Sequence source management serviceImpl\n *\n * @author Sylphy\n */\n@Slf4j\n@Service\npublic class SequenceServiceImpl implements SequenceService {\n    @Override\n    public DataResult<String> showCreateSequence(ShowCreateSequenceParam param) {\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        String ddl = metaSchema.sequenceDDL(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchemaName(), param.getSequenceName());\n        return DataResult.of(ddl);\n    }\n\n    @Override\n    public ListResult<SimpleSequence> pageQuery(SequencePageQueryParam request) {\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        List<SimpleSequence> sequences = metaSchema.sequences(Chat2DBContext.getConnection(), request.getDatabaseName(), request.getSchemaName());\n        return ListResult.of(sequences);\n    }\n\n    @Override\n    public ListResult<Sql> buildSql(Sequence oldSequence, Sequence newSequence) {\n        SqlBuilder<?> sqlBuilder = Chat2DBContext.getSqlBuilder();\n        List<Sql> sqls = new ArrayList<>();\n        if (ObjectUtil.isEmpty(oldSequence)) {\n            sqls.add(Sql.builder().sql(sqlBuilder.buildCreateSequenceSql(newSequence)).build());\n        } else {\n            sqls.add(Sql.builder().sql(sqlBuilder.buildModifySequenceSql(oldSequence, newSequence)).build());\n        }\n        return ListResult.of(sqls);\n    }\n\n    @Override\n    public ActionResult drop(DropParam param) {\n        DBManage metaSchema = Chat2DBContext.getDBManage();\n        metaSchema.dropSequence(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchema(), param.getName());\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<Sequence> query(SequenceQueryParam param){\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        Sequence sequences = metaSchema.sequences(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchemaName(), param.getSequenceName());\n        return DataResult.of(sequences);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.enums.TableVectorEnum;\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.server.domain.api.service.PinService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.domain.core.cache.CacheManage;\nimport ai.chat2db.server.domain.core.converter.PinTableConverter;\nimport ai.chat2db.server.domain.core.converter.TableConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.TableCacheDO;\nimport ai.chat2db.server.domain.repository.entity.TableCacheVersionDO;\nimport ai.chat2db.server.domain.repository.entity.TableVectorMappingDO;\nimport ai.chat2db.server.domain.repository.mapper.TableCacheMapper;\nimport ai.chat2db.server.domain.repository.mapper.TableCacheVersionMapper;\nimport ai.chat2db.server.domain.repository.mapper.TableVectorMappingMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.enums.EditStatus;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.google.common.collect.Lists;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport static ai.chat2db.server.domain.core.cache.CacheKey.getColumnKey;\nimport static ai.chat2db.server.domain.core.cache.CacheKey.getTableKey;\n\n/**\n * @author moji\n * @version DataSourceCoreServiceImpl.java, v 0.1 September 23, 2022 15:51 moji Exp $\n * @date 2022/09/23\n */\n@Service\n@Slf4j\npublic class TableServiceImpl implements TableService {\n\n    @Autowired\n    private PinService pinService;\n\n    @Autowired\n    private PinTableConverter pinTableConverter;\n\n\n    private TableCacheMapper getTableCacheMapper() {\n        return Dbutils.getMapper(TableCacheMapper.class);\n    }\n\n    @Autowired\n    private TableConverter tableConverter;\n\n\n    private TableCacheVersionMapper getVersionMapper() {\n        return Dbutils.getMapper(TableCacheVersionMapper.class);\n    }\n\n\n    private TableVectorMappingMapper getTableVectorMapper() {\n        return Dbutils.getMapper(TableVectorMappingMapper.class);\n    }\n\n    @Override\n    public DataResult<String> showCreateTable(ShowCreateTableParam param) {\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        String ddl = metaSchema.tableDDL(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchemaName(), param.getTableName());\n        return DataResult.of(ddl);\n    }\n\n    @Override\n    public ActionResult drop(DropParam param) {\n        DBManage metaSchema = Chat2DBContext.getDBManage();\n        metaSchema.dropTable(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchema(), param.getName());\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<String> createTableExample(String dbType) {\n        String sql = Chat2DBContext.getDBConfig().getSimpleCreateTable();\n        return DataResult.of(sql);\n    }\n\n    @Override\n    public DataResult<String> alterTableExample(String dbType) {\n        String sql = Chat2DBContext.getDBConfig().getSimpleAlterTable();\n        return DataResult.of(sql);\n    }\n\n    @Override\n    public DataResult<Table> query(TableQueryParam param, TableSelector selector) {\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        List<Table> tables = metaSchema.tables(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchemaName(), param.getTableName());\n        if (!CollectionUtils.isEmpty(tables)) {\n            Table table = tables.get(0);\n            table.setIndexList(\n                    metaSchema.indexes(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchemaName(), param.getTableName()));\n            table.setColumnList(\n                    metaSchema.columns(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchemaName(), param.getTableName()));\n            setPrimaryKey(table);\n            return DataResult.of(table);\n        }\n        return DataResult.of(null);\n    }\n\n    private void setPrimaryKey(Table table) {\n        if (table == null) {\n            return;\n        }\n        List<TableIndex> tableIndices = table.getIndexList();\n        if (CollectionUtils.isEmpty(tableIndices)) {\n            return;\n        }\n        List<TableColumn> columns = table.getColumnList();\n        if (CollectionUtils.isEmpty(columns)) {\n            return;\n        }\n        Map<String, TableColumn> columnMap = columns.stream()\n                .collect(Collectors.toMap(TableColumn::getName, Function.identity()));\n        List<TableIndex> indexes = new ArrayList<>();\n        for (TableIndex tableIndex : tableIndices) {\n            if (\"Primary\".equalsIgnoreCase(tableIndex.getType())) {\n                List<TableIndexColumn> indexColumns = tableIndex.getColumnList();\n                if (CollectionUtils.isNotEmpty(indexColumns)) {\n                    for (TableIndexColumn indexColumn : indexColumns) {\n                        TableColumn column = columnMap.get(indexColumn.getColumnName());\n                        if (column != null) {\n                            column.setPrimaryKey(true);\n                            column.setPrimaryKeyOrder(indexColumn.getOrdinalPosition());\n                            column.setPrimaryKeyName(tableIndex.getName());\n                        }\n                    }\n                }\n            } else {\n                indexes.add(tableIndex);\n            }\n        }\n        table.setIndexList(indexes);\n    }\n\n    @Override\n    public ListResult<Sql> buildSql(Table oldTable, Table newTable) {\n        initOldTable(oldTable, newTable);\n        SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();\n        List<Sql> sqls = new ArrayList<>();\n        if (oldTable == null) {\n            initPrimaryKey(newTable);\n            sqls.add(Sql.builder().sql(sqlBuilder.buildCreateTableSql(newTable)).build());\n        } else {\n            initUpdatePrimaryKey(oldTable, newTable);\n            sqls.add(Sql.builder().sql(sqlBuilder.buildModifyTaleSql(oldTable, newTable)).build());\n        }\n        return ListResult.of(sqls);\n    }\n\n    private void initUpdatePrimaryKey(Table oldTable, Table newTable) {\n        if (newTable == null || oldTable == null) {\n            return;\n        }\n        List<TableColumn> newColumns = getPrimaryKeyColumn(newTable);\n        List<TableColumn> oldColumns = getPrimaryKeyColumn(oldTable);\n        if (CollectionUtils.isEmpty(newColumns) && CollectionUtils.isEmpty(oldColumns)) {\n            return;\n        }\n        if (!CollectionUtils.isEmpty(newColumns) && CollectionUtils.isEmpty(oldColumns)) {\n            initPrimaryKey(newTable);\n            return;\n        }\n        if (CollectionUtils.isEmpty(newColumns) && CollectionUtils.isNotEmpty(oldColumns)) {\n            addPrimaryKey(newTable, oldColumns.get(0), EditStatus.DELETE.name());\n            return;\n        }\n        if (newColumns.size() != oldColumns.size()) {\n            for (TableColumn column : newColumns) {\n                if (column.getPrimaryKey() != null && column.getPrimaryKey()) {\n                    addPrimaryKey(newTable, column, EditStatus.MODIFY.name());\n                }\n            }\n            return;\n        }\n        boolean flag = false;\n        Map<String, TableColumn> oldColumnMap = oldColumns.stream().collect(Collectors.toMap(TableColumn::getName, Function.identity()));\n        for (TableColumn column : newColumns) {\n            TableColumn oldColumn = oldColumnMap.get(column.getName());\n            if (oldColumn == null) {\n                flag = true;\n            }\n        }\n        if (flag) {\n            for (TableColumn column : newColumns) {\n                if (column.getPrimaryKey() != null && column.getPrimaryKey()) {\n                    addPrimaryKey(newTable, column, EditStatus.MODIFY.name());\n                }\n            }\n        }\n    }\n\n    private List<TableColumn> getPrimaryKeyColumn(Table table) {\n        if (table == null || CollectionUtils.isEmpty(table.getColumnList())) {\n            return null;\n        }\n        return table.getColumnList().stream().filter(tableColumn ->\n                        tableColumn.getPrimaryKey() != null && tableColumn.getPrimaryKey())\n                .collect(Collectors.toList());\n    }\n\n    private void initPrimaryKey(Table newTable) {\n        if (newTable == null) {\n            return;\n        }\n        List<TableColumn> columns = newTable.getColumnList();\n        if (CollectionUtils.isEmpty(columns)) {\n            return;\n        }\n        for (TableColumn column : columns) {\n            if (column.getPrimaryKey() != null && column.getPrimaryKey()) {\n                addPrimaryKey(newTable, column, EditStatus.ADD.name());\n            }\n        }\n    }\n\n    private void addPrimaryKey(Table newTable, TableColumn column, String status) {\n        List<TableIndex> indexes = newTable.getIndexList();\n        if (indexes == null) {\n            indexes = new ArrayList<>();\n        }\n        TableIndex keyIndex = indexes.stream().filter(index -> \"Primary\".equalsIgnoreCase(index.getType())).findFirst().orElse(null);\n        if (keyIndex == null) {\n            keyIndex = new TableIndex();\n            keyIndex.setType(\"Primary\");\n            keyIndex.setName(StringUtils.isBlank(column.getPrimaryKeyName()) ? \"PRIMARY_KEY\" : column.getPrimaryKeyName());\n            keyIndex.setTableName(newTable.getName());\n            keyIndex.setSchemaName(newTable.getSchemaName());\n            keyIndex.setDatabaseName(newTable.getDatabaseName());\n            keyIndex.setEditStatus(status);\n            if (!EditStatus.ADD.name().equals(status)) {\n                keyIndex.setOldName(keyIndex.getName());\n            }\n            indexes.add(keyIndex);\n        }\n        List<TableIndexColumn> tableIndexColumns = keyIndex.getColumnList();\n        if (tableIndexColumns == null) {\n            tableIndexColumns = new ArrayList<>();\n        }\n        TableIndexColumn indexColumn = new TableIndexColumn();\n        indexColumn.setColumnName(column.getName());\n        indexColumn.setTableName(newTable.getName());\n        indexColumn.setSchemaName(newTable.getSchemaName());\n        indexColumn.setDatabaseName(newTable.getDatabaseName());\n        indexColumn.setOrdinalPosition(Short.valueOf(column.getPrimaryKeyOrder() + \"\"));\n        indexColumn.setEditStatus(status);\n        tableIndexColumns.add(indexColumn);\n        List<TableIndexColumn> sortTableIndexColumns = tableIndexColumns.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition)).collect(Collectors.toList());\n        Set<String> statusList = sortTableIndexColumns.stream().map(TableIndexColumn::getEditStatus).collect(Collectors.toSet());\n        if (statusList.size() == 1) {\n            //only one status ,set index status\n            keyIndex.setEditStatus(statusList.iterator().next());\n        } else {\n            //more status ,set index status modify\n            keyIndex.setEditStatus(EditStatus.MODIFY.name());\n        }\n\n        keyIndex.setColumnList(sortTableIndexColumns);\n        newTable.setIndexList(indexes);\n\n    }\n\n\n    private void initOldTable(Table oldTable, Table newTable) {\n        if (oldTable == null || newTable == null) {\n            return;\n        }\n        Map<String, TableColumn> columnMap = new HashMap<>();\n        if (CollectionUtils.isNotEmpty(oldTable.getColumnList())) {\n            for (TableColumn column : oldTable.getColumnList()) {\n                columnMap.put(column.getName(), column);\n            }\n        }\n        if (CollectionUtils.isNotEmpty(newTable.getColumnList())) {\n            for (TableColumn newColumn : newTable.getColumnList()) {\n                if (EditStatus.ADD.name().equals(newColumn.getEditStatus())) {\n                    continue;\n                }\n                String name = newColumn.getOldName() == null ? newColumn.getName() : newColumn.getOldName();\n                TableColumn oldColumn = columnMap.get(name);\n                if (oldColumn != null) {\n                    if (oldColumn.equals(newColumn) && EditStatus.MODIFY.name().equals(newColumn.getEditStatus())) {\n                        newColumn.setEditStatus(null);\n                    } else {\n                        newColumn.setOldColumn(oldColumn);\n                    }\n                }\n            }\n        }\n    }\n\n    @Override\n    public PageResult<Table> pageQuery(TablePageQueryParam param, TableSelector selector) {\n        LambdaQueryWrapper<TableCacheVersionDO> queryWrapper = new LambdaQueryWrapper<>();\n        String key = getTableKey(param.getDataSourceId(), param.getDatabaseName(), param.getSchemaName());\n        queryWrapper.eq(TableCacheVersionDO::getKey, key);\n        TableCacheVersionDO versionDO = getVersionMapper().selectOne(queryWrapper);\n        long total = 0;\n        long version = 0L;\n        if (param.isRefresh() || versionDO == null) {\n            total = addCache(param, versionDO);\n        } else {\n            if (\"2\".equals(versionDO.getStatus())) {\n                version = versionDO.getVersion() - 1;\n            } else {\n                version = versionDO.getVersion();\n            }\n            total = versionDO.getTableCount();\n        }\n        Page<TableCacheDO> page = new Page<>(param.getPageNo(), param.getPageSize());\n        // page.setSearchCount(param.getEnableReturnCount());\n        IPage<TableCacheDO> iPage = getTableCacheMapper().pageQuery(page, param.getDataSourceId(), param.getDatabaseName(), param.getSchemaName(), param.getSearchKey());\n        List<Table> tables = new ArrayList<>();\n        if (CollectionUtils.isNotEmpty(iPage.getRecords())) {\n            for (TableCacheDO tableCacheDO : iPage.getRecords()) {\n                Table t = new Table();\n                t.setName(tableCacheDO.getTableName());\n                t.setComment(tableCacheDO.getExtendInfo());\n                t.setSchemaName(tableCacheDO.getSchemaName());\n                t.setDatabaseName(tableCacheDO.getDatabaseName());\n                tables.add(t);\n            }\n        }\n        if (param.getPageNo() <= 1) {\n            tables = pinTable(tables, param);\n        }\n        return PageResult.of(tables, total, param);\n    }\n\n    private long addCache(TablePageQueryParam param, TableCacheVersionDO versionDO) {\n        LambdaQueryWrapper<TableCacheVersionDO> queryWrapper = new LambdaQueryWrapper<>();\n        String key = getTableKey(param.getDataSourceId(), param.getDatabaseName(), param.getSchemaName());\n        queryWrapper.eq(TableCacheVersionDO::getKey, key);\n        long total = 0;\n        long version = getLock(param.getDataSourceId(), param.getDatabaseName(), param.getSchemaName(), versionDO);\n        if (version == -1) {\n            int n = 0;\n            while (n < 100) {\n                try {\n                    Thread.sleep(200);\n                } catch (InterruptedException e) {\n                }\n                versionDO = getVersionMapper().selectOne(queryWrapper);\n                if (versionDO != null && \"1\".equals(versionDO.getStatus())) {\n                    version = versionDO.getVersion();\n                    total = versionDO.getTableCount();\n                    break;\n                }\n                n++;\n            }\n        } else {\n            total = addDBCache(param.getDataSourceId(), param.getDatabaseName(), param.getSchemaName(), version);\n            TableCacheVersionDO versionDO1 = new TableCacheVersionDO();\n            versionDO1.setStatus(\"1\");\n            versionDO1.setTableCount(total);\n            getVersionMapper().update(versionDO1, queryWrapper);\n        }\n        return total;\n    }\n\n    @Override\n    public ListResult<SimpleTable> queryTables(TablePageQueryParam param) {\n        LambdaQueryWrapper<TableCacheVersionDO> queryWrapper = new LambdaQueryWrapper<>();\n        String key = getTableKey(param.getDataSourceId(), param.getDatabaseName(), param.getSchemaName());\n        queryWrapper.eq(TableCacheVersionDO::getKey, key);\n        TableCacheVersionDO versionDO = getVersionMapper().selectOne(queryWrapper);\n        if (versionDO == null) {\n            addCache(param, versionDO);\n            versionDO = getVersionMapper().selectOne(queryWrapper);\n        }\n        long version = \"2\".equals(versionDO.getStatus()) ? versionDO.getVersion() - 1 : versionDO.getVersion();\n\n        LambdaQueryWrapper<TableCacheDO> query = new LambdaQueryWrapper<>();\n        query.eq(TableCacheDO::getVersion, version);\n        query.eq(TableCacheDO::getDataSourceId, param.getDataSourceId());\n        if (StringUtils.isNotBlank(param.getDatabaseName())) {\n            query.eq(TableCacheDO::getDatabaseName, param.getDatabaseName());\n        }\n        if (StringUtils.isNotBlank(param.getSchemaName())) {\n            query.eq(TableCacheDO::getSchemaName, param.getSchemaName());\n        }\n        List<SimpleTable> tables = new ArrayList<>();\n\n        for (int i = 0; i < versionDO.getTableCount() / 500 + 1; i++) {\n            Page<TableCacheDO> page = new Page<>(i + 1, 500);\n            IPage<TableCacheDO> iPage = getTableCacheMapper().selectPage(page, query);\n            if (CollectionUtils.isNotEmpty(iPage.getRecords())) {\n                for (TableCacheDO tableCacheDO : iPage.getRecords()) {\n                    SimpleTable t = new SimpleTable();\n                    t.setName(tableCacheDO.getTableName());\n                    t.setComment(tableCacheDO.getExtendInfo());\n                    tables.add(t);\n                }\n            }\n        }\n        return ListResult.of(tables);\n    }\n\n    private long addDBCache(Long dataSourceId, String databaseName, String schemaName, long version) {\n        String key = getTableKey(dataSourceId, databaseName, schemaName);\n\n        Connection connection = Chat2DBContext.getConnection();\n        long n = 0;\n        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, null,\n                new String[]{\"TABLE\", \"SYSTEM TABLE\"})) {\n            List<TableCacheDO> cacheDOS = new ArrayList<>();\n            while (resultSet.next()) {\n                TableCacheDO tableCacheDO = new TableCacheDO();\n                tableCacheDO.setDatabaseName(databaseName);\n                tableCacheDO.setSchemaName(schemaName);\n                tableCacheDO.setTableName(resultSet.getString(\"TABLE_NAME\"));\n                tableCacheDO.setExtendInfo(resultSet.getString(\"REMARKS\"));\n                tableCacheDO.setDataSourceId(dataSourceId);\n                tableCacheDO.setVersion(version);\n                tableCacheDO.setKey(key);\n                cacheDOS.add(tableCacheDO);\n                if (cacheDOS.size() >= 500) {\n                    getTableCacheMapper().batchInsert(cacheDOS);\n                    cacheDOS = new ArrayList<>();\n                }\n                n++;\n            }\n            if (!CollectionUtils.isEmpty(cacheDOS)) {\n                getTableCacheMapper().batchInsert(cacheDOS);\n            }\n            LambdaQueryWrapper<TableCacheDO> q = new LambdaQueryWrapper();\n            q.eq(TableCacheDO::getDataSourceId, dataSourceId);\n            q.lt(TableCacheDO::getVersion, version);\n            if (StringUtils.isNotBlank(databaseName)) {\n                q.eq(TableCacheDO::getDatabaseName, databaseName);\n            }\n            if (StringUtils.isNotBlank(schemaName)) {\n                q.eq(TableCacheDO::getSchemaName, schemaName);\n            }\n            getTableCacheMapper().delete(q);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n        return n;\n    }\n\n    private Long getLock(Long dataSourceId, String databaseName, String schemaName, TableCacheVersionDO versionDO) {\n        String key = getTableKey(dataSourceId, databaseName, schemaName);\n        if (versionDO == null) {\n            versionDO = new TableCacheVersionDO();\n            versionDO.setDatabaseName(databaseName);\n            versionDO.setSchemaName(schemaName);\n            versionDO.setDataSourceId(dataSourceId);\n            versionDO.setStatus(\"2\");\n            versionDO.setKey(key);\n            versionDO.setVersion(0L);\n            versionDO.setTableCount(0L);\n            try {\n                getVersionMapper().insert(versionDO);\n                return 0L;\n            } catch (Exception e) {\n                log.error(\"getLock error\", e);\n                return -1L;\n            }\n        } else {\n            long version = versionDO.getVersion() + 1;\n            LambdaQueryWrapper<TableCacheVersionDO> queryWrapper = new LambdaQueryWrapper();\n            queryWrapper.eq(TableCacheVersionDO::getId, versionDO.getId());\n            queryWrapper.eq(TableCacheVersionDO::getVersion, versionDO.getVersion());\n            versionDO.setVersion(version);\n            versionDO.setStatus(\"2\");\n            int n = getVersionMapper().update(versionDO, queryWrapper);\n            if (n == 1) {\n                return version;\n            } else {\n                return -1L;\n            }\n        }\n    }\n\n\n//    private String buildKey(Long dataSourceId, String databaseName, String schemaName) {\n//        StringBuilder stringBuilder = new StringBuilder(dataSourceId.toString());\n//        if (StringUtils.isNotBlank(databaseName)) {\n//            stringBuilder.append(\"_\").append(databaseName);\n//        }\n//        if (StringUtils.isNotBlank(schemaName)) {\n//            stringBuilder.append(\"_\").append(schemaName);\n//        }\n//        return stringBuilder.toString();\n//    }\n\n    private List<Table> pinTable(List<Table> list, TablePageQueryParam param) {\n        if (CollectionUtils.isEmpty(list)) {\n            return Lists.newArrayList();\n        }\n        PinTableParam pinTableParam = pinTableConverter.toPinTableParam(param);\n        pinTableParam.setUserId(ContextUtils.getUserId());\n        ListResult<String> listResult = pinService.queryPinTables(pinTableParam);\n        if (!listResult.success() || CollectionUtils.isEmpty(listResult.getData())) {\n            return list;\n        }\n        List<Table> tables = new ArrayList<>();\n        Map<String, Table> tableMap = list.stream().collect(Collectors.toMap(Table::getName, Function.identity()));\n        for (String tableName : listResult.getData()) {\n            Table table = tableMap.get(tableName);\n            if (table != null) {\n                table.setPinned(true);\n                tables.add(table);\n            }\n        }\n\n        for (Table table : list) {\n            if (table != null && !tables.contains(table)) {\n                tables.add(table);\n            }\n        }\n        return tables;\n    }\n\n    @Override\n    public List<TableColumn> queryColumns(TableQueryParam param) {\n        String tableColumnKey = getColumnKey(param.getDataSourceId(), param.getDatabaseName(), param.getSchemaName(), param.getTableName());\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        return CacheManage.getList(tableColumnKey, TableColumn.class,\n                (key) -> param.isRefresh(), (key) ->\n                        metaSchema.columns(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchemaName(), param.getTableName()));\n    }\n\n    @Override\n    public List<TableIndex> queryIndexes(TableQueryParam param) {\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        return metaSchema.indexes(Chat2DBContext.getConnection(), param.getDatabaseName(), param.getSchemaName(), param.getTableName());\n\n    }\n\n    @Override\n    public List<Type> queryTypes(TypeQueryParam param) {\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        return metaSchema.types(Chat2DBContext.getConnection());\n    }\n\n    @Override\n    public TableMeta queryTableMeta(TypeQueryParam param) {\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        TableMeta tableMeta = metaSchema.getTableMeta(null, null, null);\n        if (tableMeta != null) {\n            //filter primary key\n            List<IndexType> indexTypes = tableMeta.getIndexTypes();\n            if (CollectionUtils.isNotEmpty(indexTypes)) {\n                List<IndexType> types = indexTypes.stream().filter(indexType -> !\"Primary\".equalsIgnoreCase(indexType.getTypeName())).collect(Collectors.toList());\n                tableMeta.setIndexTypes(types);\n            }\n        }\n        return tableMeta;\n\n    }\n\n    @Override\n    public ActionResult saveTableVector(TableVectorParam param) {\n        if (checkTableVector(param).getData()) {\n            return ActionResult.isSuccess();\n        }\n        TableVectorMappingDO mappingDO = tableConverter.toTableVectorMappingDO(param);\n        mappingDO.setStatus(TableVectorEnum.SAVED.getCode());\n        getTableVectorMapper().insert(mappingDO);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<Boolean> checkTableVector(TableVectorParam param) {\n        LambdaQueryWrapper<TableVectorMappingDO> queryWrapper = new LambdaQueryWrapper();\n        queryWrapper.eq(TableVectorMappingDO::getApiKey, param.getApiKey());\n        queryWrapper.eq(TableVectorMappingDO::getDataSourceId, param.getDataSourceId());\n        queryWrapper.eq(TableVectorMappingDO::getDatabase, param.getDatabase());\n        queryWrapper.eq(TableVectorMappingDO::getSchema, param.getSchema());\n        TableVectorMappingDO mappingDO = getTableVectorMapper().selectOne(queryWrapper);\n        if (Objects.nonNull(mappingDO) && TableVectorEnum.SAVED.getCode().equals(mappingDO.getStatus())) {\n            return DataResult.of(true);\n        }\n        return DataResult.of(false);\n    }\n\n    @Override\n    public DataResult<String> copyDmlSql(DmlSqlCopyParam param) {\n        List<TableColumn> columns = queryColumns(param);\n        SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();\n        Table table = Table.builder().name(param.getTableName()).columnList(columns).build();\n        String sql = sqlBuilder.getTableDmlSql(table, param.getType());\n        return DataResult.of(sql);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TaskServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.enums.DeletedTypeEnum;\nimport ai.chat2db.server.domain.api.enums.TaskStatusEnum;\nimport ai.chat2db.server.domain.api.model.Task;\nimport ai.chat2db.server.domain.api.param.TaskCreateParam;\nimport ai.chat2db.server.domain.api.param.TaskPageParam;\nimport ai.chat2db.server.domain.api.param.TaskUpdateParam;\nimport ai.chat2db.server.domain.api.service.TaskService;\nimport ai.chat2db.server.domain.core.converter.TaskConverter;\nimport ai.chat2db.server.domain.repository.MapperUtils;\nimport ai.chat2db.server.domain.repository.entity.TaskDO;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.google.common.collect.Lists;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class TaskServiceImpl implements TaskService {\n\n    /**\n     * task converter\n     */\n    @Autowired\n    private TaskConverter taskConverter;\n\n    @Override\n    public DataResult<Long> create(TaskCreateParam param) {\n        TaskDO taskDO = taskConverter.todo(param);\n        taskDO.setDeleted(DeletedTypeEnum.N.name());\n        taskDO.setTaskStatus(TaskStatusEnum.INIT.name());\n        MapperUtils.getTaskMapper().insert(taskDO);\n        return DataResult.of(taskDO.getId());\n    }\n\n    @Override\n    public ActionResult updateStatus(TaskUpdateParam param) {\n        TaskDO taskDO = new TaskDO();\n        taskDO.setId(param.getId());\n        taskDO.setTaskStatus(param.getTaskStatus());\n        taskDO.setContent(param.getContent());\n        taskDO.setDownloadUrl(param.getDownloadUrl());\n        MapperUtils.getTaskMapper().updateById(taskDO);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public PageResult<Task> page(TaskPageParam param) {\n        Page<TaskDO> page = new Page<>();\n        page.setCurrent(param.getPageNo());\n        page.setSize(param.getPageSize());\n        page.setOrders(Lists.newArrayList(OrderItem.desc(\"gmt_create\")));\n        IPage<TaskDO> iPage = MapperUtils.getTaskMapper().pageQuery(page, param.getUserId(), DeletedTypeEnum.N.name());\n        if (iPage != null) {\n            return PageResult.of(taskConverter.toModel(iPage.getRecords()), param);\n        }\n        return PageResult.empty(param.getPageNo(), param.getPageSize());\n    }\n\n    @Override\n    public DataResult<Task> get(Long id) {\n        TaskDO task = MapperUtils.getTaskMapper().selectById(id);\n        return DataResult.of(taskConverter.toModel(task));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TeamServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.model.Team;\nimport ai.chat2db.server.domain.api.param.team.TeamCreateParam;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.TeamSelector;\nimport ai.chat2db.server.domain.api.param.team.TeamUpdateParam;\nimport ai.chat2db.server.domain.api.service.TeamService;\nimport ai.chat2db.server.domain.core.converter.TeamConverter;\nimport ai.chat2db.server.domain.core.converter.UserConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.DataSourceAccessDO;\nimport ai.chat2db.server.domain.repository.entity.TeamDO;\nimport ai.chat2db.server.domain.repository.entity.TeamUserDO;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceAccessMapper;\nimport ai.chat2db.server.domain.repository.mapper.TeamMapper;\nimport ai.chat2db.server.domain.repository.mapper.TeamUserMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.exception.DataAlreadyExistsBusinessException;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.tools.common.model.EasyLambdaQueryWrapper;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Service;\n\n/**\n * team\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Service\npublic class TeamServiceImpl implements TeamService {\n\n    private TeamMapper getTeamMapper() {\n        return Dbutils.getMapper(TeamMapper.class);\n    }\n\n    private TeamUserMapper getTeamUserMapper() {\n        return Dbutils.getMapper(TeamUserMapper.class);\n    }\n\n    private DataSourceAccessMapper getDataSourceAccessMapper() {\n        return Dbutils.getMapper(DataSourceAccessMapper.class);\n    }\n    @Resource\n    private TeamConverter teamConverter;\n    @Resource\n    private UserConverter userConverter;\n\n    @Override\n    public ListResult<Team> listQuery(List<Long> idList) {\n        if (CollectionUtils.isEmpty(idList)) {\n            return ListResult.empty();\n        }\n        LambdaQueryWrapper<TeamDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.in(TeamDO::getId, idList);\n        List<TeamDO> dataList = getTeamMapper().selectList(queryWrapper);\n        List<Team> list = teamConverter.do2dto(dataList);\n        return ListResult.of(list);\n    }\n\n    @Override\n    public PageResult<Team> pageQuery(TeamPageQueryParam param, TeamSelector selector) {\n        EasyLambdaQueryWrapper<TeamDO> queryWrapper = new EasyLambdaQueryWrapper<>();\n        if (StringUtils.isNotBlank(param.getSearchKey())) {\n            queryWrapper.and(wrapper -> wrapper.like(TeamDO::getCode, \"%\" + param.getSearchKey() + \"%\")\n                .or()\n                .like(TeamDO::getName, \"%\" + param.getSearchKey() + \"%\"));\n        }\n        Page<TeamDO> page = new Page<>(param.getPageNo(), param.getPageSize());\n        page.setSearchCount(param.getEnableReturnCount());\n        queryWrapper.orderBy(param.getOrderByList());\n        IPage<TeamDO> iPage = getTeamMapper().selectPage(page, queryWrapper);\n        List<Team> list = teamConverter.do2dto(iPage.getRecords());\n\n        fillData(list, selector);\n\n        return PageResult.of(list, iPage.getTotal(), param);\n    }\n\n    @Override\n    public DataResult<Long> create(TeamCreateParam param) {\n        LambdaQueryWrapper<TeamDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(TeamDO::getCode, param.getCode());\n        Page<TeamDO> page = new Page<>(1, 1);\n        page.setSearchCount(false);\n        IPage<TeamDO> iPage = getTeamMapper().selectPage(page, queryWrapper);\n        if (CollectionUtils.isNotEmpty(iPage.getRecords())) {\n            throw new DataAlreadyExistsBusinessException(\"code\", param.getCode());\n        }\n        if (RoleCodeEnum.DESKTOP.getCode().equals(param.getRoleCode())) {\n            throw new ParamBusinessException(\"roleCode\");\n        }\n\n        TeamDO data = teamConverter.param2do(param, ContextUtils.getUserId());\n        getTeamMapper().insert(data);\n        return DataResult.of(data.getId());\n    }\n\n    @Override\n    public DataResult<Long> update(TeamUpdateParam param) {\n        TeamDO data = teamConverter.param2do(param, ContextUtils.getUserId());\n        getTeamMapper().updateById(data);\n        return DataResult.of(data.getId());\n    }\n\n    @Override\n    public ActionResult delete(Long id) {\n        getTeamMapper().deleteById(id);\n\n        LambdaQueryWrapper<TeamUserDO> teamUserQueryWrapper = new LambdaQueryWrapper<>();\n        teamUserQueryWrapper.eq(TeamUserDO::getTeamId, id);\n        getTeamUserMapper().delete(teamUserQueryWrapper);\n\n        LambdaQueryWrapper<DataSourceAccessDO>  dataSourceAccessQueryWrapper = new LambdaQueryWrapper<>();\n        dataSourceAccessQueryWrapper.eq(DataSourceAccessDO::getAccessObjectId, id)\n            .eq(DataSourceAccessDO::getAccessObjectType, AccessObjectTypeEnum.TEAM.getCode())\n        ;\n        getDataSourceAccessMapper().delete(dataSourceAccessQueryWrapper);\n        return ActionResult.isSuccess();\n    }\n\n    private void fillData(List<Team> list, TeamSelector selector) {\n        if (CollectionUtils.isEmpty(list) || selector == null) {\n            return;\n        }\n        fillUser(list, selector);\n    }\n\n    private void fillUser(List<Team> list, TeamSelector selector) {\n        if (BooleanUtils.isNotTrue(selector.getModifiedUser())) {\n            return;\n        }\n        userConverter.fillDetail(EasyCollectionUtils.toList(list, Team::getModifiedUser));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TeamUserServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.TeamUser;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserComprehensivePageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserCreatParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserSelector;\nimport ai.chat2db.server.domain.api.service.TeamUserService;\nimport ai.chat2db.server.domain.core.converter.TeamConverter;\nimport ai.chat2db.server.domain.core.converter.TeamUserConverter;\nimport ai.chat2db.server.domain.core.converter.UserConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.TeamUserDO;\nimport ai.chat2db.server.domain.repository.mapper.TeamUserCustomMapper;\nimport ai.chat2db.server.domain.repository.mapper.TeamUserMapper;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.springframework.stereotype.Service;\n\n/**\n * Team User\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Service\npublic class TeamUserServiceImpl implements TeamUserService {\n\n    @Resource\n    private TeamUserConverter teamUserConverter;\n\n\n    private TeamUserCustomMapper getTeamUserCustomMapper() {\n        return Dbutils.getMapper(TeamUserCustomMapper.class);\n    }\n\n    private TeamUserMapper getTeamUserMapper() {\n        return Dbutils.getMapper(TeamUserMapper.class);\n    }\n    @Resource\n    private UserConverter userConverter;\n    @Resource\n    private TeamConverter teamConverter;\n\n    @Override\n    public PageResult<TeamUser> pageQuery(TeamUserPageQueryParam param, TeamUserSelector selector) {\n        LambdaQueryWrapper<TeamUserDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.eq(TeamUserDO::getTeamId, param.getTeamId())\n            .eq(TeamUserDO::getUserId, param.getUserId())\n        ;\n\n        Page<TeamUserDO> page = new Page<>(param.getPageNo(), param.getPageSize());\n        page.setSearchCount(param.getEnableReturnCount());\n        IPage<TeamUserDO> iPage = getTeamUserMapper().selectPage(page, queryWrapper);\n\n        List<TeamUser> list = teamUserConverter.do2dto(iPage.getRecords());\n\n        fillData(list, selector);\n\n        return PageResult.of(list, iPage.getTotal(), param);\n    }\n\n    @Override\n    public PageResult<TeamUser> comprehensivePageQuery(TeamUserComprehensivePageQueryParam param,\n        TeamUserSelector selector) {\n        Page<TeamUserDO> page = new Page<>(param.getPageNo(), param.getPageSize());\n        page.setSearchCount(param.getEnableReturnCount());\n        IPage<TeamUserDO> iPage = getTeamUserCustomMapper().comprehensivePageQuery(page, param.getTeamId(),\n            param.getUserId(), param.getTeamSearchKey(), param.getUserSearchKey());\n\n        List<TeamUser> list = teamUserConverter.do2dto(iPage.getRecords());\n\n        fillData(list, selector);\n\n        return PageResult.of(list, iPage.getTotal(), param);\n    }\n\n    @Override\n    public DataResult<Long> create(TeamUserCreatParam param) {\n        TeamUserDO data = teamUserConverter.param2do(param, ContextUtils.getUserId());\n\n        getTeamUserMapper().insert(data);\n        return DataResult.of(data.getId());\n    }\n\n    @Override\n    public ActionResult delete(Long id) {\n        getTeamUserMapper().deleteById(id);\n        return ActionResult.isSuccess();\n    }\n\n    private void fillData(List<TeamUser> list, TeamUserSelector selector) {\n        if (CollectionUtils.isEmpty(list) || selector == null) {\n            return;\n        }\n\n        fillUser(list, selector);\n\n        fillTeam(list, selector);\n    }\n\n    private void fillUser(List<TeamUser> list, TeamUserSelector selector) {\n        if (BooleanUtils.isNotTrue(selector.getUser())) {\n            return;\n        }\n        userConverter.fillDetail(EasyCollectionUtils.toList(list, TeamUser::getUser));\n    }\n\n    private void fillTeam(List<TeamUser> list, TeamUserSelector selector) {\n        if (BooleanUtils.isNotTrue(selector.getTeam())) {\n            return;\n        }\n        teamConverter.fillDetail(EasyCollectionUtils.toList(list, TeamUser::getTeam));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TriggerServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.service.TriggerService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Trigger;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class TriggerServiceImpl implements TriggerService {\n    @Override\n    public ListResult<Trigger> triggers(String databaseName, String schemaName) {\n        return ListResult.of(Chat2DBContext.getMetaData().triggers(Chat2DBContext.getConnection(),databaseName, schemaName));\n    }\n\n    @Override\n    public DataResult<Trigger> detail(String databaseName, String schemaName, String triggerName) {\n        return DataResult.of(Chat2DBContext.getMetaData().trigger(Chat2DBContext.getConnection(), databaseName, schemaName, triggerName));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/UserServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.param.user.UserCreateParam;\nimport ai.chat2db.server.domain.api.param.user.UserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.user.UserSelector;\nimport ai.chat2db.server.domain.api.param.user.UserUpdateParam;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.domain.core.converter.UserConverter;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.DataSourceAccessDO;\nimport ai.chat2db.server.domain.repository.entity.DbhubUserDO;\nimport ai.chat2db.server.domain.repository.entity.TeamUserDO;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceAccessMapper;\nimport ai.chat2db.server.domain.repository.mapper.DbhubUserMapper;\nimport ai.chat2db.server.domain.repository.mapper.TeamUserCustomMapper;\nimport ai.chat2db.server.domain.repository.mapper.TeamUserMapper;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.exception.DataAlreadyExistsBusinessException;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.tools.common.model.EasyLambdaQueryWrapper;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport jakarta.annotation.Resource;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Service;\n\n/**\n * User service\n *\n * @author Shi Yi\n */\n@Service\npublic class UserServiceImpl implements UserService {\n\n\n    private DbhubUserMapper getDbhubUserMapper() {\n        return Dbutils.getMapper(DbhubUserMapper.class);\n    }\n    @Resource\n    private UserConverter userConverter;\n\n    private TeamUserMapper getTeamUserMapper() {\n        return Dbutils.getMapper(TeamUserMapper.class);\n    }\n    private DataSourceAccessMapper getDataSourceAccessMapper() {\n        return Dbutils.getMapper(DataSourceAccessMapper.class);\n    }\n\n    @Override\n    public DataResult<User> query(Long id) {\n        return DataResult.of(userConverter.do2dto(getDbhubUserMapper().selectById(id)));\n    }\n\n    @Override\n    public DataResult<User> query(String userName) {\n        LambdaQueryWrapper<DbhubUserDO> query = new LambdaQueryWrapper<>();\n        if (Objects.nonNull(userName)) {\n            query.eq(DbhubUserDO::getUserName, userName);\n        }\n        DbhubUserDO dbhubUserDO = getDbhubUserMapper().selectOne(query);\n        return DataResult.of(userConverter.do2dto(dbhubUserDO));\n    }\n\n    @Override\n    public ListResult<User> listQuery(List<Long> idList) {\n        if (CollectionUtils.isEmpty(idList)) {\n            return ListResult.empty();\n        }\n        LambdaQueryWrapper<DbhubUserDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.in(DbhubUserDO::getId, idList);\n        List<DbhubUserDO> dataList = getDbhubUserMapper().selectList(queryWrapper);\n        List<User> list = userConverter.do2dto(dataList);\n        return ListResult.of(list);\n    }\n\n    @Override\n    public PageResult<User> pageQuery(UserPageQueryParam param, UserSelector selector) {\n        EasyLambdaQueryWrapper<DbhubUserDO> queryWrapper = new EasyLambdaQueryWrapper<>();\n        if (StringUtils.isNotBlank(param.getSearchKey())) {\n            queryWrapper.and(wrapper -> wrapper.like(DbhubUserDO::getUserName, \"%\" + param.getSearchKey() + \"%\")\n                .or()\n                .like(DbhubUserDO::getNickName, \"%\" + param.getSearchKey() + \"%\")\n                .or()\n                .like(DbhubUserDO::getEmail, \"%\" + param.getSearchKey() + \"%\"));\n        }\n        // Default not to query desktop accounts\n        queryWrapper.ne(DbhubUserDO::getId, RoleCodeEnum.DESKTOP.getDefaultUserId());\n        queryWrapper.orderBy(param.getOrderByList());\n        Page<DbhubUserDO> page = new Page<>(param.getPageNo(), param.getPageSize());\n        page.setSearchCount(param.getEnableReturnCount());\n        IPage<DbhubUserDO> iPage = getDbhubUserMapper().selectPage(page, queryWrapper);\n        List<User> list = userConverter.do2dto(iPage.getRecords());\n\n        fillData(list, selector);\n        return PageResult.of(list, iPage.getTotal(), param);\n    }\n\n    @Override\n    public DataResult<Long> update(UserUpdateParam param) {\n        if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(param.getId())) {\n            throw new BusinessException(\"user.canNotOperateSystemAccount\");\n        }\n        if (RoleCodeEnum.DESKTOP.getCode().equals(param.getRoleCode())) {\n            throw new ParamBusinessException(\"roleCode\");\n        }\n\n        DbhubUserDO data = userConverter.param2do(param, ContextUtils.getUserId());\n        if (Objects.nonNull(data.getPassword())) {\n            String bcryptPassword = DigestUtil.bcrypt(data.getPassword());\n            data.setPassword(bcryptPassword);\n        }\n\n        if (RoleCodeEnum.ADMIN.getDefaultUserId().equals(param.getId())) {\n            data.setStatus(null);\n            data.setEmail(null);\n            data.setUserName(null);\n            data.setRoleCode(null);\n        }\n        getDbhubUserMapper().updateById(data);\n        return DataResult.of(data.getId());\n    }\n\n    @Override\n    public ActionResult delete(Long id) {\n        if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(id) || RoleCodeEnum.ADMIN.getDefaultUserId().equals(id)) {\n            throw new BusinessException(\"user.canNotOperateSystemAccount\");\n        }\n        getDbhubUserMapper().deleteById(id);\n\n        LambdaQueryWrapper<TeamUserDO> teamUserQueryWrapper = new LambdaQueryWrapper<>();\n        teamUserQueryWrapper.eq(TeamUserDO::getUserId, id);\n        getTeamUserMapper().delete(teamUserQueryWrapper);\n\n        LambdaQueryWrapper<DataSourceAccessDO>  dataSourceAccessQueryWrapper = new LambdaQueryWrapper<>();\n        dataSourceAccessQueryWrapper.eq(DataSourceAccessDO::getAccessObjectId, id)\n            .eq(DataSourceAccessDO::getAccessObjectType, AccessObjectTypeEnum.USER.getCode())\n        ;\n        getDataSourceAccessMapper().delete(dataSourceAccessQueryWrapper);\n        return ActionResult.isSuccess();\n    }\n\n    @Override\n    public DataResult<Long> create(UserCreateParam param) {\n        LambdaQueryWrapper<DbhubUserDO> queryWrapper = new LambdaQueryWrapper<>();\n        queryWrapper.and(wrapper -> wrapper.eq(DbhubUserDO::getUserName, param.getUserName())\n            .or()\n            .eq(DbhubUserDO::getEmail, param.getEmail()));\n        Page<DbhubUserDO> page = new Page<>(1, 1);\n        page.setSearchCount(false);\n        IPage<DbhubUserDO> iPage = getDbhubUserMapper().selectPage(page, queryWrapper);\n        if (CollectionUtils.isNotEmpty(iPage.getRecords())) {\n            throw new DataAlreadyExistsBusinessException(\"userName or email\",\n                param.getUserName() + \" or \" + param.getEmail());\n        }\n        if (RoleCodeEnum.DESKTOP.getCode().equals(param.getRoleCode())) {\n            throw new ParamBusinessException(\"roleCode\");\n        }\n\n        DbhubUserDO data = userConverter.param2do(param, ContextUtils.getUserId());\n        String bcryptPassword = DigestUtil.bcrypt(data.getPassword());\n        data.setPassword(bcryptPassword);\n        getDbhubUserMapper().insert(data);\n        return DataResult.of(data.getId());\n    }\n\n    private void fillData(List<User> list, UserSelector selector) {\n        if (CollectionUtils.isEmpty(list) || selector == null) {\n            return;\n        }\n        fillUser(list, selector);\n    }\n\n    private void fillUser(List<User> list, UserSelector selector) {\n        if (BooleanUtils.isNotTrue(selector.getModifiedUser())) {\n            return;\n        }\n        userConverter.fillDetail(EasyCollectionUtils.toList(list, User::getModifiedUser));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/ViewServiceImpl.java",
    "content": "package ai.chat2db.server.domain.core.impl;\n\nimport ai.chat2db.server.domain.api.service.ViewService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class ViewServiceImpl implements ViewService {\n\n    @Override\n    public ListResult<Table> views(String databaseName, String schemaName) {\n        return ListResult.of(Chat2DBContext.getMetaData().views(Chat2DBContext.getConnection(),databaseName, schemaName));\n    }\n\n    @Override\n    public DataResult<Table> detail(String databaseName, String schemaName, String tableName) {\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        Table table = metaSchema.view(Chat2DBContext.getConnection(), databaseName, schemaName, tableName);\n        return DataResult.of(table);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/notification/BaseWebhookSender.java",
    "content": "package ai.chat2db.server.domain.core.notification;\n\nimport ai.chat2db.server.domain.api.param.message.MessageCreateParam;\nimport ai.chat2db.server.domain.api.service.WebhookSender;\nimport ai.chat2db.server.domain.core.enums.ExternalNotificationTypeEnum;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class BaseWebhookSender implements WebhookSender {\n\n    /**\n     * Sends a message through the specified webhook platform.\n     *\n     * @param param The parameter object containing message details and platform type.\n     * @throws IllegalArgumentException if the provided param is null or invalid.\n     * @throws RuntimeException if an error occurs while attempting to send the message.\n     */\n    public void sendMessage(MessageCreateParam param) throws IllegalArgumentException {\n        // Validate the input parameter to ensure it's not null and meets the necessary criteria.\n        if (param == null || param.getPlatformType() == null) {\n            throw new IllegalArgumentException(\"MessageCreateParam or its platform type cannot be null.\");\n        }\n\n        try {\n            // Attempt to retrieve the appropriate WebhookSender based on the platform type.\n            ExternalNotificationTypeEnum extern = ExternalNotificationTypeEnum.getByName(param.getPlatformType());\n            WebhookSender sender = extern.getWebhookSender(param.getPlatformType());\n\n            // Guard clause for null sender. Ideally, getWebhookSender should prevent this, but it's good to be cautious.\n            if (sender == null) {\n                throw new RuntimeException(\"Failed to retrieve WebhookSender for platform type: \" + param.getPlatformType());\n            }\n\n            // Send the message. Any exceptions thrown by sendMessage should be caught and handled here.\n            sender.sendMessage(param);\n        } catch (Exception e) {\n            // Wrap and re-throw any runtime exceptions as a checked exception specific to webhook sending.\n            throw new RuntimeException(\"An error occurred while sending the message.\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/notification/DingTalkWebhookSender.java",
    "content": "package ai.chat2db.server.domain.core.notification;\n\nimport ai.chat2db.server.domain.api.param.message.MessageCreateParam;\nimport okhttp3.*;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.apache.commons.codec.binary.Base64;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Juechen\n * @version : DingTalkWebhookSender.java\n */\n@Service\npublic class DingTalkWebhookSender extends BaseWebhookSender {\n\n    private static final String HMAC_SHA256_ALGORITHM = \"HmacSHA256\";\n\n    @Override\n    public void sendMessage(MessageCreateParam param) {\n        try {\n            OkHttpClient client = new OkHttpClient();\n            String secret = param.getSecretKey();\n            Long timestamp = System.currentTimeMillis();\n\n            String sign = generateSign(secret, timestamp);\n\n            String webhookUrl = param.getServiceUrl() + \"&sign=\" + sign + \"&timestamp=\" + timestamp;\n\n\n            String payload = \"{\\\"msgtype\\\": \\\"text\\\",\\\"text\\\": {\\\"content\\\": \\\"\" + param.getTextTemplate() + \"\\\"}}\";\n            RequestBody requestBody = RequestBody.create(payload, MediaType.parse(\"application/json; charset=utf-8\"));\n\n            Request request = new Request.Builder()\n                    .url(webhookUrl)\n                    .post(requestBody)\n                    .header(\"Content-Type\", \"application/json\")\n                    .build();\n\n\n            Response response = client.newCall(request).execute();\n            if (!response.isSuccessful()) {\n                throw new RuntimeException(\"Failed to send message: \" + response.code());\n            }\n            System.out.println(response.body().string());\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(e);\n        } catch (InvalidKeyException e) {\n            throw new RuntimeException(e);\n        }\n\n    }\n\n    private static String generateSign(String secret, Long timestamp) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {\n        String stringToSign = timestamp + \"\\n\" + secret;\n        Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);\n        mac.init(new SecretKeySpec(secret.getBytes(\"UTF-8\"), \"HmacSHA256\"));\n        byte[] signData = mac.doFinal(stringToSign.getBytes(\"UTF-8\"));\n        return URLEncoder.encode(new String(Base64.encodeBase64(signData)), \"UTF-8\");\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/notification/LarkWebhookSender.java",
    "content": "package ai.chat2db.server.domain.core.notification;\n\nimport ai.chat2db.server.domain.api.param.message.MessageCreateParam;\nimport okhttp3.*;\n\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\n\nimport org.apache.commons.codec.binary.Base64;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Juechen\n * @version : LarkWebhookSender.java\n */\n@Service\npublic class LarkWebhookSender extends BaseWebhookSender {\n\n    private static final String HMAC_SHA256_ALGORITHM = \"HmacSHA256\";\n\n    @Override\n    public void sendMessage(MessageCreateParam param) {\n        try {\n            OkHttpClient client = new OkHttpClient();\n            String webhookUrl = param.getServiceUrl();\n            String secret = param.getSecretKey();\n            int timestamp = (int) (System.currentTimeMillis() / 1000);\n\n            String signature = GenSign(secret, timestamp);\n\n            String payload = \"{\\\"timestamp\\\": \\\"\" + timestamp\n                    + \"\\\",\\\"sign\\\": \\\"\" + signature\n                    + \"\\\",\\\"msg_type\\\":\\\"text\\\",\\\"content\\\":{\\\"text\\\":\\\"\"+ param.getTextTemplate() +\"\\\"}}\";\n            RequestBody body = RequestBody.create(payload, MediaType.parse(\"application/json; charset=utf-8\"));\n\n\n            Request request = new Request.Builder()\n                    .url(webhookUrl)\n                    .post(body)\n                    .addHeader(\"Content-Type\", \"application/json\")\n                    .build();\n\n            Response response = client.newCall(request).execute();\n            if (!response.isSuccessful()) {\n                throw new RuntimeException(\"Failed to send message: \" + response.code());\n            }\n            System.out.println(response.body().string());\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(e);\n        } catch (InvalidKeyException e) {\n            throw new RuntimeException(e);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n\n    }\n\n    private static String GenSign(String secret, int timestamp) throws NoSuchAlgorithmException, InvalidKeyException {\n        String stringToSign = timestamp + \"\\n\" + secret;\n        Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);\n        mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), HMAC_SHA256_ALGORITHM));\n        byte[] signData = mac.doFinal(new byte[]{});\n        return new String(Base64.encodeBase64(signData));\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/notification/WeComWebhookSender.java",
    "content": "package ai.chat2db.server.domain.core.notification;\n\nimport ai.chat2db.server.domain.api.param.message.MessageCreateParam;\nimport okhttp3.*;\nimport org.springframework.stereotype.Service;\n\nimport java.io.IOException;\n\n/**\n * @author Juechen\n * @version : WeComWebhookSender.java\n */\n@Service\npublic class WeComWebhookSender extends BaseWebhookSender {\n\n    @Override\n    public void sendMessage(MessageCreateParam param) {\n        try {\n            OkHttpClient client = new OkHttpClient();\n            String webhookUrl = param.getServiceUrl();\n            String text = param.getTextTemplate();\n\n            String payload = \"{\\\"msgtype\\\": \\\"text\\\",\\\"text\\\": {\\\"content\\\": \\\"\" + text + \"\\\"}}\";\n\n            RequestBody requestBody = RequestBody.create(payload, MediaType.parse(\"application/json; charset=utf-8\"));\n\n            Request request = new Request.Builder()\n                    .url(webhookUrl)\n                    .post(requestBody)\n                    .addHeader(\"Content-Type\", \"application/json\")\n                    .build();\n\n            Response response = client.newCall(request).execute();\n            if (!response.isSuccessful()) {\n                throw new RuntimeException(\"Failed to send message: \" + response.code());\n            }\n            System.out.println(response.body().string());\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/util/DesUtil.java",
    "content": "package ai.chat2db.server.domain.core.util;\n\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.security.Key;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.security.spec.AlgorithmParameterSpec;\nimport java.util.Base64;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.CipherInputStream;\nimport javax.crypto.CipherOutputStream;\nimport javax.crypto.KeyGenerator;\nimport javax.crypto.spec.IvParameterSpec;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.logging.log4j.util.Strings;\n\n/**\n * @author moji\n * @version DESUtil.java, v 0.1 December 26, 2022 19:54 moji Exp $\n * @date 2022/12/26\n */\npublic class DesUtil {\n\n    /**\n     * CFB\n     */\n    public static final String CFB = \"CFB\";\n\n    /**\n     * OFB\n     */\n    public static final String OFB = \"OFB\";\n\n    /**\n     * CBC\n     */\n    public static final String CBC = \"CBC\";\n\n    /**\n     * iv vector\n     */\n    private static final byte[] DESIV = { (byte) 0xCE, (byte) 0x35, (byte) 0x5,\n        (byte) 0xD, (byte) 0x98, (byte) 0x91, (byte) 0x8, (byte) 0xA };\n\n    /**\n     * AlgorithmParameterSpec\n     */\n    private static AlgorithmParameterSpec IV = null;\n\n    /**\n     * SHA1PRNG\n     */\n    private static final String SHA1PRNG = \"SHA1PRNG\";\n\n    /**\n     * DES default mode\n     */\n    private static final String DES = \"DES\";\n\n    /**\n     * CBC encryption mode\n     */\n    private static final String DES_CBC_PKCS5PADDING = \"DES/CBC/PKCS5Padding\";\n\n    /**\n     * OFB encryption mode\n     */\n    private static final String DES_OFB_PKCS5PADDING = \"DES/OFB/PKCS5Padding\";\n\n    /**\n     * CFB encryption mode\n     */\n    private static final String DES_CFB_PKCS5_PADDING = \"DES/CFB/PKCS5Padding\";\n\n    /**\n     * encryption mode\n     */\n    private static final int ENCRYPT_MODE = 1;\n\n    /**\n     * des key\n     */\n    public static final String DES_KEY = \"dbhub\";\n\n    /**\n     * Decryption mode\n     */\n    private static final int DECRYPT_MODE = 2;\n\n    /**\n     * private key\n     */\n    private Key key;\n\n    public DesUtil(String str) {\n        getKey(str);\n    }\n\n    public Key getKey() {\n        return key;\n    }\n\n    public void setKey(Key key) {\n        this.key = key;\n    }\n\n    /**\n     * Get key through private key\n     * @param secretKey private key\n     * @author sucb\n     * @date 2017年2月28日下午1:17:58\n     */\n    public void getKey(String secretKey) {\n        try {\n            SecureRandom secureRandom = SecureRandom.getInstance(SHA1PRNG);\n            secureRandom.setSeed(secretKey.getBytes());\n            KeyGenerator generator = null;\n            try {\n                generator = KeyGenerator.getInstance(DES);\n            } catch (NoSuchAlgorithmException e) {\n            }\n            generator.init(secureRandom);\n            IV = new IvParameterSpec(DESIV);\n\n\n            this.key = generator.generateKey();\n            generator = null;\n\n        } catch (Exception e) {\n            throw new RuntimeException(\"Error in getKey(String secretKey), Cause: \" + e);\n        }\n    }\n\n    /**\n     * String des encryption\n     * @param data String that needs to be encrypted\n     * @param encryptType encryption mode (ECB/CBC/OFB/CFB)\n     * @return Encryption result\n     * @throws Exception exception\n     * @author sucb\n     * @date March 2, 2017 7:47:37 pm\n     */\n    public String encrypt(String data, String encryptType) throws Exception {\n        Cipher cipher = getPattern(encryptType, ENCRYPT_MODE);\n        byte[] pasByte = cipher.doFinal(data.getBytes(\"UTF-8\"));\n        return Base64.getEncoder().encodeToString(pasByte);\n    }\n\n    /**\n     * String decryption\n     * @param data The string that needs to be decrypted\n     * @param decryptType  Decryption mode (ECB/CBC/OFB/CFB)\n     * @return Decryption result\n     * @throws Exception exception\n     * @author sucb\n     * @date March 2, 2017 7:48:21 pm\n     */\n    public String decrypt(String data, String decryptType) throws Exception {\n        if (StringUtils.isBlank(data)) {\n            return Strings.EMPTY;\n        }\n        Cipher cipher = getPattern(decryptType, DECRYPT_MODE);\n        byte[] pasByte = cipher.doFinal(Base64.getDecoder().decode(data));\n        return new String(pasByte, \"UTF-8\");\n    }\n\n    /**\n     * Initialize cipher\n     * @param type  Encryption/decryption mode (ECB/CBC/OFB/CFB)\n     * @param cipherMode cipher working mode 1: encryption; 2: decryption\n     * @return cipher\n     * @throws Exception exception\n     * @author sucb\n     * @date March 2, 2017 7:49:16 pm\n     */\n    private Cipher getPattern(String type, int cipherMode) throws Exception {\n        Cipher cipher;\n        switch (type){\n            case CBC :\n                cipher = Cipher.getInstance(DES_CBC_PKCS5PADDING);\n                cipher.init(cipherMode, key, IV);\n                break;\n            case OFB :\n                cipher = Cipher.getInstance(DES_OFB_PKCS5PADDING);\n                cipher.init(cipherMode, key, IV);\n                break;\n            case CFB :\n                cipher = Cipher.getInstance(DES_CFB_PKCS5_PADDING);\n                cipher.init(cipherMode, key, IV);\n                break;\n            default :\n                cipher = Cipher.getInstance(DES);\n                cipher.init(cipherMode, key);\n                break;\n        }\n        return cipher;\n    }\n\n    /**\n     * The file file is encrypted and saved in the target file destFile.\n     * @param file The file to be encrypted such as c:/test/file.txt\n     * @param destFile The name of the file stored after encryption, such as c:/ encrypted file .txt\n     * @param encryptType encryption mode (ECB/CBC/OFB/CFB)\n     * @return Encryption result 0: Abnormal 1: Encryption successful; 5: File to be encrypted not found\n     * @author sucb\n     * @date March 2, 2017 7:56:08 pm\n     */\n    public int encryptFile(String file, String destFile, String encryptType) {\n        int result = 0;\n        try {\n            Cipher cipher = getPattern(encryptType, ENCRYPT_MODE);\n            InputStream is = new FileInputStream(file);\n            OutputStream out = new FileOutputStream(destFile);\n            CipherInputStream cis = new CipherInputStream(is, cipher);\n            byte[] buffer = new byte[1024];\n            int r;\n            while ((r = cis.read(buffer)) > 0) {\n                out.write(buffer, 0, r);\n            }\n            cis.close();\n            is.close();\n            out.close();\n            result = 1;\n        } catch (FileNotFoundException e) {\n            result = 5;\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n        return result;\n    }\n\n    /**\n     * The file file is decrypted and saved in the target file destFile.\n     * @param file The file to be decrypted such as c:/test/file.txt\n     * @param destFile The name of the file stored after decryption, such as c:/ decrypted file .txt\n     * @param decryptType Decryption mode (ECB/CBC/OFB/CFB)\n     * @return Decryption result 0: decryption abnormal; 1: decryption normal; 5: file to be decrypted not found\n     * @author sucb\n     * @date March 2, 2017 7:58:56 pm\n     */\n    public int decryptFile(String file, String destFile, String decryptType) {\n        int result = 0;\n        try {\n            Cipher cipher = getPattern(decryptType, DECRYPT_MODE);\n            InputStream is = new FileInputStream(file);\n            OutputStream out = new FileOutputStream(destFile);\n            CipherOutputStream cos = new CipherOutputStream(out, cipher);\n            byte[] buffer = new byte[1024];\n            int r;\n            while ((r = is.read(buffer)) >= 0) {\n                cos.write(buffer, 0, r);\n            }\n            cos.close();\n            out.close();\n            is.close();\n            result = 1;\n        }catch (FileNotFoundException e) {\n            result = 5;\n        }  catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/util/H2Functions.java",
    "content": "package ai.chat2db.server.domain.core.util;\n\npublic class H2Functions {\n    public static String keyGeneratorFunction(String tableName) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/util/H2Triggers.java",
    "content": "package ai.chat2db.server.domain.core.util;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Arrays;\n\nimport org.h2.api.Trigger;\n\npublic class H2Triggers implements Trigger {\n\n    @Override\n    public void init(Connection conn, String schemaName, String triggerName,\n        String tableName, boolean before, int type) throws SQLException {\n        // Initialization logic, if needed.\n    }\n\n    @Override\n    public void fire(Connection conn, Object[] oldRow, Object[] newRow)\n        throws SQLException {\n        // This method is called when the trigger is executed.\n        // In this example, let's simply print the new values when a row is inserted.\n        if (newRow != null) {\n//            System.out.println(\"New Row Inserted: \" + Arrays.toString(newRow));\n        }\n    }\n\n    @Override\n    public void close() throws SQLException {\n        // Cleanup logic, if needed.\n    }\n\n    @Override\n    public void remove() throws SQLException {\n        // Logic to execute when the trigger is dropped/removed.\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/util/MetaNameUtils.java",
    "content": "package ai.chat2db.server.domain.core.util;\n\n\nimport org.apache.commons.lang3.StringUtils;\n\npublic class MetaNameUtils {\n\n    public static String getMetaName(String tableName) {\n        if(StringUtils.isBlank(tableName)){\n            return tableName;\n        }\n        if(tableName.startsWith(\"`\") && tableName.endsWith(\"`\")){\n            return tableName.substring(1,tableName.length()-1);\n        }\n        if(tableName.startsWith(\"\\\"\") && tableName.endsWith(\"\\\"\")){\n            return tableName.substring(1,tableName.length()-1);\n        }\n        if(tableName.startsWith(\"'\") && tableName.endsWith(\"'\")){\n            return tableName.substring(1,tableName.length()-1);\n        }\n        if(tableName.startsWith(\"[\") && tableName.endsWith(\"]\")){\n            return tableName.substring(1,tableName.length()-1);\n        }\n        return tableName;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/util/PermissionUtils.java",
    "content": "package ai.chat2db.server.domain.core.util;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.tools.common.exception.PermissionDeniedBusinessException;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\n\n/**\n * Permission Utils\n *\n * @author Jiaju Zhuang\n */\npublic class PermissionUtils {\n\n    /**\n     * Verify whether the currently logged-in user has permission to operate on the current content\n     *\n     * @param createUserId The creator of the current content\n     */\n    public static void checkOperationPermission(Long createUserId) {\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        // Representative is desktop mode\n        if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(loginUser.getId())) {\n            if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(createUserId)) {\n                return;\n            } else {\n                throw new PermissionDeniedBusinessException();\n            }\n        }\n        // Administrators can edit anything\n        if (loginUser.getAdmin()) {\n            return;\n        }\n        // Not that administrators can only edit their own things\n        if (!loginUser.getId().equals(createUserId)) {\n            throw new PermissionDeniedBusinessException();\n        }\n    }\n\n    /**\n     * Verify whether you have query permission\n     *\n     * @param createUserId\n     * @return\n     */\n    public static boolean checkBaseQueryPermission(Long createUserId) {\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        // Representative is desktop mode\n        if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(loginUser.getId())) {\n            if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(createUserId)) {\n                return true;\n            } else {\n                throw new PermissionDeniedBusinessException();\n            }\n        }\n        // Administrators can edit anything\n        return loginUser.getAdmin();\n    }\n\n    /**\n     * Verify if it is an administrator\n     *\n     * @return\n     */\n    public static void checkDeskTopOrAdmin() {\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        // Representative is desktop mode\n        if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(loginUser.getId())) {\n            return;\n        }\n        if (loginUser.getAdmin()) {\n            return;\n        }\n        throw new PermissionDeniedBusinessException();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-domain</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>chat2db-server-domain-repository</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-tools-common</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.zaxxer</groupId>\n            <artifactId>HikariCP</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.flywaydb</groupId>\n            <artifactId>flyway-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.flywaydb</groupId>\n            <artifactId>flyway-mysql</artifactId>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/Dbutils.java",
    "content": "package ai.chat2db.server.domain.repository;\n\nimport ai.chat2db.server.tools.common.model.ConfigJson;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;\nimport com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.zaxxer.hikari.HikariDataSource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.ibatis.builder.xml.XMLMapperBuilder;\nimport org.apache.ibatis.logging.slf4j.Slf4jImpl;\nimport org.apache.ibatis.mapping.Environment;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\nimport org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;\nimport org.flywaydb.core.Flyway;\n\nimport javax.sql.DataSource;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.JarURLConnection;\nimport java.net.URL;\nimport java.util.Enumeration;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\n@Slf4j\npublic class Dbutils {\n\n    private static final ThreadLocal<SqlSession> SQL_SESSION_THREAD_LOCAL = new ThreadLocal<>();\n\n    public static void init() {\n    }\n\n    public static void setSession() {\n        SqlSession session = sqlSessionFactory.openSession(true);\n        SQL_SESSION_THREAD_LOCAL.set(session);\n    }\n\n    public static void removeSession() {\n        SqlSession session = SQL_SESSION_THREAD_LOCAL.get();\n        if (session != null) {\n            session.close();\n        }\n        SQL_SESSION_THREAD_LOCAL.remove();\n    }\n\n    private static SqlSessionFactory sqlSessionFactory;\n\n    static {\n        try {\n            before();\n        } catch (IOException e) {\n            log.error(\"Dbutils error\", e);\n        }\n    }\n\n\n    private static void before() throws IOException {\n        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();\n        //This is the configuration object of mybatis-plus, which enhances the configuration of mybatis.\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        //This is the initial configuration, this part of the code will be added later\n        initConfiguration(configuration);\n        //This is the initialization connector, such as the paging plug-in of mybatis-plus\n        configuration.addInterceptor(initInterceptor());\n        //Configuration log implementation\n        configuration.setLogImpl(Slf4jImpl.class);\n        //Scan the package where the mapper interface is located\n        configuration.addMappers(\"ai.chat2db.server.domain.repository.mapper\");\n        //Globalconfig required to build mybatis-plus\n        GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);\n        //This parameter will automatically generate the basic method mapping that implements baseMapper.\n        globalConfig.setSqlInjector(new DefaultSqlInjector());\n        //Set up id generator\n        globalConfig.setIdentifierGenerator(new DefaultIdentifierGenerator());\n        //Set super class mapper\n        globalConfig.setSuperMapperClass(BaseMapper.class);\n        DataSource dataSource = initDataSource();\n        Environment environment = new Environment(\"1\", new JdbcTransactionFactory(), dataSource);\n        configuration.setEnvironment(environment);\n        //Set data source\n        registryMapperXml(configuration, \"mapper\");\n        //Build sqlSessionFactory\n        sqlSessionFactory = builder.build(configuration);\n\n        initFlyway(dataSource);\n        //create session\n\n    }\n\n    private static void initFlyway(DataSource dataSource) {\n        String currentVersion = ConfigUtils.getLocalVersion();\n        ConfigJson configJson = ConfigUtils.getConfig();\n        // Represents that the current version has been successfully launched\n        if (StringUtils.isNotBlank(currentVersion) && configJson != null && StringUtils.equals(currentVersion,\n                configJson.getLatestStartupSuccessVersion())) {\n            return;\n        }else {\n            Flyway flyway = Flyway.configure()\n                    .dataSource(dataSource)\n                    .locations(\"classpath:db/migration\")\n                    .load();\n            flyway.migrate();\n\n\n            configJson.setLatestStartupSuccessVersion(currentVersion);\n            ConfigUtils.setConfig(configJson);\n        }\n    }\n\n    /**\n     * Initial configuration\n     *\n     * @param configuration\n     */\n    private static void initConfiguration(MybatisConfiguration configuration) {\n        //Turn on camel case conversion\n        configuration.setMapUnderscoreToCamelCase(true);\n        //Configure adding data to automatically return the data primary key\n        configuration.setUseGeneratedKeys(true);\n    }\n\n    /**\n     * Initialize data source\n     *\n     * @return\n     */\n    private static DataSource initDataSource() {\n        HikariDataSource dataSource = new HikariDataSource();\n        String environment = StringUtils.defaultString(System.getProperty(\"spring.profiles.active\"), \"dev\");\n        if (\"dev\".equalsIgnoreCase(environment)) {\n            dataSource.setJdbcUrl(\"jdbc:h2:file:~/.chat2db/db/chat2db_dev;MODE=MYSQL\");\n        }else if (\"test\".equalsIgnoreCase(environment)) {\n            dataSource.setJdbcUrl(\"jdbc:h2:file:~/.chat2db/db/chat2db_test;MODE=MYSQL\");\n        }else {\n            dataSource.setJdbcUrl(\"jdbc:h2:~/.chat2db/db/chat2db;MODE=MYSQL;FILE_LOCK=NO\");\n        }\n        dataSource.setDriverClassName(\"org.h2.Driver\");\n        dataSource.setIdleTimeout(60000);\n        dataSource.setAutoCommit(true);\n        dataSource.setMaximumPoolSize(500);\n        dataSource.setMinimumIdle(1);\n        dataSource.setMaxLifetime(60000 * 10);\n        dataSource.setConnectionTestQuery(\"SELECT 1\");\n        return dataSource;\n\n    }\n\n    /**\n     * Initialize interceptor\n     *\n     * @return\n     */\n    private static Interceptor initInterceptor() {\n        //Create mybatis-plus plug-in object\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        //Build a pagination plugin\n        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();\n        paginationInnerInterceptor.setDbType(DbType.H2);\n        paginationInnerInterceptor.setOverflow(true);\n        paginationInnerInterceptor.setMaxLimit(2000L);\n        interceptor.addInnerInterceptor(paginationInnerInterceptor);\n        return interceptor;\n    }\n\n    /**\n     * Parse mapper.xml file\n     *\n     * @param configuration\n     * @param classPath\n     * @throws IOException\n     */\n    private static void registryMapperXml(MybatisConfiguration configuration, String classPath) throws IOException {\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        Enumeration<URL> mapper = contextClassLoader.getResources(classPath);\n        while (mapper.hasMoreElements()) {\n            URL url = mapper.nextElement();\n            if (url.getProtocol().equals(\"file\")) {\n                String path = url.getPath();\n                File file = new File(path);\n                File[] files = file.listFiles();\n                for (File f : files) {\n                    FileInputStream in = new FileInputStream(f);\n                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(in, configuration, f.getPath(), configuration.getSqlFragments());\n                    xmlMapperBuilder.parse();\n                    in.close();\n                }\n            } else {\n                JarURLConnection urlConnection = (JarURLConnection) url.openConnection();\n                JarFile jarFile = urlConnection.getJarFile();\n                Enumeration<JarEntry> entries = jarFile.entries();\n                while (entries.hasMoreElements()) {\n                    JarEntry jarEntry = entries.nextElement();\n                    if (jarEntry.getName().endsWith(\"Mapper.xml\")) {\n                        InputStream in = jarFile.getInputStream(jarEntry);\n                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(in, configuration, jarEntry.getName(), configuration.getSqlFragments());\n                        xmlMapperBuilder.parse();\n                        in.close();\n                    }\n                }\n            }\n        }\n    }\n\n    public static <T> T getMapper(Class<T> clazz) {\n        SqlSession session = SQL_SESSION_THREAD_LOCAL.get();\n        return session.getMapper(clazz);\n    }\n\n//    public static void main(String[] args) {\n//\n//        ExecutorService e = Executors.newCachedThreadPool();\n//        for (int i = 0; i < 20; i++) {\n//            e.execute(() -> {\n//                SqlSession session = sqlSessionFactory.openSession();\n//                DataSourceMapper mapper = session.getMapper(DataSourceMapper.class);\n//                DataSourceDO dataSourceDO = mapper.selectById(1);\n//                session.close();\n//                System.out.println(JSON.toJSONString(dataSourceDO));\n//            });\n//        }\n//\n//    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/MapperUtils.java",
    "content": "package ai.chat2db.server.domain.repository;\n\nimport ai.chat2db.server.domain.repository.mapper.TaskMapper;\n\npublic class MapperUtils {\n\n    public static TaskMapper getTaskMapper() {\n        return Dbutils.getMapper(TaskMapper.class);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/ChartDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Custom dashboard\n * </p>\n *\n * @author chat2db\n * @since 2023-09-09\n */\n@Getter\n@Setter\n@TableName(\"CHART\")\npublic class ChartDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Chart name\n     */\n    private String name;\n\n    /**\n     * Chart description\n     */\n    private String description;\n\n    /**\n     * chart information\n     */\n    private String schema;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n\n    private String schemaName;\n\n    /**\n     * DDL content\n     */\n    private String ddl;\n\n    /**\n     * Whether it has been deleted, 'Y' means deleted, 'N' means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/DashboardChartRelationDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Custom dashboard\n * </p>\n *\n * @author ali-dbhub\n * @since 2023-06-09\n */\n@Getter\n@Setter\n@TableName(\"DASHBOARD_CHART_RELATION\")\npublic class DashboardChartRelationDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * report id\n     */\n    private Long dashboardId;\n\n    /**\n     * chart id\n     */\n    private Long chartId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/DashboardDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Custom dashboard\n * </p>\n *\n * @author chat2db\n * @since 2023-09-02\n */\n@Getter\n@Setter\n@TableName(\"DASHBOARD\")\npublic class DashboardDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Dashboard name\n     */\n    private String name;\n\n    /**\n     * Dashboard description\n     */\n    private String description;\n\n    /**\n     * Dashboard layout information\n     */\n    private String schema;\n\n    /**\n     * Whether it has been deleted, y means deleted, n means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/DataSourceAccessDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Data source authorization\n * </p>\n *\n * @author chat2db\n * @since 2023-08-26\n */\n@Getter\n@Setter\n@TableName(\"DATA_SOURCE_ACCESS\")\npublic class DataSourceAccessDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Creator user id\n     */\n    private Long createUserId;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * Data source id\n     */\n    private Long dataSourceId;\n\n    /**\n     * Authorization type\n     */\n    private String accessObjectType;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    private Long accessObjectId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/DataSourceDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Data source connection table\n * </p>\n *\n * @author chat2db\n * @since 2023-08-26\n */\n@Getter\n@Setter\n@TableName(\"DATA_SOURCE\")\npublic class DataSourceDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * userName\n     */\n    private String userName;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * environment type\n     */\n    private String envType;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * host address\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh configuration information json\n     */\n    private String ssh;\n\n    /**\n     * ssl configuration information json\n     */\n    private String ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver information\n     */\n    private String driver;\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Custom extension field json\n     */\n    private String extendInfo;\n\n    /**\n     * driver_config configuration\n     */\n    private String driverConfig;\n\n    /**\n     * environment id\n     */\n    private Long environmentId;\n\n    /**\n     * Connection Type\n     */\n    private String kind;\n\n    /**\n     * service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/DbhubUserDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Data source connection table\n * </p>\n *\n * @author chat2db\n * @since 2023-08-26\n */\n@Getter\n@Setter\n@TableName(\"DBHUB_USER\")\npublic class DbhubUserDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * userName\n     */\n    private String userName;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Nick name\n     */\n    private String nickName;\n\n    /**\n     * email\n     */\n    private String email;\n\n    /**\n     * role coding\n     */\n    private String roleCode;\n\n    /**\n     * user status\n     */\n    private String status;\n\n    /**\n     * Creator user id\n     */\n    private Long createUserId;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/EnvironmentDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Database connection environment\n * </p>\n *\n * @author chat2db\n * @since 2023-09-09\n */\n@Getter\n@Setter\n@TableName(\"ENVIRONMENT\")\npublic class EnvironmentDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Creator user id\n     */\n    private Long createUserId;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * environment name\n     */\n    private String name;\n\n    /**\n     * environment abbreviation\n     */\n    private String shortName;\n\n    /**\n     * color\n     */\n    private String color;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/JdbcDriverDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Getter;\nimport lombok.Setter;\n\n@Getter\n@Setter\n@TableName(\"JDBC_DRIVER\")\npublic class JdbcDriverDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * dbType\n     */\n    private String dbType;\n\n    /**\n     * jdbcDriver\n     */\n    private String jdbcDriver;\n\n    /**\n     * jdbcDriverClass\n     */\n    private String jdbcDriverClass;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/OperationLogDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * My execution record table\n * </p>\n *\n * @author chat2db\n * @since 2023-10-14\n */\n@Getter\n@Setter\n@TableName(\"OPERATION_LOG\")\npublic class OperationLogDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * status\n     */\n    private String status;\n\n    /**\n     * Number of operation lines\n     */\n    private Long operationRows;\n\n    /**\n     * Length of use\n     */\n    private Long useTime;\n\n    /**\n     * Extended Information\n     */\n    private String extendInfo;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/OperationSavedDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * My save list\n * </p>\n *\n * @author ali-dbhub\n * @since 2023-04-22\n */\n@Getter\n@Setter\n@TableName(\"OPERATION_SAVED\")\npublic class OperationSavedDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * save name\n     */\n    private String name;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * ddl statement status: DRAFT/RELEASE\n     */\n    private String status;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * schema name\n     */\n    private String dbSchemaName;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/PinTableDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\n@Getter\n@Setter\n@TableName(\"PIN_TABLE\")\npublic class PinTableDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * save name\n     */\n    private String schemaName;\n\n    /**\n     * userId\n     */\n    private Long userId;\n\n    /**\n     * tableName\n     */\n    private String tableName;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/SystemConfigDO.java",
    "content": "\npackage ai.chat2db.server.domain.repository.entity;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * @author jipengfei\n * @version : SystemConfigDO.java\n */\n@Getter\n@Setter\n@TableName(\"SYSTEM_CONFIG\")\npublic class SystemConfigDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Configuration item code\n     */\n    private String code;\n\n    /**\n     * Configuration item content\n     */\n    private String content;\n\n\n    /**\n     * Configuration summary\n     */\n    private String summary;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TableCacheDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * table cache\n * </p>\n *\n * @author chat2db\n * @since 2023-10-11\n */\n@Getter\n@Setter\n@TableName(\"TABLE_CACHE\")\npublic class TableCacheDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * table name\n     */\n    private String tableName;\n\n    /**\n     * unique index\n     */\n    @TableField(value = \"`key`\")\n    private String key;\n\n    /**\n     * version\n     */\n    private Long version;\n\n    /**\n     * table fields\n     */\n    private String columns;\n\n    /**\n     * Custom extension field json\n     */\n    private String extendInfo;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TableCacheVersionDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * table cache version\n * </p>\n *\n * @author chat2db\n * @since 2023-10-11\n */\n@Getter\n@Setter\n@TableName(\"TABLE_CACHE_VERSION\")\npublic class TableCacheVersionDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * unique index\n     */\n    @TableField(value = \"`key`\")\n    private String key;\n\n    /**\n     * version\n     */\n    private Long version;\n\n    /**\n     * Number of tables\n     */\n    private Long tableCount;\n\n    /**\n     * status\n     */\n    private String status;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TableVectorMappingDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Milvus mapping table saves records\n * </p>\n *\n * @author chat2db\n * @since 2023-10-14\n */\n@Getter\n@Setter\n@TableName(\"TABLE_VECTOR_MAPPING\")\npublic class TableVectorMappingDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * api key\n     */\n    private String apiKey;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Name database\n     */\n    private String database;\n\n    /**\n     * schema name\n     */\n    private String schema;\n\n    /**\n     * Vector saved state\n     */\n    private String status;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TaskDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * TASK TABLE\n * </p>\n *\n * @author chat2db\n * @since 2024-01-25\n */\n@Getter\n@Setter\n@TableName(\"TASK\")\npublic class TaskDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * table_name\n     */\n    private String tableName;\n\n    /**\n     * Whether it has been deleted, y means deleted, n means not deleted\n     */\n    private String deleted;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * task type, such as: DOWNLOAD_DATA, UPLOAD_TABLE_DATA, DOWNLOAD_TABLE_STRUCTURE, UPLOAD_TABLE_STRUCTURE,\n     */\n    private String taskType;\n\n    /**\n     * task status\n     */\n    private String taskStatus;\n\n    /**\n     * task progress\n     */\n    private String taskProgress;\n\n    /**\n     * task name\n     */\n    private String taskName;\n\n    /**\n     * download url\n     */\n    private String downloadUrl;\n\n    /**\n     * task content\n     */\n    private byte[] content;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TeamDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * Team\n * </p>\n *\n * @author chat2db\n * @since 2023-08-26\n */\n@Getter\n@Setter\n@TableName(\"TEAM\")\npublic class TeamDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Creator user id\n     */\n    private Long createUserId;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * Team Coding\n     */\n    private String code;\n\n    /**\n     * Team Name\n     */\n    private String name;\n\n    /**\n     * Team Status\n     */\n    private String status;\n\n    /**\n     * Team Description\n     */\n    private String description;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TeamUserDO.java",
    "content": "package ai.chat2db.server.domain.repository.entity;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * <p>\n * User team table\n * </p>\n *\n * @author chat2db\n * @since 2023-08-05\n */\n@Getter\n@Setter\n@TableName(\"TEAM_USER\")\npublic class TeamUserDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * primary key\n     */\n    @TableId(value = \"ID\", type = IdType.AUTO)\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n\n    /**\n     * Creator user id\n     */\n    private Long createUserId;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * team id\n     */\n    private Long teamId;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/ChartMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.ChartDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Custom dashboard Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-09-09\n */\npublic interface ChartMapper extends BaseMapper<ChartDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/DashboardChartRelationMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.DashboardChartRelationDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Custom dashboard Mapper interface\n * </p>\n *\n * @author ali-dbhub\n * @since 2023-06-09\n */\npublic interface DashboardChartRelationMapper extends BaseMapper<DashboardChartRelationDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/DashboardMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.DashboardDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Custom dashboard Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-09-02\n */\npublic interface DashboardMapper extends BaseMapper<DashboardDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/DataSourceAccessCustomMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.DataSourceAccessDO;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\n\n/**\n * Data Source Access Mapper\n *\n * @author Jiaju Zhuang\n */\npublic interface DataSourceAccessCustomMapper extends Mapper<DataSourceAccessDO> {\n\n    IPage<DataSourceAccessDO> comprehensivePageQuery(IPage<DataSourceAccessDO> page, @Param(\"dataSourceId\") Long dataSourceId,\n        @Param(\"accessObjectType\") String accessObjectType,\n        @Param(\"accessObjectId\") Long accessObjectId,\n        @Param(\"userOrTeamSearchKey\") String userOrTeamSearchKey,\n        @Param(\"dataSourceSearchKey\") String dataSourceSearchKey);\n\n    DataSourceAccessDO checkTeamPermission( @Param(\"dataSourceId\") Long dataSourceId, @Param(\"userId\") Long userId);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/DataSourceAccessMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.DataSourceAccessDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Data source authorization Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-08-26\n */\npublic interface DataSourceAccessMapper extends BaseMapper<DataSourceAccessDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/DataSourceCustomMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.DataSourceDO;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\n\n/**\n * Data Source Custom Mapper\n *\n * @author Jiaju Zhuang\n */\npublic interface DataSourceCustomMapper extends Mapper<DataSourceDO> {\n    IPage<DataSourceDO> selectPageWithPermission(IPage<DataSourceDO> page, @Param(\"admin\") Boolean admin,\n        @Param(\"userId\") Long userId, @Param(\"searchKey\") String searchKey, @Param(\"kind\") String kind, @Param(\"orderBy\") String orderBy);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/DataSourceMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.DataSourceDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Data source connection table Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-08-26\n */\npublic interface DataSourceMapper extends BaseMapper<DataSourceDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/DbhubUserMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.DbhubUserDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Data source connection table Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-08-26\n */\npublic interface DbhubUserMapper extends BaseMapper<DbhubUserDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/EnvironmentMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.EnvironmentDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Database connection environment Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-09-09\n */\npublic interface EnvironmentMapper extends BaseMapper<EnvironmentDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/JdbcDriverMapper.java",
    "content": "\npackage ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.JdbcDriverDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author jipengfei\n * @version : SystemConfigMapper.java\n */\npublic interface JdbcDriverMapper extends BaseMapper<JdbcDriverDO> {\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/OperationLogMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.OperationLogDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * My execution record table Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-10-14\n */\npublic interface OperationLogMapper extends BaseMapper<OperationLogDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/OperationSavedMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.OperationSavedDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * My save table Mapper interface\n * </p>\n *\n * @author ali-dbhub\n * @since 2022-12-28\n */\npublic interface OperationSavedMapper extends BaseMapper<OperationSavedDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/PinTableMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.PinTableDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface PinTableMapper extends BaseMapper<PinTableDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/SystemConfigMapper.java",
    "content": "\npackage ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.SystemConfigDO;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author jipengfei\n * @version : SystemConfigMapper.java\n */\npublic interface SystemConfigMapper  extends BaseMapper<SystemConfigDO> {\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TableCacheMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.TableCacheDO;\nimport ai.chat2db.server.domain.repository.entity.TeamUserDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * <p>\n * table cache Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-10-11\n */\npublic interface TableCacheMapper extends BaseMapper<TableCacheDO> {\n\n    void batchInsert(List<TableCacheDO> list);\n\n    IPage<TableCacheDO> pageQuery(IPage<TableCacheDO> page, @Param(\"dataSourceId\") Long dataSourceId, @Param(\"databaseName\") String databaseName, @Param(\"schemaName\") String schemaName, @Param(\"searchKey\") String searchKey);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TableCacheVersionMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.TableCacheVersionDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * table cache version Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-10-11\n */\npublic interface TableCacheVersionMapper extends BaseMapper<TableCacheVersionDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TableVectorMappingMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.TableVectorMappingDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Milvus mapping table saves records Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-10-14\n */\npublic interface TableVectorMappingMapper extends BaseMapper<TableVectorMappingDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TaskMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.TaskDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\n\n/**\n * <p>\n * TASK TABLE Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2024-01-25\n */\npublic interface TaskMapper extends BaseMapper<TaskDO> {\n\n    IPage<TaskDO> pageQuery(IPage<TaskDO> page, @Param(\"userId\") Long userId,@Param(\"deleted\") String deleted);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TeamMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.TeamDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * Team Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-08-26\n */\npublic interface TeamMapper extends BaseMapper<TeamDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TeamUserCustomMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.TeamUserDO;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\n\n/**\n * Team User Custom Mapper\n *\n * @author Jiaju Zhuang\n */\npublic interface TeamUserCustomMapper extends Mapper<TeamUserDO> {\n\n    IPage<TeamUserDO> comprehensivePageQuery(IPage<TeamUserDO> page, @Param(\"teamId\") Long teamId,\n        @Param(\"userId\") Long userId, @Param(\"teamSearchKey\") String teamSearchKey, @Param(\"userSearchKey\") String userSearchKey);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TeamUserMapper.java",
    "content": "package ai.chat2db.server.domain.repository.mapper;\n\nimport ai.chat2db.server.domain.repository.entity.TeamUserDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * User team table Mapper interface\n * </p>\n *\n * @author chat2db\n * @since 2023-08-05\n */\npublic interface TeamUserMapper extends BaseMapper<TeamUserDO> {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V1_0_0__初始化信息.sql",
    "content": "CREATE TABLE IF NOT EXISTS `data_source` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL COMMENT '修改时间',\n    `alias` varchar(128) DEFAULT NULL COMMENT '别名',\n    `url` varchar(1024) DEFAULT NULL COMMENT '连接地址',\n    `user_name` varchar(128) DEFAULT NULL COMMENT '用户名',\n    `password` varchar(256) DEFAULT NULL COMMENT '密码',\n    `type` varchar(32) DEFAULT NULL COMMENT '数据库类型',\n    `env_type` varchar(32) DEFAULT NULL COMMENT '环境类型',\n    `user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='数据源连接表'\n;\n\nCREATE TABLE IF NOT EXISTS `operation_log` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',\n    `database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',\n    `type` varchar(32) NOT NULL COMMENT '数据库类型',\n    `ddl` text DEFAULT NULL COMMENT 'ddl内容',\n    `user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='我的执行记录表'\n;\n\nCREATE TABLE IF NOT EXISTS `operation_saved` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',\n    `database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',\n    `name` varchar(128) DEFAULT NULL COMMENT '保存名称',\n    `type` varchar(32) NOT NULL COMMENT '数据库类型',\n    `status` varchar(32) NOT NULL COMMENT 'ddl语句状态:DRAFT/RELEASE',\n    `ddl` text DEFAULT NULL COMMENT 'ddl内容',\n    `tab_opened` text DEFAULT NULL COMMENT '是否在tab中被打开,y表示打开,n表示未打开',\n    `user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='我的保存表'\n;\n\n\nCREATE TABLE IF NOT EXISTS `dbhub_user` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `user_name` varchar(32) NOT NULL COMMENT '用户名',\n    `password` varchar(256) DEFAULT NULL COMMENT '密码',\n    `nick_name` varchar(256) DEFAULT NULL COMMENT '昵称',\n    `email` varchar(256) DEFAULT NULL COMMENT '邮箱',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='数据源连接表'\n;\n\nCREATE TABLE IF NOT EXISTS `system_config` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `code` varchar(32) NOT NULL COMMENT '配置项编码',\n    `content` varchar(256) DEFAULT NULL COMMENT '配置项内容',\n    `summary` varchar(256) DEFAULT NULL COMMENT '配置项说明',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='配置中心表'\n;\ncreate UNIQUE INDEX uk_code on system_config(code) ;\n\nINSERT INTO `dbhub_user` (`user_name`,`password`,`nick_name`) VALUES ('dbhub','$2a$10$yElafjDHPoPHSaCo6cjJGuWmtXWNVz/cOOOtDg99eNfvUfalzfane','管理员');"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V1_0_2__修改Console.sql",
    "content": "ALTER TABLE `operation_saved` ADD COLUMN `db_schema_name` varchar(128) NULL COMMENT 'schema名称';\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V1_0_3__增加SSH.sql",
    "content": "ALTER TABLE `data_source` ADD COLUMN `host` varchar(128) NULL COMMENT 'host地址';\nALTER TABLE `data_source` ADD COLUMN `port` varchar(128) NULL COMMENT '端口';\nALTER TABLE `data_source` ADD COLUMN `ssh` varchar(1024) NULL COMMENT 'ssh配置信息json';\nALTER TABLE `data_source` ADD COLUMN `ssl` varchar(1024) NULL COMMENT 'ssl配置信息json';\nALTER TABLE `data_source` ADD COLUMN `sid` varchar(32) NULL COMMENT 'sid';\nALTER TABLE `data_source` ADD COLUMN `driver` varchar(128) NULL COMMENT '驱动信息';\nALTER TABLE `data_source` ADD COLUMN `jdbc` varchar(128) NULL COMMENT 'jdbc版本';\nALTER TABLE `data_source` ADD COLUMN `extend_info` varchar(4096) NULL COMMENT '自定义扩展字段json';\ncreate INDEX idx_user_id on data_source(user_id) ;"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V1_0_4__增加报表.sql",
    "content": "CREATE TABLE IF NOT EXISTS `dashboard` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `name` varchar(128) DEFAULT NULL COMMENT '报表名称',\n    `description` varchar(128) DEFAULT NULL COMMENT '报表描述',\n    `schema` text DEFAULT NULL COMMENT '报表布局信息',\n    `deleted` text DEFAULT NULL COMMENT '是否被删除,y表示删除,n表示未删除',\n    `user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='自定义报表表'\n;\n\nCREATE TABLE IF NOT EXISTS `chart` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `name` varchar(128) DEFAULT NULL COMMENT '图表名称',\n    `description` varchar(128) DEFAULT NULL COMMENT '图表描述',\n    `schema` text DEFAULT NULL COMMENT '图表信息',\n    `data_source_id` bigint(20) unsigned DEFAULT NULL COMMENT '数据源连接ID',\n    `type` varchar(32) DEFAULT NULL COMMENT '数据库类型',\n    `database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',\n    `ddl` text DEFAULT NULL COMMENT 'ddl内容',\n    `deleted` text DEFAULT NULL COMMENT '是否被删除,y表示删除,n表示未删除',\n    `user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='自定义报表表'\n;\n\nCREATE TABLE IF NOT EXISTS `dashboard_chart_relation` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `dashboard_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '报表id',\n    `chart_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '图表id',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='自定义报表表'\n;\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V1_0_5__增加置顶表.sql",
    "content": "CREATE TABLE IF NOT EXISTS `pin_table` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',\n    `database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',\n    `schema_name` varchar(128) DEFAULT NULL COMMENT 'schema名称',\n    `table_name` varchar(128) DEFAULT NULL COMMENT 'table_name',\n    `deleted` text DEFAULT NULL COMMENT '是否被删除,y表示删除,n表示未删除',\n    `user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='PIN TABLES'\n;\ncreate INDEX idx_user_id_data_source_id on pin_table(user_id,data_source_id) ;"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V1_0_6__初始化demo信息.sql",
    "content": "INSERT INTO DATA_SOURCE (GMT_CREATE, GMT_MODIFIED, ALIAS, URL, USER_NAME, PASSWORD, TYPE, USER_ID, HOST, PORT, SSH,JDBC)\nVALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'DEMO@db.sqlgpt.cn', 'jdbc:mysql://db.sqlgpt.cn:3306/DEMO', 'demo', 'kok39AYoOSM=', 'MYSQL', 0, 'db.sqlgpt.cn', '3306', '{\"use\":false}', '8.0');\n\nINSERT INTO DASHBOARD (ID, GMT_CREATE, GMT_MODIFIED, NAME, DESCRIPTION, SCHEMA, DELETED, USER_ID)\nVALUES (1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, '学生成绩分析', '学生成绩分析', '[[1],[2],[3]]', 'N', 0);\n\nINSERT INTO CHART (ID, GMT_CREATE, GMT_MODIFIED,  SCHEMA, DATA_SOURCE_ID, DATABASE_NAME, DDL, DELETED, USER_ID)\nVALUES (1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,  '{\"chartType\":\"Column\",\"xAxis\":\"name\",\"yAxis\":\"total_score\"}', 1, 'DEMO', 'SELECT s.name, sc.chinese_score, sc.math_score, sc.english_score, sc.science_score, sc.humanities_score,\n(sc.chinese_score + sc.math_score + sc.english_score + sc.science_score + sc.humanities_score) AS total_score\nFROM student s\nJOIN score sc ON s.id = sc.student_id', 'N', 0);\n\nINSERT INTO CHART (ID, GMT_CREATE, GMT_MODIFIED,  SCHEMA, DATA_SOURCE_ID, DATABASE_NAME, DDL, DELETED, USER_ID)\nVALUES (2, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, '{\"chartType\":\"Pie\",\"xAxis\":\"grade\"}', 1, 'DEMO', 'SELECT s.name,\n       score.chinese_score,\n       score.math_score,\n       score.english_score,\n       score.science_score,\n       score.humanities_score,\n       (score.chinese_score + score.math_score + score.english_score + score.science_score + score.humanities_score) AS total_score,\n       CASE\n           WHEN (score.chinese_score + score.math_score + score.english_score + score.science_score + score.humanities_score) < 630 THEN \"D\"\n           WHEN (score.chinese_score + score.math_score + score.english_score + score.science_score + score.humanities_score) >= 630 AND (score.chinese_score + score.math_score + score.english_score + score.science_score + score.humanities_score) <= 735 THEN \"C\"\n           WHEN (score.chinese_score + score.math_score + score.english_score + score.science_score + score.humanities_score) > 840 THEN \"A\"\n           ELSE \"B\"\n       END AS grade\nFROM score\nJOIN student s ON score.student_id = s.id', 'N', 0);\n\nINSERT INTO CHART (ID, GMT_CREATE, GMT_MODIFIED,  SCHEMA, DATA_SOURCE_ID, DATABASE_NAME, DDL, DELETED, USER_ID)\nVALUES (3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,  '{\"chartType\":\"Line\",\"xAxis\":\"name\",\"yAxis\":\"chinese_score\"}', 1, 'DEMO', 'SELECT s.name, sc.chinese_score, sc.math_score, sc.english_score, sc.science_score, sc.humanities_score,\n(sc.chinese_score + sc.math_score + sc.english_score + sc.science_score + sc.humanities_score) AS total_score\nFROM student s\nJOIN score sc ON s.id = sc.student_id', 'N', 0);\n\nINSERT INTO DASHBOARD_CHART_RELATION (GMT_CREATE, GMT_MODIFIED, DASHBOARD_ID, CHART_ID)\nVALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 1);\nINSERT INTO DASHBOARD_CHART_RELATION (GMT_CREATE, GMT_MODIFIED, DASHBOARD_ID, CHART_ID)\nVALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 2);\nINSERT INTO DASHBOARD_CHART_RELATION (GMT_CREATE, GMT_MODIFIED, DASHBOARD_ID, CHART_ID)\nVALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 3);"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V1_0_7__自定义驱动.sql",
    "content": "CREATE TABLE IF NOT EXISTS `jdbc_driver` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `db_type` varchar(32) NOT NULL COMMENT 'db类型',\n    `jdbc_driver` varchar(512) DEFAULT NULL COMMENT 'jar包',\n    `jdbc_driver_class` varchar(512) DEFAULT NULL COMMENT 'driver class类',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='自定义驱动表'\n;\ncreate INDEX idx_db_type on jdbc_driver(db_type) ;\nALTER TABLE `data_source` ADD COLUMN `driver_config` varchar(1024) NULL COMMENT 'driver_config配置';\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V1_0_8__操作保存类型.sql",
    "content": "ALTER TABLE `operation_saved` ADD COLUMN `operation_type` varchar(1024) NULL COMMENT '操作类型';\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_0__支持环境、用户权限.sql",
    "content": "CREATE TABLE IF NOT EXISTS `environment`\n(\n    `id`               bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create`       datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified`     datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `create_user_id`   bigint(20) unsigned NOT NULL COMMENT '创建人用户id',\n    `modified_user_id` bigint(20) unsigned NOT NULL COMMENT '修改人用户id',\n    `name`             varchar(128)                 DEFAULT NOT NULL COMMENT '环境名称',\n    `short_name`       varchar(128)                 DEFAULT NULL COMMENT '环境缩写',\n    `color`            varchar(32)                  DEFAULT NULL COMMENT '颜色',\n    PRIMARY KEY (`id`)\n) ENGINE = InnoDB\n  DEFAULT CHARSET = utf8mb4 COMMENT ='数据库连接环境'\n;\n\nINSERT INTO `environment`\n(`id`, `create_user_id`, `modified_user_id`, `name`, `short_name`, `color`)\nVALUES (1, 1, 1, 'Release Environment', 'RELEASE', 'RED');\nINSERT INTO `environment`\n(`id`, `create_user_id`, `modified_user_id`, `name`, `short_name`, `color`)\nVALUES (2, 1, 1, 'Test Environment', 'TEST', 'GREEN');\n\nALTER TABLE `data_source`\n    ADD COLUMN `environment_id` bigint(20) unsigned NOT NULL DEFAULT 2 COMMENT '环境id';\n\nALTER TABLE `data_source`\n    modify COLUMN `user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '用户id';\n\nALTER TABLE `data_source`\n    ADD COLUMN `kind` varchar(32) NOT NULL DEFAULT 'PRIVATE' COMMENT '连接类型';\n\nupdate data_source\nset user_id= 1;\n\nALTER TABLE `dbhub_user`\n    ADD COLUMN `role_code` varchar(32) DEFAULT NULL COMMENT '角色编码';\n\nALTER TABLE `dbhub_user`\n    ADD `status` varchar(32) NOT NULL DEFAULT 'VALID' COMMENT '用户状态';\n\nALTER TABLE `dbhub_user`\n    ADD `create_user_id`  bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '创建人用户id';\n\nALTER TABLE `dbhub_user`\n    ADD `modified_user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '修改人用户id';\n\nupdate dbhub_user\nset role_code= 'DESKTOP',user_name='_desktop_default_user_name',password='_desktop_default_user_name',nick_name='Desktop User'\nwhere id = 1;\nINSERT INTO DBHUB_USER (USER_NAME, PASSWORD, NICK_NAME, EMAIL, ROLE_CODE) VALUES ('chat2db', 'chat2db', 'Administrator', null, 'ADMIN');\n\ncreate UNIQUE INDEX uk_user_user_name on dbhub_user (user_name);\n\n\nCREATE TABLE IF NOT EXISTS `team`\n(\n    `id`               bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create`       datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified`     datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `create_user_id`   bigint(20) unsigned NOT NULL COMMENT '创建人用户id',\n    `modified_user_id` bigint(20) unsigned NOT NULL COMMENT '修改人用户id',\n    `code`             varchar(128)                 DEFAULT NOT NULL COMMENT '团队编码',\n    `name`             varchar(512)                 DEFAULT NULL COMMENT '团队名称',\n    `status`           varchar(32)         NOT NULL DEFAULT 'VALID' COMMENT '团队状态',\n    `description`      text                         DEFAULT NULL COMMENT '团队描述',\n    PRIMARY KEY (`id`)\n) ENGINE = InnoDB\n  DEFAULT CHARSET = utf8mb4 COMMENT ='团队'\n;\n\ncreate UNIQUE INDEX uk_team_code on team (code);\n\n\nCREATE TABLE IF NOT EXISTS `team_user`\n(\n    `id`               bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create`       datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified`     datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `create_user_id`   bigint(20) unsigned NOT NULL COMMENT '创建人用户id',\n    `modified_user_id` bigint(20) unsigned NOT NULL COMMENT '修改人用户id',\n    `team_id`          bigint(20) unsigned NOT NULL COMMENT '团队id',\n    `user_id`    bigint(20) unsigned NOT NULL COMMENT '用户id',\n    PRIMARY KEY (`id`)\n) ENGINE = InnoDB\n  DEFAULT CHARSET = utf8mb4 COMMENT ='用户团队表'\n;\n\ncreate INDEX idx_team_user_team_id on team_user (`team_id`);\ncreate INDEX idx_team_user_user_id on team_user (`user_id`);\ncreate UNIQUE INDEX uk_team_user on team_user (`team_id`,`user_id`);\n\nCREATE TABLE IF NOT EXISTS `data_source_access`\n(\n    `id`                 bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create`         datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified`       datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `create_user_id`     bigint(20) unsigned NOT NULL COMMENT '创建人用户id',\n    `modified_user_id`   bigint(20) unsigned NOT NULL COMMENT '修改人用户id',\n    `data_source_id`     bigint(20) unsigned NOT NULL COMMENT '数据源id',\n    `access_object_type` varchar(32)         NOT NULL COMMENT '授权类型',\n    `access_object_id`   bigint(20) unsigned NOT NULL COMMENT '授权id,根据类型区分是用户还是团队',\n    PRIMARY KEY (`id`)\n) ENGINE = InnoDB\n  DEFAULT CHARSET = utf8mb4 COMMENT ='数据源授权'\n;\n\ncreate INDEX idx_data_source_access_data_source_id on data_source_access (`data_source_id`);\ncreate INDEX idx_data_source_access_access_object_id on data_source_access (`access_object_type`, `access_object_id`);\ncreate UNIQUE INDEX uk_data_source_access on data_source_access (`data_source_id`,`access_object_type`, `access_object_id`);\n\nALTER TABLE `operation_saved`\n    modify COLUMN  `user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '用户id';\n\nupdate operation_saved\nset user_id= 1;\n\nALTER TABLE `operation_log`\n    modify COLUMN  `user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '用户id';\n\nupdate operation_log\nset user_id= 1;\n\nALTER TABLE `dashboard`\n    modify   `user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '用户id';\nupdate dashboard\nset user_id= 1;\n\n\nALTER TABLE `chart`\n    modify `user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '用户id';\nupdate chart\nset user_id= 1;\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_10__REMOVEdEMO.sql",
    "content": "delete from DATA_SOURCE where ALIAS ='DEMO@db.sqlgpt.cn';\n\ndelete from DASHBOARD where id =ID;\n\ndelete from CHART where id<=3;\n\ndelete  from DASHBOARD_CHART_RELATION where CHART_ID<=3;\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_1__TableCache.sql",
    "content": "CREATE TABLE IF NOT EXISTS `table_cache_version` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',\n    `database_name` varchar(256) DEFAULT NULL COMMENT 'db名称',\n    `schema_name` varchar(256) DEFAULT NULL COMMENT 'schema名称',\n    `key` varchar(256) DEFAULT NULL COMMENT '唯一索引',\n    `version` bigint(20) unsigned NOT NULL COMMENT '版本',\n    `table_count` bigint(20) unsigned NOT NULL COMMENT '表数量',\n    `status` varchar(256) DEFAULT NULL COMMENT '状态',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='table cache version'\n;\ncreate INDEX idx_table_cache_version_data_source_id on table_cache_version(`data_source_id`) ;\ncreate UNIQUE INDEX uk_table_cache_version_key on table_cache_version(`key`) ;\n\nCREATE TABLE IF NOT EXISTS `table_cache` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',\n    `database_name` varchar(256) DEFAULT NULL COMMENT 'db名称',\n    `schema_name` varchar(256) DEFAULT NULL COMMENT 'schema名称',\n    `table_name` varchar(256) DEFAULT NULL COMMENT 'table名称',\n    `key` varchar(256) DEFAULT NULL COMMENT '唯一索引',\n    `version` bigint(20) unsigned NOT NULL COMMENT '版本',\n    `columns` varchar(2048) DEFAULT NULL COMMENT '表字段',\n    `extend_info` varchar(2048) NULL COMMENT '自定义扩展字段json',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='table cache'\n;\ncreate INDEX idx_table_cache_data_source_id on table_cache(`data_source_id`) ;\ncreate INDEX idx_table_cache_key_version on table_cache(`key`,`version`) ;\ncreate INDEX idx_table_cache_key_table_name on table_cache(`key`,`table_name`) ;\n\n\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_2__OPERATION.sql",
    "content": "ALTER TABLE `operation_log`\n    ADD COLUMN  `status` varchar(20)  DEFAULT 'success' COMMENT '状态';\nALTER TABLE `operation_log`\n    ADD COLUMN  `operation_rows` bigint(20) unsigned  COMMENT '操作行数';\nALTER TABLE `operation_log`\n    ADD COLUMN  `use_time` bigint(20) unsigned COMMENT '使用时长';\nALTER TABLE `operation_log`\n    ADD COLUMN  `extend_info` varchar(1024) COMMENT '扩展信息';"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_4__OPERATION.sql",
    "content": "ALTER TABLE `operation_log`\n    ADD COLUMN  `schema_name` varchar(256) COMMENT 'schema名称';\ncreate INDEX idx_op_data_source_id on operation_log(data_source_id) ;"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_5__TableVector.sql",
    "content": "CREATE TABLE IF NOT EXISTS `table_vector_mapping` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `api_key` varchar(128) DEFAULT NULL COMMENT 'api key',\n    `data_source_id` bigint(20) unsigned DEFAULT NULL COMMENT '数据源连接ID',\n    `database` text DEFAULT NULL COMMENT '数据库名称',\n    `schema` text DEFAULT NULL COMMENT 'schema名称',\n    `status` varchar(4) DEFAULT NULL COMMENT '向量保存状态',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='milvus映射表保存记录'\n;\n\ncreate INDEX idx_api_key on table_vector_mapping(api_key) ;\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_6__TableVectorUpdate.sql",
    "content": "ALTER TABLE table_vector_mapping ALTER COLUMN status VARCHAR(64);\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_7__DATASOURCE.sql",
    "content": "ALTER TABLE `data_source` ADD COLUMN `service_name` varchar(128) NULL COMMENT '服务名';\nALTER TABLE `data_source` ADD COLUMN `service_type` varchar(128) NULL COMMENT '服务类型';\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_8__Chart.sql",
    "content": "ALTER TABLE `chart` ADD COLUMN `schema_name` varchar(128) NULL COMMENT 'schemaName';\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_9__task.sql",
    "content": "CREATE TABLE IF NOT EXISTS `task` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n    `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n    `data_source_id` bigint(20) unsigned NULL COMMENT '数据源连接ID',\n    `database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',\n    `schema_name` varchar(128) DEFAULT NULL COMMENT 'schema名称',\n    `table_name` varchar(128) DEFAULT NULL COMMENT 'table_name',\n    `deleted` varchar(10) DEFAULT NULL COMMENT '是否被删除,y表示删除,n表示未删除',\n    `user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',\n    `task_type` varchar(128) DEFAULT NULL COMMENT 'task type, such as: DOWNLOAD_DATA, UPLOAD_TABLE_DATA, DOWNLOAD_TABLE_STRUCTURE, UPLOAD_TABLE_STRUCTURE,',\n    `task_status` varchar(128) DEFAULT NULL COMMENT 'task status',\n    `task_progress` varchar(128) DEFAULT NULL COMMENT 'task progress',\n    `task_name` varchar(128) DEFAULT NULL COMMENT 'task name',\n    `content` blob DEFAULT NULL COMMENT 'task content',\n    `download_url` varchar(512) DEFAULT NULL COMMENT 'down load url',\n    PRIMARY KEY (`id`)\n    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='TASK TABLE'\n;\ncreate INDEX idx_task_user_id on task(user_id) ;"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/temp/V2_1_0__补充.sql",
    "content": "# ALTER TABLE `operation_saved`\n#     modify COLUMN  `user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '用户id';\n#\n# update operation_saved\n# set user_id= 1;\n#\n# ALTER TABLE `dashboard`\n#     modify   `user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '用户id';\n# update dashboard\n# set user_id= 1;\n#\n#\n# ALTER TABLE `chart`\n#     modify `user_id` bigint(20) unsigned NOT NULL DEFAULT 1 COMMENT '用户id';\n# update chart\n# set user_id= 1;\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/ChartMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.ChartMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/DashboardChartRelationMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.DashboardChartRelationMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/DashboardMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.DashboardMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/DataSourceAccessCustomMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.DataSourceAccessCustomMapper\">\n    <select id=\"comprehensivePageQuery\" resultType=\"ai.chat2db.server.domain.repository.entity.DataSourceAccessDO\">\n        select dsa.*\n        from DATA_SOURCE_ACCESS dsa\n        <if test=\"userOrTeamSearchKey != '' and userOrTeamSearchKey != null \">\n            left join TEAM t on t.id =dsa.ACCESS_OBJECT_ID and dsa.ACCESS_OBJECT_TYPE = 'TEAM'\n            left join DBHUB_USER du on du.ID=dsa.ACCESS_OBJECT_ID and dsa.ACCESS_OBJECT_TYPE = 'USER'\n        </if>\n        <if test=\"dataSourceSearchKey != '' and dataSourceSearchKey != null \">\n            left join DATA_SOURCE ds on ds.id =dsa.DATA_SOURCE_ID\n        </if>\n        <where>\n            <if test=\"dataSourceId != null \">\n                and dsa.DATA_SOURCE_ID = #{dataSourceId}\n            </if>\n            <if test=\"accessObjectId != null \">\n                and dsa.ACCESS_OBJECT_ID = #{accessObjectId}\n            </if>\n            <if test=\"accessObjectType != null \">\n                and dsa.ACCESS_OBJECT_TYPE = #{accessObjectType}\n            </if>\n            <if test=\"userOrTeamSearchKey != '' and userOrTeamSearchKey != null \">\n                and (t.CODE like concat('%',#{userOrTeamSearchKey},'%') or t.NAME like\n                concat('%',#{userOrTeamSearchKey},'%') or\n                du.USER_NAME like concat('%',#{userOrTeamSearchKey},'%') or du.NICK_NAME like\n                concat('%',#{userOrTeamSearchKey},'%') or\n                du.EMAIL like concat('%',#{userOrTeamSearchKey},'%'))\n            </if>\n            <if test=\"dataSourceSearchKey != '' and dataSourceSearchKey != null \">\n                and (ds.ALIAS like concat('%',#{dataSourceSearchKey},'%') or ds.URL like\n                concat('%',#{dataSourceSearchKey},'%'))\n            </if>\n        </where>\n    </select>\n\n    <select id=\"checkTeamPermission\" resultType=\"ai.chat2db.server.domain.repository.entity.DataSourceAccessDO\">\n        select dsa.*\n        from DATA_SOURCE_ACCESS dsa\n        left join TEAM t on t.id =dsa.ACCESS_OBJECT_ID and dsa.ACCESS_OBJECT_TYPE = 'TEAM' and t.status='VALID'\n        left join TEAM_USER tu on tu.TEAM_ID =t.ID\n        <where>\n            dsa.DATA_SOURCE_ID = #{dataSourceId}\n            and tu.USER_ID = #{userId}\n            limit 1\n        </where>\n    </select>\n\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/DataSourceAccessMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.DataSourceAccessMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/DataSourceCustomMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.DataSourceCustomMapper\">\n\n    <select id=\"selectPageWithPermission\" resultType=\"ai.chat2db.server.domain.repository.entity.DataSourceDO\">\n        select ds.*\n        from DATA_SOURCE ds\n        <where>\n            <choose>\n                <when test=\"kind == 'PRIVATE'\">\n                    and ds.USER_ID = #{userId}\n                </when>\n                <otherwise>\n                    and (ds.USER_ID = #{userId}\n                    or exists(\n                    select 1 from DATA_SOURCE_ACCESS dsa where dsa.ACCESS_OBJECT_TYPE = 'USER'\n                    and dsa.ACCESS_OBJECT_ID = #{userId}\n                    and dsa.DATA_SOURCE_ID = ds.ID\n                    )\n                    or exists (\n                    select 1\n                    from DATA_SOURCE_ACCESS dsa\n                    LEFT JOIN TEAM_USER tu on tu.TEAM_ID = dsa.ACCESS_OBJECT_ID and dsa.ACCESS_OBJECT_TYPE = 'TEAM'\n                    left join TEAM t on t.id = tu.TEAM_ID\n                    where tu.USER_ID = #{userId}\n                    and dsa.DATA_SOURCE_ID = ds.ID\n                    and t.STATUS = 'VALID'\n                    )\n                    )\n                </otherwise>\n            </choose>\n            <if test=\"searchKey != '' and searchKey != null \">\n                and (ds.alias like concat('%',#{searchKey},'%') or ds.url like concat('%',#{searchKey},'%'))\n            </if>\n            <if test=\"kind != null\">\n                and ds.kind = #{kind}\n            </if>\n            <if test=\"orderBy != null \">\n                ${orderBy}\n            </if>\n        </where>\n    </select>\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/DataSourceMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.DataSourceMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/DbhubUserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.DbhubUserMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/EnvironmentMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.EnvironmentMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/JdbcDriverMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.JdbcDriverMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/OperationLogMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.OperationLogMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/OperationSavedMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.OperationSavedMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/PinTableMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.PinTableMapper\">\n\n</mapper>"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/SystemConfigMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.SystemConfigMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TableCacheMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.TableCacheMapper\">\n\n    <insert id=\"batchInsert\" parameterType=\"java.util.List\">\n        insert into TABLE_CACHE\n        (data_source_id,database_name,schema_name,table_name,`key`,version,columns,extend_info)\n        values\n        <foreach collection=\"list\" item=\"item\" index=\"index\" separator=\",\">\n            (#{item.dataSourceId},#{item.databaseName},#{item.schemaName},#{item.tableName},#{item.key},#{item.version},#{item.columns},#{item.extendInfo})\n        </foreach>\n    </insert>\n\n    <select id=\"pageQuery\" resultType=\"ai.chat2db.server.domain.repository.entity.TableCacheDO\">\n        select *\n        from TABLE_CACHE tc\n        <where>\n            <if test=\"dataSourceId != null \">\n                and tc.data_source_id = #{dataSourceId}\n            </if>\n            <if test=\"databaseName != null and databaseName != '' \">\n                and tc.database_name = #{databaseName}\n            </if>\n            <if test=\"schemaName != null and schemaName != '' \">\n                and tc.schema_name = #{schemaName}\n            </if>\n            <if test=\"searchKey != null and searchKey != '' \">\n                and LOWER(tc.table_name) like LOWER(concat('%',#{searchKey},'%'))\n            </if>\n        </where>\n    </select>\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TableCacheVersionMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.TableCacheVersionMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TableVectorMappingMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.TableVectorMappingMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TaskMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.TaskMapper\">\n\n    <select id=\"pageQuery\" resultType=\"ai.chat2db.server.domain.repository.entity.TaskDO\">\n        select ID,GMT_CREATE,GMT_MODIFIED,\n               DATA_SOURCE_ID,DATABASE_NAME,SCHEMA_NAME,TABLE_NAME,DELETED,USER_ID,TASK_TYPE,TASK_STATUS,\n        from TASK\n        <where>\n            <if test=\"dataSourceId != null \">\n                and DATA_SOURCE_ID = #{dataSourceId}\n            </if>\n            <if test=\"databaseName != null and databaseName != '' \">\n                and DATABASE_NAME = #{databaseName}\n            </if>\n            <if test=\"tableName != null and tableName != '' \">\n                and TABLE_NAME = #{tableName}\n            </if>\n            <if test=\"schemaName != null and schemaName != '' \">\n                and SCHEMA_NAME = #{schemaName}\n            </if>\n            <if test=\"userId != null \">\n                and USER_ID = #{userId}\n            </if>\n            <if test=\"taskType != null and taskType != '' \">\n                and TASK_TYPE = #{taskType}\n            </if>\n            <if test=\"taskStatus != null and taskStatus != '' \">\n                and TASK_STATUS = #{taskStatus}\n            </if>\n            <if test=\"deleted != null \">\n                and DELETED = #{deleted}\n            </if>\n        </where>\n    </select>\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TeamMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.TeamMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TeamUserCustomMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.TeamUserCustomMapper\">\n    <select id=\"comprehensivePageQuery\" resultType=\"ai.chat2db.server.domain.repository.entity.TeamUserDO\">\n        select tu.*\n        from TEAM_USER tu\n        <if test=\"teamSearchKey != null\">\n            left join TEAM t on t.id = tu.TEAM_ID\n        </if>\n        <if test=\"userSearchKey != null\">\n            left join DBHUB_USER du on du.id = tu.USER_ID\n        </if>\n        <where>\n            <if test=\"teamId != null \">\n                and tu.TEAM_ID = #{teamId}\n            </if>\n            <if test=\"userId != null \">\n                and tu.USER_ID = #{userId}\n            </if>\n\n            <if test=\"teamSearchKey != null \">\n                and (t.CODE like concat('%',#{teamSearchKey},'%') or t.NAME like concat('%',#{teamSearchKey},'%'))\n                and t.STATUS = 'VALID'\n            </if>\n            <if test=\"userSearchKey != null \">\n                and ( du.USER_NAME like concat('%',#{userSearchKey},'%') or du.NICK_NAME like\n                concat('%',#{userSearchKey},'%') or\n                du.EMAIL like concat('%',#{userSearchKey},'%'))\n            </if>\n        </where>\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TeamUserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"ai.chat2db.server.domain.repository.mapper.TeamUserMapper\">\n\n</mapper>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-domain/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-parent</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-domain</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>chat2db-server-domain-api</module>\n        <module>chat2db-server-domain-core</module>\n        <module>chat2db-server-domain-repository</module>\n    </modules>\n\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-server-start/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-parent</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-start</artifactId>\n    <packaging>jar</packaging>\n    <name>chat2db-server-start</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <exclusions>\n                <exclusion>\n                    <artifactId>log4j-api</artifactId>\n                    <groupId>org.apache.logging.log4j</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-web-api</artifactId>\n        </dependency>\n        <!-- Services that need to be loaded -->\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-core</artifactId>\n        </dependency>\n\n        <!-- Log using logback -->\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>jcl-over-slf4j</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>log4j-over-slf4j</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ch.qos.logback</groupId>\n            <artifactId>logback-classic</artifactId>\n        </dependency>\n\n        <!-- Start database -->\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n        </dependency>\n\n        <!-- Database version management -->\n        <dependency>\n            <groupId>org.flywaydb</groupId>\n            <artifactId>flyway-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.flywaydb</groupId>\n            <artifactId>flyway-mysql</artifactId>\n        </dependency>\n\n\n        <!-- template engine -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-thymeleaf</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.freemarker</groupId>\n            <artifactId>freemarker</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-generator</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- http -->\n        <dependency>\n            <groupId>com.dtflys.forest</groupId>\n            <artifactId>forest-spring</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.dtflys.forest</groupId>\n            <artifactId>forest-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.zalando</groupId>\n            <artifactId>logbook-spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n    <build>\n        <finalName>chat2db-server-start</finalName>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n<!--    <build>-->\n<!--        <finalName>chat2db-server-start</finalName>-->\n<!--        <plugins>-->\n<!--            <plugin>-->\n<!--                &lt;!&ndash;Remove third-party dependencies when packaging&ndash;&gt;-->\n<!--                <groupId>org.springframework.boot</groupId>-->\n<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->\n<!--                <configuration>-->\n<!--                    <layout>ZIP</layout>-->\n<!--                    <includes>-->\n<!--                        <include>-->\n<!--                            <groupId>non-exists</groupId>-->\n<!--                            <artifactId>non-exists</artifactId>-->\n<!--                        </include>-->\n<!--                    </includes>-->\n<!--                </configuration>-->\n<!--            </plugin>-->\n<!--            &lt;!&ndash;Copy third-party dependency files to the specified directory&ndash;&gt;-->\n<!--            <plugin>-->\n<!--                <groupId>org.apache.maven.plugins</groupId>-->\n<!--                <artifactId>maven-dependency-plugin</artifactId>-->\n<!--                <executions>-->\n<!--                    <execution>-->\n<!--                        <id>copy-dependencies</id>-->\n<!--                        <phase>package</phase>-->\n<!--                        <goals>-->\n<!--                            <goal>copy-dependencies</goal>-->\n<!--                        </goals>-->\n<!--                        <configuration>-->\n<!--                            &lt;!&ndash;target/lib is the output directory that depends on the jar package, configure it according to your preferences&ndash;&gt;-->\n<!--                            <outputDirectory>target/lib</outputDirectory>-->\n<!--                            <excludeTransitive>false</excludeTransitive>-->\n<!--                            <stripVersion>false</stripVersion>-->\n<!--                            <includeScope>runtime</includeScope>-->\n<!--                        </configuration>-->\n<!--                    </execution>-->\n<!--                </executions>-->\n<!--            </plugin>-->\n<!--        </plugins>-->\n<!--    </build>-->\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/Application.java",
    "content": "package ai.chat2db.server.start;\n\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cache.annotation.EnableCaching;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.scheduling.annotation.EnableAsync;\nimport org.springframework.scheduling.annotation.EnableScheduling;\nimport org.springframework.stereotype.Indexed;\n\n\n/**\n * Startup class\n *\n * @author Jiaju Zhuang\n */\n@SpringBootApplication\n@ComponentScan(value = {\"ai.chat2db.server\"})\n@Indexed\n@EnableCaching\n@EnableScheduling\n@EnableAsync\n@Slf4j\npublic class Application {\n\n    public static void main(String[] args) {\n        ConfigUtils.initProcess();\n        new Thread(() -> {\n            Dbutils.init();\n        }).start();\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/AsyncContextRefreshedListener.java",
    "content": "//package ai.chat2db.server.start.config.config;\n//\n//import ai.chat2db.server.tools.common.model.ConfigJson;\n//import ai.chat2db.server.tools.common.util.ConfigUtils;\n//import lombok.extern.slf4j.Slf4j;\n//import org.apache.commons.lang3.StringUtils;\n//import org.springframework.context.ApplicationListener;\n//import org.springframework.context.event.ContextRefreshedEvent;\n//import org.springframework.scheduling.annotation.Async;\n//import org.springframework.stereotype.Component;\n//\n///**\n// * Execute tasks after startup is completed\n// *\n// * @author Jiaju Zhuang\n// */\n//@Component\n//@Slf4j\n//public class AsyncContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {\n//    @Override\n//    @Async\n//    public void onApplicationEvent(ContextRefreshedEvent event) {\n//        // Successfully set up startup\n//        String currentVersion = ConfigUtils.getLocalVersion();\n//        ConfigJson configJson = ConfigUtils.getConfig();\n//        if (StringUtils.isNotBlank(currentVersion) && !StringUtils.equals(currentVersion,\n//            configJson.getLatestStartupSuccessVersion())) {\n//            configJson.setLatestStartupSuccessVersion(currentVersion);\n//            ConfigUtils.setConfig(configJson);\n//        }\n//    }\n//}"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/Chat2dbForestConfiguration.java",
    "content": "package ai.chat2db.server.start.config.config;\n\nimport ai.chat2db.server.tools.common.config.Chat2dbProperties;\nimport com.dtflys.forest.Forest;\nimport jakarta.annotation.Resource;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * forest config\n *\n * @author Jiaju Zhuang\n */\n@Configuration\npublic class Chat2dbForestConfiguration implements InitializingBean {\n\n    @Resource\n    private Chat2dbProperties chat2dbProperties;\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        Forest.config()\n            .setVariableValue(\"gatewayBaseUrl\", chat2dbProperties.getGateway().getBaseUrl());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/Chat2dbWebMvcConfigurer.java",
    "content": "package ai.chat2db.server.start.config.config;\n\nimport java.io.IOException;\nimport java.util.Enumeration;\n\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport com.alibaba.fastjson2.JSON;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.service.TeamUserService;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.domain.core.cache.CacheKey;\nimport ai.chat2db.server.domain.core.cache.MemoryCacheManage;\nimport ai.chat2db.server.tools.base.constant.SymbolConstant;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.config.Chat2dbProperties;\nimport ai.chat2db.server.tools.common.enums.ModeEnum;\nimport ai.chat2db.server.tools.common.exception.PermissionDeniedBusinessException;\nimport ai.chat2db.server.tools.common.exception.RedirectBusinessException;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport jakarta.annotation.Resource;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.AsyncHandlerInterceptor;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * web project configuration\n *\n * @author Shi Yi\n */\n@Configuration\n@Slf4j\npublic class Chat2dbWebMvcConfigurer implements WebMvcConfigurer {\n\n    /**\n     * api prefix\n     */\n    private static final String API_PREFIX = \"/api/\";\n\n    /**\n     * Globally released url\n     */\n    private static final String[] FRONT_PERMIT_ALL = new String[] {\"/favicon.ico\", \"/error\", \"/static/**\",\n        \"/api/system\", \"/login\"};\n\n    @Resource\n    private UserService userService;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n\n        // All requests try to add user information\n        registry.addInterceptor(new AsyncHandlerInterceptor() {\n                @Override\n                public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,\n                    @NotNull Object handler) {\n                    Dbutils.setSession();\n                    Long userId = RoleCodeEnum.DESKTOP.getDefaultUserId();\n                    Long finalUserId = userId;\n                    LoginUser loginUser = MemoryCacheManage.computeIfAbsent(CacheKey.getLoginUserKey(userId), () -> {\n                        User user = userService.query(finalUserId).getData();\n                        if (user == null) {\n                            return null;\n                        }\n                        boolean iaAdmin = RoleCodeEnum.ADMIN.getCode().equals(user.getRoleCode());\n\n                        return LoginUser.builder()\n                            .id(user.getId())\n                            .nickName(user.getNickName())\n                            .admin(iaAdmin)\n                            .roleCode(user.getRoleCode())\n                            .build();\n                    });\n\n                    if (loginUser == null) {\n                        // Indicates that the user may have been deleted\n                        return true;\n                    }\n                    loginUser.setToken(userId.toString());\n\n                    ContextUtils.setContext(Context.builder()\n                        .loginUser(loginUser)\n                        .build());\n                    return true;\n                }\n\n                @Override\n                public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,\n                    Exception ex) throws Exception {\n                    // Remove login information\n                    ContextUtils.removeContext();\n                    Dbutils.removeSession();\n                }\n            })\n            .order(1)\n            .addPathPatterns(\"/**\")\n            .excludePathPatterns(FRONT_PERMIT_ALL);\n\n        // Verify login information\n\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/JarDownloadTask.java",
    "content": "\npackage ai.chat2db.server.start.config.config;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.util.JdbcJarUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * @author jipengfei\n * @version : JarDownloadTask.java\n */\n@Component\n@Slf4j\npublic class JarDownloadTask implements CommandLineRunner {\n\n    @Override\n    public void run(String... args) throws Exception {\n        List<String> urls = new ArrayList<>();\n        Chat2DBContext.PLUGIN_MAP.forEach((k, v) -> {\n            v.getDBConfig().getDriverConfigList().forEach(driverConfig -> {\n                if (driverConfig != null && !CollectionUtils.isEmpty(driverConfig.getDownloadJdbcDriverUrls()) && (\n                    \"MYSQL\".equals(driverConfig.getDbType()))) {\n                    urls.addAll(driverConfig.getDownloadJdbcDriverUrls());\n                }\n            });\n        });\n        JdbcJarUtils.asyncDownload(urls);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/WebLogConfiguration.java",
    "content": "package ai.chat2db.server.start.config.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.zalando.logbook.BodyFilter;\n\n/**\n * log config\n *\n * @author Jiaju Zhuang\n */\n@Configuration\npublic class WebLogConfiguration {\n\n    @Bean\n    public BodyFilter bodyFilter() {\n        return BodyFilter.none();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/i18n/I18nConfig.java",
    "content": "package ai.chat2db.server.start.config.i18n;\n\nimport java.util.Locale;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.i18n.CookieLocaleResolver;\n\n/**\n * Internationalized configuration\n *\n * @author Jiaju Zhuang\n */\n@Configuration\npublic class I18nConfig {\n    @Bean\n    public CookieLocaleResolver localeResolver() {\n        CookieLocaleResolver resolver = new CookieLocaleResolver(\"CHAT2DB.LOCALE\");\n        resolver.setDefaultLocale(Locale.US);\n        return resolver;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/interceptor/CorsFilter.java",
    "content": "package ai.chat2db.server.start.config.interceptor;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.stereotype.Component;\n\n/**\n * Cors cross-domain interceptor, allowing cross-domain under any circumstances\n *\n * There will be problems when logging in across domains through the CorsRegistry policy, but it does not occur locally. The possible reason is: the loading order of the beans.\n * Temporarily solved through CorsFilter, you can study it later: CorsRegistry\n *\n * @author Shi Yi\n */\n@Component\npublic class CorsFilter implements Filter {\n\n    @Override\n    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)\n        throws IOException, ServletException {\n        HttpServletResponse response = (HttpServletResponse)res;\n        HttpServletRequest request = (HttpServletRequest)req;\n\n        response.setHeader(\"Access-Control-Allow-Origin\", request.getHeader(HttpHeaders.ORIGIN));\n        response.setHeader(\"Access-Control-Allow-Credentials\", \"true\");\n        response.setHeader(\"Access-Control-Allow-Methods\", \"POST, GET, PUT, OPTIONS, DELETE\");\n        response.setHeader(\"Access-Control-Max-Age\", \"3600\");\n        response.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, DBHUB, uid\");\n        chain.doFilter(req, res);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/DbhubTomcatConnectorCustomizer.java",
    "content": "//package ai.chat2db.server.start.config.listener;\n//\n//import ai.chat2db.server.web.api.controller.system.util.SystemUtils;\n//import lombok.extern.slf4j.Slf4j;\n//import org.apache.catalina.LifecycleState;\n//import org.apache.catalina.connector.Connector;\n//import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;\n//import org.springframework.stereotype.Component;\n//\n///**\n// * Custom tomcat parameters\n// *\n// * @author Jiaju Zhuang\n// */\n//@Component\n//@Slf4j\n//public class DbhubTomcatConnectorCustomizer implements TomcatConnectorCustomizer {\n//    @Override\n//    public void customize(Connector connector) {\n//        connector.addLifecycleListener(event -> {\n//            // Exit the system directly after receiving the shutdown event, because sometimes the system will not exit.\n//            if (LifecycleState.STOPPING == event.getLifecycle().getState()) {\n//                SystemUtils.stop();\n//            }\n//        });\n//    }\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/FailedEventApplicationListener.java",
    "content": "package ai.chat2db.server.start.config.listener;\n\nimport ai.chat2db.server.web.api.controller.system.util.SystemUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.context.event.ApplicationFailedEvent;\nimport org.springframework.context.ApplicationListener;\n\n/**\n * Listener that failed to start application\n * The application failed to start. It just stopped tomcat and did not stop the application. Stop xia here.\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class FailedEventApplicationListener implements ApplicationListener<ApplicationFailedEvent> {\n\n    @Override\n    public void onApplicationEvent(ApplicationFailedEvent event) {\n        log.error(\"Failed to start, stop application\", event.getException());\n        SystemUtils.stop();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/ManageApplicationListener.java",
    "content": "//package ai.chat2db.server.start.config.listener;\n//\n//import com.alibaba.fastjson2.JSON;\n//import com.alibaba.fastjson2.TypeReference;\n//\n//import ai.chat2db.server.tools.base.enums.SystemEnvironmentEnum;\n//import ai.chat2db.server.tools.base.wrapper.result.DataResult;\n//import cn.hutool.http.HttpUtil;\n//import lombok.extern.slf4j.Slf4j;\n//import org.apache.commons.lang3.BooleanUtils;\n//import org.apache.commons.lang3.StringUtils;\n//import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;\n//import org.springframework.context.ApplicationListener;\n//import org.springframework.util.Assert;\n//\n///**\n// * Used to manage startup\n// * Prevent starting multiple\n// *\n// * @author zhuangjiaju\n// * @date 2023/05/08\n// */\n//@Slf4j\n//public class ManageApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {\n//\n//    @Override\n//    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {\n//        Integer serverPort = event.getEnvironment().getProperty(\"server.port\", Integer.class);\n//        Assert.notNull(serverPort, \"server.port configuration information\");\n//        log.info(\"The startup port is: {}\", serverPort);\n//        String environment = event.getEnvironment().getProperty(\"spring.profiles.active\", String.class);\n//\n//        // Try to access to confirm whether the application has been started\n//        DataResult<String> dataResult;\n//        try {\n//            String body = HttpUtil.get(\"http://127.0.0.1:\" + serverPort + \"/api/system/get-version-a\", 10);\n//            dataResult = JSON.parseObject(body, new TypeReference<>() {});\n//        } catch (Exception e) {\n//            // Throwing an exception means that there is no old startup or the old one is unreliable.\n//            log.info(\"Attempts to access old applications failed. This exception is not important. It will be output during normal startup, so please ignore it.\" + e.getMessage());\n//\n//            // Try killing the old process\n//            killOldIfNecessary(environment);\n//            return;\n//        }\n//\n//        if (dataResult == null || BooleanUtils.isNotTrue(dataResult.getSuccess())) {\n//            // Try killing the old process\n//            killOldIfNecessary(environment);\n//            return;\n//        }\n//\n//        // Indicates that the old process is available\n//        log.info(\"There is already a started application on the current interface, and this application is no longer started.\");\n//        System.exit(0);\n//    }\n//\n//    private void killOldIfNecessary(String environment) {\n//        try {\n//            ProcessHandle.allProcesses().forEach(process -> {\n//                String command = process.info().command().orElse(null);\n//                // Not a java application\n//                boolean isJava = StringUtils.endsWithIgnoreCase(command, \"java\") || StringUtils.endsWithIgnoreCase(\n//                    command,\n//                    \"java.exe\");\n//                if (!isJava) {\n//                    return;\n//                }\n//                String[] arguments = process.info().arguments().orElse(null);\n//                // no parameters\n//                if (arguments == null) {\n//                    return;\n//                }\n//                // Is it dbhub?\n//                boolean isDbhub = false;\n//                String environmentArgument = null;\n//                for (String argument : arguments) {\n//                    if (StringUtils.equals(\"chat2db-server-start.jar\", argument)) {\n//                        isDbhub = true;\n//                    }\n//                    if (StringUtils.startsWith(argument, \"-Dspring.profiles.active=\")) {\n//                        environmentArgument = StringUtils.substringAfter(argument, \"-Dspring.profiles.active=\");\n//                    }\n//                }\n//                // Not dbhub\n//                if (!isDbhub) {\n//                    return;\n//                }\n//                // Determine whether it is a formal environment\n//                if (StringUtils.equals(SystemEnvironmentEnum.RELEASE.getCode(), environment) && StringUtils.equals(\n//                    SystemEnvironmentEnum.RELEASE.getCode(), environmentArgument)) {\n//                    log.info(\"The formal environment requires closing the process\");\n//                    destroyProcess(process, command, arguments);\n//                    return;\n//                }\n//\n//                // Determine whether it is a test environment\n//                if (StringUtils.equals(SystemEnvironmentEnum.TEST.getCode(), environment) && StringUtils.equals(\n//                    SystemEnvironmentEnum.TEST.getCode(), environmentArgument)) {\n//                    log.info(\"The test environment needs to shut down the process\");\n//                    destroyProcess(process, command, arguments);\n//                    return;\n//                }\n//\n//                // Determine whether it is a local environment\n//                boolean devDestroy = StringUtils.equals(SystemEnvironmentEnum.DEV.getCode(), environment) && (\n//                    environmentArgument == null\n//                        || StringUtils.equals(SystemEnvironmentEnum.DEV.getCode(), environmentArgument));\n//                if (devDestroy) {\n//                    log.info(\"The local environment needs to close the process\");\n//                    destroyProcess(process, command, arguments);\n//                }\n//            });\n//        } catch (Throwable t) {\n//            log.warn(\"Attempts to close redundant processes failed and did not affect normal startup.\", t);\n//        }\n//\n//    }\n//\n//    private void destroyProcess(ProcessHandle process, String command, String[] arguments) {\n//        log.info(\"Checked that there are processes that need to be shut down:{},{}\", JSON.toJSONString(command), JSON.toJSONString(arguments));\n//        try {\n//            process.destroy();\n//        } catch (Exception e) {\n//            log.error(\"Failed to end process\", e);\n//        }\n//    }\n//}"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/manage/ManageMessage.java",
    "content": "package ai.chat2db.server.start.config.listener.manage;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * Administrative messages\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ManageMessage implements Serializable {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * Message type\n     *\n     * @see MessageTypeEnum\n     */\n    private MessageTypeEnum messageTypeEnum;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/manage/MessageTypeEnum.java",
    "content": "package ai.chat2db.server.start.config.listener.manage;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * Message type enum\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum MessageTypeEnum implements BaseEnum<String> {\n    /**\n     * Check if it works properly\n     */\n    HEARTBEAT,\n\n\n    ;\n\n\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n    @Override\n    public String getDescription() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/mybatis/MyBatisPlusConfig.java",
    "content": "package ai.chat2db.server.start.config.mybatis;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author moji\n * @version MyBatisPlusConfig.java, v 0.1 September 29, 2022 17:38 moji Exp $\n * @date 2022/09/29\n */\n@Configuration\npublic class MyBatisPlusConfig {\n\n    /**\n     * myBatisPlus paging plug-in\n     */\n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));\n        return mybatisPlusInterceptor;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/controller/oauth/OauthController.java",
    "content": "package ai.chat2db.server.start.controller.oauth;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport jakarta.annotation.Resource;\n\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.start.controller.oauth.request.LoginRequest;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\n\nimport cn.hutool.crypto.digest.DigestUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.jetbrains.annotations.NotNull;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Objects;\n\n/**\n * Login authorization service\n *\n * @author Jiaju Zhuang\n */\n@RestController\n@RequestMapping(\"/api/oauth\")\n@Slf4j\npublic class OauthController {\n\n    @Resource\n    private UserService userService;\n\n    /**\n     * Login with username and password\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"login_a\")\n    public DataResult login(@Validated @RequestBody LoginRequest request) {\n        //  Query user\n     return DataResult.of(null);\n    }\n\n    private boolean validateAdmin(final @NotNull User user) {\n        return RoleCodeEnum.ADMIN.getDefaultUserId().equals(user.getId()) && RoleCodeEnum.ADMIN.getPassword().equals(\n                user.getPassword());\n    }\n\n    private void validateUser(final User user) {\n        if (Objects.isNull(user)) {\n            throw new BusinessException(\"oauth.userNameNotExits\");\n        }\n        if (!ValidStatusEnum.VALID.getCode().equals(user.getStatus())) {\n            throw new BusinessException(\"oauth.invalidUserName\");\n        }\n        if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(user.getId())) {\n            throw new BusinessException(\"oauth.IllegalUserName\");\n        }\n    }\n\n    /**\n     * user\n     *\n     * @return\n     */\n    @GetMapping(\"user\")\n    public DataResult<LoginUser> user() {\n        return DataResult.of(getLoginUser());\n    }\n\n    /**\n     * user\n     *\n     * @return\n     */\n    @GetMapping(\"user_a\")\n    public DataResult<LoginUser> usera() {\n        return DataResult.of(getLoginUser());\n    }\n\n    private LoginUser getLoginUser() {\n        return ContextUtils.queryLoginUser();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/controller/oauth/request/LoginRequest.java",
    "content": "package ai.chat2db.server.start.controller.oauth.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Log in\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class LoginRequest {\n    /**\n     * userName\n     */\n    @NotNull(message = \"用户名不能为空\")\n    private String userName;\n\n    /**\n     * password\n     */\n    @NotNull(message = \"密码不能为空\")\n    private String password;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/controller/thymeleaf/ThymeleafController.java",
    "content": "package ai.chat2db.server.start.controller.thymeleaf;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\n\n/**\n * Template engine configuration\n *\n * @author Jiaju Zhuang\n */\n@Controller\n@Slf4j\n@Order(Integer.MIN_VALUE)\npublic class ThymeleafController {\n\n    /**\n     * Front-end template settings\n     *\n     * @return\n     */\n    @GetMapping(value = {\"/\", \"/web/\", \"/web/**\",\"/login\",\"/workspace\",\"/dashboard\",\"/connections\",\"/team\"})\n    public String index() {\n        return \"index\";\n    }\n\n    @RequestMapping(value = \"/chat.html\", method={RequestMethod.GET}, produces=\"text/html;charset=utf-8\")\n    public String chat(){\n\n        return \"chat\";\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/EasyControllerExceptionHandler.java",
    "content": "package ai.chat2db.server.start.exception;\n\nimport java.util.Map;\n\nimport com.alibaba.fastjson2.JSON;\n\nimport ai.chat2db.server.start.exception.convertor.BindExceptionConvertor;\nimport ai.chat2db.server.start.exception.convertor.BusinessExceptionConvertor;\nimport ai.chat2db.server.start.exception.convertor.DefaultExceptionConvertor;\nimport ai.chat2db.server.start.exception.convertor.ExceptionConvertor;\nimport ai.chat2db.server.start.exception.convertor.MaxUploadSizeExceededExceptionConvertor;\nimport ai.chat2db.server.start.exception.convertor.MethodArgumentNotValidExceptionConvertor;\nimport ai.chat2db.server.start.exception.convertor.MethodArgumentTypeMismatchExceptionConvertor;\nimport ai.chat2db.server.start.exception.convertor.ParamExceptionConvertor;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.excption.SystemException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.exception.NeedLoggedInBusinessException;\nimport ai.chat2db.server.tools.common.exception.RedirectBusinessException;\nimport com.google.common.collect.Maps;\nimport jakarta.servlet.http.HttpServletRequest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.validation.BindException;\nimport org.springframework.web.HttpMediaTypeNotAcceptableException;\nimport org.springframework.web.HttpMediaTypeNotSupportedException;\nimport org.springframework.web.HttpRequestMethodNotSupportedException;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.MissingRequestHeaderException;\nimport org.springframework.web.bind.MissingServletRequestParameterException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\nimport org.springframework.web.multipart.MultipartException;\nimport org.springframework.web.servlet.ModelAndView;\n\n/**\n * Intercepting Controller exceptions\n *\n * @author Shi Yi\n */\n@ControllerAdvice\n@Slf4j\n@Order(Ordered.HIGHEST_PRECEDENCE)\npublic class EasyControllerExceptionHandler {\n\n    /**\n     * All exception handling converters\n     */\n    public static final Map<Class<?>, ExceptionConvertor> EXCEPTION_CONVERTOR_MAP = Maps.newHashMap();\n\n    static {\n        EXCEPTION_CONVERTOR_MAP.put(MethodArgumentNotValidException.class,\n            new MethodArgumentNotValidExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(BindException.class, new BindExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(BusinessException.class, new BusinessExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(NeedLoggedInBusinessException.class, new BusinessExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(MissingServletRequestParameterException.class, new ParamExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(IllegalArgumentException.class, new ParamExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(MethodArgumentTypeMismatchException.class,\n            new MethodArgumentTypeMismatchExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(MaxUploadSizeExceededException.class,\n            new MaxUploadSizeExceededExceptionConvertor());\n    }\n\n    /**\n     * Default converter\n     */\n    public static ExceptionConvertor DEFAULT_EXCEPTION_CONVERTOR = new DefaultExceptionConvertor();\n\n    /**\n     * Business abnormality\n     *\n     * @param request   request\n     * @param exception exception\n     * @return return\n     */\n    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class, IllegalArgumentException.class,\n        MissingServletRequestParameterException.class, MethodArgumentTypeMismatchException.class,\n        BusinessException.class, MaxUploadSizeExceededException.class,\n        HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotAcceptableException.class,\n        MultipartException.class, MissingRequestHeaderException.class, HttpMediaTypeNotSupportedException.class,\n        NeedLoggedInBusinessException.class})\n    @ResponseStatus(value = HttpStatus.OK)\n    @ResponseBody\n    public ActionResult handleBusinessException(HttpServletRequest request, Exception exception) {\n        ActionResult result = convert(exception);\n        log.info(\"Business exception occurred{}:{}\", request.getRequestURI(), result, exception);\n        return result;\n    }\n\n    /**\n     * Business abnormality\n     *\n     * @param request   request\n     * @param exception exception\n     * @return return\n     */\n    @ExceptionHandler({RedirectBusinessException.class})\n    public ModelAndView handleModelAndViewBizException(HttpServletRequest request, Exception exception) {\n        ModelAndView result = translateModelAndView(exception);\n        log.info(\"ModelAndView business exception occurred{}:{}\", request.getRequestURI(), result, exception);\n        return result;\n    }\n\n    public ModelAndView translateModelAndView(Throwable exception) {\n        // Parameter exception\n        if (exception instanceof RedirectBusinessException) {\n            RedirectBusinessException e = (RedirectBusinessException)exception;\n            return dealResponseModelAndView(null, e.getMessage(), e.getRedirect(), null, null);\n        }\n        // Jump to homepage by default\n        return new ModelAndView(\"redirect:/\");\n    }\n\n    private ModelAndView dealResponseModelAndView(String title, String errorMessage, String redirect, String href,\n        String buttonText) {\n        // If there is redirection information, jump\n        if (StringUtils.isNotBlank(redirect)) {\n            return new ModelAndView(\"redirect:\" + redirect);\n        }\n        // Jump to homepage by default\n        return new ModelAndView(\"redirect:/\");\n\n        // synchronous request\n        //return ModelAndViewUtils.error(title, errorMessage,href,buttonText);\n    }\n\n    /**\n     * System exception\n     *\n     * @param request   request\n     * @param exception exception\n     * @return return\n     */\n    @ExceptionHandler({SystemException.class})\n    @ResponseStatus(value = HttpStatus.OK)\n    @ResponseBody\n    public ActionResult handleSystemException(HttpServletRequest request, Exception exception) {\n        ActionResult result = convert(exception);\n        log.error(\"Business exception occurred{}:{}\", request.getRequestURI(), result, exception);\n        return result;\n    }\n\n    /**\n     * Unknown exception requires manual intervention to view logs\n     *\n     * @param request   request\n     * @param exception exception\n     * @return return\n     */\n    @ExceptionHandler(Exception.class)\n    @ResponseStatus(value = HttpStatus.OK)\n    @ResponseBody\n    public ActionResult handledException(HttpServletRequest request, Exception exception) {\n        ActionResult result = convert(exception);\n        log.error(\"An unknown exception occurred {}:{}, request parameters:{}\", request.getRequestURI(), result,\n            JSON.toJSONString(request.getParameterMap()),\n            exception);\n        return result;\n    }\n\n    public ActionResult convert(Throwable exception) {\n        ExceptionConvertor exceptionConvertor = EXCEPTION_CONVERTOR_MAP.get(exception.getClass());\n        if (exceptionConvertor == null) {\n            if (exception instanceof BusinessException) {\n                exceptionConvertor = EXCEPTION_CONVERTOR_MAP.get(BusinessException.class);\n            } else if (exception instanceof SystemException) {\n                exceptionConvertor = EXCEPTION_CONVERTOR_MAP.get(SystemException.class);\n            } else {\n                exceptionConvertor = DEFAULT_EXCEPTION_CONVERTOR;\n            }\n        }\n        return exceptionConvertor.convert(exception);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/BindExceptionConvertor.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.spi.util.ExceptionUtils;\nimport org.springframework.validation.BindException;\n\n/**\n * BindException\n *\n * @author Shi Yi\n */\npublic class BindExceptionConvertor implements ExceptionConvertor<BindException> {\n\n    @Override\n    public ActionResult convert(BindException exception) {\n        String message = ExceptionConvertorUtils.buildMessage(exception.getBindingResult());\n        return ActionResult.fail(\"common.paramError\", message, ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/BusinessExceptionConvertor.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.util.ExceptionUtils;\n\n/**\n * BusinessException\n *\n * @author Shi Yi\n */\npublic class BusinessExceptionConvertor implements ExceptionConvertor<BusinessException> {\n\n    @Override\n    public ActionResult convert(BusinessException exception) {\n        return ActionResult.fail(exception.getCode(), I18nUtils.getMessage(exception.getCode(), exception.getArgs()),\n            ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/DefaultExceptionConvertor.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.util.ExceptionUtils;\n\n/**\n * Default exception handling\n * Throw system exception directly\n *\n * @author Shi Yi\n */\npublic class DefaultExceptionConvertor implements ExceptionConvertor<Throwable> {\n\n    @Override\n    public ActionResult convert(Throwable exception) {\n        return ActionResult.fail(\"common.systemError\", I18nUtils.getMessage(\"common.systemError\"), ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/ExceptionConvertor.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\n\n/**\n * exception converter\n *\n * @author Shi Yi\n */\npublic interface ExceptionConvertor<T extends Throwable> {\n\n    /**\n     * Conversion exception\n     *\n     * @param exception\n     * @return\n     */\n    ActionResult convert(T exception);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/ExceptionConvertorUtils.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport java.util.List;\n\nimport ai.chat2db.server.tools.base.constant.SymbolConstant;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.validation.FieldError;\nimport org.springframework.validation.ObjectError;\n\n/**\n * Conversion tool class\n *\n * @author Shi Yi\n */\npublic class ExceptionConvertorUtils {\n\n    /**\n     * Extract error message from BindingResult\n     *\n     * @param result\n     * @return\n     */\n    public static String buildMessage(BindingResult result) {\n        List<ObjectError> errors = result.getAllErrors();\n        if (CollectionUtils.isEmpty(errors)) {\n            return null;\n        }\n\n        int index = 1;\n        StringBuilder msg = new StringBuilder();\n        msg.append(I18nUtils.getMessage(\"common.paramCheckError\"));\n        for (ObjectError e : errors) {\n            msg.append(index++);\n            // got error message\n            msg.append(SymbolConstant.DOT);\n            if (e instanceof FieldError fieldError) {\n                msg.append(fieldError.getField());\n                msg.append(\" : \");\n            }\n            msg.append(e.getDefaultMessage());\n            msg.append(SymbolConstant.SEMICOLON);\n        }\n        return msg.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/MaxUploadSizeExceededExceptionConvertor.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\n\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.util.ExceptionUtils;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\n\n/**\n * MaxUploadSizeExceededException\n *\n * @author Shi Yi\n */\npublic class MaxUploadSizeExceededExceptionConvertor implements ExceptionConvertor<MaxUploadSizeExceededException> {\n\n    @Override\n    public ActionResult convert(MaxUploadSizeExceededException exception) {\n        return ActionResult.fail(\"common.maxUploadSize\", I18nUtils.getMessage(\"common.maxUploadSize\"), ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/MethodArgumentNotValidExceptionConvertor.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\n\nimport ai.chat2db.spi.util.ExceptionUtils;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\n\n/**\n * MethodArgumentNotValidException\n *\n * @author Shi Yi\n */\npublic class MethodArgumentNotValidExceptionConvertor implements ExceptionConvertor<MethodArgumentNotValidException> {\n\n    @Override\n    public ActionResult convert(MethodArgumentNotValidException exception) {\n        String message = ExceptionConvertorUtils.buildMessage(exception.getBindingResult());\n        return ActionResult.fail(\"common.paramError\", message, ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/MethodArgumentTypeMismatchExceptionConvertor.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\n\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.util.ExceptionUtils;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\n\n/**\n * MethodArgumentTypeMismatchException\n *\n * @author Shi Yi\n */\npublic class MethodArgumentTypeMismatchExceptionConvertor\n    implements ExceptionConvertor<MethodArgumentTypeMismatchException> {\n\n    @Override\n    public ActionResult convert(MethodArgumentTypeMismatchException exception) {\n        return ActionResult.fail(\"common.paramError\", I18nUtils.getMessage(\"common.paramError\"), ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/exception/convertor/ParamExceptionConvertor.java",
    "content": "package ai.chat2db.server.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.spi.util.ExceptionUtils;\n\n/**\n * Parameter exceptions currently include：\n * ConstraintViolationException\n * MissingServletRequestParameterException\n * IllegalArgumentException\n *\n * @author Shi Yi\n */\npublic class ParamExceptionConvertor implements ExceptionConvertor<Throwable> {\n\n    @Override\n    public ActionResult convert(Throwable exception) {\n        return ActionResult.fail(\"common.paramError\", exception.getMessage(), ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/log/EasyLogSink.java",
    "content": "package ai.chat2db.server.start.log;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\n\nimport ai.chat2db.server.tools.common.util.LogUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\nimport org.thymeleaf.util.ContentTypeUtils;\nimport org.zalando.logbook.Correlation;\nimport org.zalando.logbook.HttpRequest;\nimport org.zalando.logbook.HttpResponse;\nimport org.zalando.logbook.Precorrelation;\nimport org.zalando.logbook.Sink;\n\n/**\n * log\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Component\npublic class EasyLogSink implements Sink {\n\n    @Override\n    public void write(final Precorrelation precorrelation, final HttpRequest request) {\n    }\n\n    @Override\n    public void write(final Correlation correlation, final HttpRequest request, final HttpResponse response) {\n        try {\n            printLog(correlation, request, response);\n        } catch (Exception e) {\n            log.error(\"Log exceptions\", e);\n        }\n    }\n\n    public void printLog(final Correlation correlation, final HttpRequest request, final HttpResponse response)\n        throws IOException {\n        // Encapsulate log object\n        WebLog webLog = new WebLog();\n\n        String method = request.getMethod();\n        // Path\n        String path = request.getPath();\n\n        webLog.setMethod(method);\n        webLog.setPath(LogUtils.cutLog(path));\n        webLog.setQuery(LogUtils.cutLog(request.getQuery()));\n        webLog.setDuration(correlation.getDuration().toMillis());\n        webLog.setStartTime(LocalDateTime.ofInstant(correlation.getStart(), ZoneId.systemDefault()));\n        webLog.setEndTime(LocalDateTime.ofInstant(correlation.getEnd(), ZoneId.systemDefault()));\n        try {\n            webLog.setRequest(LogUtils.maskString(LogUtils.cutLog(new String(request.getBody(), StandardCharsets.UTF_8))));\n            if (ContentTypeUtils.isContentTypeJSON(response.getContentType()) || ContentTypeUtils.isContentTypeHTML(\n                response.getContentType())) {\n                webLog.setResponse(LogUtils.maskString(LogUtils.cutLog(new String(response.getBody(), StandardCharsets.UTF_8))));\n            } else {\n                webLog.setResponse(response.getContentType() + \":[\" + response.getBody().length + \"]\");\n            }\n        } catch (IOException e) {\n            log.warn(\"The request to obtain the log & returns an exception. Most likely, the user has closed the stream.\", e);\n        }\n        webLog.setIp(LogUtils.getClientIp(request));\n\n        String pathAndQuery = path;\n        if (StringUtils.isNotBlank(webLog.getQuery())) {\n            pathAndQuery += \"?\" + webLog.getQuery();\n        }\n        log.info(\"http : {}|{}|{}|{}|{}\", webLog.getMethod(), pathAndQuery, webLog.getDuration(),\n            webLog.getRequest(), webLog.getResponse());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/log/LogOncePerRequestFilter.java",
    "content": "package ai.chat2db.server.start.log;\n\nimport java.io.IOException;\n\nimport ai.chat2db.server.tools.common.util.LogUtils;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport lombok.extern.slf4j.Slf4j;\nimport org.slf4j.MDC;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Intercept the log and put in the trace id\n *\n * @author Jiaju Zhuang\n */\n@Order(Ordered.HIGHEST_PRECEDENCE)\n@Component\n@Slf4j\npublic class LogOncePerRequestFilter extends OncePerRequestFilter {\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n        throws\n        ServletException, IOException {\n        try {\n            MDC.put(LogUtils.TRACE_ID, LogUtils.generateTraceId());\n            filterChain.doFilter(request, response);\n        } finally {\n            MDC.remove(LogUtils.TRACE_ID);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/log/WebLog.java",
    "content": "package ai.chat2db.server.start.log;\n\nimport java.time.LocalDateTime;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * log object\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class WebLog {\n    /**\n     * call method\n     */\n    private String method;\n\n    /**\n     * Path\n     */\n    private String path;\n\n    /**\n     * Query conditions\n     */\n    private String query;\n\n    /**\n     * Time consuming (ms)\n     */\n    private Long duration;\n\n    /**\n     * Time consuming (ms)\n     */\n    private LocalDateTime startTime;\n\n    /**\n     * Time consuming (ms)\n     */\n    private LocalDateTime endTime;\n\n    /**\n     * request\n     */\n    private String request;\n\n    /**\n     * response\n     */\n    private String response;\n\n    /**\n     * IP address\n     */\n    private String ip;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/META-INF/spring.factories",
    "content": ""
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/application-dev.yml",
    "content": "# port\nserver:\n  port: 10821\n\nchat2db:\n  gateway:\n    base-url: http://test.sqlgpt.cn/gateway\n    model-base-url: http://test.sqlgpt.cn/gateway\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: startup"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/application-release.yml",
    "content": "#spring:\n#  datasource:\n#    # Configure the relative path of the built-in database\n#    url: jdbc:h2:~/.chat2db/db/chat2db;MODE=MYSQL;FILE_LOCK=NO\n#    driver-class-name: org.h2.Driver\n# port\nserver:\n  port: 10824\n\nchat2db:\n  gateway:\n    base-url: http://test.sqlgpt.cn/gateway\n    model-base-url: http://test.sqlgpt.cn/gateway"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/application-test.yml",
    "content": "# port\nserver:\n  port: 10822\n\nchat2db:\n  gateway:\n    base-url: http://test.sqlgpt.cn/gateway\n    model-base-url: http://test.sqlgpt.cn/gateway"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/application.yml",
    "content": "spring:\n  # Default development environment\n  profiles:\n    active: dev\n  main:\n    allow-bean-definition-overriding: true\n    lazy-initialization: true\n  messages:\n    basename: i18n/messages\n    encoding: UTF-8\n    fallbackToSystemLocale: true\n  jmx:\n    enabled: false\n  # thymeleaf\n  thymeleaf:\n    prefix: classpath:/thymeleaf/\n    check-template-location: true\n    suffix: .html\n    servlet:\n      content-type: text/html\n    mode: HTML5\n  # static files\n  mvc:\n    static-path-pattern: /static/**\n  web:\n    resources:\n      static-locations[0]: classpath:/static/\n  #  Used for database table structure version management\n  servlet:\n    multipart:\n      max-file-size: -1\n      max-request-size: -1\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true\n\nchat2db:\n  version: 1.0.0\n\n# flywaydb outputs the log of executing sql\nlogging:\n    ai:\n      chat2db:\n        server:\n          domain:\n            repository:\n              mapper: debug\n\nchatgpt:\n  apiKey: sk-xxxx\n  apiHost: https://api.openai.com/\n  # You can choose GPT3 or GPT35\n  version: GPT35\n  context:\n    length: 1\n\n# Print the HTTP log\nlogbook:\n  include:\n    - /api/**\n#server:\n#  undertow:\n#    io-threads: 8\n#    worker-threads: 200\n#    buffer-size: 1024\n#    direct-buffers: true\n#    max-http-post-size: 0\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/i18n/messages.properties",
    "content": "# common\ncommon.businessError=Please try resubmitting or refreshing the page later\ncommon.systemError=An exception occurs, you can view the exception details in the log in the help menu.\ncommon.needLoggedIn=Login required\ncommon.redirect=Redirect\ncommon.paramError=The parameter is incorrect\ncommon.paramDetailError=The parameter: {0} is incorrect\ncommon.paramCheckError=The following parameters are not valid:\ncommon.maxUploadSize=The file exceeds the maximum limit\ncommon.permissionDenied=Permission denied\ncommon.dataNotFound=Data not found\ncommon.dataAlreadyExists=The data already exists in the database\ncommon.dataAlreadyExistsWithParam=The data already exists in the database,{0}:{1}\n\noauth.userNameNotExits=The current account does not exist\noauth.IllegalUserName=The current account cannot be logged in. Please change your account\noauth.passwordIncorrect=The password you entered is incorrect\noauth.invalidUserName=The current account is invalid\n\n# dataSource\ndataSource.sqlAnalysisError=Invalid statements\n# connection\nconnection.error=Connection failed, please check the connection information\nconnection.ssh.error=SSH connection failed, please check the connection information\nconnection.driver.load.error=Failed to load driver class, please check the driver jar package\n# sqlResult\nsqlResult.rowNumber=Row Number\nsqlResult.success=Execution successful\n\nuser.canNotOperateSystemAccount=System accounts cannot be operated\nexecute.exportCsv=For more data, please click on Export CSV\n\nsettings.permissionDeniedForAiConfig=Please contact the administrator to set ApiKey in \"Settings ->Custom Ai\""
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/i18n/messages_en_US.properties",
    "content": "common.businessError=Please try resubmitting or refreshing the page later\ncommon.systemError=An exception occurs, you can view the exception details in the log in the help menu.\ncommon.needLoggedIn=Login required\ncommon.redirect=Redirect\ncommon.paramError=The parameter is incorrect\ncommon.paramDetailError=The parameter: {0} is incorrect\ncommon.paramCheckError=The following parameters are not valid\ncommon.maxUploadSize=The file exceeds the maximum limit\ncommon.permissionDenied=Permission denied\ncommon.dataNotFound=Data not found\ncommon.dataAlreadyExists=The data already exists in the database\ncommon.dataAlreadyExistsWithParam=The data already exists in the database,{0}:{1}\n\noauth.userNameNotExits=The current account does not exist\noauth.IllegalUserName=The current account cannot be logged in. Please change your account\noauth.passwordError=The password you entered is incorrect\noauth.invalidUserName=The current account is invalid\n\n\ndataSource.sqlAnalysisError=Invalid statements\nconnection.error=Connection failed, please check the connection information\nconnection.ssh.error=SSH connection failed, please check the connection information\nconnection.driver.load.error=Failed to load driver class, please check the driver jar package\n# sqlResult\nsqlResult.rowNumber=Row Number\nsqlResult.success=Execution successful\n\nuser.canNotOperateSystemAccount=System accounts cannot be operated\nexecute.exportCsv=For more data, please click on Export CSV\n\nsettings.permissionDeniedForAiConfig=Please contact the administrator to set ApiKey in \"Settings ->Custom Ai\"\nmain.indexName=Name\nmain.indexFieldName=Column Name\nmain.indexType=Index Type\nmain.indexMethod=Index Method\nmain.indexNote=Index Comment\nmain.fieldNo=No\nmain.fieldName=Field Name\nmain.fieldType=Column Type\nmain.fieldLength=Length\nmain.fieldIfEmpty=Nullable\nmain.fieldDefault=Column Default\nmain.fieldDecimalPlaces=Decimal Places\nmain.fieldNote=Column Comment\nmain.databaseText=Database:\nmain.sheetName=Table Structure\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/i18n/messages_zh_CN.properties",
    "content": "common.businessError=请尝试重新提交或者刷新页面\ncommon.systemError=系统发生异常，可在帮助中点击日志查看异常详情。\ncommon.needLoggedIn=需要登陆页面\ncommon.redirect=重定向页面\ncommon.paramError=您输入的参数异常\ncommon.paramDetailError=您输入的参数:{0},存在异常\ncommon.paramCheckError=请检查以下参数:\ncommon.maxUploadSize=您输入的文件超过最大限制\ncommon.permissionDenied=您没有权限访问该页面\ncommon.dataNotFound=您访问的数据不存在\ncommon.dataAlreadyExists=数据库总已经存在该数据\ncommon.dataAlreadyExistsWithParam=数据库总已经存在该数据,{0}:{1}\n\noauth.userNameNotExits=当前账号不存在\noauth.IllegalUserName=当前账号无法登录，请换一个账号\noauth.passwordIncorrect=您输入的密码有误\noauth.invalidUserName=您输入的账号已经失效\n\ndataSource.sqlAnalysisError=不合法的执行语句\nconnection.error=数据库链接异常，请检查数据库配置\nconnection.ssh.error=SSH 链接异常，请检查SSH配置\nconnection.driver.load.error=数据库驱动加载异常，请检查驱动配置\n# sqlResult\nsqlResult.rowNumber=行号\nsqlResult.success=执行成功\n\nuser.canNotOperateSystemAccount=不能操作系统账号\nexecute.exportCsv=更多数据请点击导出csv\n\nsettings.permissionDeniedForAiConfig=请联系管理员在 “设置->自定义AI” 里面设置ApiKey\nmain.indexName=名称\nmain.indexFieldName=字段\nmain.indexType=索引类型\nmain.indexMethod=索引方法\nmain.indexNote=注释\nmain.fieldNo=序号\nmain.fieldName=字段名\nmain.fieldType=数据类型\nmain.fieldLength=长度\nmain.fieldIfEmpty=不是null\nmain.fieldDefault=默认值\nmain.fieldDecimalPlaces=小数位\nmain.fieldNote=备注\nmain.databaseText=数据库：\nmain.sheetName=表结构\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <springProperty scope=\"context\" name=\"APP_NAME\" source=\"project.name\"/>\n    <property name=\"LOG_PATH\" value=\"${user.home}/.chat2db/logs\"/>\n    <property name=\"LOG_FILE\" value=\"${LOG_PATH}/application.log\"/>\n\n    <property name=\"EASY_FILE_LOG_PATTERN\"\n              value=\"${EASY_FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39}.%line : %X{TRACE_ID} | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}\"/>\n    <property name=\"EASY_CONSOLE_LOG_PATTERN\"\n              value=\"${EASY_CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}.%line){cyan} %clr(:){faint} %X{TRACE_ID} | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}\"/>\n\n    <appender name=\"APPLICATION\"\n              class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${LOG_FILE}</file>\n        <encoder>\n            <pattern>${EASY_FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>1GB</maxFileSize>\n            <totalSizeCap>10GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${EASY_CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"APPLICATION\"/>\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n</configuration>"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/main/resources/thymeleaf/template.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Chat2DB</title>\n</head>\n<body>\n  \n</body>\n</html>"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/TestApplication.java",
    "content": "package ai.chat2db.server.start.test;\n\nimport ai.chat2db.server.start.Application;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.stereotype.Indexed;\n\n/**\n * Startup of the local environment.\n * Mainly to read some local configurations. For example, log output is different from other environments.\n *\n * @author Shi Yi\n */\n@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@Slf4j\n@Indexed\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/common/BaseTest.java",
    "content": "package ai.chat2db.server.start.test.common;\n\nimport ai.chat2db.server.start.Application;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n/**\n * Basic test class\n *\n * @author Jiaju Zhuang\n **/\n@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@Slf4j\npublic abstract class BaseTest {\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/ChartServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.chart.ChartCreateParam;\nimport ai.chat2db.server.domain.api.chart.ChartListQueryParam;\nimport ai.chat2db.server.domain.api.chart.ChartQueryParam;\nimport ai.chat2db.server.domain.api.chart.ChartUpdateParam;\nimport ai.chat2db.server.domain.api.model.Chart;\nimport ai.chat2db.server.domain.api.service.ChartService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.Arrays;\nimport java.util.Optional;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\n\npublic class ChartServiceTest extends TestApplication {\n\n    @Autowired\n    private ChartService chartService;\n\n\n    @Test\n    public void testCreateWithPermission() {\n        userLoginIdentity(false, 4L);\n//        userLoginIdentity(true, 3L);\n\n        ChartCreateParam createParam = new ChartCreateParam();\n        Optional.of(createParam).ifPresent(param -> {\n            param.setName(\"chat2db\");\n            param.setSchema(\"test\");\n            param.setDataSourceId(1L);\n            param.setType(\"MYSQL\");\n            param.setDatabaseName(\"chat2db\");\n            param.setSchemaName(\"ali_dbhub\");\n            param.setDdl(\"test\");\n        });\n\n        DataResult<Long> withPermission = chartService.createWithPermission(createParam);\n        assertNotNull(withPermission);\n\n        Long id = withPermission.getData();\n        chartService.find(id);\n\n    }\n\n\n    @Test\n    public void testUpdateWithPermission() {\n        userLoginIdentity(false, 1L);\n//        userLoginIdentity(true, 4L);\n\n        ChartUpdateParam chartUpdateParam = new ChartUpdateParam();\n        Optional.of(chartUpdateParam).ifPresent(param -> {\n            param.setId(1L);\n            param.setName(\"chat2db\");\n            param.setSchema(\"test\");\n            param.setDataSourceId(1L);\n            param.setType(\"DM\");\n            param.setDatabaseName(\"chat2db\");\n            param.setSchemaName(\"ali_dbhub\");\n            param.setDdl(\"test\");\n        });\n\n        ActionResult actionResult = chartService.updateWithPermission(chartUpdateParam);\n        assertNotNull(actionResult);\n    }\n\n\n    @Test\n    public void testFind() {\n        userLoginIdentity(false, 6L);\n//        userLoginIdentity(true, 8L);\n\n        DataResult<Chart> result = chartService.find(2L);\n        assertNotNull(result.getData());\n    }\n\n\n    @Test\n    public void testQueryExistent() {\n        userLoginIdentity(false, 7L);\n//        userLoginIdentity(true, 9L);\n\n        ChartQueryParam chartQueryParam = new ChartQueryParam();\n        chartQueryParam.setId(1L);\n        chartQueryParam.setUserId(1L);\n\n        DataResult<Chart> chartDataResult = chartService.queryExistent(chartQueryParam);\n        DataResult<Chart> queryExistent = chartService.queryExistent(chartDataResult.getData().getId());\n        assertNotNull(chartDataResult);\n        assertNotNull(queryExistent);\n        assertEquals(chartDataResult, queryExistent);\n    }\n\n\n    @Test\n    public void testListQuery() {\n        userLoginIdentity(false, 8L);\n//        userLoginIdentity(true, 10L);\n\n        ChartListQueryParam param = new ChartListQueryParam();\n        param.setIdList(Arrays.asList(4L, 5L, 6L));\n        param.setUserId(1L);\n\n        ListResult<Chart> listQuery = chartService.listQuery(param);\n        assertNotNull(listQuery);\n\n    }\n\n\n    @Test\n    public void testQueryByIds() {\n        userLoginIdentity(false, 9L);\n//        userLoginIdentity(true, 11L);\n\n        ListResult<Chart> chartListResult = chartService.queryByIds(Arrays.asList(1L, 2L, 3L));\n        assertNotNull(chartListResult.getData());\n    }\n\n    @Test\n    public void testDeleteWithPermission() {\n        userLoginIdentity(false, 10L);\n//        userLoginIdentity(true, 12L);\n\n        ActionResult actionResult = chartService.deleteWithPermission(3L);\n        assertNotNull(actionResult);\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/ConfigServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.SystemConfigParam;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.security.SecureRandom;\nimport java.util.Optional;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\npublic class ConfigServiceTest extends TestApplication {\n\n    @Autowired\n    private ConfigService configService;\n\n    @Test\n    public void testCreate() {\n        userLoginIdentity(true, 1L);\n//        userLoginIdentity(false, 2L);\n\n        SystemConfigParam systemConfigParam = new SystemConfigParam();\n        Optional.ofNullable(systemConfigParam).ifPresent(param -> {\n            param.setCode(RandomCodeGenerator.generateRandomCode(6));\n            param.setContent(RandomCodeGenerator.generateRandomCode(6));\n            param.setSummary(RandomCodeGenerator.generateRandomCode(6));\n        });\n\n        ActionResult actionResult = configService.create(systemConfigParam);\n        assertNotNull(actionResult);\n    }\n\n    @Test\n    public void testUpdate() {\n        userLoginIdentity(true, 4L);\n//        userLoginIdentity(false, 5L);\n\n        SystemConfigParam systemConfigParam = new SystemConfigParam();\n        systemConfigParam.setCode(RandomCodeGenerator.generateRandomCode(6));\n        systemConfigParam.setContent(RandomCodeGenerator.generateRandomCode(6));\n        systemConfigParam.setSummary(RandomCodeGenerator.generateRandomCode(6));\n\n        ActionResult update = configService.update(systemConfigParam);\n        assertNotNull(update);\n\n    }\n\n    @Test\n    public void testCreateOrUpdate() {\n        userLoginIdentity(true, 3L);\n//        userLoginIdentity(false, 6L);\n\n\n        SystemConfigParam systemConfigParam = new SystemConfigParam();\n        systemConfigParam.setCode(RandomCodeGenerator.generateRandomCode(6));\n        systemConfigParam.setContent(RandomCodeGenerator.generateRandomCode(6));\n        systemConfigParam.setSummary(RandomCodeGenerator.generateRandomCode(6));\n        ActionResult orUpdate = configService.createOrUpdate(systemConfigParam);\n        assertNotNull(orUpdate);\n\n    }\n\n    @Test\n    public void testFind() {\n        userLoginIdentity(true, 9L);\n//        userLoginIdentity(false, 4L);\n\n        DataResult<Config> configDataResult = configService.find(\"4TxfzW\");\n        assertNotNull(configDataResult.getData());\n    }\n\n    @Test\n    public void testDelete() {\n        userLoginIdentity(true, 11L);\n//        userLoginIdentity(false, 12L);\n\n        ActionResult result = configService.delete(\"4TxfzW\");\n        assertNotNull(result);\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n\n    public class RandomCodeGenerator {\n        private static final String CHARACTERS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n        private static final SecureRandom RANDOM = new SecureRandom();\n\n        public static String generateRandomCode(int length) {\n            StringBuilder sb = new StringBuilder(length);\n            for (int i = 0; i < length; i++) {\n                sb.append(CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length())));\n            }\n            return sb.toString();\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/ConsoleServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.param.ConsoleCloseParam;\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.service.ConsoleService;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\npublic class ConsoleServiceTest extends TestApplication {\n\n    @Autowired\n    private ConsoleService consoleService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testCreateAndCloseConsole() {\n        // MYSQL  ORACLE  POSTGRESQL\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            // creat\n            ConsoleConnectParam consoleCreateParam = new ConsoleConnectParam();\n            consoleCreateParam.setDataSourceId(dataSourceId);\n            consoleCreateParam.setConsoleId(consoleId);\n            consoleCreateParam.setDatabaseName(dialectProperties.getDatabaseName());\n            ActionResult console = consoleService.createConsole(consoleCreateParam);\n            assertNotNull(console);\n\n            // close\n            ConsoleCloseParam consoleCloseParam = new ConsoleCloseParam();\n            consoleCloseParam.setDataSourceId(dataSourceId);\n            consoleCloseParam.setConsoleId(consoleId);\n            consoleService.closeConsole(consoleCloseParam);\n            Chat2DBContext.removeContext();\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/DashboardServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.Dashboard;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardCreateParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardPageQueryParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardQueryParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardUpdateParam;\nimport ai.chat2db.server.domain.api.service.DashboardService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.tools.base.enums.YesOrNoEnum;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.ArrayList;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\npublic class DashboardServiceTest extends TestApplication {\n\n    @Autowired\n    private DashboardService dashboardService;\n\n    @Test\n    public void testCreateWithPermission() {\n        userLoginIdentity(false, 9L);\n//        userLoginIdentity(true, 11L);\n\n        DashboardCreateParam dashboardCreateParam = new DashboardCreateParam();\n        dashboardCreateParam.setName(\"chat2db\");\n        dashboardCreateParam.setSchema(\"test\");\n        dashboardCreateParam.setDescription(\"This is a test!\");\n        dashboardCreateParam.setDeleted(YesOrNoEnum.NO.getLetter());\n        dashboardCreateParam.setUserId(5L);\n        dashboardCreateParam.setChartIds(new ArrayList<Long>());\n\n        DataResult<Long> withPermission = dashboardService.createWithPermission(dashboardCreateParam);\n        assertNotNull(withPermission);\n    }\n\n    @Test\n    public void testUpdateWithPermission() {\n        // Note: Only administrators can edit this.\n        userLoginIdentity(true, 9L);\n\n        DashboardUpdateParam dashboardUpdateParam = new DashboardUpdateParam();\n        dashboardUpdateParam.setId(1L);\n        dashboardUpdateParam.setName(\"chat2db\");\n        dashboardUpdateParam.setSchema(\"test\");\n        dashboardUpdateParam.setDescription(\"This is a test!\");\n        dashboardUpdateParam.setDeleted(YesOrNoEnum.NO.getLetter());\n        dashboardUpdateParam.setUserId(5L);\n        dashboardUpdateParam.setChartIds(new ArrayList<Long>());\n\n        ActionResult actionResult = dashboardService.updateWithPermission(dashboardUpdateParam);\n        assertNotNull(actionResult);\n\n    }\n\n    @Test\n    public void testFind() {\n        userLoginIdentity(false, 4L);\n//        userLoginIdentity(true, 2L);\n\n        DataResult<Dashboard> find = dashboardService.find(2L);\n        assertNotNull(find.getData());\n    }\n\n    @Test\n    public void testQueryExistent() {\n        userLoginIdentity(false, 8L);\n\n        DashboardQueryParam param = new DashboardQueryParam();\n        param.setId(5L);\n        param.setUserId(9L);\n\n        DataResult<Dashboard> existent = dashboardService.queryExistent(param);\n        DataResult<Dashboard> dashboardDataResult = dashboardService.queryExistent(5L);\n        assertNotNull(existent.getData());\n        assertNotNull(dashboardDataResult.getData());\n        assertEquals(existent, dashboardDataResult);\n    }\n\n    @Test\n    public void testDeleteWithPermission() {\n        userLoginIdentity(false, 7L);\n//        userLoginIdentity(true, 4L);\n\n        DataResult<Dashboard> dashboardDataResult = dashboardService.find(4L);\n        if (dashboardDataResult.getData() != null) {\n            ActionResult actionResult = dashboardService.deleteWithPermission(dashboardDataResult.getData().getId());\n            assertNotNull(actionResult);\n        }\n\n    }\n\n    @Test\n    public void testQueryPage() {\n        userLoginIdentity(false, 12L);\n\n        DashboardPageQueryParam param = new DashboardPageQueryParam();\n        param.setUserId(5L);\n        param.setSearchKey(\"chat\");\n\n        PageResult<Dashboard> queryPage = dashboardService.queryPage(param);\n        assertNotNull(queryPage.getData());\n    }\n\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/DataSourceAccessBusinessServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessBusinessService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\npublic class DataSourceAccessBusinessServiceTest extends TestApplication {\n\n    @Autowired\n    private DataSourceAccessBusinessService dataSourceAccessBusinessService;\n\n    /**\n     * 1. First, determine whether it is a private data source (PRIVATE) based on the type of the data source.\n     * If it is a private data source, determine whether the currently logged-in user is the owner of the data source.\n     * If so, allow the operation, otherwise throw a permission exception.\n     * <p>\n     * 2. If the currently logged-in user is an administrator userLoginIdentity(true, **), the operation is allowed.\n     * If the currently logged-in user is a common user, determine whether the user has permission to access the data source.\n     * If so, the operation is allowed.\n     * <p>\n     * 3. If the team to which the currently logged-in user belongs has permission to access the data source, the operation is allowed.\n     * <p>\n     * 4.  If none of the above conditions are met, a permission exception is thrown.\n     */\n    @Test\n    public void testCheckPermission() {\n//        userLoginIdentity(false, 3L);\n        userLoginIdentity(true, 2L);\n\n        DataSource source = new DataSource();\n//        source.setKind(\"PRIVATE\");\n        source.setKind(\"SHARED\");\n        source.setUserId(5L);\n        source.setId(3L);\n\n        ActionResult actionResult = dataSourceAccessBusinessService.checkPermission(source);\n        assertNotNull(actionResult);\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/DataSourceAccessServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.DataSourceAccess;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessComprehensivePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessPageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessSelector;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\npublic class DataSourceAccessServiceTest extends TestApplication {\n\n    @Autowired\n    private DataSourceAccessService dataSourceAccessService;\n\n    @Test\n    public void testPageQuery() {\n//        userLoginIdentity(true,5L);\n        userLoginIdentity(false, 2L);\n\n\n        DataSourceAccessPageQueryParam queryParam = new DataSourceAccessPageQueryParam();\n        queryParam.setDataSourceId(TestUtils.nextLong());\n//        queryParam.setAccessObjectType(\"TEAM\");\n        queryParam.setAccessObjectType(\"USER\");\n        queryParam.setAccessObjectId(TestUtils.nextLong());\n        queryParam.setPageNo(3);\n        queryParam.setPageSize(5);\n\n        // Returns false by default\n        queryParam.setEnableReturnCount(true);\n\n\n        DataSourceAccessSelector accessSelector = new DataSourceAccessSelector();\n        accessSelector.setAccessObject(true);\n        accessSelector.setDataSource(true);\n        accessSelector.setDataSourceSelector(new DataSourceSelector(true));\n\n        PageResult<DataSourceAccess> result = dataSourceAccessService.pageQuery(queryParam, accessSelector);\n        assertNotNull(result);\n\n    }\n\n    @Test\n    public void testComprehensivePageQuery() {\n\n        userLoginIdentity(false, 2L);\n//        userLoginIdentity(true,5L);\n\n        DataSourceAccessComprehensivePageQueryParam param = new DataSourceAccessComprehensivePageQueryParam();\n        param.setPageNo(1);\n        param.setPageSize(10);\n        param.setEnableReturnCount(true);\n        param.setDataSourceId(TestUtils.nextLong());\n        param.setAccessObjectType(\"USER\");\n//        param.setAccessObjectType(\"TEAM\");\n        param.setAccessObjectId(TestUtils.nextLong());\n        param.setUserOrTeamSearchKey(\"test\");\n        param.setDataSourceSearchKey(\"m\");\n\n        DataSourceAccessSelector selector = new DataSourceAccessSelector();\n        selector.setAccessObject(true);\n        selector.setDataSource(true);\n        selector.setDataSourceSelector(new DataSourceSelector(true));\n\n        PageResult<DataSourceAccess> result = dataSourceAccessService.comprehensivePageQuery(param, selector);\n        assertNotNull(result);\n    }\n\n    @Test\n    public void testCreateAndDelete() {\n\n        userLoginIdentity(false, 8L);\n//        userLoginIdentity(true,6L);\n\n        DataSourceAccessCreatParam creatParam = new DataSourceAccessCreatParam();\n        creatParam.setDataSourceId(TestUtils.nextLong());\n        creatParam.setAccessObjectId(TestUtils.nextLong());\n        creatParam.setAccessObjectType(\"USER\");\n//        creatParam.setAccessObjectType(\"TEAM\");\n\n        DataResult<Long> result = dataSourceAccessService.create(creatParam);\n        assertNotNull(result);\n        ActionResult delete = dataSourceAccessService.delete(result.getData());\n        assertNotNull(delete);\n\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/DataSourceServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.datasource.*;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.param.OrderBy;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\npublic class DataSourceServiceTest extends TestApplication {\n\n    @Autowired\n    private DataSourceService dataSourceService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testCreateWithPermission() {\n//        userLoginIdentity(true, 1L);\n        userLoginIdentity(false, 2L);\n\n        DataSourceCreateParam createParam = new DataSourceCreateParam();\n        createParam.setKind(\"PRIVATE\");\n//        createParam.setKind(\"SHARED\");\n        createParam.setDriverConfig(new DriverConfig());\n\n        DataResult<Long> withPermission = dataSourceService.createWithPermission(createParam);\n        assertNotNull(withPermission.getData());\n\n    }\n\n    @Test\n    public void testUpdateWithPermission() {\n//        userLoginIdentity(true, 7L);\n        userLoginIdentity(false, 2L);\n\n        DataSourceUpdateParam updateParam = new DataSourceUpdateParam();\n        updateParam.setId(4L);\n        updateParam.setDriverConfig(new DriverConfig());\n        updateParam.setPassword(\"123456\");\n\n        DataResult<Long> result = dataSourceService.updateWithPermission(updateParam);\n        ActionResult delete = dataSourceService.deleteWithPermission(4L);\n        assertNotNull(result.getData());\n        assertNotNull(delete);\n\n    }\n\n    @Test\n    public void testQueryById() {\n        userLoginIdentity(false, 2L);\n//        userLoginIdentity(true, 7L);\n\n        DataResult<DataSource> result = dataSourceService.queryById(3L);\n        ListResult<DataSource> dataSourceListResult = dataSourceService.listQuery(new ArrayList<>(), null);\n        assertNotNull(result.getData());\n        assertNotNull(dataSourceListResult.getData());\n    }\n\n    @Test\n    public void testQueryExistent() {\n        userLoginIdentity(false, 2L);\n//        userLoginIdentity(true, 7L);\n\n        DataSourceSelector selector = new DataSourceSelector();\n        selector.setEnvironment(true);\n//        selector.setEnvironment(false);\n\n        DataResult<DataSource> result = dataSourceService.queryExistent(3L, null);\n        assertNotNull( result.getData(),\"Data should not be null\");\n    }\n\n    @Test\n    public void testCopyByIdWithPermission() {\n        userLoginIdentity(false, 2L);\n//        userLoginIdentity(true, 7L);\n\n        DataResult<Long> longDataResult = dataSourceService.copyByIdWithPermission(3L);\n        assertNotNull(longDataResult.getData());\n\n    }\n\n    @Test\n    public void testQueryPage() {\n        userLoginIdentity(false,6L);\n//        userLoginIdentity(true,9L);\n\n        DataSourcePageQueryParam queryParam = new DataSourcePageQueryParam();\n        queryParam.setSearchKey(\"test\");\n        queryParam.setPageNo(1);\n        queryParam.setPageSize(10);\n\n        DataSourceSelector selector = new DataSourceSelector();\n        selector.setEnvironment(true);\n//        selector.setEnvironment(false);\n\n        PageResult<DataSource> result = dataSourceService.queryPage(queryParam, selector);\n        assertNotNull(result.getData());\n    }\n\n    @Test\n    public void testQueryPageWithPermission() {\n//        userLoginIdentity(false,3L);\n        userLoginIdentity(true,9L);\n\n        DataSourcePageQueryParam queryParam = new DataSourcePageQueryParam();\n        queryParam.setSearchKey(\"test\");\n        queryParam.setKind(\"PRIVATE\");\n//        queryParam.setKind(\"SHARED\");\n        queryParam.setPageNo(1);\n        queryParam.setPageSize(10);\n        queryParam.setOrderByList(new ArrayList<OrderBy>());\n\n        DataSourceSelector selector = new DataSourceSelector();\n        selector.setEnvironment(true);\n\n        PageResult<DataSource> result = dataSourceService.queryPageWithPermission(queryParam, selector);\n        assertNotNull(result.getData());\n\n    }\n\n    @Test\n    public void testPreConnect() {\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n\n            DataSourcePreConnectParam param = new DataSourcePreConnectParam();\n            param.setType(dialectProperties.getDbType());\n            param.setUser(dialectProperties.getUsername());\n            param.setUrl(dialectProperties.getUrl());\n            param.setPassword(dialectProperties.getPassword());\n            param.setPort(String.valueOf(dialectProperties.getPort()));\n            param.setHost(\"localhost\");\n            param.setSsh(new SSHInfo());\n            param.setSsl(new SSLInfo());\n            param.setExtendInfo(new ArrayList<KeyValue>());\n\n            ActionResult result = dataSourceService.preConnect(param);\n            assertNotNull(result);\n\n            Long consoleId= TestUtils.nextLong();\n            Long dataSourceId= TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties,dataSourceId,consoleId);\n            ListResult<Database> connect = dataSourceService.connect(dataSourceId);\n            assertNotNull(connect.getData());\n\n            dataSourceService.close(dataSourceId);\n\n        }\n    }\n\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/DatabaseServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.param.MetaDataQueryParam;\nimport ai.chat2db.server.domain.api.param.SchemaOperationParam;\nimport ai.chat2db.server.domain.api.param.SchemaQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseQueryAllParam;\nimport ai.chat2db.server.domain.api.service.DatabaseService;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.MetaSchema;\nimport ai.chat2db.spi.model.Schema;\nimport ai.chat2db.spi.model.Sql;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\npublic class DatabaseServiceTest extends TestApplication {\n\n    @Autowired\n    private DatabaseService databaseService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testQueryAll() {\n\n        // MYSQL  ORACLE  POSTGRESQL MONGODB MARIADB\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbType = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DatabaseQueryAllParam queryAllParam = new DatabaseQueryAllParam();\n            queryAllParam.setDbType(dbType);\n            queryAllParam.setDataSourceId(dataSourceId);\n\n            ListResult<Database> result = databaseService.queryAll(queryAllParam);\n            assertNotNull(result.getData());\n        }\n\n    }\n\n    @Test\n    public void testQuerySchema() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            SchemaQueryParam schemaQueryParam = new SchemaQueryParam();\n            schemaQueryParam.setDataSourceId(dataSourceId);\n            schemaQueryParam.setDataBaseName(dialectProperties.getDatabaseName());\n\n            ListResult<Schema> schemaListResult = databaseService.querySchema(schemaQueryParam);\n            assertNotNull(schemaListResult.getData());\n        }\n    }\n\n    @Test\n    public void testQueryDatabaseSchema() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            MetaDataQueryParam param = new MetaDataQueryParam();\n            param.setDataSourceId(dataSourceId);\n            param.setRefresh(true);\n\n            DataResult<MetaSchema> metaSchemaDataResult = databaseService.queryDatabaseSchema(param);\n            assertNotNull(metaSchemaDataResult.getData());\n        }\n    }\n\n    @Test\n    public void testDeleteDatabase() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DatabaseCreateParam param = new DatabaseCreateParam();\n            param.setName(\"test\");\n\n            ActionResult actionResult = databaseService.deleteDatabase(param);\n            assertNotNull(actionResult);\n        }\n    }\n\n    @Test\n    public void testCreateDatabase() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            Database database = new Database();\n            DataResult<Sql> database1 = databaseService.createDatabase(database);\n            assertNotNull(database1);\n        }\n    }\n\n    @Test\n    public void testModifyDatabase() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DatabaseCreateParam databaseCreateParam = new DatabaseCreateParam();\n            databaseCreateParam.setName(\"test\" + TestUtils.nextLong());\n\n            ActionResult actionResult = databaseService.modifyDatabase(databaseCreateParam);\n            assertNotNull(actionResult);\n\n        }\n    }\n\n    @Test\n    public void testDeleteSchema() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            SchemaOperationParam operationParam = new SchemaOperationParam();\n            operationParam.setDatabaseName(dialectProperties.getDatabaseName());\n            operationParam.setSchemaName(\"test\" + TestUtils.nextLong());\n\n            ActionResult actionResult = databaseService.deleteSchema(operationParam);\n            assertNotNull(actionResult);\n        }\n    }\n\n\n    @Test\n    public void testCreateSchema() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            Schema schema = new Schema();\n            DataResult<Sql> result = databaseService.createSchema(schema);\n            assertNotNull(result);\n        }\n    }\n\n    @Test\n    public void testModifySchema() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            SchemaOperationParam schemaOperationParam = new SchemaOperationParam();\n            schemaOperationParam.setDatabaseName(dialectProperties.getDatabaseName());\n            schemaOperationParam.setSchemaName(\"test\" + TestUtils.nextLong());\n            schemaOperationParam.setNewSchemaName(\"test\" + TestUtils.nextLong());\n\n            ActionResult actionResult = databaseService.modifySchema(schemaOperationParam);\n            assertNotNull(actionResult);\n        }\n    }\n\n    // TODO：回头专门测试\n    @Test\n    public void testExportDatabase() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DatabaseExportParam exportParam = new DatabaseExportParam();\n            exportParam.setDatabaseName(dialectProperties.getDatabaseName());\n            exportParam.setContainData(true);\n            exportParam.setSchemaName(\"test\" + TestUtils.nextLong());\n\n            try {\n                String exportDatabase = databaseService.exportDatabase(exportParam);\n                assertNotNull(exportDatabase);\n            } catch (SQLException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/DlTemplateServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.param.DlCountParam;\nimport ai.chat2db.server.domain.api.param.DlExecuteParam;\nimport ai.chat2db.server.domain.api.param.OrderByParam;\nimport ai.chat2db.server.domain.api.param.UpdateSelectResultParam;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.spi.model.Header;\nimport ai.chat2db.spi.model.OrderBy;\nimport ai.chat2db.spi.model.ResultOperation;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.sql.Timestamp;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n\npublic class DlTemplateServiceTest extends TestApplication {\n\n    @Autowired\n    private DlTemplateService dlTemplateService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    // MYSQL: ali_dbhub_test -- test_data\n    // POSTGRESQL: ali_dbhub_test -- test -- test_data\n    // ORACLE: TEST_USER -- test_data\n    @Test\n    public void testExecute() {\n\n        userLoginIdentity(false, 6L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = 11L;\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            String testData = dialectProperties.getCrateTableSql(\"test_data006\");\n            DlExecuteParam dlExecuteParam = new DlExecuteParam();\n            dlExecuteParam.setSql(testData);\n            dlExecuteParam.setConsoleId(consoleId);\n            dlExecuteParam.setDataSourceId(dataSourceId);\n            dlExecuteParam.setTableName(\"test_data006\");\n            dlExecuteParam.setPageNo(1);\n            dlExecuteParam.setPageSize(10);\n            dlExecuteParam.setPageSizeAll(false);\n            if (dialectProperties.getDbType().equals(\"POSTGRESQL\")) {\n                dlExecuteParam.setDatabaseName(dialectProperties.getDatabaseName());\n                dlExecuteParam.setSchemaName(\"public\");\n            } else if (dialectProperties.getDbType().equals(\"ORACLE\")) {\n                dlExecuteParam.setDatabaseName(\"\");\n                dlExecuteParam.setSchemaName(\"TEST_USER\");\n            } else if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                dlExecuteParam.setDatabaseName(dialectProperties.getDatabaseName());\n                dlExecuteParam.setSchemaName(\"\");\n            } else {\n                continue;\n            }\n\n\n            ListResult<ExecuteResult> execute = dlTemplateService.execute(dlExecuteParam);\n            Assertions.assertTrue(execute.getSuccess(), execute.errorMessage());\n\n        }\n\n    }\n\n    @Test\n    public void testExecuteSelectTable() {\n\n        userLoginIdentity(false, 3L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = 20858L;\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                DlExecuteParam dlExecuteParam = new DlExecuteParam();\n                dlExecuteParam.setConsoleId(consoleId);\n                dlExecuteParam.setDataSourceId(dataSourceId);\n                dlExecuteParam.setTableName(\"test_data004\");\n                dlExecuteParam.setPageNo(1);\n                dlExecuteParam.setPageSize(10);\n                dlExecuteParam.setPageSizeAll(false);\n\n                ListResult<ExecuteResult> execute = dlTemplateService.executeSelectTable(dlExecuteParam);\n                Assertions.assertTrue(execute.getSuccess(), execute.errorMessage());\n            }\n\n        }\n    }\n\n    @Test\n    public void testExecuteUpdate() {\n\n        userLoginIdentity(false, 7L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            String testData = dialectProperties.getInsertSql(\"test_data006\", new Timestamp(new Date().getTime()), 1L, \"test\");\n            DlExecuteParam dlExecuteParam = new DlExecuteParam();\n            dlExecuteParam.setSql(testData);\n            dlExecuteParam.setConsoleId(consoleId);\n            dlExecuteParam.setDataSourceId(dataSourceId);\n            dlExecuteParam.setTableName(\"test_data006\");\n            dlExecuteParam.setPageNo(1);\n            dlExecuteParam.setPageSize(10);\n            dlExecuteParam.setPageSizeAll(false);\n            if (dialectProperties.getDbType().equals(\"POSTGRESQL\")) {\n                dlExecuteParam.setDatabaseName(dialectProperties.getDatabaseName());\n                dlExecuteParam.setSchemaName(\"public\");\n            } else if (dialectProperties.getDbType().equals(\"ORACLE\")) {\n                dlExecuteParam.setDatabaseName(\"\");\n                dlExecuteParam.setSchemaName(\"TEST_USER\");\n            } else if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                dlExecuteParam.setDatabaseName(dialectProperties.getDatabaseName());\n                dlExecuteParam.setSchemaName(\"\");\n            } else {\n                continue;\n            }\n\n\n            DataResult<ExecuteResult> result = dlTemplateService.executeUpdate(dlExecuteParam);\n            Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n\n        }\n    }\n\n    @Test\n    public void testCount() {\n\n        userLoginIdentity(true, 8L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DlCountParam param = new DlCountParam();\n            DataResult<Long> count = null;\n            if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                param.setSql(\"select * from test_data\");\n                count = dlTemplateService.count(param);\n                System.out.println(\"Mysql Total rows:\" + count.getData());\n                Assertions.assertTrue(count.getSuccess(), count.errorMessage());\n            } else if (dialectProperties.getDbType().equals(\"ORACLE\")) {\n                param.setSql(\"select * from TEST_USER.DEMO\");\n                count = dlTemplateService.count(param);\n                System.out.println(\"Oracle Total rows:\" + count.getData());\n                Assertions.assertTrue(count.getSuccess(), count.errorMessage());\n            }else if (dialectProperties.getDbType().equals(\"POSTGRESQL\")) {\n                param.setSql(\"select * from test.reference_table\");\n                count = dlTemplateService.count(param);\n                System.out.println(\"PG Total rows:\" + count.getData());\n                Assertions.assertTrue(count.getSuccess(), count.errorMessage());\n            } else if (dialectProperties.getDbType().equals(\"MARIADB\")) {\n                param.setSql(\"select * from test.test_data\");\n                count = dlTemplateService.count(param);\n                System.out.println(\"Mariadb Total rows:\" + count.getData());\n                Assertions.assertTrue(count.getSuccess(), count.errorMessage());\n            }\n        }\n\n    }\n\n    @Test\n    public void testUpdateSelectResult() {\n\n        userLoginIdentity(true, 8L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            UpdateSelectResultParam param = new UpdateSelectResultParam();\n            param.setTableName(\"test_data\");\n            param.setHeaderList(new ArrayList<Header>());\n            param.setOperations(new ArrayList<ResultOperation>());\n\n            DataResult<String> result = dlTemplateService.updateSelectResult(param);\n            System.out.println(\"result:\" + result.getData());\n            Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n        }\n\n    }\n\n    @Test\n    public void testGetOrderBySql() {\n\n        userLoginIdentity(false, 4L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                ArrayList<OrderBy> orderByList = new ArrayList<>();\n                OrderBy orderBy1 = new OrderBy();\n                orderBy1.setColumnName(\"number\");\n                orderBy1.setAsc(true);\n                orderByList.add(orderBy1);\n\n                OrderByParam orderByParam = new OrderByParam();\n                orderByParam.setOrderByList(orderByList);\n                orderByParam.setOriginSql(\"select * from test_data\");\n\n                DataResult<String> result = dlTemplateService.getOrderBySql(orderByParam);\n                System.out.println(\"Mysql Final Sql:\" + result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n\n            }\n        }\n\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/EnvironmentServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.Environment;\nimport ai.chat2db.server.domain.api.param.EnvironmentPageQueryParam;\nimport ai.chat2db.server.domain.api.service.EnvironmentService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : EnvironmentServiceTest.java\n */\npublic class EnvironmentServiceTest extends TestApplication {\n\n    @Autowired\n    private EnvironmentService environmentService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testListQuery() {\n\n        userLoginIdentity(false, 6L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            ArrayList<Long> list = new ArrayList<>();\n            list.add(1L);\n            list.add(2L);\n            list.add(3L);\n\n            ListResult<Environment> query = environmentService.listQuery(list);\n            Assertions.assertTrue(query.getSuccess(), query.getErrorMessage());\n            Assertions.assertFalse(query.getData().isEmpty(), \"Result should not be empty for non-empty input list\");\n        }\n    }\n\n    @Test\n    public void testPageQuery() {\n\n        userLoginIdentity(false, 3L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            EnvironmentPageQueryParam param = new EnvironmentPageQueryParam();\n            param.setSearchKey(\"release\");\n//            param.setSearchKey(\"test\");\n            param.setPageNo(1);\n            param.setPageSize(10);\n\n            PageResult<Environment> query = environmentService.pageQuery(param);\n            Assertions.assertTrue(query.getSuccess(), query.getErrorMessage());\n            Assertions.assertFalse(query.getData().isEmpty(), \"Result should not be empty for non-empty input list\");\n        }\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/FunctionServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.service.FunctionService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.model.Function;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : FunctionServiceTest.java\n */\npublic class FunctionServiceTest extends TestApplication {\n\n    @Autowired\n    private FunctionService functionService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testFunctions() {\n\n        userLoginIdentity(false, 3L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            ListResult<Function> functions = functionService.functions(dialectProperties.getDatabaseName(), null);\n            Assertions.assertTrue(functions.getSuccess(), functions.errorMessage());\n\n            if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                DataResult<Function> detail = functionService.detail(dialectProperties.getDatabaseName(), null, \"add_numbers\");\n                Assertions.assertTrue(detail.getSuccess(), detail.errorMessage());\n            }\n\n        }\n    }\n\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/JdbcDriverServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.service.JdbcDriverService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.config.DBConfig;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * @author Juechen\n * @version : JdbcDriverServiceTest.java\n */\npublic class JdbcDriverServiceTest extends TestApplication {\n\n    @Autowired\n    private JdbcDriverService jdbcDriverService;\n\n    @Test\n    public void testGetDrivers() {\n\n        userLoginIdentity(false, 2L);\n\n        String dbType = \"POSTGRESQL\";\n        DataResult<DBConfig> drivers = jdbcDriverService.getDrivers(dbType);\n        Assertions.assertTrue(drivers.success(), drivers.errorMessage());\n    }\n\n    @Test\n    public void testUpload() {\n\n        userLoginIdentity(false, 2L);\n\n        String dbType = \"MYSQL\";\n        ActionResult result = jdbcDriverService.upload(dbType, \"com.mysql.cj.jdbc.Driver\", \"mysql-connector-java-8.0.30.jar\");\n        Assertions.assertTrue(result.success(), result.errorMessage());\n    }\n\n    @Test\n    public void testDownload() {\n        userLoginIdentity(false, 5L);\n\n        String dbType = \"ORACLE\";\n        DataResult<DBConfig> drivers = jdbcDriverService.getDrivers(dbType);\n        Assertions.assertTrue(drivers.success(), drivers.errorMessage());\n    }\n\n\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/OperationLogServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.OperationLog;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogPageQueryParam;\nimport ai.chat2db.server.domain.api.service.OperationLogService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : OperationLogServiceTest.java\n */\npublic class OperationLogServiceTest extends TestApplication {\n\n    @Autowired\n    private OperationLogService operationLogService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n\n    @Test\n    public void testCreate() {\n\n        userLoginIdentity(true, 1L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            OperationLogCreateParam param = new OperationLogCreateParam();\n            param.setDataSourceId(dataSourceId);\n            param.setType(dialectProperties.getDbType());\n\n            DataResult<Long> result = operationLogService.create(param);\n            Assertions.assertTrue(result.success(), result.errorMessage());\n        }\n    }\n\n    @Test\n    public void testQueryPage() {\n        userLoginIdentity(false,14L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                OperationLogPageQueryParam param = new OperationLogPageQueryParam();\n                param.setDataSourceId(dataSourceId);\n                param.setSearchKey(\"test\");\n                param.setUserId(3L);\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchemaName(\"\");\n                param.setPageNo(1);\n                param.setPageSize(10);\n\n                PageResult<OperationLog> queryPage = operationLogService.queryPage(param);\n                System.out.println(queryPage.getData());\n                Assertions.assertTrue(queryPage.success(), queryPage.errorMessage());\n            }\n        }\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/OperationServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.Operation;\nimport ai.chat2db.server.domain.api.param.operation.OperationPageQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationSavedParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationUpdateParam;\nimport ai.chat2db.server.domain.api.service.OperationService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : OperationServiceTest.java\n */\npublic class OperationServiceTest extends TestApplication {\n\n    @Autowired\n    private OperationService operationService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n\n    @Test\n    public void testCreateWithPermission() {\n\n        userLoginIdentity(true, 7L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            OperationSavedParam param = new OperationSavedParam();\n            param.setDataSourceId(dataSourceId);\n            param.setType(dialectProperties.getDbType());\n//            param.setStatus(\"DRAFT\");\n            param.setStatus(\"RELEASE\");\n\n            DataResult<Long> result = operationService.createWithPermission(param);\n            System.out.println(dialectProperties.getDbType() + \"---\" + result.getData());\n            Assertions.assertTrue(result.success(), result.getErrorMessage());\n        }\n    }\n\n    @Test\n    public void testUpdateWithPermission() {\n\n        userLoginIdentity(true, 3L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            OperationUpdateParam param = new OperationUpdateParam();\n            param.setId(9L);\n\n            ActionResult result = operationService.updateWithPermission(param);\n            Assertions.assertTrue(result.success(), result.getErrorMessage());\n        }\n\n    }\n\n    @Test\n    public void testFind() {\n        userLoginIdentity(true, 6L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DataResult<Operation> result = operationService.find(18L);\n            Assertions.assertTrue(result.success(), result.getErrorMessage());\n        }\n    }\n\n    @Test\n    public void testQueryExistent() {\n        userLoginIdentity(false, 7L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            OperationQueryParam param = new OperationQueryParam();\n            param.setId(11L);\n\n            DataResult<Operation> result = operationService.queryExistent(10L);\n            DataResult<Operation> result1 = operationService.queryExistent(param);\n            Assertions.assertTrue(result.success(), result.getErrorMessage());\n            Assertions.assertTrue(result1.success(), result1.getErrorMessage());\n        }\n    }\n\n    @Test\n    public void testDeleteWithPermission() {\n\n        userLoginIdentity(true, 8L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            OperationSavedParam param = new OperationSavedParam();\n            param.setDataSourceId(10L);\n            param.setType(dialectProperties.getDbType());\n//            param.setStatus(\"DRAFT\");\n            param.setStatus(\"RELEASE\");\n\n            DataResult<Long> service = operationService.createWithPermission(param);\n            ActionResult result = operationService.deleteWithPermission(service.getData());\n            Assertions.assertTrue(result.success(), result.getErrorMessage());\n\n        }\n    }\n\n    @Test\n    public void testQueryPage() {\n\n        userLoginIdentity(false, 9L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            OperationPageQueryParam param = new OperationPageQueryParam();\n            param.setStatus(\"RELEASE\");\n            param.setSearchKey(\"test\");\n            param.setDataSourceId(dataSourceId);\n            param.setDatabaseName(dialectProperties.getDatabaseName());\n            param.setTabOpened(\"Y\");\n            param.setPageNo(1);\n            param.setPageSize(10);\n            param.setOrderByDesc(true);\n            param.setOrderByCreateDesc(true);\n\n            PageResult<Operation> result = operationService.queryPage(param);\n            Assertions.assertTrue(result.success(), result.getErrorMessage());\n\n        }\n\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/PinServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.param.PinTableParam;\nimport ai.chat2db.server.domain.api.service.PinService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : PinServiceImplTest.java\n */\npublic class PinServiceTest extends TestApplication {\n\n    @Autowired\n    private PinService pinService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testPinTable() {\n\n        userLoginIdentity(true, 7L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            PinTableParam param = new PinTableParam();\n            param.setDatabaseName(dialectProperties.getDatabaseName());\n            param.setUserId(7L);\n            param.setDataSourceId(dataSourceId);\n            param.setSchemaName(\"ali_dbhub_test\");\n            param.setTableName(\"t_user\");\n\n            ActionResult result = pinService.pinTable(param);\n            Assertions.assertTrue(result.success(), result.errorMessage());\n        }\n\n    }\n\n    @Test\n    public void testDeletePinTable() {\n\n        userLoginIdentity(false,8L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            PinTableParam param = new PinTableParam();\n            param.setDatabaseName(dialectProperties.getDatabaseName());\n            param.setUserId(91L);\n            param.setDataSourceId(dataSourceId);\n            param.setSchemaName(\"ali_dbhub_test\");\n            param.setTableName(\"t_user\");\n\n            ActionResult result = pinService.deletePinTable(param);\n            Assertions.assertTrue(result.success(), result.errorMessage());\n        }\n    }\n\n    @Test\n    public void testQueryPinTables() {\n\n        userLoginIdentity(false,8L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            PinTableParam param = new PinTableParam();\n            param.setDatabaseName(dialectProperties.getDatabaseName());\n            param.setUserId(18L);\n            param.setDataSourceId(dataSourceId);\n            param.setSchemaName(\"ali_dbhub_test\");\n            param.setTableName(\"t_user\");\n\n            ListResult<String> result = pinService.queryPinTables(param);\n            Assertions.assertTrue(result.success(), result.errorMessage());\n        }\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/ProcedureServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.service.ProcedureService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.model.Procedure;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : ProcedureServiceTest.java\n */\npublic class ProcedureServiceTest extends TestApplication {\n\n    @Autowired\n    private ProcedureService procedureService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testProcedures() {\n\n        userLoginIdentity(false, 2L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            String databaseName = \"ali_dbhub_test\";\n//            String databaseName = \"Northwind\";\n//            String databaseName = \"618\";\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId, databaseName);\n\n            if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                // No parameters are required here\n                ListResult<Procedure> result = procedureService.procedures(databaseName, \"\");\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n\n                for (Procedure procedure : result.getData()) {\n                    String procedureName = procedure.getProcedureName();\n                    DataResult<Procedure> detail = procedureService.detail(databaseName, \"\", procedureName);\n                    System.out.println(detail.getData().getProcedureBody());\n\n                    Assertions.assertTrue(detail.getSuccess(), detail.errorMessage());\n                }\n\n            } else if (dialectProperties.getDbType().equals(\"POSTGRESQL\")) {\n                ListResult<Procedure> result = procedureService.procedures(databaseName, \"test\");\n\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n\n                for (Procedure procedure : result.getData()) {\n                    String procedureName = procedure.getProcedureName();\n                    DataResult<Procedure> detail = procedureService.detail(databaseName, \"test\", procedureName);\n                    System.out.println(detail.getData().getProcedureBody());\n\n                    Assertions.assertTrue(detail.getSuccess(), detail.errorMessage());\n                }\n            } else if (dialectProperties.getDbType().equals(\"ORACLE\")) {\n                ListResult<Procedure> result = procedureService.procedures(\"\", \"TEST_USER\");\n\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n\n                for (Procedure procedure : result.getData()) {\n                    String procedureName = procedure.getProcedureName();\n                    DataResult<Procedure> detail = procedureService.detail(\"\", \"TEST_USER\", procedureName);\n                    System.out.println(detail.getData().getProcedureBody());\n\n                    Assertions.assertTrue(detail.getSuccess(), detail.errorMessage());\n                }\n            }\n        }\n\n    }\n\n    @Test\n    public void testUpdate() throws SQLException {\n\n        userLoginIdentity(false, 8L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            String databaseName = \"ali_dbhub_test\";\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId, databaseName);\n\n            if (dialectProperties.getDbType().equals(\"MYSQL\")) {\n                Procedure procedure = new Procedure();\n                procedure.setProcedureName(\"demo_procedure\");\n                procedure.setProcedureBody(\"CREATE PROCEDURE demo_procedure(IN param1 VARCHAR(50))\\n\" +\n                        \"BEGIN\\n\" +\n                        \"END;\");\n                ActionResult result = procedureService.update(databaseName, \"\", procedure);\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            } else if (dialectProperties.getDbType().equals(\"ORACLE\")) {\n                Procedure procedure = new Procedure();\n                procedure.setProcedureName(\"demo_procedure\");\n                procedure.setProcedureBody(\"CREATE OR REPLACE PROCEDURE demo_procedure12345 (\\n\" +\n                        \"    p_param1 NUMBER\\n\" +\n                        \")\\n\" +\n                        \"IS\\n\" +\n                        \"BEGIN\\n\" +\n                        \"END;\\n\");\n                ActionResult result = procedureService.update(\"\", \"TEST_USER\", procedure);\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            } else if (dialectProperties.getDbType().equals(\"POSTGRESQL\")) {\n                Procedure procedure = new Procedure();\n                procedure.setProcedureName(\"demo_procedure\");\n                procedure.setProcedureBody(\"CREATE OR REPLACE PROCEDURE demo_procedure(param123 VARCHAR)\\n\" +\n                        \"LANGUAGE plpgsql\\n\" +\n                        \"AS $$\\n\" +\n                        \"BEGIN\\n\" +\n                        \"END;\\n\" +\n                        \"$$;\");\n                ActionResult result = procedureService.update(databaseName, \"test\", procedure);\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            }\n        }\n\n    }\n\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/TableServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.param.DropParam;\nimport ai.chat2db.server.domain.api.param.ShowCreateTableParam;\nimport ai.chat2db.server.domain.api.param.TableQueryParam;\nimport ai.chat2db.server.domain.api.param.TableSelector;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.model.Table;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * @author Juechen\n * @version : TableServiceTest.java\n */\npublic class TableServiceTest extends TestApplication {\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testShowCreateTable() {\n\n        userLoginIdentity(false, 5L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (Objects.equals(dialectProperties.getDbType(), \"MYSQL\")) {\n                ShowCreateTableParam param = new ShowCreateTableParam();\n                param.setTableName(\"chart\");\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchemaName(\"\");\n\n                DataResult<String> result = tableService.showCreateTable(param);\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            } else if (Objects.equals(dialectProperties.getDbType(), \"ORACLE\")) {\n                ShowCreateTableParam param = new ShowCreateTableParam();\n                param.setTableName(\"DEMO\");\n                param.setDatabaseName(\"\");\n                param.setSchemaName(\"TEST_USER\");\n\n                DataResult<String> result = tableService.showCreateTable(param);\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            } else if (Objects.equals(dialectProperties.getDbType(), \"MARIADB\")) {\n                ShowCreateTableParam param = new ShowCreateTableParam();\n                param.setTableName(\"test_data\");\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchemaName(\"\");\n\n                DataResult<String> result = tableService.showCreateTable(param);\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            }\n        }\n\n    }\n\n    @Test\n    public void testDrop() {\n        userLoginIdentity(false, 6L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            String databaseName = dialectProperties.getDatabaseName();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId, databaseName);\n\n            if (Objects.equals(dialectProperties.getDbType(), \"MYSQL\")) {\n                DropParam param = new DropParam();\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchema(\"\");\n                param.setName(\"employee_details\");\n\n                ActionResult result = tableService.drop(param);\n                Assertions.assertTrue(result.success(), result.errorMessage());\n            } else if (Objects.equals(dialectProperties.getDbType(), \"ORACLE\")) {\n                DropParam param = new DropParam();\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchema(\"TEST_USER\");\n                param.setName(\"TEST_USER.DEMO\");\n\n                ActionResult result = tableService.drop(param);\n                Assertions.assertTrue(result.success(), result.errorMessage());\n            } else if (Objects.equals(dialectProperties.getDbType(), \"MARIADB\")) {\n                DropParam param = new DropParam();\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchema(\"\");\n                param.setName(\"test_data\");\n\n                ActionResult result = tableService.drop(param);\n                Assertions.assertTrue(result.success(), result.errorMessage());\n            } else if (Objects.equals(dialectProperties.getDbType(), \"POSTGRESQL\")) {\n                DropParam param = new DropParam();\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchema(\"test\");\n                param.setName(\"test.categories_2\");\n\n                ActionResult result = tableService.drop(param);\n                Assertions.assertTrue(result.success(), result.errorMessage());\n            }\n\n        }\n    }\n\n    @Test\n    public void testQuery() {\n        userLoginIdentity(false, 11L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (Objects.equals(dialectProperties.getDbType(), \"MYSQL\")) {\n                TableQueryParam param = new TableQueryParam();\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchemaName(\"\");\n                param.setTableName(\"access_control_apply_record\");\n                TableSelector selector = new TableSelector();\n                selector.setColumnList(true);\n                selector.setIndexList(false);\n\n                DataResult<Table> result = tableService.query(param, selector);\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            } else if (Objects.equals(dialectProperties.getDbType(), \"ORACLE\")) {\n                TableQueryParam param = new TableQueryParam();\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchemaName(\"TEST_USER\");\n                param.setTableName(\"DEMO_TABLE\");\n                TableSelector selector = new TableSelector();\n                selector.setColumnList(true);\n                selector.setIndexList(false);\n\n                DataResult<Table> result = tableService.query(param, selector);\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            } else if (Objects.equals(dialectProperties.getDbType(), \"POSTGRESQL\")) {\n                TableQueryParam param = new TableQueryParam();\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchemaName(\"test\");\n                param.setTableName(\"dept\");\n                TableSelector selector = new TableSelector();\n                selector.setColumnList(true);\n                selector.setIndexList(false);\n\n                DataResult<Table> result = tableService.query(param, selector);\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.getSuccess(), result.errorMessage());\n            }\n        }\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/TaskServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.Task;\nimport ai.chat2db.server.domain.api.param.TaskCreateParam;\nimport ai.chat2db.server.domain.api.param.TaskPageParam;\nimport ai.chat2db.server.domain.api.param.TaskUpdateParam;\nimport ai.chat2db.server.domain.api.service.TaskService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : TaskServiceTest.java\n */\npublic class TaskServiceTest extends TestApplication {\n\n    @Autowired\n    private TaskService taskService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testCreate() {\n\n        userLoginIdentity(true, 9L);\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (dialectProperties.getDbType().equalsIgnoreCase(\"MYSQL\")) {\n                TaskCreateParam param = new TaskCreateParam();\n                param.setDataSourceId(dataSourceId);\n                param.setDatabaseName(dialectProperties.getDatabaseName());\n                param.setSchemaName(\"\");\n                param.setTableName(\"access_token\");\n                param.setUserId(9L);\n                // INIT -> DOWNLOAD_DATA, UPLOAD_TABLE_DATA, DOWNLOAD_TABLE_STRUCTURE, UPLOAD_TABLE_STRUCTURE\n                param.setTaskType(\"DOWNLOAD_DATA\");\n                param.setTaskName(\"juechen\");\n\n                DataResult<Long> result = taskService.create(param);\n                DataResult<Task> taskDataResult = taskService.get(result.getData());\n                System.out.println(taskDataResult.getData());\n                Assertions.assertTrue(result.success(), result.errorMessage());\n            }\n        }\n\n    }\n\n    @Test\n    public void testPage() {\n        userLoginIdentity(true, 12L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (dialectProperties.getDbType().equalsIgnoreCase(\"MYSQL\")) {\n                TaskPageParam param = new TaskPageParam();\n                param.setPageNo(1);\n                param.setPageSize(10);\n                param.setUserId(9L);\n\n                PageResult<Task> result = taskService.page(param);\n                System.out.println(result.getData());\n                Assertions.assertTrue(result.success(), result.errorMessage());\n            }\n        }\n    }\n\n    @Test\n    public void testUpdateStatus() {\n        userLoginIdentity(true, 5L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (dialectProperties.getDbType().equalsIgnoreCase(\"MYSQL\")) {\n                TaskUpdateParam param = new TaskUpdateParam();\n                param.setId(9L);\n                // DOWNLOAD_DATA, UPLOAD_TABLE_DATA, DOWNLOAD_TABLE_STRUCTURE, UPLOAD_TABLE_STRUCTURE\n                param.setTaskStatus(\"DOWNLOAD_TABLE_STRUCTURE\");\n                param.setContent(new byte[0]);\n                param.setDownloadUrl(\"success!\");\n\n                ActionResult result = taskService.updateStatus(param);\n                DataResult<Task> taskDataResult = taskService.get(param.getId());\n                System.out.println(taskDataResult.getData());\n                Assertions.assertTrue(result.success(), result.errorMessage());\n            }\n        }\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/TeamServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.Team;\nimport ai.chat2db.server.domain.api.param.team.TeamCreateParam;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.TeamSelector;\nimport ai.chat2db.server.domain.api.param.team.TeamUpdateParam;\nimport ai.chat2db.server.domain.core.impl.TeamServiceImpl;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.ArrayList;\n\n/**\n * @author Juechen\n * @version : TeamServiceTest.java\n */\npublic class TeamServiceTest extends TestApplication {\n\n    @Autowired\n    private TeamServiceImpl teamService;\n\n    @Test\n    public void testCreate() {\n        userLoginIdentity(false,8L);\n        TeamCreateParam param = new TeamCreateParam();\n        param.setCode(\"57935\");\n        param.setStatus(\"VALID\");\n        param.setRoleCode(\"87129\");\n        param.setName(\"DEMO3\");\n        param.setDescription(\"this is just a test!\");\n\n        DataResult<Long> result = teamService.create(param);\n        System.out.println(\"create team_id :\" + result.getData());\n        Assertions.assertTrue(result.getSuccess(), result.getErrorMessage());\n\n        ArrayList<Long> list = new ArrayList<>();\n        list.add(result.getData());\n        ListResult<Team> result1 = teamService.listQuery(list);\n        System.out.println(\"current team :\" + result1.getData());\n        Assertions.assertTrue(result1.getSuccess(), result1.getErrorMessage());\n\n        TeamPageQueryParam pageQueryParam = new TeamPageQueryParam();\n        pageQueryParam.setPageNo(1);\n        pageQueryParam.setPageSize(10);\n        pageQueryParam.setSearchKey(\"MO\");\n        pageQueryParam.setEnableReturnCount(true);\n        pageQueryParam.setOrderByList(new ArrayList<>());\n        TeamSelector selector = new TeamSelector();\n        selector.setModifiedUser(false);\n\n        PageResult<Team> result2 = teamService.pageQuery(pageQueryParam, selector);\n        for (Team team : result2.getData()) {\n            System.out.println(\"pageList :\" + team + \"/n\");\n        }\n        Assertions.assertTrue(result2.getSuccess(), result2.getErrorMessage());\n\n        TeamUpdateParam teamUpdateParam = new TeamUpdateParam();\n        teamUpdateParam.setId(result.getData());\n        teamUpdateParam.setStatus(\"INVALID\");\n        teamUpdateParam.setDescription(\"already update!\");\n        teamUpdateParam.setName(\"Juechen\");\n\n        DataResult<Long> result3 = teamService.update(teamUpdateParam);\n        System.out.println(\"update team_id :\" + result3.getData());\n        Assertions.assertTrue(result3.getSuccess(), result3.getErrorMessage());\n        ArrayList<Long> list2 = new ArrayList<>();\n        list2.add(result.getData());\n        ListResult<Team> result4 = teamService.listQuery(list);\n        System.out.println(\"current team :\" + result4.getData());\n        Assertions.assertTrue(result4.getSuccess(), result4.getErrorMessage());\n\n        ActionResult delete = teamService.delete(result3.getData());\n        Assertions.assertTrue(delete.getSuccess(), delete.getErrorMessage());\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/TeamUserServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.TeamUser;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserComprehensivePageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserCreatParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserSelector;\nimport ai.chat2db.server.domain.api.service.TeamUserService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * @author Juechen\n * @version : TeamUserServiceTest.java\n */\npublic class TeamUserServiceTest extends TestApplication {\n\n    @Autowired\n    private TeamUserService teamUserService;\n\n    @Test\n    public void testPageQuery() {\n        userLoginIdentity(false,4L);\n\n        TeamUserCreatParam teamUserCreatParam = new TeamUserCreatParam();\n        teamUserCreatParam.setTeamId(1L);\n        teamUserCreatParam.setUserId(5L);\n\n        DataResult<Long> longDataResult = teamUserService.create(teamUserCreatParam);\n        System.out.println(\"create id :\" + longDataResult.getData());\n        Assertions.assertTrue(longDataResult.getSuccess(),longDataResult.getErrorMessage());\n\n        TeamUserPageQueryParam param = new TeamUserPageQueryParam();\n        param.setTeamId(teamUserCreatParam.getTeamId());\n        param.setUserId(teamUserCreatParam.getUserId());\n        TeamUserSelector selector = new TeamUserSelector();\n        selector.setTeam(true);\n        selector.setUser(true);\n\n        PageResult<TeamUser> pageQuery = teamUserService.pageQuery(param, selector);\n        System.out.println(\"value :\" + pageQuery.getData());\n        Assertions.assertTrue(pageQuery.getSuccess(),pageQuery.getErrorMessage());\n\n        TeamUserComprehensivePageQueryParam pageQueryParam = new TeamUserComprehensivePageQueryParam();\n        pageQueryParam.setPageNo(1);\n        pageQueryParam.setPageSize(10);\n        pageQueryParam.setUserId(teamUserCreatParam.getUserId());\n        pageQueryParam.setTeamId(teamUserCreatParam.getTeamId());\n        pageQueryParam.setTeamSearchKey(\"DE\");\n        PageResult<TeamUser> comprehensivePageQuery = teamUserService.comprehensivePageQuery(pageQueryParam, selector);\n        System.out.println(\"total value :\" + comprehensivePageQuery.getData());\n        Assertions.assertTrue(comprehensivePageQuery.getSuccess(),comprehensivePageQuery.getErrorMessage());\n\n        ActionResult actionResult = teamUserService.delete(longDataResult.getData());\n        Assertions.assertTrue(actionResult.getSuccess(),actionResult.getErrorMessage());\n\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/TriggerServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.service.TriggerService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.model.Trigger;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : TriggerServiceTest.java\n */\npublic class TriggerServiceTest extends TestApplication {\n\n    @Autowired\n    private TriggerService triggerService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testTriggers() {\n        userLoginIdentity(false,9L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (dialectProperties.getDbType().equalsIgnoreCase(\"mysql\")) {\n                String databaseName = \"ali_dbhub_test\";\n                ListResult<Trigger> triggers = triggerService.triggers(databaseName, \"\");\n                for (Trigger trigger : triggers.getData()) {\n                    DataResult<Trigger> detail = triggerService.detail(databaseName, \"\", trigger.getTriggerName());\n                    System.out.println(detail.getData());\n                }\n                Assertions.assertTrue(triggers.getSuccess(), triggers.getErrorMessage());\n            } else if (dialectProperties.getDbType().equalsIgnoreCase(\"postgresql\")) {\n                String databaseName = \"ali_dbhub_test\";\n                ListResult<Trigger> triggers = triggerService.triggers(databaseName, \"test\");\n                for (Trigger trigger : triggers.getData()) {\n                    DataResult<Trigger> detail = triggerService.detail(databaseName, \"test\", trigger.getTriggerName());\n                    System.out.println(detail.getData());\n                }\n                Assertions.assertTrue(triggers.getSuccess(), triggers.getErrorMessage());\n            } else if (dialectProperties.getDbType().equalsIgnoreCase(\"oracle\")) {\n                String schemaName = \"TEST_USER\";\n                ListResult<Trigger> triggers = triggerService.triggers(\"\", schemaName);\n                for (Trigger trigger : triggers.getData()) {\n                    DataResult<Trigger> detail = triggerService.detail(\"\", schemaName, trigger.getTriggerName());\n                    System.out.println(detail.getData());\n                }\n                Assertions.assertTrue(triggers.getSuccess(), triggers.getErrorMessage());\n            }\n        }\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/UserServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.param.user.UserCreateParam;\nimport ai.chat2db.server.domain.api.param.user.UserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.user.UserSelector;\nimport ai.chat2db.server.domain.api.param.user.UserUpdateParam;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * @author Juechen\n * @version : UserServiceTest.java\n */\npublic class UserServiceTest extends TestApplication {\n\n    @Autowired\n    private UserService userService;\n\n    @Test\n    public void testAllMethods() {\n        userLoginIdentity(false,8L);\n\n        UserCreateParam userCreateParam = new UserCreateParam();\n        userCreateParam.setUserName(\"test_username08\");\n        userCreateParam.setEmail(\"123456789@gmail.com\");\n        userCreateParam.setPassword(\"123456\");\n        userCreateParam.setRoleCode(\"TEST\");\n        userCreateParam.setStatus(\"VALID\");\n        userCreateParam.setNickName(\"test_username686\");\n        DataResult<Long> dataResult = userService.create(userCreateParam);\n        System.out.println(\"create id:\" + dataResult.getData());\n        Assertions.assertTrue(dataResult.getSuccess(),dataResult.getErrorMessage());\n\n        DataResult<User> query = userService.query(dataResult.getData());\n        System.out.println(\"Specify id：\" + query.getData());\n        Assertions.assertTrue(query.getSuccess(),query.getErrorMessage());\n\n        DataResult<User> user_name = userService.query(\"_desktop_default_user_name\");\n        System.out.println(\"Specify user_name: \" + user_name.getData());\n        Assertions.assertTrue(user_name.getSuccess(),user_name.getErrorMessage());\n\n        UserPageQueryParam param = new UserPageQueryParam();\n        param.setPageNo(1);\n        param.setPageSize(8);\n        param.setEnableReturnCount(false);\n        param.setSearchKey(\"\");\n        UserSelector selector = new UserSelector();\n        selector.setModifiedUser(false);\n\n        PageResult<User> result = userService.pageQuery(param, selector);\n        for (User user : result.getData()) {\n            System.out.println(\"list:\" + user);\n        }\n        Assertions.assertTrue(result.getSuccess(),result.getErrorMessage());\n\n        // if id is 1, an BusinessException will be thrown\n        ActionResult actionResult = userService.delete(dataResult.getData());\n        Assertions.assertTrue(actionResult.getSuccess(),actionResult.getErrorMessage());\n\n        PageResult<User> pageQuery = userService.pageQuery(param, selector);\n        for (User user : pageQuery.getData()) {\n            System.out.println(\"After deletion list:\" + user);\n        }\n        Assertions.assertTrue(pageQuery.getSuccess(),pageQuery.getErrorMessage());\n\n    }\n\n    @Test\n    public void testUpdate() {\n        userLoginIdentity(false,8L);\n\n        UserUpdateParam userUpdateParam = new UserUpdateParam();\n        // If the id is 1, a \"user.canNotOperateSystemAccount\" exception will be thrown.\n        // userUpdateParam.setId(1L);\n        userUpdateParam.setId(3L);\n        userUpdateParam.setRoleCode(\"TEST05\");\n        userUpdateParam.setStatus(\"INVALID\");\n        userUpdateParam.setEmail(\"385962@gmail.com\");\n        userUpdateParam.setPassword(\"385962\");\n\n        DataResult<User> query = userService.query(userUpdateParam.getId());\n        System.out.println(\"Original data :\" + query.getData());\n        Assertions.assertTrue(query.getSuccess(),query.getErrorMessage());\n\n        DataResult<Long> update = userService.update(userUpdateParam);\n        System.out.println(\"update id :\" + update.getData());\n        Assertions.assertTrue(update.getSuccess(),update.getErrorMessage());\n\n        DataResult<User> result = userService.query(userUpdateParam.getId());\n        System.out.println(\"update data :\" + result.getData());\n        Assertions.assertTrue(result.getSuccess(),result.getErrorMessage());\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/ViewServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.service.ViewService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.start.test.TestApplication;\nimport ai.chat2db.server.start.test.dialect.DialectProperties;\nimport ai.chat2db.server.start.test.dialect.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.spi.model.Table;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * @author Juechen\n * @version : ViewServiceTest.java\n */\npublic class ViewServiceTest extends TestApplication {\n\n    @Autowired\n    private ViewService viewService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    public void testViews() {\n        userLoginIdentity(false, 9L);\n\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            if (dialectProperties.getDbType().equalsIgnoreCase(\"mysql\")) {\n                String databaseName = \"ali_dbhub_test\";\n                ListResult<Table> views = viewService.views(databaseName, null);\n                for (Table table : views.getData()) {\n                    DataResult<Table> detail = viewService.detail(databaseName, null, table.getName());\n                    System.out.println(\"mysql:\" + detail.getData());\n                }\n                Assertions.assertTrue(views.getSuccess(),views.getErrorMessage());\n            } else if (dialectProperties.getDbType().equalsIgnoreCase(\"postgresql\")) {\n                String databaseName = \"ali_dbhub_test\";\n                String schemaName = \"test\";\n                ListResult<Table> views = viewService.views(databaseName, schemaName);\n                for (Table table : views.getData()) {\n                    DataResult<Table> detail = viewService.detail(databaseName, schemaName, table.getName());\n                    System.out.println(\"postgresql:\" + detail.getData());\n                }\n                Assertions.assertTrue(views.getSuccess(),views.getErrorMessage());\n            } else if (dialectProperties.getDbType().equalsIgnoreCase(\"oracle\")) {\n                String schemaName = \"TEST_USER\";\n                ListResult<Table> views = viewService.views(\"\", schemaName);\n                for (Table table : views.getData()) {\n                    DataResult<Table> detail = viewService.detail(\"\", schemaName, table.getName());\n                    System.out.println(\"oracle:\" + detail.getData());\n                }\n                Assertions.assertTrue(views.getSuccess(),views.getErrorMessage());\n            }\n        }\n    }\n\n    /**\n     * Save the current user identity (administrator or normal user) and user ID to the context and database session for subsequent use.\n     *\n     * @param isAdmin\n     * @param userId\n     */\n    private static void userLoginIdentity(boolean isAdmin, Long userId) {\n        Context context = Context.builder().loginUser(\n                LoginUser.builder().admin(isAdmin).id(userId).build()\n        ).build();\n        ContextUtils.setContext(context);\n        Dbutils.setSession();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/core/WebhookServiceTest.java",
    "content": "package ai.chat2db.server.start.test.core;\n\nimport ai.chat2db.server.domain.api.param.message.MessageCreateParam;\nimport ai.chat2db.server.domain.core.notification.BaseWebhookSender;\nimport ai.chat2db.server.start.test.TestApplication;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n\n/**\n * @author Juechen\n * @version : WebhookServiceTest.java\n */\npublic class WebhookServiceTest extends TestApplication {\n\n    @Autowired\n    private BaseWebhookSender baseWebhookSender;\n\n    @Test\n    public void test() {\n        MessageCreateParam param = new MessageCreateParam();\n//        param.setServiceUrl(\"https://oapi.dingtalk.com/robot/send?access_token=3dc1c8a55a3ba966d38fb37466c93c536ac210895304e2682966252ea8f8a252\");\n//        param.setSecretKey(\"SEC5058616c6ea2e5745abeb381d510579538ea5baa7cdd28a386c809289b1f1db9\");\n//        param.setPlatformType(\"DingTalk\");\n//        param.setTextTemplate(\"你好，钉钉！\");\n\n        param.setServiceUrl(\"https://open.feishu.cn/open-apis/bot/v2/hook/da4c4585-b320-4a72-8fbe-920b48c4a0c9\");\n        param.setSecretKey(\"tm3p2x2IBs8Lh8cBiJo1F\");\n        param.setPlatformType(\"LaRK\");\n        param.setTextTemplate(\"你好，飞书\");\n\n//        param.setServiceUrl(\"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=346b7d1e-39bd-4146-89e4-bca5fe05f5b4\");\n//        param.setSecretKey(\"\");\n//        param.setPlatformType(\"WeCom\");\n//        param.setTextTemplate(\"你好，企业微信\");\n        baseWebhookSender.sendMessage(param);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/dialect/DialectProperties.java",
    "content": "package ai.chat2db.server.start.test.dialect;\n\nimport java.util.Date;\n\n/**\n * Dialect configuration\n */\npublic interface DialectProperties {\n\n    /**\n     * Supported database types\n     *\n     * @return\n     */\n    String getDbType();\n\n    /**\n     * connection\n     *\n     * @return\n     */\n    String getUrl();\n\n    /**\n     * Abnormal connection\n     *\n     * @return\n     */\n    String getErrorUrl();\n\n    /**\n     * userName\n     *\n     * @return\n     */\n\n    String getUsername();\n\n    /**\n     * password\n     *\n     * @return\n     */\n    String getPassword();\n\n    /**\n     *  Name database\n     *\n     * @return\n     */\n    String getDatabaseName();\n\n    /**\n     * The case depends on the specific database:\n     * Create table structure: test table\n     * Field:\n     * id primary key auto-increment\n     * date date is not empty\n     * number long integer type\n     * string string length 100 default value \"DATA\"\n     *\n     * Index (plus $tableName_ because some database indexes are globally unique):\n     * $tableName_idx_date date index reverse order\n     * $tableName_uk_number unique index\n     * $tableName_idx_number_string joint index\n     *\n     * @return\n     */\n    String getCrateTableSql(String tableName);\n\n    /**\n     * Create table structure\n     *\n     * @return\n     */\n    String getDropTableSql(String tableName);\n\n    /**\n     * Create a piece of data\n     *\n     * @return\n     */\n    String getInsertSql(String tableName, Date date, Long number, String string);\n\n    /**\n     * Query a query sql\n     *\n     * @return\n     */\n    String getSelectSqlById(String tableName, Long id);\n\n    /**\n     * Get a sql whose table structure does not exist\n     *\n     * @return\n     */\n    String getTableNotFoundSqlById(String tableName);\n\n    /**\n     * Convert case\n     * Some database table structures store uppercase letters by default\n     * Some databases store lowercase by default\n     *\n     * @param string\n     * @return\n     */\n    String toCase(String string);\n\n    /**\n     * port\n     * @return\n     */\n    Integer getPort();\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/dialect/MariadbDialectProperties.java",
    "content": "package ai.chat2db.server.start.test.dialect;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n\n@Component\npublic class MariadbDialectProperties implements DialectProperties{\n    @Override\n    public String getDbType() {\n        return \"MARIADB\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:mariadb://localhost:3303/\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:mariadb://error:3303/\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"root\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return null;\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        return null;\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return null;\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return null;\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return null;\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return null;\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n\n    @Override\n    public Integer getPort() {\n        return 13303;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/dialect/MongodbDialectProperties.java",
    "content": "package ai.chat2db.server.start.test.dialect;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n\n@Component\npublic class MongodbDialectProperties implements DialectProperties{\n    @Override\n    public String getDbType() {\n        return \"MONGODB\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"mongodb://localhost:27017/\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"mongodb://error:27017/\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"test\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"test@123456\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return null;\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        return null;\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return null;\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return null;\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return null;\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return null;\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n\n    @Override\n    public Integer getPort() {\n        return 27017;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/dialect/MysqlDialectProperties.java",
    "content": "package ai.chat2db.server.start.test.dialect;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n\n/**\n * mysql\n *\n * @author Jiaju Zhuang\n */\n@Component\npublic class MysqlDialectProperties implements DialectProperties {\n\n    @Override\n    public String getDbType() {\n        return \"MYSQL\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:mysql://localhost:3306\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:mysql://error.rm-8vb099vo8309mcngk.mysql.zhangbei.rds.aliyuncs.com:3306\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"root\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"ali_dbhub_test\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        return \"CREATE TABLE `\" + tableName + \"`\\n\\t\"\n            + \"(\\n\\t\"\n            + \"    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'Primary key auto-increment',\\n\\t\"\n            + \"    `date`   datetime(3)                          not null COMMENT 'date',\\n\\t\"\n            + \"    `number` bigint COMMENT 'long integer',\\n\\t\"\n            + \"    `string` VARCHAR(100) default 'DATA' COMMENT 'name',\\n\\t\"\n            + \"    index \" + tableName + \"_idx_date (date desc) comment 'date index',\\n\\t\"\n            + \"    unique \" + tableName + \"_uk_number (number) comment 'unique index',\\n\\t\"\n            + \"    index \" + tableName + \"_idx_number_string (number, date) comment 'Union index'\\n\\t\"\n            + \") COMMENT ='Test table';\";\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO `\" + tableName + \"` (date,number,string) VALUES ('\" + DateUtil.format(date,\n            DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\\t\"\n            + \"from \" + tableName + \"\\n\\t\"\n            + \"where `id` = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\"\n            + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n\n    @Override\n    public Integer getPort() {\n        return 3306;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/dialect/OracleDialectProperties.java",
    "content": "package ai.chat2db.server.start.test.dialect;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n@Component\npublic class OracleDialectProperties implements DialectProperties {\n\n    @Override\n    public String getDbType() {\n        return \"ORACLE\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:oracle:thin:@localhost:1521:XE\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:oracle:thin:@localhost:1521:XE1\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"system\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"TEST_USER\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        return \"CREATE TABLE TEST_USER.\" + tableName + \" (\\n\" +\n                \"  id NUMBER PRIMARY KEY,\\n\" +\n                \"  created_date DATE,\\n\" +\n                \"  amount INT,\\n\" +\n                \"  string VARCHAR2(100)\\n\" +\n                \");\";\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO TEST_USER.\" + tableName + \" (date,number,string) VALUES ('\" + DateUtil.format(date,\n                DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\\t\"\n                + \"from \" + tableName + \"\\n\\t\"\n                + \"where `id` = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\"\n                + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n\n    @Override\n    public Integer getPort() {\n        return 11521;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/dialect/PostgresqlDialectProperties.java",
    "content": "/**\n * Alipay.com Inc.\n * Copyright (c) 2004-2022 All Rights Reserved.\n */\npackage ai.chat2db.server.start.test.dialect;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n\n/**\n * @author jipengfei\n * @version : PgDialectProperties.java, v 0.1 December 13, 2022 21:48 jipengfei Exp $\n */\n@Component\npublic class PostgresqlDialectProperties implements DialectProperties {\n\n    @Override\n    public String getDbType() {\n        return \"POSTGRESQL\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:postgresql://localhost:5431/ali_dbhub_test\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:postgresql://error:5431/ali_dbhub\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"ali_dbhub_test\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        String sql = \"CREATE TABLE \" + tableName + \"\\n\"\n            + \"(\\n\"\n            + \"    id     serial\\n\"\n            + \"        constraint \" + tableName + \"_pk primary key,\\n\"\n            + \"    date   timestamp,\\n\"\n            + \"    number int,\\n\"\n            + \"    string varchar(100) default 'DATA'\\n\"\n            + \");\\n\";\n        return sql;\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO \" + tableName + \" (date,number,string) VALUES ('\" + DateUtil.format(date,\n            DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\"\n            + \"from \" + tableName + \"\\n\"\n            + \"where id = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\"\n            + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n\n    @Override\n    public Integer getPort() {\n        return 5432;\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/dialect/TestUtils.java",
    "content": "package ai.chat2db.server.start.test.dialect;\n\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * Test tool class\n */\npublic class TestUtils {\n\n    public static final AtomicLong ATOMIC_LONG = new AtomicLong();\n\n    /**\n     * a globally unique long\n     *\n     * @return\n     */\n    public static long nextLong() {\n        return ATOMIC_LONG.incrementAndGet();\n    }\n\n    /**\n     * If the default value is something like 'DATA'\n     * then you need to remove ''\n     *\n     * @param defaultValue\n     * @return\n     */\n    public static String unWrapperDefaultValue(String defaultValue) {\n        if (defaultValue == null) {\n            return null;\n        }\n        if (defaultValue.startsWith(\"'\") && defaultValue.endsWith(\"'\")) {\n            if (defaultValue.length() < 2) {\n                return defaultValue;\n            } else if (defaultValue.length() == 2) {\n                return \"\";\n            } else {\n                return defaultValue.substring(1, defaultValue.length() - 1);\n            }\n        }\n        return defaultValue;\n    }\n\n    public static void buildContext(DialectProperties dialectProperties, Long dataSourceId, Long consoleId) {\n        buildContext(dialectProperties, dataSourceId, consoleId, dialectProperties.getDatabaseName());\n    }\n\n    public static void buildContext(DialectProperties dialectProperties, Long dataSourceId, Long consoleId, String databaseName) {\n        ConnectInfo connectInfo = createConnectInfo(dialectProperties, dataSourceId, consoleId, databaseName);\n        Chat2DBContext.putContext(connectInfo);\n    }\n\n    private static ConnectInfo createConnectInfo(DialectProperties dialectProperties, Long dataSourceId, Long consoleId, String databaseName) {\n        ConnectInfo connectInfo = new ConnectInfo();\n        connectInfo.setUser(dialectProperties.getUsername());\n        connectInfo.setPort(dialectProperties.getPort());\n        connectInfo.setHost(\"localhost\");\n        connectInfo.setSsh(new SSHInfo());\n        connectInfo.setConsoleId(consoleId);\n        connectInfo.setDataSourceId(dataSourceId);\n        connectInfo.setPassword(dialectProperties.getPassword());\n        connectInfo.setDbType(dialectProperties.getDbType());\n        connectInfo.setUrl(dialectProperties.getUrl());\n        connectInfo.setDatabase(databaseName);\n        connectInfo.setConsoleOwn(false);\n        return connectInfo;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/druid/SerializationUtilsTest.java",
    "content": "package ai.chat2db.server.start.test.druid;\n\nimport com.alibaba.fastjson2.JSON;\n\nimport ai.chat2db.server.domain.core.cache.MemoryCacheManage;\nimport ai.chat2db.server.start.test.dto.TestDTO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.SerializationUtils;\nimport org.junit.jupiter.api.Test;\n\n@Slf4j\npublic class SerializationUtilsTest {\n\n    @Test\n    public void test() {\n        TestDTO test = TestDTO.builder().name(\"test\").build();\n\n        byte[] bytes = SerializationUtils.serialize(test);\n\n        TestDTO t2 = SerializationUtils.deserialize(bytes);\n\n        log.info(\"tt{}\", t2);\n    }\n\n    @Test\n    public void cache() throws InterruptedException {\n        TestDTO test = TestDTO.builder().name(\"test\").build();\n        MemoryCacheManage.put(\"t1\", test);\n        TestDTO t1 = MemoryCacheManage.get(\"t1\");\n        log.info(\"t1:{}\", JSON.toJSONString(t1));\n        Thread.sleep(12000);\n        t1 = MemoryCacheManage.get(\"t1\");\n        log.info(\"t1:{}\", JSON.toJSONString(t1));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/druid/SqlUtilsTest.java",
    "content": "package ai.chat2db.server.start.test.druid;\n\nimport java.util.List;\n\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.PagerUtils;\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLDataTypeImpl;\nimport com.alibaba.druid.sql.ast.SQLLimit;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;\nimport com.alibaba.druid.sql.ast.statement.SQLNotNullConstraint;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;\nimport com.alibaba.druid.sql.parser.SQLParserFeature;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Test;\n\n@Slf4j\npublic class SqlUtilsTest {\n\n    @Test\n    public void test() {\n        List<SQLStatement> sqlStatements = SQLUtils.parseStatements(\"select 1 from test;\", DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatements);\n        sqlStatements = SQLUtils.parseStatements(\"use xxx;select 1 from test;explain select 1 from test\", DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatements);\n        sqlStatements = SQLUtils.parseStatements(\"select 1 from1 test\", DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatements);\n    }\n\n    @Test\n    public void test55() {\n        List<SQLStatement> sqlStatements = SQLUtils.parseStatements(\"create table test(id int) comment 'xx';\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatements);\n    }\n\n    @Test\n    public void test2() {\n        String sql = \"select * from test\";\n        log.info(\"Pagination: {} ----- {} --- {}\", PagerUtils.count(sql, DbType.mysql),\n            PagerUtils.limit(sql, DbType.mysql, 1000, 999),\n            PagerUtils.limit(sql, DbType.mysql, 1000, 999, true));\n        sql = \"select * from test where id=1 limit 100;\";\n        log.info(\"Pagination: {} ----- {} --- {}\", PagerUtils.count(sql, DbType.mysql),\n            PagerUtils.limit(sql, DbType.mysql, 1000, 999),\n            PagerUtils.limit(sql, DbType.mysql, 1000, 999, true));\n\n        sql = \"select * from test where  id=1 limit 100,10;\";\n        log.info(\"Pagination: {} ----- {} --- {}\", PagerUtils.count(sql, DbType.mysql),\n            PagerUtils.limit(sql, DbType.mysql, 1000, 999),\n            PagerUtils.limit(sql, DbType.mysql, 1000, 999, true));\n\n        sql = \"select * from test where  id=1 limit 100,10;\";\n        log.info(\"Pagination: {} ----- {} --- {}\", PagerUtils.count(sql, DbType.mysql),\n            PagerUtils.limit(sql, DbType.mysql, 2, 2),\n            PagerUtils.limit(sql, DbType.mysql, 2, 2, true));\n\n        sql = \"select * from test  union select * from test2\";\n        log.info(\"Pagination: {} ----- {} --- {}\", PagerUtils.count(sql, DbType.mysql),\n            PagerUtils.limit(sql, DbType.mysql, 2, 2),\n            PagerUtils.limit(sql, DbType.mysql, 2, 2, true));\n\n        sql = \"select * from test  union select * from test2\";\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, DbType.mysql);\n        SQLSelectStatement sqlSelectStatement = (SQLSelectStatement)sqlStatement;\n        log.info(\"test{}\", sqlSelectStatement);\n    }\n\n    @Test\n    public void test56() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"create table test(id int  ,name varchar(32) not null default 'xx' comment 'name',nu int auto_increment,\"\n                + \"index ds(id) ,primary key (id,nu)) \"\n                + \"comment 'xx';\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void test4() {\n        MySqlCreateTableStatement x = new MySqlCreateTableStatement();\n        x.setTableName(\"ff\");\n        x.setComment(new MySqlCharExpr(null));\n        SQLColumnDefinition c = new SQLColumnDefinition();\n        x.addColumn(c);\n\n        c.setName(\"name\");\n        SQLDataTypeImpl sqlDataType = new SQLDataTypeImpl();\n        sqlDataType.setName(\"varchar(32)\");\n        c.setDataType(sqlDataType);\n        c.addConstraint(new SQLNotNullConstraint());\n        c.setComment(new MySqlCharExpr(\"xname\"));\n        //x.addColumn();\n        log.info(x.toString());\n    }\n\n    @Test\n    public void testreaname() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"rename table data_ops_table_test_1667268894825 to data_ops_table_test_166726889482511;\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void testcomment() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"\\n\"\n                + \"alter table data_ops_table_test_166726889482511\\n\"\n                + \"    comment 'Test table 33';\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void dropindex() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"drop index data_ops_table_test_1667268894825_idx_date on data_ops_table_test_1667268894825;\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void createindex() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"\\n\"\n                + \"create index data_ops_table_test_1667268894825_idx_date\\n\"\n                + \"    on data_ops_table_test_1667268894825 (date desc, id asc)\\n\"\n                + \"    comment 'date index';\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void addColumn() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"alter table data_ops_table_test_1667268894825\\n\"\n                + \"    add column_5 int default de null;\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void change() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"alter table data_ops_table_test_1667268894825\\n\"\n                + \"    change number number1 bigint null comment 'long integer';\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void modify() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"alter table data_ops_table_test_1667268894825\\n\"\n                + \"    modify number1 bigint null comment 'long integer';\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void dropColumn() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"alter table data_ops_table_test_1667268894825\\n\"\n                + \"    drop column string;\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void dropPrimaryKey() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"ALTER TABLE `ali_dbhub_test`.`data_ops_table_test_1671368857363` \\n\"\n                + \"DROP PRIMARY KEY,\\n\"\n                + \"ADD PRIMARY KEY (`date`) USING BTREE;\",\n            DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void coment() {\n        try {\n\n            SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n                \"comment on index DATA_OPS_TEMPLATE_TEST_1672663574919_idx_date is 'Date index xx';\\n\",\n                DbType.h2, SQLParserFeature.PrintSQLWhileParsingFailed);\n            log.info(\"Parse sql:{}\", sqlStatement);\n        } catch (Exception e) {\n            log.error(\"error\", e);\n        }\n    }\n\n    @Test\n    public void errro() {\n        List<SQLStatement> sqlStatementList = SQLUtils.parseStatements(\n            \"alter table data_ops_table_test_1667268894825  drop column string;comment on index \"\n                + \"DATA_OPS_TEMPLATE_TEST_1672663574919_idx_date is 'Date index xx';\\n\",\n            DbType.h2, SQLParserFeature.PrintSQLWhileParsingFailed);\n        log.info(\"Parse sql:{}\", sqlStatementList);\n    }\n\n\n\n    @Test\n    public void creattable() {\n        List<SQLStatement> sqlStatementList = SQLUtils.parseStatements(\n            \"CREATE TABLE `data_ops_table_test_1673096155228`\\n\"\n                + \"\\t(\\n\"\n                + \"\\t    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'Primary key auto-increment',\\n\"\n                + \"\\t    `date`   datetime(3)                          not null COMMENT 'date',\\n\"\n                + \"\\t    `number` bigint COMMENT 'long integer',\\n\"\n                + \"\\t    `string` VARCHAR(100) default 'DATA' COMMENT 'name',\\n\"\n                + \"\\t    index data_ops_table_test_1673096155228_idx_date (date desc) comment 'date index',\\n\"\n                + \"\\t    unique data_ops_table_test_1673096155228_uk_number (number) comment 'unique index',\\n\"\n                + \"\\t    index data_ops_table_test_1673096155228_idx_number_string (number, date) comment 'Union index'\\n\"\n                + \"\\t) COMMENT ='Test table';\", DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlStatementList);\n    }\n\n\n    @Test\n    public void testlimit2() {\n        SQLLimit sqlLimit= SQLUtils.getLimit(\"select * from t_orderdetail limit 0,1\",DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlLimit);\n        sqlLimit= SQLUtils.getLimit(\"select * from t_orderdetail\",DbType.mysql);\n        log.info(\"Parse sql:{}\", sqlLimit);\n    }\n\n\n    @Test\n    public void test57() {\n        java.sql.Date date=new java.sql.Date(System.currentTimeMillis());\n\n        log.info(\"{}\",date);\n\n        java.sql.Timestamp ts=new   java.sql.Timestamp(System.currentTimeMillis());\n\n        log.info(\"{}\",ts);\n\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/druid/SqlUtilsTest2.java",
    "content": "package ai.chat2db.server.start.test.druid;\n\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\n\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Test;\n\n@Slf4j\npublic class SqlUtilsTest2 {\n\n    @Test\n    public void coment() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"comment on index myindex is 'datexxx';\\n\",\n            DbType.h2);\n        log.info(\"Parse sql:{}\", sqlStatement);\n    }\n\n    @Test\n    public void SELECT() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\n            \"SELECT * FROM score a left join user b on a.id=b.id LIMIT 10\",\n            DbType.mysql);\n\n        if (!(sqlStatement instanceof SQLSelectStatement)) {\n            throw new BusinessException(\"dataSource.sqlAnalysisError\");\n        }\n        SQLSelectStatement sqlSelectStatement = (SQLSelectStatement)sqlStatement;\n        log.info(\"解析sql1:{}\", sqlSelectStatement);\n\n        log.info(\"解析sql2:{}\", sqlSelectStatement.getSelect().getFirstQueryBlock().getFrom().toString());\n    }\n\n    @Test\n    public void select2() {\n        log.info(\"tablename:{}\",getTable(\"SELECT * FROM score LIMIT 10\"));\n        log.info(\"tablename:{}\",getTable(\"SELECT * FROM score a LIMIT 10\"));\n        log.info(\"tablename:{}\",getTable(\"SELECT * FROM score a left join user b on a.id=b.id LIMIT 10\"));\n    }\n\n\n    @Test\n    public void insert() {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(\"INSERT INTO chat2db.`order` (id, user_id, total_price, created_at, updated_at) VALUES (8, 345, 5601.16, '2022-09-18 11:21:12', '2023-04-30 11:21:12');\",\n            DbType.mysql);\n        log.info(\"Parse sql1:{}\", sqlStatement);\n\n    }\n\n    private String getTable(String sql) {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql,\n            DbType.mysql);\n\n        if (!(sqlStatement instanceof SQLSelectStatement)) {\n            throw new BusinessException(\"dataSource.sqlAnalysisError\");\n        }\n        SQLSelectStatement sqlSelectStatement = (SQLSelectStatement)sqlStatement;\n        SQLExprTableSource sqlExprTableSource = (SQLExprTableSource)getSQLExprTableSource(\n            sqlSelectStatement.getSelect().getFirstQueryBlock().getFrom());\n        return sqlExprTableSource.getTableName();\n    }\n\n    private SQLTableSource getSQLExprTableSource(SQLTableSource sqlTableSource) {\n        if (sqlTableSource instanceof SQLExprTableSource sqlExprTableSource) {\n            return sqlExprTableSource;\n        } else if (sqlTableSource instanceof SQLJoinTableSource sqlJoinTableSource) {\n            return getSQLExprTableSource(sqlJoinTableSource.getLeft());\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/dto/TestDTO.java",
    "content": "package ai.chat2db.server.start.test.dto;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TestDTO implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    private String name;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/mybatis/MybatisGeneratorTest.java",
    "content": "package ai.chat2db.server.start.test.mybatis;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\nimport ai.chat2db.server.start.test.common.BaseTest;\nimport com.baomidou.mybatisplus.generator.FastAutoGenerator;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.OutputFile;\nimport com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;\nimport com.google.common.collect.Lists;\nimport com.zaxxer.hikari.HikariDataSource;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Generate mapper of mybatis\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class MybatisGeneratorTest extends BaseTest {\n    @Resource\n    private DataSource dataSource;\n\n    @Test\n    public void coreGenerator() {\n\n        HikariDataSource dataSource = new HikariDataSource();\n        String environment = StringUtils.defaultString(System.getProperty(\"spring.profiles.active\"), \"dev\");\n        if (\"dev\".equalsIgnoreCase(environment)) {\n            dataSource.setJdbcUrl(\"jdbc:h2:file:~/.chat2db/db/chat2db_dev;MODE=MYSQL\");\n        }else if (\"test\".equalsIgnoreCase(environment)) {\n            dataSource.setJdbcUrl(\"jdbc:h2:file:~/.chat2db/db/chat2db_test;MODE=MYSQL\");\n        }else {\n            dataSource.setJdbcUrl(\"jdbc:h2:~/.chat2db/db/chat2db;MODE=MYSQL;FILE_LOCK=NO\");\n        }\n        dataSource.setDriverClassName(\"org.h2.Driver\");\n        dataSource.setIdleTimeout(60000);\n        dataSource.setAutoCommit(true);\n        dataSource.setMaximumPoolSize(500);\n        dataSource.setMinimumIdle(1);\n        dataSource.setMaxLifetime(60000 * 10);\n        dataSource.setConnectionTestQuery(\"SELECT 1\");\n        this.dataSource = dataSource;\n        //doGenerator(Lists.newArrayList(\"data_source\"));\n        //doGenerator(Lists.newArrayList(\"operation_log\"));\n        //doGenerator(Lists.newArrayList(\"operation_saved\"));\n        //doGenerator(Lists.newArrayList(\"environment\",\"data_source\",\"team\",\"team_dbhub_user\",\"data_source_access\",\n        // \"dbhub_user\"));\n        doGenerator(Lists.newArrayList(\"TASK\"));\n    }\n\n    private void doGenerator(List<String> tableList) {\n\n        // The current project address is the chat2db-server-start address.\n        String outputDir = System.getProperty(\"user.dir\")\n            + \"/../chat2db-server-domain/chat2db-server-domain-repository/src/main\"\n            + \"/java\";\n        String xmlDir = System.getProperty(\"user.dir\")\n            + \"/../chat2db-server-domain/chat2db-server-domain-repository/src/main\"\n            + \"/resources/mapper\";\n\n        // Do not generate service controller\n        Map<OutputFile, String> pathInfo = new HashMap<>();\n        pathInfo.put(OutputFile.service, null);\n        pathInfo.put(OutputFile.serviceImpl, null);\n        pathInfo.put(OutputFile.xml, xmlDir);\n        pathInfo.put(OutputFile.controller, null);\n\n        FastAutoGenerator\n            .create(new DataSourceConfig.Builder(dataSource)\n                .typeConvert(new MySqlTypeConvert()))\n            //Global configuration\n            .globalConfig(builder -> {\n                // Set author\n                builder.author(\"chat2db\")\n                    //Do not open the folder after execution\n                    .disableOpenDir()\n                    // Or use date\n                    .dateType(DateType.ONLY_DATE)\n                    // Specify output directory\n                    .outputDir(outputDir);\n            })\n            //Package configuration\n            .packageConfig(builder -> {\n                // Set parent package name\n                builder.parent(\"ai.chat2db.server.domain.repository\")\n                    //Generate solid layer\n                    .entity(\"entity\")\n                    .pathInfo(pathInfo)\n                    //Generate mapper layer\n                    .mapper(\"mapper\");\n            })\n            //Policy configuration\n            .strategyConfig(builder -> {\n                // Set the table name to be generated\n                builder.addInclude(tableList)\n                    //Enable entity class configuration\n                    .entityBuilder()\n                    .formatFileName(\"%sDO\")\n                    .enableFileOverride()\n                    //.addTableFills(new Column(\"gmt_create\", FieldFill.INSERT)) // Table field filling\n                    //.addTableFills(new Column(\"update_time\", FieldFill.INSERT_UPDATE)) // Table field filling\n                    //Turn on lombok\n                    .enableLombok()\n                    .mapperBuilder()\n                    //// overwrite file\n                    .enableFileOverride()\n                ;\n\n            })\n            //Template configuration\n            .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板，默认的是Velocity引擎模板\n            //execute\n            .execute();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/sql/DbhubJdbcTemplateTest.java",
    "content": "package ai.chat2db.server.start.test.sql;\n\nimport java.sql.Connection;\nimport java.sql.Statement;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\n/**\n * template\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class DbhubJdbcTemplateTest {\n\n    private static JdbcTemplate jdbcTemplate;\n\n    @BeforeAll\n    public static void prepare() throws Exception {\n        log.info(\"connect mysql\");\n    }\n\n    @Test\n    @Order(2)\n    public void test() throws Exception {\n\n        jdbcTemplate.execute(\"use data_ops_test\");\n\n        Connection connection = jdbcTemplate.getDataSource().getConnection();\n        Statement statement = connection.createStatement();\n        boolean execute = statement.execute(\"select * from test_query\");\n        log.info(\"execute：{}\",execute);\n\n        statement = connection.createStatement();\n        execute = statement.execute(\"INSERT INTO `test_query` (name,date,number) VALUES ('姓名','2022-01-01',123);\");\n        log.info(\"execute：{}\",execute);\n        //Returns:\n        //true if the first result is a ResultSet object; false if it is an update count or there are no results\n        statement = connection.createStatement();\n        execute = statement.execute(\"show tables\");\n        log.info(\"execute：{}\",execute);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-start/src/test/resources/logback-test-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <springProperty scope=\"context\" name=\"APP_NAME\" source=\"project.name\"/>\n\n    <property name=\"EASY_CONSOLE_LOG_PATTERN\"\n              value=\"${EASY_CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}.%line){cyan} %clr(:){faint} %X{TRACE_ID} | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${EASY_CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n</configuration>"
  },
  {
    "path": "chat2db-server/chat2db-server-test/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-parent</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-test</artifactId>\n    <packaging>jar</packaging>\n    <name>chat2db-server-test</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-start</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.github.jsqlparser</groupId>\n            <artifactId>jsqlparser</artifactId>\n            <version>4.6</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.github.vertical-blank</groupId>\n            <artifactId>sql-formatter</artifactId>\n            <version>2.0.4</version>\n            <scope>test</scope>\n        </dependency>\n<!--        <dependency>-->\n<!--            <groupId>mysql</groupId>-->\n<!--            <artifactId>mysql-connector-java</artifactId>-->\n<!--            <version>8.0.30</version>-->\n<!--        </dependency>-->\n<!--        <dependency>-->\n<!--            <groupId>com.github.jsqlparser</groupId>-->\n<!--            <artifactId>jsqlparser</artifactId>-->\n<!--            <version>4.6</version>-->\n<!--            <scope>test</scope>-->\n<!--        </dependency>-->\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/common/BaseTest.java",
    "content": "package ai.chat2db.server.test.common;\n\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.server.start.Application;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n/**\n * Basic test class\n *\n * @author Jiaju Zhuang\n **/\n@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@Slf4j\n@TestMethodOrder(value = MethodOrderer.OrderAnnotation.class)\npublic abstract class BaseTest {\n\n    public void putConnect(String url, String username, String password, String dbType, String database,\n        Long dataSourceId, Long consoleId) {\n        ConnectInfo connectInfo = new ConnectInfo();\n        connectInfo.setUser(username);\n        connectInfo.setConsoleId(consoleId);\n        connectInfo.setDataSourceId(dataSourceId);\n        connectInfo.setPassword(password);\n        connectInfo.setDbType(dbType);\n        connectInfo.setUrl(url);\n        connectInfo.setDatabase(database);\n        connectInfo.setConsoleOwn(false);\n        Chat2DBContext.putContext(connectInfo);\n    }\n\n    public void removeConnect() {\n        Chat2DBContext.removeContext();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/ConfigServiceTest.java",
    "content": "//\n//package ai.chat2db.server.test.domain.data.service;\n//\n//import ai.chat2db.server.domain.api.param.SystemConfigParam;\n//import ai.chat2db.server.domain.api.service.ConfigService;\n//import ai.chat2db.server.test.common.BaseTest;\n//\n//import lombok.extern.slf4j.Slf4j;\n//import org.junit.jupiter.api.Test;\n//import org.springframework.beans.factory.annotation.Autowired;\n//\n///**\n// * @author jipengfei\n// * @version : ConfihServiceTest.java\n// */\n//@Slf4j\n//public class ConfigServiceTest extends BaseTest {\n//\n//    @Autowired\n//    private ConfigService configService;\n//\n//    @Test\n//    public void testCreate() {\n//        SystemConfigParam systemConfigParam = new SystemConfigParam();\n//        systemConfigParam.setCode(\"test\");\n//        systemConfigParam.setContent(\"test1\");\n//        configService.createOrUpdate(systemConfigParam);\n//    }\n//}"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/ConsoleOperationsTest.java",
    "content": "package ai.chat2db.server.test.domain.data.service;\n\nimport java.util.List;\n\nimport jakarta.annotation.Resource;\n\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.service.ConsoleService;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.param.ConsoleCloseParam;\nimport ai.chat2db.server.test.common.BaseTest;\nimport ai.chat2db.server.test.domain.data.service.dialect.DialectProperties;\nimport ai.chat2db.server.test.domain.data.utils.TestUtils;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * Data source testing\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class ConsoleOperationsTest extends BaseTest {\n    @Resource\n    private DataSourceService dataSourceService;\n    @Resource\n    private ConsoleService consoleService;\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    @Order(1)\n    public void createAndClose() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbTypeEnum = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n            dataSourceCreateParam.setUser(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            dataSourceService.preConnect(dataSourceCreateParam);\n\n            // creat\n            ConsoleConnectParam consoleCreateParam = new ConsoleConnectParam();\n            consoleCreateParam.setDataSourceId(dataSourceId);\n            consoleCreateParam.setConsoleId(consoleId);\n            consoleCreateParam.setDatabaseName(dialectProperties.getDatabaseName());\n            consoleService.createConsole(consoleCreateParam);\n\n            // close\n            ConsoleCloseParam consoleCloseParam = new ConsoleCloseParam();\n            consoleCloseParam.setDataSourceId(dataSourceId);\n            consoleCloseParam.setConsoleId(consoleId);\n            consoleService.closeConsole(consoleCloseParam);\n            TestUtils.remove();\n        }\n    }\n\n    @Test\n    @Order(2)\n    public void createAfterDataSourceClose() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbTypeEnum = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n            dataSourceCreateParam.setUser(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            dataSourceService.preConnect(dataSourceCreateParam);\n\n            dataSourceService.close(dataSourceId);\n\n            TestUtils.remove();\n        }\n    }\n\n    @Test\n    @Order(3)\n    public void closeDataSourceAfterCreateConsole() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbTypeEnum = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, consoleId);\n\n            DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n            dataSourceCreateParam.setUser(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            dataSourceService.preConnect(dataSourceCreateParam);\n\n            // Create a console\n            ConsoleConnectParam consoleCreateParam = new ConsoleConnectParam();\n            consoleCreateParam.setDataSourceId(dataSourceId);\n            consoleCreateParam.setConsoleId(consoleId);\n            consoleCreateParam.setDatabaseName(dialectProperties.getDatabaseName());\n            consoleService.createConsole(consoleCreateParam);\n\n            dataSourceService.close(dataSourceId);\n            TestUtils.remove();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/DatabaseOperationsTest.java",
    "content": "package ai.chat2db.server.test.domain.data.service;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.model.Database;\nimport jakarta.annotation.Resource;\n\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.DatabaseService;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseQueryAllParam;\nimport ai.chat2db.server.test.common.BaseTest;\nimport ai.chat2db.server.test.domain.data.service.dialect.DialectProperties;\nimport ai.chat2db.server.test.domain.data.utils.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport com.alibaba.fastjson2.JSON;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * Database testing\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class DatabaseOperationsTest extends BaseTest {\n    @Resource\n    private DataSourceService dataSourceService;\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n    @Resource\n    private DatabaseService databaseService;\n\n    @Test\n    @Order(1)\n    public void queryAll() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbTypeEnum = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n\n            // Prepare context\n            putConnect(dialectProperties.getUrl(), dialectProperties.getUsername(), dialectProperties.getPassword(),\n                dialectProperties.getDbType(), dialectProperties.getDatabaseName(), dataSourceId, null);\n\n            DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n            dataSourceCreateParam.setUser(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            dataSourceService.preConnect(dataSourceCreateParam);\n\n            DatabaseQueryAllParam databaseQueryAllParam = new DatabaseQueryAllParam();\n            databaseQueryAllParam.setDataSourceId(dataSourceId);\n            ListResult<Database> databaseList = databaseService.queryAll(databaseQueryAllParam);\n            log.info(\"Querying the database returns: {}\", JSON.toJSONString(databaseList));\n\n            Database Database = databaseList.getData().stream()\n                .filter(database -> dialectProperties.getDatabaseName().equals(database.getName()))\n                .findFirst()\n                .orElse(null);\n            Assertions.assertNotNull(Database, \"Query database failed\");\n\n            removeConnect();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/ExampleOperationsTest.java",
    "content": "package ai.chat2db.server.test.domain.data.service;\n\nimport java.util.List;\n\nimport jakarta.annotation.Resource;\n\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.test.common.BaseTest;\nimport ai.chat2db.server.test.domain.data.service.dialect.DialectProperties;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * Sample\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class ExampleOperationsTest extends BaseTest {\n\n    @Resource\n    private TableService tableService;\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    @Order(1)\n    public void example() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            DataResult<String> createTable = tableService.createTableExample(dialectProperties.getDbType());\n            log.info(\"Return table creation statement: {}\", createTable);\n            Assertions.assertNotNull(createTable, \"Query sample failed\");\n            DataResult<String> alterTable = tableService.alterTableExample(dialectProperties.getDbType());\n            log.info(\"Return the statement to create and modify the table: {}\", alterTable);\n            Assertions.assertNotNull(alterTable, \"Query sample failed\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/JdbcOperationsTest.java",
    "content": "package ai.chat2db.server.test.domain.data.service;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.spi.model.Header;\nimport jakarta.annotation.Resource;\n\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.param.DlExecuteParam;\nimport ai.chat2db.server.domain.api.service.ConsoleService;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.test.common.BaseTest;\nimport ai.chat2db.server.test.domain.data.service.dialect.DialectProperties;\nimport ai.chat2db.server.test.domain.data.utils.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport com.alibaba.fastjson2.JSON;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * query test\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class JdbcOperationsTest extends BaseTest {\n    /**\n     * Table Name\n     */\n    public static final String TABLE_NAME = \"DATA_OPS_TEMPLATE_TEST_\" + System.currentTimeMillis();\n    private final static String STRING = \"STR\";\n    private final static Date DATE = new Date();\n    private final static long NUMBER = 1L;\n\n    @Resource\n    private DataSourceService dataSourceService;\n    @Resource\n    private ConsoleService consoleService;\n\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Resource\n    private DlTemplateService dlTemplateService;\n\n    @Test\n    @Order(1)\n    public void execute() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbTypeEnum = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n\n            // Prepare context\n            putConnect(dialectProperties.getUrl(), dialectProperties.getUsername(), dialectProperties.getPassword(),\n                dialectProperties.getDbType(), dialectProperties.getDatabaseName(), dataSourceId, consoleId);\n\n            DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n            dataSourceCreateParam.setUser(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            dataSourceService.preConnect(dataSourceCreateParam);\n\n            // Create a console\n            ConsoleConnectParam consoleCreateParam = new ConsoleConnectParam();\n            consoleCreateParam.setDataSourceId(dataSourceId);\n            consoleCreateParam.setConsoleId(consoleId);\n            consoleCreateParam.setDatabaseName(dialectProperties.getDatabaseName());\n            consoleService.createConsole(consoleCreateParam);\n\n            DlExecuteParam templateQueryParam = new DlExecuteParam();\n            templateQueryParam.setConsoleId(consoleId);\n            templateQueryParam.setDataSourceId(dataSourceId);\n            templateQueryParam.setSql(dialectProperties.getCrateTableSql(TABLE_NAME));\n            dlTemplateService.execute(templateQueryParam);\n\n            // insert\n            templateQueryParam = new DlExecuteParam();\n            templateQueryParam.setConsoleId(consoleId);\n            templateQueryParam.setDataSourceId(dataSourceId);\n            templateQueryParam.setSql(dialectProperties.getInsertSql(TABLE_NAME, DATE, NUMBER, STRING));\n            ListResult<ExecuteResult> executeResult = dlTemplateService.execute(templateQueryParam);\n            Assertions.assertTrue(executeResult.getSuccess(), \"Query data failed\");\n            // Assertions.assertEquals(1, listResult.getUpdateCount(), \"Query data failed\");\n\n            // query\n            templateQueryParam = new DlExecuteParam();\n            templateQueryParam.setConsoleId(consoleId);\n            templateQueryParam.setDataSourceId(dataSourceId);\n            templateQueryParam.setSql(dialectProperties.getSelectSqlById(TABLE_NAME, 1L));\n            executeResult = dlTemplateService.execute(templateQueryParam);\n            log.info(\"Return data:{}\", JSON.toJSONString(executeResult));\n            Assertions.assertTrue(executeResult.getSuccess(), \"Query data failed\");\n            List<Header> headerList = executeResult.getData().get(0).getHeaderList();\n            Assertions.assertEquals(4L, headerList.size(), \"Query data failed\");\n            Assertions.assertEquals(dialectProperties.toCase(\"ID\"), headerList.get(0).getName(), \"Query data failed\");\n\n            List<List<String>> dataList = executeResult.getData().get(0).getDataList();\n            Assertions.assertEquals(1L, dataList.size(), \"Query data failed\");\n            List<String> data1 = dataList.get(0);\n            Assertions.assertEquals(Long.toString(NUMBER), data1.get(0), \"Query data failed\");\n            log.info(\"date:{},{}\", DATE, data1.get(1));\n            Assertions.assertEquals(DateUtil.format(DATE, DatePattern.NORM_DATETIME_FORMAT), data1.get(1),\n                \"Query data failed\");\n            Assertions.assertEquals(Long.toString(NUMBER), data1.get(2), \"Query data failed\");\n            Assertions.assertEquals(STRING, data1.get(3), \"Query data failed\");\n\n            // Exception sql\n            templateQueryParam = new DlExecuteParam();\n            templateQueryParam.setConsoleId(consoleId);\n            templateQueryParam.setDataSourceId(dataSourceId);\n            templateQueryParam.setSql(dialectProperties.getTableNotFoundSqlById(TABLE_NAME));\n            executeResult = dlTemplateService.execute(templateQueryParam);\n            log.info(\"Abnormal sql execution result: {}\", JSON.toJSONString(executeResult));\n            Assertions.assertFalse(executeResult.getSuccess(), \"Exception sql error\");\n            Assertions.assertNotNull(executeResult.getErrorMessage(), \"Exception sql error\");\n\n            removeConnect();\n        }\n    }\n\n    @Test\n    @Order(Integer.MAX_VALUE)\n    public void dropTable() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            try {\n                String dbTypeEnum = dialectProperties.getDbType();\n                Long dataSourceId = TestUtils.nextLong();\n                Long consoleId = TestUtils.nextLong();\n\n                DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n                dataSourceCreateParam.setType(dbTypeEnum);\n                dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n                dataSourceCreateParam.setUser(dialectProperties.getUsername());\n                dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n                dataSourceService.preConnect(dataSourceCreateParam);\n\n                // Create a console\n                ConsoleConnectParam consoleCreateParam = new ConsoleConnectParam();\n                consoleCreateParam.setDataSourceId(dataSourceId);\n                consoleCreateParam.setConsoleId(consoleId);\n                consoleCreateParam.setDatabaseName(dialectProperties.getDatabaseName());\n                consoleService.createConsole(consoleCreateParam);\n\n                // Create table structure\n                DlExecuteParam templateQueryParam = new DlExecuteParam();\n                templateQueryParam.setConsoleId(consoleId);\n                templateQueryParam.setDataSourceId(dataSourceId);\n                templateQueryParam.setSql(dialectProperties.getDropTableSql(TABLE_NAME));\n                dlTemplateService.execute(templateQueryParam);\n            } catch (Exception e) {\n                log.warn(\"Failed to delete table structure.\", e);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/SQLExecutorOperationsTest.java",
    "content": "package ai.chat2db.server.test.domain.data.service;\n\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.test.common.BaseTest;\nimport ai.chat2db.server.test.domain.data.service.dialect.DialectProperties;\nimport ai.chat2db.server.test.domain.data.utils.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport com.alibaba.fastjson2.JSON;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * Data source testing\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class SQLExecutorOperationsTest extends BaseTest {\n    @Resource\n    private DataSourceService dataSourceService;\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Test\n    @Order(1)\n    public void createAndClose() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbTypeEnum = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, null);\n            // creat\n            DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n            dataSourceCreateParam.setUser(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            ActionResult dataSourceConnect = dataSourceService.preConnect(dataSourceCreateParam);\n            Assertions.assertTrue(dataSourceConnect.getSuccess(), \"Failed to create database connection pool\");\n            // Assertions.assertTrue(DataCenterUtils.JDBC_ACCESSOR_MAP.containsKey(dataSourceId), \"Failed to create database connection pool\");\n\n            // cloes\n            dataSourceService.close(dataSourceId);\n            TestUtils.remove();\n        }\n    }\n\n    @Test\n    @Order(2)\n    public void test() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbTypeEnum = dialectProperties.getDbType();\n\n            // creat\n            DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getErrorUrl());\n            dataSourceCreateParam.setUser(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            ActionResult dataSourceConnect = dataSourceService.preConnect(dataSourceCreateParam);\n            log.info(\"Create database returns: {}\", JSON.toJSONString(dataSourceConnect));\n            Assertions.assertFalse(dataSourceConnect.getSuccess(), \"Database creation failed error\");\n        }\n    }\n    @Test\n    @Order(3)\n    public void createDataSource(){\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            if(!dialectProperties.getDbType().equals(\"CLICKHOUSE\")){\n                continue;\n            }\n            String dbTypeEnum = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n            TestUtils.buildContext(dialectProperties, dataSourceId, null);\n            // creat\n            DataSourceCreateParam dataSourceCreateParam = new DataSourceCreateParam();\n            dataSourceCreateParam.setAlias(dialectProperties.getDbType()+\"_unittest_\"+dialectProperties.getDbType());\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n            dataSourceCreateParam.setUserName(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            DataResult<Long> dataSourceConnect = dataSourceService.createWithPermission(dataSourceCreateParam);\n            Assertions.assertTrue(dataSourceConnect.getSuccess(), \"Failed to create database connection pool\");\n            // Assertions.assertTrue(DataCenterUtils.JDBC_ACCESSOR_MAP.containsKey(dataSourceId), \"Failed to create database connection pool\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/TableOperationsTest.java",
    "content": "package ai.chat2db.server.test.domain.data.service;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.fastjson2.JSON;\n\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.param.DlExecuteParam;\nimport ai.chat2db.server.domain.api.param.DropParam;\nimport ai.chat2db.server.domain.api.param.ShowCreateTableParam;\nimport ai.chat2db.server.domain.api.param.TablePageQueryParam;\nimport ai.chat2db.server.domain.api.param.TableQueryParam;\nimport ai.chat2db.server.domain.api.param.TableSelector;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.service.ConsoleService;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.test.common.BaseTest;\nimport ai.chat2db.server.test.domain.data.service.dialect.DialectProperties;\nimport ai.chat2db.server.test.domain.data.utils.TestUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport ai.chat2db.spi.enums.CollationEnum;\nimport ai.chat2db.spi.enums.IndexTypeEnum;\nimport ai.chat2db.spi.model.Sql;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport com.google.common.collect.Lists;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * Data source testing\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class TableOperationsTest extends BaseTest {\n    /**\n     * Table Name\n     */\n    public static final String TABLE_NAME = \"data_ops_table_test_\" + System.currentTimeMillis();\n\n    @Resource\n    private DataSourceService dataSourceService;\n    @Resource\n    private ConsoleService consoleService;\n    @Autowired\n    private List<DialectProperties> dialectPropertiesList;\n\n    @Resource\n    private DlTemplateService dlTemplateService;\n    @Resource\n    private TableService tableService;\n    //@Resource\n    //private SqlOperations sqlOperations;\n\n    @Test\n    @Order(1)\n    public void table() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            String dbTypeEnum = dialectProperties.getDbType();\n            Long dataSourceId = TestUtils.nextLong();\n            Long consoleId = TestUtils.nextLong();\n\n            // Prepare context\n            putConnect(dialectProperties.getUrl(), dialectProperties.getUsername(), dialectProperties.getPassword(),\n                dialectProperties.getDbType(), dialectProperties.getDatabaseName(), dataSourceId, consoleId);\n\n            DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n\n            dataSourceCreateParam.setType(dbTypeEnum);\n            dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n            dataSourceCreateParam.setUser(dialectProperties.getUsername());\n            dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n            dataSourceService.preConnect(dataSourceCreateParam);\n\n            // Create a console\n            ConsoleConnectParam consoleCreateParam = new ConsoleConnectParam();\n            consoleCreateParam.setDataSourceId(dataSourceId);\n            consoleCreateParam.setConsoleId(consoleId);\n            consoleCreateParam.setDatabaseName(dialectProperties.getDatabaseName());\n            consoleService.createConsole(consoleCreateParam);\n\n            // Create table structure\n\n            DlExecuteParam templateQueryParam = new DlExecuteParam();\n            templateQueryParam.setConsoleId(consoleId);\n            templateQueryParam.setDataSourceId(dataSourceId);\n            templateQueryParam.setSql(dialectProperties.getCrateTableSql(TABLE_NAME));\n            dlTemplateService.execute(templateQueryParam);\n            // Query table creation statement\n            ShowCreateTableParam showCreateTableParam = ShowCreateTableParam.builder()\n                .dataSourceId(dataSourceId)\n                .databaseName(dialectProperties.getDatabaseName())\n                .tableName(dialectProperties.toCase(TABLE_NAME))\n                .build();\n            if (dialectProperties.getDbType() == \"POSTGRESQL\") {\n                showCreateTableParam.setSchemaName(\"public\");\n            }\n\n            DataResult<String> createTable = tableService.showCreateTable(showCreateTableParam);\n            log.info(\"Table creation statement: {}\", createTable.getData());\n            if (dialectProperties.getDbType() != \"H2\") {\n                Assertions.assertTrue(createTable.getData().contains(dialectProperties.toCase(TABLE_NAME)),\n                    \"Query table structure failed\");\n            }\n\n            //  Query table structure\n            TablePageQueryParam tablePageQueryParam = new TablePageQueryParam();\n            tablePageQueryParam.setDataSourceId(dataSourceId);\n            tablePageQueryParam.setDatabaseName(dialectProperties.getDatabaseName());\n            tablePageQueryParam.setTableName(dialectProperties.toCase(TABLE_NAME));\n            if (dialectProperties.getDbType() == \"POSTGRESQL\") {\n                tablePageQueryParam.setSchemaName(\"public\");\n            }\n            List<Table> tableList = tableService.pageQuery(tablePageQueryParam, TableSelector.builder()\n                .columnList(Boolean.TRUE)\n                .indexList(Boolean.TRUE)\n                .build()).getData();\n            log.info(\"Analyzing data returns {}\", JSON.toJSONString(tableList));\n            Assertions.assertNotEquals(0L, tableList.size(), \"Query table structure failed\");\n            Table table = tableList.get(0);\n            // Assertions.assertEquals(dialectProperties.toCase(TABLE_NAME), table.getName(), \"Query table structure failed\");\n            if (dialectProperties.getDbType() != \"POSTGRESQL\") {\n                Assertions.assertEquals(\"Test table\", table.getComment(), \"Query table structure failed\");\n            }\n            TableQueryParam tableQueryParam = new TableQueryParam();\n            tableQueryParam.setTableName(table.getName());\n            tableQueryParam.setDataSourceId(dataSourceId);\n            tableQueryParam.setDatabaseName(dialectProperties.getDatabaseName());\n            if (dialectProperties.getDbType() == \"POSTGRESQL\") {\n                tableQueryParam.setSchemaName(\"public\");\n            }\n            List<TableColumn> columnList = tableService.queryColumns(tableQueryParam);\n            Assertions.assertEquals(4L, columnList.size(), \"Query table structure failed\");\n            TableColumn id = columnList.get(0);\n            Assertions.assertEquals(dialectProperties.toCase(\"id\"), id.getName(), \"Query table structure failed\");\n            Assertions.assertEquals(\"Primary key auto-increment\", id.getComment(), \"Query table structure failed\");\n            Assertions.assertTrue(id.getAutoIncrement(), \"Query table structure failed\");\n            //Assertions.assertFalse(id.getNullable(), \"Query table structure failed\");\n\n            TableColumn string = columnList.get(3);\n            Assertions.assertEquals(dialectProperties.toCase(\"string\"), string.getName(), \"Query table structure failed\");\n            //Assertions.assertTrue(string.getNullable(), \"Query table structure failed\");\n            Assertions.assertEquals(\"DATA\", TestUtils.unWrapperDefaultValue(string.getDefaultValue()),\n                \"Query table structure failed\");\n            if (dialectProperties.getDbType() == \"POSTGRESQL\") {\n                tablePageQueryParam.setSchemaName(\"public\");\n            }\n            List<TableIndex> tableIndexList = tableService.queryIndexes(tableQueryParam);\n            log.info(\"Analyzing data returns {}\", JSON.toJSONString(tableIndexList));\n            Assertions.assertEquals(4L, tableIndexList.size(), \"Query table structure failed\");\n            Map<String, TableIndex> tableIndexMap = EasyCollectionUtils.toIdentityMap(tableIndexList,\n                TableIndex::getName);\n            TableIndex idxDate = tableIndexMap.get(dialectProperties.toCase(TABLE_NAME + \"_idx_date\"));\n            Assertions.assertEquals(\"date index\", idxDate.getComment(), \"Query table structure failed\");\n            Assertions.assertEquals(IndexTypeEnum.NORMAL.getCode(), idxDate.getType(), \"Query table structure failed\");\n            Assertions.assertEquals(1L, idxDate.getColumnList().size(), \"Query table structure failed\");\n            Assertions.assertEquals(dialectProperties.toCase(\"date\"), idxDate.getColumnList().get(0).getColumnName(),\n                \"Query table structure failed\");\n            Assertions.assertEquals(CollationEnum.DESC.getCode(), idxDate.getColumnList().get(0).getCollation(),\n                \"Query table structure failed\");\n\n            TableIndex ukNumber = tableIndexMap.get(dialectProperties.toCase(TABLE_NAME + \"_uk_number\"));\n            Assertions.assertEquals(\"unique index\", ukNumber.getComment(), \"Query table structure failed\");\n            Assertions.assertEquals(IndexTypeEnum.UNIQUE.getCode(), ukNumber.getType(), \"Query table structure failed\");\n\n            TableIndex idxNumberString = tableIndexMap.get(dialectProperties.toCase(TABLE_NAME + \"_idx_number_string\"));\n            Assertions.assertEquals(2, idxNumberString.getColumnList().size(), \"Query table structure failed\");\n\n            // Delete table structure\n            DropParam dropParam = DropParam.builder()\n                .dataSourceId(dataSourceId)\n                .databaseName(dialectProperties.getDatabaseName())\n                .name(dialectProperties.toCase(TABLE_NAME))\n                .build();\n            tableService.drop(dropParam);\n            //  Query table structure\n            tablePageQueryParam = new TablePageQueryParam();\n            tablePageQueryParam.setDataSourceId(dataSourceId);\n            tablePageQueryParam.setDatabaseName(dialectProperties.getDatabaseName());\n            tablePageQueryParam.setTableName(dialectProperties.toCase(TABLE_NAME));\n            tableList = tableService.pageQuery(tablePageQueryParam, TableSelector.builder()\n                .columnList(Boolean.TRUE)\n                .indexList(Boolean.TRUE)\n                .build()).getData();\n            log.info(\"After deleting the table, the data returns {}\", JSON.toJSONString(tableList));\n            Assertions.assertEquals(0L, tableList.size(), \"Query table structure failed\");\n\n            // Test the table creation statement\n            testBuildSql(dialectProperties, dataSourceId, consoleId);\n\n            removeConnect();\n        }\n\n    }\n\n    private void testBuildSql(DialectProperties dialectProperties, Long dataSourceId, Long consoleId) {\n        if (dialectProperties.getDbType() != \"MYSQL\") {\n            log.error(\"Currently the test case only supports mysql\");\n            return;\n        }\n        // Create new table\n        //    CREATE TABLE `DATA_OPS_TEMPLATE_TEST_1673093980449`\n        //    (\n        //    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'Primary key auto-increment',\n        //    `date`   datetime(3)                          not null COMMENT 'date',\n        //    `number` bigint COMMENT 'long integer',\n        //    `string` VARCHAR(100) default 'DATA' COMMENT 'name',\n        //        index DATA_OPS_TEMPLATE_TEST_1673093980449_idx_date (date desc) comment 'date index',\n        //        unique DATA_OPS_TEMPLATE_TEST_1673093980449_uk_number (number) comment 'unique index',\n        //        index DATA_OPS_TEMPLATE_TEST_1673093980449_idx_number_string (number, date) comment 'Union index'\n        //) COMMENT ='Test table';\n        //        * The case depends on the specific database:\n        //* Create table structure: Test table\n        //       * Fields:\n        //* id   Primary key auto-increment\n        //* date date is not null\n        //       * number long integer\n        //       * string  String length 100 default value \"DATA\"\n        //       *\n        //* Index (plus $tableName_ because some database indexes are globally unique):\n        //* $tableName_idx_date date index reverse order\n        //       * $tableName_uk_number unique index\n        //       * $tableName_idx_number_string Union index\n        String tableName = dialectProperties.toCase(\"data_ops_table_test_\" + System.currentTimeMillis());\n        Table newTable = new Table();\n        newTable.setName(tableName);\n        newTable.setComment(\"Test table\");\n        List<TableColumn> tableColumnList = new ArrayList<>();\n        newTable.setColumnList(tableColumnList);\n        //id\n        TableColumn idTableColumn = new TableColumn();\n        idTableColumn.setName(\"id\");\n        idTableColumn.setAutoIncrement(Boolean.TRUE);\n        idTableColumn.setPrimaryKey(Boolean.TRUE);\n        //idTableColumn.setNullable(Boolean.FALSE);\n        idTableColumn.setComment(\"Primary key auto-increment\");\n        idTableColumn.setColumnType(\"bigint\");\n        tableColumnList.add(idTableColumn);\n\n        // date\n        TableColumn dateTableColumn = new TableColumn();\n        dateTableColumn.setName(\"date\");\n        //dateTableColumn.setNullable(Boolean.FALSE);\n        dateTableColumn.setComment(\"date\");\n        dateTableColumn.setColumnType(\"datetime(3)\");\n        tableColumnList.add(dateTableColumn);\n\n        // number\n        TableColumn numberTableColumn = new TableColumn();\n        numberTableColumn.setName(\"number\");\n        numberTableColumn.setComment(\"long integer\");\n        numberTableColumn.setColumnType(\"bigint\");\n        tableColumnList.add(numberTableColumn);\n\n        // string\n        TableColumn stringTableColumn = new TableColumn();\n        stringTableColumn.setName(\"string\");\n        stringTableColumn.setComment(\"name\");\n        stringTableColumn.setColumnType(\"varchar(100)\");\n        stringTableColumn.setDefaultValue(\"DATA\");\n        tableColumnList.add(stringTableColumn);\n\n        // index\n        List<TableIndex> tableIndexList = new ArrayList<>();\n        newTable.setIndexList(tableIndexList);\n\n        //        index DATA_OPS_TEMPLATE_TEST_1673093980449_idx_date (date desc) comment 'date index',\n        tableIndexList.add(TableIndex.builder()\n            .name(tableName + \"_idx_date\")\n            .type(IndexTypeEnum.NORMAL.getCode())\n            .comment(\"date index\")\n            .columnList(Lists.newArrayList(TableIndexColumn.builder()\n                .columnName(\"date\")\n                .collation(CollationEnum.DESC.getCode())\n                .build()))\n            .build());\n\n        //        unique DATA_OPS_TEMPLATE_TEST_1673093980449_uk_number (number) comment 'unique index',\n        tableIndexList.add(TableIndex.builder()\n            .name(tableName + \"_uk_number\")\n            .type(IndexTypeEnum.UNIQUE.getCode())\n            .comment(\"unique index\")\n            .columnList(Lists.newArrayList(TableIndexColumn.builder()\n                .columnName(\"number\")\n                .build()))\n            .build());\n        //        index DATA_OPS_TEMPLATE_TEST_1673093980449_idx_number_string (number, date) comment 'Union index'\n        tableIndexList.add(TableIndex.builder()\n            .name(tableName + \"_idx_number_string\")\n            .type(IndexTypeEnum.NORMAL.getCode())\n            .comment(\"Union index\")\n            .columnList(Lists.newArrayList(TableIndexColumn.builder()\n                    .columnName(\"number\")\n                    .build(),\n                TableIndexColumn.builder()\n                    .columnName(\"date\")\n                    .build()))\n            .build());\n        // build sql\n        List<Sql> buildTableSqlList = tableService.buildSql(null, newTable).getData();\n        log.info(\"The structural statement to create a table is:{}\", JSON.toJSONString(buildTableSqlList));\n        for (Sql sql : buildTableSqlList) {\n            DlExecuteParam templateQueryParam = new DlExecuteParam();\n            templateQueryParam.setConsoleId(consoleId);\n            templateQueryParam.setDataSourceId(dataSourceId);\n            templateQueryParam.setSql(sql.getSql());\n            dlTemplateService.execute(templateQueryParam);\n        }\n\n        // Check table structure\n        checkTable(tableName, dialectProperties, dataSourceId);\n\n        //  Go to the database to query the table structure\n        TableQueryParam tablePageQueryParam = new TableQueryParam();\n        tablePageQueryParam.setDataSourceId(dataSourceId);\n        tablePageQueryParam.setDatabaseName(dialectProperties.getDatabaseName());\n        tablePageQueryParam.setTableName(dialectProperties.toCase(tableName));\n        Table table = tableService.query(tablePageQueryParam, TableSelector.builder()\n            .columnList(Boolean.TRUE)\n            .indexList(Boolean.TRUE)\n            .build()).getData();\n        log.info(\"Analyzing data returns {}\", JSON.toJSONString(table));\n        Assertions.assertNotNull(table, \"Query table structure failed\");\n        Table oldTable = table;\n        Assertions.assertEquals(dialectProperties.toCase(tableName), oldTable.getName(), \"Query table structure failed\");\n        Assertions.assertEquals(\"Test table\", oldTable.getComment(), \"Query table structure failed\");\n\n        // Modify table structure\n        // build sql\n        log.info(\"oldTable：{}\", JSON.toJSONString(oldTable));\n        log.info(\"newTable：{}\", JSON.toJSONString(newTable));\n        buildTableSqlList = tableService.buildSql(oldTable, newTable).getData();\n        log.info(\"Modify the table structure: {}\", JSON.toJSONString(buildTableSqlList));\n        Assertions.assertTrue(!buildTableSqlList.isEmpty(), \"构建sql失败\");\n        //  Let’s query again. There will be 2 objects.\n        tablePageQueryParam = new TableQueryParam();\n        tablePageQueryParam.setDataSourceId(dataSourceId);\n        tablePageQueryParam.setDatabaseName(dialectProperties.getDatabaseName());\n        tablePageQueryParam.setTableName(dialectProperties.toCase(tableName));\n        newTable = tableService.query(tablePageQueryParam, TableSelector.builder()\n            .columnList(Boolean.TRUE)\n            .indexList(Boolean.TRUE)\n            .build()).getData();\n\n        // Modify fields\n\n        // Add a new field\n        newTable.getColumnList().add(TableColumn.builder()\n            .name(\"add_string\")\n            .columnType(\"varchar(20)\")\n            .comment(\"New string\")\n            .build());\n\n        // Add a new index\n        newTable.getIndexList().add(TableIndex.builder()\n            .name(tableName + \"_idx_string_new\")\n            .type(IndexTypeEnum.NORMAL.getCode())\n            .comment(\"new string index\")\n            .columnList(Lists.newArrayList(TableIndexColumn.builder()\n                .columnName(\"add_string\")\n                .collation(CollationEnum.DESC.getCode())\n                .build()))\n            .build());\n\n        // Query table structure changes\n        log.info(\"oldTable：{}\", JSON.toJSONString(oldTable));\n        log.info(\"newTable：{}\", JSON.toJSONString(newTable));\n        buildTableSqlList = tableService.buildSql(oldTable, newTable).getData();\n        log.info(\"Modify the table structure: {}\", JSON.toJSONString(buildTableSqlList));\n\n        // Delete table structure\n        dropTable(tableName, dialectProperties, dataSourceId);\n    }\n\n    private void dropTable(String tableName, DialectProperties dialectProperties, Long dataSourceId) {\n        // Delete table structure\n        DropParam dropParam = DropParam.builder()\n            .dataSourceId(dataSourceId)\n            .databaseName(dialectProperties.getDatabaseName())\n            .name(dialectProperties.toCase(tableName))\n            .build();\n        tableService.drop(dropParam);\n        //  Query table structure\n        TablePageQueryParam tablePageQueryParam = new TablePageQueryParam();\n        tablePageQueryParam.setDataSourceId(dataSourceId);\n        tablePageQueryParam.setDatabaseName(dialectProperties.getDatabaseName());\n        tablePageQueryParam.setTableName(dialectProperties.toCase(tableName));\n        List<Table> tableList = tableService.pageQuery(tablePageQueryParam, TableSelector.builder()\n            .columnList(Boolean.TRUE)\n            .indexList(Boolean.TRUE)\n            .build()).getData();\n        log.info(\"After deleting the table, the data returns {}\", JSON.toJSONString(tableList));\n        Assertions.assertEquals(0L, tableList.size(), \"Query table structure failed\");\n    }\n\n    private void checkTable(String tableName, DialectProperties dialectProperties, Long dataSourceId) {\n        //  Query table structure\n        TablePageQueryParam tablePageQueryParam = new TablePageQueryParam();\n        tablePageQueryParam.setDataSourceId(dataSourceId);\n        tablePageQueryParam.setDatabaseName(dialectProperties.getDatabaseName());\n        tablePageQueryParam.setTableName(dialectProperties.toCase(tableName));\n        List<Table> tableList = tableService.pageQuery(tablePageQueryParam, TableSelector.builder()\n            .columnList(Boolean.TRUE)\n            .indexList(Boolean.TRUE)\n            .build()).getData();\n\n        log.info(\"Analyzing data returns {}\", JSON.toJSONString(tableList));\n        Assertions.assertEquals(1L, tableList.size(), \"Query table structure failed\");\n        Table table = tableList.get(0);\n        Assertions.assertEquals(dialectProperties.toCase(tableName), table.getName(), \"Query table structure failed\");\n        Assertions.assertEquals(\"Test table\", table.getComment(), \"Query table structure failed\");\n        TableQueryParam tableQueryParam = new TableQueryParam();\n        tableQueryParam.setTableName(table.getName());\n        tableQueryParam.setDataSourceId(dataSourceId);\n        tableQueryParam.setDatabaseName(dialectProperties.getDatabaseName());\n\n        List<TableColumn> columnList = tableService.queryColumns(tableQueryParam);\n        Assertions.assertEquals(4L, columnList.size(), \"Query table structure failed\");\n        TableColumn id = columnList.get(0);\n        Assertions.assertEquals(dialectProperties.toCase(\"id\"), id.getName(), \"Query table structure failed\");\n        Assertions.assertEquals(\"Primary key auto-increment\", id.getComment(), \"Query table structure failed\");\n        Assertions.assertTrue(id.getAutoIncrement(), \"Query table structure failed\");\n        //Assertions.assertFalse(id.getNullable(), \"Query table structure failed\");\n        Assertions.assertTrue(id.getPrimaryKey(), \"Query table structure failed\");\n\n        TableColumn string = columnList.get(3);\n        Assertions.assertEquals(dialectProperties.toCase(\"string\"), string.getName(), \"Query table structure failed\");\n        //Assertions.assertTrue(string.getNullable(), \"Query table structure failed\");\n        Assertions.assertEquals(\"DATA\", TestUtils.unWrapperDefaultValue(string.getDefaultValue()),\n            \"Query table structure failed\");\n\n        List<TableIndex> tableIndexList = tableService.queryIndexes(tableQueryParam);\n        Assertions.assertEquals(4L, tableIndexList.size(), \"Query table structure failed\");\n        Map<String, TableIndex> tableIndexMap = EasyCollectionUtils.toIdentityMap(tableIndexList,\n            TableIndex::getName);\n        TableIndex idxDate = tableIndexMap.get(dialectProperties.toCase(tableName + \"_idx_date\"));\n        Assertions.assertEquals(\"date index\", idxDate.getComment(), \"Query table structure failed\");\n        Assertions.assertEquals(IndexTypeEnum.NORMAL.getCode(), idxDate.getType(), \"Query table structure failed\");\n        Assertions.assertEquals(1L, idxDate.getColumnList().size(), \"Query table structure failed\");\n        Assertions.assertEquals(dialectProperties.toCase(\"date\"), idxDate.getColumnList().get(0).getColumnName(),\n            \"Query table structure failed\");\n        Assertions.assertEquals(CollationEnum.DESC.getCode(), idxDate.getColumnList().get(0).getCollation(),\n            \"Query table structure failed\");\n\n        TableIndex ukNumber = tableIndexMap.get(dialectProperties.toCase(tableName + \"_uk_number\"));\n        Assertions.assertEquals(\"unique index\", ukNumber.getComment(), \"Query table structure failed\");\n        Assertions.assertEquals(IndexTypeEnum.UNIQUE.getCode(), ukNumber.getType(), \"Query table structure failed\");\n\n        TableIndex idxNumberString = tableIndexMap.get(dialectProperties.toCase(tableName + \"_idx_number_string\"));\n        Assertions.assertEquals(2, idxNumberString.getColumnList().size(), \"Query table structure failed\");\n    }\n\n    @Test\n    @Order(Integer.MAX_VALUE)\n    public void dropTable() {\n        for (DialectProperties dialectProperties : dialectPropertiesList) {\n            try {\n                String dbTypeEnum = dialectProperties.getDbType();\n                Long dataSourceId = TestUtils.nextLong();\n                Long consoleId = TestUtils.nextLong();\n\n                DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n                dataSourceCreateParam.setType(dbTypeEnum);\n                dataSourceCreateParam.setUrl(dialectProperties.getUrl());\n                dataSourceCreateParam.setUser(dialectProperties.getUsername());\n                dataSourceCreateParam.setPassword(dialectProperties.getPassword());\n                dataSourceService.preConnect(dataSourceCreateParam);\n\n                // Create a console\n                ConsoleConnectParam consoleCreateParam = new ConsoleConnectParam();\n                consoleCreateParam.setDataSourceId(dataSourceId);\n                consoleCreateParam.setConsoleId(consoleId);\n                consoleCreateParam.setDatabaseName(dialectProperties.getDatabaseName());\n                consoleService.createConsole(consoleCreateParam);\n\n                // Create table structure\n                DlExecuteParam templateQueryParam = new DlExecuteParam();\n                templateQueryParam.setConsoleId(consoleId);\n                templateQueryParam.setDataSourceId(dataSourceId);\n                templateQueryParam.setSql(dialectProperties.getDropTableSql(TABLE_NAME));\n                dlTemplateService.execute(templateQueryParam);\n            } catch (Exception e) {\n                log.warn(\"Failed to delete table structure.\", e);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/dialect/ClickHouseDialectProperties.java",
    "content": "package ai.chat2db.server.test.domain.data.service.dialect;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n@Component\npublic class ClickHouseDialectProperties implements DialectProperties {\n\n    @Override\n    public String getDbType() {\n        return \"CLICKHOUSE\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:clickhouse://localhost:8123\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:postgresql://error:5432/ali_dbhub\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"default\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        String sql = \"CREATE TABLE \" + tableName + \"\\n\"\n                + \"(\\n\"\n                + \"    id     serial\\n\"\n                + \"        constraint \" + tableName + \"_pk primary key,\\n\"\n                + \"    date   timestamp,\\n\"\n                + \"    number int,\\n\"\n                + \"    string varchar(100) default 'DATA'\\n\"\n                + \");\\n\";\n        sql += \"comment on table \" + tableName + \" is 'Test table';\\n\";\n        sql += \"comment on column \" + tableName + \".id is 'Primary key auto-increment';\\n\";\n        sql += \"comment on column \" + tableName + \".date is 'date';\\n\";\n        sql += \"comment on column \" + tableName + \".number is 'long integer';\\n\";\n        sql += \"comment on column \" + tableName + \".string is 'name';\\n\";\n        sql += \"create index \" + tableName + \"idx_date on \" + tableName + \" (date desc);\";\n        sql += \"create unique index \" + tableName + \"_uk_number on \" + tableName + \" (number);\";\n        sql += \"create index \" + tableName + \"_idx_number_string on \" + tableName + \" (number, date);\";\n        sql += \"comment on index \" + tableName + \"_uk_number is 'date index';\";\n        sql += \"comment on index \" + tableName + \"_uk_number is 'unique index';\";\n        sql += \"comment on index \" + tableName + \"_idx_number_string is 'Union index';\";\n        return sql;\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO \" + tableName + \" (date,number,string) VALUES ('\" + DateUtil.format(date,\n                DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\"\n                + \"from \" + tableName + \"\\n\"\n                + \"where id = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\"\n                + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/dialect/DialectProperties.java",
    "content": "package ai.chat2db.server.test.domain.data.service.dialect;\n\nimport java.util.Date;\n\n\n\n/**\n * Dialect configuration\n *\n * @author Jiaju Zhuang\n */\npublic interface DialectProperties {\n\n    /**\n     * Supported database types\n     *\n     * @return\n     */\n    String getDbType();\n\n    /**\n     * connection\n     *\n     * @return\n     */\n    String getUrl();\n\n    /**\n     * Abnormal connection\n     *\n     * @return\n     */\n    String getErrorUrl();\n\n    /**\n     * userName\n     *\n     * @return\n     */\n\n    String getUsername();\n\n    /**\n     * password\n     *\n     * @return\n     */\n    String getPassword();\n\n    /**\n     *  Name database\n     *\n     * @return\n     */\n    String getDatabaseName();\n\n    /**\n     * The case depends on the specific database:\n     * Create table structure: test table\n     * Field:\n     * id primary key auto-increment\n     * date date is not empty\n     * number long integer type\n     * string string length 100 default value \"DATA\"\n     *\n     * Index (plus $tableName_ because some database indexes are globally unique):\n     * $tableName_idx_date date index reverse order\n     * $tableName_uk_number unique index\n     * $tableName_idx_number_string joint index\n     *\n     * @return\n     */\n    String getCrateTableSql(String tableName);\n\n    /**\n     * Create table structure\n     *\n     * @return\n     */\n    String getDropTableSql(String tableName);\n\n    /**\n     * Create a piece of data\n     *\n     * @return\n     */\n    String getInsertSql(String tableName, Date date, Long number, String string);\n\n    /**\n     * Query a query sql\n     *\n     * @return\n     */\n    String getSelectSqlById(String tableName, Long id);\n\n    /**\n     * Get a sql whose table structure does not exist\n     *\n     * @return\n     */\n    String getTableNotFoundSqlById(String tableName);\n\n    /**\n     * Convert case\n     * Some database table structures store uppercase letters by default\n     * Some databases store lowercase by default\n     *\n     * @param string\n     * @return\n     */\n    String toCase(String string);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/dialect/H2DialectProperties.java",
    "content": "package ai.chat2db.server.test.domain.data.service.dialect;\n\nimport java.util.Date;\n\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\n/**\n * h2\n *\n * @author Jiaju Zhuang\n */\n@Component\npublic class H2DialectProperties implements DialectProperties {\n\n    @Override\n    public String getDbType() {\n        return \"H2\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:h2:~/.dbhub/db/ali_dbhub_dev;MODE=MYSQL\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:h2:tcp://error:8084/error\";\n    }\n\n    @Override\n    public String getUsername() {\n        return null;\n    }\n\n    @Override\n    public String getPassword() {\n        return null;\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"ALI_DBHUB_DEV\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        // TODO druid has sql parsing bug\n        String sql = \"CREATE TABLE `\" + tableName + \"`\\n\\t\"\n            + \"(\\n\\t\"\n            + \"    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'Primary key auto-increment',\\n\\t\"\n            + \"    `date`   datetime                          not null COMMENT 'date',\\n\\t\"\n            + \"    `number` bigint COMMENT 'long integer',\\n\\t\"\n            + \"    `string` VARCHAR(100) default 'DATA' COMMENT 'name'\\n\\t\"\n            + \");\\n\\t\";\n        sql += \"comment on table \" + tableName + \" is 'Test table';\\n\\t\";\n        sql += \"create index \" + tableName + \"_idx_date on \" + tableName + \"(DATE desc);\\n\\t\";\n        sql += \"comment on index \" + tableName + \"_idx_date is 'date index';\\n\\t\";\n        sql += \"create unique index \" + tableName + \"_uk_number   on \" + tableName + \"(NUMBER);\\n\\t\";\n        sql += \"comment on index \" + tableName + \"_uk_number is 'unique index';\\n\\t\";\n        sql += \"create index \" + tableName + \"_idx_number_string   on \" + tableName + \"(NUMBER, DATE);\\n\\t\";\n        sql += \"comment on index \" + tableName + \"_idx_number_string is 'Union index';\\n\\t\";\n        return sql;\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO `\" + tableName + \"` (date,number,string) VALUES ('\" + DateUtil.format(date,\n            DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\\t\"\n            + \"from \" + tableName + \"\\n\\t\"\n            + \"where `id` = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\\t\"\n            + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootUpperCase(string);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/dialect/MysqlDialectProperties.java",
    "content": "package ai.chat2db.server.test.domain.data.service.dialect;\n\nimport java.util.Date;\n\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\n/**\n * mysql\n *\n * @author Jiaju Zhuang\n */\n@Component\npublic class MysqlDialectProperties implements DialectProperties {\n\n    @Override\n    public String getDbType() {\n        return \"MYSQL\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:mysql://localhost:3306\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:mysql://error.rm-8vb099vo8309mcngk.mysql.zhangbei.rds.aliyuncs.com:3306\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"root\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"ali_dbhub_test\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        return \"CREATE TABLE `\" + tableName + \"`\\n\\t\"\n            + \"(\\n\\t\"\n            + \"    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'Primary key auto-increment',\\n\\t\"\n            + \"    `date`   datetime(3)                          not null COMMENT 'date',\\n\\t\"\n            + \"    `number` bigint COMMENT 'long integer',\\n\\t\"\n            + \"    `string` VARCHAR(100) default 'DATA' COMMENT 'name',\\n\\t\"\n            + \"    index \" + tableName + \"_idx_date (date desc) comment 'date index',\\n\\t\"\n            + \"    unique \" + tableName + \"_uk_number (number) comment 'unique index',\\n\\t\"\n            + \"    index \" + tableName + \"_idx_number_string (number, date) comment 'Union index'\\n\\t\"\n            + \") COMMENT ='Test table';\";\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO `\" + tableName + \"` (date,number,string) VALUES ('\" + DateUtil.format(date,\n            DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\\t\"\n            + \"from \" + tableName + \"\\n\\t\"\n            + \"where `id` = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\"\n            + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/dialect/OracleDialectProperties.java",
    "content": "package ai.chat2db.server.test.domain.data.service.dialect;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n@Component\npublic class OracleDialectProperties implements DialectProperties {\n\n    @Override\n    public String getDbType() {\n        return \"ORACLE\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:oracle:thin:@localhost:1521:XE\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:oracle:thin:@localhost:1521:XE1\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"system\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"XEPDB1\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        return \"CREATE TABLE `\" + tableName + \"`\\n\\t\"\n                + \"(\\n\\t\"\n                + \"    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'Primary key auto-increment',\\n\\t\"\n                + \"    `date`   datetime(3)                          not null COMMENT 'date',\\n\\t\"\n                + \"    `number` bigint COMMENT 'long integer',\\n\\t\"\n                + \"    `string` VARCHAR(100) default 'DATA' COMMENT 'name',\\n\\t\"\n                + \"    index \" + tableName + \"_idx_date (date desc) comment 'date index',\\n\\t\"\n                + \"    unique \" + tableName + \"_uk_number (number) comment 'unique index',\\n\\t\"\n                + \"    index \" + tableName + \"_idx_number_string (number, date) comment 'Union index'\\n\\t\"\n                + \") COMMENT ='Test table';\";\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO `\" + tableName + \"` (date,number,string) VALUES ('\" + DateUtil.format(date,\n                DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\\t\"\n                + \"from \" + tableName + \"\\n\\t\"\n                + \"where `id` = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\"\n                + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/dialect/PostgresqlDialectProperties.java",
    "content": "/**\n * Alipay.com Inc.\n * Copyright (c) 2004-2022 All Rights Reserved.\n */\npackage ai.chat2db.server.test.domain.data.service.dialect;\n\nimport java.util.Date;\n\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author jipengfei\n * @version : PgDialectProperties.java, v 0.1 December 13, 2022 21:48 jipengfei Exp $\n */\n@Component\npublic class PostgresqlDialectProperties implements DialectProperties {\n\n    @Override\n    public String getDbType() {\n        return \"POSTGRESQL\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:postgresql://localhost:5432/ali_dbhub_test\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:postgresql://error:5432/ali_dbhub\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"ali_dbhub\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"ali_dbhub_test\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        String sql = \"CREATE TABLE \" + tableName + \"\\n\"\n            + \"(\\n\"\n            + \"    id     serial\\n\"\n            + \"        constraint \" + tableName + \"_pk primary key,\\n\"\n            + \"    date   timestamp,\\n\"\n            + \"    number int,\\n\"\n            + \"    string varchar(100) default 'DATA'\\n\"\n            + \");\\n\";\n        sql += \"comment on table \" + tableName + \" is 'Test table';\\n\";\n        sql += \"comment on column \" + tableName + \".id is 'Primary key auto-increment';\\n\";\n        sql += \"comment on column \" + tableName + \".date is 'date';\\n\";\n        sql += \"comment on column \" + tableName + \".number is 'long integer';\\n\";\n        sql += \"comment on column \" + tableName + \".string is 'name';\\n\";\n        sql += \"create index \" + tableName + \"idx_date on \" + tableName + \" (date desc);\";\n        sql += \"create unique index \" + tableName + \"_uk_number on \" + tableName + \" (number);\";\n        sql += \"create index \" + tableName + \"_idx_number_string on \" + tableName + \" (number, date);\";\n        sql += \"comment on index \" + tableName + \"_uk_number is 'date index';\";\n        sql += \"comment on index \" + tableName + \"_uk_number is 'unique index';\";\n        sql += \"comment on index \" + tableName + \"_idx_number_string is 'Union index';\";\n        return sql;\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO \" + tableName + \" (date,number,string) VALUES ('\" + DateUtil.format(date,\n            DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\"\n            + \"from \" + tableName + \"\\n\"\n            + \"where id = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\"\n            + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/dialect/SQLITEDialectProperties.java",
    "content": "\npackage ai.chat2db.server.test.domain.data.service.dialect;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n\n/**\n * @author jipengfei\n * @version : SQLITEDialectProperties.java\n */\n@Component\npublic class SQLITEDialectProperties implements DialectProperties{\n    @Override\n    public String getDbType() {\n        return \"SQLITE\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:sqlite:identifier.sqlite\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return null;\n    }\n\n    @Override\n    public String getUsername() {\n        return null;\n    }\n\n    @Override\n    public String getPassword() {\n        return null;\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return \"main\";\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        return \"CREATE TABLE `\" + tableName + \"`\\n\\t\"\n            + \"(\\n\\t\"\n            + \"    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'Primary key auto-increment',\\n\\t\"\n            + \"    `date`   datetime(3)                          not null COMMENT 'date',\\n\\t\"\n            + \"    `number` bigint COMMENT 'long integer',\\n\\t\"\n            + \"    `string` VARCHAR(100) default 'DATA' COMMENT 'name',\\n\\t\"\n            + \"    index \" + tableName + \"_idx_date (date desc) comment 'date index',\\n\\t\"\n            + \"    unique \" + tableName + \"_uk_number (number) comment 'unique index',\\n\\t\"\n            + \"    index \" + tableName + \"_idx_number_string (number, date) comment 'Union index'\\n\\t\"\n            + \") COMMENT ='Test table';\";\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO `\" + tableName + \"` (date,number,string) VALUES ('\" + DateUtil.format(date,\n            DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\\t\"\n            + \"from \" + tableName + \"\\n\\t\"\n            + \"where `id` = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\"\n            + \"from \" + tableName + \"_notfound;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n        return StringUtils.toRootLowerCase(string);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/service/dialect/SQLServerDialectProperties.java",
    "content": "\npackage ai.chat2db.server.test.domain.data.service.dialect;\n\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n\n/**\n * @author jipengfei\n * @version : SQLServerDialectProperties.java\n */\n@Component\npublic class SQLServerDialectProperties implements DialectProperties{\n    @Override\n    public String getDbType() {\n        return \"SQLSERVER\";\n    }\n\n    @Override\n    public String getUrl() {\n        return \"jdbc:sqlserver://localhost:1433;encrypt=true;trustServerCertificate=true;integratedSecurity=false;Trusted_Connection=yes\";\n    }\n\n    @Override\n    public String getErrorUrl() {\n        return \"jdbc:sqlserver://localhost:14331;encrypt=true;trustServerCertificate=true;integratedSecurity=true;\";\n    }\n\n    @Override\n    public String getUsername() {\n        return \"sa\";\n    }\n\n    @Override\n    public String getPassword() {\n        return \"Ali_dbhub!\";\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return null;\n    }\n\n    @Override\n    public String getCrateTableSql(String tableName) {\n        return \"CREATE TABLE [dbo].[\"+tableName+\"] ( [id] bigint NOT NULL, [date] datetime NOT NULL, [String] varchar(1) NOT NULL, [number] bigint NULL);CREATE UNIQUE CLUSTERED INDEX [id] ON [dbo].[table_name] ( [id] ASC);CREATE NONCLUSTERED INDEX [table_name_date_index] ON [dbo].[table_name] ( [date] ASC);CREATE NONCLUSTERED INDEX [table_name_String_index] ON [dbo].[table_name] ( [String] ASC);CREATE UNIQUE NONCLUSTERED INDEX [table_name_pk] ON [dbo].[table_name] ( [number] ASC);EXEC sp_addextendedproperty @name=N'MS_Description', @value=N'mmm', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'table_name', @level2type=N'COLUMN', @level2name=N'id';EXEC sp_addextendedproperty @name=N'MS_Description', @value=N'mmm', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'table_name', @level2type=N'COLUMN', @level2name=N'date';EXEC sp_addextendedproperty @name=N'MS_Description', @value=N'mmm', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'table_name', @level2type=N'COLUMN', @level2name=N'String';EXEC sp_addextendedproperty @name=N'MS_Description', @value=N'mmm', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'table_name', @level2type=N'COLUMN', @level2name=N'number';\";\n    }\n\n    @Override\n    public String getDropTableSql(String tableName) {\n        return \"drop table \" + tableName + \";\";\n    }\n\n    @Override\n    public String getInsertSql(String tableName, Date date, Long number, String string) {\n        return \"INSERT INTO \" + tableName + \" (date,number,string) VALUES ('\" + DateUtil.format(date,\n            DatePattern.NORM_DATETIME_MS_FORMAT) + \"','\" + number + \"','\" + string + \"');\";\n    }\n\n    @Override\n    public String getSelectSqlById(String tableName, Long id) {\n        return \"select *\\n\\t\"\n            + \"from \" + tableName + \"\\n\\t\"\n            + \"where `id` = '\" + id + \"';\";\n    }\n\n    @Override\n    public String getTableNotFoundSqlById(String tableName) {\n        return \"select *\\n\\t\"\n            + \"from \" + tableName + \"_not_find ;\";\n    }\n\n    @Override\n    public String toCase(String string) {\n         return StringUtils.toRootLowerCase(string);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/domain/data/utils/TestUtils.java",
    "content": "package ai.chat2db.server.test.domain.data.utils;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport ai.chat2db.server.test.domain.data.service.dialect.DialectProperties;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\n\n/**\n * Test tool class\n *\n * @author Jiaju Zhuang\n */\npublic class TestUtils {\n\n    public static final AtomicLong ATOMIC_LONG = new AtomicLong();\n\n    /**\n     * a globally unique long\n     *\n     * @return\n     */\n    public static long nextLong() {\n        return ATOMIC_LONG.incrementAndGet();\n    }\n\n    /**\n     * If the default value is something like 'DATA'\n     * then you need to remove ''\n     *\n     * @param defaultValue\n     * @return\n     */\n    public static String unWrapperDefaultValue(String defaultValue) {\n        if (defaultValue == null) {\n            return null;\n        }\n        if (defaultValue.startsWith(\"'\") && defaultValue.endsWith(\"'\")) {\n            if (defaultValue.length() < 2) {\n                return defaultValue;\n            } else if (defaultValue.length() == 2) {\n                return \"\";\n            } else {\n                return defaultValue.substring(1, defaultValue.length() - 1);\n            }\n        }\n        return defaultValue;\n    }\n\n    public static void buildContext(DialectProperties dialectProperties,Long dataSourceId,Long consoleId){\n        ConnectInfo connectInfo = new ConnectInfo();\n        connectInfo.setUser(dialectProperties.getUsername());\n        connectInfo.setConsoleId(consoleId);\n        connectInfo.setDataSourceId(dataSourceId);\n        connectInfo.setPassword(dialectProperties.getPassword());\n        connectInfo.setDbType(dialectProperties.getDbType());\n        connectInfo.setUrl(dialectProperties.getUrl());\n        connectInfo.setDatabase(dialectProperties.getDatabaseName());\n        connectInfo.setConsoleOwn(false);\n        Chat2DBContext.putContext(connectInfo);\n    }\n\n    public static void remove(){\n        Chat2DBContext.removeContext();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/temp/HttpTest.java",
    "content": ""
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/temp/SQLParseTest.java",
    "content": "package ai.chat2db.server.test.temp;\n\nimport com.github.vertical_blank.sqlformatter.SqlFormatter;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\n\npublic class SQLParseTest {\n\n    public static void main(String[] args) throws JSQLParserException {\n        String sql = \"CREATE OR REPLACE PROCEDURE public.raise_salary(emp_id integer, percentage numeric)\\n\"\n            + \" LANGUAGE plpgsql\\n\"\n            + \"AS $procedure$\\n\"\n            + \"BEGIN\\n\"\n            + \"    UPDATE employees\\n\"\n            + \"    SET salary = salary + (salary * percentage / 100)\\n\"\n            + \"    WHERE id = emp_id;\\n\"\n            + \"COMMIT;\\n\"\n            + \"END; -- sdsd\\n\"\n            + \"$procedure$\";\n\n        Statements statements = CCJSqlParserUtil.parseStatements(sql);\n\n        // If there are multiple statements, the actual type after parsing is StatementList\n\n\n        // Iterate through each statement\n        for (Statement stmt : statements.getStatements()) {\n            // If it is a single statement, the actual type is Statement\n            System.out.println(stmt.toString());\n\n            System.out.println(\" dddd:\"+SqlFormatter.format(stmt.toString()));\n            System.out.println(\" hu:\"+ cn.hutool.db.sql.SqlFormatter.format(stmt.toString()));\n        }\n        String s = SqlFormatter.format(\"SELECT * FROM table1\");\n        System.out.println(s);\n\n\n\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/temp/SqlTest.java",
    "content": "package ai.chat2db.server.test.temp;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.spi.model.Sql;\nimport jakarta.annotation.Resource;\n\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.param.DlExecuteParam;\nimport ai.chat2db.server.domain.api.service.ConsoleService;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.domain.api.param.SqlAnalyseParam;\nimport ai.chat2db.server.test.common.BaseTest;\nimport ai.chat2db.server.test.domain.data.service.dialect.MysqlDialectProperties;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport com.alibaba.fastjson2.JSON;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Test;\n\n@Slf4j\npublic class SqlTest extends BaseTest {\n\n    @Resource\n    private TableService tableService;\n    @Resource\n    private MysqlDialectProperties mysqlDialectProperties;\n    @Resource\n    private DataSourceService dataSourceService;\n    @Resource\n    private ConsoleService consoleService;\n    @Resource\n    private DlTemplateService dlTemplateService;\n\n    @Test\n    public void test() {\n        // creat\n        DataSourcePreConnectParam dataSourceCreateParam = new DataSourcePreConnectParam();\n\n        dataSourceCreateParam.setType(\"MYSQL\");\n        dataSourceCreateParam.setUrl(mysqlDialectProperties.getUrl());\n        dataSourceCreateParam.setUser(mysqlDialectProperties.getUsername());\n        dataSourceCreateParam.setPassword(mysqlDialectProperties.getPassword());\n        ActionResult actionResult = dataSourceService.preConnect(dataSourceCreateParam);\n\n        DataResult<String> createTable = tableService.createTableExample(\"MYSQL\");\n        log.info(\"sql1：{}\", createTable.getData());\n        SqlAnalyseParam sqlAnalyseParam = new SqlAnalyseParam();\n        sqlAnalyseParam.setDataSourceId(1L);\n        sqlAnalyseParam.setSql(createTable.getData());\n        List<Sql> sqlList = new ArrayList<>();\n        sqlList.add(Sql.builder().sql(createTable.getData()).build());\n\n        // Create a console\n        ConsoleConnectParam consoleCreateParam = new ConsoleConnectParam();\n        consoleCreateParam.setDataSourceId(1L);\n        consoleCreateParam.setConsoleId(1L);\n        consoleCreateParam.setDatabaseName(mysqlDialectProperties.getDatabaseName());\n        consoleService.createConsole(consoleCreateParam);\n\n        // delete\n        DlExecuteParam templateQueryParam = new DlExecuteParam();\n        templateQueryParam.setConsoleId(1L);\n        templateQueryParam.setDataSourceId(1L);\n        templateQueryParam.setSql(\"drop table test;\");\n        ListResult<ExecuteResult> executeResult = dlTemplateService.execute(templateQueryParam);\n        log.info(\"result:{}\", JSON.toJSONString(executeResult));\n\n        // Create table structure\n        templateQueryParam = new DlExecuteParam();\n        templateQueryParam.setConsoleId(1L);\n        templateQueryParam.setDataSourceId(1L);\n        templateQueryParam.setSql(sqlList.get(0).getSql());\n        executeResult = dlTemplateService.execute(templateQueryParam);\n        log.info(\"result:{}\", JSON.toJSONString(executeResult));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/temp/TempTest.java",
    "content": "package ai.chat2db.server.test.temp;\n\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\npublic class TempTest {\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/java/ai/chat2db/server/test/temp/UserTest.java",
    "content": "package ai.chat2db.server.test.temp;\n\nimport ai.chat2db.server.test.common.BaseTest;\n\nimport cn.hutool.crypto.digest.DigestUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Test;\n\n@Slf4j\npublic class UserTest extends BaseTest {\n\n    @Test\n    public void test() {\n        log.info(\"password:{}\", DigestUtil.bcrypt(\"dbhub\"));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/resources/h2/init.sql",
    "content": "DROP TABLE if exists test_query;\n\nCREATE TABLE  `test_query`\n(\n    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '主键',\n    `name`   VARCHAR(100) COMMENT '名字',\n     `date`   datetime  COMMENT '时间',\n     `number`   int  COMMENT '数字'\n)  ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='测试表';\n\n\nINSERT INTO `test_query` (name,date,number) VALUES ('姓名','2022-01-01',123);"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/resources/h2/init_close.sql",
    "content": "DROP TABLE if exists test_close;\n\nCREATE TABLE `test_close`\n(\n    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '主键',\n    `name`   VARCHAR(100) COMMENT '名字',\n     `date`   datetime  COMMENT '时间',\n     `number`   int  COMMENT '数字'\n)  ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='测试表';\n\n\nINSERT INTO `test_close` (name,date,number) VALUES ('姓名','2022-01-01',123);"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/resources/h2/init_transaction.sql",
    "content": "DROP TABLE if exists test_transaction;\n\nCREATE TABLE `test_transaction`\n(\n    `id`     bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '主键',\n    `name`   VARCHAR(100) COMMENT '名字',\n     `date`   datetime  COMMENT '时间',\n     `number`   int  COMMENT '数字'\n)  ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='测试表';\n\n\nINSERT INTO `test_transaction` (name,date,number) VALUES ('姓名','2022-01-01',123);"
  },
  {
    "path": "chat2db-server/chat2db-server-test/src/test/resources/logback-test-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n</configuration>"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-tools</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-tools-base</artifactId>\n    <packaging>jar</packaging>\n    <name>chat2db-server-tools-base</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId>\n            <scope>provided</scope>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/constant/EasyToolsConstant.java",
    "content": "package ai.chat2db.server.tools.base.constant;\n\n/**\n * constant\n *\n * @author Shi Yi\n */\npublic interface EasyToolsConstant {\n\n    /**\n     * Log tracking id\n     */\n    String LOG_TRACE_ID = \"EAGLEEYE_TRACE_ID\";\n\n    /**\n     * Maximum paging size\n     */\n    int MAX_PAGE_SIZE = 1000;\n\n    /**\n     * serializedid\n     */\n    long SERIAL_VERSION_UID = 1L;\n\n    /**\n     * Maximum number of loops to prevent many loops from entering an infinite loop\n     */\n    int MAXIMUM_ITERATIONS = 10 * 1000;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/constant/SymbolConstant.java",
    "content": "package ai.chat2db.server.tools.base.constant;\n\n/**\n * Constant enumeration of common symbols\n *\n * @author Shi Yi\n **/\npublic class SymbolConstant {\n    /**\n     * +\n     */\n    public static final String PLUS = \"+\";\n    /**\n     * -\n     */\n    public static final String MINUS = \"-\";\n    /**\n     * *\n     */\n    public static final String ASTERISK = \"*\";\n    /**\n     * /\n     */\n    public static final String SLASH = \"/\";\n    /**\n     * apostrophe\"'\"\n     */\n    public static final String SQUOT = \"'\";\n    /**\n     * apostrophe\"\"\"\n     */\n    public static final String DOUBLE_SQUOT = \"\\\"\";\n    /**\n     * empty string \"\"\n     */\n    public static final String EMPTY = \"\";\n    /**\n     * delimiter \"-\"\n     */\n    public static final String SEPARATOR = \"-\";\n    /**\n     * equal sign \"=\"\n     */\n    public static final String EQ = \"=\";\n    /**\n     * semicolon \";\"\n     */\n    public static final String SEMICOLON = \";\";\n    /**\n     * comma \",\"\n     */\n    public static final String COMMA = \",\";\n    /**\n     * point \".\"\n     */\n    public static final String DOT = \".\";\n    /**\n     * colon \":\"\n     */\n    public static final String COLON = \":\";\n    /**\n     * blank line \"\\n\\n\"\n     */\n    public static final String BLANK_LINE = \"\\n\\n\";\n    /**\n     * new line \"\\n\"\n     */\n    public static final String NEW_LINE = \"\\n\";\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/BaseEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\n/**\n * Basic enumeration\n *\n * Due to the limitations of Java enumeration inheritance, the enumeration base class can only be designed as an interface.\n * Please ensure that the subclass must be an enumeration type.\n *\n * @author Jiaju Zhuang\n **/\npublic interface BaseEnum<T> {\n\n    /**\n     * Returns the enumeration code.\n     * It is generally recommended to directly return the name of the enumeration\n     *\n     * @return code\n     */\n    T getCode();\n\n    /**\n     * Returns the description of the enumeration.\n     * Return the enumerated Chinese to facilitate front-end drop-down\n     *\n     * @return description\n     */\n    String getDescription();\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/DataSourceTypeEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\nimport lombok.Getter;\n\n/**\n * @author moji\n * @version ConnectionTypeEnum.java, v 0.1 September 16, 2022 14:59 moji Exp $\n * @date 2022/09/16\n */\n@Getter\npublic enum DataSourceTypeEnum implements BaseEnum<String> {\n\n    /**\n     * mysql database connection\n     */\n    MYSQL(\"mysql database connection\"),\n\n    /**\n     * redis database connection\n     */\n    REDIS(\"redis database connection\"),\n\n    /**\n     * sqlserver database connection\n     */\n    SQLSERVER(\"sqlserver database connection\"),\n\n    /**\n     * mongo database connection\n     */\n    MONGODB(\"mongo database connection\"),\n\n    ;\n\n    final String description;\n\n    DataSourceTypeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/DeletedIdEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\nimport lombok.Getter;\n\n/**\n * Delete mark enumeration\n * <p>\n * In order to be compatible with unique primary key + tombstone.\n * Use DeletedId to mark whether the current data is deleted.\n * If it is 0, it means it has not been deleted.\n * Anything else means it has been deleted.\n * When deleting, execute the statement: update set deleted_id = di where condition = condition;\n *\n * @author Shi Yi\n */\n@Getter\npublic enum DeletedIdEnum implements BaseEnum<Long> {\n\n    /**\n     * Not deleted\n     */\n    NOT_DELETED(0L, \"Not deleted\"),\n\n    ;\n\n    final Long code;\n    final String description;\n\n    DeletedIdEnum(Long code, String description) {\n        this.code = code;\n        this.description = description;\n    }\n\n    /**\n     * Determine whether the current data has been logically deleted\n     *\n     * @param deletedId deleted_id in table\n     * @return Has it been deleted?\n     */\n    public static boolean isDeleted(Long deletedId) {\n        return !NOT_DELETED.getCode().equals(deletedId);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/OperationEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\nimport lombok.Getter;\n\n/**\n * Operation enumeration\n *\n * @author Shi Yi\n */\n@Getter\npublic enum OperationEnum implements BaseEnum<String> {\n    /**\n     * creat\n     */\n    CREATE(\"creat\"),\n\n    /**\n     * update\n     */\n    UPDATE(\"update\"),\n\n    /**\n     * delete\n     */\n    DELETE(\"delete\"),\n\n    ;\n\n    final String description;\n\n    OperationEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/OrderByDirectionEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\n/**\n * Enumeration of sorting directions\n *\n * @author Shi Yi\n */\npublic enum OrderByDirectionEnum implements BaseEnum<String> {\n\n    /**\n     * asc\n     */\n    ASC,\n    /**\n     * desc\n     */\n    DESC;\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n    @Override\n    public String getDescription() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/StatusEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\nimport lombok.Getter;\n\n/**\n * @author moji\n * @version StatusEnum.java, v 0.1 September 25, 2022 16:57 moji Exp $\n * @date 2022/09/25\n */\n@Getter\npublic enum StatusEnum implements BaseEnum<String> {\n\n    /**\n     * draft\n     */\n    DRAFT(\"draft\"),\n\n    /**\n     * release\n     */\n    RELEASE(\"release\"),\n\n    ;\n\n    final String description;\n\n    StatusEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/SystemEnvironmentEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\nimport lombok.Getter;\n\n/**\n * System environment\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum SystemEnvironmentEnum implements BaseEnum<String> {\n\n    /**\n     * dev\n     */\n    DEV(\"dev\", \"本地\"),\n\n    /**\n     * test\n     */\n    TEST(\"test\", \"测试\"),\n\n    /**\n     * release\n     */\n    RELEASE(\"release\", \"正式\"),\n\n    ;\n\n    final String code;\n\n    final String description;\n\n    SystemEnvironmentEnum(String code, String description) {\n        this.code = code;\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/WhiteListTypeEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\nimport lombok.Getter;\n\n/**\n * @author moji\n * @version WhiteListTypeEnum.java, v 0.1 September 25, 2022 16:57 moji Exp $\n * @date 2022/09/25\n */\n@Getter\npublic enum WhiteListTypeEnum implements BaseEnum<String> {\n\n    /**\n     * vector interface\n     */\n    VECTOR(\"VECTOR\"),\n\n    ;\n\n    final String description;\n\n    WhiteListTypeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/enums/YesOrNoEnum.java",
    "content": "package ai.chat2db.server.tools.base.enums;\n\nimport lombok.Getter;\n\n/**\n * Whether to enumerate\n *\n * @author Shi Yi\n */\n@Getter\npublic enum YesOrNoEnum implements BaseEnum<String> {\n\n    /**\n     * yes\n     */\n    YES(\"Y\", \"是\", true),\n    /**\n     * no\n     */\n    NO(\"N\", \"否\", false),\n\n    ;\n\n    final String letter;\n    final String description;\n    final boolean booleanValue;\n\n    YesOrNoEnum(String letter, String description, boolean booleanValue) {\n        this.letter = letter;\n        this.description = description;\n        this.booleanValue = booleanValue;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n    /**\n     * Convert based on boolean value\n     *\n     * @param booleanValue Boolean value\n     * @return\n     */\n    public static YesOrNoEnum valueOf(Boolean booleanValue) {\n        if (booleanValue == null) {\n            return null;\n        }\n        if (booleanValue) {\n            return YesOrNoEnum.YES;\n        }\n        return YesOrNoEnum.NO;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/excption/BusinessException.java",
    "content": "package ai.chat2db.server.tools.base.excption;\n\nimport lombok.Data;\n\n/**\n * Business abnormality.\n * Those that do not require manual intervention are called business exceptions.\n *\n * @author zhuangjiaju\n * @date 2021/06/26\n */\n@Data\npublic class BusinessException extends RuntimeException {\n    /**\n     * The encoding of the exception\n     */\n    private String code;\n    /**\n     * Exception information parameters\n     */\n    private Object[] args;\n\n    public BusinessException() {\n        this(\"common.businessError\");\n    }\n\n    public BusinessException(String code) {\n        this(code, null);\n    }\n\n    public BusinessException(String code, Object[] args) {\n        super(code);\n        this.code = code;\n        this.args = args;\n    }\n\n    public BusinessException(String code, Object[] args, Throwable throwable) {\n        super(code, throwable);\n        this.code = code;\n        this.args = args;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/excption/SystemException.java",
    "content": "package ai.chat2db.server.tools.base.excption;\n\nimport lombok.Data;\n\n/**\n * Business abnormality.\n * Simply put, exceptions that require manual intervention are called system exceptions.\n *\n * @author zhuangjiaju\n * @date 2021/06/26\n */\n@Data\npublic class SystemException extends RuntimeException {\n\n    /**\n     * The encoding of the exception\n     */\n    private String code;\n    /**\n     * Exception information parameters\n     */\n    private Object[] args;\n\n    public SystemException() {\n        this(\"common.systemError\");\n    }\n\n    public SystemException(String code) {\n        this(code, null);\n    }\n\n    public SystemException(String code, Object[] args) {\n        super(code);\n        this.code = code;\n        this.args = args;\n    }\n\n    public SystemException(String code, Object[] args, Throwable throwable) {\n        super(code, throwable);\n        this.code = code;\n        this.args = args;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/handler/EasyCallBackHandler.java",
    "content": "package ai.chat2db.server.tools.base.handler;\n\n/**\n * Callback handler\n * For example, this method will be executed when meatq calls back.\n *\n * @author Shi Yi\n */\npublic interface EasyCallBackHandler {\n    /**\n     * Called before handling the callback\n     */\n    default void preHandle() {\n    }\n\n    /**\n     * Called after handling the callback\n     * If an exception is thrown, it will not be handled.\n     */\n    default void postHandle() {\n    }\n\n    /**\n     * Called after handling the callback\n     * Will be called regardless of whether there is an exception or not\n     */\n    default void afterCompletion() {\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/Result.java",
    "content": "package ai.chat2db.server.tools.base.wrapper;\n\n/**\n * @author qiuyuyu\n * @date 2022/01/20\n */\npublic interface Result<T> extends Traceable{\n    /**\n     * whether succeed\n     *\n     * @return\n     * @mock true\n     */\n    boolean success();\n\n    /**\n     * Is the setting successful?\n     *\n     * @return\n     */\n    void success(boolean success);\n\n    /**\n     * error coding\n     *\n     * @return\n     * @mock 000000\n     */\n    String errorCode();\n\n    /**\n     * Set error encoding\n     *\n     * @param errorCode\n     */\n    void errorCode(String errorCode);\n\n    /**\n     * error message\n     *\n     * @return\n     */\n    String errorMessage();\n\n\n    /**\n     * Set error message\n     *\n     * @param errorMessage\n     */\n    void errorMessage(String errorMessage);\n\n    /**\n     * error detail stack info\n     */\n    void errorDetail(String errorDetail);\n\n    /**\n     * error detail\n     *\n     * @return\n     */\n    String errorDetail();\n\n    /**\n     * solution link\n     */\n    void solutionLink(String solutionLink);\n\n    /**\n     * solution link\n     *\n     * @return\n     */\n    String solutionLink();\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/Traceable.java",
    "content": "package ai.chat2db.server.tools.base.wrapper;\n\n/**\n * Is it possible to track\n *\n * @author Shi Yi\n */\npublic interface Traceable {\n    /**\n     * Get traceId\n     *\n     * @return traceId\n     */\n    String getTraceId();\n\n    /**\n     * Set traceId\n     *\n     * @param traceId\n     */\n    void setTraceId(String traceId);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/param/OrderBy.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.param;\n\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.enums.OrderByDirectionEnum;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * sorted objects\n *\n * @author Shi Yi\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class OrderBy implements Serializable {\n    /**\n     * sort field\n     */\n    private String orderConditionName;\n    /**\n     * Sorting direction\n     */\n    private OrderByDirectionEnum direction;\n\n    public static OrderBy of(String property, OrderByDirectionEnum direction) {\n        return new OrderBy(property, direction);\n    }\n\n    public static OrderBy asc(String property) {\n        return new OrderBy(property, OrderByDirectionEnum.ASC);\n    }\n\n    public static OrderBy desc(String property) {\n        return new OrderBy(property, OrderByDirectionEnum.DESC);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/param/OrderCondition.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.param;\n\n/**\n * Sorting conditions\n *\n * @author Shi Yi\n */\npublic interface OrderCondition {\n\n    /**\n     * Return column name\n     *\n     * @return\n     */\n    OrderBy getOrderBy();\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/param/PageQueryParam.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.param;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.enums.OrderByDirectionEnum;\n\nimport jakarta.validation.constraints.Min;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.experimental.SuperBuilder;\nimport org.hibernate.validator.constraints.Range;\n\n/**\n * Parameters of paging query\n *\n * @author zhuangjiaju\n * @date 2021/06/26\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\npublic class PageQueryParam implements Serializable {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * page number\n     */\n    @NotNull(message = \"Pagination page number cannot be empty\")\n    @Min(value = 1, message = \"Pagination page number must be greater than 0\")\n    private Integer pageNo;\n    /**\n     * Paging Size\n     */\n    @NotNull(message = \"Paging size cannot be empty\")\n    @Range(min = 1, max = EasyToolsConstant.MAX_PAGE_SIZE,\n        message = \"Paging size must be between 1-\" + EasyToolsConstant.MAX_PAGE_SIZE)\n    private Integer pageSize;\n\n    /**\n     * Whether to return the total number of items\n     * Not returned by default to improve performance\n     */\n    private Boolean enableReturnCount;\n\n    /**\n     * sort\n     */\n    private List<OrderBy> orderByList;\n\n    public PageQueryParam() {\n        this.pageNo = 1;\n        this.pageSize = 100;\n        this.enableReturnCount = Boolean.FALSE;\n    }\n\n    /**\n     * Query all data\n     */\n    public void queryAll() {\n        this.pageNo = 1;\n        this.pageSize = Integer.MAX_VALUE;\n    }\n\n    /**\n     * Query 1 piece of data\n     */\n    public void queryOne() {\n        this.pageNo = 1;\n        this.pageSize = 1;\n    }\n\n    /**\n     * Add a new sort and replace the original sort\n     *\n     * @param orderBy sort\n     * @return Sorting parameters\n     */\n    public PageQueryParam orderBy(OrderBy orderBy) {\n        orderByList = new ArrayList<>();\n        orderByList.add(orderBy);\n        return this;\n    }\n\n    /**\n     * Add a new sort and replace the original sort\n     *\n     * @param orderConditionName sort field\n     * @param direction          Sorting direction\n     * @return Sorting parameters\n     */\n    public PageQueryParam orderBy(String orderConditionName, OrderByDirectionEnum direction) {\n        return orderBy(new OrderBy(orderConditionName, direction));\n    }\n\n    /**\n     * Add a new sort and replace the original sort\n     *\n     * @param orderCondition Sorting conditions\n     * @return Sorting parameters\n     */\n    public PageQueryParam orderBy(OrderCondition orderCondition) {\n        return orderBy(orderCondition.getOrderBy());\n    }\n\n    /**\n     * Add a new sort\n     *\n     * @param orderBy sort\n     * @return Sorting parameters\n     */\n    public PageQueryParam andOrderBy(OrderBy orderBy) {\n        orderByList.add(orderBy);\n        return this;\n    }\n\n    /**\n     * Add a new sort\n     *\n     * @param orderConditionName sort field\n     * @param direction          Sorting direction\n     * @return Sorting parameters\n     */\n    public PageQueryParam andOrderBy(String orderConditionName, OrderByDirectionEnum direction) {\n        return andOrderBy(new OrderBy(orderConditionName, direction));\n    }\n\n    /**\n     * Add a new sort\n     *\n     * @param orderCondition Sorting conditions\n     * @return Sorting parameters\n     */\n    public PageQueryParam andOrderBy(OrderCondition orderCondition) {\n        return andOrderBy(orderCondition.getOrderBy());\n    }\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/param/QueryParam.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.param;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.enums.OrderByDirectionEnum;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * query parameters\n *\n * @author zhuangjiaju\n * @date 2021/06/26\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class QueryParam implements Serializable {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * sort\n     */\n    private List<OrderBy> orderByList;\n\n    /**\n     * Add a new sort and replace the original sort\n     *\n     * @param orderBy sort\n     * @return Sorting parameters\n     */\n    public QueryParam orderBy(OrderBy orderBy) {\n        orderByList = new ArrayList<>();\n        orderByList.add(orderBy);\n        return this;\n    }\n\n    /**\n     * Add a new sort and replace the original sort\n     *\n     * @param orderConditionName sort field\n     * @param direction          Sorting direction\n     * @return Sorting parameters\n     */\n    public QueryParam orderBy(String orderConditionName, OrderByDirectionEnum direction) {\n        return orderBy(new OrderBy(orderConditionName, direction));\n    }\n\n    /**\n     * Add a new sort and replace the original sort\n     *\n     * @param orderCondition Sorting conditions\n     * @return Sorting parameters\n     */\n    public QueryParam orderBy(OrderCondition orderCondition) {\n        return orderBy(orderCondition.getOrderBy());\n    }\n\n    /**\n     * Add a new sort\n     *\n     * @param orderBy sort\n     * @return Sorting parameters\n     */\n    public QueryParam andOrderBy(OrderBy orderBy) {\n        orderByList.add(orderBy);\n        return this;\n    }\n\n    /**\n     * Add a new sort\n     *\n     * @param orderConditionName sort field\n     * @param direction          Sorting direction\n     * @return Sorting parameters\n     */\n    public QueryParam andOrderBy(String orderConditionName, OrderByDirectionEnum direction) {\n        return andOrderBy(new OrderBy(orderConditionName, direction));\n    }\n\n    /**\n     * Add a new sort\n     *\n     * @param orderCondition Sorting conditions\n     * @return Sorting parameters\n     */\n    public QueryParam andOrderBy(OrderCondition orderCondition) {\n        return andOrderBy(orderCondition.getOrderBy());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/request/PageQueryRequest.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.request;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.experimental.SuperBuilder;\nimport org.hibernate.validator.constraints.Range;\n\n/**\n * Parameters of paging query\n *\n * @author zhuangjiaju\n * @date 2021/06/26\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\npublic class PageQueryRequest implements Serializable {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    /**\n     * page number\n     *\n     * @mock 1\n     */\n    @NotNull(message = \"Pagination page number cannot be empty\")\n    private Integer pageNo;\n    /**\n     * Number of pagination items\n     *\n     * @demo 10\n     */\n    @NotNull(message = \"Paging size cannot be empty\")\n    @Range(min = 1, max = EasyToolsConstant.MAX_PAGE_SIZE,\n        message = \"Paging size must be between 1-\" + EasyToolsConstant.MAX_PAGE_SIZE)\n    private Integer pageSize;\n\n    public PageQueryRequest() {\n        this.pageNo = 1;\n        this.pageSize = 10;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/result/ActionResult.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.result;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.wrapper.Result;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * action return object\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\npublic class ActionResult implements Serializable, Result {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    /**\n     * whether succeed\n     *\n     * @mock true\n     */\n    private Boolean success;\n\n    /**\n     * error coding\n     *\n     * @see CommonErrorEnum\n     */\n    private String errorCode;\n    /**\n     * error message\n     */\n    private String errorMessage;\n\n    /**\n     * error detail\n     */\n    private String errorDetail;\n\n    /**\n     * solution link\n     */\n    private String solutionLink;\n\n    /**\n     * traceId\n     */\n    private String traceId;\n\n    public ActionResult() {\n        this.success = Boolean.TRUE;\n    }\n\n    /**\n     * Return success\n     *\n     * @return operation result\n     */\n    public static ActionResult isSuccess() {\n        return new ActionResult();\n    }\n\n    @Override\n    public boolean success() {\n        return success;\n    }\n\n    @Override\n    public void success(boolean success) {\n        this.success = success;\n    }\n\n    @Override\n    public String errorCode() {\n        return errorCode;\n    }\n\n    @Override\n    public void errorCode(String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public String errorMessage() {\n        return errorMessage;\n    }\n\n    @Override\n    public void errorMessage(String errorMessage) {\n        this.errorMessage = errorMessage;\n    }\n\n    @Override\n    public void errorDetail(String errorDetail) {\n        this.errorDetail = errorDetail;\n    }\n\n    @Override\n    public String errorDetail() {\n        return errorDetail;\n    }\n\n    @Override\n    public void solutionLink(String solutionLink) {\n        this.solutionLink = solutionLink;\n    }\n\n    @Override\n    public String solutionLink() {\n        return solutionLink;\n    }\n\n    /**\n     * Return failure\n     *\n     * @param errorCode    error code\n     * @param errorMessage error message\n     * @param errorDetail  error detail\n     * @return operation result\n     */\n    public static ActionResult fail(String errorCode, String errorMessage, String errorDetail) {\n        ActionResult result = new ActionResult();\n        result.errorCode = errorCode;\n        result.errorMessage = errorMessage;\n        result.success = Boolean.FALSE;\n        result.solutionLink(\"https://github.com/chat2db/Chat2DB/wiki/Chat2DB\");\n        result.errorDetail(errorDetail);\n        return result;\n    }\n\n    public DataResult<Boolean> toBooleaSuccessnDataResult() {\n        return DataResult.<Boolean>builder()\n            .success(success)\n            .errorCode(errorCode)\n            .errorMessage(errorMessage)\n            .errorDetail(errorDetail)\n            .solutionLink(solutionLink)\n            .traceId(traceId)\n            .data(Boolean.TRUE)\n            .build();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/result/DataResult.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.result;\n\nimport java.io.Serializable;\nimport java.util.function.Function;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.wrapper.Result;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * data return object\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\npublic class DataResult<T> implements Serializable, Result<T> {\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    /**\n     * whether succeed\n     *\n     * @mock true\n     */\n    private Boolean success;\n\n    /**\n     * error coding\n     *\n     * @see CommonErrorEnum\n     */\n    private String errorCode;\n\n    /**\n     * error message\n     */\n    private String errorMessage;\n\n    /**\n     * error detail\n     */\n    private String errorDetail;\n\n    /**\n     * solution link\n     */\n    private String solutionLink;\n\n    /**\n     * Data information\n     */\n    private T data;\n\n    /**\n     * traceId\n     */\n    private String traceId;\n\n    public DataResult() {\n        this.success = Boolean.TRUE;\n    }\n\n    private DataResult(T data) {\n        this();\n        this.data = data;\n    }\n\n    /**\n     * Construct the return object\n     *\n     * @param data object to be constructed\n     * @param <T> The object type to be constructed\n     * @return the returned result\n     */\n    public static <T> DataResult<T> of(T data) {\n        return new DataResult<>(data);\n    }\n\n    /**\n     * Construct an empty return object\n     *\n     * @param <T> The object type to be constructed\n     * @return the returned result\n     */\n    public static <T> DataResult<T> empty() {\n        return new DataResult<>();\n    }\n\n    /**\n     * Build exception return\n     *\n     * @param errorCode error coding\n     * @param errorMessage error message\n     * @param <T> The object type to be constructed\n     * @return the returned result\n     */\n    public static <T> DataResult<T> error(String errorCode, String errorMessage) {\n        DataResult<T> result = new DataResult<>();\n        result.errorCode = errorCode;\n        result.errorMessage = errorMessage;\n        result.success = false;\n        return result;\n    }\n\n\n    /**\n     * Determine whether data exists\n     *\n     * @param dataResult\n     * @return whether data exists\n     */\n    public static boolean hasData(DataResult<?> dataResult) {\n        return dataResult != null && dataResult.getSuccess() && dataResult.getData() != null;\n    }\n\n    /**\n     * Convert the current type to another type\n     *\n     * @param mapper conversion method\n     * @param <R> Return type\n     * @return the returned result\n     */\n    public <R> DataResult<R> map(Function<T, R> mapper) {\n        R returnData = hasData(this) ? mapper.apply(getData()) : null;\n        DataResult<R> dataResult = new DataResult<>();\n        dataResult.setSuccess(getSuccess());\n        dataResult.setErrorCode(getErrorCode());\n        dataResult.setErrorMessage(getErrorMessage());\n        dataResult.setData(returnData);\n        dataResult.setTraceId(getTraceId());\n        return dataResult;\n    }\n\n    @Override\n    public boolean success() {\n        return success;\n    }\n\n    @Override\n    public void success(boolean success) {\n        this.success = success;\n    }\n\n    @Override\n    public String errorCode() {\n        return errorCode;\n    }\n\n    @Override\n    public void errorCode(String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public String errorMessage() {\n        return errorMessage;\n    }\n\n    @Override\n    public void errorMessage(String errorMessage) {\n        this.errorMessage = errorMessage;\n    }\n\n    @Override\n    public void errorDetail(String errorDetail) {\n        this.errorDetail = errorDetail;\n    }\n\n    @Override\n    public String errorDetail() {\n        return errorDetail;\n    }\n\n    @Override\n    public void solutionLink(String solutionLink) {\n        this.solutionLink = solutionLink;\n    }\n\n    @Override\n    public String solutionLink() {\n        return solutionLink;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/result/ListResult.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.result;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.wrapper.Result;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * data return object\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\npublic class ListResult<T> implements Serializable, Result<T> {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    /**\n     * whether succeed\n     *\n     * @mock true\n     */\n    private Boolean success;\n\n    /**\n     * error coding\n     */\n    private String errorCode;\n    /**\n     * Exception information\n     */\n    private String errorMessage;\n    /**\n     * Data information\n     */\n    private List<T> data;\n    /**\n     * traceId\n     */\n    private String traceId;\n\n    /**\n     * error detail\n     */\n    private String errorDetail;\n\n    /**\n     * solution link\n     */\n    private String solutionLink;\n\n    public ListResult() {\n        this.success = Boolean.TRUE;\n    }\n\n    private ListResult(List<T> data) {\n        this();\n        this.data = data;\n    }\n\n    /**\n     * Build the list and return the object\n     *\n     * @param data object to be constructed\n     * @param <T> The object type to be constructed\n     * @return the returned list\n     */\n    public static <T> ListResult<T> of(List<T> data) {\n        return new ListResult<>(data);\n    }\n\n    /**\n     * Build an empty list and return the object\n     *\n     * @param <T> The type of object to be constructed\n     * @return the returned list\n     */\n    public static <T> ListResult<T> empty() {\n        return of(Collections.emptyList());\n    }\n\n    /**\n     * Build exception return list\n     *\n     * @param errorCode error coding\n     * @param errorMessage error message\n     * @param <T> The object type to be constructed\n     * @return the returned list\n     */\n    public static <T> ListResult<T> error(String errorCode, String errorMessage) {\n        ListResult<T> result = new ListResult<>();\n        result.errorCode = errorCode;\n        result.errorMessage = errorMessage;\n        result.success = Boolean.TRUE;\n        return result;\n    }\n\n    /**\n     * Determine whether data exists\n     *\n     * @param listResult\n     * @return whether data exists\n     */\n    public static boolean hasData(ListResult<?> listResult) {\n        return listResult != null && listResult.getSuccess() && listResult.getData() != null && !listResult.getData()\n            .isEmpty();\n    }\n\n    /**\n     * Convert the current type to another type\n     *\n     * @param mapper conversion method\n     * @param <R> Return type\n     * @return paging return object\n     */\n    public <R> ListResult<R> map(Function<T, R> mapper) {\n        List<R> returnData = hasData(this) ? getData().stream().map(mapper).collect(Collectors.toList())\n            : Collections.emptyList();\n        ListResult<R> listResult = new ListResult<>();\n        listResult.setSuccess(getSuccess());\n        listResult.setErrorCode(getErrorCode());\n        listResult.setErrorMessage(getErrorMessage());\n        listResult.setData(returnData);\n        listResult.setTraceId(getTraceId());\n        return listResult;\n    }\n\n    @Override\n    public boolean success() {\n        return success;\n    }\n\n    @Override\n    public void success(boolean success) {\n        this.success = success;\n    }\n\n    @Override\n    public String errorCode() {\n        return errorCode;\n    }\n\n    @Override\n    public void errorCode(String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public String errorMessage() {\n        return errorMessage;\n    }\n\n    @Override\n    public void errorMessage(String errorMessage) {\n        this.errorMessage = errorMessage;\n    }\n\n    @Override\n    public void errorDetail(String errorDetail) {\n        this.errorDetail = errorDetail;\n    }\n\n    @Override\n    public String errorDetail() {\n        return errorDetail;\n    }\n\n    @Override\n    public void solutionLink(String solutionLink) {\n        this.solutionLink = solutionLink;\n    }\n\n    @Override\n    public String solutionLink() {\n        return solutionLink;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/result/PageResult.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.result;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.wrapper.Result;\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult.Page;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * data return object\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\npublic class PageResult<T> implements Serializable, Result<List<T>> {\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    /**\n     * whether succeed\n     *\n     * @mock true\n     */\n    private Boolean success;\n\n    /**\n     * error coding\n     */\n    private String errorCode;\n    /**\n     * Exception information\n     */\n    private String errorMessage;\n    /**\n     * Data information\n     */\n    private List<T> data;\n    /**\n     * Page coding\n     */\n    private Integer pageNo;\n    /**\n     * Paging Size\n     */\n    private Integer pageSize;\n    /**\n     * Total\n     */\n    private Long total;\n    /**\n     * traceId\n     */\n    private String traceId;\n    /**\n     * Is there a next page?\n     */\n    private Boolean hasNextPage;\n\n    /**\n     * error detail\n     */\n    private String errorDetail;\n\n    /**\n     * solution link\n     */\n    private String solutionLink;\n\n    public PageResult() {\n        this.pageNo = 1;\n        this.pageSize = 10;\n        this.total = 0L;\n        this.success = Boolean.TRUE;\n    }\n\n    private PageResult(List<T> data, Long total, Long pageNo, Long pageSize) {\n        this();\n        this.data = data;\n        this.total = total;\n        if (pageNo != null) {\n            this.pageNo = Math.toIntExact(pageNo);\n        }\n        if (pageSize != null) {\n            this.pageSize = Math.toIntExact(pageSize);\n        }\n    }\n\n    private PageResult(List<T> data, Long total, Integer pageNo, Integer pageSize) {\n        this();\n        this.data = data;\n        this.total = total;\n        if (pageNo != null) {\n            this.pageNo = pageNo;\n        }\n        if (pageSize != null) {\n            this.pageSize = pageSize;\n        }\n    }\n\n    /**\n     * Construct paging return object\n     *\n     * @param data object returned\n     * @param total total number of items\n     * @param pageNo page number\n     * @param pageSize paging size\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> PageResult<T> of(List<T> data, Long total, Long pageNo, Long pageSize) {\n        return new PageResult<>(data, total, pageNo, pageSize);\n    }\n\n    /**\n     * Construct paging return object\n     *\n     * @param data object returned\n     * @param total total number of items\n     * @param pageNo page number\n     * @param pageSize paging size\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> PageResult<T> of(List<T> data, Long total, Integer pageNo, Integer pageSize) {\n        return new PageResult<>(data, total, pageNo, pageSize);\n    }\n\n    /**\n     * Construct paging return object\n     *\n     * @param data object returned\n     * @param total total number of items\n     * @param param paging parameters\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> PageResult<T> of(List<T> data, Long total, PageQueryParam param) {\n        return new PageResult<>(data, total, param.getPageNo(), param.getPageSize());\n    }\n\n    /**\n     * Construct paging return object\n     * Total number of items returned\n     *\n     * @param data object returned\n     * @param param paging parameters\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> PageResult<T> of(List<T> data, PageQueryParam param) {\n        return new PageResult<>(data, 0L, param.getPageNo(), param.getPageSize());\n    }\n\n    /**\n     * Construct an empty return object\n     *\n     * @param pageNo page number\n     * @param pageSize paging size\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> PageResult<T> empty(Long pageNo, Long pageSize) {\n        return of(Collections.emptyList(), 0L, pageNo, pageSize);\n    }\n\n    /**\n     * Construct an empty return object\n     *\n     * @param pageNo page number\n     * @param pageSize paging size\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> PageResult<T> empty(Integer pageNo, Integer pageSize) {\n        return of(Collections.emptyList(), 0L, pageNo, pageSize);\n    }\n\n    /**\n     * Determine whether there is a next page\n     * Calculated based on paging size to prevent total from being empty\n     *\n     * @return Is there a next page?\n     */\n    public Boolean calculateHasNextPage() {\n        // There is a paging size calculated based on the paging\n        if (total > 0) {\n            return (long)pageSize * pageNo <= total;\n        }\n        // No data, definitely no next page\n        if (data == null || data.isEmpty()) {\n            return false;\n        }\n        // The current number is less than the number of pages\n        return data.size() >= pageSize;\n    }\n\n    /**\n     * Determine whether there is a next page\n     * Calculated based on paging size to prevent total from being empty\n     *\n     * @return Is there a next page?\n     * @deprecated using {@link #getHasNextPage()} ()}\n     */\n    @Deprecated\n    public boolean hasNextPage() {\n        return getHasNextPage();\n    }\n\n    public Boolean getHasNextPage() {\n        if (hasNextPage == null) {\n            hasNextPage = calculateHasNextPage();\n        }\n        return hasNextPage;\n    }\n\n    /**\n     * Determine whether data exists\n     *\n     * @return whether data exists\n     */\n    public boolean hasData() {\n        return hasData(this);\n    }\n\n    /**\n     * Return query exception information\n     *\n     * @param errorCode error coding\n     * @param errorMessage error message\n     * @param <T> The returned object\n     * @return paging return object\n     */\n    public static <T> PageResult<T> error(String errorCode, String errorMessage) {\n        PageResult<T> result = new PageResult<>();\n        result.errorCode = errorCode;\n        result.errorMessage = errorMessage;\n        result.success = Boolean.FALSE;\n        return result;\n    }\n\n    /**\n     * Determine whether data exists\n     *\n     * @param pageResult\n     * @return whether data exists\n     */\n    public static boolean hasData(PageResult<?> pageResult) {\n        return pageResult != null && pageResult.getSuccess() && pageResult.getData() != null && !pageResult.getData()\n            .isEmpty();\n    }\n\n    /**\n     * Convert the current type to another type\n     *\n     * @param mapper conversion method\n     * @param <R> Return type\n     * @return paging return object\n     */\n    public <R> PageResult<R> map(Function<T, R> mapper) {\n        List<R> returnData = hasData(this) ? getData().stream().map(mapper).collect(Collectors.toList())\n            : Collections.emptyList();\n        PageResult<R> pageResult = new PageResult<>();\n        pageResult.setSuccess(getSuccess());\n        pageResult.setErrorCode(getErrorCode());\n        pageResult.setErrorMessage(getErrorMessage());\n        pageResult.setData(returnData);\n        pageResult.setPageNo(getPageNo());\n        pageResult.setPageSize(getPageSize());\n        pageResult.setTotal(getTotal());\n        pageResult.setTraceId(getTraceId());\n        return pageResult;\n    }\n\n    /**\n     * Convert the current type to another type\n     *\n     * @param mapper conversion method\n     * @param <R> Return type\n     * @return paging return object\n     */\n    public <R> ListResult<R> mapToList(Function<T, R> mapper) {\n        List<R> returnData = hasData(this) ? getData().stream().map(mapper).collect(Collectors.toList())\n            : Collections.emptyList();\n        ListResult<R> result = new ListResult<>();\n        result.setSuccess(getSuccess());\n        result.setErrorCode(getErrorCode());\n        result.setErrorMessage(getErrorMessage());\n        result.setTraceId(getTraceId());\n        result.setData(returnData);\n        return result;\n    }\n\n    /**\n     * Convert the current type to another type\n     * and converted to web type\n     * Note here that if the current project also uses <code>PageResult</code> in the web layer, you can directly use the <code>map</code> method interface.\n     *\n     * @param mapper conversion method\n     * @param <R> Return type\n     * @return paging return object\n     */\n    public <R> WebPageResult<R> mapToWeb(Function<T, R> mapper) {\n        List<R> returnData = hasData(this) ? getData().stream().map(mapper).collect(Collectors.toList())\n            : Collections.emptyList();\n        WebPageResult<R> pageResult = new WebPageResult<>();\n        pageResult.setSuccess(getSuccess());\n        pageResult.setErrorCode(getErrorCode());\n        pageResult.setErrorMessage(getErrorMessage());\n        pageResult.setTraceId(getTraceId());\n        // Reset a paging information\n        Page<R> page = new Page<>();\n        pageResult.setData(page);\n        page.setData(returnData);\n        page.setPageNo(getPageNo());\n        page.setPageSize(getPageSize());\n        page.setTotal(getTotal());\n        pageResult.setData(page);\n        return pageResult;\n    }\n\n    @Override\n    public boolean success() {\n        return success;\n    }\n\n    @Override\n    public void success(boolean success) {\n        this.success = success;\n    }\n\n    @Override\n    public String errorCode() {\n        return errorCode;\n    }\n\n    @Override\n    public void errorCode(String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public String errorMessage() {\n        return errorMessage;\n    }\n\n    @Override\n    public void errorMessage(String errorMessage) {\n        this.errorMessage = errorMessage;\n    }\n\n    @Override\n    public void errorDetail(String errorDetail) {\n        this.errorDetail = errorDetail;\n    }\n\n    @Override\n    public String errorDetail() {\n        return errorDetail;\n    }\n\n    @Override\n    public void solutionLink(String solutionLink) {\n        this.solutionLink = solutionLink;\n    }\n\n    @Override\n    public String solutionLink() {\n        return solutionLink;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-base/src/main/java/ai/chat2db/server/tools/base/wrapper/result/web/WebPageResult.java",
    "content": "package ai.chat2db.server.tools.base.wrapper.result.web;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.wrapper.Result;\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.experimental.SuperBuilder;\n\n/**\n *The return object of data\n * Consistent with PageResult, you can also use PageResult directly.\n * This is an additional class created because the front end of some projects needs to encapsulate data+pageNo together.\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\npublic class WebPageResult<T> implements Serializable, Result<List<T>> {\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    /**\n     * whether succeed\n     *\n     * @mock true\n     */\n    private Boolean success;\n    /**\n     * Exception coding\n     */\n    private String errorCode;\n    /**\n     * Exception information\n     */\n    private String errorMessage;\n    /**\n     * Data information\n     */\n    private Page<T> data;\n    /**\n     * traceId\n     */\n    private String traceId;\n\n    /**\n     * error detail\n     */\n    private String errorDetail;\n\n    /**\n     * solution link\n     */\n    private String solutionLink;\n\n    public WebPageResult() {\n        this.success = Boolean.TRUE;\n        this.data = new Page<>();\n    }\n\n    private WebPageResult(List<T> data, Long total, Long pageNo, Long pageSize) {\n        this.success = Boolean.TRUE;\n        this.data = new Page<>(data, total, pageNo, pageSize);\n    }\n\n    private WebPageResult(List<T> data, Long total, Integer pageNo, Integer pageSize) {\n        this.success = Boolean.TRUE;\n        this.data = new Page<>(data, total, pageNo, pageSize);\n    }\n\n    /**\n     * Build pagination return object\n     *\n     * @param data object returned\n     * @param total total number of items\n     * @param pageNo page number\n     * @param pageSize paging size\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> WebPageResult<T> of(List<T> data, Long total, Long pageNo, Long pageSize) {\n        return new WebPageResult<>(data, total, pageNo, pageSize);\n    }\n\n    /**\n     * Construct paging return object\n     *\n     * @param data object returned\n     * @param total total number of items\n     * @param pageNo page number\n     * @param pageSize paging size\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> WebPageResult<T> of(List<T> data, Long total, Integer pageNo, Integer pageSize) {\n        return new WebPageResult<>(data, total, pageNo, pageSize);\n    }\n\n    /**\n     * Construct paging return object\n     *\n     * @param data object returned\n     * @param total total number of items\n     * @param param paging parameters\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> WebPageResult<T> of(List<T> data, Long total, PageQueryParam param) {\n        return new WebPageResult<>(data, total, param.getPageNo(), param.getPageSize());\n    }\n\n    /**\n     * Construct an empty return object\n     *\n     * @param pageNo page number\n     * @param pageSize paging size\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> WebPageResult<T> empty(Long pageNo, Long pageSize) {\n        return of(Collections.emptyList(), 0L, pageNo, pageSize);\n    }\n\n    /**\n     * Construct an empty return object\n     *\n     * @param pageNo page number\n     * @param pageSize paging size\n     * @param <T> The returned object type\n     * @return paging return object\n     */\n    public static <T> WebPageResult<T> empty(Integer pageNo, Integer pageSize) {\n        return of(Collections.emptyList(), 0L, pageNo, pageSize);\n    }\n\n    /**\n     * Determine whether there is a next page\n     * Calculated based on paging size to prevent total from being empty\n     *\n     * @return Is there a next page?\n     * @deprecated using {@link #getHasNextPage()} ()}\n     */\n    @Deprecated\n    public boolean hasNextPage() {\n        return getHasNextPage();\n    }\n\n    public Boolean getHasNextPage() {\n        if (data == null) {\n            return Boolean.FALSE;\n        }\n        return data.getHasNextPage();\n    }\n\n    /**\n     * Return query exception information\n     *\n     * @param errorCode error code\n     * @param errorMessage error message\n     * @param <T> The returned object\n     * @return paging return object\n     */\n    public static <T> WebPageResult<T> error(String errorCode, String errorMessage) {\n        WebPageResult<T> result = new WebPageResult<>();\n        result.errorCode = errorCode;\n        result.errorMessage = errorMessage;\n        result.success = Boolean.FALSE;\n        return result;\n    }\n\n    /**\n     * Determine whether data exists\n     *\n     * @param pageResult\n     * @return whether data exists\n     */\n    public static boolean hasData(WebPageResult<?> pageResult) {\n        return pageResult != null && pageResult.getSuccess() && pageResult.getData() != null\n            && pageResult.getData().getData() != null && !pageResult.getData().getData().isEmpty();\n    }\n\n    /**\n     * Convert the current type to another type\n     *\n     * @param mapper conversion method\n     * @param <R> Return type\n     * @return paging return object\n     */\n    public <R> WebPageResult<R> map(Function<T, R> mapper) {\n        List<R> returnData = hasData(this) ? getData().getData().stream().map(mapper).collect(Collectors.toList())\n            : Collections.emptyList();\n        WebPageResult<R> pageResult = new WebPageResult<>();\n        pageResult.setSuccess(getSuccess());\n        pageResult.setErrorCode(getErrorCode());\n        pageResult.setErrorMessage(getErrorMessage());\n        pageResult.setTraceId(getTraceId());\n        // Reset a paging information\n        Page<R> page = new Page<>();\n        pageResult.setData(page);\n        page.setData(returnData);\n        page.setPageNo(data.getPageNo());\n        page.setPageSize(data.getPageSize());\n        page.setTotal(data.getTotal());\n        return pageResult;\n    }\n\n    @Override\n    public boolean success() {\n        return success;\n    }\n\n    @Override\n    public void success(boolean success) {\n        this.success = success;\n    }\n\n    @Override\n    public String errorCode() {\n        return errorCode;\n    }\n\n    @Override\n    public void errorCode(String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public String errorMessage() {\n        return errorMessage;\n    }\n\n    @Override\n    public void errorMessage(String errorMessage) {\n        this.errorMessage = errorMessage;\n    }\n\n    @Override\n    public void errorDetail(String errorDetail) {\n        this.errorDetail = errorDetail;\n    }\n\n    @Override\n    public String errorDetail() {\n        return errorDetail;\n    }\n\n    @Override\n    public void solutionLink(String solutionLink) {\n        this.solutionLink = solutionLink;\n    }\n\n    @Override\n    public String solutionLink() {\n        return solutionLink;\n    }\n\n    /**\n     * Pagination information\n     *\n     * @param <T>\n     */\n    @Data\n    public static class Page<T> {\n        /**\n         * Data information\n         */\n        private List<T> data;\n        /**\n         * Page coding\n         */\n        private Integer pageNo;\n        /**\n         * Paging Size\n         */\n        private Integer pageSize;\n        /**\n         * Total\n         */\n        private Long total;\n        /**\n         * Is there a next page?\n         */\n        private Boolean hasNextPage;\n\n        public Page() {\n            this.pageNo = 1;\n            this.pageSize = 10;\n            this.total = 0L;\n        }\n\n        private Page(List<T> data, Long total, Long pageNo, Long pageSize) {\n            this();\n            this.data = data;\n            this.total = total;\n            if (pageNo != null) {\n                this.pageNo = Math.toIntExact(pageNo);\n            }\n            if (pageSize != null) {\n                this.pageSize = Math.toIntExact(pageSize);\n            }\n        }\n\n        private Page(List<T> data, Long total, Integer pageNo, Integer pageSize) {\n            this();\n            this.data = data;\n            this.total = total;\n            if (pageNo != null) {\n                this.pageNo = pageNo;\n            }\n            if (pageSize != null) {\n                this.pageSize = pageSize;\n            }\n        }\n\n        public Boolean getHasNextPage() {\n            if (hasNextPage == null) {\n                hasNextPage = calculateHasNextPage();\n            }\n            return hasNextPage;\n        }\n\n        /**\n         * Determine whether there is a next page\n         * Calculated based on paging size to prevent total from being empty\n         *\n         * @return Is there a next page?\n         */\n        public Boolean calculateHasNextPage() {\n            // There is a paging size calculated based on the paging\n            if (total > 0) {\n                return (long)pageSize * pageNo <= total;\n            }\n            // No data, definitely no next page\n            if (data == null || data.isEmpty()) {\n                return false;\n            }\n            // The current number is less than the number of pages\n            return data.size() >= pageSize;\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-tools</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-tools-common</artifactId>\n    <packaging>jar</packaging>\n    <name>chat2db-server-tools-common</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-tools-base</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-collections4</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.fastjson2</groupId>\n            <artifactId>fastjson2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-all</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct-processor</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.zalando</groupId>\n            <artifactId>logbook-spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context-indexer</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/config/Chat2dbProperties.java",
    "content": "package ai.chat2db.server.tools.common.config;\n\nimport ai.chat2db.server.tools.common.enums.ModeEnum;\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author moji\n * @version SystemProperties.java, v 0.1 November 13, 2022 14:28 moji Exp $\n * @date 2022/11/13\n */\n@Configuration\n@ConfigurationProperties(prefix = \"chat2db\")\n@Data\npublic class Chat2dbProperties {\n\n    /**\n     * version\n     */\n    private String version;\n\n    /**\n     * gateway\n     */\n    private GatewayProperties gateway;\n\n    /**\n     * mode\n     */\n    private ModeEnum mode;\n\n    @Data\n    public static class GatewayProperties {\n\n        private String baseUrl;\n        private String modelBaseUrl;\n\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/config/GlobalDict.java",
    "content": "package ai.chat2db.server.tools.common.config;\n\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * global dictionary\n *\n * @author lzy\n */\npublic interface GlobalDict {\n    /**\n     * template file\n     **/\n    List<String> TEMPLATE_FILE = Arrays.asList(\"template.html\", \"template_diy.docx\", \"sub_template_diy.docx\");\n    /**\n     * Template storage directory\n     **/\n    String templateDir = ConfigUtils.CONFIG_BASE_PATH + File.separator + \"template\" + File.separator;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/enums/ModeEnum.java",
    "content": "package ai.chat2db.server.tools.common.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * model\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum ModeEnum implements BaseEnum<String> {\n    /**\n     * DESKTOP\n     */\n    DESKTOP(\"DESKTOP\"),\n\n    /**\n     * WEB\n     */\n    WEB(\"WEB\"),\n\n    ;\n    final String description;\n\n    ModeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/exception/ConnectionException.java",
    "content": "package ai.chat2db.server.tools.common.exception;\n\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport lombok.Getter;\n\n@Getter\npublic class ConnectionException extends BusinessException {\n\n\n    public ConnectionException() {\n        this(\"connection.error\");\n    }\n\n    public ConnectionException(String code) {\n        this(code, null);\n    }\n\n    public ConnectionException(String code, Object[] args) {\n        super(code,args);\n    }\n\n    public ConnectionException(String code, Object[] args, Throwable throwable) {\n        super(code,args, throwable);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/exception/DataAlreadyExistsBusinessException.java",
    "content": "package ai.chat2db.server.tools.common.exception;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport lombok.Getter;\n\n/**\n * Data already exists exception\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic class DataAlreadyExistsBusinessException extends BusinessException {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    public DataAlreadyExistsBusinessException() {\n        super(\"common.dataAlreadyExists\");\n    }\n\n    public DataAlreadyExistsBusinessException(String key, Object value) {\n        super(\"common.dataAlreadyExistsWithParam\", new Object[] {key, value});\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/exception/DataNotFoundException.java",
    "content": "package ai.chat2db.server.tools.common.exception;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport lombok.Getter;\n\n/**\n * Data not found exceptions\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic class DataNotFoundException extends BusinessException {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    public DataNotFoundException() {\n        super(\"common.dataNotFound\");\n    }\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/exception/NeedLoggedInBusinessException.java",
    "content": "package ai.chat2db.server.tools.common.exception;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport lombok.Getter;\n\n/**\n * User login exception\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic class NeedLoggedInBusinessException extends BusinessException {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    public NeedLoggedInBusinessException() {\n        super(\"common.needLoggedIn\");\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/exception/ParamBusinessException.java",
    "content": "package ai.chat2db.server.tools.common.exception;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport lombok.Getter;\n\n/**\n * Parameter exceptions\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic class ParamBusinessException extends BusinessException {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    public ParamBusinessException() {\n        super(\"common.paramError\");\n    }\n\n    public ParamBusinessException(String paramString) {\n        super(\"common.paramDetailError\", new Object[] {paramString});\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/exception/PermissionDeniedBusinessException.java",
    "content": "package ai.chat2db.server.tools.common.exception;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport lombok.Getter;\n\n/**\n * Permission Denied\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic class PermissionDeniedBusinessException extends BusinessException {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    public PermissionDeniedBusinessException() {\n        super(\"common.permissionDenied\");\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/exception/RedirectBusinessException.java",
    "content": "package ai.chat2db.server.tools.common.exception;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport lombok.Getter;\n\n/**\n * Business exceptions that require redirection\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic class RedirectBusinessException extends BusinessException {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    private final String redirect;\n\n    public RedirectBusinessException(String redirect) {\n        super(\"common.redirect\");\n        this.redirect = redirect;\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/model/ConfigJson.java",
    "content": "package ai.chat2db.server.tools.common.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Configuration information for chat2db\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ConfigJson {\n\n    /**\n     * Last successfully launched version\n     */\n    private String latestStartupSuccessVersion;\n\n    /**\n     * jwt\n     */\n    private String jwtSecretKey;\n\n    /**\n     * The unique ID of the  system\n     */\n    private String systemUuid;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/model/Context.java",
    "content": "package ai.chat2db.server.tools.common.model;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * contextual information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Context implements Serializable {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /***\n     * User Info\n     */\n    private LoginUser loginUser;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/model/EasyLambdaQueryWrapper.java",
    "content": "package ai.chat2db.server.tools.common.model;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Predicate;\n\nimport ai.chat2db.server.tools.base.wrapper.param.OrderBy;\nimport ai.chat2db.server.tools.common.util.EasySqlUtils;\nimport com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;\nimport com.baomidou.mybatisplus.core.conditions.SharedString;\nimport com.baomidou.mybatisplus.core.conditions.query.Query;\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport static com.baomidou.mybatisplus.core.enums.SqlKeyword.EQ;\nimport static com.baomidou.mybatisplus.core.enums.SqlKeyword.ORDER_BY;\n\n/**\n * Custom query wrapper\n *\n * @author Jiaju Zhuang\n */\npublic class EasyLambdaQueryWrapper<T> extends AbstractLambdaWrapper<T, EasyLambdaQueryWrapper<T>>\n    implements Query<EasyLambdaQueryWrapper<T>, T, SFunction<T, ?>> {\n\n    public void orderBy(List<OrderBy> orderByList) {\n        if (CollectionUtils.isEmpty(orderByList)) {\n            return;\n        }\n        for (OrderBy orderBy : orderByList) {\n            appendSqlSegments(ORDER_BY, EasySqlUtils.columnToSqlSegment(orderBy.getOrderConditionName()),\n                EasySqlUtils.parseOrderBy(orderBy.getDirection()));\n        }\n    }\n\n    public EasyLambdaQueryWrapper<T> eqWhenPresent(SFunction<T, ?> column, Object val) {\n        if (val != null) {\n            addCondition(true, column, EQ, val);\n        }\n        return typedThis;\n    }\n\n    public EasyLambdaQueryWrapper<T> likeWhenPresent(SFunction<T, ?> column, Object val) {\n        if (val != null) {\n            return like(true, column, val);\n        }\n        return typedThis;\n    }\n\n    public EasyLambdaQueryWrapper<T> inWhenPresent(SFunction<T, ?> column, Collection<?> coll) {\n        if (coll != null) {\n            return in(true, column, coll);\n        }\n        return typedThis;\n    }\n\n    // The following are the methods that come with the system\n    /**\n     * Query field\n     */\n    private SharedString sqlSelect = new SharedString();\n\n    public EasyLambdaQueryWrapper() {\n        this((T)null);\n    }\n\n    public EasyLambdaQueryWrapper(T entity) {\n        super.setEntity(entity);\n        super.initNeed();\n    }\n\n    public EasyLambdaQueryWrapper(Class<T> entityClass) {\n        super.setEntityClass(entityClass);\n        super.initNeed();\n    }\n\n    EasyLambdaQueryWrapper(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq,\n        Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,\n        SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {\n        super.setEntity(entity);\n        super.setEntityClass(entityClass);\n        this.paramNameSeq = paramNameSeq;\n        this.paramNameValuePairs = paramNameValuePairs;\n        this.expression = mergeSegments;\n        this.sqlSelect = sqlSelect;\n        this.paramAlias = paramAlias;\n        this.lastSql = lastSql;\n        this.sqlComment = sqlComment;\n        this.sqlFirst = sqlFirst;\n    }\n\n    /**\n     * SELECT some SQL settings\n     *\n     * @param columns query fields\n     */\n    @SafeVarargs\n    @Override\n    public final EasyLambdaQueryWrapper<T> select(SFunction<T, ?>... columns) {\n        return select(Arrays.asList(columns));\n    }\n\n    public EasyLambdaQueryWrapper<T> select(List<SFunction<T, ?>> columns) {\n        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(columns)) {\n            this.sqlSelect.setStringValue(columnsToString(false, columns));\n        }\n        return typedThis;\n    }\n\n    /**\n     * Filter the field information of the query (except primary key!)\n     * <p>Example 1: As long as the java field name starts with \"test\" -> select(i -&gt; i.getProperty().startsWith(\"test\"))</p>\n     * <p>Example 2: As long as the java field attribute is of type CharSequence -> select(TableFieldInfo::isCharSequence)</p>\n     * <p>Example 3: As long as the java field does not have a filling strategy -> select(i -&gt; i.getFieldFill() == FieldFill.DEFAULT)</p>\n     * <p>Example 4: Want all fields -> select(i -&gt; true)</p>\n     * <p>Example 5: As long as the primary key field -> select(i -&gt; false)</p>\n     *\n     * @param predicate filtering method\n     * @return this\n     */\n    @Override\n    public EasyLambdaQueryWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {\n        if (entityClass == null) {\n            entityClass = getEntityClass();\n        } else {\n            setEntityClass(entityClass);\n        }\n        Assert.notNull(entityClass, \"entityClass can not be null\");\n        this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate));\n        return typedThis;\n    }\n\n    @Override\n    public String getSqlSelect() {\n        return sqlSelect.getStringValue();\n    }\n\n    /**\n     * Used to generate nested sql\n     * <p>Therefore sqlSelect does not pass down</p>\n     */\n    @Override\n    protected EasyLambdaQueryWrapper<T> instance() {\n        return new EasyLambdaQueryWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,\n            new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(),\n            SharedString.emptyString());\n    }\n\n    @Override\n    public void clear() {\n        super.clear();\n        sqlSelect.toNull();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/model/IntegerWrapper.java",
    "content": "package ai.chat2db.server.tools.common.model;\n\nimport java.io.Serializable;\n\n/**\n * Plastic encapsulation class\n *\n * @author Shi Yi\n */\npublic class IntegerWrapper extends Number implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private int value;\n\n    public IntegerWrapper(int initialValue) {\n        value = initialValue;\n    }\n\n    public IntegerWrapper() {\n    }\n\n    public final int get() {\n        return value;\n    }\n\n    public final void set(int newValue) {\n        value = newValue;\n    }\n\n    public final int getAndIncrement() {\n        return getAndAdd(1);\n    }\n\n    public final int getAndDecrement() {\n        return getAndAdd(-1);\n    }\n\n    public final int getAndAdd(int delta) {\n        int oldValue = value;\n        value += delta;\n        return oldValue;\n    }\n\n    public final int incrementAndGet() {\n        return addAndGet(1);\n    }\n\n    public final int decrementAndGet() {\n        return addAndGet(-1);\n    }\n\n    public final int addAndGet(int delta) {\n        value += delta;\n        return value;\n    }\n\n    public final void increment() {\n        add(1);\n    }\n\n    public final void decrement() {\n        add(-1);\n    }\n\n    public final void add(int delta) {\n        value += delta;\n    }\n\n    @Override\n    public int intValue() {\n        return get();\n    }\n\n    @Override\n    public long longValue() {\n        return get();\n    }\n\n    @Override\n    public float floatValue() {\n        return get();\n    }\n\n    @Override\n    public double doubleValue() {\n        return get();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/model/LoginUser.java",
    "content": "package ai.chat2db.server.tools.common.model;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Login user information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class LoginUser implements Serializable {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * user id\n     */\n    private Long id;\n\n    /**\n     * nick name\n     */\n    private String nickName;\n\n    /**\n     * Is it an administrator\n     */\n    private Boolean admin;\n\n    /**\n     * role coding\n     *\n     * @see RoleCodeEnum\n     */\n    private String roleCode;\n\n\n    private String token;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/ConfigUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport ai.chat2db.server.tools.common.model.ConfigJson;\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.lang.UUID;\nimport com.alibaba.fastjson2.JSON;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.File;\nimport java.util.Optional;\n\n/**\n * Configure information on the user side\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class ConfigUtils {\n    public static String CONFIG_BASE_PATH = System.getProperty(\"user.home\") + File.separator + \".chat2db\";\n\n    public static final String APP_PATH = getAppPath();\n\n    private static String version = null;\n    public static File versionFile = null;\n    public static File configFile;\n    private static ConfigJson config = null;\n\n    public static File clientIdFile;\n    private static String clientId = null;\n\n    static {\n        String environment = StringUtils.defaultString(System.getProperty(\"spring.profiles.active\"), \"dev\");\n        if (APP_PATH != null) {\n            versionFile = new File(\n                    getAppPath() + File.separator + \"versions\" + File.separator + \"version\");\n            if (!versionFile.exists()) {\n                versionFile = null;\n            }\n        }\n        configFile = new File(\n                CONFIG_BASE_PATH + File.separator + \"config\" + File.separator + \"config_\" + environment + \".json\");\n        if (!configFile.exists()) {\n            FileUtil.writeUtf8String(JSON.toJSONString(new ConfigJson()), configFile);\n        }\n\n        clientIdFile = new File(\n                CONFIG_BASE_PATH + File.separator + \"config\" + File.separator + \"client_uuid\");\n        if (!clientIdFile.exists()) {\n            String uuid = UUID.fastUUID().toString(true);\n            FileUtil.writeUtf8String(uuid, clientIdFile);\n            clientId = uuid;\n        }\n    }\n\n    public static void updateVersion(String version) {\n        if (versionFile == null) {\n            log.warn(\"VERSION_FILE is null\");\n            return;\n        }\n        FileUtil.writeUtf8String(version, versionFile);\n        ConfigUtils.version = version;\n    }\n\n    public static String getLocalVersion() {\n        if (versionFile == null) {\n            log.warn(\"VERSION_FILE is null\");\n            return null;\n        }\n        if (version != null) {\n            return version;\n        }\n        version = StringUtils.trim(FileUtil.readUtf8String(versionFile));\n        return version;\n    }\n\n    public static String getLatestLocalVersion() {\n        if (versionFile == null) {\n            log.warn(\"VERSION_FILE is null\");\n            return null;\n        }\n        return StringUtils.trim(FileUtil.readUtf8String(versionFile));\n    }\n\n\n    public static ConfigJson getConfig() {\n        if (config == null) {\n            config = JSON.parseObject(StringUtils.trim(FileUtil.readUtf8String(configFile)), ConfigJson.class);\n        }\n        return config;\n    }\n\n    public static String getClientId() {\n        if (clientId == null) {\n            clientId = StringUtils.trim(FileUtil.readUtf8String(clientIdFile));\n        }\n        return clientId;\n    }\n\n    public static void setConfig(ConfigJson config) {\n        String stringConfigJson = JSON.toJSONString(config);\n        FileUtil.writeUtf8String(stringConfigJson, configFile);\n        ConfigUtils.config = config;\n        log.info(\"set config:{}\", stringConfigJson);\n    }\n\n    private static String getAppPath() {\n        try {\n            String jarPath = System.getProperty(\"project.path\");\n            return FileUtil.getParent(jarPath, 4);\n        } catch (Exception e) {\n            log.error(\"getAppPath error\", e);\n            return null;\n        }\n    }\n\n    public static void initProcess() {\n        try {\n            ProcessHandle currentProcess = ProcessHandle.current();\n            long pid = currentProcess.pid();\n            String environment = StringUtils.defaultString(System.getProperty(\"spring.profiles.active\"), \"dev\");\n            File pidFile = new File(CONFIG_BASE_PATH + File.separator + \"config\" + File.separator + environment + \"app.pid\");\n            if (!pidFile.exists()) {\n                FileUtil.writeUtf8String(String.valueOf(pid), pidFile);\n            } else {\n                String oldPid = FileUtil.readUtf8String(pidFile);\n                log.info(\"oldPid:{}\", oldPid);\n                if (StringUtils.isNotBlank(oldPid)) {\n                    Optional<ProcessHandle> processHandle = ProcessHandle.of(Long.parseLong(oldPid));\n                    //log.error(\"processHandle:{}\", JSON.toJSONString(processHandle));\n                    processHandle.ifPresent(handle -> {\n                        ProcessHandle.Info info = handle.info();\n                        String[] arguments = info.arguments().orElse(null);\n                        log.info(\"arguments:{}\", JSON.toJSONString(arguments));\n                        if (arguments == null) {\n                            return;\n                        }\n                        for (String argument : arguments) {\n                            if (StringUtils.equals(\"chat2db-server-start.jar\", argument)) {\n                                handle.destroy();\n                                log.info(\"destroy old process--------\");\n                                break;\n                            }\n                            if (argument.contains(\"Application\")) {\n                                handle.destroy();\n                                log.info(\"destroy old process--------\");\n                                break;\n                            }\n                        }\n                    });\n                }\n\n                FileUtil.writeUtf8String(String.valueOf(pid), pidFile);\n            }\n\n        } catch (Exception e) {\n            log.error(\"updatePid error\", e);\n        }\n\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/ContextUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport ai.chat2db.server.tools.common.exception.NeedLoggedInBusinessException;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\n\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * Context tool class\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class ContextUtils {\n\n    /**\n     * Store context\n     */\n    private static final ThreadLocal<Context> CONTEXT_THREAD_LOCAL = new ThreadLocal<>();\n\n    /**\n     * Get user id\n     *\n     * @return\n     */\n    public static Long getUserId() {\n        return getLoginUser().getId();\n    }\n\n    /**\n     * Get user information\n     *\n     * @return may return empty\n     */\n    public static LoginUser queryLoginUser() {\n        // Go to get login information\n        Context context = queryContext();\n        if (context == null) {\n            return null;\n        }\n        if (context.getLoginUser() == null) {\n            return null;\n        }\n        return context.getLoginUser();\n    }\n\n    /**\n     * Get user information\n     *\n     * @return If it cannot be obtained, a re-login exception will be thrown.\n     */\n    public static LoginUser getLoginUser() {\n        // Go to get login information\n        Context context = queryContext();\n        if (context != null && context.getLoginUser() != null) {\n            return context.getLoginUser();\n        }\n        // Determine that the user must log in\n        throw new NeedLoggedInBusinessException();\n    }\n\n    /**\n     * query context\n     *\n     * @return The interceptor of SaTokenWebMvcConfigurer, when called elsewhere, at least a Context will be returned, and there will be at least tokenValue in it.\n     */\n    public static Context queryContext() {\n        return CONTEXT_THREAD_LOCAL.get();\n    }\n\n    /**\n     * Set context\n     *\n     * @param context\n     * @return\n     */\n    public static void setContext(Context context) {\n        CONTEXT_THREAD_LOCAL.set(context);\n    }\n\n    /**\n     * remove context\n     */\n    public static void removeContext() {\n        CONTEXT_THREAD_LOCAL.remove();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyBooleanUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\n/**\n * Boolean tool class\n *\n * @author Jiaju Zhuang\n */\npublic class EasyBooleanUtils {\n\n    /**\n     * Determine whether two Boolean values are the same\n     *\n     * @param b1\n     * @param b2\n     * @param defaultValue Default value, assuming that b1 and b2 are empty, which default value should be taken?\n     * @return\n     */\n    public static boolean equals(Boolean b1, Boolean b2, Boolean defaultValue) {\n        if (b1 == b2) {\n            return true;\n        }\n        if (b1 == null) {\n            b1 = defaultValue;\n        }\n        if (b2 == null) {\n            b2 = defaultValue;\n        }\n        return b1 == b2;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyCollectionUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport cn.hutool.db.meta.Table;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.springframework.boot.autoconfigure.security.SecurityProperties;\n\n/**\n * Collection tool class\n *\n * @author Jiaju Zhuang\n */\npublic class EasyCollectionUtils {\n\n    /**\n     * Collection stream\n     *\n     * @param collection collection\n     * @param <T> Return type\n     * @return the stream of the collection\n     */\n    public static <T> Stream<T> stream(Collection<T> collection) {\n        return collection != null ? collection.stream() : Stream.empty();\n    }\n\n    /**\n     * Return the first element. If there is none, return empty.\n     *\n     * @param collection collection\n     * @param <T> data type\n     * @return Returns the first element, which may be empty\n     */\n    public static <T> T findFirst(Collection<T> collection) {\n        return stream(collection)\n            .findFirst()\n            .orElse(null);\n    }\n\n    /**\n     * Convert a collection into a list\n     * <p>\n     * Will filter the empty data before and after conversion in the collection, so the input and output parameters will be inconsistent.\n     *\n     * @param collection collection\n     * @param function conversion function\n     * @param <T> Data type before conversion\n     * @param <R> Data type after conversion\n     * @return list If the input parameter is empty, an empty array will be returned and cannot be modified.\n     */\n    public static <T, R> List<R> toList(Collection<T> collection, Function<T, R> function) {\n        return stream(collection)\n            .filter(Objects::nonNull)\n            .map(function)\n            .filter(Objects::nonNull)\n            .collect(Collectors.toList());\n    }\n\n    /**\n     * Convert a collection into a set\n     * <p>\n     * Will filter out empty data before and after conversion in the collection\n     *\n     * @param collection collection\n     * @param function conversion function\n     * @param <T> Data type before conversion\n     * @param <R> Data type after conversion\n     * @return list If the input parameter is empty, an empty array will be returned and cannot be modified.\n     */\n    public static <T, R> Set<R> toSet(Collection<T> collection, Function<T, R> function) {\n        return stream(collection)\n            .filter(Objects::nonNull)\n            .map(function)\n            .filter(Objects::nonNull)\n            .collect(Collectors.toSet());\n    }\n\n    /**\n     * Convert a set into a map. If there is a key conflict, the second one will prevail.\n     *\n     * @param collection collection\n     * @param keyFunction keyFunction\n     * @param valueFunction valueFunction\n     * @param <K> key data type\n     * @param <V> value data type\n     * @param <T> Data type before conversion\n     * @return Convert to future map\n     */\n    public static <K, V, T> Map<K, V> toMap(Collection<T> collection, Function<? super T, K> keyFunction,\n        Function<? super T, V> valueFunction) {\n        return stream(collection)\n            .filter(Objects::nonNull)\n            .collect(Collectors.toMap(keyFunction, valueFunction, (oldValue, newValue) -> newValue));\n    }\n\n    /**\n     * Convert a set into a map. The value of the map is the value of the set. If there is a key conflict, the second one shall prevail.\n     *\n     * @param collection collection\n     * @param keyFunction keyFunction\n     * @param <K> key data type\n     * @param <T> Data type before conversion\n     * @return Convert to future map\n     */\n    public static <K, T> Map<K, T> toIdentityMap(Collection<T> collection, Function<? super T, K> keyFunction) {\n        return toMap(collection, keyFunction, Function.identity());\n    }\n\n    /**\n     * Add another set to a set\n     *\n     * @param collection original collection\n     * @param collectionAdd The collection to be added\n     * @param <C>\n     * @return whether data has been added\n     */\n    public static <C> boolean addAll(final Collection<C> collection, final Collection<C> collectionAdd) {\n        if (collectionAdd == null) {\n            return false;\n        }\n        return collection.addAll(collectionAdd);\n    }\n\n    /**\n     * Determine if the length of a set is 0 but not null\n     *\n     * @param collection collection\n     * @return\n     */\n    public static boolean isEmptyButNotNull(final Collection<?> collection) {\n        return collection != null && collection.isEmpty();\n    }\n\n    /**\n     * Determine whether there is an array with a length of 0 but not null in a bunch of collections\n     *\n     * @param collections returns false if it is empty\n     * @return\n     */\n    public static boolean isAnyEmptyButNotNull(final Collection<?>... collections) {\n        if (ArrayUtils.isEmpty(collections)) {\n            return false;\n        }\n        for (final Collection<?> collection : collections) {\n            if (isEmptyButNotNull(collection)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Add an object to the collection\n     * @param collection original collection\n     * @param objectAdd the object to be added\n     * @param <T>\n     */\n    public static <T> void add(Collection<T> collection, T objectAdd) {\n        if(Objects.isNull(objectAdd)){\n            return;\n        }\n        collection.add(objectAdd);\n    }\n\n    /**\n     * Deduplication based on specified field collection\n     * @param collection original collection\n     * @param keyFunction keyFunction\n     * @param <E>\n     * @param <R>\n     * @return the collection after deduplication\n     */\n    public static <E,R> List<E> distinctByKey(Collection<E> collection, Function<E, R> keyFunction){\n        return stream(collection).filter(distinctByKey(keyFunction)).collect(Collectors.toList());\n    }\n\n\n    static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {\n        Map<Object, Boolean> seen = new ConcurrentHashMap<>();\n        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;\n    }\n\n    public static <E> List<E> union(List<? extends E> list1, List<? extends E> list2) {\n        ArrayList<E> result = new ArrayList();\n        if(list1 != null && list1.size()>0) {\n            result.addAll(list1);\n        }\n        if(list2!= null && list2.size()>0) {\n            result.addAll(list2);\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyEnumUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\n\n/**\n * enum tool class\n * <p>\n * Mainly to solve the problem of each enumeration,\n * you need to write a function to get the value according to the code,\n * which does not seem very friendly.\n *\n * @author Jiaju Zhuang\n */\npublic class EasyEnumUtils {\n    /**\n     * Enumeration cache does not need to loop to read the enumeration every time\n     */\n    private static final Map<String, Map<?, BaseEnum<?>>> ENUM_CACHE = new ConcurrentHashMap<>();\n\n    /**\n     * Get the description of the enumeration based on an enumeration type\n     *\n     * @param clazz enumeration class\n     * @param code Enumeration encoding\n     * @param <T> The type of enumeration\n     * @return If the code cannot be found, the return value is empty.\n     */\n    public static <T extends BaseEnum<?>> String getDescription(final Class<T> clazz, final String code) {\n        BaseEnum<?> baseEnum = getEnum(clazz, code);\n        if (baseEnum == null) {\n            return null;\n        }\n        return baseEnum.getDescription();\n    }\n\n    /**\n     * Get the description of the enumeration based on an enumeration type\n     *\n     * @param clazz enumeration class\n     * @param code Enumeration encoding\n     * @param <T> The type of enumeration\n     * @return If the code cannot be found, the return value is empty.\n     */\n    public static <T extends BaseEnum<?>> T getEnum(final Class<T> clazz, final String code) {\n        return getEnumMap(clazz).get(code);\n    }\n\n    /**\n     * Verify whether it is a valid enumeration\n     *\n     * @param clazz enumeration class\n     * @param code the encoding of the enumeration, null is also considered a valid enumeration\n     * @param <T> The type of enumeration\n     * @return Is it valid?\n     */\n    public static <T extends BaseEnum<?>> boolean isValidEnum(final Class<T> clazz, final String code) {\n        return isValidEnum(clazz, code, true);\n    }\n\n    /**\n     * Verify whether it is a valid enumeration\n     *\n     * @param clazz enumeration class\n     * @param code The encoding of the enumeration. If it is empty, it is considered an invalid enumeration.\n     * @param ignoreNull whether to ignore empty codes\n     * @param <T> The type of enumeration\n     * @return Is it valid?\n     */\n    public static <T extends BaseEnum<?>> boolean isValidEnum(final Class<T> clazz, final String code,\n        final boolean ignoreNull) {\n        if (code == null) {\n            return ignoreNull;\n        }\n        return getEnumMap(clazz).containsKey(code);\n    }\n\n    /**\n     * Get the map of an enumerated code Enum\n     *\n     * @param clazz enumeration class\n     * @param <T> The type of enumeration\n     * @return Map<code, Enum>\n     */\n    public static <T extends BaseEnum<?>> Map<String, T> getEnumMap(final Class<T> clazz) {\n        String className = clazz.getName();\n        Map<?, BaseEnum<?>> result = ENUM_CACHE.computeIfAbsent(className, value -> {\n            T[] baseEnums = clazz.getEnumConstants();\n            return Arrays.stream(baseEnums)\n                .collect(Collectors.toMap(BaseEnum::getCode, Function.identity()));\n        });\n        return (Map)result;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyIntegerUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\npublic class EasyIntegerUtils {\n\n    /**\n     * Determine whether two Boolean values are the same\n     *\n     * @param b1\n     * @param b2\n     * @param defaultValue default value,\n     *                     assuming that b1 b2 is empty,\n     *                     which default value should be taken?\n     * @return\n     */\n    public static boolean equals(Integer b1, Integer b2, Integer defaultValue) {\n        if (b1 == b2) {\n            return true;\n        }\n        if (b1 == null) {\n            b1 = defaultValue;\n        }\n        if (b2 == null) {\n            b2 = defaultValue;\n        }\n        return b1 == b2;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyOptionalUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\n/**\n * Optional tool class\n *\n * @author Jiaju Zhuang\n */\npublic class EasyOptionalUtils {\n\n    /**\n     * Get the value of an object that may not be null\n     *\n     * @param source original object\n     * @param function conversion method\n     * @param <T>\n     * @param <R>\n     * @return Return value If empty, return null\n     */\n    public static <T, R> R mapTo(T source, Function<T, R> function) {\n        return mapTo(source, function, null);\n    }\n\n    /**\n     * Get the value of an object that may not be null\n     *\n     * @param source original object\n     * @param function conversion method\n     * @param defaultValue default value\n     * @param <T>\n     * @param <R>\n     * @return return value\n     */\n    public static <T, R> R mapTo(T source, Function<T, R> function, R defaultValue) {\n        return Optional.ofNullable(source).map(function).orElse(defaultValue);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasySqlUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport ai.chat2db.server.tools.base.enums.OrderByDirectionEnum;\nimport ai.chat2db.server.tools.base.wrapper.param.OrderBy;\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport com.baomidou.mybatisplus.core.conditions.segments.ColumnSegment;\nimport com.baomidou.mybatisplus.core.conditions.segments.OrderBySegmentList;\nimport com.baomidou.mybatisplus.core.enums.SqlKeyword;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport static com.baomidou.mybatisplus.core.enums.SqlKeyword.ASC;\nimport static com.baomidou.mybatisplus.core.enums.SqlKeyword.DESC;\n\n/**\n * sql utils\n *\n * @author Jiaju Zhuang\n */\npublic class EasySqlUtils {\n\n    public static String orderBy(List<OrderBy> orderByList) {\n        if (CollectionUtils.isEmpty(orderByList)) {\n            return null;\n        }\n        OrderBySegmentList orderBySegmentList = new OrderBySegmentList();\n        for (OrderBy orderBy : orderByList) {\n            orderBySegmentList.addAll(\n                Arrays.asList(SqlKeyword.ORDER_BY, columnToSqlSegment(orderBy.getOrderConditionName()),\n                    parseOrderBy(orderBy.getDirection())));\n        }\n        return orderBySegmentList.getSqlSegment();\n    }\n\n    /**\n     * get columnName\n     */\n    public static ColumnSegment columnToSqlSegment(String column) {\n        return () -> column;\n    }\n\n    public static ISqlSegment parseOrderBy(OrderByDirectionEnum direction) {\n        if (direction == OrderByDirectionEnum.ASC) {\n            return ASC;\n        }\n        return DESC;\n    }\n\n    public static String buildLikeRightFuzzy(String param) {\n        if (param == null) {\n            return null;\n        }\n        return param + \"%\";\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyStringUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport com.google.common.base.Strings;\nimport com.google.common.collect.Maps;\nimport jakarta.validation.constraints.NotNull;\nimport org.apache.commons.lang3.RegExUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * String utility class\n *\n * @author Jiaju Zhuang\n */\npublic class EasyStringUtils {\n    /**\n     * 0 characters\n     */\n    private static final char ZERO_CHAR = '0';\n\n    /**\n     * Remove the 0 in front of the job number\n     *\n     * @param userId employee ID\n     * @return modified job number\n     */\n    public static String cutUserId(String userId) {\n        if (!StringUtils.isNumeric(userId)) {\n            return userId;\n        }\n        int startIndex = 0;\n        for (int i = 0; i < userId.length(); i++) {\n            char c = userId.charAt(i);\n            // Query the first position that is not 0\n            if (ZERO_CHAR == c) {\n                startIndex = i + 1;\n            } else {\n                break;\n            }\n        }\n        // Maybe the entire account is 0\n        if (startIndex == userId.length()) {\n            return \"0\";\n        }\n        return userId.substring(startIndex);\n    }\n\n    /**\n     * Remove the job number after the flower name\n     *\n     * @param name name or nickname\n     * @return the name or nickname after removing the work number\n     */\n    public static String cutName(String name, String workNo) {\n        if (StringUtils.isBlank(workNo) || StringUtils.isBlank(name)) {\n            return name;\n        }\n        // There may be 0 knots here\n        String cutName = RegExUtils.removeFirst(name, workNo);\n        int lastIndex = cutName.length();\n        for (int i = cutName.length() - 1; i >= 0; i--) {\n            char c = cutName.charAt(i);\n            // Query the last position that is not 0\n            if (ZERO_CHAR == c) {\n                lastIndex = i;\n            } else {\n                break;\n            }\n        }\n        return cutName.substring(0, lastIndex);\n    }\n\n    /**\n     * Add 0 in front of the job number\n     *\n     * @param userId employee ID\n     * @return modified job number\n     */\n    public static String padUserId(String userId) {\n        if (!StringUtils.isNumeric(userId)) {\n            return userId;\n        }\n        return StringUtils.leftPad(userId, 6, '0');\n    }\n\n    /**\n     * Build the name of the display\n     *\n     * @param name     name\n     * @param nickName flower name\n     * @return display name name (flower name)\n     */\n    public static String buildShowName(String name, String nickName) {\n        StringBuilder showName = new StringBuilder();\n        if (StringUtils.isNotBlank(name)) {\n            showName.append(name);\n        }\n        if (StringUtils.isNotBlank(nickName)) {\n            showName.append(\"(\");\n            showName.append(nickName);\n            showName.append(\")\");\n        }\n        return showName.toString();\n    }\n\n    /**\n     * Splice multiple strings together\n     *\n     * @param delimiter delimiter cannot be empty\n     * @param elements  string can be empty and empty strings will be ignored\n     * @return\n     */\n    public static String join(CharSequence delimiter, CharSequence... elements) {\n        if (elements == null) {\n            return null;\n        }\n        List<CharSequence> charSequenceList = Arrays.stream(elements).filter(\n                org.apache.commons.lang3.StringUtils::isNotBlank).collect(Collectors.toList());\n        if (charSequenceList.isEmpty()) {\n            return null;\n        }\n        return String.join(delimiter, charSequenceList);\n    }\n\n    /**\n     * Limit the length of a string string. If it exceeds the length, it will be replaced with...\n     *\n     * @param str    string\n     * @param length limit length\n     * @return\n     */\n    public static String limitString(String str, int length) {\n        if (Objects.isNull(str)) {\n            return null;\n        }\n        String limitString = StringUtils.substring(str, 0, length);\n        if (limitString.length() == length) {\n            limitString += \"...\";\n        }\n        return limitString;\n    }\n\n    /**\n     * 对字符串中的特定字符进行转义处理。\n     * <p>\n     * 根据提供的转义映射表，该方法遍历输入字符串中的每个字符，如果字符存在于映射表中，\n     * 则在该字符前插入映射表中对应的转义字符，否则保持字符不变。这常用于创建符合特定格式要求的字符串，\n     * 比如SQL查询中的字符串转义，或为文本添加特殊标记等。\n     *\n     * @param str       目标字符串，需要进行转义处理的原始字符串。\n     * @param escapeMap 映射关系表，键为需要被转义的字符，值为该字符前应添加的转义字符。\n     *                  例如，如果希望转义单引号(')，可以传入Map<Character, Character>中键为单引号，值也为单引号的映射。\n     * @return 转义后的字符串，含有特定字符前添加了转义符的副本。\n     */\n    public static String escapeString(@NotNull String str, Map<Character, Character> escapeMap) {\n        if (str == null) {\n            return null;\n        }\n        if (StringUtils.isBlank(str)) {\n            return str;\n        }\n\n\n        StringBuilder escapedString = new StringBuilder(str.length() * 2);\n\n        for (char c : str.toCharArray()) {\n            if (escapeMap.containsKey(c)) {\n                escapedString.append(escapeMap.get(c)).append(c);\n            } else {\n                escapedString.append(c);\n            }\n        }\n        return escapedString.toString();\n    }\n\n    public static String escapeString(String str) {\n        HashMap<Character, Character> escapeMap = Maps.newHashMapWithExpectedSize(2);\n        // (char)39 -> '\n        escapeMap.put((char) 39, (char) 39);\n        // (char)92 -> \\\n        escapeMap.put((char) 92, (char) 92);\n        return escapeString(str, escapeMap);\n    }\n\n    /**\n     * @param value str=\"'abc\\\"\n     * @return \"'''abc\\\\'\"\n     */\n    public static String escapeAndQuoteString(String value) {\n        return quoteString(escapeString(value));\n    }\n\n    /**\n     * @param value     \"abcd\"\n     * @param quoteChar '%'\n     * @return \"%abcd%\"\n     */\n    public static String quoteString(String value, char quoteChar) {\n        return quoteChar + value + quoteChar;\n    }\n\n    /**\n     * @param value \"abcd\"\n     * @return \"'abcd'\"\n     */\n    public static String quoteString(String value) {\n        // (char)39 -> '\n        return quoteString(value, (char) 39);\n    }\n\n    public static String getBitString(byte[] bytes, final int precision) {\n        if (bytes == null || bytes.length == 0) {\n            return \"\";\n        }\n\n        StringBuilder builder = new StringBuilder(precision);\n        for (byte b : bytes) {\n            builder.append(Integer.toBinaryString(b & 0xFF));\n        }\n\n        // 获取完整的二进制字符串\n        String bitString = builder.toString();\n\n        // 填充前导零以匹配所需的总长度\n        bitString = Strings.padStart(bitString, precision, '0');\n\n        return bitString;\n    }\n\n\n    public static String escapeLineString(String str) {\n        if (StringUtils.isBlank(str)) {\n            return str;\n        }\n        return str;\n        // TODO Need to be implemented in the future with different data types\n//        return str.replace(\"\\r\\n\", \"\\\\r\\\\n\")\n//                .replace(\"\\n\", \"\\\\n\")\n//                .replace(\"\\r\", \"\\\\r\");\n    }\n\n\n    public static String sqlEscape(String str) {\n        if (StringUtils.isBlank(str)) {\n            return str;\n        }\n        str = str.trim();\n        if (str.endsWith(\";\")) {\n            str = str.substring(0, str.length() - 1);\n        }\n        return str;\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/I18nUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport java.util.Locale;\n\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.NoSuchMessageException;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.context.i18n.LocaleContextHolder;\nimport org.springframework.lang.Nullable;\nimport org.springframework.stereotype.Component;\n\n/**\n * i18n utility\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Component\n@Lazy(value = false)\npublic class I18nUtils implements InitializingBean {\n    public static final String DEFAULT_MESSAGE_CODE=\"common.systemError\";\n    @Resource\n    private MessageSource messageSource;\n    private static MessageSource messageSourceStatic;\n\n    public static String getMessage(String messageCode) {\n        return getMessage(messageCode, null);\n    }\n\n    public static String getMessage(String messageCode, @Nullable Object[] args) {\n        try {\n            return messageSourceStatic.getMessage(messageCode, args, LocaleContextHolder.getLocale());\n        } catch (NoSuchMessageException e) {\n            log.error(\"no message.\", e);\n        }\n        return messageSourceStatic.getMessage(DEFAULT_MESSAGE_CODE, args, LocaleContextHolder.getLocale());\n    }\n\n    /**\n     * Is it in English?\n     *\n     * @return\n     */\n    public static Boolean isEn() {\n        return LocaleContextHolder.getLocale().equals(Locale.US);\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        messageSourceStatic = messageSource;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/LogUtils.java",
    "content": "package ai.chat2db.server.tools.common.util;\n\nimport java.util.Objects;\nimport java.util.regex.Pattern;\n\nimport cn.hutool.core.lang.UUID;\nimport cn.hutool.core.net.NetUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.zalando.logbook.HttpHeaders;\nimport org.zalando.logbook.HttpRequest;\n\n/**\n * Log utility\n *\n * @author Jiaju Zhuang\n */\npublic class LogUtils {\n\n    private static final ThreadLocal<String> TRACE_ID_THREAD_LOCAL = new ThreadLocal<>();\n\n    /**\n     * Request headers for client IPs\n     */\n    private static final String[] CLIENT_IP_HEADERS = {\"X-Forwarded-For\", \"X-Real-IP\", \"Proxy-Client-IP\",\n        \"WL-Proxy-Client-IP\", \"HTTP_CLIENT_IP\", \"HTTP_X_FORWARDED_FOR\"};\n\n    /**\n     * Maximum log length\n     */\n    public static final int MAX_LOG_LENGTH = 20000;\n\n    public static final String TRACE_ID = \"TRACE_ID\";\n    public static final String TRACE_ID_HEADER = \"X-Chat2DB-Trace-Id\";\n\n    /**\n     * newline character\n     */\n    private static final Pattern LINE_FEED_PATTERN = Pattern.compile(\"\\r|\\n\");\n\n    /**\n     * mask string\n     *\n     * @param input\n     * @return\n     */\n    public static String maskString(String input) {\n        if (StringUtils.isBlank(input)) {\n            return input;\n        }\n\n        StringBuilder maskedString = new StringBuilder(input);\n        for (int i = 0; i < input.length(); i += 4) {\n            maskedString.setCharAt(i, '*');\n        }\n        return maskedString.toString();\n    }\n\n    /**\n     * Remove newlines\n     *\n     * @param log\n     * @return\n     */\n    public static String removeCrlf(String log) {\n        if (Objects.isNull(log)) {\n            return null;\n        }\n        return LINE_FEED_PATTERN.matcher(log).replaceAll(\"\");\n    }\n\n    /**\n     * cut log\n     *\n     * @param log\n     * @return\n     */\n    public static String cutLog(Object log) {\n        if (Objects.isNull(log)) {\n            return null;\n        }\n        return EasyStringUtils.limitString(removeCrlf(log.toString()), MAX_LOG_LENGTH);\n    }\n\n    /**\n     * Return traceId\n     *\n     * @return\n     */\n    public static String generateTraceId() {\n        String traceId = UUID.fastUUID().toString().replaceAll(\"-\", \"\");\n        TRACE_ID_THREAD_LOCAL.set(traceId);\n        return traceId;\n    }\n\n    /**\n     * Gets the trace ID\n     *\n     * @return\n     */\n    public static String getTraceId() {\n        return TRACE_ID_THREAD_LOCAL.get();\n    }\n\n    /**\n     * Remove the trace ID\n     *\n     * @return\n     */\n    public static void removeTraceId() {\n        TRACE_ID_THREAD_LOCAL.remove();\n    }\n\n    /**\n     * Obtain the client IP\n     *\n     * @param request\n     * @return\n     */\n    public static String getClientIp(HttpRequest request) {\n        HttpHeaders httpHeaders = request.getHeaders();\n        String ip;\n        for (String header : CLIENT_IP_HEADERS) {\n            ip = httpHeaders.getFirst(header);\n            if (!NetUtil.isUnknown(ip)) {\n                return NetUtil.getMultistageReverseProxyIp(ip);\n            }\n        }\n        ip = request.getRemote();\n        return NetUtil.getMultistageReverseProxyIp(ip);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-tools/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-parent</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-tools</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>chat2db-server-tools-base</module>\n        <module>chat2db-server-tools-common</module>\n    </modules>\n\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-web</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-admin-api</artifactId>\n    <packaging>jar</packaging>\n    <name>chat2db-server-admin-api</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-tools-common</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-common-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n        </dependency>\n\n        <!-- http -->\n        <dependency>\n            <groupId>com.dtflys.forest</groupId>\n            <artifactId>forest-spring</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>easyexcel</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/common/CommonAdminController.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.common;\n\nimport java.util.List;\n\nimport ai.chat2db.server.admin.api.controller.common.converter.CommonAdminConverter;\nimport ai.chat2db.server.admin.api.controller.common.vo.TeamUserListVO;\nimport ai.chat2db.server.admin.api.controller.datasource.vo.SimpleDataSourceVO;\nimport ai.chat2db.server.admin.api.controller.team.vo.SimpleTeamVO;\nimport ai.chat2db.server.admin.api.controller.user.vo.SimpleUserVO;\nimport ai.chat2db.server.common.api.controller.request.CommonQueryRequest;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam;\nimport ai.chat2db.server.domain.api.param.user.UserPageQueryParam;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.TeamService;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport com.google.common.collect.Lists;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Some general data queries\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/common\")\n@RestController\npublic class CommonAdminController {\n    private static final DataSourceSelector DATA_SOURCE_SELECTOR = DataSourceSelector.builder()\n        .environment(Boolean.TRUE)\n        .build();\n    @Resource\n    private UserService userService;\n    @Resource\n    private TeamService teamService;\n    @Resource\n    private DataSourceService dataSourceService;\n    @Resource\n    private CommonAdminConverter commonAdminConverter;\n\n    /**\n     * Fuzzy query of users or teams\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/team_user/list\")\n    public ListResult<TeamUserListVO> teamUserList(@Valid CommonQueryRequest request) {\n        UserPageQueryParam userPageQueryParam = commonAdminConverter.request2paramUser(request);\n        List<TeamUserListVO> result = Lists.newArrayList();\n        result.addAll(userService.pageQuery(userPageQueryParam, null)\n            .mapToList(commonAdminConverter::dto2voTeamUser)\n            .getData());\n\n        TeamPageQueryParam teamPageQueryParam = commonAdminConverter.request2paramTeam(request);\n        result.addAll(teamService.pageQuery(teamPageQueryParam, null)\n            .mapToList(commonAdminConverter::dto2voTeamUser)\n            .getData());\n        return ListResult.of(result);\n    }\n\n    /**\n     * Fuzzy query of users\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/user/list\")\n    public ListResult<SimpleUserVO> userList(@Valid CommonQueryRequest request) {\n        return userService.pageQuery(commonAdminConverter.request2paramUser(request), null)\n            .mapToList(commonAdminConverter::dto2voUser);\n    }\n\n    /**\n     * Fuzzy query of  teams\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/team/list\")\n    public ListResult<SimpleTeamVO> teamList(@Valid CommonQueryRequest request) {\n        return teamService.pageQuery(commonAdminConverter.request2paramTeam(request), null)\n            .mapToList(commonAdminConverter::dto2voTeam);\n    }\n\n    /**\n     * Fuzzy query of data source\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/data_source/list\")\n    public ListResult<SimpleDataSourceVO> dataSourceList(@Valid CommonQueryRequest request) {\n        return dataSourceService.queryPageWithPermission(commonAdminConverter.request2paramDataSource(request),\n                DATA_SOURCE_SELECTOR)\n            .mapToList(commonAdminConverter::dto2voDataSource);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/common/converter/CommonAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.common.converter;\n\nimport ai.chat2db.server.admin.api.controller.common.vo.TeamUserListVO;\nimport ai.chat2db.server.admin.api.controller.datasource.vo.SimpleDataSourceVO;\nimport ai.chat2db.server.admin.api.controller.team.vo.SimpleTeamVO;\nimport ai.chat2db.server.admin.api.controller.user.vo.SimpleUserVO;\nimport ai.chat2db.server.common.api.controller.request.CommonQueryRequest;\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.model.Team;\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam;\nimport ai.chat2db.server.domain.api.param.user.UserPageQueryParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\", imports = {AccessObjectTypeEnum.class, DataSourceKindEnum.class})\npublic abstract class CommonAdminConverter {\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"pageSize\", expression = \"java(10)\"),\n    })\n    public abstract TeamPageQueryParam request2paramTeam(CommonQueryRequest request);\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"pageSize\", expression = \"java(10)\"),\n    })\n    public abstract UserPageQueryParam request2paramUser(CommonQueryRequest request);\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"pageSize\", expression = \"java(10)\"),\n        @Mapping(target = \"kind\", expression = \"java(DataSourceKindEnum.SHARED.getCode())\"),\n    })\n    public abstract DataSourcePageQueryParam request2paramDataSource(CommonQueryRequest request);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract SimpleTeamVO dto2voTeam(Team dto);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract SimpleDataSourceVO dto2voDataSource(DataSource dto);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract SimpleUserVO dto2voUser(User dto);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"type\", expression = \"java(AccessObjectTypeEnum.TEAM.getCode())\"),\n        @Mapping(target = \"code\", source = \"code\"),\n        @Mapping(target = \"name\", source = \"name\"),\n    })\n    public abstract TeamUserListVO dto2voTeamUser(Team dto);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"type\", expression = \"java(AccessObjectTypeEnum.USER.getCode())\"),\n        @Mapping(target = \"code\", source = \"userName\"),\n        @Mapping(target = \"name\", source = \"nickName\"),\n    })\n    public abstract TeamUserListVO dto2voTeamUser(User dto);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/common/request/TeamUserPageQueryRequest.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.common.request;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport lombok.Data;\n\n/**\n * Common pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamUserPageQueryRequest extends PageQueryRequest {\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    private String type;\n\n    /**\n     * searchKey\n     */\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/common/vo/TeamUserListVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.common.vo;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * DataSource Access Object\n * It could be a user or a team\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TeamUserListVO implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    private Long id;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    private String type;\n\n    /**\n     * The name of the code that belongs to the authorization type, such as user account, team code\n     */\n    private String code;\n\n    /**\n     * Code that belongs to the authorization type, such as user name, team name\n     */\n    private String name;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/DataSourceAccessAdminController.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.datasource;\n\nimport ai.chat2db.server.admin.api.controller.datasource.converter.DataSourceAccessAdminConverter;\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceAccessBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceAccessPageQueryRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.vo.DataSourceAccessPageQueryVO;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessSelector;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Data Source Access Management\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/data_source/access\")\n@RestController\npublic class DataSourceAccessAdminController {\n\n    private static final DataSourceAccessSelector DATA_SOURCE_ACCESS_SELECTOR = DataSourceAccessSelector.builder()\n        .accessObject(Boolean.TRUE)\n        .build();\n\n    @Resource\n    private DataSourceAccessService dataSourceAccessService;\n    @Resource\n    private DataSourceAccessAdminConverter dataSourceAccessAdminConverter;\n\n    /**\n     * Pagination query\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/page\")\n    public WebPageResult<DataSourceAccessPageQueryVO> page(@Valid DataSourceAccessPageQueryRequest request) {\n        return dataSourceAccessService.comprehensivePageQuery(dataSourceAccessAdminConverter.request2param(request),\n                DATA_SOURCE_ACCESS_SELECTOR)\n            .mapToWeb(dataSourceAccessAdminConverter::dto2vo);\n    }\n\n    /**\n     * batch\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/batch_create\")\n    public ActionResult batchCreate(@Valid @RequestBody DataSourceAccessBatchCreateRequest request) {\n        request.getAccessObjectList()\n            .forEach(accessObject -> dataSourceAccessService.create(DataSourceAccessCreatParam.builder()\n                .dataSourceId(request.getDataSourceId())\n                .accessObjectId(accessObject.getId())\n                .accessObjectType(accessObject.getType())\n                .build()));\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public DataResult<Boolean> delete(@PathVariable Long id) {\n        return dataSourceAccessService.delete(id).toBooleaSuccessnDataResult();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/DataSourceAdminController.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.datasource;\n\nimport ai.chat2db.server.admin.api.controller.datasource.converter.DataSourceAdminConverter;\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceCloneRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceCreateRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceUpdateRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.vo.DataSourcePageQueryVO;\nimport ai.chat2db.server.common.api.controller.request.CommonPageQueryRequest;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePageQueryParam.OrderCondition;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceUpdateParam;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Data Source Management\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/data_source\")\n@RestController\npublic class DataSourceAdminController {\n\n    private static final DataSourceSelector DATA_SOURCE_SELECTOR = DataSourceSelector.builder()\n        .environment(Boolean.TRUE)\n        .build();\n    @Resource\n    private DataSourceService dataSourceService;\n    @Resource\n    private DataSourceAdminConverter dataSourceAdminConverter;\n\n    /**\n     * Pagination query\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/page\")\n    public WebPageResult<DataSourcePageQueryVO> page(@Valid CommonPageQueryRequest request) {\n        DataSourcePageQueryParam param = dataSourceAdminConverter.request2param(request);\n        param.orderBy(OrderCondition.ID_DESC);\n        return dataSourceService.queryPageWithPermission(param, DATA_SOURCE_SELECTOR)\n            .mapToWeb(dataSourceAdminConverter::dto2vo);\n    }\n\n    /**\n     * create\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/create\")\n    public DataResult<Long> create(@Valid @RequestBody DataSourceCreateRequest request) {\n        DataSourceCreateParam param = dataSourceAdminConverter.createReq2param(request);\n        return dataSourceService.createWithPermission(param);\n    }\n\n    /**\n     * update\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/update\")\n    public DataResult<Long> update(@Valid @RequestBody DataSourceUpdateRequest request) {\n        DataSourceUpdateParam param = dataSourceAdminConverter.updateReq2param(request);\n        return dataSourceService.updateWithPermission(param);\n    }\n\n    /**\n     * clone\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/clone\")\n    public DataResult<Long> clone(@RequestBody DataSourceCloneRequest request) {\n        return dataSourceService.copyByIdWithPermission(request.getId());\n    }\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     * @version 2.1.0\n     */\n    @DeleteMapping(\"/{id}\")\n    public DataResult<Boolean> delete(@PathVariable Long id) {\n        return dataSourceService.deleteWithPermission(id).toBooleaSuccessnDataResult();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/converter/DataSourceAccessAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.datasource.converter;\n\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceAccessBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceAccessPageQueryRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.vo.DataSourceAccessPageQueryVO;\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.DataSourceAccess;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessBatchCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessComprehensivePageQueryParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\", imports = {DataSourceKindEnum.class})\npublic abstract class DataSourceAccessAdminConverter {\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"searchKey\", target = \"userOrTeamSearchKey\"),\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n    })\n    public abstract DataSourceAccessComprehensivePageQueryParam request2param(DataSourceAccessPageQueryRequest request);\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    public abstract DataSourceAccessBatchCreatParam request2param(DataSourceAccessBatchCreateRequest request);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract DataSourceAccessPageQueryVO dto2vo(DataSourceAccess dto);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/converter/DataSourceAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.datasource.converter;\n\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceCreateRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceUpdateRequest;\nimport ai.chat2db.server.admin.api.controller.datasource.vo.DataSourcePageQueryVO;\nimport ai.chat2db.server.common.api.controller.request.CommonPageQueryRequest;\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceUpdateParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\",imports = {DataSourceKindEnum.class})\npublic abstract class DataSourceAdminConverter {\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n        @Mapping(target = \"kind\", expression = \"java(DataSourceKindEnum.SHARED.getCode())\"),\n    })\n    public abstract DataSourcePageQueryParam request2param(CommonPageQueryRequest request);\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n    })\n    public abstract DataSourcePageQueryParam request2paramAccess(CommonPageQueryRequest request);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract DataSourcePageQueryVO dto2vo(DataSource dto);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"user\", target = \"userName\"),\n        @Mapping(target = \"kind\", expression = \"java(DataSourceKindEnum.SHARED.getCode())\"),\n    })\n    public abstract DataSourceCreateParam createReq2param(DataSourceCreateRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"user\", target = \"userName\")\n    })\n    public abstract DataSourceUpdateParam updateReq2param(DataSourceUpdateRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/request/DataSourceAccessBatchCreateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.datasource.request;\n\nimport java.util.List;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceAccessBatchCreateRequest {\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * DataSource Access Object\n     */\n    @NotNull\n    private List<DataSourceAccessObjectRequest> accessObjectList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/request/DataSourceAccessObjectRequest.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.datasource.request;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * DataSource Access Object\n * It could be a user or a team\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceAccessObjectRequest implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    private Long id;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    private String type;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/request/DataSourceAccessPageQueryRequest.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.datasource.request;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Common pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class DataSourceAccessPageQueryRequest extends PageQueryRequest {\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * searchKey\n     */\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/request/DataSourceCloneRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.datasource.request;\n\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCloneRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceCloneRequest {\n\n    /**\n     * primary key id\n     */\n    private Long id;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/request/DataSourceCreateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.datasource.request;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCreateRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceCreateRequest {\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    @NotNull\n    private String url;\n\n    /**\n     * Connect username\n     */\n    private String user;\n\n    /**\n     * password\n     */\n    @NotNull\n    private String password;\n\n    /**\n     * Certification type\n     */\n    private String authenticationType;\n\n    /**\n     * Connection Type\n     */\n    @NotNull\n    private String type;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n\n    /**\n     * environment id\n     */\n    @NotNull\n    private Long environmentId;\n\n    /**\n     * service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/request/DataSourceUpdateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.datasource.request;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCreateRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceUpdateRequest {\n\n    /**\n     * primary key id\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * Connect users\n     */\n    private String user;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Connection Type\n     */\n    private String type;\n\n    /**\n     * environment type\n     *\n     * @see EnvTypeEnum\n     */\n    private String envType;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n\n    /**\n     * environment id\n     */\n    @NotNull\n    private Long environmentId;\n\n    /**\n     * service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/vo/DataSourceAccessObjectVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.datasource.vo;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * DataSource Access Object\n * It could be a user or a team\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceAccessObjectVO implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    private Long id;\n\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    private String type;\n\n    /**\n     * The name of the code that belongs to the authorization type, such as user account, team code\n     */\n    private String code;\n\n    /**\n     * Code that belongs to the authorization type, such as user name, team name\n     */\n    private String name;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/vo/DataSourceAccessPageQueryVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.datasource.vo;\n\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class DataSourceAccessPageQueryVO {\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n    /**\n     * Authorization type\n     *\n     * @see AccessObjectTypeEnum\n     */\n    @NotNull\n    private String accessObjectType;\n\n    /**\n     * Authorization ID, distinguish whether it is a user or a team according to the type\n     */\n    @NotNull\n    private Long accessObjectId;\n\n    /**\n     * Authorization object\n     */\n    @NotNull\n    private DataSourceAccessObjectVO accessObject;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/vo/DataSourcePageQueryVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.datasource.vo;\n\nimport ai.chat2db.server.common.api.controller.vo.SimpleEnvironmentVO;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class DataSourcePageQueryVO {\n\n    /**\n     * primary key id\n     */\n    private Long id;\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * environment id\n     */\n    private Long environmentId;\n\n    /**\n     * environment\n     */\n    private SimpleEnvironmentVO environment;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/datasource/vo/SimpleDataSourceVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.datasource.vo;\n\nimport ai.chat2db.server.common.api.controller.vo.SimpleEnvironmentVO;\nimport lombok.Data;\n\n/**\n * Data Source\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class SimpleDataSourceVO {\n\n    /**\n     * primary key id\n     */\n    private Long id;\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * environment id\n     */\n    private Long environmentId;\n\n    /**\n     * environment\n     */\n    private SimpleEnvironmentVO environment;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/TeamAdminController.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.team;\n\nimport ai.chat2db.server.admin.api.controller.team.converter.TeamAdminConverter;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamCreateRequest;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamUpdateRequest;\nimport ai.chat2db.server.admin.api.controller.team.vo.TeamPageQueryVO;\nimport ai.chat2db.server.common.api.controller.request.CommonPageQueryRequest;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam.OrderCondition;\nimport ai.chat2db.server.domain.api.param.team.TeamSelector;\nimport ai.chat2db.server.domain.api.service.TeamService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Team Management\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/team\")\n@RestController\npublic class TeamAdminController {\n    private static final TeamSelector TEAM_SELECTOR = TeamSelector.builder()\n        .modifiedUser(Boolean.TRUE)\n        .build();\n\n    @Resource\n    private TeamService teamService;\n    @Resource\n    private TeamAdminConverter teamAdminConverter;\n\n    /**\n     * Pagination query\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/page\")\n    public WebPageResult<TeamPageQueryVO> page(@Valid CommonPageQueryRequest request) {\n        TeamPageQueryParam param = teamAdminConverter.request2param(request);\n        param.orderBy(OrderCondition.ID_DESC);\n        return teamService.pageQuery(param, TEAM_SELECTOR)\n            .mapToWeb(teamAdminConverter::dto2vo);\n    }\n\n    /**\n     * create\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/create\")\n    public DataResult<Long> create(@RequestBody TeamCreateRequest request) {\n        return teamService.create(teamAdminConverter.request2param(request));\n    }\n\n    /**\n     * update\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/update\")\n    public DataResult<Long> update(@RequestBody TeamUpdateRequest request) {\n        return teamService.update(teamAdminConverter.request2param(request));\n    }\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public DataResult<Boolean> delete(@PathVariable Long id) {\n        return teamService.delete(id).toBooleaSuccessnDataResult();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/TeamDataSourceAdminController.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.team;\n\nimport ai.chat2db.server.admin.api.controller.team.converter.TeamDataSourcesAdminConverter;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamDataSourceBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamPageCommonQueryRequest;\nimport ai.chat2db.server.admin.api.controller.team.vo.TeamDataSourcePageQueryVO;\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessPageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessSelector;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Team Data Source Management\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/team/data_source\")\n@RestController\npublic class TeamDataSourceAdminController {\n    private static final DataSourceAccessSelector DATA_SOURCE_ACCESS_SELECTOR = DataSourceAccessSelector.builder()\n        .dataSource(Boolean.TRUE)\n        .dataSourceSelector(DataSourceSelector.builder()\n            .environment(Boolean.TRUE)\n            .build())\n        .build();\n\n    @Resource\n    private DataSourceAccessService dataSourceAccessService;\n    @Resource\n    private TeamDataSourcesAdminConverter teamDataSourcesAdminConverter;\n\n    /**\n     * Pagination query\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/page\")\n    public WebPageResult<TeamDataSourcePageQueryVO> page(@Valid TeamPageCommonQueryRequest request) {\n        return dataSourceAccessService.comprehensivePageQuery(teamDataSourcesAdminConverter.request2param(request),\n                DATA_SOURCE_ACCESS_SELECTOR)\n            .mapToWeb(teamDataSourcesAdminConverter::dto2vo);\n    }\n\n    /**\n     * create\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/batch_create\")\n    public ActionResult create(@Valid @RequestBody TeamDataSourceBatchCreateRequest request) {\n        request.getDataSourceIdList()\n            .forEach(dataSourceId -> {\n                DataSourceAccessPageQueryParam dataSourceAccessPageQueryParam = new DataSourceAccessPageQueryParam();\n                dataSourceAccessPageQueryParam.setDataSourceId(dataSourceId);\n                dataSourceAccessPageQueryParam.setAccessObjectType(AccessObjectTypeEnum.TEAM.getCode());\n                dataSourceAccessPageQueryParam.setAccessObjectId(request.getTeamId());\n                dataSourceAccessPageQueryParam.queryOne();\n                if (dataSourceAccessService.pageQuery(dataSourceAccessPageQueryParam, null).hasData()) {\n                    return;\n                }\n                dataSourceAccessService.create(DataSourceAccessCreatParam.builder()\n                    .dataSourceId(dataSourceId)\n                    .accessObjectId(request.getTeamId())\n                    .accessObjectType(AccessObjectTypeEnum.TEAM.getCode())\n                    .build());\n            });\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public DataResult<Boolean> delete(@PathVariable Long id) {\n        return dataSourceAccessService.delete(id).toBooleaSuccessnDataResult();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/TeamUserAdminController.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.team;\n\nimport ai.chat2db.server.admin.api.controller.team.converter.TeamUserAdminConverter;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamPageCommonQueryRequest;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamUserBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.team.vo.TeamUserPageQueryVO;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserCreatParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserSelector;\nimport ai.chat2db.server.domain.api.service.TeamUserService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Team User Management\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/team/user\")\n@RestController\npublic class TeamUserAdminController {\n    private static final TeamUserSelector TEAM_USER_SELECTOR = TeamUserSelector.builder()\n        .user(Boolean.TRUE)\n        .build();\n\n    @Resource\n    private TeamUserService teamUserService;\n    @Resource\n    private TeamUserAdminConverter teamUserAdminConverter;\n\n    /**\n     * Pagination query\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/page\")\n    public WebPageResult<TeamUserPageQueryVO> page(@Valid TeamPageCommonQueryRequest request) {\n        return teamUserService.comprehensivePageQuery(teamUserAdminConverter.request2param(request), TEAM_USER_SELECTOR)\n            .mapToWeb(teamUserAdminConverter::dto2vo);\n    }\n\n    /**\n     * create\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/batch_create\")\n    public ActionResult create(@Valid @RequestBody TeamUserBatchCreateRequest request) {\n        request.getUserIdList()\n            .forEach(userId -> {\n                TeamUserPageQueryParam teamUserPageQueryParam = new TeamUserPageQueryParam();\n                teamUserPageQueryParam.setTeamId(request.getTeamId());\n                teamUserPageQueryParam.setUserId(userId);\n                teamUserPageQueryParam.queryOne();\n                if (teamUserService.pageQuery(teamUserPageQueryParam, null).hasData()) {\n                    return;\n                }\n                teamUserService.create(TeamUserCreatParam.builder()\n                    .teamId(request.getTeamId())\n                    .userId(userId)\n                    .build());\n            });\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public DataResult<Boolean> delete(@PathVariable Long id) {\n        return teamUserService.delete(id).toBooleaSuccessnDataResult();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/converter/TeamAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.team.converter;\n\nimport ai.chat2db.server.admin.api.controller.team.request.TeamCreateRequest;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamUpdateRequest;\nimport ai.chat2db.server.admin.api.controller.team.vo.TeamPageQueryVO;\nimport ai.chat2db.server.common.api.controller.request.CommonPageQueryRequest;\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.Team;\nimport ai.chat2db.server.domain.api.param.team.TeamCreateParam;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.TeamUpdateParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\",imports = {DataSourceKindEnum.class})\npublic abstract class TeamAdminConverter {\n\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n    })\n    public abstract TeamPageQueryParam request2param(CommonPageQueryRequest request);\n\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract TeamPageQueryVO dto2vo(Team dto);\n\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract TeamCreateParam request2param(TeamCreateRequest request);\n\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract TeamUpdateParam request2param(TeamUpdateRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/converter/TeamDataSourcesAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.team.converter;\n\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceAccessBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamPageCommonQueryRequest;\nimport ai.chat2db.server.admin.api.controller.team.vo.TeamDataSourcePageQueryVO;\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.DataSourceAccess;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessBatchCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessComprehensivePageQueryParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\", imports = {DataSourceKindEnum.class, AccessObjectTypeEnum.class})\npublic abstract class TeamDataSourcesAdminConverter {\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"accessObjectId\", source = \"teamId\"),\n        @Mapping(target = \"accessObjectType\", expression = \"java(AccessObjectTypeEnum.TEAM.name())\"),\n        @Mapping(source = \"searchKey\", target = \"dataSourceSearchKey\"),\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n    })\n    public abstract DataSourceAccessComprehensivePageQueryParam request2param(TeamPageCommonQueryRequest request);\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    public abstract DataSourceAccessBatchCreatParam request2param(DataSourceAccessBatchCreateRequest request);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"teamId\", source = \"accessObjectId\"),\n    })\n    public abstract TeamDataSourcePageQueryVO dto2vo(DataSourceAccess dto);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/converter/TeamUserAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.team.converter;\n\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceAccessBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.team.request.TeamPageCommonQueryRequest;\nimport ai.chat2db.server.admin.api.controller.team.vo.TeamUserPageQueryVO;\nimport ai.chat2db.server.domain.api.model.TeamUser;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessBatchCreatParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserComprehensivePageQueryParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class TeamUserAdminConverter {\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"searchKey\", target = \"userSearchKey\"),\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n    })\n    public abstract TeamUserComprehensivePageQueryParam request2param(TeamPageCommonQueryRequest request);\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    public abstract DataSourceAccessBatchCreatParam request2param(DataSourceAccessBatchCreateRequest request);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract TeamUserPageQueryVO dto2vo(TeamUser dto);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/request/TeamCreateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.team.request;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamCreateRequest {\n\n    /**\n     * team coding\n     */\n    @NotNull\n    private String code;\n\n    /**\n     * Team Name\n     */\n    @NotNull\n    private String name;\n\n    /**\n     * Team status\n     *\n     * @see ai.chat2db.server.domain.api.enums.ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n\n    /**\n     * Team description\n     */\n    private String description;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/request/TeamDataSourceBatchCreateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.team.request;\n\nimport java.util.List;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TeamDataSourceBatchCreateRequest {\n\n    /**\n     * team id\n     */\n    @NotNull\n    private Long teamId;\n\n\n    /**\n     * Data Source id list\n     */\n    @NotNull\n    private List<Long> dataSourceIdList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/request/TeamPageCommonQueryRequest.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.team.request;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamPageCommonQueryRequest extends PageQueryRequest {\n    /**\n     * team id\n     */\n    @NotNull\n    private Long teamId;\n\n    /**\n     * searchKey\n     */\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/request/TeamUpdateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.team.request;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * update\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamUpdateRequest {\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * Team Name\n     */\n    @NotNull\n    private String name;\n\n    /**\n     * Team status\n     *\n     * @see ai.chat2db.server.domain.api.enums.ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n\n    /**\n     * role coding\n     *\n     * @see ai.chat2db.server.domain.api.enums.RoleCodeEnum\n     */\n    @NotNull\n    private String roleCode;\n\n    /**\n     * Team description\n     */\n    private String description;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/request/TeamUserBatchCreateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.team.request;\n\nimport java.util.List;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TeamUserBatchCreateRequest {\n\n    /**\n     * team id\n     */\n    @NotNull\n    private Long teamId;\n\n    /**\n     * user id list\n     */\n    @NotNull\n    private List<Long> userIdList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/vo/SimpleTeamVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.team.vo;\n\nimport lombok.Data;\n\n/**\n * team\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class SimpleTeamVO {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * team coding\n     */\n    private String code;\n\n    /**\n     * Team Name\n     */\n    private String name;\n\n\n    /**\n     * Team status\n     *\n     * @see ai.chat2db.server.domain.api.enums.ValidStatusEnum\n     */\n    private String status;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/vo/TeamDataSourcePageQueryVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.team.vo;\n\nimport ai.chat2db.server.admin.api.controller.datasource.vo.SimpleDataSourceVO;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamDataSourcePageQueryVO {\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * team id\n     */\n    private Long teamId;\n\n    /**\n     * Data Source\n     */\n    private SimpleDataSourceVO dataSource;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/vo/TeamPageQueryVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.team.vo;\n\nimport java.util.Date;\n\nimport ai.chat2db.server.common.api.controller.vo.SimpleUserVO;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamPageQueryVO {\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * team coding\n     */\n    private String code;\n\n    /**\n     * Team Name\n     */\n    private String name;\n\n    /**\n     * Team status\n     *\n     * @see ai.chat2db.server.domain.api.enums.ValidStatusEnum\n     */\n    private String status;\n\n    /**\n     * Team description\n     */\n    private String description;\n\n    /**\n     * Change the time\n     */\n    private Date gmtModified;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * Modifier user\n     */\n    private SimpleUserVO modifiedUser;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/team/vo/TeamUserPageQueryVO.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.team.vo;\n\nimport ai.chat2db.server.admin.api.controller.user.vo.SimpleUserVO;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class TeamUserPageQueryVO {\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * team id\n     */\n    private Long teamId;\n\n    /**\n     * user\n     */\n    private SimpleUserVO user;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/UserAdminController.java",
    "content": "package ai.chat2db.server.admin.api.controller.user;\n\nimport ai.chat2db.server.admin.api.controller.user.converter.UserAdminConverter;\nimport ai.chat2db.server.admin.api.controller.user.request.UserCreateRequest;\nimport ai.chat2db.server.admin.api.controller.user.request.UserUpdateRequest;\nimport ai.chat2db.server.admin.api.controller.user.vo.UserPageQueryVO;\nimport ai.chat2db.server.common.api.controller.request.CommonPageQueryRequest;\nimport ai.chat2db.server.domain.api.param.team.TeamPageQueryParam.OrderCondition;\nimport ai.chat2db.server.domain.api.param.user.UserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.user.UserSelector;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * User Management\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/user\")\n@RestController\npublic class UserAdminController {\n\n    private static final UserSelector USER_SELECTOR = UserSelector.builder()\n        .modifiedUser(Boolean.TRUE)\n        .build();\n\n    @Resource\n    private UserService userService;\n    @Resource\n    private UserAdminConverter userAdminConverter;\n\n    /**\n     * Pagination query\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/page\")\n    public WebPageResult<UserPageQueryVO> page(@Valid CommonPageQueryRequest request) {\n        UserPageQueryParam param = userAdminConverter.request2param(request);\n        param.orderBy(OrderCondition.ID_DESC);\n        return userService.pageQuery(param, USER_SELECTOR)\n            .mapToWeb(userAdminConverter::dto2vo);\n    }\n\n    /**\n     * create\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/create\")\n    public DataResult<Long> create(@Valid @RequestBody UserCreateRequest request) {\n        return userService.create(userAdminConverter.request2param(request));\n    }\n\n    /**\n     * update\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/update\")\n    public DataResult<Long> update(@RequestBody UserUpdateRequest request) {\n        return userService.update(userAdminConverter.request2param(request));\n    }\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public DataResult<Boolean> delete(@PathVariable Long id) {\n        return userService.delete(id).toBooleaSuccessnDataResult();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/UserDataSourceAdminController.java",
    "content": "package ai.chat2db.server.admin.api.controller.user;\n\nimport ai.chat2db.server.admin.api.controller.user.converter.UserDataSourcesAdminConverter;\nimport ai.chat2db.server.admin.api.controller.user.request.UserDataSourceBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.user.request.UserPageCommonQueryRequest;\nimport ai.chat2db.server.admin.api.controller.user.vo.UserDataSourcePageQueryVO;\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessPageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessSelector;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * User Data Source Management\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/user/data_source\")\n@RestController\npublic class UserDataSourceAdminController {\n    private static final DataSourceAccessSelector DATA_SOURCE_ACCESS_SELECTOR = DataSourceAccessSelector.builder()\n        .dataSource(Boolean.TRUE)\n        .dataSourceSelector(DataSourceSelector.builder()\n            .environment(Boolean.TRUE)\n            .build())\n        .build();\n\n    @Resource\n    private DataSourceAccessService dataSourceAccessService;\n    @Resource\n    private UserDataSourcesAdminConverter userDataSourcesAdminConverter;\n\n    /**\n     * Pagination query\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/page\")\n    public WebPageResult<UserDataSourcePageQueryVO> page(@Valid UserPageCommonQueryRequest request) {\n        return dataSourceAccessService.comprehensivePageQuery(userDataSourcesAdminConverter.request2param(request),\n                DATA_SOURCE_ACCESS_SELECTOR)\n            .mapToWeb(userDataSourcesAdminConverter::dto2vo);\n    }\n\n    /**\n     * create\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/batch_create\")\n    public ActionResult create(@RequestBody UserDataSourceBatchCreateRequest request) {\n        request.getDataSourceIdList()\n            .forEach(dataSourceId -> {\n                DataSourceAccessPageQueryParam dataSourceAccessPageQueryParam = new DataSourceAccessPageQueryParam();\n                dataSourceAccessPageQueryParam.setDataSourceId(dataSourceId);\n                dataSourceAccessPageQueryParam.setAccessObjectType(AccessObjectTypeEnum.USER.getCode());\n                dataSourceAccessPageQueryParam.setAccessObjectId(request.getUserId());\n                dataSourceAccessPageQueryParam.queryOne();\n                if (dataSourceAccessService.pageQuery(dataSourceAccessPageQueryParam, null).hasData()) {\n                    return;\n                }\n                dataSourceAccessService.create(DataSourceAccessCreatParam.builder()\n                    .dataSourceId(dataSourceId)\n                    .accessObjectId(request.getUserId())\n                    .accessObjectType(AccessObjectTypeEnum.USER.getCode())\n                    .build());\n            });\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public DataResult<Boolean> delete(@PathVariable Long id) {\n        return dataSourceAccessService.delete(id).toBooleaSuccessnDataResult();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/UserTeamAdminController.java",
    "content": "package ai.chat2db.server.admin.api.controller.user;\n\nimport ai.chat2db.server.admin.api.controller.user.converter.UserTeamAdminConverter;\nimport ai.chat2db.server.admin.api.controller.user.request.UserPageCommonQueryRequest;\nimport ai.chat2db.server.admin.api.controller.user.request.UserTeamBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.user.vo.UserTeamPageQueryVO;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserCreatParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserSelector;\nimport ai.chat2db.server.domain.api.service.TeamUserService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * User Team Management\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/admin/user/team\")\n@RestController\npublic class UserTeamAdminController {\n    private static final TeamUserSelector TEAM_USER_SELECTOR = TeamUserSelector.builder()\n        .team(Boolean.TRUE)\n        .build();\n    @Resource\n    private TeamUserService teamUserService;\n    @Resource\n    private UserTeamAdminConverter userTeamAdminConverter;\n\n    /**\n     * Pagination query\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/page\")\n    public WebPageResult<UserTeamPageQueryVO> page(@Valid UserPageCommonQueryRequest request) {\n        return teamUserService.comprehensivePageQuery(userTeamAdminConverter.request2param(request), TEAM_USER_SELECTOR)\n            .mapToWeb(userTeamAdminConverter::dto2vo);\n    }\n\n    /**\n     * create\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @PostMapping(\"/batch_create\")\n    public ActionResult batchCreate(@Valid @RequestBody UserTeamBatchCreateRequest request) {\n        request.getTeamIdList()\n            .forEach(teamId -> {\n                TeamUserPageQueryParam teamUserPageQueryParam = new TeamUserPageQueryParam();\n                teamUserPageQueryParam.setTeamId(teamId);\n                teamUserPageQueryParam.setUserId(request.getUserId());\n                teamUserPageQueryParam.queryOne();\n                if (teamUserService.pageQuery(teamUserPageQueryParam, null).hasData()) {\n                    return;\n                }\n                teamUserService.create(TeamUserCreatParam.builder()\n                    .teamId(teamId)\n                    .userId(request.getUserId())\n                    .build());\n            });\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * delete\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public DataResult<Boolean> delete(@PathVariable Long id) {\n        return teamUserService.delete(id).toBooleaSuccessnDataResult();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/converter/UserAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.converter;\n\nimport ai.chat2db.server.admin.api.controller.user.request.UserCreateRequest;\nimport ai.chat2db.server.admin.api.controller.user.request.UserUpdateRequest;\nimport ai.chat2db.server.admin.api.controller.user.vo.UserPageQueryVO;\nimport ai.chat2db.server.common.api.controller.request.CommonPageQueryRequest;\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.param.user.UserCreateParam;\nimport ai.chat2db.server.domain.api.param.user.UserPageQueryParam;\nimport ai.chat2db.server.domain.api.param.user.UserUpdateParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class UserAdminConverter {\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n    })\n    public abstract UserPageQueryParam request2param(CommonPageQueryRequest request);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract UserPageQueryVO dto2vo(User dto);\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract UserCreateParam request2param(UserCreateRequest request);\n\n    /**\n     * conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract UserUpdateParam request2param(UserUpdateRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/converter/UserDataSourcesAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.converter;\n\nimport ai.chat2db.server.admin.api.controller.datasource.request.DataSourceAccessBatchCreateRequest;\nimport ai.chat2db.server.admin.api.controller.user.request.UserPageCommonQueryRequest;\nimport ai.chat2db.server.admin.api.controller.user.vo.UserDataSourcePageQueryVO;\nimport ai.chat2db.server.domain.api.enums.AccessObjectTypeEnum;\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.DataSourceAccess;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessBatchCreatParam;\nimport ai.chat2db.server.domain.api.param.datasource.access.DataSourceAccessComprehensivePageQueryParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\", imports = {DataSourceKindEnum.class, AccessObjectTypeEnum.class})\npublic abstract class UserDataSourcesAdminConverter {\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"userId\", target = \"accessObjectId\"),\n        @Mapping(target = \"accessObjectType\", expression = \"java(AccessObjectTypeEnum.USER.name())\"),\n        @Mapping(source = \"searchKey\", target = \"userOrTeamSearchKey\"),\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n    })\n    public abstract DataSourceAccessComprehensivePageQueryParam request2param(UserPageCommonQueryRequest request);\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    public abstract DataSourceAccessBatchCreatParam request2param(DataSourceAccessBatchCreateRequest request);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"userId\", source = \"accessObjectId\"),\n    })\n    public abstract UserDataSourcePageQueryVO dto2vo(DataSourceAccess dto);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/converter/UserTeamAdminConverter.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.converter;\n\nimport ai.chat2db.server.admin.api.controller.user.request.UserPageCommonQueryRequest;\nimport ai.chat2db.server.admin.api.controller.user.vo.UserTeamPageQueryVO;\nimport ai.chat2db.server.domain.api.model.TeamUser;\nimport ai.chat2db.server.domain.api.param.team.user.TeamUserComprehensivePageQueryParam;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class UserTeamAdminConverter {\n\n    /**\n     * convert\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"searchKey\", target = \"teamSearchKey\"),\n        @Mapping(target = \"enableReturnCount\", expression = \"java(true)\"),\n    })\n    public abstract TeamUserComprehensivePageQueryParam request2param(UserPageCommonQueryRequest request);\n\n    /**\n     * conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract UserTeamPageQueryVO dto2vo(TeamUser dto);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/request/UserCreateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.request;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * create\n *@author Jiaju Zhuang\n */\n@Data\npublic class UserCreateRequest {\n    /**\n     * userName\n     */\n    @NotNull\n    private String userName;\n\n    /**\n     * password\n     */\n    @NotNull\n    private String password;\n\n    /**\n     * Nick name\n     */\n    @NotNull\n    private String nickName;\n\n    /**\n     * email\n     */\n    @NotNull\n    private String email;\n\n    /**\n     * role coding\n     *\n     * @see RoleCodeEnum\n     */\n    @NotNull\n    private String roleCode;\n\n    /**\n     * user status\n     *\n     * @see ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/request/UserDataSourceBatchCreateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.request;\n\nimport java.util.List;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserDataSourceBatchCreateRequest {\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * Data Source id list\n     */\n    @NotNull\n    private List<Long> dataSourceIdList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/request/UserPageCommonQueryRequest.java",
    "content": "\npackage ai.chat2db.server.admin.api.controller.user.request;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class UserPageCommonQueryRequest extends PageQueryRequest {\n    /**\n     * user id\n     */\n    @NotNull\n    private Long userId;\n\n    /**\n     * searchKey\n     */\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/request/UserTeamBatchCreateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.request;\n\nimport java.util.List;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * create\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserTeamBatchCreateRequest {\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * team id list\n     */\n    @NotNull\n    private List<Long> teamIdList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/request/UserUpdateRequest.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.request;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * create\n *@author Jiaju Zhuang\n */\n@Data\npublic class UserUpdateRequest {\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Nick name\n     */\n    @NotNull\n    private String nickName;\n\n    /**\n     * email\n     */\n    @NotNull\n    private String email;\n\n\n    /**\n     * role coding\n     *\n     * @see RoleCodeEnum\n     */\n    private String roleCode;\n\n    /**\n     * user status\n     *\n     * @see ValidStatusEnum\n     */\n    @NotNull\n    private String status;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/vo/SimpleUserVO.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.vo;\n\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * user\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class SimpleUserVO {\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * userName\n     */\n    @NotNull\n    private String userName;\n\n    /**\n     * Nick name\n     */\n    @NotNull\n    private String nickName;\n\n    /**\n     * user status\n     *\n     * @see ValidStatusEnum\n     */\n    private String status;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/vo/UserDataSourcePageQueryVO.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.vo;\n\nimport ai.chat2db.server.admin.api.controller.datasource.vo.SimpleDataSourceVO;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class UserDataSourcePageQueryVO {\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * Data Source\n     */\n    private SimpleDataSourceVO dataSource;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/vo/UserPageQueryVO.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.vo;\n\nimport java.util.Date;\n\nimport ai.chat2db.server.common.api.controller.vo.SimpleUserVO;\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class UserPageQueryVO {\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * userName\n     */\n    @NotNull\n    private String userName;\n\n    /**\n     * Nick name\n     */\n    @NotNull\n    private String nickName;\n\n    /**\n     * user status\n     *\n     * @see ValidStatusEnum\n     */\n    private String status;\n\n    /**\n     * email\n     */\n    @NotNull\n    private String email;\n\n    /**\n     * role coding\n     *\n     * @see RoleCodeEnum\n     */\n    private String roleCode;\n\n    /**\n     * Change the time\n     */\n    private Date gmtModified;\n\n    /**\n     * Modifier user id\n     */\n    private Long modifiedUserId;\n\n    /**\n     * Modifier user\n     */\n    private SimpleUserVO modifiedUser;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-admin-api/src/main/java/ai/chat2db/server/admin/api/controller/user/vo/UserTeamPageQueryVO.java",
    "content": "package ai.chat2db.server.admin.api.controller.user.vo;\n\nimport ai.chat2db.server.admin.api.controller.team.vo.SimpleTeamVO;\nimport lombok.Data;\n\n/**\n * Pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class UserTeamPageQueryVO {\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * user id\n     */\n    private Long userId;\n\n    /**\n     * team\n     */\n    private SimpleTeamVO team;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-common-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-web</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-common-api</artifactId>\n    <packaging>jar</packaging>\n    <name>chat2db-server-common-api</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-tools-common</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-common-api/src/main/java/ai/chat2db/server/common/api/controller/CommonCommonController.java",
    "content": "package ai.chat2db.server.common.api.controller;\n\nimport ai.chat2db.server.common.api.controller.converter.EnvironmentCommonConverter;\nimport ai.chat2db.server.common.api.controller.vo.SimpleEnvironmentVO;\nimport ai.chat2db.server.domain.api.param.EnvironmentPageQueryParam;\nimport ai.chat2db.server.domain.api.service.EnvironmentService;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport jakarta.annotation.Resource;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Basic interface\n *\n * @author Jiaju Zhuang\n */\n@RequestMapping(\"/api/common\")\n@RestController\npublic class CommonCommonController {\n\n    @Resource\n    private EnvironmentService environmentService;\n    @Resource\n    private EnvironmentCommonConverter environmentCommonConverter;\n\n    /**\n     * Query all environments\n     *\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/environment/list_all\")\n    public ListResult<SimpleEnvironmentVO> environmentList() {\n        EnvironmentPageQueryParam environmentPageQueryParam = new EnvironmentPageQueryParam();\n        environmentPageQueryParam.setPageSize(Integer.MIN_VALUE);\n        return ListResult.of(\n            environmentCommonConverter.dto2vo(environmentService.pageQuery(environmentPageQueryParam).getData()));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-common-api/src/main/java/ai/chat2db/server/common/api/controller/converter/EnvironmentCommonConverter.java",
    "content": "package ai.chat2db.server.common.api.controller.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.common.api.controller.vo.SimpleEnvironmentVO;\nimport ai.chat2db.server.domain.api.model.Environment;\nimport lombok.extern.slf4j.Slf4j;\nimport org.mapstruct.Mapper;\n\n/**\n * converter\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Mapper(componentModel = \"spring\")\npublic abstract class EnvironmentCommonConverter {\n\n\n    /**\n     * convert\n     *\n     * @param list\n     * @return\n     */\n    public abstract List<SimpleEnvironmentVO> dto2vo(List<Environment> list);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-common-api/src/main/java/ai/chat2db/server/common/api/controller/request/CommonPageQueryRequest.java",
    "content": "package ai.chat2db.server.common.api.controller.request;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport lombok.Data;\n\n/**\n * Common pagination query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class CommonPageQueryRequest extends PageQueryRequest {\n\n\n    /**\n     * searchKey\n     */\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-common-api/src/main/java/ai/chat2db/server/common/api/controller/request/CommonQueryRequest.java",
    "content": "package ai.chat2db.server.common.api.controller.request;\n\nimport lombok.Data;\n\n/**\n * Common  query\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class CommonQueryRequest {\n\n    /**\n     * searchKey\n     */\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-common-api/src/main/java/ai/chat2db/server/common/api/controller/vo/SimpleEnvironmentVO.java",
    "content": "package ai.chat2db.server.common.api.controller.vo;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Environment\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SimpleEnvironmentVO implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * environment name\n     */\n    private String name;\n\n    /**\n     * environment abbreviation\n     */\n    private String shortName;\n\n    /**\n     * color\n     */\n    private String color;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-common-api/src/main/java/ai/chat2db/server/common/api/controller/vo/SimpleUserVO.java",
    "content": "package ai.chat2db.server.common.api.controller.vo;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * user\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class\nSimpleUserVO implements Serializable {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * username\n     */\n    private String userName;\n\n    /**\n     * Nick name\n     */\n    private String nickName;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-web</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-web-api</artifactId>\n    <packaging>jar</packaging>\n    <name>chat2db-server-web-api</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-tools-common</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-common-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aspects</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.unfbx</groupId>\n            <artifactId>chatgpt-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.theokanning.openai-gpt3-java</groupId>\n            <artifactId>service</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n        </dependency>\n\n        <!-- http -->\n        <dependency>\n            <groupId>com.dtflys.forest</groupId>\n            <artifactId>forest-spring</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.dtflys.forest</groupId>\n            <artifactId>forest-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>easyexcel</artifactId>\n        </dependency>\n        <!--poi-tl-->\n        <dependency>\n            <groupId>com.deepoove</groupId>\n            <artifactId>poi-tl</artifactId>\n        </dependency>\n        <!--pdf-->\n        <dependency>\n            <groupId>com.itextpdf</groupId>\n            <artifactId>itext-asian</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.itextpdf</groupId>\n            <artifactId>itextpdf</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.pdfbox</groupId>\n            <artifactId>pdfbox</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-spi</artifactId>\n        </dependency>\n\n        <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->\n        <dependency>\n            <groupId>com.auth0</groupId>\n            <artifactId>java-jwt</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.github.vertical-blank</groupId>\n            <artifactId>sql-formatter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-websocket</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/aspect/ConnectionInfoAspect.java",
    "content": "package ai.chat2db.server.web.api.aspect;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author jipengfei\n * @version : ConnectionInfoAspect.java\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\npublic @interface ConnectionInfoAspect {\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/aspect/ConnectionInfoHandler.java",
    "content": "package ai.chat2db.server.web.api.aspect;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.service.DataSourceAccessBusinessService;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceConsoleRequestInfo;\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author jipengfei\n * @version : ConnectionInfoHandler.java\n */\n@Component\n@Aspect\n@Slf4j\npublic class ConnectionInfoHandler {\n\n    @Autowired\n    private DataSourceService dataSourceService;\n    @Resource\n    private DataSourceAccessBusinessService dataSourceAccessBusinessService;\n\n    @Around(\"within(@ai.chat2db.server.web.api.aspect.ConnectionInfoAspect *)\")\n    public Object connectionInfoHandler(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {\n        try {\n            Object[] params = proceedingJoinPoint.getArgs();\n            if (params != null && params.length > 0) {\n                for (int i = 0; i < params.length; i++) {\n                    Object param = params[i];\n                    if (param instanceof DataSourceBaseRequest) {\n                        Long dataSourceId = ((DataSourceBaseRequest) param).getDataSourceId();\n                        String schemaName = ((DataSourceBaseRequest) param).getSchemaName();\n                        String database = ((DataSourceBaseRequest) param).getDatabaseName();\n                        Chat2DBContext.putContext(toInfo(dataSourceId, database, null, schemaName));\n                    } else if (param instanceof DataSourceConsoleRequestInfo) {\n                        Long dataSourceId = ((DataSourceConsoleRequestInfo) param).getDataSourceId();\n                        Long consoleId = ((DataSourceConsoleRequestInfo) param).getConsoleId();\n                        String database = ((DataSourceConsoleRequestInfo) param).getDatabaseName();\n                        Chat2DBContext.putContext(toInfo(dataSourceId, database, consoleId, null));\n                    } else if (param instanceof DataSourceBaseRequestInfo) {\n                        Long dataSourceId = ((DataSourceBaseRequestInfo) param).getDataSourceId();\n                        String database = ((DataSourceBaseRequestInfo) param).getDatabaseName();\n                        Chat2DBContext.putContext(toInfo(dataSourceId, database));\n                    }\n                }\n            }\n            return proceedingJoinPoint.proceed();\n        } finally {\n            Chat2DBContext.removeContext();\n        }\n    }\n\n    public ConnectInfo toInfo(Long dataSourceId, String database, Long consoleId, String schemaName) {\n        DataResult<DataSource> result = dataSourceService.queryById(dataSourceId);\n        DataSource dataSource = result.getData();\n        if (!result.success() || dataSource == null) {\n            throw new ParamBusinessException(\"dataSourceId\");\n        }\n\n        // Verify permissions\n        dataSourceAccessBusinessService.checkPermission(dataSource);\n\n        ConnectInfo connectInfo = new ConnectInfo();\n        connectInfo.setAlias(dataSource.getAlias());\n        connectInfo.setUser(dataSource.getUserName());\n        connectInfo.setConsoleId(consoleId);\n        connectInfo.setDataSourceId(dataSourceId);\n        connectInfo.setPassword(dataSource.getPassword());\n        connectInfo.setDbType(dataSource.getType());\n        connectInfo.setUrl(dataSource.getUrl());\n        connectInfo.setDatabase(database);\n        connectInfo.setSchemaName(schemaName);\n        connectInfo.setConsoleOwn(false);\n        connectInfo.setDriver(dataSource.getDriver());\n        connectInfo.setSsh(dataSource.getSsh());\n        connectInfo.setSsl(dataSource.getSsl());\n        connectInfo.setJdbc(dataSource.getJdbc());\n        connectInfo.setExtendInfo(dataSource.getExtendInfo());\n        connectInfo.setUrl(dataSource.getUrl());\n        connectInfo.setPort(StringUtils.isNotBlank(dataSource.getPort()) ? Integer.parseInt(dataSource.getPort()) : null);\n        connectInfo.setHost(dataSource.getHost());\n        connectInfo.setLoginUser(ContextUtils.getLoginUser().getId() + \"\");\n        DriverConfig driverConfig = dataSource.getDriverConfig();\n        if (driverConfig != null && driverConfig.notEmpty()) {\n            connectInfo.setDriverConfig(driverConfig);\n        }\n        return connectInfo;\n    }\n\n    public ConnectInfo toInfo(Long dataSourceId, String database) {\n        return toInfo(dataSourceId, database, null, null);\n    }\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/PageController.java",
    "content": "\npackage ai.chat2db.server.web.api.controller;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\n\n/**\n * @author jipengfei\n * @version : PageController.java\n *     patterns.add(\"/register.html\");\n *         patterns.add(\"/login.html\");\n *         patterns.add(\"/users/reg\");\n *         patterns.add(\"/users/login\");\n */\n@Slf4j\n@RequestMapping(\"/\")\n@Controller\npublic class PageController {\n\n    @RequestMapping(value = \"/login.html\", method={RequestMethod.GET}, produces=\"text/html;charset=utf-8\")\n    public String login(){\n        return \"login\";\n    }\n\n    @RequestMapping(value = \"/register.html\", method={RequestMethod.GET}, produces=\"text/html;charset=utf-8\")\n    public String register(){\n        return \"register\";\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/AiConfigController.java",
    "content": "package ai.chat2db.server.web.api.controller.ai;\n\nimport java.util.Objects;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.SystemConfigParam;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.config.Chat2dbProperties;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;\nimport ai.chat2db.server.web.api.controller.ai.rest.client.RestAIClient;\nimport ai.chat2db.server.web.api.http.GatewayClientService;\nimport ai.chat2db.server.web.api.http.response.ApiKeyResponse;\nimport ai.chat2db.server.web.api.http.response.InviteQrCodeResponse;\nimport ai.chat2db.server.web.api.http.response.QrCodeResponse;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * AI configuration information interface\n *\n * @author Jiaju Zhuang\n */\n@RestController\n@ConnectionInfoAspect\n@RequestMapping(\"/api/ai/config\")\n@Slf4j\npublic class AiConfigController {\n\n    @Resource\n    private GatewayClientService gatewayClientService;\n\n    @Autowired\n    private ConfigService configService;\n    @Resource\n    private Chat2dbProperties chat2dbProperties;\n\n    /**\n     * AI configuration information interface\n     *\n     * @return\n     */\n    @GetMapping(\"/getLoginQrCode\")\n    public DataResult<QrCodeResponse> getLoginQrCode() {\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        if (RoleCodeEnum.USER.getCode().equals(loginUser.getRoleCode())) {\n            return DataResult.of(\n                QrCodeResponse.builder().tip(I18nUtils.getMessage(\"settings.permissionDeniedForAiConfig\")).build());\n        }\n        return gatewayClientService.getLoginQrCode();\n    }\n\n    /**\n     * Query login status\n     *\n     * @param token\n     * @return\n     */\n    @GetMapping(\"/getLoginStatus\")\n    public DataResult<QrCodeResponse> getLoginStatus(@RequestParam(required = false) String token) {\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        if (RoleCodeEnum.USER.getCode().equals(loginUser.getRoleCode())) {\n            return DataResult.of(QrCodeResponse.builder().build());\n        }\n        DataResult<QrCodeResponse> dataResult = gatewayClientService.getLoginStatus(token);\n        QrCodeResponse qrCodeResponse = dataResult.getData();\n        // Representative successfully logged in\n        if (StringUtils.isNotBlank(qrCodeResponse.getApiKey())) {\n            SystemConfigParam sqlSourceParam = SystemConfigParam.builder().code(RestAIClient.AI_SQL_SOURCE)\n                .content(AiSqlSourceEnum.CHAT2DBAI.getCode()).build();\n            configService.createOrUpdate(sqlSourceParam);\n            SystemConfigParam param = SystemConfigParam.builder()\n                .code(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).content(qrCodeResponse.getApiKey())\n                .build();\n            configService.createOrUpdate(param);\n            SystemConfigParam hostParam = SystemConfigParam.builder()\n                .code(Chat2dbAIClient.CHAT2DB_OPENAI_HOST)\n                .content(chat2dbProperties.getGateway().getModelBaseUrl() + \"/model\")\n                .build();\n            configService.createOrUpdate(hostParam);\n            Chat2dbAIClient.refresh();\n        }\n        return dataResult;\n    }\n\n    /**\n     * Return remaining times\n     *\n     * @return\n     */\n    @GetMapping(\"/remaininguses\")\n    public DataResult<ApiKeyResponse> remaininguses() {\n        String apiKey = getApiKey();\n        if (StringUtils.isBlank(apiKey)) {\n            return DataResult.of(ApiKeyResponse.builder()\n                .build());\n        }\n        return gatewayClientService.remaininguses(apiKey);\n    }\n\n    /**\n     * Obtain invitation QR code\n     *\n     * @return\n     */\n    @GetMapping(\"/getInviteQrCode\")\n    public DataResult<InviteQrCodeResponse> getInviteQrCode() {\n        String apiKey = getApiKey();\n        if (StringUtils.isBlank(apiKey)) {\n            return DataResult.of(new InviteQrCodeResponse());\n        }\n        return gatewayClientService.getInviteQrCode(apiKey);\n    }\n\n    private String getApiKey() {\n        DataResult<Config> apiKey = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY);\n        return Objects.nonNull(apiKey.getData()) ? apiKey.getData().getContent() : null;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java",
    "content": "package ai.chat2db.server.web.api.controller.ai;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.ShowCreateTableParam;\nimport ai.chat2db.server.domain.api.param.TableQueryParam;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.tools.base.enums.WhiteListTypeEnum;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.tools.common.util.EasyEnumUtils;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.azure.client.AzureOpenAIClient;\nimport ai.chat2db.server.web.api.controller.ai.azure.listener.AzureOpenAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatRole;\nimport ai.chat2db.server.web.api.controller.ai.baichuan.client.BaichuanAIClient;\nimport ai.chat2db.server.web.api.controller.ai.baichuan.listener.BaichuanChatAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.listener.Chat2dbAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.claude.client.ClaudeAIClient;\nimport ai.chat2db.server.web.api.controller.ai.claude.listener.ClaudeAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.claude.model.ClaudeChatCompletionsOptions;\nimport ai.chat2db.server.web.api.controller.ai.claude.model.ClaudeChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.config.LocalCache;\nimport ai.chat2db.server.web.api.controller.ai.converter.ChatConverter;\nimport ai.chat2db.server.web.api.controller.ai.enums.PromptType;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatAIClient;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.listener.FastChatAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;\nimport ai.chat2db.server.web.api.controller.ai.openai.client.OpenAIClient;\nimport ai.chat2db.server.web.api.controller.ai.openai.listener.OpenAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;\nimport ai.chat2db.server.web.api.controller.ai.request.ChatRequest;\nimport ai.chat2db.server.web.api.controller.ai.rest.client.RestAIClient;\nimport ai.chat2db.server.web.api.controller.ai.rest.listener.RestAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.tongyi.client.TongyiChatAIClient;\nimport ai.chat2db.server.web.api.controller.ai.tongyi.listener.TongyiChatAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.wenxin.client.WenxinAIClient;\nimport ai.chat2db.server.web.api.controller.ai.wenxin.listener.WenxinAIEventSourceListener;\nimport ai.chat2db.server.web.api.controller.ai.zhipu.client.ZhipuChatAIClient;\nimport ai.chat2db.server.web.api.controller.ai.zhipu.listener.ZhipuChatAIEventSourceListener;\nimport ai.chat2db.server.web.api.http.GatewayClientService;\nimport ai.chat2db.server.web.api.http.model.EsTableSchema;\nimport ai.chat2db.server.web.api.http.model.TableSchema;\nimport ai.chat2db.server.web.api.http.request.EsTableSchemaRequest;\nimport ai.chat2db.server.web.api.http.request.TableSchemaRequest;\nimport ai.chat2db.server.web.api.http.request.WhiteListRequest;\nimport ai.chat2db.server.web.api.http.response.EsTableSchemaResponse;\nimport ai.chat2db.server.web.api.http.response.TableSchemaResponse;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.json.JSONUtil;\nimport com.alibaba.fastjson2.JSON;\nimport com.google.common.collect.Lists;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\n/**\n * description：\n *\n * @author https:www.unfbx.com\n * @date 2023-03-01\n */\n@RestController\n@ConnectionInfoAspect\n@RequestMapping(\"/api/ai\")\n@Slf4j\npublic class ChatController {\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private ChatConverter chatConverter;\n\n    @Autowired\n    private DataSourceService dataSourceService;\n\n    @Value(\"${chatgpt.context.length}\")\n    private Integer contextLength;\n\n    @Value(\"${chatgpt.version}\")\n    private String gptVersion;\n\n    @Resource\n    private GatewayClientService gatewayClientService;\n\n    /**\n     * chat timeout\n     */\n    private static final Long CHAT_TIMEOUT = Duration.ofMinutes(50).toMillis();\n\n    /**\n     * Maximum number of tokens for prompts\n     */\n    private Integer MAX_PROMPT_LENGTH = 3850;\n\n    /**\n     * token conversion string length\n     */\n    private Integer TOKEN_CONVERT_CHAR_LENGTH = 4;\n\n    /**\n     * Return token size\n     */\n    private Integer RETURN_TOKEN_LENGTH = 150;\n\n\n    /**\n     * Custom model streaming output interface DEMO\n     * <p>\n     *     Note: For custom AI that uses its own local streaming output, the interface input and output must be consistent with this sample.\n     * </p>\n     *\n     * @param queryRequest\n     * @return\n     * @throws IOException\n     */\n    @PostMapping(\"/custom/stream/chat\")\n    @CrossOrigin\n    public SseEmitter customChat(@RequestBody ChatRequest queryRequest) throws IOException {\n        SseEmitter emitter = new SseEmitter(CHAT_TIMEOUT);\n\n        // Set event handler for SSEEmitter\n        emitter.onCompletion(() -> log.info(LocalDateTime.now() + \", on completion\"));\n        emitter.onTimeout(() -> {\n            log.info(LocalDateTime.now() + \", uid# on timeout\");\n            emitter.complete();\n        });\n\n        // Start a new thread to generate SSE events\n        new Thread(() -> {\n            try {\n                for (int i = 0; i < 10; i++) {\n                    emitter.send(SseEmitter.event().name(\"message\").data(\"Event \" + i));\n                    Thread.sleep(1000);\n                }\n            } catch (Exception e) {\n                emitter.completeWithError(e);\n            } finally {\n                emitter.complete();\n            }\n        }).start();\n\n        return emitter;\n    }\n\n    /**\n     * Custom model non-streaming output interface DEMO\n     * <p>\n     *       Note: Use your own local flying flow output to customize the AI. The interface input and output must be consistent with this sample.\n     * </p>\n     *\n     * @param queryRequest\n     * @return\n     * @throws IOException\n     */\n    @PostMapping(\"/custom/non/stream/chat\")\n    @CrossOrigin\n    public String customNonStreamChat(@RequestBody ChatRequest queryRequest) throws IOException {\n        String data = \"The custom AI sample interface is connected successfully! ! ! !\";\n        return data;\n    }\n\n    /**\n     * SQL conversion model\n     *\n     * @param queryRequest\n     * @param headers\n     * @return\n     * @throws IOException\n     */\n    @GetMapping(\"/chat\")\n    @CrossOrigin\n    public SseEmitter completions(ChatQueryRequest queryRequest, @RequestHeader Map<String, String> headers)\n        throws IOException {\n        //The default timeout is 30 seconds. If set to 0L, it will never timeout.\n        SseEmitter sseEmitter = new SseEmitter(CHAT_TIMEOUT);\n        String uid = headers.get(\"uid\");\n        if (StrUtil.isBlank(uid)) {\n            throw new ParamBusinessException(\"uid\");\n        }\n\n        //Prompt message cannot be empty\n        if (StringUtils.isBlank(queryRequest.getMessage())) {\n            throw new ParamBusinessException(\"message\");\n        }\n\n        return distributeAISql(queryRequest, sseEmitter, uid);\n    }\n\n    /**\n     * distribute with different AI\n     *\n     * @return\n     */\n    public SseEmitter distributeAISql(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config config = configService.find(RestAIClient.AI_SQL_SOURCE).getData();\n        String aiSqlSource = AiSqlSourceEnum.CHAT2DBAI.getCode();\n        if (Objects.nonNull(config)) {\n            aiSqlSource = config.getContent();\n        }\n        AiSqlSourceEnum aiSqlSourceEnum = AiSqlSourceEnum.getByName(aiSqlSource);\n        if (Objects.isNull(aiSqlSourceEnum)) {\n            aiSqlSourceEnum = AiSqlSourceEnum.OPENAI;\n        }\n        uid = aiSqlSourceEnum.getCode() + uid;\n        switch (Objects.requireNonNull(aiSqlSourceEnum)) {\n            case OPENAI :\n                return chatWithOpenAi(queryRequest, sseEmitter, uid);\n            case CHAT2DBAI:\n                return chatWithChat2dbAi(queryRequest, sseEmitter, uid);\n            case RESTAI :\n                return chatWithRestAi(queryRequest, sseEmitter, uid);\n            case FASTCHATAI:\n                return chatWithFastChatAi(queryRequest, sseEmitter, uid);\n            case AZUREAI :\n                return chatWithAzureAi(queryRequest, sseEmitter, uid);\n            case CLAUDEAI:\n                return chatWithClaudeAi(queryRequest, sseEmitter, uid);\n            case WENXINAI:\n                return chatWithWenxinAi(queryRequest, sseEmitter, uid);\n            case BAICHUANAI:\n                return chatWithBaichuanAi(queryRequest, sseEmitter, uid);\n            case TONGYIQIANWENAI:\n                return chatWithTongyiChatAi(queryRequest, sseEmitter, uid);\n            case ZHIPUAI:\n                return chatWithZhipuChatAi(queryRequest, sseEmitter, uid);\n        }\n        return chatWithOpenAi(queryRequest, sseEmitter, uid);\n    }\n\n    /**\n     * Chat using a custom AI interface\n     *\n     * @param prompt\n     * @param sseEmitter\n     * @return\n     */\n    private SseEmitter chatWithRestAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        List<FastChatMessage> messages = getFastChatMessage(uid, prompt);\n\n        buildSseEmitter(sseEmitter, uid);\n\n        RestAIEventSourceListener restAIEventSourceListener = new RestAIEventSourceListener(sseEmitter);\n        RestAIClient.getInstance().streamCompletions(messages, restAIEventSourceListener);\n        LocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n    /**\n     * Using the OPENAI SQL interface\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid)\n        throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {\n            log.error(\"The prompt exceeds the maximum length: {}, input length: {}, please re-enter\", MAX_PROMPT_LENGTH,\n                prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);\n            throw new ParamBusinessException();\n        }\n\n        List<Message> messages = new ArrayList<>();\n        prompt = prompt.replaceAll(\"#\", \"\");\n        log.info(prompt);\n        Message currentMessage = Message.builder().content(prompt).role(Message.Role.USER).build();\n        messages.add(currentMessage);\n        buildSseEmitter(sseEmitter, uid);\n\n        OpenAIEventSourceListener openAIEventSourceListener = new OpenAIEventSourceListener(sseEmitter);\n        OpenAIClient.getInstance().streamChatCompletion(messages, openAIEventSourceListener);\n        LocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n    /**\n     * Using the OPENAI SQL interface\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithChat2dbAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid)\n        throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {\n            log.error(\"exceed max token length:{}，input length:{}\", MAX_PROMPT_LENGTH,\n                prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);\n            throw new ParamBusinessException();\n        }\n\n        prompt = prompt.replaceAll(\"#\", \"\");\n        log.info(prompt);\n        Message currentMessage = Message.builder().content(prompt).role(Message.Role.USER).build();\n        List<Message> messages = new ArrayList<>();\n        messages.add(currentMessage);\n        buildSseEmitter(sseEmitter, uid);\n\n        Chat2dbAIEventSourceListener openAIEventSourceListener = new Chat2dbAIEventSourceListener(sseEmitter);\n        Chat2dbAIClient.getInstance().streamCompletions(messages, openAIEventSourceListener);\n        LocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n    /**\n     * chat with azure openai\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithAzureAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {\n            log.error(\"The prompt exceeds the maximum length: {}, input length: {}, please re-enter\", MAX_PROMPT_LENGTH,\n                    prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);\n            throw new ParamBusinessException();\n        }\n        List<AzureChatMessage> messages = (List<AzureChatMessage>)LocalCache.CACHE.get(uid);\n        if (CollectionUtils.isNotEmpty(messages)) {\n            if (messages.size() >= contextLength) {\n                messages = messages.subList(1, contextLength);\n            }\n        } else {\n            messages = Lists.newArrayList();\n        }\n        AzureChatMessage currentMessage = new AzureChatMessage(AzureChatRole.USER).setContent(prompt);\n        messages.add(currentMessage);\n\n        buildSseEmitter(sseEmitter, uid);\n\n        AzureOpenAIEventSourceListener sourceListener = new AzureOpenAIEventSourceListener(sseEmitter);\n        AzureOpenAIClient.getInstance().streamCompletions(messages, sourceListener);\n        LocalCache.CACHE.put(uid, messages, LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n    /**\n     * chat with fast chat openai\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithFastChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        List<FastChatMessage> messages = getFastChatMessage(uid, prompt);\n\n        buildSseEmitter(sseEmitter, uid);\n\n        FastChatAIEventSourceListener sourceListener = new FastChatAIEventSourceListener(sseEmitter);\n        FastChatAIClient.getInstance().streamCompletions(messages, sourceListener);\n        LocalCache.CACHE.put(uid, messages, LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n    /**\n     * chat with zhipu chat openai\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithZhipuChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        List<FastChatMessage> messages = getFastChatMessage(uid, prompt);\n\n        buildSseEmitter(sseEmitter, uid);\n\n        ZhipuChatAIEventSourceListener sourceListener = new ZhipuChatAIEventSourceListener(sseEmitter);\n        ZhipuChatAIClient.getInstance().streamCompletions(messages, sourceListener);\n        LocalCache.CACHE.put(uid, messages, LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n    /**\n     * chat with tongyi chat openai\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithTongyiChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        List<FastChatMessage> messages = getFastChatMessage(uid, prompt);\n\n        buildSseEmitter(sseEmitter, uid);\n\n        TongyiChatAIEventSourceListener sourceListener = new TongyiChatAIEventSourceListener(sseEmitter);\n        TongyiChatAIClient.getInstance().streamCompletions(messages, sourceListener);\n        LocalCache.CACHE.put(uid, messages, LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n    /**\n     * chat with baichuan chat openai\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithBaichuanAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        List<FastChatMessage> messages = getFastChatMessage(uid, prompt);\n\n        buildSseEmitter(sseEmitter, uid);\n\n        BaichuanChatAIEventSourceListener sourceListener = new BaichuanChatAIEventSourceListener(sseEmitter);\n        BaichuanAIClient.getInstance().streamCompletions(messages, sourceListener);\n        LocalCache.CACHE.put(uid, messages, LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n    /**\n     * get fast chat message\n     *\n     * @param uid\n     * @param prompt\n     * @return\n     */\n    private List<FastChatMessage> getFastChatMessage(String uid, String prompt) {\n        List<FastChatMessage> messages = (List<FastChatMessage>)LocalCache.CACHE.get(uid);\n        if (CollectionUtils.isNotEmpty(messages)) {\n            if (messages.size() >= contextLength) {\n                messages = messages.subList(1, contextLength);\n            }\n        } else {\n            messages = Lists.newArrayList();\n        }\n        FastChatMessage currentMessage = new FastChatMessage(FastChatRole.USER).setContent(prompt);\n        messages.add(currentMessage);\n        return messages;\n    }\n\n    /**\n     * chat with wenxin chat openai\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithWenxinAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        List<FastChatMessage> messages = getFastChatMessage(uid, prompt);\n        if (messages.size() >= 2 && messages.size() % 2 == 0) {\n            messages.remove(messages.size() - 1);\n        }\n\n        buildSseEmitter(sseEmitter, uid);\n\n        WenxinAIEventSourceListener sourceListener = new WenxinAIEventSourceListener(sseEmitter);\n        WenxinAIClient.getInstance().streamCompletions(messages, sourceListener);\n        LocalCache.CACHE.put(uid, messages, LocalCache.TIMEOUT);\n        return sseEmitter;\n    }\n\n\n    /**\n     * chat with claude ai\n     *\n     * @param queryRequest\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter chatWithClaudeAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {\n        String prompt = buildPrompt(queryRequest);\n        ClaudeChatMessage claudeChatMessage = new ClaudeChatMessage();\n        claudeChatMessage.setText(prompt);\n        ClaudeChatCompletionsOptions chatCompletionsOptions = new ClaudeChatCompletionsOptions();\n        chatCompletionsOptions.setPrompt(prompt);\n        claudeChatMessage.setCompletion(chatCompletionsOptions);\n\n        buildSseEmitter(sseEmitter, uid);\n\n        ClaudeAIEventSourceListener sourceListener = new ClaudeAIEventSourceListener(sseEmitter);\n        ClaudeAIClient.getInstance().streamCompletions(claudeChatMessage, sourceListener);\n        return sseEmitter;\n    }\n\n    /**\n     * construct sseEmitter\n     *\n     * @param sseEmitter\n     * @param uid\n     * @return\n     * @throws IOException\n     */\n    private SseEmitter buildSseEmitter(SseEmitter sseEmitter, String uid) throws IOException {\n        sseEmitter.send(SseEmitter.event().id(uid).name(\"connect successfully！！！！\").data(LocalDateTime.now()).reconnectTime(3000));\n        sseEmitter.onCompletion(() -> {\n            log.info(LocalDateTime.now() + \", uid#\" + uid + \", on completion\");\n        });\n        sseEmitter.onTimeout(\n            () -> log.info(LocalDateTime.now() + \", uid#\" + uid + \", on timeout#\" + sseEmitter.getTimeout()));\n        sseEmitter.onError(\n            throwable -> {\n                try {\n                    log.info(LocalDateTime.now() + \", uid#\" + \"765431\" + \", on error#\" + throwable.toString());\n                    sseEmitter.send(SseEmitter.event().id(\"765431\").name(\"An exception occurs!\").data(throwable.getMessage())\n                        .reconnectTime(3000));\n                } catch (IOException e) {\n                    log.error(\"An exception occurs!{}\", e.getMessage(), e);\n                }\n            }\n        );\n        return sseEmitter;\n    }\n\n    /**\n     * Build schema parameters\n     *\n     * @param tableQueryParam\n     * @param tableNames\n     * @return\n     */\n    private String buildTableColumn(TableQueryParam tableQueryParam,\n        List<String> tableNames) {\n        if (CollectionUtils.isEmpty(tableNames)) {\n            return \"\";\n        }\n        List<String> schemaContent = Lists.newArrayList();\n        try {\n             schemaContent = tableNames.stream().map(tableName -> {\n                tableQueryParam.setTableName(tableName);\n                return queryTableDdl(tableName, tableQueryParam);\n            }).collect(Collectors.toList());\n        } catch (Exception exception) {\n            log.error(\"query table error, do nothing\");\n        }\n\n        return JSON.toJSONString(schemaContent);\n    }\n\n    /**\n     * query table schema\n     *\n     * @param tableName\n     * @param request\n     * @return\n     */\n    private String queryTableDdl(String tableName, TableQueryParam request) {\n        ShowCreateTableParam param = new ShowCreateTableParam();\n        param.setTableName(tableName);\n        param.setDataSourceId(request.getDataSourceId());\n        param.setDatabaseName(request.getDatabaseName());\n        param.setSchemaName(request.getSchemaName());\n        DataResult<String> tableSchema = tableService.showCreateTable(param);\n        return tableSchema.getData();\n    }\n\n    /**\n     * build prompt\n     *\n     * @param queryRequest\n     * @return\n     */\n    private String buildPrompt(ChatQueryRequest queryRequest) {\n        if (PromptType.TEXT_GENERATION.getCode().equals(queryRequest.getPromptType())) {\n            return queryRequest.getMessage();\n        }\n\n        // Query schema information\n        String dataSourceType = queryDatabaseType(queryRequest);\n        String properties = \"\";\n        if (CollectionUtils.isNotEmpty(queryRequest.getTableNames())) {\n            TableQueryParam queryParam = chatConverter.chat2tableQuery(queryRequest);\n            properties = buildTableColumn(queryParam, queryRequest.getTableNames());\n        } else {\n            properties = mappingDatabaseSchema(queryRequest);\n        }\n        String prompt = queryRequest.getMessage();\n        String promptType = StringUtils.isBlank(queryRequest.getPromptType()) ? PromptType.NL_2_SQL.getCode()\n            : queryRequest.getPromptType();\n        PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);\n        String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : \"\";\n        String schemaProperty = StringUtils.isNotEmpty(properties) ? String.format(\n            \"### Please follow the below table properties and SQL input%s. %s\\n#\\n### %s SQL tables, with their properties:\\n#\\n# \"\n                + \"%s\\n#\\n#\\n### SQL input: %s\", pType.getDescription(), ext, dataSourceType,\n            properties, prompt) : String.format(\"### Please follow the below SQL input%s. %s\\n#\\n### SQL input: %s\",\n            pType.getDescription(), ext, prompt);\n        switch (pType) {\n            case SQL_2_SQL:\n                schemaProperty = StringUtils.isNotBlank(queryRequest.getDestSqlType()) ? String.format(\n                    \"%s\\n#\\n### Target SQL type: %s\", schemaProperty, queryRequest.getDestSqlType()) : String.format(\n                    \"%s\\n#\\n### Target SQL type: %s\", schemaProperty, dataSourceType);\n            default:\n                break;\n        }\n        String cleanedInput = schemaProperty.replaceAll(\"[\\r\\t]\", \"\");\n        return cleanedInput;\n    }\n\n    /**\n     * query chat2db apikey\n     *\n     * @return\n     */\n    public String getApiKey() {\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config config = configService.find(RestAIClient.AI_SQL_SOURCE).getData();\n        String aiSqlSource = AiSqlSourceEnum.CHAT2DBAI.getCode();\n        // only sync for chat2db ai\n        if (Objects.isNull(config) || !aiSqlSource.equals(config.getContent())) {\n            return null;\n        }\n        Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();\n        if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {\n            return null;\n        }\n        return keyConfig.getContent();\n    }\n\n    /**\n     * query database type\n     *\n     * @param queryRequest\n     * @return\n     */\n    public String queryDatabaseType(ChatQueryRequest queryRequest) {\n        // Query schema information\n        DataResult<DataSource> dataResult = dataSourceService.queryById(queryRequest.getDataSourceId());\n        String dataSourceType = dataResult.getData().getType();\n        if (StringUtils.isBlank(dataSourceType)) {\n            dataSourceType = \"MYSQL\";\n        }\n        return dataSourceType;\n    }\n\n    public String mappingDatabaseSchema(ChatQueryRequest queryRequest) {\n        String properties = \"\";\n        String apiKey = getApiKey();\n        if (StringUtils.isNotBlank(apiKey)) {\n            boolean res = gatewayClientService.checkInWhite(new WhiteListRequest(apiKey, WhiteListTypeEnum.VECTOR.getCode())).getData();\n            if (res) {\n//                properties = queryDatabaseSchema(queryRequest) + querySchemaByEs(queryRequest);\n                properties = queryDatabaseSchema(queryRequest);\n            }\n        }\n        return properties;\n    }\n\n    /**\n     * query database schema\n     *\n     * @param queryRequest\n     * @return\n     * @throws IOException\n     */\n    public String queryDatabaseSchema(ChatQueryRequest queryRequest) {\n        // request embedding\n        FastChatEmbeddingResponse response = distributeAIEmbedding(queryRequest.getMessage());\n        List<List<BigDecimal>> contentVector = new ArrayList<>();\n        if (Objects.isNull(response) || CollectionUtils.isEmpty(response.getData())) {\n            return \"\";\n        }\n        contentVector.add(response.getData().get(0).getEmbedding());\n\n        // search embedding\n        TableSchemaRequest tableSchemaRequest = new TableSchemaRequest();\n        tableSchemaRequest.setSchemaVector(contentVector);\n        tableSchemaRequest.setDataSourceId(queryRequest.getDataSourceId());\n        tableSchemaRequest.setDatabaseName(queryRequest.getDatabaseName());\n        tableSchemaRequest.setDataSourceSchema(queryRequest.getSchemaName());\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();\n        if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {\n            return \"\";\n        }\n        tableSchemaRequest.setApiKey(keyConfig.getContent());\n        try {\n            DataResult<TableSchemaResponse> result = gatewayClientService.schemaVectorSearch(tableSchemaRequest);\n            List<String> schemas = Lists.newArrayList();\n            if (Objects.nonNull(result.getData()) && CollectionUtils.isNotEmpty(result.getData().getTableSchemas())) {\n                for(TableSchema data: result.getData().getTableSchemas()){\n                    schemas.add(data.getTableSchema());\n                }\n            }\n            if (CollectionUtils.isEmpty(schemas)) {\n                return \"\";\n            }\n            String res = JSON.toJSONString(schemas);\n            log.info(\"search vector result:{}\", res);\n            return res;\n        } catch (Exception exception) {\n            log.error(\"query table error, do nothing\");\n            return \"\";\n        }\n    }\n\n    /**\n     * query database schema\n     *\n     * @param queryRequest\n     * @return\n     * @throws IOException\n     */\n    public String querySchemaByEs(ChatQueryRequest queryRequest) {\n        // search embedding\n        EsTableSchemaRequest tableSchemaRequest = new EsTableSchemaRequest();\n        tableSchemaRequest.setSearchKey(queryRequest.getMessage());\n        tableSchemaRequest.setDataSourceId(queryRequest.getDataSourceId());\n        tableSchemaRequest.setDatabaseName(queryRequest.getDatabaseName());\n        tableSchemaRequest.setSchemaName(queryRequest.getSchemaName());\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();\n        if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {\n            return \"\";\n        }\n        tableSchemaRequest.setApiKey(keyConfig.getContent());\n        try {\n            DataResult<EsTableSchemaResponse> result = gatewayClientService.schemaEsSearch(tableSchemaRequest);\n            List<String> schemas = Lists.newArrayList();\n            if (Objects.nonNull(result.getData()) && CollectionUtils.isNotEmpty(result.getData().getTableSchemas())) {\n                for(EsTableSchema data: result.getData().getTableSchemas()){\n                    schemas.add(data.getTableSchemaContent());\n                }\n            }\n            if (CollectionUtils.isEmpty(schemas)) {\n                return \"\";\n            }\n            String res = JSON.toJSONString(schemas);\n            log.info(\"search es result:{}\", res);\n            return res;\n        } catch (Exception exception) {\n            log.error(\"query es table error, do nothing\");\n            return \"\";\n        }\n    }\n\n    /**\n     * distribute embedding with different AI\n     *\n     * @return\n     */\n    public FastChatEmbeddingResponse distributeAIEmbedding(String input) {\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config config = configService.find(RestAIClient.AI_SQL_SOURCE).getData();\n        String aiSqlSource = config.getContent();\n        if (Objects.isNull(aiSqlSource)) {\n            return null;\n        }\n        AiSqlSourceEnum aiSqlSourceEnum = AiSqlSourceEnum.getByName(aiSqlSource);\n        switch (Objects.requireNonNull(aiSqlSourceEnum)) {\n            case CHAT2DBAI:\n                return embeddingWithChat2dbAi(input);\n            case FASTCHATAI:\n                return embeddingWithFastChatAi(input);\n        }\n        return null;\n    }\n\n    /**\n     * embedding with fast chat openai\n     *\n     * @param input\n     * @return\n     * @throws IOException\n     */\n    private FastChatEmbeddingResponse embeddingWithFastChatAi(String input) {\n        FastChatEmbeddingResponse response = FastChatAIClient.getInstance().embeddings(input);\n        return response;\n    }\n\n    /**\n     * embedding with open ai\n     *\n     * @param input\n     * @return\n     */\n    private FastChatEmbeddingResponse embeddingWithChat2dbAi(String input) {\n        FastChatEmbeddingResponse embeddings = Chat2dbAIClient.getInstance().embeddings(input);\n        return embeddings;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/DocParser/AbstractParser.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.DocParser;\n\nimport java.io.InputStream;\nimport java.util.List;\n\n/**\n * @author CYY\n * @date March 20, 2023 8:13 am\n * @description\n */\npublic abstract class AbstractParser {\n    public abstract List<String> parse(InputStream inputStream) throws Exception;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/DocParser/PdfParse.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.DocParser;\n\nimport org.apache.pdfbox.pdmodel.PDDocument;\nimport org.apache.pdfbox.text.PDFTextStripper;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author CYY\n * @date March 11, 2023 3:23 pm\n * @description\n */\npublic class PdfParse extends AbstractParser {\n    private static final int MAX_LENGTH = 200;\n\n    @Override\n    public List<String> parse(InputStream inputStream) throws IOException {\n        // Open PDF file\n        PDDocument document = PDDocument.load(inputStream);\n        // Create a PDFTextStripper object\n        PDFTextStripper stripper = new PDFTextStripper();\n        // Get text content\n        String text = stripper.getText(document);\n        // Filter characters\n        text = text.replaceAll(\"\\\\s\", \" \").replaceAll(\"(\\\\r\\\\n|\\\\r|\\\\n|\\\\n\\\\r)\",\" \");\n        String[] sentence = text.split(\"。\");\n        List<String> ans = new ArrayList<>();\n        for (String s : sentence) {\n            if (s.length() > MAX_LENGTH) {\n                for (int index = 0; index < sentence.length; index = (index + 1) * MAX_LENGTH) {\n                    String substring = s.substring(index, MAX_LENGTH);\n                    if(substring.length() < 5) continue;\n                    ans.add(substring);\n                }\n            } else {\n                ans.add(s);\n            }\n        }\n        // Close document\n        document.close();\n        return ans;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/EmbeddingController.java",
    "content": "package ai.chat2db.server.web.api.controller.ai;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.ShowCreateTableParam;\nimport ai.chat2db.server.domain.api.param.TablePageQueryParam;\nimport ai.chat2db.server.domain.api.param.TableSelector;\nimport ai.chat2db.server.domain.api.param.TableVectorParam;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.tools.base.enums.WhiteListTypeEnum;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;\nimport ai.chat2db.server.web.api.controller.ai.rest.client.RestAIClient;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.TableBriefQueryRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.TableMilvusQueryRequest;\nimport ai.chat2db.server.web.api.http.GatewayClientService;\nimport ai.chat2db.server.web.api.http.request.EsTableSchemaRequest;\nimport ai.chat2db.server.web.api.http.request.TableSchemaRequest;\nimport ai.chat2db.server.web.api.http.request.WhiteListRequest;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport ai.chat2db.spi.model.Table;\nimport com.google.common.collect.Lists;\nimport jakarta.annotation.Resource;\nimport jakarta.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * @author moji\n */\n@RestController\n@ConnectionInfoAspect\n@RequestMapping(\"/api/ai/embedding\")\n@Slf4j\npublic class EmbeddingController extends ChatController {\n\n\n    @Resource\n    private GatewayClientService gatewayClientService;\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    @Autowired\n    private TableService tableService;\n\n    /**\n     * check if in white list\n     */\n    @GetMapping(\"/white/check\")\n    public DataResult<Boolean> checkInWhite(WhiteListRequest request) {\n        request.setWhiteType(WhiteListTypeEnum.VECTOR.getCode());\n        if (StringUtils.isBlank(request.getApiKey())) {\n            return DataResult.of(false);\n        }\n        try {\n            DataResult<Boolean> res = gatewayClientService.checkInWhite(request);\n        } catch (Exception ex) {\n            log.error(\"checkInWhite error\", ex);\n        }\n        return DataResult.of(false);\n    }\n\n    /**\n     * save datasource embeddings\n     *\n     * @param request\n     * @return\n     * @throws IOException\n     */\n    @PostMapping(\"/datasource\")\n    @CrossOrigin\n    public ActionResult embeddings(@Valid TableMilvusQueryRequest request)\n        throws Exception {\n\n        // query tables\n        request.setPageNo(1);\n        request.setPageSize(1000);\n        TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);\n        TableSelector tableSelector = new TableSelector();\n        tableSelector.setColumnList(false);\n        tableSelector.setIndexList(false);\n        PageResult<Table> tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);\n\n        List<Table> tables = tableDTOPageResult.getData();\n        if (CollectionUtils.isEmpty(tables)) {\n            return ActionResult.isSuccess();\n        }\n\n        String tableName = tables.get(0).getName();\n        String tableSchema = queryTableDdl(tableName, request);\n\n        if (StringUtils.isBlank(tableSchema)) {\n            throw new ParamBusinessException(\"tableSchema is empty\");\n        }\n\n        // save first table embedding\n        TableSchemaRequest tableSchemaRequest = new TableSchemaRequest();\n        tableSchemaRequest.setDataSourceId(request.getDataSourceId());\n        tableSchemaRequest.setApiKey(request.getApikey());\n        tableSchemaRequest.setDeleteBeforeInsert(true);\n        tableSchemaRequest.setDataSourceSchema(request.getSchemaName());\n        tableSchemaRequest.setDatabaseName(request.getDatabaseName());\n\n        saveTableEmbedding(tableSchema, tableSchemaRequest);\n\n        // save other table embedding\n        tableSchemaRequest.setDeleteBeforeInsert(false);\n        for (int i = 1; i < tables.size(); i++) {\n            tableName = tables.get(i).getName();\n            tableSchema = queryTableDdl(tableName, request);\n            if (StringUtils.isBlank(tableSchema)) {\n                continue;\n            }\n            saveTableEmbedding(tableSchema, tableSchemaRequest);\n        }\n\n        // query all the tables\n        Long totalTableCount = tableDTOPageResult.getTotal();\n        Integer pageSize = queryParam.getPageSize();\n        if (pageSize < totalTableCount) {\n            for (int i = 2; i < totalTableCount/pageSize + 1; i++) {\n                queryParam.setPageNo(i);\n                tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);\n                tables = tableDTOPageResult.getData();\n                for (Table table : tables) {\n                    tableName = table.getName();\n                    tableSchema = queryTableDdl(tableName, request);\n                    if (StringUtils.isBlank(tableSchema)) {\n                        continue;\n                    }\n                    saveTableEmbedding(tableSchema, tableSchemaRequest);\n                }\n            }\n        }\n\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * save datasource schema\n     *\n     * @param request\n     * @return\n     * @throws IOException\n     */\n    @PostMapping(\"/datasource/es\")\n    @CrossOrigin\n    public ActionResult es(@Valid EsTableSchemaRequest request)\n            throws Exception {\n\n        // query tables\n        TablePageQueryParam queryParam = rdbWebConverter.schemaReq2page(request);\n        TableSelector tableSelector = new TableSelector();\n        tableSelector.setColumnList(false);\n        tableSelector.setIndexList(false);\n        queryParam.setPageNo(1);\n        queryParam.setPageSize(1000);\n        PageResult<Table> tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);\n\n        List<Table> tables = tableDTOPageResult.getData();\n        if (CollectionUtils.isEmpty(tables)) {\n            return ActionResult.isSuccess();\n        }\n        String tableName = tables.get(0).getName();\n        String tableSchema = queryTableDdlByEs(tableName, request);\n        request.setTableName(tableName);\n        request.setTableSchemaContent(tableSchema);\n\n        if (StringUtils.isBlank(tableSchema)) {\n            throw new ParamBusinessException(\"tableSchema is empty\");\n        }\n\n        // save first table embedding\n        request.setDeleteBeforeInsert(true);\n        saveTableEs(request);\n\n        // save other table embedding\n        request.setDeleteBeforeInsert(false);\n        for (int i = 1; i < tables.size(); i++) {\n            tableName = tables.get(i).getName();\n            tableSchema = queryTableDdlByEs(tableName, request);\n            if (StringUtils.isBlank(tableSchema)) {\n                continue;\n            }\n            request.setTableName(tableName);\n            request.setTableSchemaContent(tableSchema);\n            saveTableEs(request);\n        }\n\n        // query all the tables\n        Long totalTableCount = tableDTOPageResult.getTotal();\n        Integer pageSize = queryParam.getPageSize();\n        if (pageSize < totalTableCount) {\n            for (int i = 2; i < totalTableCount/pageSize + 1; i++) {\n                queryParam.setPageNo(i);\n                tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);\n                tables = tableDTOPageResult.getData();\n                for (Table table : tables) {\n                    tableName = table.getName();\n                    tableSchema = queryTableDdlByEs(tableName, request);\n                    if (StringUtils.isBlank(tableSchema)) {\n                        continue;\n                    }\n                    request.setTableName(tableName);\n                    request.setTableSchemaContent(tableSchema);\n                    saveTableEs(request);\n                }\n            }\n        }\n\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * sync table vector\n     *\n     * @param param\n     */\n    public void syncTableVector(TableBriefQueryRequest param) throws Exception {\n        TableVectorParam vectorParam = rdbWebConverter.param2param(param);\n        if (Objects.isNull(vectorParam.getDataSourceId())) {\n            return;\n        }\n        if (StringUtils.isBlank(vectorParam.getDatabase()) && StringUtils.isBlank(vectorParam.getSchema())) {\n            return;\n        }\n\n        String apiKey = getApiKey();\n        if (StringUtils.isBlank(apiKey)) {\n            return;\n        }\n\n        TableMilvusQueryRequest request = rdbWebConverter.request2request(param);\n        request.setApikey(apiKey);\n\n        vectorParam.setApiKey(apiKey);\n        DataResult<Boolean> result = tableService.checkTableVector(vectorParam);\n        if (result.getData()) {\n            return;\n        }\n\n        // check if in white list\n        boolean res = gatewayClientService.checkInWhite(new WhiteListRequest(apiKey, WhiteListTypeEnum.VECTOR.getCode())).getData();\n        if (!res) {\n            return;\n        }\n\n        embeddings(request);\n\n        tableService.saveTableVector(vectorParam);\n    }\n\n    /**\n     * save table embedding\n     *\n     * @param tableSchema\n     * @param tableSchemaRequest\n     * @throws Exception\n     */\n    private void saveTableEmbedding(String tableSchema, TableSchemaRequest tableSchemaRequest) throws Exception{\n        List<String> schemaList = Lists.newArrayList(tableSchema);\n        tableSchemaRequest.setSchemaList(schemaList);\n\n        List<List<BigDecimal>> contentVector = new ArrayList<>();\n        for(String str : schemaList){\n            // request embedding\n            FastChatEmbeddingResponse response = distributeAIEmbedding(str);\n            if(response == null){\n                throw new ParamBusinessException();\n            }\n            contentVector.add(response.getData().get(0).getEmbedding());\n        }\n        if (CollectionUtils.isEmpty(contentVector)) {\n            throw new ParamBusinessException();\n        }\n        tableSchemaRequest.setSchemaVector(contentVector);\n\n        // save table embedding\n        gatewayClientService.schemaVectorSave(tableSchemaRequest);\n    }\n\n    /**\n     * sync table vector\n     *\n     * @param param\n     */\n    public void syncTableEs(TableBriefQueryRequest param) throws Exception {\n        EsTableSchemaRequest esParam = rdbWebConverter.req2req(param);\n        if (Objects.isNull(esParam.getDataSourceId())) {\n            return;\n        }\n        if (StringUtils.isBlank(esParam.getDatabaseName()) && StringUtils.isBlank(esParam.getSchemaName())) {\n            return;\n        }\n\n        String apiKey = getApiKey();\n        if (StringUtils.isBlank(apiKey)) {\n            return;\n        }\n\n        esParam.setApiKey(apiKey);\n        es(esParam);\n    }\n\n    /**\n     * save table schema\n     *\n     * @param tableSchemaRequest\n     * @throws Exception\n     */\n    private void saveTableEs(EsTableSchemaRequest tableSchemaRequest) throws Exception{\n        // save table es\n        gatewayClientService.schemaEsSave(tableSchemaRequest);\n    }\n\n    /**\n     * query table schema\n     *\n     * @param tableName\n     * @param request\n     * @return\n     */\n    private String queryTableDdl(String tableName, TableBriefQueryRequest request) {\n        ShowCreateTableParam param = new ShowCreateTableParam();\n        param.setTableName(tableName);\n        param.setDataSourceId(request.getDataSourceId());\n        param.setDatabaseName(request.getDatabaseName());\n        param.setSchemaName(request.getSchemaName());\n        DataResult<String> tableSchema = tableService.showCreateTable(param);\n        return tableSchema.getData();\n    }\n\n    /**\n     * query table schema\n     *\n     * @param tableName\n     * @param request\n     * @return\n     */\n    private String queryTableDdlByEs(String tableName, EsTableSchemaRequest request) {\n        ShowCreateTableParam param = new ShowCreateTableParam();\n        param.setTableName(tableName);\n        param.setDataSourceId(request.getDataSourceId());\n        param.setDatabaseName(request.getDatabaseName());\n        param.setSchemaName(request.getSchemaName());\n        DataResult<String> tableSchema = tableService.showCreateTable(param);\n        return tableSchema.getData();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/KnowledgeController.java",
    "content": "package ai.chat2db.server.web.api.controller.ai;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.DocParser.AbstractParser;\nimport ai.chat2db.server.web.api.controller.ai.DocParser.PdfParse;\nimport ai.chat2db.server.web.api.controller.ai.enums.PromptType;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;\nimport ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;\nimport ai.chat2db.server.web.api.http.GatewayClientService;\nimport ai.chat2db.server.web.api.http.model.Knowledge;\nimport ai.chat2db.server.web.api.http.request.KnowledgeRequest;\nimport ai.chat2db.server.web.api.http.response.KnowledgeResponse;\nimport cn.hutool.core.util.StrUtil;\nimport com.alibaba.fastjson2.JSON;\nimport jakarta.annotation.Resource;\nimport jakarta.servlet.http.HttpServletRequest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author moji\n */\n@RestController\n@ConnectionInfoAspect\n@RequestMapping(\"/api/ai/knowledge\")\n@Slf4j\npublic class KnowledgeController extends ChatController {\n\n\n    /**\n     * chat timeout\n     */\n    private static final Long CHAT_TIMEOUT = Duration.ofMinutes(50).toMillis();\n\n\n    @Resource\n    private GatewayClientService gatewayClientService;\n\n    /**\n     * save knowledge from pdf file\n     *\n     * @param file\n     * @return\n     * @throws IOException\n     */\n    @PostMapping(\"/embeddings\")\n    @CrossOrigin\n    public ActionResult embeddings(MultipartFile file, HttpServletRequest request)\n        throws Exception {\n        AbstractParser pdfParse = new PdfParse();\n        List<String> sentenceList = pdfParse.parse(file.getInputStream());\n\n        List<Integer> contentWordCount = new ArrayList<>();\n        List<List<BigDecimal>> contentVector = new ArrayList<>();\n        for(String str : sentenceList){\n            contentWordCount.add(str.length());\n\n            // request embedding\n            FastChatEmbeddingResponse response = distributeAIEmbedding(str);\n            if(response == null){\n                continue;\n            }\n            contentVector.add(response.getData().get(0).getEmbedding());\n        }\n\n        KnowledgeRequest knowledgeRequest = new KnowledgeRequest();\n        knowledgeRequest.setContentVector(contentVector);\n        knowledgeRequest.setSentenceList(sentenceList);\n        // save knowledge embedding\n        ActionResult actionResult = gatewayClientService.knowledgeVectorSave(knowledgeRequest);\n        return actionResult;\n    }\n\n    /**\n     * search knowledge\n     *\n     * @param queryRequest\n     * @return\n     * @throws IOException\n     */\n    @GetMapping(\"/search\")\n    @CrossOrigin\n    public SseEmitter search(ChatQueryRequest queryRequest, @RequestHeader Map<String, String> headers)\n            throws Exception {\n        // request embedding\n        FastChatEmbeddingResponse response = distributeAIEmbedding(queryRequest.getMessage());\n        List<List<BigDecimal>> contentVector = new ArrayList<>();\n        contentVector.add(response.getData().get(0).getEmbedding());\n\n        // search embedding\n        KnowledgeRequest knowledgeRequest = new KnowledgeRequest();\n        knowledgeRequest.setContentVector(contentVector);\n        DataResult<KnowledgeResponse> result = gatewayClientService.knowledgeVectorSearch(knowledgeRequest);\n        queryRequest.setPromptType(PromptType.TEXT_GENERATION.getCode());\n        String prompt = queryRequest.getMessage();\n        if (CollectionUtils.isNotEmpty(result.getData().getKnowledgeList())) {\n            List<String> contents = new ArrayList<>();\n            for(Knowledge data: result.getData().getKnowledgeList()){\n                contents.add(data.getContent());\n            }\n\n            prompt = String.format(\"Based on %s. Please answer %s.\", JSON.toJSONString(contents), prompt);\n            queryRequest.setMessage(prompt);\n        }\n\n        // chat with AI\n        SseEmitter sseEmitter = new SseEmitter(CHAT_TIMEOUT);\n        String uid = headers.get(\"uid\");\n        if (StrUtil.isBlank(uid)) {\n            throw new ParamBusinessException(\"uid\");\n        }\n\n        if (StringUtils.isBlank(queryRequest.getMessage())) {\n            throw new ParamBusinessException(\"message\");\n        }\n\n        return distributeAISql(queryRequest, sseEmitter, uid);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/TextGenerationController.java",
    "content": "package ai.chat2db.server.web.api.controller.ai;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.enums.PromptType;\nimport ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;\nimport ai.chat2db.server.web.api.http.GatewayClientService;\nimport cn.hutool.core.util.StrUtil;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.Map;\n\n/**\n * @author moji\n */\n@RestController\n@ConnectionInfoAspect\n@RequestMapping(\"/api/ai/text/generation\")\n@Slf4j\npublic class TextGenerationController extends ChatController {\n\n\n    /**\n     * chat timeout time\n     */\n    private static final Long CHAT_TIMEOUT = Duration.ofMinutes(50).toMillis();\n\n\n    @Resource\n    private GatewayClientService gatewayClientService;\n\n    /**\n     * sql auto complete\n     *\n     * @param queryRequest\n     * @return\n     * @throws IOException\n     */\n    @GetMapping(\"/prompt\")\n    @CrossOrigin\n    public SseEmitter prompt(ChatQueryRequest queryRequest, @RequestHeader Map<String, String> headers)\n            throws Exception {\n        queryRequest.setPromptType(PromptType.TEXT_GENERATION.getCode());\n\n        String promptTemplate = \"### Instructions:\\n\" +\n                \"Your task is generate a SQL query according to the prompt %s.\\n\" +\n                \"Adhere to these rules:\\n\" +\n                \"- **Deliberately go through the prompt and database schema word by word** to appropriately answer the question\\n\" +\n                \"- **Use Table Aliases** to prevent ambiguity. For example, `SELECT table1.col1, table2.col1 FROM table1 JOIN table2 ON table1.id = table2.id`.\\n\" +\n                \"\\n\" +\n                \"### Input:\\n\" +\n                \"Generate a SQL query according to the prompt `%s`.\\n\" +\n                \"%s\\n\" +\n                \"\\n\" +\n                \"### Response:\\n\" +\n                \"Based on your instructions, here is the SQL query I have generated to complete the prompt `{%s}`:\\n\" +\n                \"```sql\";\n\n        // query database schema info\n        String databaseType = queryDatabaseType(queryRequest);\n        String schemas = queryDatabaseSchema(queryRequest);\n        if (StringUtils.isNotBlank(schemas)) {\n            databaseType = String.format(\", given a %s database schema\", databaseType);\n            schemas = String.format(\"This query will run on a database whose schema is represented in this string:\\n$s\", schemas);\n        } else  {\n            databaseType = \"\";\n            schemas = \"\";\n        }\n        String prompt = String.format(promptTemplate, databaseType, queryRequest.getMessage(), schemas, queryRequest.getMessage());\n        queryRequest.setMessage(prompt);\n\n        // chat with AI\n        SseEmitter sseEmitter = new SseEmitter(CHAT_TIMEOUT);\n        String uid = headers.get(\"uid\");\n        if (StrUtil.isBlank(uid)) {\n            throw new ParamBusinessException(\"uid\");\n        }\n\n        if (StringUtils.isBlank(queryRequest.getMessage())) {\n            throw new ParamBusinessException(\"message\");\n        }\n\n        return distributeAISql(queryRequest, sseEmitter, uid);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/client/AzureOpenAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.azure.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author jipengfei\n * @version : OpenAIClient.java\n */\n@Slf4j\npublic class AzureOpenAIClient {\n\n    /**\n     * AZURE OPENAI KEY\n     */\n    public static final String AZURE_CHATGPT_API_KEY = \"azure.chatgpt.apiKey\";\n\n    /**\n     * AZURE OPENAI ENDPOINT\n     */\n    public static final String AZURE_CHATGPT_ENDPOINT = \"azure.chatgpt.endpoint\";\n\n    /**\n     * AZURE OPENAI DEPLOYMENT ID\n     */\n    public static final String AZURE_CHATGPT_DEPLOYMENT_ID = \"azure.chatgpt.deployment.id\";\n\n    private static AzureOpenAiStreamClient OPEN_AI_CLIENT;\n    private static String apiKey;\n\n    public static AzureOpenAiStreamClient getInstance() {\n        if (OPEN_AI_CLIENT != null) {\n            return OPEN_AI_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static AzureOpenAiStreamClient singleton() {\n        if (OPEN_AI_CLIENT == null) {\n            synchronized (AzureOpenAIClient.class) {\n                if (OPEN_AI_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return OPEN_AI_CLIENT;\n    }\n\n    public static void refresh() {\n        String key = \"\";\n        String apiEndpoint = \"\";\n        String deployId = \"gpt-3.5-turbo\";\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(AZURE_CHATGPT_ENDPOINT).getData();\n        if (apiHostConfig != null && StringUtils.isNotBlank(apiHostConfig.getContent())) {\n            apiEndpoint = apiHostConfig.getContent();\n        }\n        Config config = configService.find(AZURE_CHATGPT_API_KEY).getData();\n        if (config != null && StringUtils.isNotBlank(config.getContent())) {\n            key = config.getContent();\n        }\n        Config deployConfig = configService.find(AZURE_CHATGPT_DEPLOYMENT_ID).getData();\n        if (deployConfig != null && StringUtils.isNotBlank(deployConfig.getContent())) {\n            deployId = deployConfig.getContent();\n        }\n        log.info(\"refresh azure openai apikey:{}\", maskApiKey(key));\n        OPEN_AI_CLIENT = AzureOpenAiStreamClient.builder().apiKey(key).endpoint(apiEndpoint).deployId(deployId)\n            .build();\n        apiKey = key;\n    }\n\n    private static String maskApiKey(String input) {\n        if (input == null) {\n            return input;\n        }\n\n        StringBuilder maskedString = new StringBuilder(input);\n        for (int i = input.length() / 2; i < input.length() / 2; i++) {\n            maskedString.setCharAt(i, '*');\n        }\n        return maskedString.toString();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/client/AzureOpenAiStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.azure.client;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.azure.interceptor.AzureHeaderAuthorizationInterceptor;\nimport ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatCompletionsOptions;\nimport ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatMessage;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport okhttp3.sse.EventSources;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * Custom AI interface client\n *\n * @author moji\n */\n@Slf4j\npublic class AzureOpenAiStreamClient {\n\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String apiKey;\n\n    /**\n     * endpoint\n     */\n    @Getter\n    @NotNull\n    private String endpoint;\n\n    /**\n     * deployId\n     */\n    @Getter\n    private String deployId;\n\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n\n    /**\n     * @param builder\n     */\n    private AzureOpenAiStreamClient(Builder builder) {\n        this.apiKey = builder.apiKey;\n        this.endpoint = builder.endpoint;\n        this.deployId = builder.deployId;\n        if (Objects.isNull(builder.okHttpClient)) {\n            builder.okHttpClient = this.okHttpClient();\n        }\n        okHttpClient = builder.okHttpClient;\n    }\n\n    /**\n     * okhttpclient\n     */\n    private OkHttpClient okHttpClient() {\n        OkHttpClient okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new AzureHeaderAuthorizationInterceptor(this.apiKey))\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n        return okHttpClient;\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static AzureOpenAiStreamClient.Builder builder() {\n        return new AzureOpenAiStreamClient.Builder();\n    }\n\n    public static final class Builder {\n        private String apiKey;\n\n        private String endpoint;\n\n        private String deployId;\n\n        /**\n         * Customize OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public AzureOpenAiStreamClient.Builder apiKey(String apiKeyValue) {\n            this.apiKey = apiKeyValue;\n            return this;\n        }\n\n        /**\n         * @param endpointValue\n         * @return\n         */\n        public AzureOpenAiStreamClient.Builder endpoint(String endpointValue) {\n            this.endpoint = endpointValue;\n            return this;\n        }\n\n        /**\n         * @param deployIdValue\n         * @return\n         */\n        public AzureOpenAiStreamClient.Builder deployId(String deployIdValue) {\n            this.deployId = deployIdValue;\n            return this;\n        }\n\n        public AzureOpenAiStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public AzureOpenAiStreamClient build() {\n            return new AzureOpenAiStreamClient(this);\n        }\n\n    }\n\n    /**\n     * Q&A interface stream form\n     *\n     * @param chatMessages\n     * @param eventSourceListener\n     */\n    public void streamCompletions(List<AzureChatMessage> chatMessages, EventSourceListener eventSourceListener) {\n        if (CollectionUtils.isEmpty(chatMessages)) {\n            log.error(\"param error：Azure Prompt cannot be empty\");\n            throw new ParamBusinessException(\"prompt\");\n        }\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：AzureEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        log.info(\"Azure Open AI, prompt:{}\", chatMessages.get(chatMessages.size() - 1).getContent());\n        try {\n\n            AzureChatCompletionsOptions chatCompletionsOptions = new AzureChatCompletionsOptions(chatMessages);\n            chatCompletionsOptions.setStream(true);\n            chatCompletionsOptions.setModel(this.deployId);\n\n            EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(chatCompletionsOptions);\n            if (!endpoint.endsWith(\"/\")) {\n                endpoint = endpoint + \"/\";\n            }\n            String url = this.endpoint + \"openai/deployments/\"+ deployId + \"/chat/completions?api-version=2023-05-15\";\n            Request request = new Request.Builder()\n                .url(url)\n                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                .build();\n            //Create event\n            EventSource eventSource = factory.newEventSource(request, eventSourceListener);\n            log.info(\"finish invoking azure ai\");\n        } catch (Exception e) {\n            log.error(\"azure ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/interceptor/AzureHeaderAuthorizationInterceptor.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.azure.interceptor;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport cn.hutool.core.util.RandomUtil;\nimport cn.hutool.http.ContentType;\nimport cn.hutool.http.Header;\nimport lombok.Getter;\nimport okhttp3.Interceptor;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\n/**\n * Description: Request to add header apikey\n *\n * @author grt\n * @since 2023-03-23\n */\n@Getter\npublic class AzureHeaderAuthorizationInterceptor implements Interceptor {\n\n    private String apiKey;\n\n    public AzureHeaderAuthorizationInterceptor(String apiKey) {\n        this.apiKey = apiKey;\n    }\n\n    @Override\n    public Response intercept(Chain chain) throws IOException {\n        Request original = chain.request();\n        Request request = original.newBuilder()\n                .header(\"api-key\", apiKey)\n                .header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue())\n                .method(original.method(), original.body())\n                .build();\n        return chain.proceed(request);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/listener/AzureOpenAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.azure.listener;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\nimport ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatChoice;\nimport ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatCompletions;\nimport ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.azure.model.AzureCompletionsUsage;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\n/**\n * description：OpenAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class AzureOpenAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n\n    public AzureOpenAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"AzureOpenAI建立sse连接...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"AzureOpenAI returns data: {}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"AzureOpenAI returns data ended\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n\n        AzureChatCompletions chatCompletions = mapper.readValue(data, AzureChatCompletions.class);\n        String text = \"\";\n        log.info(\"Model ID={} is created at {}.\", chatCompletions.getId(),\n            chatCompletions.getCreated());\n        for (AzureChatChoice choice : chatCompletions.getChoices()) {\n            AzureChatMessage message = choice.getDelta();\n            if (message != null) {\n                log.info(\"Index: {}, Chat Role: {}\", choice.getIndex(), message.getRole());\n                if (message.getContent() != null) {\n                    text = message.getContent();\n                }\n            }\n        }\n\n        AzureCompletionsUsage usage = chatCompletions.getUsage();\n        if (usage != null) {\n            log.info(\n                \"Usage: number of prompt token is {}, number of completion token is {}, and number of total \"\n                    + \"tokens in request and response is {}.%n\", usage.getPromptTokens(),\n                usage.getCompletionTokens(), usage.getTotalTokens());\n        }\n\n        Message message = new Message();\n        message.setContent(text);\n        sseEmitter.send(SseEmitter.event()\n            .id(null)\n            .data(message)\n            .reconnectTime(3000));\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        try {\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        sseEmitter.complete();\n        log.info(\"AzureOpenAI close sse connection...\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = Objects.nonNull(t) ? t.getMessage() : \"\";\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                if (StringUtils.isBlank(bodyString) && Objects.nonNull(t)) {\n                    bodyString = t.getMessage();\n                }\n                log.error(\"Azure OpenAI sse response：{}\", bodyString);\n            } else {\n                log.error(\"Azure OpenAI sse response：{}，error：{}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"Azure OpenAI error：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Azure OpenAI sends data abnormally:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatChoice.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n/**\n * The representation of a single prompt completion as part of an overall chat completions request. Generally, `n`\n * choices are generated per provided prompt with a default value of 1. Token limits and other settings may limit the\n * number of choices generated.\n */\n@Data\npublic final class AzureChatChoice {\n\n    /*\n     * The chat message for a given chat completions prompt.\n     */\n    @JsonProperty(value = \"message\")\n    private AzureChatMessage message;\n\n    /*\n     * The ordered index associated with this chat completions choice.\n     */\n    @JsonProperty(value = \"index\")\n    private int index;\n\n    /*\n     * The reason that this chat completions choice completed its generated.\n     */\n    @JsonProperty(value = \"finish_reason\")\n    private AzureCompletionsFinishReason finishReason;\n\n    /*\n     * The delta message content for a streaming response.\n     */\n    @JsonProperty(value = \"delta\")\n    private AzureChatMessage delta;\n\n    /**\n     * Creates an instance of ChatChoice class.\n     *\n     * @param index the index value to set.\n     * @param finishReason the finishReason value to set.\n     */\n    @JsonCreator\n    private AzureChatChoice(\n            @JsonProperty(value = \"index\") int index,\n            @JsonProperty(value = \"finish_reason\") AzureCompletionsFinishReason finishReason) {\n        this.index = index;\n        this.finishReason = finishReason;\n    }\n\n    /**\n     * Get the message property: The chat message for a given chat completions prompt.\n     *\n     * @return the message value.\n     */\n    public AzureChatMessage getMessage() {\n        return this.message;\n    }\n\n    /**\n     * Get the index property: The ordered index associated with this chat completions choice.\n     *\n     * @return the index value.\n     */\n    public int getIndex() {\n        return this.index;\n    }\n\n    /**\n     * Get the finishReason property: The reason that this chat completions choice completed its generated.\n     *\n     * @return the finishReason value.\n     */\n    public AzureCompletionsFinishReason getFinishReason() {\n        return this.finishReason;\n    }\n\n    /**\n     * Get the delta property: The delta message content for a streaming response.\n     *\n     * @return the delta value.\n     */\n    public AzureChatMessage getDelta() {\n        return this.delta;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatCompletions.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n@Data\npublic class AzureChatCompletions {\n\n    /*\n     * A unique identifier associated with this chat completions response.\n     */\n    private String id;\n\n    /*\n     * The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     */\n    private int created;\n\n    /*\n     * The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1.\n     * Token limits and other settings may limit the number of choices generated.\n     */\n    @JsonProperty(value = \"choices\")\n    private List<AzureChatChoice> choices;\n\n    /*\n     * Usage information for tokens processed and generated as part of this completions operation.\n     */\n    private AzureCompletionsUsage usage;\n\n    /**\n     * Creates an instance of ChatCompletions class.\n     *\n     * @param id the id value to set.\n     * @param created the created value to set.\n     * @param choices the choices value to set.\n     * @param usage the usage value to set.\n     */\n    @JsonCreator\n    private AzureChatCompletions(\n        @JsonProperty(value = \"id\") String id,\n        @JsonProperty(value = \"created\") int created,\n        @JsonProperty(value = \"choices\") List<AzureChatChoice> choices,\n        @JsonProperty(value = \"usage\") AzureCompletionsUsage usage) {\n        this.id = id;\n        this.created = created;\n        this.choices = choices;\n        this.usage = usage;\n    }\n\n    /**\n     * Get the id property: A unique identifier associated with this chat completions response.\n     *\n     * @return the id value.\n     */\n    public String getId() {\n        return this.id;\n    }\n\n    /**\n     * Get the created property: The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     *\n     * @return the created value.\n     */\n    public int getCreated() {\n        return this.created;\n    }\n\n    /**\n     * Get the choices property: The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1. Token limits and other\n     * settings may limit the number of choices generated.\n     *\n     * @return the choices value.\n     */\n    public List<AzureChatChoice> getChoices() {\n        return this.choices;\n    }\n\n    /**\n     * Get the usage property: Usage information for tokens processed and generated as part of this completions\n     * operation.\n     *\n     * @return the usage value.\n     */\n    public AzureCompletionsUsage getUsage() {\n        return this.usage;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatCompletionsOptions.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n/**\n * The configuration information for a chat completions request. Completions support a wide variety of tasks and\n * generate text that continues from or \"completes\" provided prompt data.\n */\n@Data\npublic final class AzureChatCompletionsOptions {\n\n    /*\n     * The collection of context messages associated with this chat completions request.\n     * Typical usage begins with a chat message for the System role that provides instructions for\n     * the behavior of the assistant, followed by alternating messages between the User and\n     * Assistant roles.\n     */\n    private List<AzureChatMessage> messages;\n\n    ///*\n    // * The maximum number of tokens to generate.\n    // */\n    ////@JsonProperty(value = \"max_tokens\")\n    //private Integer maxTokens;\n    //\n    ///*\n    // * The sampling temperature to use that controls the apparent creativity of generated completions.\n    // * Higher values will make output more random while lower values will make results more focused\n    // * and deterministic.\n    // * It is not recommended to modify temperature and top_p for the same completions request as the\n    // * interaction of these two settings is difficult to predict.\n    // */\n    ////@JsonProperty(value = \"temperature\")\n    //private Double temperature;\n    //\n    ///*\n    // * An alternative to sampling with temperature called nucleus sampling. This value causes the\n    // * model to consider the results of tokens with the provided probability mass. As an example, a\n    // * value of 0.15 will cause only the tokens comprising the top 15% of probability mass to be\n    // * considered.\n    // * It is not recommended to modify temperature and top_p for the same completions request as the\n    // * interaction of these two settings is difficult to predict.\n    // */\n    ////@JsonProperty(value = \"top_p\")\n    //private Double topP;\n    //\n    ///*\n    // * A map between GPT token IDs and bias scores that influences the probability of specific tokens\n    // * appearing in a completions response. Token IDs are computed via external tokenizer tools, while\n    // * bias scores reside in the range of -100 to 100 with minimum and maximum values corresponding to\n    // * a full ban or exclusive selection of a token, respectively. The exact behavior of a given bias\n    // * score varies by model.\n    // */\n    ////@JsonProperty(value = \"logit_bias\")\n    //private Map<String, Integer> logitBias;\n    //\n    ///*\n    // * An identifier for the caller or end user of the operation. This may be used for tracking\n    // * or rate-limiting purposes.\n    // */\n    ////@JsonProperty(value = \"user\")\n    //private String user;\n    //\n    ///*\n    // * The number of chat completions choices that should be generated for a chat completions\n    // * response.\n    // * Because this setting can generate many completions, it may quickly consume your token quota.\n    // * Use carefully and ensure reasonable settings for max_tokens and stop.\n    // */\n    ////@JsonProperty(value = \"n\")\n    //private Integer n;\n    //\n    ///*\n    // * A collection of textual sequences that will end completions generation.\n    // */\n    ////@JsonProperty(value = \"stop\")\n    //private List<String> stop;\n    //\n    ///*\n    // * A value that influences the probability of generated tokens appearing based on their existing\n    // * presence in generated text.\n    // * Positive values will make tokens less likely to appear when they already exist and increase the\n    // * model's likelihood to output new topics.\n    // */\n    ////@JsonProperty(value = \"presence_penalty\")\n    //private Double presencePenalty;\n    //\n    ///*\n    // * A value that influences the probability of generated tokens appearing based on their cumulative\n    // * frequency in generated text.\n    // * Positive values will make tokens less likely to appear as their frequency increases and\n    // * decrease the likelihood of the model repeating the same statements verbatim.\n    // */\n    ////@JsonProperty(value = \"frequency_penalty\")\n    //private Double frequencyPenalty;\n\n    /*\n     * A value indicating whether chat completions should be streamed for this request.\n     */\n    //@JsonProperty(value = \"stream\")\n    private Boolean stream;\n    //\n    /*\n     * The model name to provide as part of this completions request.\n     * Not applicable to Azure OpenAI, where deployment information should be included in the Azure\n     * resource URI that's connected to.\n     */\n    //@JsonProperty(value = \"model\")\n    private String model;\n\n    /**\n     * Creates an instance of ChatCompletionsOptions class.\n     *\n     * @param messages the messages value to set.\n     */\n    @JsonCreator\n    public AzureChatCompletionsOptions(@JsonProperty(value = \"messages\") List<AzureChatMessage> messages) {\n        this.messages = messages;\n    }\n    //\n    ///**\n    // * Get the messages property: The collection of context messages associated with this chat completions request.\n    // * Typical usage begins with a chat message for the System role that provides instructions for the behavior of the\n    // * assistant, followed by alternating messages between the User and Assistant roles.\n    // *\n    // * @return the messages value.\n    // */\n    //public List<AzureChatMessage> getMessages() {\n    //    return this.messages;\n    //}\n    //\n    ///**\n    // * Get the maxTokens property: The maximum number of tokens to generate.\n    // *\n    // * @return the maxTokens value.\n    // */\n    //public Integer getMaxTokens() {\n    //    return this.maxTokens;\n    //}\n    //\n    ///**\n    // * Set the maxTokens property: The maximum number of tokens to generate.\n    // *\n    // * @param maxTokens the maxTokens value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setMaxTokens(Integer maxTokens) {\n    //    this.maxTokens = maxTokens;\n    //    return this;\n    //}\n    //\n    ///**\n    // * Get the temperature property: The sampling temperature to use that controls the apparent creativity of generated\n    // * completions. Higher values will make output more random while lower values will make results more focused and\n    // * deterministic. It is not recommended to modify temperature and top_p for the same completions request as the\n    // * interaction of these two settings is difficult to predict.\n    // *\n    // * @return the temperature value.\n    // */\n    //public Double getTemperature() {\n    //    return this.temperature;\n    //}\n    //\n    ///**\n    // * Set the temperature property: The sampling temperature to use that controls the apparent creativity of generated\n    // * completions. Higher values will make output more random while lower values will make results more focused and\n    // * deterministic. It is not recommended to modify temperature and top_p for the same completions request as the\n    // * interaction of these two settings is difficult to predict.\n    // *\n    // * @param temperature the temperature value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setTemperature(Double temperature) {\n    //    this.temperature = temperature;\n    //    return this;\n    //}\n    //\n    ///**\n    // * Get the topP property: An alternative to sampling with temperature called nucleus sampling. This value causes the\n    // * model to consider the results of tokens with the provided probability mass. As an example, a value of 0.15 will\n    // * cause only the tokens comprising the top 15% of probability mass to be considered. It is not recommended to\n    // * modify temperature and top_p for the same completions request as the interaction of these two settings is\n    // * difficult to predict.\n    // *\n    // * @return the topP value.\n    // */\n    //public Double getTopP() {\n    //    return this.topP;\n    //}\n    //\n    ///**\n    // * Set the topP property: An alternative to sampling with temperature called nucleus sampling. This value causes the\n    // * model to consider the results of tokens with the provided probability mass. As an example, a value of 0.15 will\n    // * cause only the tokens comprising the top 15% of probability mass to be considered. It is not recommended to\n    // * modify temperature and top_p for the same completions request as the interaction of these two settings is\n    // * difficult to predict.\n    // *\n    // * @param topP the topP value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setTopP(Double topP) {\n    //    this.topP = topP;\n    //    return this;\n    //}\n    //\n    ///**\n    // * Get the logitBias property: A map between GPT token IDs and bias scores that influences the probability of\n    // * specific tokens appearing in a completions response. Token IDs are computed via external tokenizer tools, while\n    // * bias scores reside in the range of -100 to 100 with minimum and maximum values corresponding to a full ban or\n    // * exclusive selection of a token, respectively. The exact behavior of a given bias score varies by model.\n    // *\n    // * @return the logitBias value.\n    // */\n    //public Map<String, Integer> getLogitBias() {\n    //    return this.logitBias;\n    //}\n    //\n    ///**\n    // * Set the logitBias property: A map between GPT token IDs and bias scores that influences the probability of\n    // * specific tokens appearing in a completions response. Token IDs are computed via external tokenizer tools, while\n    // * bias scores reside in the range of -100 to 100 with minimum and maximum values corresponding to a full ban or\n    // * exclusive selection of a token, respectively. The exact behavior of a given bias score varies by model.\n    // *\n    // * @param logitBias the logitBias value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setLogitBias(Map<String, Integer> logitBias) {\n    //    this.logitBias = logitBias;\n    //    return this;\n    //}\n    //\n    ///**\n    // * Get the user property: An identifier for the caller or end user of the operation. This may be used for tracking\n    // * or rate-limiting purposes.\n    // *\n    // * @return the user value.\n    // */\n    //public String getUser() {\n    //    return this.user;\n    //}\n    //\n    ///**\n    // * Set the user property: An identifier for the caller or end user of the operation. This may be used for tracking\n    // * or rate-limiting purposes.\n    // *\n    // * @param user the user value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setUser(String user) {\n    //    this.user = user;\n    //    return this;\n    //}\n    //\n    ///**\n    // * Get the n property: The number of chat completions choices that should be generated for a chat completions\n    // * response. Because this setting can generate many completions, it may quickly consume your token quota. Use\n    // * carefully and ensure reasonable settings for max_tokens and stop.\n    // *\n    // * @return the n value.\n    // */\n    //public Integer getN() {\n    //    return this.n;\n    //}\n    //\n    ///**\n    // * Set the n property: The number of chat completions choices that should be generated for a chat completions\n    // * response. Because this setting can generate many completions, it may quickly consume your token quota. Use\n    // * carefully and ensure reasonable settings for max_tokens and stop.\n    // *\n    // * @param n the n value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setN(Integer n) {\n    //    this.n = n;\n    //    return this;\n    //}\n    //\n    ///**\n    // * Get the stop property: A collection of textual sequences that will end completions generation.\n    // *\n    // * @return the stop value.\n    // */\n    //public List<String> getStop() {\n    //    return this.stop;\n    //}\n    //\n    ///**\n    // * Set the stop property: A collection of textual sequences that will end completions generation.\n    // *\n    // * @param stop the stop value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setStop(List<String> stop) {\n    //    this.stop = stop;\n    //    return this;\n    //}\n    //\n    ///**\n    // * Get the presencePenalty property: A value that influences the probability of generated tokens appearing based on\n    // * their existing presence in generated text. Positive values will make tokens less likely to appear when they\n    // * already exist and increase the model's likelihood to output new topics.\n    // *\n    // * @return the presencePenalty value.\n    // */\n    //public Double getPresencePenalty() {\n    //    return this.presencePenalty;\n    //}\n    //\n    ///**\n    // * Set the presencePenalty property: A value that influences the probability of generated tokens appearing based on\n    // * their existing presence in generated text. Positive values will make tokens less likely to appear when they\n    // * already exist and increase the model's likelihood to output new topics.\n    // *\n    // * @param presencePenalty the presencePenalty value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setPresencePenalty(Double presencePenalty) {\n    //    this.presencePenalty = presencePenalty;\n    //    return this;\n    //}\n    //\n    ///**\n    // * Get the frequencyPenalty property: A value that influences the probability of generated tokens appearing based on\n    // * their cumulative frequency in generated text. Positive values will make tokens less likely to appear as their\n    // * frequency increases and decrease the likelihood of the model repeating the same statements verbatim.\n    // *\n    // * @return the frequencyPenalty value.\n    // */\n    //public Double getFrequencyPenalty() {\n    //    return this.frequencyPenalty;\n    //}\n    //\n    ///**\n    // * Set the frequencyPenalty property: A value that influences the probability of generated tokens appearing based on\n    // * their cumulative frequency in generated text. Positive values will make tokens less likely to appear as their\n    // * frequency increases and decrease the likelihood of the model repeating the same statements verbatim.\n    // *\n    // * @param frequencyPenalty the frequencyPenalty value to set.\n    // * @return the ChatCompletionsOptions object itself.\n    // */\n    //public AzureChatCompletionsOptions setFrequencyPenalty(Double frequencyPenalty) {\n    //    this.frequencyPenalty = frequencyPenalty;\n    //    return this;\n    //}\n\n    /**\n     * Get the stream property: A value indicating whether chat completions should be streamed for this request.\n     *\n     * @return the stream value.\n     */\n    public Boolean isStream() {\n        return this.stream;\n    }\n\n    /**\n     * Set the stream property: A value indicating whether chat completions should be streamed for this request.\n     *\n     * @param stream the stream value to set.\n     * @return the ChatCompletionsOptions object itself.\n     */\n    public AzureChatCompletionsOptions setStream(Boolean stream) {\n        this.stream = stream;\n        return this;\n    }\n\n    /**\n     * Get the model property: The model name to provide as part of this completions request. Not applicable to Azure\n     * OpenAI, where deployment information should be included in the Azure resource URI that's connected to.\n     *\n     * @return the model value.\n     */\n    public String getModel() {\n        return this.model;\n    }\n\n    /**\n     * Set the model property: The model name to provide as part of this completions request. Not applicable to Azure\n     * OpenAI, where deployment information should be included in the Azure resource URI that's connected to.\n     *\n     * @param model the model value to set.\n     * @return the ChatCompletionsOptions object itself.\n     */\n    public AzureChatCompletionsOptions setModel(String model) {\n        this.model = model;\n        return this;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatMessage.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n@Data\npublic class AzureChatMessage {\n\n\n    /*\n     * The role associated with this message payload.\n     */\n    @JsonProperty(value = \"role\")\n    private AzureChatRole role;\n\n    /*\n     * The text associated with this message payload.\n     */\n    @JsonProperty(value = \"content\")\n    private String content;\n\n    /**\n     * Creates an instance of ChatMessage class.\n     *\n     * @param role the role value to set.\n     */\n    @JsonCreator\n    public AzureChatMessage(@JsonProperty(value = \"role\") AzureChatRole role) {\n        this.role = role;\n    }\n\n    /**\n     * Get the role property: The role associated with this message payload.\n     *\n     * @return the role value.\n     */\n    public AzureChatRole getRole() {\n        return this.role;\n    }\n\n    /**\n     * Get the content property: The text associated with this message payload.\n     *\n     * @return the content value.\n     */\n    public String getContent() {\n        return this.content;\n    }\n\n    /**\n     * Set the content property: The text associated with this message payload.\n     *\n     * @param content the content value to set.\n     * @return the ChatMessage object itself.\n     */\n    public AzureChatMessage setContent(String content) {\n        this.content = content;\n        return this;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatRole.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\n\npublic class AzureChatRole extends AzureExpandableStringEnum<AzureChatRole> {\n\n    /** The role that instructs or sets the behavior of the assistant. */\n    public static final AzureChatRole SYSTEM = fromString(\"system\");\n\n    /** The role that provides responses to system-instructed, user-prompted input. */\n    public static final AzureChatRole ASSISTANT = fromString(\"assistant\");\n\n    /** The role that provides input for chat completions. */\n    public static final AzureChatRole USER = fromString(\"user\");\n\n    /**\n     * Creates a new instance of ChatRole value.\n     *\n     * @deprecated Use the {@link #fromString(String)} factory method.\n     */\n    @Deprecated\n    public AzureChatRole() {}\n\n    /**\n     * Creates or finds a ChatRole from its string representation.\n     *\n     * @param name a name to look for.\n     * @return the corresponding ChatRole.\n     */\n    @JsonCreator\n    public static AzureChatRole fromString(String name) {\n        return fromString(name, AzureChatRole.class);\n    }\n\n\n    /**\n     * Gets known ChatRole values.\n     *\n     * @return known ChatRole values.\n     */\n    public static Collection<AzureChatRole> values() {\n        return values(AzureChatRole.class);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChoice.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n/**\n * The representation of a single prompt completion as part of an overall completions request. Generally, `n` choices\n * are generated per provided prompt with a default value of 1. Token limits and other settings may limit the number of\n * choices generated.\n */\n@Data\npublic final class AzureChoice {\n\n    /*\n     * The generated text for a given completions prompt.\n     */\n    @JsonProperty(value = \"text\")\n    private String text;\n\n    /*\n     * The ordered index associated with this completions choice.\n     */\n    @JsonProperty(value = \"index\")\n    private int index;\n\n    /*\n     * The log probabilities model for tokens associated with this completions choice.\n     */\n    @JsonProperty(value = \"logprobs\")\n    private AzureCompletionsLogProbabilityModel logprobs;\n\n    /*\n     * Reason for finishing\n     */\n    @JsonProperty(value = \"finish_reason\")\n    private AzureCompletionsFinishReason finishReason;\n\n    /**\n     * Creates an instance of Choice class.\n     *\n     * @param text the text value to set.\n     * @param index the index value to set.\n     * @param logprobs the logprobs value to set.\n     * @param finishReason the finishReason value to set.\n     */\n    @JsonCreator\n    private AzureChoice(\n            @JsonProperty(value = \"text\") String text,\n            @JsonProperty(value = \"index\") int index,\n            @JsonProperty(value = \"logprobs\") AzureCompletionsLogProbabilityModel logprobs,\n            @JsonProperty(value = \"finish_reason\") AzureCompletionsFinishReason finishReason) {\n        this.text = text;\n        this.index = index;\n        this.logprobs = logprobs;\n        this.finishReason = finishReason;\n    }\n\n    /**\n     * Get the text property: The generated text for a given completions prompt.\n     *\n     * @return the text value.\n     */\n    public String getText() {\n        return this.text;\n    }\n\n    /**\n     * Get the index property: The ordered index associated with this completions choice.\n     *\n     * @return the index value.\n     */\n    public int getIndex() {\n        return this.index;\n    }\n\n    /**\n     * Get the logprobs property: The log probabilities model for tokens associated with this completions choice.\n     *\n     * @return the logprobs value.\n     */\n    public AzureCompletionsLogProbabilityModel getLogprobs() {\n        return this.logprobs;\n    }\n\n    /**\n     * Get the finishReason property: Reason for finishing.\n     *\n     * @return the finishReason value.\n     */\n    public AzureCompletionsFinishReason getFinishReason() {\n        return this.finishReason;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureCompletions.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n@Data\npublic class AzureCompletions {\n\n    /*\n     * A unique identifier associated with this completions response.\n     */\n    @JsonProperty(value = \"id\")\n    private String id;\n\n    /*\n     * The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     */\n    @JsonProperty(value = \"created\")\n    private int created;\n\n    /*\n     * The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1.\n     * Token limits and other settings may limit the number of choices generated.\n     */\n    @JsonProperty(value = \"choices\")\n    private List<AzureChoice> choices;\n\n    /*\n     * Usage information for tokens processed and generated as part of this completions operation.\n     */\n    @JsonProperty(value = \"usage\")\n    private AzureCompletionsUsage usage;\n\n    /**\n     * Creates an instance of Completions class.\n     *\n     * @param id the id value to set.\n     * @param created the created value to set.\n     * @param choices the choices value to set.\n     * @param usage the usage value to set.\n     */\n    @JsonCreator\n    private AzureCompletions(\n        @JsonProperty(value = \"id\") String id,\n        @JsonProperty(value = \"created\") int created,\n        @JsonProperty(value = \"choices\") List<AzureChoice> choices,\n        @JsonProperty(value = \"usage\") AzureCompletionsUsage usage) {\n        this.id = id;\n        this.created = created;\n        this.choices = choices;\n        this.usage = usage;\n    }\n\n    /**\n     * Get the id property: A unique identifier associated with this completions response.\n     *\n     * @return the id value.\n     */\n    public String getId() {\n        return this.id;\n    }\n\n    /**\n     * Get the created property: The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     *\n     * @return the created value.\n     */\n    public int getCreated() {\n        return this.created;\n    }\n\n    /**\n     * Get the choices property: The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1. Token limits and other\n     * settings may limit the number of choices generated.\n     *\n     * @return the choices value.\n     */\n    public List<AzureChoice> getChoices() {\n        return this.choices;\n    }\n\n    /**\n     * Get the usage property: Usage information for tokens processed and generated as part of this completions\n     * operation.\n     *\n     * @return the usage value.\n     */\n    public AzureCompletionsUsage getUsage() {\n        return this.usage;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureCompletionsFinishReason.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\n\n/** Representation of the manner in which a completions response concluded. */\npublic final class AzureCompletionsFinishReason extends AzureExpandableStringEnum<AzureCompletionsFinishReason> {\n\n    /** Completions ended normally and reached its end of token generation. */\n    public static final AzureCompletionsFinishReason STOPPED = fromString(\"stopped\");\n\n    /** Completions exhausted available token limits before generation could complete. */\n    public static final AzureCompletionsFinishReason TOKEN_LIMIT_REACHED = fromString(\"tokenLimitReached\");\n\n    /**\n     * Completions generated a response that was identified as potentially sensitive per content moderation policies.\n     */\n    public static final AzureCompletionsFinishReason CONTENT_FILTERED = fromString(\"contentFiltered\");\n\n    /**\n     * Creates a new instance of CompletionsFinishReason value.\n     *\n     * @deprecated Use the {@link #fromString(String)} factory method.\n     */\n    @Deprecated\n    public AzureCompletionsFinishReason() {}\n\n    /**\n     * Creates or finds a CompletionsFinishReason from its string representation.\n     *\n     * @param name a name to look for.\n     * @return the corresponding CompletionsFinishReason.\n     */\n    @JsonCreator\n    public static AzureCompletionsFinishReason fromString(String name) {\n        return fromString(name, AzureCompletionsFinishReason.class);\n    }\n\n    /**\n     * Gets known CompletionsFinishReason values.\n     *\n     * @return known CompletionsFinishReason values.\n     */\n    public static Collection<AzureCompletionsFinishReason> values() {\n        return values(AzureCompletionsFinishReason.class);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureCompletionsLogProbabilityModel.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n/** Representation of a log probabilities model for a completions generation. */\n@Data\npublic final class AzureCompletionsLogProbabilityModel {\n\n    /*\n     * The textual forms of tokens evaluated in this probability model.\n     */\n    @JsonProperty(value = \"tokens\")\n    private List<String> tokens;\n\n    /*\n     * A collection of log probability values for the tokens in this completions data.\n     */\n    @JsonProperty(value = \"token_logprobs\")\n    private List<Double> tokenLogprobs;\n\n    /*\n     * A mapping of tokens to maximum log probability values in this completions data.\n     */\n    @JsonProperty(value = \"top_logprobs\")\n    private List<Map<String, Double>> topLogprobs;\n\n    /*\n     * The text offsets associated with tokens in this completions data.\n     */\n    @JsonProperty(value = \"text_offset\")\n    private List<Integer> textOffset;\n\n    /**\n     * Creates an instance of CompletionsLogProbabilityModel class.\n     *\n     * @param tokens the tokens value to set.\n     * @param tokenLogprobs the tokenLogprobs value to set.\n     * @param topLogprobs the topLogprobs value to set.\n     * @param textOffset the textOffset value to set.\n     */\n    @JsonCreator\n    private AzureCompletionsLogProbabilityModel(\n            @JsonProperty(value = \"tokens\") List<String> tokens,\n            @JsonProperty(value = \"token_logprobs\") List<Double> tokenLogprobs,\n            @JsonProperty(value = \"top_logprobs\") List<Map<String, Double>> topLogprobs,\n            @JsonProperty(value = \"text_offset\") List<Integer> textOffset) {\n        this.tokens = tokens;\n        this.tokenLogprobs = tokenLogprobs;\n        this.topLogprobs = topLogprobs;\n        this.textOffset = textOffset;\n    }\n\n    /**\n     * Get the tokens property: The textual forms of tokens evaluated in this probability model.\n     *\n     * @return the tokens value.\n     */\n    public List<String> getTokens() {\n        return this.tokens;\n    }\n\n    /**\n     * Get the tokenLogprobs property: A collection of log probability values for the tokens in this completions data.\n     *\n     * @return the tokenLogprobs value.\n     */\n    public List<Double> getTokenLogprobs() {\n        return this.tokenLogprobs;\n    }\n\n    /**\n     * Get the topLogprobs property: A mapping of tokens to maximum log probability values in this completions data.\n     *\n     * @return the topLogprobs value.\n     */\n    public List<Map<String, Double>> getTopLogprobs() {\n        return this.topLogprobs;\n    }\n\n    /**\n     * Get the textOffset property: The text offsets associated with tokens in this completions data.\n     *\n     * @return the textOffset value.\n     */\n    public List<Integer> getTextOffset() {\n        return this.textOffset;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureCompletionsUsage.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n/**\n * Representation of the token counts processed for a completions request. Counts consider all tokens across prompts,\n * choices, choice alternates, best_of generations, and other consumers.\n */\n@Data\npublic final class AzureCompletionsUsage {\n\n    /*\n     * The number of tokens generated across all completions emissions.\n     */\n    @JsonProperty(value = \"completion_tokens\")\n    private int completionTokens;\n\n    /*\n     * The number of tokens in the provided prompts for the completions request.\n     */\n    @JsonProperty(value = \"prompt_tokens\")\n    private int promptTokens;\n\n    /*\n     * The total number of tokens processed for the completions request and response.\n     */\n    @JsonProperty(value = \"total_tokens\")\n    private int totalTokens;\n\n    /**\n     * Creates an instance of CompletionsUsage class.\n     *\n     * @param completionTokens the completionTokens value to set.\n     * @param promptTokens the promptTokens value to set.\n     * @param totalTokens the totalTokens value to set.\n     */\n    @JsonCreator\n    private AzureCompletionsUsage(\n            @JsonProperty(value = \"completion_tokens\") int completionTokens,\n            @JsonProperty(value = \"prompt_tokens\") int promptTokens,\n            @JsonProperty(value = \"total_tokens\") int totalTokens) {\n        this.completionTokens = completionTokens;\n        this.promptTokens = promptTokens;\n        this.totalTokens = totalTokens;\n    }\n\n    /**\n     * Get the completionTokens property: The number of tokens generated across all completions emissions.\n     *\n     * @return the completionTokens value.\n     */\n    public int getCompletionTokens() {\n        return this.completionTokens;\n    }\n\n    /**\n     * Get the promptTokens property: The number of tokens in the provided prompts for the completions request.\n     *\n     * @return the promptTokens value.\n     */\n    public int getPromptTokens() {\n        return this.promptTokens;\n    }\n\n    /**\n     * Get the totalTokens property: The total number of tokens processed for the completions request and response.\n     *\n     * @return the totalTokens value.\n     */\n    public int getTotalTokens() {\n        return this.totalTokens;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureExpandableStringEnum.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n\npackage ai.chat2db.server.web.api.controller.ai.azure.model;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport ai.chat2db.server.web.api.controller.ai.azure.util.AzureReflectionUtils;\nimport com.fasterxml.jackson.annotation.JsonValue;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static java.lang.invoke.MethodType.methodType;\n\n/**\n * Base implementation for expandable, single string enums.\n *\n * @param <T> a specific expandable enum type\n */\npublic abstract class AzureExpandableStringEnum<T extends AzureExpandableStringEnum<T>> {\n    private static final Map<Class<?>, MethodHandle> CONSTRUCTORS = new ConcurrentHashMap<>();\n    private static final Map<Class<?>, ConcurrentHashMap<String, ? extends AzureExpandableStringEnum<?>>> VALUES\n        = new ConcurrentHashMap<>();\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(AzureExpandableStringEnum.class);\n    private String name;\n    private Class<T> clazz;\n\n    /**\n     * Creates a new instance of {@link AzureExpandableStringEnum} without a {@link #toString()} value.\n     * <p>\n     * This constructor shouldn't be called as it will produce a {@link AzureExpandableStringEnum} which doesn't\n     * have a String enum value.\n     *\n     * @deprecated Use the {@link #fromString(String, Class)} factory method.\n     */\n    @Deprecated\n    public AzureExpandableStringEnum() {\n    }\n\n    /**\n     * Creates an instance of the specific expandable string enum from a String.\n     *\n     * @param name The value to create the instance from.\n     * @param clazz The class of the expandable string enum.\n     * @param <T> the class of the expandable string enum.\n     * @return The expandable string enum instance.\n     *\n     * @throws RuntimeException wrapping implementation class constructor exception (if any is thrown).\n     */\n    @SuppressWarnings({\"unchecked\", \"deprecation\"})\n    protected static <T extends AzureExpandableStringEnum<T>> T fromString(String name, Class<T> clazz) {\n        if (name == null) {\n            return null;\n        }\n\n        ConcurrentHashMap<String, ?> clazzValues = VALUES.computeIfAbsent(clazz, key -> new ConcurrentHashMap<>());\n        T value = (T) clazzValues.get(name);\n\n        if (value != null) {\n            return value;\n        } else {\n            MethodHandle ctor = CONSTRUCTORS.computeIfAbsent(clazz, AzureExpandableStringEnum::getDefaultConstructor);\n\n            if (ctor == null) {\n                // logged in ExpandableStringEnum::getDefaultConstructor\n                return null;\n            }\n\n            try {\n                value = (T) ctor.invoke();\n            } catch (Throwable e) {\n                LOGGER.warn(\"Failed to create {}, default constructor threw exception\", clazz.getName(), e);\n                return null;\n            }\n\n            return value.nameAndAddValue(name, value, clazz);\n        }\n    }\n\n    private static <T> MethodHandle getDefaultConstructor(Class<T> clazz) {\n        try {\n            MethodHandles.Lookup lookup = AzureReflectionUtils.getLookupToUse(clazz);\n            return lookup.findConstructor(clazz, methodType(void.class));\n        } catch (NoSuchMethodException | IllegalAccessException e) {\n            LOGGER.info(\"Can't find or access default constructor for {}, make sure corresponding package is open to azure-core\", clazz.getName(), e);\n        } catch (Exception e) {\n            LOGGER.info(\"Failed to get lookup for {}\", clazz.getName(), e);\n        }\n\n        return null;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    T nameAndAddValue(String name, T value, Class<T> clazz) {\n        this.name = name;\n        this.clazz = clazz;\n\n        ((ConcurrentHashMap<String, T>) VALUES.get(clazz)).put(name, value);\n        return (T) this;\n    }\n\n    /**\n     * Gets a collection of all known values to an expandable string enum type.\n     *\n     * @param clazz the class of the expandable string enum.\n     * @param <T> the class of the expandable string enum.\n     * @return A collection of all known values for the given {@code clazz}.\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected static <T extends AzureExpandableStringEnum<T>> Collection<T> values(Class<T> clazz) {\n        return new ArrayList<T>((Collection<T>) VALUES.getOrDefault(clazz, new ConcurrentHashMap<>()).values());\n    }\n\n    @Override\n    @JsonValue\n    public String toString() {\n        return this.name;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(this.clazz, this.name);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == null) {\n            return false;\n        } else if (clazz == null || !clazz.isAssignableFrom(obj.getClass())) {\n            return false;\n        } else if (obj == this) {\n            return true;\n        } else if (this.name == null) {\n            return ((AzureExpandableStringEnum<T>) obj).name == null;\n        } else {\n            return this.name.equals(((AzureExpandableStringEnum<T>) obj).name);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/util/AzureReflectionUtils.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n\npackage ai.chat2db.server.web.api.controller.ai.azure.util;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodType;\nimport java.lang.reflect.Constructor;\nimport java.security.PrivilegedExceptionAction;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Utility methods that aid in performing reflective operations.\n */\n@SuppressWarnings(\"deprecation\")\npublic final class AzureReflectionUtils {\n    private static final Logger LOGGER = LoggerFactory.getLogger(AzureReflectionUtils.class);\n\n    private static final boolean MODULE_BASED;\n\n    private static final MethodHandle CLASS_GET_MODULE_METHOD_HANDLE;\n    private static final MethodHandle MODULE_IS_NAMED_METHOD_HANDLE;\n    private static final MethodHandle MODULE_ADD_READS_METHOD_HANDLE;\n    private static final MethodHandle METHOD_HANDLES_PRIVATE_LOOKUP_IN_METHOD_HANDLE;\n    private static final MethodHandle MODULE_IS_OPEN_UNCONDITIONALLY_METHOD_HANDLE;\n    private static final MethodHandle MODULE_IS_OPEN_TO_OTHER_MODULE_METHOD_HANDLE;\n\n    private static final MethodHandles.Lookup LOOKUP;\n    private static final Object CORE_MODULE;\n\n    private static final MethodHandle JDK_INTERNAL_PRIVATE_LOOKUP_IN_CONSTRUCTOR;\n\n    static {\n        boolean moduleBased = false;\n        MethodHandle classGetModule = null;\n        MethodHandle moduleIsNamed = null;\n        MethodHandle moduleAddReads = null;\n        MethodHandle methodHandlesPrivateLookupIn = null;\n        MethodHandle moduleIsOpenUnconditionally = null;\n        MethodHandle moduleIsOpenToOtherModule = null;\n\n        MethodHandles.Lookup lookup = MethodHandles.lookup();\n        Object coreModule = null;\n\n        MethodHandle jdkInternalPrivateLookupInConstructor = null;\n\n        try {\n            Class<?> moduleClass = Class.forName(\"java.lang.Module\");\n            classGetModule = lookup.unreflect(Class.class.getDeclaredMethod(\"getModule\"));\n            moduleIsNamed = lookup.unreflect(moduleClass.getDeclaredMethod(\"isNamed\"));\n            moduleAddReads = lookup.unreflect(moduleClass.getDeclaredMethod(\"addReads\", moduleClass));\n            methodHandlesPrivateLookupIn = lookup.findStatic(MethodHandles.class, \"privateLookupIn\",\n                MethodType.methodType(MethodHandles.Lookup.class, Class.class, MethodHandles.Lookup.class));\n            moduleIsOpenUnconditionally = lookup.unreflect(moduleClass.getDeclaredMethod(\"isOpen\", String.class));\n            moduleIsOpenToOtherModule = lookup.unreflect(\n                moduleClass.getDeclaredMethod(\"isOpen\", String.class, moduleClass));\n\n            coreModule = classGetModule.invokeWithArguments(AzureReflectionUtils.class);\n            moduleBased = true;\n        } catch (Throwable throwable) {\n            if (throwable instanceof Error) {\n                throw (Error) throwable;\n            } else {\n                LOGGER.error(\"Unable to create MethodHandles to use Java 9+ MethodHandles.privateLookupIn. \"\n                        + \"Will attempt to fallback to using the package-private constructor.\", throwable);\n            }\n        }\n\n        if (!moduleBased) {\n            try {\n                Constructor<MethodHandles.Lookup> privateLookupInConstructor =\n                    MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);\n\n                if (!privateLookupInConstructor.isAccessible()) {\n                    privateLookupInConstructor.setAccessible(true);\n                }\n\n                jdkInternalPrivateLookupInConstructor = lookup.unreflectConstructor(privateLookupInConstructor);\n            } catch (ReflectiveOperationException ex) {\n                LOGGER.error(\"Unable to use package-private MethodHandles.Lookup constructor.\", ex);\n            }\n        }\n\n        MODULE_BASED = moduleBased;\n        CLASS_GET_MODULE_METHOD_HANDLE = classGetModule;\n        MODULE_IS_NAMED_METHOD_HANDLE = moduleIsNamed;\n        MODULE_ADD_READS_METHOD_HANDLE = moduleAddReads;\n        METHOD_HANDLES_PRIVATE_LOOKUP_IN_METHOD_HANDLE = methodHandlesPrivateLookupIn;\n        MODULE_IS_OPEN_UNCONDITIONALLY_METHOD_HANDLE = moduleIsOpenUnconditionally;\n        MODULE_IS_OPEN_TO_OTHER_MODULE_METHOD_HANDLE = moduleIsOpenToOtherModule;\n        LOOKUP = lookup;\n        CORE_MODULE = coreModule;\n        JDK_INTERNAL_PRIVATE_LOOKUP_IN_CONSTRUCTOR = jdkInternalPrivateLookupInConstructor;\n    }\n\n    /**\n     * Gets the {@link MethodHandles.Lookup} to use when performing reflective operations.\n     * <p>\n     * If Java 8 is being used this will always return {@link MethodHandles.Lookup#publicLookup()} as Java 8 doesn't\n     * have module boundaries that will prevent reflective access to the {@code targetClass}.\n     * <p>\n     * If Java 9 or above is being used this will return a {@link MethodHandles.Lookup} based on whether the module\n     * containing the {@code targetClass} exports the package containing the class. Otherwise, the\n     * {@link MethodHandles.Lookup} associated to {@code com.azure.core} will attempt to read the module containing\n     * {@code targetClass}.\n     *\n     * @param targetClass The {@link Class} that will need to be reflectively accessed.\n     * @return The {@link MethodHandles.Lookup} that will allow {@code com.azure.core} to access the {@code targetClass}\n     * reflectively.\n     * @throws Exception If the underlying reflective calls throw an exception.\n     */\n    public static MethodHandles.Lookup getLookupToUse(Class<?> targetClass) throws Exception {\n        try {\n            if (MODULE_BASED) {\n                Object responseModule = CLASS_GET_MODULE_METHOD_HANDLE.invoke(targetClass);\n\n                // The unnamed module is opened unconditionally, have Core read it and use a private proxy lookup to\n                // enable all lookup scenarios.\n                if (!(boolean) MODULE_IS_NAMED_METHOD_HANDLE.invoke(responseModule)) {\n                    MODULE_ADD_READS_METHOD_HANDLE.invokeWithArguments(CORE_MODULE, responseModule);\n                    return performSafePrivateLookupIn(targetClass);\n                }\n\n\n                // If the response module is the Core module return the Core private lookup.\n                if (responseModule == CORE_MODULE) {\n                    return LOOKUP;\n                }\n\n                // Next check if the target class module is opened either unconditionally or to Core's module. If so,\n                // also use a private proxy lookup to enable all lookup scenarios.\n                String packageName = targetClass.getPackage().getName();\n                if ((boolean) MODULE_IS_OPEN_UNCONDITIONALLY_METHOD_HANDLE\n                    .invokeWithArguments(responseModule, packageName)\n                    || (boolean) MODULE_IS_OPEN_TO_OTHER_MODULE_METHOD_HANDLE\n                    .invokeWithArguments(responseModule, packageName, CORE_MODULE)) {\n                    MODULE_ADD_READS_METHOD_HANDLE.invokeWithArguments(CORE_MODULE, responseModule);\n                    return performSafePrivateLookupIn(targetClass);\n                }\n\n                // Otherwise, return the public lookup as there are no specialty ways to access the other module.\n                return MethodHandles.publicLookup();\n            } else {\n                return (MethodHandles.Lookup) JDK_INTERNAL_PRIVATE_LOOKUP_IN_CONSTRUCTOR.invoke(targetClass);\n            }\n        } catch (Throwable throwable) {\n            // invoke(Class<?) throws a Throwable as the underlying method being called through reflection can throw\n            // anything, but the constructor being called is owned by the Java SDKs which won't throw Throwable. So,\n            // only Error needs to be inspected and handled specially, otherwise it can be assumed the Throwable is\n            // a type of Exception which can be thrown based on this method having Exception checked.\n            if (throwable instanceof Error) {\n                throw (Error) throwable;\n            } else {\n                throw (Exception) throwable;\n            }\n        }\n    }\n\n    @SuppressWarnings(\"removal\")\n    private static MethodHandles.Lookup performSafePrivateLookupIn(Class<?> targetClass) throws Throwable {\n        // MethodHandles::privateLookupIn() throws SecurityException if denied by the security manager\n        if (System.getSecurityManager() == null) {\n            return (MethodHandles.Lookup) METHOD_HANDLES_PRIVATE_LOOKUP_IN_METHOD_HANDLE\n                .invokeExact(targetClass, LOOKUP);\n        } else {\n            return java.security.AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandles.Lookup>) () -> {\n                try {\n                    return (MethodHandles.Lookup) METHOD_HANDLES_PRIVATE_LOOKUP_IN_METHOD_HANDLE\n                        .invokeExact(targetClass, LOOKUP);\n                } catch (Throwable throwable) {\n                    if (throwable instanceof Error) {\n                        throw (Error) throwable;\n                    } else {\n                        throw (Exception) throwable;\n                    }\n                }\n            });\n        }\n    }\n\n    public static boolean isModuleBased() {\n        return MODULE_BASED;\n    }\n\n    AzureReflectionUtils() {\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/client/BaichuanAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.baichuan.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author moji\n * @date 23/09/26\n */\n@Slf4j\npublic class BaichuanAIClient {\n\n    /**\n     * BAICHUAN OPENAI KEY\n     */\n    public static final String BAICHUAN_API_KEY = \"baichuan.chatgpt.apiKey\";\n\n    /**\n     * BAICHUAN OPENAI SECRET KEY\n     */\n    public static final String BAICHUAN_SECRET_KEY = \"baichuan.chatgpt.secretKey\";\n\n    /**\n     * BAICHUAN OPENAI HOST\n     */\n    public static final String BAICHUAN_HOST = \"baichuan.host\";\n\n    /**\n     * BAICHUAN OPENAI model\n     */\n    public static final String BAICHUAN_MODEL= \"baichuan.model\";\n\n    /**\n     * BAICHUAN OPENAI embedding model\n     */\n    public static final String BAICHUAN_EMBEDDING_MODEL = \"baichuan.embedding.model\";\n\n    private static BaichuanAIStreamClient BAICHUAN_AI_CLIENT;\n\n\n    public static BaichuanAIStreamClient getInstance() {\n        if (BAICHUAN_AI_CLIENT != null) {\n            return BAICHUAN_AI_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static BaichuanAIStreamClient singleton() {\n        if (BAICHUAN_AI_CLIENT == null) {\n            synchronized (BaichuanAIClient.class) {\n                if (BAICHUAN_AI_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return BAICHUAN_AI_CLIENT;\n    }\n\n    public static void refresh() {\n        String apiKey = \"\";\n        String apiHost = \"https://api.baichuan-ai.com/v1/stream/chat\";\n        String model = \"Baichuan2-53B\";\n        String secretKey = \"\";\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(BAICHUAN_HOST).getData();\n        if (apiHostConfig != null && StringUtils.isNotBlank(apiHostConfig.getContent())) {\n            apiHost = apiHostConfig.getContent();\n            if (apiHost.endsWith(\"/\")) {\n                apiHost = apiHost.substring(0, apiHost.length() - 1);\n            }\n        }\n        Config config = configService.find(BAICHUAN_API_KEY).getData();\n        if (config != null && StringUtils.isNotBlank(config.getContent())) {\n            apiKey = config.getContent();\n        }\n        Config secretConfig = configService.find(BAICHUAN_SECRET_KEY).getData();\n        if (secretConfig != null && StringUtils.isNotBlank(secretConfig.getContent())) {\n            secretKey = secretConfig.getContent();\n        }\n        Config deployConfig = configService.find(BAICHUAN_MODEL).getData();\n        if (deployConfig != null && StringUtils.isNotBlank(deployConfig.getContent())) {\n            model = deployConfig.getContent();\n        }\n        BAICHUAN_AI_CLIENT = BaichuanAIStreamClient.builder().apiKey(apiKey).secretKey(secretKey)\n                .apiHost(apiHost).model(model).build();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/client/BaichuanAIStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.baichuan.client;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.baichuan.interceptor.BaichuanHeaderAuthorizationInterceptor;\nimport ai.chat2db.server.web.api.controller.ai.baichuan.model.BaichuanChatCompletionsOptions;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.*;\nimport okhttp3.sse.EventSourceListener;\nimport okio.BufferedSource;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Fast Chat Aligned Client\n *\n * @author moji\n */\n@Slf4j\npublic class BaichuanAIStreamClient {\n\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String apiKey;\n\n    @Getter\n    @NotNull\n    private String secretKey;\n\n    /**\n     * apiHost\n     */\n    @Getter\n    @NotNull\n    private String apiHost;\n\n    /**\n     * model\n     */\n    @Getter\n    private String model;\n\n    /**\n     * embeddingModel\n     */\n    @Getter\n    private String embeddingModel;\n\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n\n    /**\n     * @param builder\n     */\n    private BaichuanAIStreamClient(Builder builder) {\n        this.apiKey = builder.apiKey;\n        this.apiHost = builder.apiHost;\n        this.model = builder.model;\n        this.secretKey = builder.secretKey;\n        this.embeddingModel = builder.embeddingModel;\n        if (Objects.isNull(builder.okHttpClient)) {\n            builder.okHttpClient = this.okHttpClient();\n        }\n        okHttpClient = builder.okHttpClient;\n    }\n\n    /**\n     * okhttpclient\n     */\n    private OkHttpClient okHttpClient() {\n        OkHttpClient okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new BaichuanHeaderAuthorizationInterceptor(this.apiKey, this.secretKey))\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n        return okHttpClient;\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static BaichuanAIStreamClient.Builder builder() {\n        return new BaichuanAIStreamClient.Builder();\n    }\n\n    /**\n     * builder\n     */\n    public static final class Builder {\n        private String apiKey;\n\n        private String secretKey;\n\n        private String apiHost;\n\n        private String model;\n\n        private String embeddingModel;\n\n        /**\n         * OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public BaichuanAIStreamClient.Builder apiKey(String apiKeyValue) {\n            this.apiKey = apiKeyValue;\n            return this;\n        }\n\n        public BaichuanAIStreamClient.Builder secretKey(String secretKey) {\n            this.secretKey = secretKey;\n            return this;\n        }\n\n        /**\n         * @param apiHostValue\n         * @return\n         */\n        public BaichuanAIStreamClient.Builder apiHost(String apiHostValue) {\n            this.apiHost = apiHostValue;\n            return this;\n        }\n\n        /**\n         * @param modelValue\n         * @return\n         */\n        public BaichuanAIStreamClient.Builder model(String modelValue) {\n            this.model = modelValue;\n            return this;\n        }\n\n        public BaichuanAIStreamClient.Builder embeddingModel(String embeddingModelValue) {\n            this.embeddingModel = embeddingModelValue;\n            return this;\n        }\n\n        public BaichuanAIStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public BaichuanAIStreamClient build() {\n            return new BaichuanAIStreamClient(this);\n        }\n\n    }\n\n    /**\n     * Q&A interface stream form\n     *\n     * @param chatMessages\n     * @param eventSourceListener\n     */\n    public void streamCompletions(List<FastChatMessage> chatMessages, EventSourceListener eventSourceListener) {\n        if (CollectionUtils.isEmpty(chatMessages)) {\n            log.error(\"param error：Baichuan Chat Prompt cannot be empty\");\n            throw new ParamBusinessException(\"prompt\");\n        }\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：Baichuan ChatEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        log.info(\"Baichuan AI, prompt:{}\", chatMessages.get(chatMessages.size() - 1).getContent());\n        try {\n\n            BaichuanChatCompletionsOptions chatCompletionsOptions = new BaichuanChatCompletionsOptions();\n            chatCompletionsOptions.setModel(this.model);\n            chatCompletionsOptions.setMessages(chatMessages);\n\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(chatCompletionsOptions);\n            Request request = new Request.Builder()\n                .url(apiHost)\n                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                .build();\n            //Create event\n            //Send the request and process the response\n            try (Response response = this.okHttpClient.newCall(request).execute()) {\n                if (!response.isSuccessful()) {\n                    throw new IOException(\"Unexpected code \" + response);\n                }\n\n                //Read and output response data\n                BufferedSource source = response.body().source();\n                while (!source.exhausted()) {\n                    String content = source.readUtf8Line();\n                    eventSourceListener.onEvent(null, \"[DATA]\", null, content);\n                }\n                eventSourceListener.onEvent(null, \"[DONE]\", null, \"[DONE]\");\n            } catch (Exception e) {\n                log.error(\"baichuan ai error\", e);\n            }\n\n            log.info(\"finish invoking baichuan ai\");\n        } catch (Exception e) {\n            log.error(\"baichuan ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/interceptor/BaichuanHeaderAuthorizationInterceptor.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.baichuan.interceptor;\n\nimport cn.hutool.http.ContentType;\nimport cn.hutool.http.Header;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.*;\nimport okio.Buffer;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\n\n/**\n * header apikey\n *\n * @author grt\n * @since 2023-03-23\n */\n@Slf4j\n@Getter\npublic class BaichuanHeaderAuthorizationInterceptor implements Interceptor {\n\n    private String apiKey;\n\n    private String secretKey;\n\n    public BaichuanHeaderAuthorizationInterceptor(String apiKey, String secretKey) {\n        this.apiKey = apiKey;\n        this.secretKey = secretKey;\n    }\n\n\n    @Override\n    public Response intercept(Chain chain) throws IOException {\n        Request originalRequest = chain.request();\n\n        // Get the current timestamp (UTC standard timestamp)\n        long timestamp = System.currentTimeMillis() / 1000;\n\n        // Get the original HTTP-Body\n        RequestBody originalRequestBody = originalRequest.body();\n        Buffer buffer = new Buffer();\n        if (originalRequestBody != null) {\n            originalRequestBody.writeTo(buffer);\n        }\n        String httpBody = buffer.readUtf8();\n\n        // Calculate X-BC-Signature\n        String signature = calculateSignature(secretKey, httpBody, timestamp);\n\n        //Create a new request and add custom request headers\n        Request newRequest = originalRequest.newBuilder()\n                .addHeader(\"Authorization\", \"Bearer \" + apiKey)\n                .addHeader(\"Content-Type\", \"application/json\")\n                .addHeader(\"X-BC-Sign-Algo\", \"MD5\")\n                .addHeader(\"X-BC-Timestamp\", String.valueOf(timestamp))\n                .addHeader(\"X-BC-Signature\", signature)\n                .method(originalRequest.method(), RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), httpBody))\n                .build();\n\n        return chain.proceed(newRequest);\n    }\n\n    private String calculateSignature(String secretKey, String httpBody, long timestamp) {\n        String toHash = secretKey + httpBody + timestamp;\n        return md5(toHash);\n    }\n\n    private String md5(String s) {\n        try {\n            MessageDigest digest = MessageDigest.getInstance(\"MD5\");\n            byte[] result = digest.digest(s.getBytes(StandardCharsets.UTF_8));\n            StringBuilder sb = new StringBuilder();\n            for (byte b : result) {\n                sb.append(String.format(\"%02x\", b));\n            }\n            return sb.toString();\n        } catch (Exception e) {\n            log.error(\"baichuan secret key md5 error\", e);\n            return \"\";\n        }\n    }\n\n    private String calculateSignature(String secretKey, RequestBody body, long timestamp) {\n        try {\n            String requestBody = bodyToString(body);\n            String rawSignature = secretKey + requestBody + timestamp;\n\n            // Use MD5 to calculate signature\n            MessageDigest md = MessageDigest.getInstance(\"MD5\");\n            byte[] mdBytes = md.digest(rawSignature.getBytes(StandardCharsets.UTF_8));\n\n            // Convert MD5 byte array to Base64 encoded string\n            return Base64.getEncoder().encodeToString(mdBytes);\n        } catch (IOException | NoSuchAlgorithmException e) {\n            log.error(\"baichuan secret key md5 error\", e);\n            return \"\";\n        }\n    }\n\n    private String bodyToString(RequestBody body) throws IOException {\n        //Convert RequestBody to string\n        return body == null ? \"\" : body.toString();\n    }\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/listener/BaichuanChatAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.baichuan.listener;\n\nimport ai.chat2db.server.web.api.controller.ai.baichuan.model.BaichuanChatCompletions;\nimport ai.chat2db.server.web.api.controller.ai.baichuan.model.BaichuanChatMessage;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\n/**\n * description：OpenAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class BaichuanChatAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n\n    public BaichuanChatAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"Baichuan Chat Sse connecting...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"Baichuan Chat AI response data：{}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"Baichuan Chat AI closed\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n\n        BaichuanChatCompletions chatCompletions = mapper.readValue(data, BaichuanChatCompletions.class);\n        String text = \"\";\n        log.info(\"code={} msg={}\", chatCompletions.getCode(), chatCompletions.getMsg());\n        for (BaichuanChatMessage message : chatCompletions.getData().getMessages()) {\n            if (message != null) {\n                log.info(\"message: {}, Chat Role: {}\", message.getContent(), message.getRole());\n                if (message.getContent() != null) {\n                    text = message.getContent();\n                }\n            }\n        }\n\n        Message message = new Message();\n        message.setContent(text);\n        sseEmitter.send(SseEmitter.event()\n            .id(null)\n            .data(message)\n            .reconnectTime(3000));\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        try {\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        sseEmitter.complete();\n        log.info(\"FastChatAI close sse connection...\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = Objects.nonNull(t) ? t.getMessage() : \"\";\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                if (StringUtils.isBlank(bodyString)) {\n                    if (Objects.nonNull(t)) {\n                        bodyString = t.getMessage();\n                    } else {\n                        bodyString = String.valueOf(response.code());\n                    }\n\n                }\n                log.error(\"Baichuan Chat AI sse response：{}\", bodyString);\n            } else {\n                log.error(\"Baichuan Chat AI sse response：{}，error：{}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"Baichuan Chat AI error：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Baichuan Chat AI send data error:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatCompletions.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.baichuan.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class BaichuanChatCompletions {\n\n    /*\n     * A unique identifier associated with this chat completions response.\n     */\n    private String msg;\n\n    private int code;\n\n    /*\n     * The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1.\n     * Token limits and other settings may limit the number of choices generated.\n     */\n    @JsonProperty(value = \"data\")\n    private BaichuanChatData data;\n\n    /*\n     * Usage information for tokens processed and generated as part of this completions operation.\n     */\n    private BaichuanChatCompletionsUsage usage;\n\n    /**\n     * Creates an instance of ChatCompletions class.\n     *\n     * @param msg the id value to set.\n     * @param code the created value to set.\n     * @param choices the choices value to set.\n     * @param usage the usage value to set.\n     */\n    @JsonCreator\n    private BaichuanChatCompletions(\n        @JsonProperty(value = \"msg\") String msg,\n        @JsonProperty(value = \"code\") int code,\n        @JsonProperty(value = \"data\") BaichuanChatData choices,\n        @JsonProperty(value = \"usage\") BaichuanChatCompletionsUsage usage) {\n        this.msg = msg;\n        this.code = code;\n        this.data = choices;\n        this.usage = usage;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatCompletionsOptions.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.baichuan.model;\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n/**\n * The configuration information for a chat completions request. Completions support a wide variety of tasks and\n * generate text that continues from or \"completes\" provided prompt data.\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic final class BaichuanChatCompletionsOptions {\n\n    /*\n     * The collection of context messages associated with this chat completions request.\n     * Typical usage begins with a chat message for the System role that provides instructions for\n     * the behavior of the assistant, followed by alternating messages between the User and\n     * Assistant roles.\n     */\n    private List<FastChatMessage> messages;\n\n    //\n    /*\n     * The model name to provide as part of this completions request.\n     * Not applicable to Fast Chat AI, where deployment information should be included in the Fast Chat\n     * resource URI that's connected to.\n     */\n    private String model;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatCompletionsUsage.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.baichuan.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * Representation of the token counts processed for a completions request. Counts consider all tokens across prompts,\n * choices, choice alternates, best_of generations, and other consumers.\n */\n@Data\n@NoArgsConstructor\npublic final class BaichuanChatCompletionsUsage {\n\n    /*\n     * The number of tokens generated across all completions emissions.\n     */\n    @JsonProperty(value = \"answer_tokens\")\n    private int answerTokens;\n\n    /*\n     * The number of tokens in the provided prompts for the completions request.\n     */\n    @JsonProperty(value = \"prompt_tokens\")\n    private int promptTokens;\n\n    /*\n     * The total number of tokens processed for the completions request and response.\n     */\n    @JsonProperty(value = \"total_tokens\")\n    private int totalTokens;\n\n    /**\n     * Creates an instance of CompletionsUsage class.\n     *\n     * @param completionTokens the completionTokens value to set.\n     * @param promptTokens the promptTokens value to set.\n     * @param totalTokens the totalTokens value to set.\n     */\n    @JsonCreator\n    private BaichuanChatCompletionsUsage(\n            @JsonProperty(value = \"answer_tokens\") int completionTokens,\n            @JsonProperty(value = \"prompt_tokens\") int promptTokens,\n            @JsonProperty(value = \"total_tokens\") int totalTokens) {\n        this.answerTokens = completionTokens;\n        this.promptTokens = promptTokens;\n        this.totalTokens = totalTokens;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatData.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.baichuan.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * The representation of a single prompt completion as part of an overall completions request. Generally, `n` choices\n * are generated per provided prompt with a default value of 1. Token limits and other settings may limit the number of\n * choices generated.\n */\n@Data\npublic final class BaichuanChatData {\n\n    /*\n     * The log probabilities model for tokens associated with this completions choice.\n     */\n    @JsonProperty(value = \"messages\")\n    private List<BaichuanChatMessage> messages;\n\n\n\n    /**\n     * Creates an instance of Choice class.\n     *\n     * @param message the message value to set\n     */\n    @JsonCreator\n    private BaichuanChatData(\n            @JsonProperty(value = \"messages\") List<BaichuanChatMessage> message) {\n        this.messages = message;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatMessage.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.baichuan.model;\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n@Data\npublic class BaichuanChatMessage {\n\n\n    /*\n     * The role associated with this message payload.\n     */\n    @JsonProperty(value = \"role\")\n    private FastChatRole role;\n\n    /*\n     * The text associated with this message payload.\n     */\n    @JsonProperty(value = \"content\")\n    private String content;\n\n    /*\n     * Reason for finishing\n     */\n    @JsonProperty(value = \"finish_reason\")\n    private String finishReason;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/client/Chat2DBAIStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.chat2db.client;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.interceptor.Chat2dbHeaderAuthorizationInterceptor;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatOpenAiApi;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbedding;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.ChatCompletion;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport com.unfbx.chatgpt.interceptor.HeaderAuthorizationInterceptor;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.*;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport okhttp3.sse.EventSources;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport retrofit2.Retrofit;\nimport retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;\nimport retrofit2.converter.jackson.JacksonConverterFactory;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Fast Chat Aligned Client\n *\n * @author moji\n */\n@Slf4j\npublic class Chat2DBAIStreamClient {\n\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String apiKey;\n\n    /**\n     * apiHost\n     */\n    @Getter\n    @NotNull\n    private String apiHost;\n\n    /**\n     * model\n     */\n    @Getter\n    private String model;\n\n    /**\n     * embeddingModel\n     */\n    @Getter\n    private String embeddingModel;\n\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n    @Getter\n    private FastChatOpenAiApi fastChatOpenAiApi;\n\n    /**\n     * @param builder\n     */\n    private Chat2DBAIStreamClient(Builder builder) {\n        this.apiKey = builder.apiKey;\n        this.apiHost = builder.apiHost;\n        if (!apiHost.endsWith(\"/\")){\n            apiHost = apiHost + \"/\";\n        }\n        this.model = builder.model;\n        this.embeddingModel = builder.embeddingModel;\n        if (Objects.isNull(builder.okHttpClient)) {\n            builder.okHttpClient = this.okHttpClient();\n        }\n        okHttpClient = builder.okHttpClient;\n        this.fastChatOpenAiApi = new Retrofit.Builder()\n                .baseUrl(apiHost)\n                .client(okHttpClient)\n                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())\n                .addConverterFactory(JacksonConverterFactory.create())\n                .build().create(FastChatOpenAiApi.class);\n    }\n\n    /**\n     * okhttpclient\n     */\n    private OkHttpClient okHttpClient() {\n        OkHttpClient okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new Chat2dbHeaderAuthorizationInterceptor(this.apiKey, this.model))\n            .connectTimeout(50, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n        return okHttpClient;\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static Chat2DBAIStreamClient.Builder builder() {\n        return new Chat2DBAIStreamClient.Builder();\n    }\n\n    /**\n     * builder\n     */\n    public static final class Builder {\n        private String apiKey;\n\n        private String apiHost;\n\n        private String model;\n\n        private String embeddingModel;\n\n        /**\n         * OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public Chat2DBAIStreamClient.Builder apiKey(String apiKeyValue) {\n            this.apiKey = apiKeyValue;\n            return this;\n        }\n\n        /**\n         * @param apiHostValue\n         * @return\n         */\n        public Chat2DBAIStreamClient.Builder apiHost(String apiHostValue) {\n            this.apiHost = apiHostValue;\n            return this;\n        }\n\n        /**\n         * @param modelValue\n         * @return\n         */\n        public Chat2DBAIStreamClient.Builder model(String modelValue) {\n            this.model = modelValue;\n            return this;\n        }\n\n        public Chat2DBAIStreamClient.Builder embeddingModel(String embeddingModelValue) {\n            this.embeddingModel = embeddingModelValue;\n            return this;\n        }\n\n        public Chat2DBAIStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public Chat2DBAIStreamClient build() {\n            return new Chat2DBAIStreamClient(this);\n        }\n\n    }\n\n    /**\n     * Q&A interface stream form\n     *\n     * @param chatMessages\n     * @param eventSourceListener\n     */\n    public void streamCompletions(List<Message> chatMessages, EventSourceListener eventSourceListener) {\n        if (CollectionUtils.isEmpty(chatMessages)) {\n            log.error(\"param error:Chat Prompt cannot be empty\");\n            throw new ParamBusinessException(\"prompt\");\n        }\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：ChatEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        try {\n            ChatCompletion chatCompletion = ChatCompletion.builder()\n                    .messages(chatMessages)\n                    .stream(true)\n                    .build();\n\n            EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(chatCompletion);\n            Request request = new Request.Builder()\n                    .url(this.apiHost + \"v1/chat/completions\")\n                    .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                    .build();\n            //Create event\n            EventSource eventSource = factory.newEventSource(request, eventSourceListener);\n            log.info(\"finish invoking chat ai\");\n        } catch (Exception e) {\n            log.error(\"chat ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n    /**\n     * Creates an embedding vector representing the input text.\n     *\n     * @param input\n     * @return EmbeddingResponse\n     */\n    public FastChatEmbeddingResponse embeddings(String input) {\n        FastChatEmbedding embedding = FastChatEmbedding.builder().input(input).build();\n        if (StringUtils.isNotBlank(this.embeddingModel)) {\n            embedding.setModel(this.embeddingModel);\n        }\n        return this.embeddings(embedding);\n    }\n\n    /**\n     * Creates an embedding vector representing the input text.\n     *\n     * @param embedding\n     * @return EmbeddingResponse\n     */\n    public FastChatEmbeddingResponse embeddings(FastChatEmbedding embedding) {\n        try {\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(embedding);\n            Request request = new Request.Builder()\n                    .url(this.apiHost + \"v1/embeddings\")\n                    .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                    .build();\n\n            FastChatEmbeddingResponse chatEmbeddingResponse = null;\n            Response response = this.okHttpClient.newCall(request).execute();\n            if (response.isSuccessful()) {\n                String body = response.body().string();\n                chatEmbeddingResponse = mapper.readValue(body, FastChatEmbeddingResponse.class);\n            }\n            log.info(\"finish invoking chat embedding\");\n            return chatEmbeddingResponse;\n        } catch (Exception e) {\n            log.error(\"chat ai error\", e);\n            throw new ParamBusinessException();\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/client/Chat2dbAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.chat2db.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport com.unfbx.chatgpt.constant.OpenAIConst;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Objects;\n\n/**\n * @author jipengfei\n * @version : OpenAIClient.java\n */\n@Slf4j\npublic class Chat2dbAIClient {\n\n    public static final String CHAT2DB_OPENAI_KEY = \"chat2db.apiKey\";\n\n    /**\n     * OPENAI interface domain name\n     */\n    public static final String CHAT2DB_OPENAI_HOST = \"chat2db.apiHost\";\n\n    /**\n     * OPENAI model\n     */\n    public static final String CHAT2DB_OPENAI_MODEL = \"chat2db.model\";\n\n    /**\n     * FASTCHAT OPENAI embedding model\n     */\n    public static final String CHAT2DB_EMBEDDING_MODEL= \"fastchat.embedding.model\";\n\n\n    private static volatile Chat2DBAIStreamClient CHAT2DB_AI_STREAM_CLIENT;\n\n    public static Chat2DBAIStreamClient getInstance() {\n        if (CHAT2DB_AI_STREAM_CLIENT != null) {\n            return CHAT2DB_AI_STREAM_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static Chat2DBAIStreamClient singleton() {\n        if (CHAT2DB_AI_STREAM_CLIENT == null) {\n            synchronized (Chat2dbAIClient.class) {\n                if (CHAT2DB_AI_STREAM_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return CHAT2DB_AI_STREAM_CLIENT;\n    }\n\n    public static void refresh() {\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n\n        CHAT2DB_AI_STREAM_CLIENT = Chat2DBAIStreamClient.builder().apiHost(getApiHost(configService))\n                .apiKey(getApiKey(configService)).model(getModel(configService)).build();\n    }\n\n    private static String getApiHost(ConfigService configService) {\n        Config apiHostConfig = configService.find(CHAT2DB_OPENAI_HOST).getData();\n\n        if (Objects.nonNull(apiHostConfig)) {\n            return apiHostConfig.getContent();\n        }\n\n        String apiHost = ApplicationContextUtil.getProperty(CHAT2DB_OPENAI_HOST);\n\n        if (apiHost.isBlank()) {\n            return OpenAIConst.OPENAI_HOST;\n        }\n\n        return apiHost;\n    }\n\n    private static String getApiKey(ConfigService configService) {\n        String apiKey;\n\n        Config config = configService.find(CHAT2DB_OPENAI_KEY).getData();\n\n        if (Objects.nonNull(config)) {\n            apiKey = config.getContent();\n        } else {\n            apiKey = ApplicationContextUtil.getProperty(CHAT2DB_OPENAI_KEY);\n        }\n\n        log.info(\"refresh chat2db apikey:{}\", maskApiKey(apiKey));\n\n        return apiKey;\n    }\n\n    private static String getModel(ConfigService configService) {\n        Config modelConfig = configService.find(CHAT2DB_OPENAI_MODEL).getData();\n\n        if (Objects.nonNull(modelConfig)) {\n            return modelConfig.getContent();\n        }\n\n        return null;\n    }\n\n    private static String maskApiKey(String input) {\n        if (Objects.isNull(input)) {\n            return null;\n        }\n\n        StringBuilder maskedString = new StringBuilder(input);\n        for (int i = input.length() / 4; i < input.length() / 2; i++) {\n            maskedString.setCharAt(i, '*');\n        }\n        return maskedString.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/interceptor/Chat2dbHeaderAuthorizationInterceptor.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.chat2db.interceptor;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport ai.chat2db.server.web.api.util.StringUtils;\nimport cn.hutool.http.ContentType;\nimport cn.hutool.http.Header;\nimport lombok.Getter;\nimport okhttp3.Interceptor;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\nimport java.io.IOException;\n\n/**\n * Description: Request to add header apikey\n *\n * @author grt\n * @since 2023-03-23\n */\n@Getter\npublic class Chat2dbHeaderAuthorizationInterceptor implements Interceptor {\n\n    private String apiKey;\n\n    private String model;\n\n    public Chat2dbHeaderAuthorizationInterceptor(String apiKey, String model) {\n        this.apiKey = apiKey;\n        this.model = model;\n        if (StringUtils.isEmpty(model)) {\n            this.model = AiSqlSourceEnum.OPENAI.getCode();\n        }\n    }\n\n    @Override\n    public Response intercept(Chain chain) throws IOException {\n        Request original = chain.request();\n        Request request = original.newBuilder()\n                .header(Header.AUTHORIZATION.getValue(), \"Bearer \" + apiKey)\n                .header(\"X-CHAT2DB-AI-TYPE\", model)\n                .header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue())\n                .method(original.method(), original.body())\n                .build();\n        return chain.proceed(request);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/listener/Chat2dbAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.chat2db.listener;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.web.api.controller.ai.baichuan.model.BaichuanChatCompletions;\nimport ai.chat2db.server.web.api.controller.ai.baichuan.model.BaichuanChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.response.ChatCompletionResponse;\nimport ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletions;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.util.Objects;\n\n/**\n * Description: Chat2dbAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class Chat2dbAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    public Chat2dbAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"Chat2db AI 建立sse连接...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"Chat2db AI returns data: {}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"Chat2db AI return data is over\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n        ObjectMapper mapper = new ObjectMapper();\n        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n        ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class);\n        String text = completionResponse.getChoices().get(0).getDelta() == null\n                ? completionResponse.getChoices().get(0).getText()\n                : completionResponse.getChoices().get(0).getDelta().getContent();\n        String completionId = completionResponse.getId();\n\n        Message message = new Message();\n        if (text != null) {\n            message.setContent(text);\n            sseEmitter.send(SseEmitter.event()\n                .id(completionId)\n                .data(message)\n                .reconnectTime(3000));\n        }\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        sseEmitter.complete();\n        log.info(\"Chat2db AI closes sse connection...\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = null;\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                log.error(\"Chat2db AI sse connection exception data: {}\", bodyString, t);\n            } else {\n                log.error(\"Chat2db AI sse connection exception data: {}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"Chat2db AI Error：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Exception in sending data:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/client/ClaudeAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.claude.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author jipengfei\n * @version : OpenAIClient.java\n */\n@Slf4j\npublic class ClaudeAIClient {\n\n    public static final String CLAUDE_SESSION_KEY = \"claude.sessionKey\";\n\n    public static final String CLAUDE_API_HOST = \"claude.apiHost\";\n\n    public static final String CLAUDE_ORG_ID = \"claude.orgId\";\n\n    public static final String CLAUDE_USER_ID = \"claude.userId\";\n\n\n    private static ClaudeAiStreamClient CLAUDE_AI_STREAM_CLIENT;\n    private static String apiKey;\n\n    public static ClaudeAiStreamClient getInstance() {\n        if (CLAUDE_AI_STREAM_CLIENT != null) {\n            return CLAUDE_AI_STREAM_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static ClaudeAiStreamClient singleton() {\n        if (CLAUDE_AI_STREAM_CLIENT == null) {\n            synchronized (ClaudeAIClient.class) {\n                if (CLAUDE_AI_STREAM_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return CLAUDE_AI_STREAM_CLIENT;\n    }\n\n    public static void refresh() {\n        String apikey = \"\";\n        String orgId = \"\";\n        String userId = \"\";\n        String apiHost = \"https://claude.ai/api/append_message\";\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(CLAUDE_API_HOST).getData();\n        if (apiHostConfig != null) {\n            apiHost = apiHostConfig.getContent();\n        }\n        Config config = configService.find(CLAUDE_SESSION_KEY).getData();\n        if (config != null) {\n            apikey = config.getContent();\n        }\n        Config orgConfig = configService.find(CLAUDE_ORG_ID).getData();\n        if (orgConfig != null) {\n            orgId = orgConfig.getContent();\n        }\n        Config userConfig = configService.find(CLAUDE_USER_ID).getData();\n        if (userConfig != null) {\n            userId = userConfig.getContent();\n        }\n        log.info(\"refresh claude sessionKey:{}\", maskApiKey(apikey));\n        CLAUDE_AI_STREAM_CLIENT = ClaudeAiStreamClient.builder().apiHost(apiHost)\n                .sessionKey(apikey).orgId(orgId).userId(userId).build();\n        apiKey = apikey;\n    }\n\n    private static String maskApiKey(String input) {\n        if (StringUtils.isBlank(input)) {\n            return input;\n        }\n\n        StringBuilder maskedString = new StringBuilder(input);\n        for (int i = input.length() / 4; i < input.length() / 2; i++) {\n            maskedString.setCharAt(i, '*');\n        }\n        return maskedString.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/client/ClaudeAiStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.claude.client;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.claude.interceptor.ClaudeHeaderAuthorizationInterceptor;\nimport ai.chat2db.server.web.api.controller.ai.claude.model.ClaudeChatMessage;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport okhttp3.sse.EventSources;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Custom AI interface client\n *\n * @author moji\n */\n@Slf4j\npublic class ClaudeAiStreamClient {\n\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String sessionKey;\n\n    /**\n     * endpoint\n     */\n    @Getter\n    @NotNull\n    private String orgId;\n\n    /**\n     * deployId\n     */\n    @Getter\n    private String apiHost;\n\n    @Getter\n    private String userId;\n\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n\n    /**\n     * @param builder\n     */\n    private ClaudeAiStreamClient(Builder builder) {\n        this.sessionKey = builder.sessionKey;\n        this.orgId = builder.orgId;\n        this.apiHost = builder.apiHost;\n        this.userId = builder.userId;\n        if (Objects.isNull(builder.okHttpClient)) {\n            builder.okHttpClient = this.okHttpClient();\n        }\n        okHttpClient = builder.okHttpClient;\n    }\n\n    /**\n     * okhttpclient\n     */\n    private OkHttpClient okHttpClient() {\n        OkHttpClient okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new ClaudeHeaderAuthorizationInterceptor(this.sessionKey, this.orgId))\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n        return okHttpClient;\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static ClaudeAiStreamClient.Builder builder() {\n        return new ClaudeAiStreamClient.Builder();\n    }\n\n    public static final class Builder {\n        private String sessionKey;\n\n        private String orgId;\n\n        private String apiHost;\n\n        private String userId;\n\n        /**\n         * Customize OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public ClaudeAiStreamClient.Builder sessionKey(String sessionKey) {\n            this.sessionKey = sessionKey;\n            return this;\n        }\n\n        /**\n         * @param apiHost\n         * @return\n         */\n        public ClaudeAiStreamClient.Builder apiHost(String apiHost) {\n            this.apiHost = apiHost;\n            return this;\n        }\n\n        /**\n         * @param orgId\n         * @return\n         */\n        public ClaudeAiStreamClient.Builder orgId(String orgId) {\n            this.orgId = orgId;\n            return this;\n        }\n\n        public ClaudeAiStreamClient.Builder userId(String userId) {\n            this.userId = userId;\n            return this;\n        }\n\n        public ClaudeAiStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public ClaudeAiStreamClient build() {\n            return new ClaudeAiStreamClient(this);\n        }\n\n    }\n\n    /**\n     * chat\n     *\n     * @param claudeChatMessage\n     * @param eventSourceListener\n     */\n    public void streamCompletions(ClaudeChatMessage claudeChatMessage, EventSourceListener eventSourceListener) {\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：AzureEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        log.info(\"Claude AI, prompt:{}\", claudeChatMessage.getText());\n        try {\n            claudeChatMessage.setOrganization_uuid(this.orgId);\n            claudeChatMessage.setConversation_uuid(this.userId);\n            EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(claudeChatMessage);\n\n            Request request = new Request.Builder()\n                .url(this.apiHost)\n                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                .build();\n            //Create event\n            EventSource eventSource = factory.newEventSource(request, eventSourceListener);\n            log.info(\"finish invoking claude ai\");\n        } catch (Exception e) {\n            log.error(\"claude ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/interceptor/ClaudeHeaderAuthorizationInterceptor.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.claude.interceptor;\n\nimport cn.hutool.http.ContentType;\nimport cn.hutool.http.Header;\nimport lombok.Getter;\nimport okhttp3.Interceptor;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\nimport java.io.IOException;\n\n/**\n * Description: Request to add header apikey\n *\n * @author grt\n * @since 2023-03-23\n */\n@Getter\npublic class ClaudeHeaderAuthorizationInterceptor implements Interceptor {\n\n    private String sessionKey;\n\n    private String orgId;\n\n    public ClaudeHeaderAuthorizationInterceptor(String sessionKey, String orgId) {\n        this.orgId = orgId;\n        this.sessionKey = sessionKey;\n    }\n\n    @Override\n    public Response intercept(Chain chain) throws IOException {\n        Request original = chain.request();\n        Request request = original.newBuilder()\n                .header(\"Cookie\", \"sessionKey=\" + sessionKey)\n                .header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue())\n                .method(original.method(), original.body())\n                .build();\n        return chain.proceed(request);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/listener/ClaudeAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.claude.listener;\n\nimport ai.chat2db.server.web.api.controller.ai.claude.model.ClaudeCompletionResponse;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.util.Objects;\n\n/**\n * ClaudeAIEventSourceListener\n */\n@Slf4j\npublic class ClaudeAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n\n    public ClaudeAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"ClaudeAIEventSourceListener...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"Claude AI data：{}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"Claude AI end\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n        // Read JSON\n        ClaudeCompletionResponse completionResponse = mapper.readValue(data, ClaudeCompletionResponse.class);\n        String text = completionResponse.getCompletion();\n        Message message = new Message();\n        if (text != null) {\n            message.setContent(text);\n            sseEmitter.send(SseEmitter.event()\n                .id(null)\n                .data(message)\n                .reconnectTime(3000));\n        }\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        sseEmitter.complete();\n        log.info(\"Claude AI closed...\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = null;\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                log.error(\"Claude sse error：{}\", bodyString, t);\n            } else {\n                log.error(\"Claude sse body error：{}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"Claude sse error：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Exception in sending data:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/model/ClaudeChatCompletionsOptions.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.claude.model;\n\nimport lombok.Data;\n\n@Data\npublic final class ClaudeChatCompletionsOptions {\n\n    private Boolean incremental = true;\n\n    private String model = \"claude-2\";\n\n    private String prompt;\n\n    private String timezone = \"Asia/Shanghai\";\n\n    private Boolean stream = true;\n\n    public Boolean isStream() {\n        return this.stream;\n    }\n\n    public ClaudeChatCompletionsOptions setStream(Boolean stream) {\n        this.stream = stream;\n        return this;\n    }\n\n    public String getModel() {\n        return this.model;\n    }\n\n    public ClaudeChatCompletionsOptions setModel(String model) {\n        this.model = model;\n        return this;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/model/ClaudeChatMessage.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.claude.model;\n\nimport lombok.Data;\n\n@Data\npublic class ClaudeChatMessage {\n\n    private String conversation_uuid;\n\n    private String organization_uuid;\n\n    private String text;\n\n    private ClaudeChatCompletionsOptions completion;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/model/ClaudeCompletionResponse.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.claude.model;\n\nimport com.unfbx.chatgpt.entity.common.Usage;\nimport lombok.Data;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * @author moji\n * @version : ClaudeCompletionResponse.java\n */\n@Data\npublic class ClaudeCompletionResponse implements Serializable {\n    @Serial\n    private static final long serialVersionUID = 4968922211204353592L;\n    private String log_id;\n    private String stop_reason;\n    private String stop;\n    private String model;\n    private String completion;\n    private Usage usage;\n    private ClaudeMessageLimit messageLimit;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/model/ClaudeMessageLimit.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.claude.model;\n\nimport lombok.Data;\n\n@Data\npublic class ClaudeMessageLimit {\n\n    private String type;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/config/LocalCache.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.config;\n\nimport cn.hutool.cache.CacheUtil;\nimport cn.hutool.cache.impl.TimedCache;\nimport cn.hutool.core.date.DateUnit;\n\n/**\n * description：\n *\n * @author https:www.unfbx.com\n * @date 2023-03-10\n */\npublic class LocalCache {\n    /**\n     * Cache duration\n     */\n    public static final long TIMEOUT = 5 * DateUnit.MINUTE.getMillis();\n    /**\n     * Cleanup interval\n     */\n    private static final long CLEAN_TIMEOUT = 5 * DateUnit.MINUTE.getMillis();\n    /**\n     * Cache object\n     */\n    public static final TimedCache<String, Object> CACHE = CacheUtil.newTimedCache(TIMEOUT);\n\n    static {\n        //Start a scheduled task\n        CACHE.schedulePrune(CLEAN_TIMEOUT);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/converter/ChatConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.converter;\n\nimport ai.chat2db.server.domain.api.param.TableQueryParam;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatItem;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsUsage;\nimport ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;\n\nimport com.unfbx.chatgpt.entity.common.Usage;\nimport com.unfbx.chatgpt.entity.embeddings.EmbeddingResponse;\nimport com.unfbx.chatgpt.entity.embeddings.Item;\nimport org.mapstruct.Mapper;\n\n/**\n * @author moji\n * @version ChatConverter.java, v 0.1 April 2, 2023 13:31 moji Exp $\n * @date 2023/04/02\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class ChatConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract TableQueryParam chat2tableQuery(ChatQueryRequest request);\n\n    /**\n     * chat convert\n     *\n     * @param item\n     * @return\n     */\n    public abstract FastChatItem item2ChatItem(Item item);\n\n    /**\n     * usage convert\n     *\n     * @param usage\n     * @return\n     */\n    public abstract FastChatCompletionsUsage usage2usage(Usage usage);\n\n    /**\n     * response convert\n     *\n     * @param embeddingResponse\n     * @return\n     */\n    public abstract FastChatEmbeddingResponse response2response(EmbeddingResponse embeddingResponse);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/GptVersionType.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\n\nimport lombok.Getter;\n\n/**\n * @author moji\n * @version GptModelType.java, v 0.1 April 9, 2023 19:05 moji Exp $\n * @date 2023/04/09\n */\n@Getter\npublic enum GptVersionType implements BaseEnum {\n\n    /**\n     * GPT-3\n     */\n    GPT3(\"GPT-3\"),\n\n    /**\n     * GPT-3-5\n     */\n    GPT35(\"GPT-3.5\"),\n    ;\n\n    final String description;\n\n    GptVersionType(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\n\nimport lombok.Getter;\n\n/**\n * prompt type\n *\n * @author moji\n * @version PromptType.java, v 0.1 April 9, 2023 15:36 moji Exp $\n * @date 2023/04/09\n */\n@Getter\npublic enum PromptType implements BaseEnum<String> {\n\n    /**\n     * Convert natural language to SQL\n     */\n    NL_2_SQL(\"Convert natural language into SQL queries\"),\n\n    /**\n     * Interpret SQL\n     */\n    SQL_EXPLAIN(\"Interpret SQL\"),\n\n    /**\n     * SQL optimization\n     */\n    SQL_OPTIMIZER(\"Provide optimization suggestions\"),\n\n    /**\n     * SQL conversion\n     */\n    SQL_2_SQL(\"Perform SQL conversion\"),\n\n    /**\n     * text generation\n     */\n    TEXT_GENERATION(\"text generation\"),\n    ;\n\n    final String description;\n\n    PromptType(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/client/FastChatAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.fastchat.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author moji\n * @date 23/09/26\n */\n@Slf4j\npublic class FastChatAIClient {\n\n    /**\n     * FASTCHAT OPENAI KEY\n     */\n    public static final String FASTCHAT_API_KEY = \"fastchat.chatgpt.apiKey\";\n\n    /**\n     * FASTCHAT OPENAI HOST\n     */\n    public static final String FASTCHAT_HOST = \"fastchat.host\";\n\n    /**\n     * FASTCHAT OPENAI model\n     */\n    public static final String FASTCHAT_MODEL= \"fastchat.model\";\n\n    /**\n     * FASTCHAT OPENAI embedding model\n     */\n    public static final String FASTCHAT_EMBEDDING_MODEL = \"fastchat.embedding.model\";\n\n    private static FastChatAIStreamClient FASTCHAT_AI_CLIENT;\n\n\n    public static FastChatAIStreamClient getInstance() {\n        if (FASTCHAT_AI_CLIENT != null) {\n            return FASTCHAT_AI_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static FastChatAIStreamClient singleton() {\n        if (FASTCHAT_AI_CLIENT == null) {\n            synchronized (FastChatAIClient.class) {\n                if (FASTCHAT_AI_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return FASTCHAT_AI_CLIENT;\n    }\n\n    public static void refresh() {\n        String apiKey = \"\";\n        String apiHost = \"\";\n        String model = \"\";\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(FASTCHAT_HOST).getData();\n        if (apiHostConfig != null && StringUtils.isNotBlank(apiHostConfig.getContent())) {\n            apiHost = apiHostConfig.getContent();\n        }\n        Config config = configService.find(FASTCHAT_API_KEY).getData();\n        if (config != null && StringUtils.isNotBlank(config.getContent())) {\n            apiKey = config.getContent();\n        }\n        Config deployConfig = configService.find(FASTCHAT_MODEL).getData();\n        if (deployConfig != null && StringUtils.isNotBlank(deployConfig.getContent())) {\n            model = deployConfig.getContent();\n        }\n        FASTCHAT_AI_CLIENT = FastChatAIStreamClient.builder().apiKey(apiKey).apiHost(apiHost).model(model)\n            .build();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/client/FastChatAIStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.client;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbedding;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.interceptor.FastChatHeaderAuthorizationInterceptor;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsOptions;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.reactivex.Single;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport okhttp3.sse.EventSources;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport retrofit2.Retrofit;\nimport retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;\nimport retrofit2.converter.jackson.JacksonConverterFactory;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Fast Chat Aligned Client\n *\n * @author moji\n */\n@Slf4j\npublic class FastChatAIStreamClient {\n\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String apiKey;\n\n    /**\n     * apiHost\n     */\n    @Getter\n    @NotNull\n    private String apiHost;\n\n    /**\n     * model\n     */\n    @Getter\n    private String model;\n\n    /**\n     * embeddingModel\n     */\n    @Getter\n    private String embeddingModel;\n\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n    @Getter\n    private FastChatOpenAiApi fastChatOpenAiApi;\n\n\n    /**\n     * @param builder\n     */\n    private FastChatAIStreamClient(Builder builder) {\n        this.apiKey = builder.apiKey;\n        this.apiHost = builder.apiHost;\n        this.model = builder.model;\n        this.embeddingModel = builder.embeddingModel;\n        if (Objects.isNull(builder.okHttpClient)) {\n            builder.okHttpClient = this.okHttpClient();\n        }\n        okHttpClient = builder.okHttpClient;\n        this.fastChatOpenAiApi = new Retrofit.Builder()\n                .baseUrl(apiHost)\n                .client(okHttpClient)\n                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())\n                .addConverterFactory(JacksonConverterFactory.create())\n                .build().create(FastChatOpenAiApi.class);\n    }\n\n    /**\n     * okhttpclient\n     */\n    private OkHttpClient okHttpClient() {\n        OkHttpClient okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new FastChatHeaderAuthorizationInterceptor(this.apiKey))\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n        return okHttpClient;\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static FastChatAIStreamClient.Builder builder() {\n        return new FastChatAIStreamClient.Builder();\n    }\n\n    /**\n     * builder\n     */\n    public static final class Builder {\n        private String apiKey;\n\n        private String apiHost;\n\n        private String model;\n\n        private String embeddingModel;\n\n        /**\n         * OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public FastChatAIStreamClient.Builder apiKey(String apiKeyValue) {\n            this.apiKey = apiKeyValue;\n            return this;\n        }\n\n        /**\n         * @param apiHostValue\n         * @return\n         */\n        public FastChatAIStreamClient.Builder apiHost(String apiHostValue) {\n            this.apiHost = apiHostValue;\n            return this;\n        }\n\n        /**\n         * @param modelValue\n         * @return\n         */\n        public FastChatAIStreamClient.Builder model(String modelValue) {\n            this.model = modelValue;\n            return this;\n        }\n\n        public FastChatAIStreamClient.Builder embeddingModel(String embeddingModelValue) {\n            this.embeddingModel = embeddingModelValue;\n            return this;\n        }\n\n        public FastChatAIStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public FastChatAIStreamClient build() {\n            return new FastChatAIStreamClient(this);\n        }\n\n    }\n\n    /**\n     * Q&A interface stream form\n     *\n     * @param chatMessages\n     * @param eventSourceListener\n     */\n    public void streamCompletions(List<FastChatMessage> chatMessages, EventSourceListener eventSourceListener) {\n        if (CollectionUtils.isEmpty(chatMessages)) {\n            log.error(\"param error：Fast Chat Prompt cannot be empty\");\n            throw new ParamBusinessException(\"prompt\");\n        }\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：FastChatEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        log.info(\"Fast Chat AI, prompt:{}\", chatMessages.get(chatMessages.size() - 1).getContent());\n        try {\n\n            FastChatCompletionsOptions chatCompletionsOptions = new FastChatCompletionsOptions(chatMessages);\n            chatCompletionsOptions.setStream(true);\n            chatCompletionsOptions.setModel(this.model);\n\n            EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(chatCompletionsOptions);\n            Request request = new Request.Builder()\n                .url(apiHost)\n                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                .build();\n            //Create event\n            EventSource eventSource = factory.newEventSource(request, eventSourceListener);\n            log.info(\"finish invoking fast chat ai\");\n        } catch (Exception e) {\n            log.error(\"fast chat ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n    /**\n     * Creates an embedding vector representing the input text.\n     *\n     * @param input\n     * @return EmbeddingResponse\n     */\n    public FastChatEmbeddingResponse embeddings(String input) {\n        FastChatEmbedding embedding = FastChatEmbedding.builder().input(input).build();\n        if (StringUtils.isNotBlank(this.embeddingModel)) {\n            embedding.setModel(this.embeddingModel);\n        }\n        return this.embeddings(embedding);\n    }\n\n    /**\n     * Creates an embedding vector representing the input text.\n     *\n     * @param embedding\n     * @return EmbeddingResponse\n     */\n    public FastChatEmbeddingResponse embeddings(FastChatEmbedding embedding) {\n        Single<FastChatEmbeddingResponse> embeddings = this.fastChatOpenAiApi.embeddings(embedding);\n        return embeddings.blockingGet();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/client/FastChatOpenAiApi.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.client;\r\n\r\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbedding;\r\nimport ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;\r\nimport com.unfbx.chatgpt.entity.billing.CreditGrantsResponse;\r\nimport com.unfbx.chatgpt.entity.chat.ChatCompletion;\r\nimport com.unfbx.chatgpt.entity.chat.ChatCompletionResponse;\r\nimport com.unfbx.chatgpt.entity.common.DeleteResponse;\r\nimport com.unfbx.chatgpt.entity.common.OpenAiResponse;\r\nimport com.unfbx.chatgpt.entity.completions.Completion;\r\nimport com.unfbx.chatgpt.entity.completions.CompletionResponse;\r\nimport com.unfbx.chatgpt.entity.edits.Edit;\r\nimport com.unfbx.chatgpt.entity.edits.EditResponse;\r\nimport com.unfbx.chatgpt.entity.embeddings.Embedding;\r\nimport com.unfbx.chatgpt.entity.embeddings.EmbeddingResponse;\r\nimport com.unfbx.chatgpt.entity.engines.Engine;\r\nimport com.unfbx.chatgpt.entity.files.File;\r\nimport com.unfbx.chatgpt.entity.files.UploadFileResponse;\r\nimport com.unfbx.chatgpt.entity.fineTune.Event;\r\nimport com.unfbx.chatgpt.entity.fineTune.FineTune;\r\nimport com.unfbx.chatgpt.entity.fineTune.FineTuneResponse;\r\nimport com.unfbx.chatgpt.entity.images.Image;\r\nimport com.unfbx.chatgpt.entity.images.ImageResponse;\r\nimport com.unfbx.chatgpt.entity.models.Model;\r\nimport com.unfbx.chatgpt.entity.models.ModelResponse;\r\nimport com.unfbx.chatgpt.entity.moderations.Moderation;\r\nimport com.unfbx.chatgpt.entity.moderations.ModerationResponse;\r\nimport com.unfbx.chatgpt.entity.whisper.WhisperResponse;\r\nimport io.reactivex.Single;\r\nimport okhttp3.MultipartBody;\r\nimport okhttp3.RequestBody;\r\nimport okhttp3.ResponseBody;\r\nimport retrofit2.http.*;\r\n\r\nimport java.util.Map;\r\n\r\n/**\r\n * Description: open ai official api interface\r\n *\r\n * @author https:www.unfbx.com\r\n *  2023-02-15\r\n */\r\npublic interface FastChatOpenAiApi {\r\n\r\n    /**\r\n     * Creates an embedding vector representing the input text.\r\n     *\r\n     * @param embedding\r\n     * @return Single EmbeddingResponse\r\n     */\r\n    @POST(\"v1/embeddings\")\r\n    Single<FastChatEmbeddingResponse> embeddings(@Body FastChatEmbedding embedding);\r\n\r\n}\r\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/embeddings/FastChatEmbedding.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.embeddings;\r\n\r\nimport com.fasterxml.jackson.annotation.JsonInclude;\r\nimport com.unfbx.chatgpt.exception.BaseException;\r\nimport com.unfbx.chatgpt.exception.CommonError;\r\nimport lombok.*;\r\nimport lombok.extern.slf4j.Slf4j;\r\n\r\nimport java.io.Serializable;\r\nimport java.util.Objects;\r\n\r\n/**\r\n * description：\r\n *\r\n * @author https:www.unfbx.com\r\n *  2023-02-15\r\n */\r\n@Getter\r\n@Slf4j\r\n@Builder\r\n@JsonInclude(JsonInclude.Include.NON_NULL)\r\n@NoArgsConstructor\r\n@AllArgsConstructor\r\npublic class FastChatEmbedding implements Serializable {\r\n    @NonNull\r\n    @Builder.Default\r\n    private String model = Model.TEXT_EMBEDDING_ADA_002.getName();\r\n    /**\r\n     * Required: Length cannot exceed: 8192\r\n     */\r\n    @NonNull\r\n    private String input;\r\n\r\n    private String user;\r\n\r\n    public void setModel(Model model) {\r\n        if (Objects.isNull(model)) {\r\n            model = Model.TEXT_EMBEDDING_ADA_002;\r\n        }\r\n        this.model = model.getName();\r\n    }\r\n\r\n    public void setModel(String model) {\r\n        if (Objects.isNull(model)) {\r\n            model = Model.TEXT_EMBEDDING_ADA_002.getName();\r\n        }\r\n        this.model = model;\r\n    }\r\n\r\n    public void setInput(String input) {\r\n        if (input == null || \"\".equals(input)) {\r\n            log.error(\"input cannot be empty\");\r\n            throw new BaseException(CommonError.PARAM_ERROR);\r\n        }\r\n        if (input.length() > 8192) {\r\n            log.error(\"input is too long\");\r\n            throw new BaseException(CommonError.PARAM_ERROR);\r\n        }\r\n        this.input = input;\r\n    }\r\n\r\n    public void setUser(String user) {\r\n        this.user = user;\r\n    }\r\n\r\n    @Getter\r\n    @AllArgsConstructor\r\n    public enum Model {\r\n        TEXT_EMBEDDING_ADA_002(\"text-embedding-ada-002\"),\r\n        ;\r\n        private String name;\r\n    }\r\n}\r\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/embeddings/FastChatEmbeddingResponse.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.embeddings;\r\n\r\nimport com.unfbx.chatgpt.entity.common.Usage;\r\nimport lombok.Data;\r\n\r\nimport java.io.Serializable;\r\nimport java.util.List;\r\n\r\n/**\r\n * description：\r\n *\r\n * @author https:www.unfbx.com\r\n *  2023-02-15\r\n */\r\n@Data\r\npublic class FastChatEmbeddingResponse implements Serializable {\r\n\r\n    private String object;\r\n    private List<FastChatItem> data;\r\n    private String model;\r\n    private Usage usage;\r\n}\r\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/embeddings/FastChatItem.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.embeddings;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.io.Serializable;\r\nimport java.math.BigDecimal;\r\nimport java.util.List;\r\n\r\n@Data\r\npublic class FastChatItem implements Serializable {\r\n    private String object;\r\n    private List<BigDecimal> embedding;\r\n    private Integer index;\r\n}\r\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/interceptor/FastChatHeaderAuthorizationInterceptor.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.interceptor;\n\nimport cn.hutool.core.util.RandomUtil;\nimport cn.hutool.http.ContentType;\nimport cn.hutool.http.Header;\nimport com.google.common.collect.Lists;\nimport lombok.Getter;\nimport okhttp3.Interceptor;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\nimport java.io.IOException;\n\n/**\n * header apikey\n *\n * @author grt\n * @since 2023-03-23\n */\n@Getter\npublic class FastChatHeaderAuthorizationInterceptor implements Interceptor {\n\n    private String apiKey;\n\n    public FastChatHeaderAuthorizationInterceptor(String apiKey) {\n        this.apiKey = apiKey;\n    }\n\n    @Override\n    public Response intercept(Chain chain) throws IOException {\n        Request original = chain.request();\n        Request request = original.newBuilder()\n                // replace to your corresponding field and value\n                .header(Header.AUTHORIZATION.getValue(), \"Bearer \" + apiKey)\n                .header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue())\n                .method(original.method(), original.body())\n                .build();\n        return chain.proceed(request);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/listener/FastChatAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.listener;\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatChoice;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletions;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsUsage;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\n/**\n * description：OpenAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class FastChatAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n\n    public FastChatAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"Fast Chat Sse connecting...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"Fast Chat AI response data：{}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"Fast Chat AI closed\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n\n        FastChatCompletions chatCompletions = mapper.readValue(data, FastChatCompletions.class);\n        String text = \"\";\n        log.info(\"Model={} is created at {}.\", chatCompletions.getId(),\n            chatCompletions.getCreated());\n        for (FastChatChoice choice : chatCompletions.getChoices()) {\n            FastChatMessage message = choice.getDelta();\n            if (message != null) {\n                log.info(\"Index: {}, Chat Role: {}\", choice.getIndex(), message.getRole());\n                if (message.getContent() != null) {\n                    text = message.getContent();\n                }\n            }\n        }\n\n        FastChatCompletionsUsage usage = chatCompletions.getUsage();\n        if (usage != null) {\n            log.info(\n                \"Usage: number of prompt token is {}, number of completion token is {}, and number of total \"\n                    + \"tokens in request and response is {}.%n\", usage.getPromptTokens(),\n                usage.getCompletionTokens(), usage.getTotalTokens());\n        }\n\n        Message message = new Message();\n        message.setContent(text);\n        sseEmitter.send(SseEmitter.event()\n            .id(null)\n            .data(message)\n            .reconnectTime(3000));\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        try {\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        sseEmitter.complete();\n        log.info(\"FastChatAI close sse connection...\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = Objects.nonNull(t) ? t.getMessage() : \"\";\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                if (StringUtils.isBlank(bodyString) && Objects.nonNull(t)) {\n                    bodyString = t.getMessage();\n                }\n                log.error(\"Fast Chat AI sse response：{}\", bodyString);\n            } else {\n                log.error(\"Fast Chat AI sse response：{}，error：{}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"Fast Chat AI error：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Fast Chat AI send data error:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatChoice.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.fastchat.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n/**\n * The representation of a single prompt completion as part of an overall completions request. Generally, `n` choices\n * are generated per provided prompt with a default value of 1. Token limits and other settings may limit the number of\n * choices generated.\n */\n@Data\npublic final class FastChatChoice {\n\n    /*\n     * The generated text for a given completions prompt.\n     */\n    @JsonProperty(value = \"text\")\n    private String text;\n\n    /*\n     * The ordered index associated with this completions choice.\n     */\n    @JsonProperty(value = \"index\")\n    private int index;\n\n    /*\n     * The log probabilities model for tokens associated with this completions choice.\n     */\n    @JsonProperty(value = \"delta\")\n    private FastChatMessage delta;\n\n    /*\n     * Reason for finishing\n     */\n    @JsonProperty(value = \"finish_reason\")\n    private FastChatCompletionsFinishReason finishReason;\n\n    /**\n     * Creates an instance of Choice class.\n     *\n     * @param text the text value to set.\n     * @param index the index value to set.\n     * @param delta the message value to set\n     * @param finishReason the finishReason value to set.\n     */\n    @JsonCreator\n    private FastChatChoice(\n            @JsonProperty(value = \"text\") String text,\n            @JsonProperty(value = \"index\") int index,\n            @JsonProperty(value = \"delta\") FastChatMessage delta,\n            @JsonProperty(value = \"finish_reason\") FastChatCompletionsFinishReason finishReason) {\n        this.text = text;\n        this.index = index;\n        this.delta = delta;\n        this.finishReason = finishReason;\n    }\n\n    /**\n     * Get the text property: The generated text for a given completions prompt.\n     *\n     * @return the text value.\n     */\n    public String getText() {\n        return this.text;\n    }\n\n    /**\n     * Get the index property: The ordered index associated with this completions choice.\n     *\n     * @return the index value.\n     */\n    public int getIndex() {\n        return this.index;\n    }\n\n    /**\n     * Get the logprobs property: The log probabilities model for tokens associated with this completions choice.\n     *\n     * @return the logprobs value.\n     */\n    public FastChatMessage getDelta() {\n        return this.delta;\n    }\n\n    /**\n     * Get the finishReason property: Reason for finishing.\n     *\n     * @return the finishReason value.\n     */\n    public FastChatCompletionsFinishReason getFinishReason() {\n        return this.finishReason;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatCompletions.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class FastChatCompletions {\n\n    /*\n     * A unique identifier associated with this chat completions response.\n     */\n    private String id;\n\n    /*\n     * The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     */\n    private int created;\n\n    /**\n     * model\n     */\n    private String model;\n\n    /**\n     * object\n     */\n    private String object;\n\n    /*\n     * The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1.\n     * Token limits and other settings may limit the number of choices generated.\n     */\n    @JsonProperty(value = \"choices\")\n    private List<FastChatChoice> choices;\n\n    /*\n     * Usage information for tokens processed and generated as part of this completions operation.\n     */\n    private FastChatCompletionsUsage usage;\n\n    /**\n     * Creates an instance of ChatCompletions class.\n     *\n     * @param id the id value to set.\n     * @param created the created value to set.\n     * @param choices the choices value to set.\n     * @param usage the usage value to set.\n     */\n    @JsonCreator\n    private FastChatCompletions(\n        @JsonProperty(value = \"id\") String id,\n        @JsonProperty(value = \"created\") int created,\n        @JsonProperty(value = \"model\") String model,\n        @JsonProperty(value = \"object\") String object,\n        @JsonProperty(value = \"choices\") List<FastChatChoice> choices,\n        @JsonProperty(value = \"usage\") FastChatCompletionsUsage usage) {\n        this.id = id;\n        this.created = created;\n        this.model = model;\n        this.object = object;\n        this.choices = choices;\n        this.usage = usage;\n    }\n\n    /**\n     * Get the id property: A unique identifier associated with this chat completions response.\n     *\n     * @return the id value.\n     */\n    public String getId() {\n        return this.id;\n    }\n\n    /**\n     * Get the created property: The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     *\n     * @return the created value.\n     */\n    public int getCreated() {\n        return this.created;\n    }\n\n    /**\n     * Get the choices property: The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1. Token limits and other\n     * settings may limit the number of choices generated.\n     *\n     * @return the choices value.\n     */\n    public List<FastChatChoice> getChoices() {\n        return this.choices;\n    }\n\n    /**\n     * Get the usage property: Usage information for tokens processed and generated as part of this completions\n     * operation.\n     *\n     * @return the usage value.\n     */\n    public FastChatCompletionsUsage getUsage() {\n        return this.usage;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatCompletionsFinishReason.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.fastchat.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\n\nimport java.util.Collection;\n\n/** Representation of the manner in which a completions response concluded. */\npublic final class FastChatCompletionsFinishReason extends FastChatExpandableStringEnum<FastChatCompletionsFinishReason> {\n\n    /** Completions ended normally and reached its end of token generation. */\n    public static final FastChatCompletionsFinishReason STOPPED = fromString(\"stopped\");\n\n    /** Completions exhausted available token limits before generation could complete. */\n    public static final FastChatCompletionsFinishReason TOKEN_LIMIT_REACHED = fromString(\"tokenLimitReached\");\n\n    /**\n     * Completions generated a response that was identified as potentially sensitive per content moderation policies.\n     */\n    public static final FastChatCompletionsFinishReason CONTENT_FILTERED = fromString(\"contentFiltered\");\n\n    /**\n     * Creates a new instance of CompletionsFinishReason value.\n     *\n     * @deprecated Use the {@link #fromString(String)} factory method.\n     */\n    @Deprecated\n    public FastChatCompletionsFinishReason() {}\n\n    /**\n     * Creates or finds a CompletionsFinishReason from its string representation.\n     *\n     * @param name a name to look for.\n     * @return the corresponding CompletionsFinishReason.\n     */\n    @JsonCreator\n    public static FastChatCompletionsFinishReason fromString(String name) {\n        return fromString(name, FastChatCompletionsFinishReason.class);\n    }\n\n    /**\n     * Gets known CompletionsFinishReason values.\n     *\n     * @return known CompletionsFinishReason values.\n     */\n    public static Collection<FastChatCompletionsFinishReason> values() {\n        return values(FastChatCompletionsFinishReason.class);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatCompletionsOptions.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.fastchat.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * The configuration information for a chat completions request. Completions support a wide variety of tasks and\n * generate text that continues from or \"completes\" provided prompt data.\n */\n@Data\npublic final class FastChatCompletionsOptions {\n\n    /*\n     * The collection of context messages associated with this chat completions request.\n     * Typical usage begins with a chat message for the System role that provides instructions for\n     * the behavior of the assistant, followed by alternating messages between the User and\n     * Assistant roles.\n     */\n    private List<FastChatMessage> messages;\n\n\n    /*\n     * A value indicating whether chat completions should be streamed for this request.\n     */\n    private Boolean stream;\n    //\n    /*\n     * The model name to provide as part of this completions request.\n     * Not applicable to Fast Chat AI, where deployment information should be included in the Fast Chat\n     * resource URI that's connected to.\n     */\n    private String model;\n\n    /**\n     * Creates an instance of ChatCompletionsOptions class.\n     *\n     * @param messages the messages value to set.\n     */\n    @JsonCreator\n    public FastChatCompletionsOptions(@JsonProperty(value = \"messages\") List<FastChatMessage> messages) {\n        this.messages = messages;\n    }\n\n\n    /**\n     * Get the stream property: A value indicating whether chat completions should be streamed for this request.\n     *\n     * @return the stream value.\n     */\n    public Boolean isStream() {\n        return this.stream;\n    }\n\n    /**\n     * Set the stream property: A value indicating whether chat completions should be streamed for this request.\n     *\n     * @param stream the stream value to set.\n     * @return the ChatCompletionsOptions object itself.\n     */\n    public FastChatCompletionsOptions setStream(Boolean stream) {\n        this.stream = stream;\n        return this;\n    }\n\n    /**\n     * Get the model property: The model name to provide as part of this completions request. Not applicable to Fast Chat AI,\n     * where deployment information should be included in the Fast Chat AI resource URI that's connected to.\n     *\n     * @return the model value.\n     */\n    public String getModel() {\n        return this.model;\n    }\n\n    /**\n     * Set the model property: The model name to provide as part of this completions request. Not applicable to Fast Chat AI,\n     * where deployment information should be included in the Fast Chat AI resource URI that's connected to.\n     *\n     * @param model the model value to set.\n     * @return the ChatCompletionsOptions object itself.\n     */\n    public FastChatCompletionsOptions setModel(String model) {\n        this.model = model;\n        return this;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatCompletionsUsage.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.fastchat.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * Representation of the token counts processed for a completions request. Counts consider all tokens across prompts,\n * choices, choice alternates, best_of generations, and other consumers.\n */\n@Data\n@NoArgsConstructor\npublic final class FastChatCompletionsUsage {\n\n    /*\n     * The number of tokens generated across all completions emissions.\n     */\n    @JsonProperty(value = \"completion_tokens\")\n    private int completionTokens;\n\n    /*\n     * The number of tokens in the provided prompts for the completions request.\n     */\n    @JsonProperty(value = \"prompt_tokens\")\n    private int promptTokens;\n\n    /*\n     * The total number of tokens processed for the completions request and response.\n     */\n    @JsonProperty(value = \"total_tokens\")\n    private int totalTokens;\n\n    /**\n     * Creates an instance of CompletionsUsage class.\n     *\n     * @param completionTokens the completionTokens value to set.\n     * @param promptTokens the promptTokens value to set.\n     * @param totalTokens the totalTokens value to set.\n     */\n    @JsonCreator\n    private FastChatCompletionsUsage(\n            @JsonProperty(value = \"completion_tokens\") int completionTokens,\n            @JsonProperty(value = \"prompt_tokens\") int promptTokens,\n            @JsonProperty(value = \"total_tokens\") int totalTokens) {\n        this.completionTokens = completionTokens;\n        this.promptTokens = promptTokens;\n        this.totalTokens = totalTokens;\n    }\n\n    /**\n     * Get the completionTokens property: The number of tokens generated across all completions emissions.\n     *\n     * @return the completionTokens value.\n     */\n    public int getCompletionTokens() {\n        return this.completionTokens;\n    }\n\n    /**\n     * Get the promptTokens property: The number of tokens in the provided prompts for the completions request.\n     *\n     * @return the promptTokens value.\n     */\n    public int getPromptTokens() {\n        return this.promptTokens;\n    }\n\n    /**\n     * Get the totalTokens property: The total number of tokens processed for the completions request and response.\n     *\n     * @return the totalTokens value.\n     */\n    public int getTotalTokens() {\n        return this.totalTokens;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatExpandableStringEnum.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n\npackage ai.chat2db.server.web.api.controller.ai.fastchat.model;\n\nimport ai.chat2db.server.web.api.controller.ai.azure.util.AzureReflectionUtils;\nimport com.fasterxml.jackson.annotation.JsonValue;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static java.lang.invoke.MethodType.methodType;\n\n/**\n * Base implementation for expandable, single string enums.\n *\n * @param <T> a specific expandable enum type\n */\npublic abstract class FastChatExpandableStringEnum<T extends FastChatExpandableStringEnum<T>> {\n    private static final Map<Class<?>, MethodHandle> CONSTRUCTORS = new ConcurrentHashMap<>();\n    private static final Map<Class<?>, ConcurrentHashMap<String, ? extends FastChatExpandableStringEnum<?>>> VALUES\n        = new ConcurrentHashMap<>();\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(FastChatExpandableStringEnum.class);\n    private String name;\n    private Class<T> clazz;\n\n    /**\n     * Creates a new instance of {@link FastChatExpandableStringEnum} without a {@link #toString()} value.\n     * <p>\n     * This constructor shouldn't be called as it will produce a {@link FastChatExpandableStringEnum} which doesn't\n     * have a String enum value.\n     *\n     * @deprecated Use the {@link #fromString(String, Class)} factory method.\n     */\n    @Deprecated\n    public FastChatExpandableStringEnum() {\n    }\n\n    /**\n     * Creates an instance of the specific expandable string enum from a String.\n     *\n     * @param name The value to create the instance from.\n     * @param clazz The class of the expandable string enum.\n     * @param <T> the class of the expandable string enum.\n     * @return The expandable string enum instance.\n     *\n     * @throws RuntimeException wrapping implementation class constructor exception (if any is thrown).\n     */\n    @SuppressWarnings({\"unchecked\", \"deprecation\"})\n    protected static <T extends FastChatExpandableStringEnum<T>> T fromString(String name, Class<T> clazz) {\n        if (name == null) {\n            return null;\n        }\n\n        ConcurrentHashMap<String, ?> clazzValues = VALUES.computeIfAbsent(clazz, key -> new ConcurrentHashMap<>());\n        T value = (T) clazzValues.get(name);\n\n        if (value != null) {\n            return value;\n        } else {\n            MethodHandle ctor = CONSTRUCTORS.computeIfAbsent(clazz, FastChatExpandableStringEnum::getDefaultConstructor);\n\n            if (ctor == null) {\n                // logged in ExpandableStringEnum::getDefaultConstructor\n                return null;\n            }\n\n            try {\n                value = (T) ctor.invoke();\n            } catch (Throwable e) {\n                LOGGER.warn(\"Failed to create {}, default constructor threw exception\", clazz.getName(), e);\n                return null;\n            }\n\n            return value.nameAndAddValue(name, value, clazz);\n        }\n    }\n\n    private static <T> MethodHandle getDefaultConstructor(Class<T> clazz) {\n        try {\n            MethodHandles.Lookup lookup = AzureReflectionUtils.getLookupToUse(clazz);\n            return lookup.findConstructor(clazz, methodType(void.class));\n        } catch (NoSuchMethodException | IllegalAccessException e) {\n            LOGGER.info(\"Can't find or access default constructor for {}\", clazz.getName(), e);\n        } catch (Exception e) {\n            LOGGER.info(\"Failed to get lookup for {}\", clazz.getName(), e);\n        }\n\n        return null;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    T nameAndAddValue(String name, T value, Class<T> clazz) {\n        this.name = name;\n        this.clazz = clazz;\n\n        ((ConcurrentHashMap<String, T>) VALUES.get(clazz)).put(name, value);\n        return (T) this;\n    }\n\n    /**\n     * Gets a collection of all known values to an expandable string enum type.\n     *\n     * @param clazz the class of the expandable string enum.\n     * @param <T> the class of the expandable string enum.\n     * @return A collection of all known values for the given {@code clazz}.\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected static <T extends FastChatExpandableStringEnum<T>> Collection<T> values(Class<T> clazz) {\n        return new ArrayList<T>((Collection<T>) VALUES.getOrDefault(clazz, new ConcurrentHashMap<>()).values());\n    }\n\n    @Override\n    @JsonValue\n    public String toString() {\n        return this.name;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(this.clazz, this.name);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == null) {\n            return false;\n        } else if (clazz == null || !clazz.isAssignableFrom(obj.getClass())) {\n            return false;\n        } else if (obj == this) {\n            return true;\n        } else if (this.name == null) {\n            return ((FastChatExpandableStringEnum<T>) obj).name == null;\n        } else {\n            return this.name.equals(((FastChatExpandableStringEnum<T>) obj).name);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatMessage.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n@Data\npublic class FastChatMessage {\n\n    /*\n     * The role associated with this message payload.\n     */\n    @JsonProperty(value = \"role\")\n    private FastChatRole role;\n\n    /*\n     * The text associated with this message payload.\n     */\n    @JsonProperty(value = \"content\")\n    private String content;\n\n    /**\n     * Creates an instance of ChatMessage class.\n     *\n     * @param role the role value to set.\n     */\n    @JsonCreator\n    public FastChatMessage(@JsonProperty(value = \"role\") FastChatRole role) {\n        this.role = role;\n    }\n\n    /**\n     * Get the role property: The role associated with this message payload.\n     *\n     * @return the role value.\n     */\n    public FastChatRole getRole() {\n        return this.role;\n    }\n\n    /**\n     * Get the content property: The text associated with this message payload.\n     *\n     * @return the content value.\n     */\n    public String getContent() {\n        return this.content;\n    }\n\n    /**\n     * Set the content property: The text associated with this message payload.\n     *\n     * @param content the content value to set.\n     * @return the ChatMessage object itself.\n     */\n    public FastChatMessage setContent(String content) {\n        this.content = content;\n        return this;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatRole.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.fastchat.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\n\nimport java.util.Collection;\n\npublic class FastChatRole extends FastChatExpandableStringEnum<FastChatRole> {\n\n    /** The role that instructs or sets the behavior of the assistant. */\n    public static final FastChatRole SYSTEM = fromString(\"system\");\n\n    /** The role that provides responses to system-instructed, user-prompted input. */\n    public static final FastChatRole ASSISTANT = fromString(\"assistant\");\n\n    /** The role that provides input for chat completions. */\n    public static final FastChatRole USER = fromString(\"user\");\n\n    /**\n     * Creates a new instance of ChatRole value.\n     *\n     * @deprecated Use the {@link #fromString(String)} factory method.\n     */\n    @Deprecated\n    public FastChatRole() {}\n\n    /**\n     * Creates or finds a ChatRole from its string representation.\n     *\n     * @param name a name to look for.\n     * @return the corresponding ChatRole.\n     */\n    @JsonCreator\n    public static FastChatRole fromString(String name) {\n        return fromString(name, FastChatRole.class);\n    }\n\n\n    /**\n     * Gets known ChatRole values.\n     *\n     * @return known ChatRole values.\n     */\n    public static Collection<FastChatRole> values() {\n        return values(FastChatRole.class);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/client/OpenAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.openai.client;\n\nimport java.net.InetSocketAddress;\nimport java.net.Proxy;\nimport java.util.Objects;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\n\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport com.google.common.collect.Lists;\nimport com.unfbx.chatgpt.OpenAiStreamClient;\nimport com.unfbx.chatgpt.constant.OpenAIConst;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.OkHttpClient;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author jipengfei\n * @version : OpenAIClient.java\n */\n@Slf4j\npublic class OpenAIClient {\n\n    public static final String OPENAI_KEY = \"chatgpt.apiKey\";\n\n    /**\n     * OPENAI interface domain name\n     */\n    public static final String OPENAI_HOST = \"chatgpt.apiHost\";\n\n    /**\n     * Proxy IP\n     */\n    public static final String PROXY_HOST = \"chatgpt.proxy.host\";\n\n    /**\n     * proxy port\n     */\n    public static final String PROXY_PORT = \"chatgpt.proxy.port\";\n\n    private static OpenAiStreamClient OPEN_AI_STREAM_CLIENT;\n    private static String apiKey;\n\n    public static OpenAiStreamClient getInstance() {\n        if (OPEN_AI_STREAM_CLIENT != null) {\n            return OPEN_AI_STREAM_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static OpenAiStreamClient singleton() {\n        if (OPEN_AI_STREAM_CLIENT == null) {\n            synchronized (OpenAIClient.class) {\n                if (OPEN_AI_STREAM_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return OPEN_AI_STREAM_CLIENT;\n    }\n\n    public static void refresh() {\n        String apikey;\n        String apiHost = ApplicationContextUtil.getProperty(OPENAI_HOST);\n        if (StringUtils.isBlank(apiHost)) {\n            apiHost = OpenAIConst.OPENAI_HOST;\n        }\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(OPENAI_HOST).getData();\n        if (apiHostConfig != null) {\n            apiHost = apiHostConfig.getContent();\n        }\n        Config config = configService.find(OPENAI_KEY).getData();\n        if (config != null) {\n            apikey = config.getContent();\n        } else {\n            apikey = ApplicationContextUtil.getProperty(OPENAI_KEY);\n        }\n        String host = System.getProperty(\"http.proxyHost\");\n        Config hostConfig = configService.find(PROXY_HOST).getData();\n        if (hostConfig != null) {\n            host = hostConfig.getContent();\n        }\n        Integer port = Objects.nonNull(System.getProperty(\"http.proxyPort\")) ? Integer.valueOf(\n            System.getProperty(\"http.proxyPort\")) : null;\n        Config portConfig = configService.find(PROXY_PORT).getData();\n        if (portConfig != null && StringUtils.isNotBlank(portConfig.getContent())) {\n            port = Integer.valueOf(portConfig.getContent());\n        }\n        log.info(\"refresh openai apikey:{}\", maskApiKey(apikey));\n        if (Objects.nonNull(host) && Objects.nonNull(port)) {\n            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));\n            OkHttpClient okHttpClient = new OkHttpClient.Builder().proxy(proxy).build();\n            OPEN_AI_STREAM_CLIENT = OpenAiStreamClient.builder().apiHost(apiHost).apiKey(\n                Lists.newArrayList(apikey)).okHttpClient(okHttpClient).build();\n        } else {\n            OPEN_AI_STREAM_CLIENT = OpenAiStreamClient.builder().apiHost(apiHost).apiKey(\n                Lists.newArrayList(apikey)).build();\n        }\n        apiKey = apikey;\n    }\n\n    private static String maskApiKey(String input) {\n        if (input == null) {\n            return input;\n        }\n\n        StringBuilder maskedString = new StringBuilder(input);\n        for (int i = input.length() / 4; i < input.length() / 2; i++) {\n            maskedString.setCharAt(i, '*');\n        }\n        return maskedString.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.openai.listener;\n\nimport java.util.Objects;\n\nimport ai.chat2db.server.web.api.controller.ai.response.ChatCompletionResponse;\n\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\n/**\n * description：OpenAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class OpenAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    public OpenAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"OpenAI建立sse连接...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"OpenAI returns data: {}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"OpenAI returns data ended\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n        ObjectMapper mapper = new ObjectMapper();\n        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n        // Read JSON\n        ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class);\n        String text = completionResponse.getChoices().get(0).getDelta() == null\n            ? completionResponse.getChoices().get(0).getText()\n            : completionResponse.getChoices().get(0).getDelta().getContent();\n        Message message = new Message();\n        if (text != null) {\n            message.setContent(text);\n            sseEmitter.send(SseEmitter.event()\n                .id(completionResponse.getId())\n                .data(message)\n                .reconnectTime(3000));\n        }\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        sseEmitter.complete();\n        log.info(\"OpenAI closes sse connection...\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                if (\"No route to host\".equals(message)) {\n                    message = \"The network connection timed out. Please Baidu solve the network problem by yourself.\";\n                }\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = null;\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                log.error(\"OpenAI sse connection exception data: {}\", bodyString, t);\n            } else {\n                log.error(\"OpenAI sse connection exception data: {}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"An exception occurred, please view the detailed log in the help：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Exception in sending data:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/request/ChatQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.request;\n\nimport java.util.List;\n\nimport ai.chat2db.server.web.api.controller.ai.enums.PromptType;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * Chat query input parameters\n *\n * @author moji\n * @version ChatQueryRequest.java, v 0.1 April 2, 2023 13:28 moji Exp $\n * @date 2023/04/02\n */\n@Data\npublic class ChatQueryRequest extends DataSourceBaseRequest {\n\n    /**\n     * Enter message\n     */\n    private String message;\n\n    /**\n     * SQL function type\n     * @see PromptType\n     */\n    private String promptType;\n\n    /**\n     * table name list\n     */\n    private List<String> tableNames;\n\n    /**\n     * Target SQL data type\n     * @see ai.chat2db.server.domain.support.enums.DbTypeEnum\n     */\n    private String destSqlType;\n\n    /**\n     * More remarks: such as requirements or restrictions, etc.\n     */\n    private String ext;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/request/ChatRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.request;\n\nimport java.util.List;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * Chat query input parameters\n *\n * @author moji\n * @version ChatQueryRequest.java, v 0.1 April 2, 2023 13:28 moji Exp $\n * @date 2023/04/02\n */\n@Data\npublic class ChatRequest {\n\n    private String prompt;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/response/ChatChoice.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.response;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : ChatChoice.java\n */\n@Data\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class ChatChoice implements Serializable {\n    @Serial\n    private static final long serialVersionUID = 6347129660363472014L;\n\n    private long index;\n    /**\n     * If the request parameter stream is true, the return value is delta.\n     */\n    @JsonProperty(\"delta\")\n    private Message delta;\n    /**\n     * If the request parameter stream is false, the return value is message.\n     */\n    @JsonProperty(\"message\")\n    private Message message;\n    /**\n     * If the request parameter stream is false, the return value is message.\n     */\n    @JsonProperty(\"finish_reason\")\n    private String finishReason;\n\n    /**\n     * If the request parameter stream is true, the return value is text.\n     */\n    private String text;\n\n    /**\n     * If the request parameter stream is true, the return value is logprobs.\n     */\n    private String logprobs;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/response/ChatCompletionResponse.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.response;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.List;\n\nimport com.unfbx.chatgpt.entity.common.Usage;\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : ChatCompletionResponse.java\n */\n@Data\npublic class ChatCompletionResponse implements Serializable {\n    @Serial\n    private static final long serialVersionUID = 4968922211204353592L;\n    private String id;\n    private String object;\n    private long created;\n    private String model;\n    private List<ChatChoice> choices;\n    private Usage usage;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/client/RestAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.rest.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author moji\n * @version : RestAIClient.java\n */\n@Slf4j\npublic class RestAIClient {\n\n    /**\n     * Interface source selected by AI SQL\n     */\n    public static final String AI_SQL_SOURCE = \"ai.sql.source\";\n\n    /**\n     * Customized AI interface KEY\n     */\n    public static final String REST_AI_API_KEY = \"rest.ai.apiKey\";\n\n    /**\n     * Customized AI interface address\n     */\n    public static final String REST_AI_URL = \"rest.ai.url\";\n\n    /**\n     * Custom AI interface request method\n     */\n    public static final String REST_AI_STREAM_OUT = \"rest.ai.stream\";\n\n    /**\n     * Custom AI interface model\n     */\n    public static final String REST_AI_MODEL = \"rest.ai.model\";\n\n\n\n    private static RestAIStreamClient REST_AI_STREAM_CLIENT;\n\n    public static RestAIStreamClient getInstance() {\n        if (REST_AI_STREAM_CLIENT != null) {\n            return REST_AI_STREAM_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static RestAIStreamClient singleton() {\n        if (REST_AI_STREAM_CLIENT == null) {\n            synchronized (RestAIClient.class) {\n                if (REST_AI_STREAM_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return REST_AI_STREAM_CLIENT;\n    }\n\n    /**\n     * Refresh client\n     */\n    public static void refresh() {\n        String apiUrl = \"\";\n        String apiKey = \"\";\n        String model = \"\";\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(REST_AI_URL).getData();\n        if (apiHostConfig != null) {\n            apiUrl = apiHostConfig.getContent();\n        }\n        Config config = configService.find(REST_AI_API_KEY).getData();\n        if (config != null) {\n            apiKey = config.getContent();\n        }\n        Config deployConfig = configService.find(REST_AI_MODEL).getData();\n        if (deployConfig != null && StringUtils.isNotBlank(deployConfig.getContent())) {\n            model = deployConfig.getContent();\n        }\n        REST_AI_STREAM_CLIENT = RestAIStreamClient.builder().apiKey(apiKey).apiHost(apiUrl).model(model)\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/client/RestAIStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.rest.client;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.interceptor.FastChatHeaderAuthorizationInterceptor;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsOptions;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport okhttp3.sse.EventSources;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Custom AI interface client\n * @author moji\n */\n@Slf4j\npublic class RestAIStreamClient {\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String apiKey;\n\n    /**\n     * apiHost\n     */\n    @Getter\n    @NotNull\n    private String apiHost;\n\n    /**\n     * model\n     */\n    @Getter\n    private String model;\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n    /**\n     * Construct instance object\n     *\n     * @param builder\n     */\n    public RestAIStreamClient(Builder builder) {\n        this.apiKey = builder.apiKey;\n        this.apiHost = builder.apiHost;\n        this.model = builder.model;\n        this.okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new FastChatHeaderAuthorizationInterceptor(this.apiKey))\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static RestAIStreamClient.Builder builder() {\n        return new RestAIStreamClient.Builder();\n    }\n\n    /**\n     * builder\n     */\n    public static final class Builder {\n        private String apiKey;\n\n        private String apiHost;\n\n        private String model;\n\n\n        /**\n         * OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public RestAIStreamClient.Builder apiKey(String apiKeyValue) {\n            this.apiKey = apiKeyValue;\n            return this;\n        }\n\n        /**\n         * @param apiHostValue\n         * @return\n         */\n        public RestAIStreamClient.Builder apiHost(String apiHostValue) {\n            this.apiHost = apiHostValue;\n            return this;\n        }\n\n        /**\n         * @param modelValue\n         * @return\n         */\n        public RestAIStreamClient.Builder model(String modelValue) {\n            this.model = modelValue;\n            return this;\n        }\n\n\n        public RestAIStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public RestAIStreamClient build() {\n            return new RestAIStreamClient(this);\n        }\n\n    }\n\n\n    /**\n     * Q&A interface stream form\n     *\n     * @param chatMessages\n     * @param eventSourceListener\n     */\n    public void streamCompletions(List<FastChatMessage> chatMessages, EventSourceListener eventSourceListener) {\n        if (CollectionUtils.isEmpty(chatMessages)) {\n            log.error(\"param error：Rest AI Prompt cannot be empty\");\n            throw new ParamBusinessException(\"prompt\");\n        }\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：RestAIEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        log.info(\"Rest AI, prompt:{}\", chatMessages.get(chatMessages.size() - 1).getContent());\n        try {\n\n            FastChatCompletionsOptions chatCompletionsOptions = new FastChatCompletionsOptions(chatMessages);\n            chatCompletionsOptions.setStream(true);\n            chatCompletionsOptions.setModel(this.model);\n\n            EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(chatCompletionsOptions);\n            Request request = new Request.Builder()\n                    .url(apiHost)\n                    .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                    .build();\n            //Create event\n            EventSource eventSource = factory.newEventSource(request, eventSourceListener);\n            log.info(\"finish invoking rest ai\");\n        } catch (Exception e) {\n            log.error(\"rest ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/listener/RestAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.rest.listener;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\nimport ai.chat2db.server.web.api.controller.ai.rest.model.RestAIChatCompletions;\nimport ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletions;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\n/**\n * description：RESTAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class RestAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    public RestAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"REST AI建立sse连接...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"REST AI return data:{}\", data);\n        String end = \"[DONE]\";\n        if (data.equals(end)) {\n            log.info(\"REST AI returns data finished\");\n            sseEmitter.send(SseEmitter.event()\n                .id(end)\n                .data(end)\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n        Message message = new Message();\n        if (StringUtils.isNotBlank(data)) {\n            RestAIChatCompletions chatCompletions = mapper.readValue(data, RestAIChatCompletions.class);\n            String text = chatCompletions.getChoices().get(0).getDelta()==null?\n                    chatCompletions.getChoices().get(0).getText()\n                    :chatCompletions.getChoices().get(0).getDelta().getContent();\n            message.setContent(text);\n            sseEmitter.send(SseEmitter.event()\n                .id(id)\n                .data(message)\n                .reconnectTime(3000));\n        }\n    }\n\n    @SneakyThrows\n    @Override\n    public void onClosed(EventSource eventSource) {\n        log.info(\"REST AI close sse connection...\");\n        try {\n            sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\")\n                    .reconnectTime(3000));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        sseEmitter.complete();\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = null;\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                log.error(\"REST AI sse body error：{}，exception：{}\", bodyString, t);\n            } else {\n                log.error(\"REST AI sse response error：{}，exception：{}\", response, t);\n            }\n            if (Objects.nonNull(eventSource)) {\n                eventSource.cancel();\n            }\n            Message message = new Message();\n            message.setContent(\"Rest AI Error:\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Exception in sending data:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/model/RestAIChatCompletions.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.rest.model;\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatChoice;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class RestAIChatCompletions {\n\n    /*\n     * A unique identifier associated with this chat completions response.\n     */\n    private String id;\n\n    /*\n     * The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     */\n    private int created;\n\n    /**\n     * model\n     */\n    private String model;\n\n    /**\n     * object\n     */\n    private String object;\n\n    /*\n     * The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1.\n     * Token limits and other settings may limit the number of choices generated.\n     */\n    @JsonProperty(value = \"choices\")\n    private List<FastChatChoice> choices;\n\n\n    /**\n     * Creates an instance of ChatCompletions class.\n     *\n     * @param id the id value to set.\n     * @param created the created value to set.\n     * @param choices the choices value to set.\n     */\n    @JsonCreator\n    private RestAIChatCompletions(\n        @JsonProperty(value = \"id\") String id,\n        @JsonProperty(value = \"created\") int created,\n        @JsonProperty(value = \"model\") String model,\n        @JsonProperty(value = \"object\") String object,\n        @JsonProperty(value = \"choices\") List<FastChatChoice> choices) {\n        this.id = id;\n        this.created = created;\n        this.model = model;\n        this.object = object;\n        this.choices = choices;\n    }\n\n    /**\n     * Get the id property: A unique identifier associated with this chat completions response.\n     *\n     * @return the id value.\n     */\n    public String getId() {\n        return this.id;\n    }\n\n    /**\n     * Get the created property: The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     *\n     * @return the created value.\n     */\n    public int getCreated() {\n        return this.created;\n    }\n\n    /**\n     * Get the choices property: The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1. Token limits and other\n     * settings may limit the number of choices generated.\n     *\n     * @return the choices value.\n     */\n    public List<FastChatChoice> getChoices() {\n        return this.choices;\n    }\n\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/model/RestAiCompletion.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.rest.model;\n\nimport java.io.Serializable;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author moji\n * @version RestAiCompletion.java, v 0.1 May 27, 2023 14:00 moji Exp $\n * @date 2023/05/27\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class RestAiCompletion implements Serializable {\n\n    /**\n     * hint\n     */\n    private String prompt;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/client/TongyiChatAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.tongyi.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author moji\n * @date 23/09/26\n */\n@Slf4j\npublic class TongyiChatAIClient {\n\n    /**\n     * TONGYI OPENAI KEY\n     */\n    public static final String TONGYI_API_KEY = \"tongyi.chatgpt.apiKey\";\n\n    /**\n     * TONGYI OPENAI HOST\n     */\n    public static final String TONGYI_HOST = \"tongyi.host\";\n\n    /**\n     * TONGYI OPENAI model\n     */\n    public static final String TONGYI_MODEL= \"tongyi.model\";\n\n    /**\n     * TONGYI OPENAI embedding model\n     */\n    public static final String TONGYI_EMBEDDING_MODEL = \"tongyi.embedding.model\";\n\n    private static TongyiChatAIStreamClient TONGYI_AI_CLIENT;\n\n\n    public static TongyiChatAIStreamClient getInstance() {\n        if (TONGYI_AI_CLIENT != null) {\n            return TONGYI_AI_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static TongyiChatAIStreamClient singleton() {\n        if (TONGYI_AI_CLIENT == null) {\n            synchronized (TongyiChatAIClient.class) {\n                if (TONGYI_AI_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return TONGYI_AI_CLIENT;\n    }\n\n    public static void refresh() {\n        String apiKey = \"\";\n        String apiHost = \"\";\n        String model = \"\";\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(TONGYI_HOST).getData();\n        if (apiHostConfig != null && StringUtils.isNotBlank(apiHostConfig.getContent())) {\n            apiHost = apiHostConfig.getContent();\n        }\n        Config config = configService.find(TONGYI_API_KEY).getData();\n        if (config != null && StringUtils.isNotBlank(config.getContent())) {\n            apiKey = config.getContent();\n        }\n        Config deployConfig = configService.find(TONGYI_MODEL).getData();\n        if (deployConfig != null && StringUtils.isNotBlank(deployConfig.getContent())) {\n            model = deployConfig.getContent();\n        }\n        TONGYI_AI_CLIENT = TongyiChatAIStreamClient.builder().apiKey(apiKey).apiHost(apiHost).model(model)\n            .build();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/client/TongyiChatAIStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.tongyi.client;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.interceptor.FastChatHeaderAuthorizationInterceptor;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.tongyi.model.TongyiChatCompletionsOptions;\nimport ai.chat2db.server.web.api.controller.ai.tongyi.model.TongyiChatMessage;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport okhttp3.sse.EventSources;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * tongyi Chat Aligned Client\n *\n * @author moji\n */\n@Slf4j\npublic class TongyiChatAIStreamClient {\n\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String apiKey;\n\n    /**\n     * apiHost\n     */\n    @Getter\n    @NotNull\n    private String apiHost;\n\n    /**\n     * model\n     */\n    @Getter\n    private String model;\n\n    /**\n     * embeddingModel\n     */\n    @Getter\n    private String embeddingModel;\n\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n\n    /**\n     * @param builder\n     */\n    private TongyiChatAIStreamClient(Builder builder) {\n        this.apiKey = builder.apiKey;\n        this.apiHost = builder.apiHost;\n        this.model = builder.model;\n        this.embeddingModel = builder.embeddingModel;\n        if (Objects.isNull(builder.okHttpClient)) {\n            builder.okHttpClient = this.okHttpClient();\n        }\n        okHttpClient = builder.okHttpClient;\n    }\n\n    /**\n     * okhttpclient\n     */\n    private OkHttpClient okHttpClient() {\n        OkHttpClient okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new FastChatHeaderAuthorizationInterceptor(this.apiKey))\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n        return okHttpClient;\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static TongyiChatAIStreamClient.Builder builder() {\n        return new TongyiChatAIStreamClient.Builder();\n    }\n\n    /**\n     * builder\n     */\n    public static final class Builder {\n        private String apiKey;\n\n        private String apiHost;\n\n        private String model;\n\n        private String embeddingModel;\n\n        /**\n         * OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public TongyiChatAIStreamClient.Builder apiKey(String apiKeyValue) {\n            this.apiKey = apiKeyValue;\n            return this;\n        }\n\n        /**\n         * @param apiHostValue\n         * @return\n         */\n        public TongyiChatAIStreamClient.Builder apiHost(String apiHostValue) {\n            this.apiHost = apiHostValue;\n            return this;\n        }\n\n        /**\n         * @param modelValue\n         * @return\n         */\n        public TongyiChatAIStreamClient.Builder model(String modelValue) {\n            this.model = modelValue;\n            return this;\n        }\n\n        public TongyiChatAIStreamClient.Builder embeddingModel(String embeddingModelValue) {\n            this.embeddingModel = embeddingModelValue;\n            return this;\n        }\n\n        public TongyiChatAIStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public TongyiChatAIStreamClient build() {\n            return new TongyiChatAIStreamClient(this);\n        }\n\n    }\n\n    /**\n     * Q&A interface stream form\n     *\n     * @param chatMessages\n     * @param eventSourceListener\n     */\n    public void streamCompletions(List<FastChatMessage> chatMessages, EventSourceListener eventSourceListener) {\n        if (CollectionUtils.isEmpty(chatMessages)) {\n            log.error(\"param error：Tongyi Chat Prompt cannot be empty\");\n            throw new ParamBusinessException(\"prompt\");\n        }\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：TongyiChatEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        log.info(\"Tongyi Chat AI, prompt:{}\", chatMessages.get(chatMessages.size() - 1).getContent());\n        try {\n\n            TongyiChatCompletionsOptions chatCompletionsOptions = new TongyiChatCompletionsOptions();\n            chatCompletionsOptions.setStream(true);\n            chatCompletionsOptions.setModel(this.model);\n            Map<String, Object> parameters = new HashMap<>();\n            parameters.put(\"result_format\", \"text\");\n            chatCompletionsOptions.setParameters(parameters);\n            TongyiChatMessage tongyiChatMessage = new TongyiChatMessage();\n            tongyiChatMessage.setMessages(chatMessages);\n            chatCompletionsOptions.setInput(tongyiChatMessage);\n\n            EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(chatCompletionsOptions);\n            Request request = new Request.Builder()\n                .url(apiHost)\n                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                .build();\n            //Create event\n            EventSource eventSource = factory.newEventSource(request, eventSourceListener);\n            log.info(\"finish invoking tongyi chat ai\");\n        } catch (Exception e) {\n            log.error(\"tongyi chat ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/listener/TongyiChatAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.tongyi.listener;\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatChoice;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletions;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsUsage;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.tongyi.model.TongyiChatCompletions;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\n/**\n * description：OpenAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class TongyiChatAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n\n    public TongyiChatAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"Tongyi Chat Sse connecting...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"Tongyi Chat AI response data：{}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"Tongyi Chat AI closed\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n\n        TongyiChatCompletions chatCompletions = mapper.readValue(data, TongyiChatCompletions.class);\n        String text = chatCompletions.getOutput().getText();\n        log.info(\"id: {}, text: {}\", chatCompletions.getId(), text);\n\n        Message message = new Message();\n        message.setContent(text);\n        sseEmitter.send(SseEmitter.event()\n            .id(null)\n            .data(message)\n            .reconnectTime(3000));\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        try {\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        sseEmitter.complete();\n        log.info(\"TongyiChatAI close sse connection...\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = Objects.nonNull(t) ? t.getMessage() : \"\";\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                if (StringUtils.isBlank(bodyString) && Objects.nonNull(t)) {\n                    bodyString = t.getMessage();\n                }\n                log.error(\"Tongyi Chat AI sse response：{}\", bodyString);\n            } else {\n                log.error(\"Tongyi Chat AI sse response：{}，error：{}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"Tongyi Chat AI error：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Tongyi Chat AI send data error:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatCompletions.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.tongyi.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n\n@Data\npublic class TongyiChatCompletions {\n\n    /*\n     * A unique identifier associated with this chat completions response.\n     */\n    @JsonProperty(value = \"request_id\")\n    private String id;\n\n    /*\n     * The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1.\n     * Token limits and other settings may limit the number of choices generated.\n     */\n    private TongyiChatOutput output;\n\n    /*\n     * Usage information for tokens processed and generated as part of this completions operation.\n     */\n    private TongyiChatCompletionsUsage usage;\n\n    /**\n     * Creates an instance of ChatCompletions class.\n     *\n     * @param id the id value to set.\n     * @param choices the choices value to set.\n     * @param usage the usage value to set.\n     */\n    @JsonCreator\n    private TongyiChatCompletions(\n        @JsonProperty(value = \"request_id\") String id,\n        @JsonProperty(value = \"output\") TongyiChatOutput choices,\n        @JsonProperty(value = \"usage\") TongyiChatCompletionsUsage usage) {\n        this.id = id;\n        this.output = choices;\n        this.usage = usage;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatCompletionsOptions.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.tongyi.model;\n\nimport lombok.Data;\n\nimport java.util.Map;\n\n/**\n * The configuration information for a chat completions request. Completions support a wide variety of tasks and\n * generate text that continues from or \"completes\" provided prompt data.\n */\n@Data\npublic final class TongyiChatCompletionsOptions {\n\n    /*\n     * The collection of context messages associated with this chat completions request.\n     * Typical usage begins with a chat message for the System role that provides instructions for\n     * the behavior of the assistant, followed by alternating messages between the User and\n     * Assistant roles.\n     */\n    private TongyiChatMessage input;\n\n    /*\n     * A value indicating whether chat completions should be streamed for this request.\n     */\n    private Boolean stream;\n\n    /*\n     * The model name to provide as part of this completions request.\n     * Not applicable to Fast Chat AI, where deployment information should be included in the Fast Chat\n     * resource URI that's connected to.\n     */\n    private String model;\n\n    /**\n     * parameters\n     */\n    private Map<String, Object> parameters;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatCompletionsUsage.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.tongyi.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * Representation of the token counts processed for a completions request. Counts consider all tokens across prompts,\n * choices, choice alternates, best_of generations, and other consumers.\n */\n@Data\n@NoArgsConstructor\npublic final class TongyiChatCompletionsUsage {\n\n    /*\n     * The number of tokens generated across all completions emissions.\n     */\n    @JsonProperty(value = \"output_tokens\")\n    private int outputTokens;\n\n    /*\n     * The number of tokens in the provided prompts for the completions request.\n     */\n    @JsonProperty(value = \"input_tokens\")\n    private int inputTokens;\n\n\n    /**\n     * Creates an instance of CompletionsUsage class.\n     *\n     * @param completionTokens the completionTokens value to set.\n     * @param promptTokens the promptTokens value to set.\n     */\n    @JsonCreator\n    private TongyiChatCompletionsUsage(\n            @JsonProperty(value = \"output_tokens\") int completionTokens,\n            @JsonProperty(value = \"input_tokens\") int promptTokens) {\n        this.outputTokens = completionTokens;\n        this.inputTokens = promptTokens;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatMessage.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.tongyi.model;\n\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class TongyiChatMessage {\n\n    private List<FastChatMessage> messages;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatOutput.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.tongyi.model;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n/**\n * The representation of a single prompt completion as part of an overall completions request. Generally, `n` choices\n * are generated per provided prompt with a default value of 1. Token limits and other settings may limit the number of\n * choices generated.\n */\n@Data\npublic final class TongyiChatOutput {\n\n    /*\n     * The generated text for a given completions prompt.\n     */\n    @JsonProperty(value = \"text\")\n    private String text;\n\n    /*\n     * Reason for finishing\n     */\n    @JsonProperty(value = \"finish_reason\")\n    private String finishReason;\n\n    /**\n     * Creates an instance of Choice class.\n     *\n     * @param text the text value to set.\n     * @param finishReason the finishReason value to set.\n     */\n    @JsonCreator\n    private TongyiChatOutput(\n            @JsonProperty(value = \"text\") String text,\n            @JsonProperty(value = \"finish_reason\") String finishReason) {\n        this.text = text;\n        this.finishReason = finishReason;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/client/WenxinAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.wenxin.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatAIStreamClient;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author moji\n * @date 23/09/26\n */\n@Slf4j\npublic class WenxinAIClient {\n\n    /**\n     * WENXIN_ACCESS_TOKEN\n     */\n    public static final String WENXIN_ACCESS_TOKEN = \"wenxin.access.token\";\n\n    /**\n     * WENXIN_HOST\n     */\n    public static final String WENXIN_HOST = \"wenxin.host\";\n\n    /**\n     * WENXIN_MODEL\n     */\n    public static final String WENXIN_MODEL= \"wenxin.model\";\n\n    /**\n     * Wenxin embedding model\n     */\n    public static final String WENXIN_EMBEDDING_MODEL = \"wenxin.embedding.model\";\n\n    private static WenxinAIStreamClient WENXIN_AI_CLIENT;\n\n\n    public static WenxinAIStreamClient getInstance() {\n        if (WENXIN_AI_CLIENT != null) {\n            return WENXIN_AI_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static WenxinAIStreamClient singleton() {\n        if (WENXIN_AI_CLIENT == null) {\n            synchronized (WenxinAIClient.class) {\n                if (WENXIN_AI_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return WENXIN_AI_CLIENT;\n    }\n\n    public static void refresh() {\n        String apiHost = \"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro\";\n        String accessToken = \"\";\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(WENXIN_HOST).getData();\n        if (apiHostConfig != null && StringUtils.isNotBlank(apiHostConfig.getContent())) {\n            apiHost = apiHostConfig.getContent();\n            if (apiHost.endsWith(\"/\")) {\n                apiHost = apiHost.substring(0, apiHost.length() - 1);\n            }\n        }\n        Config config = configService.find(WENXIN_ACCESS_TOKEN).getData();\n        if (config != null && StringUtils.isNotBlank(config.getContent())) {\n            accessToken = config.getContent();\n        }\n        WENXIN_AI_CLIENT = WenxinAIStreamClient.builder().accessToken(accessToken).apiHost(apiHost).build();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/client/WenxinAIStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.wenxin.client;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsOptions;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.wenxin.interceptor.AccessTokenInterceptor;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.*;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport okhttp3.sse.EventSources;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Fast Chat Aligned Client\n *\n * @author moji\n */\n@Slf4j\npublic class WenxinAIStreamClient {\n\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String accessToken;\n\n    /**\n     * apiHost\n     */\n    @Getter\n    @NotNull\n    private String apiHost;\n\n    /**\n     * model\n     */\n    @Getter\n    private String model;\n\n    /**\n     * embeddingModel\n     */\n    @Getter\n    private String embeddingModel;\n\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n\n    /**\n     * @param builder\n     */\n    private WenxinAIStreamClient(Builder builder) {\n        this.accessToken = builder.accessToken;\n        this.apiHost = builder.apiHost;\n        this.model = builder.model;\n        this.embeddingModel = builder.embeddingModel;\n        if (Objects.isNull(builder.okHttpClient)) {\n            builder.okHttpClient = this.okHttpClient();\n        }\n        okHttpClient = builder.okHttpClient;\n    }\n\n    /**\n     * okhttpclient\n     */\n    private OkHttpClient okHttpClient() {\n        OkHttpClient okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new AccessTokenInterceptor(this.accessToken))\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n        return okHttpClient;\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static WenxinAIStreamClient.Builder builder() {\n        return new WenxinAIStreamClient.Builder();\n    }\n\n    /**\n     * builder\n     */\n    public static final class Builder {\n        private String accessToken;\n\n        private String apiHost;\n\n        private String model;\n\n        private String embeddingModel;\n\n        /**\n         * OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public WenxinAIStreamClient.Builder accessToken(String accessToken) {\n            this.accessToken = accessToken;\n            return this;\n        }\n\n        /**\n         * @param apiHostValue\n         * @return\n         */\n        public WenxinAIStreamClient.Builder apiHost(String apiHostValue) {\n            this.apiHost = apiHostValue;\n            return this;\n        }\n\n        /**\n         * @param modelValue\n         * @return\n         */\n        public WenxinAIStreamClient.Builder model(String modelValue) {\n            this.model = modelValue;\n            return this;\n        }\n\n        public WenxinAIStreamClient.Builder embeddingModel(String embeddingModelValue) {\n            this.embeddingModel = embeddingModelValue;\n            return this;\n        }\n\n        public WenxinAIStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public WenxinAIStreamClient build() {\n            return new WenxinAIStreamClient(this);\n        }\n\n    }\n\n    /**\n     * Q&A interface stream form\n     *\n     * @param chatMessages\n     * @param eventSourceListener\n     */\n    public void streamCompletions(List<FastChatMessage> chatMessages, EventSourceListener eventSourceListener) {\n        if (CollectionUtils.isEmpty(chatMessages)) {\n            log.error(\"param error：Wenxin Prompt cannot be empty\");\n            throw new ParamBusinessException(\"prompt\");\n        }\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：WenxinEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        log.info(\"Wenxin Chat AI, prompt:{}\", chatMessages.get(chatMessages.size() - 1).getContent());\n        try {\n\n            FastChatCompletionsOptions chatCompletionsOptions = new FastChatCompletionsOptions(chatMessages);\n            chatCompletionsOptions.setStream(true);\n\n            EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(chatCompletionsOptions);\n            Request request = new Request.Builder()\n                .url(apiHost)\n                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                .build();\n            //Create event\n            EventSource eventSource = factory.newEventSource(request, eventSourceListener);\n            log.info(\"finish invoking fast chat ai\");\n        } catch (Exception e) {\n            log.error(\"wenxin chat ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/interceptor/AccessTokenInterceptor.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.wenxin.interceptor;\n\nimport okhttp3.HttpUrl;\nimport okhttp3.Interceptor;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\nimport java.io.IOException;\n\npublic class AccessTokenInterceptor implements Interceptor {\n    private final String accessToken;\n\n    public AccessTokenInterceptor(String accessToken) {\n        this.accessToken = accessToken;\n    }\n\n    @Override\n    public Response intercept(Chain chain) throws IOException {\n        Request originalRequest = chain.request();\n        HttpUrl originalHttpUrl = originalRequest.url();\n\n        // Use HttpUrl.Builder to add query parameter access_token\n        HttpUrl urlWithAccessToken = originalHttpUrl.newBuilder()\n                .addQueryParameter(\"access_token\", accessToken)\n                .build();\n\n        // Create a new request and apply the new URL to it\n        Request newRequest = originalRequest.newBuilder()\n                .url(urlWithAccessToken)\n                .build();\n\n        return chain.proceed(newRequest);\n    }\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/listener/WenxinAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.wenxin.listener;\n\nimport ai.chat2db.server.web.api.controller.ai.wenxin.model.WenxinChatCompletions;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\n/**\n * description：OpenAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class WenxinAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n\n    public WenxinAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"Wenxin chat Sse connecting...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, String data) {\n        log.info(\"Wenxin AI response data：{}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"Wenxin AI closed\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n\n        WenxinChatCompletions chatCompletions = mapper.readValue(data, WenxinChatCompletions.class);\n        String text = chatCompletions.getResult();\n        log.info(\"Model={} is created at {}. message:{}\", chatCompletions.getObject(),\n            chatCompletions.getCreated(), text);\n\n        Message message = new Message();\n        message.setContent(text);\n        sseEmitter.send(SseEmitter.event()\n            .id(null)\n            .data(message)\n            .reconnectTime(3000));\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        try {\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        sseEmitter.complete();\n        log.info(\"WenxinChatAI close sse connection...\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = Objects.nonNull(t) ? t.getMessage() : \"\";\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                if (StringUtils.isBlank(bodyString) && Objects.nonNull(t)) {\n                    bodyString = t.getMessage();\n                }\n                log.error(\"Wenxin chat AI sse response：{}\", bodyString);\n            } else {\n                log.error(\"Wenxin chat AI sse response：{}，error：{}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"Wenxin chat AI error：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Wenxin chat AI send data error:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/model/WenxinChatCompletions.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.wenxin.model;\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsUsage;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\n@Data\npublic class WenxinChatCompletions {\n\n    /*\n     * A unique identifier associated with this chat completions response.\n     */\n    private String id;\n\n    /*\n     * The first timestamp associated with generation activity for this completions response,\n     * represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.\n     */\n    private int created;\n\n    /**\n     * model\n     */\n    @JsonProperty(value = \"is_truncated\")\n    private String isTruncated;\n\n    @JsonProperty(value = \"need_clear_history\")\n    private String needClearHistory;\n\n    /**\n     * object\n     */\n    private String object;\n\n    /*\n     * The collection of completions choices associated with this completions response.\n     * Generally, `n` choices are generated per provided prompt with a default value of 1.\n     * Token limits and other settings may limit the number of choices generated.\n     */\n    private String result;\n\n    /*\n     * Usage information for tokens processed and generated as part of this completions operation.\n     */\n    private FastChatCompletionsUsage usage;\n\n    /**\n     * Creates an instance of ChatCompletions class.\n     *\n     * @param id the id value to set.\n     * @param created the created value to set.\n     * @param result the result value to set.\n     * @param usage the usage value to set.\n     */\n    @JsonCreator\n    private WenxinChatCompletions(\n        @JsonProperty(value = \"id\") String id,\n        @JsonProperty(value = \"created\") int created,\n        @JsonProperty(value = \"is_truncated\") String isTruncated,\n        @JsonProperty(value = \"need_clear_history\") String needClearHistory,\n        @JsonProperty(value = \"object\") String object,\n        @JsonProperty(value = \"result\") String result,\n        @JsonProperty(value = \"usage\") FastChatCompletionsUsage usage) {\n        this.id = id;\n        this.created = created;\n        this.isTruncated = isTruncated;\n        this.needClearHistory = needClearHistory;\n        this.object = object;\n        this.result = result;\n        this.usage = usage;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIClient.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.ai.zhipu.client;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author moji\n * @date 23/09/26\n */\n@Slf4j\npublic class ZhipuChatAIClient {\n\n    /**\n     * ZHIPU OPENAI KEY\n     */\n    public static final String ZHIPU_API_KEY = \"zhipu.chatgpt.apiKey\";\n\n    /**\n     * ZHIPU OPENAI HOST\n     */\n    public static final String ZHIPU_HOST = \"zhipu.host\";\n\n    /**\n     * ZHIPU OPENAI model\n     */\n    public static final String ZHIPU_MODEL= \"zhipu.model\";\n\n    /**\n     * ZHIPU OPENAI embedding model\n     */\n    public static final String ZHIPU_EMBEDDING_MODEL = \"zhipu.embedding.model\";\n\n    private static ZhipuChatAIStreamClient ZHIPU_AI_CLIENT;\n\n\n    public static ZhipuChatAIStreamClient getInstance() {\n        if (ZHIPU_AI_CLIENT != null) {\n            return ZHIPU_AI_CLIENT;\n        } else {\n            return singleton();\n        }\n    }\n\n    private static ZhipuChatAIStreamClient singleton() {\n        if (ZHIPU_AI_CLIENT == null) {\n            synchronized (ZhipuChatAIClient.class) {\n                if (ZHIPU_AI_CLIENT == null) {\n                    refresh();\n                }\n            }\n        }\n        return ZHIPU_AI_CLIENT;\n    }\n\n    public static void refresh() {\n        String apiKey = \"\";\n        String apiHost = \"\";\n        String model = \"\";\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config apiHostConfig = configService.find(ZHIPU_HOST).getData();\n        if (apiHostConfig != null && StringUtils.isNotBlank(apiHostConfig.getContent())) {\n            apiHost = apiHostConfig.getContent();\n        }\n        Config config = configService.find(ZHIPU_API_KEY).getData();\n        if (config != null && StringUtils.isNotBlank(config.getContent())) {\n            apiKey = config.getContent();\n        }\n        Config deployConfig = configService.find(ZHIPU_MODEL).getData();\n        if (deployConfig != null && StringUtils.isNotBlank(deployConfig.getContent())) {\n            model = deployConfig.getContent();\n        }\n        ZHIPU_AI_CLIENT = ZhipuChatAIStreamClient.builder().apiKey(apiKey).apiHost(apiHost).model(model)\n            .build();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.zhipu.client;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.zhipu.interceptor.ZhipuChatHeaderAuthorizationInterceptor;\nimport ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions;\nimport cn.hutool.http.ContentType;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport okhttp3.sse.EventSources;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Zhipu Chat Aligned Client\n *\n * @author moji\n */\n@Slf4j\npublic class ZhipuChatAIStreamClient {\n\n    /**\n     * apikey\n     */\n    @Getter\n    @NotNull\n    private String apiKey;\n\n    @Getter\n    private String key;\n\n    @Getter\n    private String secret;\n\n    /**\n     * apiHost\n     */\n    @Getter\n    @NotNull\n    private String apiHost;\n\n    /**\n     * model\n     */\n    @Getter\n    private String model;\n\n    /**\n     * embeddingModel\n     */\n    @Getter\n    private String embeddingModel;\n\n    /**\n     * okHttpClient\n     */\n    @Getter\n    private OkHttpClient okHttpClient;\n\n\n    /**\n     * @param builder\n     */\n    private ZhipuChatAIStreamClient(Builder builder) {\n        this.apiKey = builder.apiKey;\n        this.key = builder.key;\n        this.secret = builder.secret;\n        this.apiHost = builder.apiHost;\n        this.model = builder.model;\n        this.embeddingModel = builder.embeddingModel;\n        if (Objects.isNull(builder.okHttpClient)) {\n            builder.okHttpClient = this.okHttpClient();\n        }\n        okHttpClient = builder.okHttpClient;\n    }\n\n    /**\n     * okhttpclient\n     */\n    private OkHttpClient okHttpClient() {\n        OkHttpClient okHttpClient = new OkHttpClient\n            .Builder()\n            .addInterceptor(new ZhipuChatHeaderAuthorizationInterceptor(this.key, this.secret))\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .writeTimeout(50, TimeUnit.SECONDS)\n            .readTimeout(50, TimeUnit.SECONDS)\n            .build();\n        return okHttpClient;\n    }\n\n    /**\n     * structure\n     *\n     * @return\n     */\n    public static ZhipuChatAIStreamClient.Builder builder() {\n        return new ZhipuChatAIStreamClient.Builder();\n    }\n\n    /**\n     * builder\n     */\n    public static final class Builder {\n        private String apiKey;\n\n        private String key;\n\n        private String secret;\n\n        private String apiHost;\n\n        private String model;\n\n        private String embeddingModel;\n\n        /**\n         * OkhttpClient\n         */\n        private OkHttpClient okHttpClient;\n\n        public Builder() {\n        }\n\n        public ZhipuChatAIStreamClient.Builder apiKey(String apiKeyValue) {\n            this.apiKey = apiKeyValue;\n            String[] arrStr = apiKey.split(\"\\\\.\");\n            if (arrStr.length != 2) {\n                throw new RuntimeException(\"invalid apiSecretKey\");\n            }\n            this.key = arrStr[0];\n            this.secret = arrStr[1];\n            return this;\n        }\n\n        /**\n         * @param apiHostValue\n         * @return\n         */\n        public ZhipuChatAIStreamClient.Builder apiHost(String apiHostValue) {\n            this.apiHost = apiHostValue;\n            return this;\n        }\n\n        /**\n         * @param modelValue\n         * @return\n         */\n        public ZhipuChatAIStreamClient.Builder model(String modelValue) {\n            this.model = modelValue;\n            return this;\n        }\n\n        public ZhipuChatAIStreamClient.Builder embeddingModel(String embeddingModelValue) {\n            this.embeddingModel = embeddingModelValue;\n            return this;\n        }\n\n        public ZhipuChatAIStreamClient.Builder okHttpClient(OkHttpClient val) {\n            this.okHttpClient = val;\n            return this;\n        }\n\n        public ZhipuChatAIStreamClient build() {\n            return new ZhipuChatAIStreamClient(this);\n        }\n\n    }\n\n    /**\n     * Q&A interface stream form\n     *\n     * @param chatMessages\n     * @param eventSourceListener\n     */\n    public void streamCompletions(List<FastChatMessage> chatMessages, EventSourceListener eventSourceListener) {\n        if (CollectionUtils.isEmpty(chatMessages)) {\n            log.error(\"param error：Zhipu Chat Prompt cannot be empty\");\n            throw new ParamBusinessException(\"prompt\");\n        }\n        if (Objects.isNull(eventSourceListener)) {\n            log.error(\"param error：Zhipu ChatEventSourceListener cannot be empty\");\n            throw new ParamBusinessException();\n        }\n        log.info(\"Zhipu Chat AI, prompt:{}\", chatMessages.get(chatMessages.size() - 1).getContent());\n        try {\n            // It is recommended to check the demo package code directly. The update here may not be timely.\n            ZhipuChatCompletionsOptions completionsOptions = new ZhipuChatCompletionsOptions();\n            completionsOptions.setPrompt(chatMessages);\n            completionsOptions.setModel(this.model);\n            String requestId = String.valueOf(System.currentTimeMillis());\n            completionsOptions.setRequestId(requestId);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            String requestBody = mapper.writeValueAsString(completionsOptions);\n            log.info(\"使用的model:{}\", this.model);\n            EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);\n            Request request = new Request.Builder()\n                .url(apiHost)\n                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))\n                .build();\n            //Create event\n            EventSource eventSource = factory.newEventSource(request, eventSourceListener);\n            log.info(\"finish invoking zhipu chat ai\");\n        } catch (Exception e) {\n            log.error(\"fast chat ai error\", e);\n            eventSourceListener.onFailure(null, e, null);\n            throw new ParamBusinessException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/interceptor/ZhipuChatHeaderAuthorizationInterceptor.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.zhipu.interceptor;\n\nimport ai.chat2db.server.web.api.controller.ai.zhipu.util.ZhipuUtils;\nimport cn.hutool.http.ContentType;\nimport cn.hutool.http.Header;\nimport lombok.Getter;\nimport okhttp3.Interceptor;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\nimport java.io.IOException;\n\n/**\n * header apikey\n *\n * @author grt\n * @since 2023-03-23\n */\n@Getter\npublic class ZhipuChatHeaderAuthorizationInterceptor implements Interceptor {\n\n    private String key;\n\n    private String secret;\n\n    public ZhipuChatHeaderAuthorizationInterceptor(String key, String secret) {\n        this.key = key;\n        this.secret = secret;\n    }\n\n    @Override\n    public Response intercept(Chain chain) throws IOException {\n        Request original = chain.request();\n        String token = ZhipuUtils.getToken(key, secret);\n        Request request = original.newBuilder()\n                // replace to your corresponding field and value\n                .header(Header.AUTHORIZATION.getValue(), token)\n                .header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue())\n                .method(original.method(), original.body())\n                .build();\n        return chain.proceed(request);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.zhipu.listener;\n\nimport ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletions;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.unfbx.chatgpt.entity.chat.Message;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\nimport okhttp3.sse.EventSource;\nimport okhttp3.sse.EventSourceListener;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\nimport java.util.Objects;\n\n/**\n * description：OpenAIEventSourceListener\n *\n * @author https:www.unfbx.com\n * @date 2023-02-22\n */\n@Slf4j\npublic class ZhipuChatAIEventSourceListener extends EventSourceListener {\n\n    private SseEmitter sseEmitter;\n\n    private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n\n    public ZhipuChatAIEventSourceListener(SseEmitter sseEmitter) {\n        this.sseEmitter = sseEmitter;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onOpen(EventSource eventSource, Response response) {\n        log.info(\"Zhipu Chat Sse connecting...\");\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @SneakyThrows\n    @Override\n    public void onEvent(EventSource eventSource, String id, String type, @NotNull String data) {\n        log.info(\"Zhipu Chat AI response data：{}\", data);\n        if (data.equals(\"[DONE]\")) {\n            log.info(\"Zhipu Chat AI closed\");\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\")\n                .reconnectTime(3000));\n            sseEmitter.complete();\n            return;\n        }\n\n        ZhipuChatCompletions chatCompletions = mapper.readValue(data, ZhipuChatCompletions.class);\n        String text = chatCompletions.getChoices().get(0).getDelta()==null?\n                chatCompletions.getChoices().get(0).getText()\n                :chatCompletions.getChoices().get(0).getDelta().getContent();\n\n        Message message = new Message();\n        message.setContent(text);\n        sseEmitter.send(SseEmitter.event()\n            .id(null)\n            .data(message)\n            .reconnectTime(3000));\n    }\n\n    @Override\n    public void onClosed(EventSource eventSource) {\n        log.info(\"Zhipu Chat AI closes sse connection closed\");\n    }\n\n    @Override\n    public void onFailure(EventSource eventSource, Throwable t, Response response) {\n        try {\n            if (Objects.isNull(response)) {\n                String message = t.getMessage();\n                Message sseMessage = new Message();\n                sseMessage.setContent(message);\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[ERROR]\")\n                    .data(sseMessage));\n                sseEmitter.send(SseEmitter.event()\n                    .id(\"[DONE]\")\n                    .data(\"[DONE]\"));\n                sseEmitter.complete();\n                return;\n            }\n            ResponseBody body = response.body();\n            String bodyString = Objects.nonNull(t) ? t.getMessage() : \"\";\n            if (Objects.nonNull(body)) {\n                bodyString = body.string();\n                if (StringUtils.isBlank(bodyString) && Objects.nonNull(t)) {\n                    bodyString = t.getMessage();\n                }\n                log.error(\"Zhipu Chat AI sse response：{}\", bodyString);\n            } else {\n                log.error(\"Zhipu Chat AI sse response：{}，error：{}\", response, t);\n            }\n            eventSource.cancel();\n            Message message = new Message();\n            message.setContent(\"Zhipu Chat AI error：\" + bodyString);\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[ERROR]\")\n                .data(message));\n            sseEmitter.send(SseEmitter.event()\n                .id(\"[DONE]\")\n                .data(\"[DONE]\"));\n            sseEmitter.complete();\n        } catch (Exception exception) {\n            log.error(\"Zhipu Chat AI send data error:\", exception);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatBody.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.zhipu.model;\n\nimport ai.chat2db.server.web.api.controller.ai.baichuan.model.BaichuanChatMessage;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatChoice;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsUsage;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * The representation of a single prompt completion as part of an overall completions request. Generally, `n` choices\n * are generated per provided prompt with a default value of 1. Token limits and other settings may limit the number of\n * choices generated.\n */\n@Data\npublic final class ZhipuChatBody {\n\n    /*\n     * The log probabilities model for tokens associated with this completions choice.\n     */\n    @JsonProperty(value = \"choices\")\n    private List<FastChatChoice> choices;\n\n    @JsonProperty(value = \"usage\")\n    private FastChatCompletionsUsage usage;\n    @JsonCreator\n    private ZhipuChatBody(\n        @JsonProperty(value = \"choices\") List<FastChatChoice> choices,\n        @JsonProperty(value = \"usage\") FastChatCompletionsUsage usage) {\n        this.choices = choices;\n        this.usage = usage;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletions.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.zhipu.model;\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatChoice;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsUsage;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class ZhipuChatCompletions {\n\n    /*\n     * A unique identifier associated with this chat completions response.\n     */\n    @JsonProperty(value = \"id\")\n    private String id;\n    @JsonProperty(value = \"created\")\n    private Long created;\n\n    @JsonProperty(value = \"choices\")\n    private List<FastChatChoice> choices;\n\n    @JsonProperty(value = \"usage\")\n    private FastChatCompletionsUsage usage;\n    @JsonCreator\n    private ZhipuChatCompletions(\n        @JsonProperty(value = \"id\") String id,\n        @JsonProperty(value = \"created\") Long created,\n        @JsonProperty(value = \"choices\") List<FastChatChoice> choices,\n        @JsonProperty(value = \"usage\") FastChatCompletionsUsage usage) {\n        this.id = id;\n        this.created = created;\n        this.choices = choices;\n        this.usage = usage;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java",
    "content": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n// Code generated by Microsoft (R) AutoRest Code Generator.\npackage ai.chat2db.server.web.api.controller.ai.zhipu.model;\n\nimport ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * The configuration information for a chat completions request. Completions support a wide variety of tasks and\n * generate text that continues from or \"completes\" provided prompt data.\n */\n@Data\npublic final class ZhipuChatCompletionsOptions {\n\n    @JsonProperty(value = \"request_id\")\n    private String requestId;\n\n    // sse-params\n    @JsonProperty(value = \"stream\")\n    private Boolean stream = true;\n\n    @JsonProperty(value = \"sseFormat\")\n    private String sseFormat = \"data\";\n\n\n    /*\n     * The collection of context messages associated with this chat completions request.\n     * Typical usage begins with a chat message for the System role that provides instructions for\n     * the behavior of the assistant, followed by alternating messages between the User and\n     * Assistant roles.\n     */\n    @JsonProperty(value = \"messages\")\n    private List<FastChatMessage> prompt;\n\n\n    //\n    /*\n     * The model name to provide as part of this completions request.\n     * Not applicable to Fast Chat AI, where deployment information should be included in the Fast Chat\n     * resource URI that's connected to.\n     */\n    @JsonProperty(value = \"model\")\n    private String model;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/util/ZhipuUtils.java",
    "content": "package ai.chat2db.server.web.api.controller.ai.zhipu.util;\n\nimport com.auth0.jwt.JWT;\nimport com.auth0.jwt.algorithms.Algorithm;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.Calendar;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Slf4j\npublic class ZhipuUtils {\n\n    private static final String tokenV3KeyPrefix = \"zhipu_oapi_token_v3\";\n\n\n    public static String getToken(String key, String secret) {\n        String newToken = createJwt(key, secret);\n        return newToken;\n    }\n\n    private static String createJwt(String key, String secret) {\n        Algorithm alg;\n        try {\n            alg = Algorithm.HMAC256(secret.getBytes(\"utf-8\"));\n        } catch (Exception e) {\n            log.info(\"create jwt error\", e);\n            return null;\n        }\n\n        Map<String, Object> payload = new HashMap<>();\n        payload.put(\"api_key\", key);\n        payload.put(\"exp\", System.currentTimeMillis() + 30 * 60 * 1000);\n        payload.put(\"timestamp\", Calendar.getInstance().getTimeInMillis());\n        Map<String, Object> headerClaims = new HashMap<>();\n        headerClaims.put(\"alg\", \"HS256\");\n        headerClaims.put(\"sign_type\", \"SIGN\");\n        String token = JWT.create().withPayload(payload).withHeader(headerClaims).sign(alg);\n        return token;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/config/ConfigController.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.config;\n\nimport java.util.Objects;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport ai.chat2db.server.domain.api.model.AIConfig;\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.SystemConfigParam;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.domain.core.util.PermissionUtils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.azure.client.AzureOpenAIClient;\nimport ai.chat2db.server.web.api.controller.ai.baichuan.client.BaichuanAIClient;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;\nimport ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatAIClient;\nimport ai.chat2db.server.web.api.controller.ai.rest.client.RestAIClient;\nimport ai.chat2db.server.web.api.controller.ai.tongyi.client.TongyiChatAIClient;\nimport ai.chat2db.server.web.api.controller.ai.wenxin.client.WenxinAIClient;\nimport ai.chat2db.server.web.api.controller.ai.zhipu.client.ZhipuChatAIClient;\nimport ai.chat2db.server.web.api.controller.config.request.AIConfigCreateRequest;\nimport ai.chat2db.server.web.api.controller.config.request.SystemConfigRequest;\nimport ai.chat2db.server.web.api.controller.ai.openai.client.OpenAIClient;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author jipengfei\n * @version : ConfigController.java\n */\n@ConnectionInfoAspect\n@RequestMapping(\"/api/config\")\n@RestController\npublic class ConfigController {\n\n    @Autowired\n    private ConfigService configService;\n\n    @PostMapping(\"/system_config\")\n    public ActionResult systemConfig(@RequestBody SystemConfigRequest request) {\n        SystemConfigParam param = SystemConfigParam.builder().code(request.getCode()).content(request.getContent())\n            .build();\n        configService.createOrUpdate(param);\n        if (OpenAIClient.OPENAI_KEY.equals(request.getCode())) {\n            OpenAIClient.refresh();\n        }\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * Save ChatGPT related configurations\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/system_config/ai\")\n    public ActionResult addChatGptSystemConfig(@RequestBody AIConfigCreateRequest request) {\n        PermissionUtils.checkDeskTopOrAdmin();\n\n        String sqlSource = request.getAiSqlSource();\n        AiSqlSourceEnum aiSqlSourceEnum = AiSqlSourceEnum.getByName(sqlSource);\n        if (Objects.isNull(aiSqlSourceEnum)) {\n            sqlSource = AiSqlSourceEnum.CHAT2DBAI.getCode();\n            aiSqlSourceEnum = AiSqlSourceEnum.CHAT2DBAI;\n        }\n        SystemConfigParam param = SystemConfigParam.builder().code(RestAIClient.AI_SQL_SOURCE).content(sqlSource)\n            .build();\n        configService.createOrUpdate(param);\n\n        switch (Objects.requireNonNull(aiSqlSourceEnum)) {\n            case OPENAI:\n                saveOpenAIConfig(request);\n                break;\n            case CHAT2DBAI:\n                saveChat2dbAIConfig(request);\n                break;\n            case RESTAI:\n                saveRestAIConfig(request);\n                break;\n            case AZUREAI:\n                saveAzureAIConfig(request);\n                break;\n            case FASTCHATAI:\n                saveFastChatAIConfig(request);\n                break;\n            case TONGYIQIANWENAI:\n                saveTongyiChatAIConfig(request);\n                break;\n            case WENXINAI:\n                saveWenxinAIConfig(request);\n                break;\n            case BAICHUANAI:\n                saveBaichuanAIConfig(request);\n                break;\n            case ZHIPUAI:\n                saveZhipuChatAIConfig(request);\n                break;\n        }\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * save chat2db ai config\n     *\n     * @param request\n     */\n    private void saveChat2dbAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam param = SystemConfigParam.builder().code(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).content(\n            request.getApiKey()).build();\n        configService.createOrUpdate(param);\n        SystemConfigParam hostParam = SystemConfigParam.builder().code(Chat2dbAIClient.CHAT2DB_OPENAI_HOST).content(\n            request.getApiHost()).build();\n        configService.createOrUpdate(hostParam);\n        SystemConfigParam modelParam = SystemConfigParam.builder().code(Chat2dbAIClient.CHAT2DB_OPENAI_MODEL).content(\n                request.getModel()).build();\n        configService.createOrUpdate(modelParam);\n        Chat2dbAIClient.refresh();\n    }\n\n    /**\n     * save open ai config\n     *\n     * @param request\n     */\n    private void saveOpenAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam param = SystemConfigParam.builder().code(OpenAIClient.OPENAI_KEY).content(\n            request.getApiKey()).build();\n        configService.createOrUpdate(param);\n        SystemConfigParam hostParam = SystemConfigParam.builder().code(OpenAIClient.OPENAI_HOST).content(\n            request.getApiHost()).build();\n        configService.createOrUpdate(hostParam);\n        SystemConfigParam httpProxyHostParam = SystemConfigParam.builder().code(OpenAIClient.PROXY_HOST).content(\n            request.getHttpProxyHost()).build();\n        configService.createOrUpdate(httpProxyHostParam);\n        SystemConfigParam httpProxyPortParam = SystemConfigParam.builder().code(OpenAIClient.PROXY_PORT).content(\n            request.getHttpProxyPort()).build();\n        configService.createOrUpdate(httpProxyPortParam);\n        OpenAIClient.refresh();\n    }\n\n    /**\n     * save rest ai config\n     *\n     * @param request\n     */\n    private void saveRestAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam param = SystemConfigParam.builder().code(RestAIClient.REST_AI_API_KEY).content(\n                request.getApiKey()).build();\n        configService.createOrUpdate(param);\n        SystemConfigParam restParam = SystemConfigParam.builder().code(RestAIClient.REST_AI_URL).content(\n            request.getApiHost()).build();\n        configService.createOrUpdate(restParam);\n        SystemConfigParam modelParam = SystemConfigParam.builder().code(RestAIClient.REST_AI_MODEL)\n                .content(request.getModel()).build();\n        configService.createOrUpdate(modelParam);\n        RestAIClient.refresh();\n    }\n\n    /**\n     * save azure config\n     *\n     * @param request\n     */\n    private void saveAzureAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam apikeyParam = SystemConfigParam.builder().code(AzureOpenAIClient.AZURE_CHATGPT_API_KEY)\n            .content(\n                request.getApiKey()).build();\n        configService.createOrUpdate(apikeyParam);\n        SystemConfigParam endpointParam = SystemConfigParam.builder().code(AzureOpenAIClient.AZURE_CHATGPT_ENDPOINT)\n            .content(\n                request.getApiHost()).build();\n        configService.createOrUpdate(endpointParam);\n        SystemConfigParam modelParam = SystemConfigParam.builder().code(AzureOpenAIClient.AZURE_CHATGPT_DEPLOYMENT_ID)\n            .content(\n                request.getModel()).build();\n        configService.createOrUpdate(modelParam);\n        AzureOpenAIClient.refresh();\n    }\n\n    /**\n     * save common fast chat ai config\n     *\n     * @param request\n     */\n    private void saveFastChatAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam apikeyParam = SystemConfigParam.builder().code(FastChatAIClient.FASTCHAT_API_KEY)\n                .content(request.getApiKey()).build();\n        configService.createOrUpdate(apikeyParam);\n        SystemConfigParam apiHostParam = SystemConfigParam.builder().code(FastChatAIClient.FASTCHAT_HOST)\n                .content(request.getApiHost()).build();\n        configService.createOrUpdate(apiHostParam);\n        SystemConfigParam modelParam = SystemConfigParam.builder().code(FastChatAIClient.FASTCHAT_MODEL)\n                .content(request.getModel()).build();\n        configService.createOrUpdate(modelParam);\n        FastChatAIClient.refresh();\n    }\n\n    /**\n     * save common zhipu chat ai config\n     *\n     * @param request\n     */\n    private void saveZhipuChatAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam apikeyParam = SystemConfigParam.builder().code(ZhipuChatAIClient.ZHIPU_API_KEY)\n                .content(request.getApiKey()).build();\n        configService.createOrUpdate(apikeyParam);\n        SystemConfigParam apiHostParam = SystemConfigParam.builder().code(ZhipuChatAIClient.ZHIPU_HOST)\n                .content(request.getApiHost()).build();\n        configService.createOrUpdate(apiHostParam);\n        SystemConfigParam modelParam = SystemConfigParam.builder().code(ZhipuChatAIClient.ZHIPU_MODEL)\n                .content(request.getModel()).build();\n        configService.createOrUpdate(modelParam);\n        ZhipuChatAIClient.refresh();\n    }\n\n    /**\n     * save common tongyi chat ai config\n     *\n     * @param request\n     */\n    private void saveTongyiChatAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam apikeyParam = SystemConfigParam.builder().code(TongyiChatAIClient.TONGYI_API_KEY)\n                .content(request.getApiKey()).build();\n        configService.createOrUpdate(apikeyParam);\n        SystemConfigParam apiHostParam = SystemConfigParam.builder().code(TongyiChatAIClient.TONGYI_HOST)\n                .content(request.getApiHost()).build();\n        configService.createOrUpdate(apiHostParam);\n        SystemConfigParam modelParam = SystemConfigParam.builder().code(TongyiChatAIClient.TONGYI_MODEL)\n                .content(request.getModel()).build();\n        configService.createOrUpdate(modelParam);\n        TongyiChatAIClient.refresh();\n    }\n\n    /**\n     * save common wenxin chat ai config\n     *\n     * @param request\n     */\n    private void saveWenxinAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam apikeyParam = SystemConfigParam.builder().code(WenxinAIClient.WENXIN_ACCESS_TOKEN)\n                .content(request.getApiKey()).build();\n        configService.createOrUpdate(apikeyParam);\n        SystemConfigParam apiHostParam = SystemConfigParam.builder().code(WenxinAIClient.WENXIN_HOST)\n                .content(request.getApiHost()).build();\n        configService.createOrUpdate(apiHostParam);\n        WenxinAIClient.refresh();\n    }\n\n    /**\n     * save common fast chat ai config\n     *\n     * @param request\n     */\n    private void saveBaichuanAIConfig(AIConfigCreateRequest request) {\n        SystemConfigParam apikeyParam = SystemConfigParam.builder().code(BaichuanAIClient.BAICHUAN_API_KEY)\n                .content(request.getApiKey()).build();\n        configService.createOrUpdate(apikeyParam);\n        SystemConfigParam secretKeyParam = SystemConfigParam.builder().code(BaichuanAIClient.BAICHUAN_SECRET_KEY)\n                .content(request.getSecretKey()).build();\n        configService.createOrUpdate(secretKeyParam);\n        SystemConfigParam apiHostParam = SystemConfigParam.builder().code(BaichuanAIClient.BAICHUAN_HOST)\n                .content(request.getApiHost()).build();\n        configService.createOrUpdate(apiHostParam);\n        SystemConfigParam modelParam = SystemConfigParam.builder().code(BaichuanAIClient.BAICHUAN_MODEL)\n                .content(request.getModel()).build();\n        configService.createOrUpdate(modelParam);\n        BaichuanAIClient.refresh();\n    }\n\n    @GetMapping(\"/system_config/{code}\")\n    public DataResult<Config> getSystemConfig(@PathVariable(\"code\") String code) {\n        DataResult<Config> result = configService.find(code);\n        return DataResult.of(result.getData());\n    }\n\n    /**\n     * ai config info\n     *\n     * @return\n     */\n    @GetMapping(\"/system_config/ai\")\n    public DataResult<AIConfig> getChatAiSystemConfig(String aiSqlSource) {\n        DataResult<Config> dbSqlSource = configService.find(RestAIClient.AI_SQL_SOURCE);\n        if (StringUtils.isBlank(aiSqlSource)) {\n            if (Objects.nonNull(dbSqlSource.getData())) {\n                aiSqlSource = dbSqlSource.getData().getContent();\n            }\n        }\n        AIConfig config = new AIConfig();\n        AiSqlSourceEnum aiSqlSourceEnum = AiSqlSourceEnum.getByName(aiSqlSource);\n        if (Objects.isNull(aiSqlSourceEnum)) {\n            aiSqlSource = AiSqlSourceEnum.CHAT2DBAI.getCode();\n            config.setAiSqlSource(aiSqlSource);\n            return DataResult.of(config);\n        }\n        config.setAiSqlSource(aiSqlSource);\n        switch (Objects.requireNonNull(aiSqlSourceEnum)) {\n            case OPENAI:\n                DataResult<Config> apiKey = configService.find(OpenAIClient.OPENAI_KEY);\n                DataResult<Config> apiHost = configService.find(OpenAIClient.OPENAI_HOST);\n                DataResult<Config> httpProxyHost = configService.find(OpenAIClient.PROXY_HOST);\n                DataResult<Config> httpProxyPort = configService.find(OpenAIClient.PROXY_PORT);\n                config.setApiKey(Objects.nonNull(apiKey.getData()) ? apiKey.getData().getContent() : \"\");\n                config.setApiHost(Objects.nonNull(apiHost.getData()) ? apiHost.getData().getContent() : \"\");\n                config.setHttpProxyHost(\n                    Objects.nonNull(httpProxyHost.getData()) ? httpProxyHost.getData().getContent() : \"\");\n                config.setHttpProxyPort(\n                    Objects.nonNull(httpProxyPort.getData()) ? httpProxyPort.getData().getContent() : \"\");\n                break;\n            case CHAT2DBAI:\n                DataResult<Config> chat2dbApiKey = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY);\n                DataResult<Config> chat2dbApiHost = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_HOST);\n                DataResult<Config> chat2dbModel = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_MODEL);\n                config.setApiKey(Objects.nonNull(chat2dbApiKey.getData()) ? chat2dbApiKey.getData().getContent() : \"\");\n                config.setApiHost(\n                    Objects.nonNull(chat2dbApiHost.getData()) ? chat2dbApiHost.getData().getContent() : \"\");\n                config.setModel(Objects.nonNull(chat2dbModel.getData()) ? chat2dbModel.getData().getContent() : \"\");\n                break;\n            case AZUREAI:\n                DataResult<Config> azureApiKey = configService.find(AzureOpenAIClient.AZURE_CHATGPT_API_KEY);\n                DataResult<Config> azureEndpoint = configService.find(AzureOpenAIClient.AZURE_CHATGPT_ENDPOINT);\n                DataResult<Config> azureDeployId = configService.find(AzureOpenAIClient.AZURE_CHATGPT_DEPLOYMENT_ID);\n                config.setApiKey(Objects.nonNull(azureApiKey.getData()) ? azureApiKey.getData().getContent() : \"\");\n                config.setApiHost(Objects.nonNull(azureEndpoint.getData()) ? azureEndpoint.getData().getContent() : \"\");\n                config.setModel(Objects.nonNull(azureDeployId.getData()) ? azureDeployId.getData().getContent() : \"\");\n                break;\n            case RESTAI:\n                DataResult<Config> restAIApiKey = configService.find(RestAIClient.REST_AI_API_KEY);\n                DataResult<Config> restAIApiHost = configService.find(RestAIClient.REST_AI_URL);\n                DataResult<Config> restAIModel = configService.find(RestAIClient.REST_AI_MODEL);\n                config.setApiKey(Objects.nonNull(restAIApiKey.getData()) ? restAIApiKey.getData().getContent() : \"\");\n                config.setApiHost(Objects.nonNull(restAIApiHost.getData()) ? restAIApiHost.getData().getContent() : \"\");\n                config.setModel(Objects.nonNull(restAIModel.getData()) ? restAIModel.getData().getContent() : \"\");\n                break;\n            case FASTCHATAI:\n                DataResult<Config> fastChatApiKey = configService.find(FastChatAIClient.FASTCHAT_API_KEY);\n                DataResult<Config> fastChatApiHost = configService.find(FastChatAIClient.FASTCHAT_HOST);\n                DataResult<Config> fastChatModel = configService.find(FastChatAIClient.FASTCHAT_MODEL);\n                config.setApiKey(Objects.nonNull(fastChatApiKey.getData()) ? fastChatApiKey.getData().getContent() : \"\");\n                config.setApiHost(Objects.nonNull(fastChatApiHost.getData()) ? fastChatApiHost.getData().getContent() : \"\");\n                config.setModel(Objects.nonNull(fastChatModel.getData()) ? fastChatModel.getData().getContent() : \"\");\n                break;\n            case WENXINAI:\n                DataResult<Config> wenxinAccessToken = configService.find(WenxinAIClient.WENXIN_ACCESS_TOKEN);\n                DataResult<Config> wenxinApiHost = configService.find(WenxinAIClient.WENXIN_HOST);\n                config.setApiKey(Objects.nonNull(wenxinAccessToken.getData()) ? wenxinAccessToken.getData().getContent() : \"\");\n                config.setApiHost(Objects.nonNull(wenxinApiHost.getData()) ? wenxinApiHost.getData().getContent() : \"\");\n                break;\n            case BAICHUANAI:\n                DataResult<Config> baichuanApiKey = configService.find(BaichuanAIClient.BAICHUAN_API_KEY);\n                DataResult<Config> baichuanSecretKey = configService.find(BaichuanAIClient.BAICHUAN_SECRET_KEY);\n                DataResult<Config> baichuanApiHost = configService.find(BaichuanAIClient.BAICHUAN_HOST);\n                DataResult<Config> baichuanModel = configService.find(BaichuanAIClient.BAICHUAN_MODEL);\n                config.setApiKey(Objects.nonNull(baichuanApiKey.getData()) ? baichuanApiKey.getData().getContent() : \"\");\n                config.setSecretKey(Objects.nonNull(baichuanSecretKey.getData()) ? baichuanSecretKey.getData().getContent() : \"\");\n                config.setApiHost(Objects.nonNull(baichuanApiHost.getData()) ? baichuanApiHost.getData().getContent() : \"\");\n                config.setModel(Objects.nonNull(baichuanModel.getData()) ? baichuanModel.getData().getContent() : \"\");\n                break;\n            case TONGYIQIANWENAI:\n                DataResult<Config> tongyiApiKey = configService.find(TongyiChatAIClient.TONGYI_API_KEY);\n                DataResult<Config> tongyiApiHost = configService.find(TongyiChatAIClient.TONGYI_HOST);\n                DataResult<Config> tongyiModel = configService.find(TongyiChatAIClient.TONGYI_MODEL);\n                config.setApiKey(Objects.nonNull(tongyiApiKey.getData()) ? tongyiApiKey.getData().getContent() : \"\");\n                config.setApiHost(Objects.nonNull(tongyiApiHost.getData()) ? tongyiApiHost.getData().getContent() : \"\");\n                config.setModel(Objects.nonNull(tongyiModel.getData()) ? tongyiModel.getData().getContent() : \"\");\n                break;\n            case ZHIPUAI:\n                DataResult<Config> zhipuApiKey = configService.find(ZhipuChatAIClient.ZHIPU_API_KEY);\n                DataResult<Config> zhipuApiHost = configService.find(ZhipuChatAIClient.ZHIPU_HOST);\n                DataResult<Config> zhipuModel = configService.find(ZhipuChatAIClient.ZHIPU_MODEL);\n                config.setApiKey(Objects.nonNull(zhipuApiKey.getData()) ? zhipuApiKey.getData().getContent() : \"\");\n                config.setApiHost(Objects.nonNull(zhipuApiHost.getData()) ? zhipuApiHost.getData().getContent() : \"\");\n                config.setModel(Objects.nonNull(zhipuModel.getData()) ? zhipuModel.getData().getContent() : \"\");\n                break;\n            default:\n                break;\n        }\n\n        return DataResult.of(config);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/config/request/AIConfigCreateRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.config.request;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : SystemConfigRequest.java\n */\n@Data\npublic class AIConfigCreateRequest {\n\n    /**\n     * APIKEY\n     */\n    private String apiKey;\n\n    /**\n     * SECRETKEY\n     */\n    private String secretKey;\n\n    /**\n     * APIHOST\n     */\n    private String apiHost;\n\n    /**\n     * api http proxy host\n     */\n    private String httpProxyHost;\n\n    /**\n     * api http proxy port\n     */\n    private String httpProxyPort;\n\n    /**\n     * @see AiSqlSourceEnum\n     */\n    @NotNull\n    private String aiSqlSource;\n\n    /**\n     * return data stream\n     * Optional, default value is TRUE\n     */\n    private Boolean stream = Boolean.TRUE;\n\n    /**\n     * deployed model, default gpt-3.5-turbo\n     */\n    private String model;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/config/request/AISystemConfigRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.config.request;\n\nimport ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : SystemConfigRequest.java\n */\n@Data\npublic class AISystemConfigRequest {\n\n    /**\n     * chat2db APIKEY\n     */\n    private String chat2dbApiKey;\n\n    /**\n     * chat2db APIHOST\n     */\n    private String chat2dbApiHost;\n\n    /**\n     * OpenAi APIKEY\n     * Required when using the OpenAi interface, you can go to the OpenAI official website to view APIKEY\n     */\n    private String apiKey;\n\n    /**\n     * OpenAi APIHOST\n     * Optional, the default value is https://api.openai.com/\n     */\n    private String apiHost;\n\n    /**\n     * http proxy Host\n     * Optional, used to set the HTTP proxy host when requesting the OPENAI interface\n     */\n    private String httpProxyHost;\n\n    /**\n     * http proxy Port\n     * Optional, used to set the HTTP proxy port when requesting the OPENAI interface\n     */\n    private String httpProxyPort;\n\n    /**\n     * AI source\n     * @see AiSqlSourceEnum\n     */\n    private String aiSqlSource;\n\n    /**\n     * Customized AI interface\n     * Required when selecting custom AI, used to set the REST interface URL of the custom AI\n     */\n    private String restAiUrl;\n\n    /**\n     * Whether the Rest interface has streaming output\n     * Optional, default value is TRUE\n     */\n    private Boolean restAiStream = Boolean.TRUE;\n\n    /**\n     * Get Azure OpenAI key credential from the Azure Portal\n     */\n    private String azureApiKey;\n\n    /**\n     * Get Azure OpenAI endpoint from the Azure Portal\n     */\n    private String azureEndpoint;\n\n    /**\n     * deploymentId of the deployed model, default gpt-3.5-turbo\n     */\n    private String azureDeploymentId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/config/request/SystemConfigRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.config.request;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : SystemConfigRequest.java\n */\n@Data\npublic class SystemConfigRequest {\n\n    private String code;\n\n    private String content;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/ChartController.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard;\n\nimport ai.chat2db.server.domain.api.chart.ChartCreateParam;\nimport ai.chat2db.server.domain.api.chart.ChartListQueryParam;\nimport ai.chat2db.server.domain.api.chart.ChartQueryParam;\nimport ai.chat2db.server.domain.api.chart.ChartUpdateParam;\nimport ai.chat2db.server.domain.api.service.ChartService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.controller.dashboard.converter.ChartWebConverter;\nimport ai.chat2db.server.web.api.controller.dashboard.request.ChartCreateRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.request.ChartQueryRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.request.ChartUpdateRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.vo.ChartVO;\nimport jakarta.validation.Valid;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Save chart class\n *\n * @author moji\n * @version ChartController.java, v 0.1 September 18, 2022 10:55 moji Exp $\n * @date 2022/09/18\n */\n@RequestMapping(\"/api/chart\")\n@RestController\npublic class ChartController {\n\n    @Autowired\n    private ChartService chartService;\n\n    @Autowired\n    private ChartWebConverter chartWebConverter;\n\n    /**\n     * Query chart details based on id\n     *\n     * @param id\n     * @return\n     */\n    @GetMapping(\"/{id}\")\n    public DataResult<ChartVO> get(@PathVariable(\"id\") Long id) {\n        ChartQueryParam param = new ChartQueryParam();\n        param.setId(id);\n        param.setUserId(ContextUtils.getUserId());\n        return chartService.queryExistent(param)\n            .map(chartWebConverter::model2vo);\n    }\n\n    /**\n     * Query report list based on ID list\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/listByIds\")\n    public ListResult<ChartVO> list(ChartQueryRequest request) {\n        ChartListQueryParam param = new ChartListQueryParam();\n        param.setIdList(request.getIds());\n        param.setUserId(ContextUtils.getUserId());\n        return chartService.listQuery(param)\n            .map(chartWebConverter::model2vo);\n    }\n\n    /**\n     * Save chart\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/create\")\n    public DataResult<Long> create(@Valid @RequestBody ChartCreateRequest request) {\n        ChartCreateParam chartCreateParam = chartWebConverter.req2param(request);\n        return chartService.createWithPermission(chartCreateParam);\n    }\n\n    /**\n     * Update chart\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/update\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public ActionResult update(@RequestBody ChartUpdateRequest request) {\n        ChartUpdateParam param = chartWebConverter.req2updateParam(request);\n        return chartService.updateWithPermission(param);\n    }\n\n    /**\n     * Delete chart\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public ActionResult delete(@PathVariable(\"id\") Long id) {\n        return chartService.deleteWithPermission(id);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/DashboardController.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Dashboard;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardCreateParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardPageQueryParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardQueryParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardUpdateParam;\nimport ai.chat2db.server.domain.api.service.DashboardService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.controller.dashboard.converter.DashboardWebConverter;\nimport ai.chat2db.server.web.api.controller.dashboard.request.DashboardCreateRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.request.DashboardUpdateRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.vo.DashboardVO;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Save dashboard class\n *\n * @author moji\n * @version DashboardController.java, v 0.1 September 18, 2022 10:55 moji Exp $\n * @date 2022/09/18\n */\n@RequestMapping(\"/api/dashboard\")\n@RestController\npublic class DashboardController {\n\n    @Autowired\n    private DashboardService dashboardService;\n\n    @Autowired\n    private DashboardWebConverter dashboardWebConverter;\n\n    /**\n     * Query dashboard list\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public WebPageResult<DashboardVO> list(DashboardPageQueryParam request) {\n        request.setUserId(ContextUtils.getUserId());\n        PageResult<Dashboard> result = dashboardService.queryPage(request);\n        List<DashboardVO> dashboardVOS = dashboardWebConverter.model2vo(result.getData());\n        return WebPageResult.of(dashboardVOS, result.getTotal(), result.getPageNo(), result.getPageSize());\n    }\n\n    /**\n     * Query dashboard details based on id\n     *\n     * @param id\n     * @return\n     */\n    @GetMapping(\"/{id}\")\n    public DataResult<DashboardVO> get(@PathVariable(\"id\") Long id) {\n        DashboardQueryParam param = new DashboardQueryParam();\n        param.setId(id);\n        param.setUserId(ContextUtils.getUserId());\n        return dashboardService.queryExistent(param)\n            .map(dashboardWebConverter::model2vo);\n    }\n\n    /**\n     * Save dashboard\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/create\")\n    public DataResult<Long> create(@RequestBody DashboardCreateRequest request) {\n        DashboardCreateParam param = dashboardWebConverter.req2param(request);\n        return dashboardService.createWithPermission(param);\n    }\n\n    /**\n     * Update dashboard\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/update\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public ActionResult update(@RequestBody DashboardUpdateRequest request) {\n        DashboardUpdateParam param = dashboardWebConverter.req2updateParam(request);\n        return dashboardService.updateWithPermission(param);\n    }\n\n    /**\n     * Delete dashboard\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public ActionResult delete(@PathVariable(\"id\") Long id) {\n        return dashboardService.deleteWithPermission(id);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/converter/ChartWebConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Chart;\nimport ai.chat2db.server.domain.api.chart.ChartCreateParam;\nimport ai.chat2db.server.domain.api.chart.ChartUpdateParam;\nimport ai.chat2db.server.web.api.controller.dashboard.request.ChartCreateRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.request.ChartUpdateRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.vo.ChartVO;\n\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * @author moji\n * @version ChartWebConverter.java, v 0.1 June 9, 2023 15:46 moji Exp $\n * @date 2023/06/09\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class ChartWebConverter {\n\n    /**\n     * Model conversion\n     *\n     * @param chart\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"connectable\", expression = \"java(chart.getDataSourceName() != null)\"),\n    })\n    public abstract ChartVO model2vo(Chart chart);\n\n    /**\n     * Model conversion\n     *\n     * @param charts\n     * @return\n     */\n    public abstract List<ChartVO> model2vo(List<Chart> charts);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract ChartCreateParam req2param(ChartCreateRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract ChartUpdateParam req2updateParam(ChartUpdateRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/converter/DashboardWebConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Dashboard;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardCreateParam;\nimport ai.chat2db.server.domain.api.param.dashboard.DashboardUpdateParam;\nimport ai.chat2db.server.web.api.controller.dashboard.request.DashboardCreateRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.request.DashboardUpdateRequest;\nimport ai.chat2db.server.web.api.controller.dashboard.vo.DashboardVO;\n\nimport org.mapstruct.Mapper;\n\n/**\n * @author moji\n * @version DashboardWebConverter.java, v 0.1 June 9, 2023 15:45 moji Exp $\n * @date 2023/06/09\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class DashboardWebConverter {\n\n    /**\n     * Model conversion\n     *\n     * @param dashboard\n     * @return\n     */\n    public abstract DashboardVO model2vo(Dashboard dashboard);\n\n    /**\n     * Model conversion\n     *\n     * @param dashboards\n     * @return\n     */\n    public abstract List<DashboardVO> model2vo(List<Dashboard> dashboards);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract DashboardCreateParam req2param(DashboardCreateRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract DashboardUpdateParam req2updateParam(DashboardUpdateRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/request/ChartCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.request;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ChartCreateParam.java, v 0.1 June 9, 2023 15:38 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class ChartCreateRequest {\n\n\n    /**\n     * Chart name\n     */\n    private String name;\n\n    /**\n     * description\n     */\n    private String description;\n\n    /**\n     * chart information\n     */\n    private String schema;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/request/ChartQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.request;\n\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ChartQueryRequest.java, v 0.1 June 9, 2023 17:46 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class ChartQueryRequest {\n\n    /**\n     * Chart ID list\n     */\n    private List<Long> ids;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/request/ChartUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.request;\n\nimport java.time.LocalDateTime;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ChartUpdateParam.java, v 0.1 June 9, 2023 15:39 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class ChartUpdateRequest {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * Chart name\n     */\n    private String name;\n\n    /**\n     * chart information\n     */\n    private String schema;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/request/DashboardCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.request;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DashboardSaveParam.java, v 0.1 June 9, 2023 15:29 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class DashboardCreateRequest {\n\n    /**\n     * Dashboard name\n     */\n    private String name;\n\n    /**\n     * Dashboard layout information\n     */\n    private String schema;\n\n    /**\n     * Chart ID list\n     */\n    private List<Long> chartIds;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/request/DashboardUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.request;\n\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DashboardSaveParam.java, v 0.1 June 9, 2023 15:29 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class DashboardUpdateRequest {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n\n    /**\n     * Dashboard name\n     */\n    private String name;\n\n    /**\n     * Dashboard layout information\n     */\n    private String schema;\n\n    /**\n     * Chart ID list\n     */\n    private List<Long> chartIds;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/vo/ChartVO.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.vo;\n\nimport java.util.Date;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version Chart.java, v 0.1 June 9, 2023 15:37 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class ChartVO {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Chart name\n     */\n    private String name;\n\n    /**\n     * Chart description\n     */\n    private String description;\n\n    /**\n     * chart information\n     */\n    private String schema;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * Data source name\n     */\n    private String dataSourceName;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * Whether it can be connected, false means it cannot be connected, which means the data source has been deleted or does not exist.\n     */\n    private Boolean connectable;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/dashboard/vo/DashboardVO.java",
    "content": "package ai.chat2db.server.web.api.controller.dashboard.vo;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version Dashboard.java, v 0.1 June 9, 2023 15:32 moji Exp $\n * @date 2023/06/09\n */\n@Data\npublic class DashboardVO {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    private Date gmtCreate;\n\n    /**\n     * modified time\n     */\n    private Date gmtModified;\n\n    /**\n     * Dashboard name\n     */\n    private String name;\n\n    /**\n     * Dashboard description\n     */\n    private String description;\n\n    /**\n     * Dashboard layout information\n     */\n    private String schema;\n\n    /**\n     * Chart ID list\n     */\n    private List<Long> chartIds;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/DataSourceController.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.ConsoleCloseParam;\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceSelector;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceUpdateParam;\nimport ai.chat2db.server.domain.api.service.ConsoleService;\nimport ai.chat2db.server.domain.api.service.DataSourceService;\nimport ai.chat2db.server.tools.common.exception.ConnectionException;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.ssh.SSHManager;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.data.source.converter.DataSourceWebConverter;\nimport ai.chat2db.server.web.api.controller.data.source.converter.SSHWebConverter;\nimport ai.chat2db.server.web.api.controller.data.source.request.ConsoleCloseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.ConsoleConnectRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceAttachRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceCloneRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceCloseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceCreateRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceQueryRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceTestRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceUpdateRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.SSHTestRequest;\nimport ai.chat2db.server.web.api.controller.data.source.vo.DataSourceVO;\nimport ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO;\nimport com.jcraft.jsch.Session;\nimport jakarta.validation.Valid;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Database connection class\n *\n * @author moji\n * @version ConnectionController.java, v 0.1 September 16, 2022 14:07 moji Exp $\n * @date 2022/09/16\n */\n@ConnectionInfoAspect\n@RequestMapping(\"/api/connection\")\n@RestController\n@Slf4j\npublic class DataSourceController {\n\n    private static final DataSourceSelector DATA_SOURCE_SELECTOR = DataSourceSelector.builder()\n        .environment(Boolean.TRUE)\n        .build();\n\n    @Autowired\n    private DataSourceService dataSourceService;\n\n    @Autowired\n    private ConsoleService consoleService;\n\n    @Autowired\n    private DataSourceWebConverter dataSourceWebConverter;\n\n    @Autowired\n    private SSHWebConverter sshWebConverter;\n\n    /**\n     * Database connection test\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(\"/datasource/pre_connect\")\n    public ActionResult preConnect(@RequestBody DataSourceTestRequest request) {\n        DataSourcePreConnectParam param = dataSourceWebConverter.testRequest2param(request);\n        return dataSourceService.preConnect(param);\n    }\n\n    /**\n     * Database connection test\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(\"/ssh/pre_connect\")\n    public ActionResult sshConnect(@RequestBody SSHTestRequest request) {\n        Session session = null;\n        try {\n            session = SSHManager.getSSHSession(sshWebConverter.toInfo(request));\n        } catch (Exception e) {\n            log.error(\"sshConnect error\", e);\n            throw new ConnectionException(\"connection.ssh.error\", null, e);\n        } finally {\n            if (session != null) {\n                session.disconnect();\n            }\n        }\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * Database connection\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/datasource/connect\")\n    public ListResult<DatabaseVO> attach(@Valid @NotNull DataSourceAttachRequest request) {\n        ListResult<Database> databaseDTOListResult = dataSourceService.connect(request.getId());\n        List<DatabaseVO> databaseVOS = dataSourceWebConverter.databaseDto2vo(databaseDTOListResult.getData());\n        return ListResult.of(databaseVOS);\n    }\n\n    /**\n     * Close database connection\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/datasource/close\")\n    public ActionResult close(@Valid @NotNull DataSourceCloseRequest request) {\n        return dataSourceService.close(request.getId());\n    }\n\n    /**\n     * Console connection\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/console/connect\")\n    public ActionResult connect(@Valid @NotNull ConsoleConnectRequest request) {\n        ConsoleConnectParam consoleConnectParam = dataSourceWebConverter.request2connectParam(request);\n        return consoleService.createConsole(consoleConnectParam);\n    }\n\n    /**\n     * Close the Console connection\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/console/close\")\n    public ActionResult closeConsole(@Valid @NotNull ConsoleCloseRequest request) {\n        ConsoleCloseParam closeParam = dataSourceWebConverter.request2closeParam(request);\n        return consoleService.closeConsole(closeParam);\n    }\n\n    /**\n     * Query the database connection I established\n     *\n     * @param request\n     * @return\n     * @version 2.1.0\n     */\n    @GetMapping(\"/datasource/list\")\n    public WebPageResult<DataSourceVO> list(DataSourceQueryRequest request) {\n        DataSourcePageQueryParam param = dataSourceWebConverter.queryReq2param(request);\n        PageResult<DataSource> result = dataSourceService.queryPageWithPermission(param, DATA_SOURCE_SELECTOR);\n        List<DataSourceVO> dataSourceVOS = dataSourceWebConverter.dto2vo(result.getData());\n        return WebPageResult.of(dataSourceVOS, result.getTotal(), result.getPageNo(), result.getPageSize());\n    }\n\n    /**\n     * Get connection content\n     *\n     * @param id\n     * @return\n     */\n    @GetMapping(\"/datasource/{id}\")\n    public DataResult<DataSourceVO> queryById(@PathVariable(\"id\") Long id) {\n        DataResult<DataSource> dataResult = dataSourceService.queryExistent(id, DATA_SOURCE_SELECTOR);\n        DataSourceVO dataSourceVO = dataSourceWebConverter.dto2vo(dataResult.getData());\n        if (StringUtils.isNotBlank(dataSourceVO.getUser())) {\n            dataSourceVO.setAuthenticationType(\"1\");\n        } else {\n            dataSourceVO.setAuthenticationType(\"2\");\n        }\n        return DataResult.of(dataSourceVO);\n    }\n\n    /**\n     * save connection\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/datasource/create\")\n    public DataResult<Long> create(@RequestBody DataSourceCreateRequest request) {\n        DataSourceCreateParam param = dataSourceWebConverter.createReq2param(request);\n        return dataSourceService.createWithPermission(param);\n    }\n\n    /**\n     * Update connection\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/datasource/update\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public DataResult<Long> update(@RequestBody DataSourceUpdateRequest request) {\n        DataSourceUpdateParam param = dataSourceWebConverter.updateReq2param(request);\n        return dataSourceService.updateWithPermission(param);\n    }\n\n    /**\n     *  clone connection\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/datasource/clone\")\n    public DataResult<Long> copy(@RequestBody DataSourceCloneRequest request) {\n        return dataSourceService.copyByIdWithPermission(request.getId());\n    }\n\n    /**\n     * Delete connection\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/datasource/{id}\")\n    public ActionResult delete(@PathVariable Long id) {\n        return dataSourceService.deleteWithPermission(id);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/converter/DataSourceWebConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.enums.DataSourceKindEnum;\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.param.ConsoleCloseParam;\nimport ai.chat2db.server.domain.api.param.ConsoleConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePageQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourcePreConnectParam;\nimport ai.chat2db.server.domain.api.param.datasource.DataSourceUpdateParam;\nimport ai.chat2db.server.web.api.controller.data.source.request.ConsoleCloseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.ConsoleConnectRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceCreateRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceQueryRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceTestRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceUpdateRequest;\nimport ai.chat2db.server.web.api.controller.data.source.vo.DataSourceVO;\nimport ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO;\nimport ai.chat2db.spi.model.Database;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * @author moji\n * @version DataSourceWebConverter.java, v 0.1 September 23, 2022 16:45 moji Exp $\n * @date 2022/09/23\n */\n@Mapper(componentModel = \"spring\", imports = {DataSourceKindEnum.class})\npublic abstract class DataSourceWebConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"user\", target = \"userName\"),\n        @Mapping(target = \"kind\", expression = \"java(DataSourceKindEnum.PRIVATE.getCode())\")\n    })\n    public abstract DataSourceCreateParam createReq2param(DataSourceCreateRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"user\", target = \"userName\")\n    })\n    public abstract DataSourceUpdateParam updateReq2param(DataSourceUpdateRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract DataSourcePageQueryParam queryReq2param(DataSourceQueryRequest request);\n\n    /**\n     * Model conversion\n     *\n     * @param dataSource\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"user\", source = \"userName\")\n    })\n    public abstract DataSourceVO dto2vo(DataSource dataSource);\n\n    /**\n     * Model conversion\n     *\n     * @param dataSources\n     * @return\n     */\n    public abstract List<DataSourceVO> dto2vo(List<DataSource> dataSources);\n\n    /**\n     * Model conversion\n     *\n     * @param databaseDTO\n     * @return\n     */\n    public abstract DatabaseVO databaseDto2vo(Database databaseDTO);\n\n    /**\n     * Model conversion\n     *\n     * @param databaseDTOS\n     * @return\n     */\n    public abstract List<DatabaseVO> databaseDto2vo(List<Database> databaseDTOS);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract DataSourcePreConnectParam testRequest2param(DataSourceTestRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract ConsoleConnectParam request2connectParam(ConsoleConnectRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract ConsoleCloseParam request2closeParam(ConsoleCloseRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/converter/SSHWebConverter.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.data.source.converter;\n\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.server.web.api.controller.data.source.request.SSHTestRequest;\n\nimport org.mapstruct.Mapper;\n\n/**\n * @author jipengfei\n * @version : SSHWebConverter.java\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class SSHWebConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract SSHInfo toInfo(SSHTestRequest request);\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/ConsoleCloseRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConsoleContentRequest.java, v 0.1 October 30, 2022 15:52 moji Exp $\n * @date 2022/10/30\n */\n@Data\npublic class ConsoleCloseRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo{\n\n    /**\n     * console id\n     */\n    private Long consoleId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/ConsoleConnectRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConsoleContentRequest.java, v 0.1 October 30, 2022 15:52 moji Exp $\n * @date 2022/10/30\n */\n@Data\npublic class ConsoleConnectRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {\n\n    /**\n     * console id\n     */\n    private Long consoleId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceAttachRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCreateRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceAttachRequest implements DataSourceBaseRequestInfo{\n\n    /**\n     * primary key id\n     */\n    @NotNull\n    private Long id;\n\n    @Override\n    public Long getDataSourceId() {\n        return id;\n    }\n\n    @Override\n    public String getDatabaseName() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceBaseRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version MysqlBaseRequest.java, v 0.1 September 18, 2022 11:51 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class DataSourceBaseRequest implements DataSourceBaseRequestInfo{\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located\n     */\n    private String schemaName;\n\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceBaseRequestInfo.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.data.source.request;\n\n/**\n * @author jipengfei\n * @version : DataSourceBaseRequestInfo.java\n */\npublic interface DataSourceBaseRequestInfo {\n\n    /**\n     * Get datasource id\n     * @return\n     */\n    Long getDataSourceId();\n\n    /**\n     * get datasource name\n     * @return\n     */\n    String getDatabaseName();\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceCloneRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCloneRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceCloneRequest {\n\n    /**\n     * primary key id\n     */\n    private Long id;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceCloseRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCreateRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceCloseRequest {\n\n    /**\n     * primary key id\n     */\n    @NotNull\n    private Long id;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceConsoleRequestInfo.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.data.source.request;\n\n/**\n * @author jipengfei\n * @version : DataSourceConsoleRequestInfo.java\n */\npublic interface DataSourceConsoleRequestInfo extends DataSourceBaseRequestInfo{\n\n    /**\n     *\n     * @return\n     */\n     Long getConsoleId();\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCreateRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceCreateRequest {\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    @NotNull\n    private String url;\n\n    /**\n     * Connect username\n     */\n    private String user;\n\n    /**\n     * password\n     */\n    @NotNull\n    private String password;\n\n    /**\n     * Certification type\n     */\n    private String authenticationType;\n\n    /**\n     * Connection Type\n     */\n    @NotNull\n    private String type;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n\n\n    /**\n     * environment id\n     */\n    @NotNull\n    private Long environmentId;\n\n    /**\n     * service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceQueryRequest extends PageQueryRequest {\n\n    /**\n     * Alias fuzzy search terms\n     */\n    private String searchKey;\n    /**\n     * Connection Type\n     *\n     * @see ai.chat2db.server.domain.api.enums.DataSourceKindEnum\n     */\n    private String kind;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceTestRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCreateRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceTestRequest {\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    @NotNull\n    private String url;\n\n    /**\n     * Connect users\n     */\n    private String user;\n\n    /**\n     * password\n     */\n    @NotNull\n    private String password;\n\n    /**\n     * Database connection type\n     */\n    @NotNull\n    private String type;\n\n    /**\n     * Certification type\n     */\n    private String authenticationType;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/DataSourceUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.request;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionCreateRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceUpdateRequest {\n\n    /**\n     * primary key id\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * Connect users\n     */\n    private String user;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Connection Type\n     */\n    private String type;\n\n    /**\n     * environment type\n     * @see EnvTypeEnum\n     */\n    private String envType;\n\n    /**\n     * environment id\n     */\n    private Integer environmentId;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n\n    /**\n     * service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/request/SSHTestRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.data.source.request;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : SSHTestRequest.java\n */\n@Data\npublic class SSHTestRequest {\n    private boolean use;\n\n    private String hostName;\n\n    private String port;\n\n    private String userName;\n\n    private String localPort;\n\n    private String authenticationType;\n\n    private String password;\n\n    private String keyFile;\n\n    private String passphrase;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/vo/DataSourceVO.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.vo;\n\nimport java.util.List;\n\nimport ai.chat2db.server.common.api.controller.vo.SimpleEnvironmentVO;\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionVO.java, v 0.1 September 16, 2022 14:15 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataSourceVO {\n\n    /**\n     * primary key id\n     */\n    private Long id;\n\n    /**\n     * Connection alias\n     */\n    private String alias;\n\n    /**\n     * connection address\n     */\n    private String url;\n\n    /**\n     * Connect users\n     */\n    private String user;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * Certification type\n     */\n    private String authenticationType;\n    /**\n     * Connection Type\n     */\n    private String type;\n\n    /**\n     * environment type\n     */\n    private String envType;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * port\n     */\n    private String port;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    ///**\n    // * ssh\n    // */\n    //private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n    /**\n     * Driver configuration\n     */\n    private DriverConfig driverConfig;\n\n    /**\n     * environment id\n     */\n    private Long environmentId;\n\n    /**\n     * environment\n     */\n    private SimpleEnvironmentVO environment;\n\n    /**\n     * Connection Type\n     *\n     * @see ai.chat2db.server.domain.api.enums.DataSourceKindEnum\n     */\n    private String kind;\n\n    /**\n     * service name\n     */\n    private String serviceName;\n\n    /**\n     * Service type\n     */\n    private String serviceType;\n\n    /**\n     * Whether to support database\n     */\n    private boolean supportDatabase;\n\n    /**\n     * Whether to support schema\n     */\n    private boolean supportSchema;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/vo/DatabaseVO.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.vo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DatabaseVO.java, v 0.1 September 16, 2022 17:24 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DatabaseVO {\n\n    /**\n     * DB name\n     */\n    private String name;\n\n    /**\n     * DB description\n     */\n    private String description;\n\n    /**\n     * The number of tables or keys under DB\n     */\n    private Integer count;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/data/source/vo/EnvVO.java",
    "content": "package ai.chat2db.server.web.api.controller.data.source.vo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version EnvVO.java, v 0.1 September 18, 2022 14:06 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class EnvVO {\n\n    /**\n     * environment code\n     */\n    private String code;\n\n    /**\n     * environment name\n     */\n    private String name;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/driver/JdbcDriverController.java",
    "content": "package ai.chat2db.server.web.api.controller.driver;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.service.JdbcDriverService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.web.api.controller.driver.request.JdbcDriverRequest;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.util.JdbcJarUtils;\nimport org.apache.commons.io.FilenameUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\n/**\n * JDBC driver management\n *\n * @author moji\n * @version JdbcDriverController.java, v 0.1 September 16, 2022 17:41 moji Exp $\n * @date 2022/09/16\n */\n@RequestMapping(\"/api/jdbc/driver\")\n@RestController\npublic class JdbcDriverController {\n\n    @Autowired\n    private JdbcDriverService jdbcDriverService;\n\n    /**\n     * Query current DB driver information\n     *\n     * @param dbType\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public DataResult<DBConfig> list(@RequestParam String dbType) {\n        return jdbcDriverService.getDrivers(dbType);\n    }\n\n    /**\n     * Download driver\n     *\n     * @param dbType\n     * @return\n     */\n\n    @GetMapping(\"/download\")\n    public ActionResult download(@RequestParam String dbType) {\n        return jdbcDriverService.download(dbType);\n    }\n\n    /**\n     * Upload driver\n     *\n     * @param multipartFiles\n     * @return\n     */\n    @PostMapping(\"/upload\")\n    public ListResult<String> upload(@RequestParam MultipartFile[] multipartFiles) {\n        List<String> list = new ArrayList<>();\n        for (int i = 0; i < multipartFiles.length; i++) {\n\n            MultipartFile multipartFile = multipartFiles[i];\n            String originalFilename = FilenameUtils.getName(multipartFile.getOriginalFilename());\n            String location = JdbcJarUtils.PATH + originalFilename;\n            try {\n                multipartFile.transferTo(new File(location));\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n            list.add(originalFilename);\n        }\n        return ListResult.of(list);\n    }\n\n    /**\n     * save\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/save\")\n    public ActionResult save(@RequestBody JdbcDriverRequest request) {\n\n        return jdbcDriverService.upload(request.getDbType(), request.getJdbcDriverClass(),\n            String.join(\",\", request.getJdbcDriver()));\n    }\n\n    ///**\n    // * Delete driver\n    // *\n    // * @param request\n    // * @return\n    // */\n    //@DeleteMapping(\"/delete\")\n    //public ActionResult delete(@RequestBody KeyDeleteRequest request) {\n    //    return null;\n    //}\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/driver/request/JdbcDriverRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.driver.request;\n\nimport java.util.List;\n\nimport lombok.Data;\n\n@Data\npublic class JdbcDriverRequest {\n    String jdbcDriverClass;\n    String dbType;\n\n    List<String> jdbcDriver;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/ConverterController.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx;\n\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport ai.chat2db.server.web.api.controller.ncx.service.ConverterService;\nimport ai.chat2db.server.web.api.controller.ncx.vo.UploadVO;\nimport ai.chat2db.server.web.api.util.FileUtils;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.io.File;\nimport java.util.Objects;\nimport java.util.UUID;\n\n/**\n * ConverterController\n *\n * @author lzy\n **/\n@RequestMapping(\"/api/converter\")\n@RestController\n@Slf4j\npublic class ConverterController {\n\n    @Autowired\n    private ConverterService converterService;\n\n    /**\n     * Export tutorial\n     *\n     * @param file file\n     * @return DataResult<UploadVO>\n     * @see <a href=\"https://blog.csdn.net/kkk123445/article/details/122514124?spm=1001.2014.3001.5502\" />\n     **/\n    @SneakyThrows\n    @PostMapping(\"/ncx/upload\")\n    public DataResult<UploadVO> ncxUploadFile(@RequestParam(\"file\") MultipartFile file) {\n        log.info(\"Start uploading ncx\");\n        // Verify file suffix\n        String fileExtension = FileUtils.getFileExtension(Objects.requireNonNull(file.getOriginalFilename()));\n        if (!fileExtension.equalsIgnoreCase(FileUtils.ConfigFile.NCX.name())) {\n            return DataResult.error(\"1\", \"The uploaded file must be an ncx file！\");\n        }\n        File temp = new File(ConfigUtils.CONFIG_BASE_PATH + File.separator + UUID.randomUUID() + \".tmp\");\n        file.transferTo(temp);\n        return DataResult.of(converterService.uploadFile(temp));\n    }\n\n    @SneakyThrows\n    @PostMapping(\"/dbp/upload\")\n    public DataResult<UploadVO> edbpUploadFile(@RequestParam(\"file\") MultipartFile file) {\n        // Verify file suffix\n        String fileExtension = FileUtils.getFileExtension(Objects.requireNonNull(file.getOriginalFilename()));\n        if (!fileExtension.equalsIgnoreCase(FileUtils.ConfigFile.DBP.name())) {\n            return DataResult.error(\"1\", \"The uploaded file must be a dbp file！\");\n        }\n        File temp = new File(ConfigUtils.CONFIG_BASE_PATH + File.separator + UUID.randomUUID() + \".tmp\");\n        file.transferTo(temp);\n        return DataResult.of(converterService.dbpUploadFile(temp));\n    }\n\n\n    /**\n     * Import the connection information of datagrip, copy the connection through ctrl/cmd + c (shift multiple selection), and then import it.\n     * There is no password in the currently copied connection information, and there is no ssh connection information either.\n     *\n     * @param text text\n     * @return DataResult<UploadVO>\n     **/\n    @SneakyThrows\n    @PostMapping(\"/datagrip/upload\")\n    public DataResult<UploadVO> datagripUploadFile(@RequestParam(\"text\") String text) {\n        return DataResult.of(converterService.datagripUploadFile(text));\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/cipher/CommonCipher.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.cipher;\n\nimport java.util.Formatter;\n\n/**\n * CommonCipher Public encryption/decryption\n *\n * @author lzy\n */\npublic abstract class CommonCipher {\n\n    public String encryptString(String plaintext) {\n        return null;\n    }\n\n    public String decryptString(String ciphertext) {\n        return null;\n    }\n\n    public String printHexBinary(byte[] data) {\n        StringBuilder hexBuilder = new StringBuilder();\n        Formatter formatter = new Formatter(hexBuilder);\n        for (byte b : data) {\n            formatter.format(\"%02x\", b);\n        }\n        return hexBuilder.toString();\n    }\n\n    public static byte[] parseHexBinary(String data) {\n        return hexStringToByteArray(data);\n    }\n\n    public static byte[] hexStringToByteArray(String hex) {\n        if (hex.length() % 2 != 0) {\n            throw new IllegalArgumentException(\"Hex string length must be even\");\n        }\n        byte[] bytes = new byte[hex.length() / 2];\n        for (int i = 0; i < hex.length(); i += 2) {\n            String byteString = hex.substring(i, i + 2);\n            bytes[i / 2] = (byte) Integer.parseInt(byteString, 16);\n        }\n        return bytes;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/cipher/Navicat11Cipher.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.cipher;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.util.Arrays;\n\n/**\n * Navicat11 and below password encryption and decryption\n *\n * @author lzy\n */\npublic class Navicat11Cipher extends CommonCipher {\n    public static final String DefaultUserKey = \"3DC5CA39\";\n    private static byte[] IV;\n\n    private static SecretKeySpec key;\n    private static Cipher encryptor;\n    private static Cipher decrypt;\n\n    private static void initKey() {\n        try {\n            MessageDigest sha1 = MessageDigest.getInstance(\"SHA1\");\n            byte[] userKey_data = Navicat11Cipher.DefaultUserKey.getBytes(StandardCharsets.UTF_8);\n            sha1.update(userKey_data, 0, userKey_data.length);\n            key = new SecretKeySpec(sha1.digest(), \"Blowfish\");\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static void initCipherEncrypt() {\n        try {\n            // Must use NoPadding\n            encryptor = Cipher.getInstance(\"Blowfish/ECB/NoPadding\");\n            encryptor.init(Cipher.ENCRYPT_MODE, key);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static void initCipherDecrypt() {\n        try {\n            // Must use NoPadding\n            decrypt = Cipher.getInstance(\"Blowfish/ECB/NoPadding\");\n            decrypt.init(Cipher.DECRYPT_MODE, key);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static void initIV() {\n        try {\n            byte[] initVec = parseHexBinary(\"FFFFFFFFFFFFFFFF\");\n            IV = encryptor.doFinal(initVec);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private void xorBytes(byte[] a, byte[] b) {\n        for (int i = 0; i < a.length; i++) {\n            int aVal = a[i] & 0xff; // convert byte to integer\n            int bVal = b[i] & 0xff;\n            a[i] = (byte) (aVal ^ bVal); // xor aVal and bVal and typecast to byte\n        }\n    }\n\n    private void xorBytes(byte[] a, byte[] b, int l) {\n        for (int i = 0; i < l; i++) {\n            int aVal = a[i] & 0xff; // convert byte to integer\n            int bVal = b[i] & 0xff;\n            a[i] = (byte) (aVal ^ bVal); // xor aVal and bVal and typecast to byte\n        }\n    }\n\n    static {\n        initKey();\n        initCipherEncrypt();\n        initCipherDecrypt();\n        initIV();\n    }\n\n    private byte[] Encrypt(byte[] inData) {\n        try {\n            byte[] CV = Arrays.copyOf(IV, IV.length);\n            byte[] ret = new byte[inData.length];\n\n            int blocks_len = inData.length / 8;\n            int left_len = inData.length % 8;\n\n            for (int i = 0; i < blocks_len; i++) {\n                byte[] temp = Arrays.copyOfRange(inData, i * 8, (i * 8) + 8);\n\n                xorBytes(temp, CV);\n                temp = encryptor.doFinal(temp);\n                xorBytes(CV, temp);\n\n                System.arraycopy(temp, 0, ret, i * 8, 8);\n            }\n\n            if (left_len != 0) {\n                CV = encryptor.doFinal(CV);\n                byte[] temp = Arrays.copyOfRange(inData, blocks_len * 8, (blocks_len * 8) + left_len);\n                xorBytes(temp, CV, left_len);\n                System.arraycopy(temp, 0, ret, blocks_len * 8, temp.length);\n            }\n\n            return ret;\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public String encryptString(String inputString) {\n        try {\n            byte[] inData = inputString.getBytes(StandardCharsets.UTF_8);\n            byte[] outData = Encrypt(inData);\n            return printHexBinary(outData);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private byte[] Decrypt(byte[] inData) {\n        try {\n            byte[] cv = Arrays.copyOf(IV, IV.length);\n            byte[] ret = new byte[inData.length];\n\n            int blocks_len = inData.length / 8;\n            int left_len = inData.length % 8;\n\n            for (int i = 0; i < blocks_len; i++) {\n                byte[] temp = Arrays.copyOfRange(inData, i * 8, (i * 8) + 8);\n\n                temp = decrypt.doFinal(temp);\n                xorBytes(temp, cv);\n                System.arraycopy(temp, 0, ret, i * 8, 8);\n                for (int j = 0; j < cv.length; j++) {\n                    cv[j] = (byte) (cv[j] ^ inData[i * 8 + j]);\n                }\n            }\n\n            if (left_len != 0) {\n                cv = encryptor.doFinal(cv);\n                byte[] temp = Arrays.copyOfRange(inData, blocks_len * 8, (blocks_len * 8) + left_len);\n\n                xorBytes(temp, cv, left_len);\n                for (int j = 0; j < temp.length; j++) {\n                    ret[blocks_len * 8 + j] = temp[j];\n                }\n            }\n\n            return ret;\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public String decryptString(String hexString) {\n        if (StringUtils.isEmpty(hexString)) {\n            return \"\";\n        }\n        try {\n            byte[] inData = parseHexBinary(hexString);\n            byte[] outData = Decrypt(inData);\n            return new String(outData, StandardCharsets.UTF_8);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/cipher/Navicat12Cipher.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.cipher;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * Navicat12 and above password encryption and decryption\n *\n * @author lzy\n */\npublic class Navicat12Cipher extends CommonCipher {\n    private static final SecretKeySpec AES_KEY;\n    private static final IvParameterSpec AES_IV;\n\n    static {\n        AES_KEY = new SecretKeySpec(\"libcckeylibcckey\".getBytes(StandardCharsets.UTF_8), \"AES\");\n        AES_IV = new IvParameterSpec(\"libcciv libcciv \".getBytes(StandardCharsets.UTF_8));\n    }\n\n    @Override\n    public String encryptString(String plaintext) {\n        try {\n            Cipher cipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");\n            cipher.init(Cipher.ENCRYPT_MODE, AES_KEY, AES_IV);\n            byte[] ret = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));\n            return printHexBinary(ret);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public String decryptString(String ciphertext) {\n        if (StringUtils.isEmpty(ciphertext)) {\n            return \"\";\n        }\n        try {\n            Cipher cipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");\n            cipher.init(Cipher.DECRYPT_MODE, AES_KEY, AES_IV);\n            byte[] ret = cipher.doFinal(parseHexBinary(ciphertext));\n            return new String(ret, StandardCharsets.UTF_8);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/dbeaver/DBSValueEncryptor.java",
    "content": "/*\n * DBeaver - Universal Database Manager\n * Copyright (C) 2010-2023 DBeaver Corp and others\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ai.chat2db.server.web.api.controller.ncx.dbeaver;\n\n/**\n * Value encryptor\n */\npublic interface DBSValueEncryptor {\n\n    byte[] encryptValue(byte[] value);\n\n    byte[] decryptValue(byte[] value);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/dbeaver/DefaultValueEncryptor.java",
    "content": "/*\n * DBeaver - Universal Database Manager\n * Copyright (C) 2010-2023 DBeaver Corp and others\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.server.web.api.controller.ncx.dbeaver;\n\nimport org.apache.poi.util.IOUtils;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.CipherInputStream;\nimport javax.crypto.CipherOutputStream;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\n/**\n * Default value encryptor.\n *\n * Uses Eclipse secure preferences to read/write secrets.\n */\npublic class DefaultValueEncryptor implements DBSValueEncryptor {\n\n    public static final String CIPHER_NAME = \"AES/CBC/PKCS5Padding\";\n    public static final String KEY_ALGORITHM = \"AES\";\n\n    private static final byte[] LOCAL_KEY_CACHE = new byte[] { -70, -69, 74, -97, 119, 74, -72, 83, -55, 108, 45, 101, 61, -2, 84, 74 };\n\n    private final SecretKey secretKey;\n    private final Cipher cipher;\n\n    public DefaultValueEncryptor(SecretKey secretKey) {\n        this.secretKey = secretKey;\n        try {\n            this.cipher = Cipher.getInstance(CIPHER_NAME);\n        } catch (Exception e) {\n            throw new IllegalStateException(\"Internal error during encrypted init\", e);\n        }\n    }\n\n    /**\n     * View the default SecretKey through DBeaver source code\n     **/\n    public static SecretKey getLocalSecretKey() {\n        return new SecretKeySpec(LOCAL_KEY_CACHE, DefaultValueEncryptor.KEY_ALGORITHM);\n    }\n\n    public static SecretKey makeSecretKeyFromPassword(String password) {\n        byte[] bytes = password.getBytes(StandardCharsets.UTF_8);\n        byte[] passBytes = Arrays.copyOf(bytes, 16);\n        return new SecretKeySpec(passBytes, KEY_ALGORITHM);\n    }\n\n    @Override\n    public byte[] encryptValue(byte[] value) {\n        try {\n            cipher.init(Cipher.ENCRYPT_MODE, secretKey);\n            byte[] iv = cipher.getIV();\n\n            ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();\n            try (CipherOutputStream cipherOut = new CipherOutputStream(resultBuffer, cipher)) {\n                resultBuffer.write(iv);\n                cipherOut.write(value);\n            }\n            return resultBuffer.toByteArray();\n        } catch (Exception e) {\n            throw new RuntimeException(\"Error encrypting value\", e);\n        }\n    }\n\n    @Override\n    public byte[] decryptValue(byte[] value) {\n        try (InputStream byteStream = new ByteArrayInputStream(value)) {\n            byte[] fileIv = new byte[16];\n            byteStream.read(fileIv);\n            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(fileIv));\n\n            try (CipherInputStream cipherIn = new CipherInputStream(byteStream, cipher)) {\n                ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();\n                IOUtils.copy(cipherIn, resultBuffer);\n                return resultBuffer.toByteArray();\n            }\n\n        } catch (Exception e) {\n            throw new RuntimeException(\"Error decrypting value\", e);\n        }\n    }\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/enums/DataBaseType.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.enums;\n\nimport lombok.Getter;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * DataBaseType\n *\n * @author lzy\n **/\n@Getter\npublic enum DataBaseType {\n    /**\n     * MYSQL\n     */\n    MYSQL(\"jdbc:mysql://%s:%s\"),\n    /**\n     * ORACLE\n     */\n    ORACLE(\"jdbc:oracle:thin:@%s:%s:XE\"),\n    /**\n     * SQL_SERVER\n     */\n    SQLSERVER(\"jdbc:sqlserver://%s:%s\"),\n    /**\n     * SQL_SERVER\n     */\n    SQLITE(\"jdbc:sqlite:%s\"),\n    /**\n     * POSTGRESQL\n     **/\n    POSTGRESQL(\"jdbc:postgresql://%s:%s\"),\n    /**\n     * DB2\n     **/\n    DB2(\"jdbc:db2://%s:%s\"),\n    /**\n     * Mariadb\n     **/\n    MARIADB(\"jdbc:mariadb://%s:%s\"),\n    /**\n     * DM\n     **/\n    DM(\"jdbc:dm://%s:%s\"),\n    /**\n     * KINGBASE\n     **/\n    KINGBASE(\"jdbc:kingbase8://%s:%s\"),\n    /**\n     * Presto\n     **/\n    PRESTO(\"jdbc:presto://%s:%s\"),\n    /**\n     * OceanBase\n     **/\n    OCEANBASE(\"jdbc:oceanbase://%s:%s\"),\n    /**\n     * Hive\n     **/\n    HIVE(\"jdbc:hive2://%s:%s\"),\n    /**\n     * ClickHouse\n     **/\n    CLICKHOUSE(\"jdbc:clickhouse://%s:%s\");\n\n    private String urlString;\n\n    DataBaseType(String urlString) {\n        this.urlString = urlString;\n    }\n\n    public void setUrlString(String urlString) {\n        this.urlString = urlString;\n    }\n\n    public static DataBaseType matchType(String value) {\n        if (StringUtils.isNotEmpty(value)) {\n            for (DataBaseType dataBase : DataBaseType.values()) {\n                //kingbase -> kingbase8\n                if (value.toUpperCase().contains(dataBase.name())) {\n                    return dataBase;\n                }\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/enums/ExportConstants.java",
    "content": "/*\n * DBeaver - Universal Database Manager\n * Copyright (C) 2010-2023 DBeaver Corp and others\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ai.chat2db.server.web.api.controller.ncx.enums;\n\n/**\n * Import/Export constants\n */\npublic class ExportConstants {\n\n    public static final String ARCHIVE_FILE_EXT = \".dbp\"; //NON-NLS-1\n    public static final String CONFIG_FILE = \".dbeaver\"; //NON-NLS-1\n    public static final String CONFIG_DATASOURCE_FILE = \"data-sources.json\"; //NON-NLS-1\n    public static final String CONFIG_CREDENTIALS_FILE = \"credentials-config.json\"; //NON-NLS-1\n    public static final String DIR_PROJECTS = \"projects\"; //NON-NLS-1\n    public static final String DIR_DRIVERS = \"drivers\"; //NON-NLS-1\n    public static final String DIR_CONNECTIONS = \"connections\"; //NON-NLS-1\n    public static final String DIR_CONFIGURATION = \"configuration\"; //NON-NLS-1\n    public static final String GENERIC = \"generic\"; //NON-NLS-1\n\n    public static final String META_FILENAME = \"meta.xml\"; //NON-NLS-1\n\n    public static final String TAG_ARCHIVE = \"archive\"; //NON-NLS-1\n    public static final String TAG_SOURCE = \"source\";\n    public static final String TAG_PROJECTS = \"projects\"; //NON-NLS-1\n    public static final String TAG_PROJECT = \"project\"; //NON-NLS-1\n    public static final String TAG_RESOURCE = \"resource\"; //NON-NLS-1\n    public static final String TAG_ATTRIBUTE = \"attribute\"; //NON-NLS-1\n    public static final String TAG_LIBRARIES = \"libraries\"; //NON-NLS-1\n\n    public static final String ATTR_VERSION = \"version\"; //NON-NLS-1\n    public static final String ATTR_HOST = \"host\";\n    public static final String ATTR_ADDRESS = \"address\";\n    public static final String ATTR_TIME = \"time\";\n    public static final String ATTR_QUALIFIER = \"qualifier\"; //NON-NLS-1\n    public static final String ATTR_NAME = \"name\"; //NON-NLS-1\n    public static final String ATTR_VALUE = \"value\"; //NON-NLS-1\n    public static final String ATTR_DIRECTORY = \"directory\"; //NON-NLS-1\n    public static final String ATTR_DESCRIPTION = \"description\"; //NON-NLS-1\n    public static final String ATTR_CHARSET = \"charset\"; //NON-NLS-1\n    public static final String ATTR_PATH = \"path\"; //NON-NLS-1\n    public static final String ATTR_FILE = \"file\"; //NON-NLS-1\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/enums/VersionEnum.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.enums;\n\n/**\n * navicat version enumeration (version distinguishes navicat encryption algorithm)\n *\n * @author lzy\n */\npublic enum VersionEnum {\n    /**\n     * navicat11\n     */\n    native11,\n    /**\n     * navicat12+\n     */\n    navicat12more\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/factory/CipherFactory.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.factory;\n\nimport ai.chat2db.server.web.api.controller.ncx.cipher.CommonCipher;\nimport ai.chat2db.server.web.api.controller.ncx.cipher.Navicat11Cipher;\nimport ai.chat2db.server.web.api.controller.ncx.cipher.Navicat12Cipher;\nimport ai.chat2db.server.web.api.controller.ncx.enums.VersionEnum;\nimport lombok.SneakyThrows;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * CipherFactory\n *\n * @author lzy\n **/\n@Service\npublic class CipherFactory {\n    /**\n     * NavicatCipher cache pool\n     */\n    private static final Map<String, CommonCipher> REPORT_POOL = new ConcurrentHashMap<>(0);\n\n    static {\n        REPORT_POOL.put(VersionEnum.native11.name(), new Navicat11Cipher());\n        REPORT_POOL.put(VersionEnum.navicat12more.name(), new Navicat12Cipher());\n    }\n\n    /**\n     * Get the corresponding encryption/decryption method\n     *\n     * @param type type\n     * @return ITokenGranter\n     */\n    @SneakyThrows\n    public static CommonCipher get(String type) {\n        CommonCipher cipher = REPORT_POOL.get(type);\n        if (cipher == null) {\n            throw new ClassNotFoundException(\"no CommonCipher was found\");\n        } else {\n            return cipher;\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/service/ConverterService.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.service;\n\nimport ai.chat2db.server.web.api.controller.ncx.vo.UploadVO;\n\nimport java.io.File;\nimport java.io.InputStream;\n\n/**\n * ConverterService\n *\n * @author lzy\n **/\npublic interface ConverterService {\n\n    UploadVO uploadFile(File file);\n\n    UploadVO dbpUploadFile(File file);\n\n    UploadVO datagripUploadFile(String text);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/service/impl/ConverterServiceImpl.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.service.impl;\n\nimport ai.chat2db.server.domain.core.util.DesUtil;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.domain.repository.entity.DataSourceDO;\nimport ai.chat2db.server.domain.repository.mapper.ChartMapper;\nimport ai.chat2db.server.domain.repository.mapper.DataSourceMapper;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.controller.ncx.cipher.CommonCipher;\nimport ai.chat2db.server.web.api.controller.ncx.dbeaver.DefaultValueEncryptor;\nimport ai.chat2db.server.web.api.controller.ncx.enums.DataBaseType;\nimport ai.chat2db.server.web.api.controller.ncx.enums.ExportConstants;\nimport ai.chat2db.server.web.api.controller.ncx.enums.VersionEnum;\nimport ai.chat2db.server.web.api.controller.ncx.factory.CipherFactory;\nimport ai.chat2db.server.web.api.controller.ncx.service.ConverterService;\nimport ai.chat2db.server.web.api.controller.ncx.vo.UploadVO;\nimport ai.chat2db.server.web.api.util.XMLUtils;\nimport ai.chat2db.spi.model.SSHInfo;\nimport cn.hutool.core.io.FileUtil;\nimport com.alibaba.excel.util.FileUtils;\nimport com.alibaba.fastjson2.JSON;\nimport com.alibaba.fastjson2.JSONObject;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NamedNodeMap;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\n/**\n * ConverterServiceImpl\n *\n * @author lzy\n **/\n@Service\n@Slf4j\n@Transactional(rollbackFor = Exception.class)\npublic class ConverterServiceImpl implements ConverterService {\n\n    private static final double NAVICAT11 = 1.1D;\n\n    private static CommonCipher cipher;\n\n    /**\n     * Connection information header\n     **/\n    private static final String DATASOURCE_SETTINGS = \"#DataSourceSettings#\";\n    private static final String XML_HEADER = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\";\n    /**\n     * xml connection information start flag\n     **/\n    private static final String BEGIN = \"#BEGIN#\";\n    /**\n     * Password json key\n     **/\n    private static final String connection = \"#connection\";\n\n\n    private DataSourceMapper getDataSourceMapper(){\n        return Dbutils.getMapper(DataSourceMapper.class);\n    }\n    /**\n     * jdbc universal matching ip and port\n     */\n    public static final Pattern IP_PORT = Pattern.compile(\"jdbc:(?<type>[a-z]+)://(?<host>[a-zA-Z0-9-//.]+):(?<port>[0-9]+)\");\n    /**\n     * oracle matching ip and port\n     */\n    public static final Pattern ORACLE_IP_PORT = Pattern.compile(\"jdbc:(?<type>[a-z]+):(?<child>[a-z]+):@(?<host>[a-zA-Z0-9-//.]+):(?<port>[0-9]+)\");\n\n    @Override\n    public UploadVO uploadFile(File file) {\n        UploadVO vo = new UploadVO();\n        try {\n            // List<Map <connection name, Map<property name, value>>> The connection to be imported\n            List<Map<String, Map<String, String>>> configMap = new ArrayList<>();\n            //1、Create a DocumentBuilderFactory object\n            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\n            //2、Create a DocumentBuilder object\n            //Create DocumentBuilder object\n            DocumentBuilder db = dbf.newDocumentBuilder();\n            //3、Load the xml file into the current project through the parser method of the DocumentBuilder object\n            Document document = db.parse(file);\n            //Get the collection of all Connections nodes\n            NodeList connectList = document.getElementsByTagName(\"Connection\");\n\n            NodeList nodeList = document.getElementsByTagName(\"Connections\");\n            //Select the first node\n            NamedNodeMap verMap = nodeList.item(0).getAttributes();\n            double version = Double.parseDouble((verMap.getNamedItem(\"Ver\").getNodeValue()));\n            if (version <= NAVICAT11) {\n                cipher = CipherFactory.get(VersionEnum.native11.name());\n            } else {\n                cipher = CipherFactory.get(VersionEnum.navicat12more.name());\n            }\n            //Configure map\n            Map<String, Map<String, String>> connectionMap = new HashMap<>();\n            //Traverse each Connections node\n            for (int i = 0; i < connectList.getLength(); i++) {\n                //Get a Connection node through the item(i) method, the index value of nodeList starts from 0\n                Node connect = connectList.item(i);\n                //Get the collection of all properties of the Connection node\n                NamedNodeMap attrs = connect.getAttributes();\n                //Traverse the properties of Connection\n                Map<String, String> map = new HashMap<>(0);\n                for (int j = 0; j < attrs.getLength(); j++) {\n                    //Obtain a certain attribute of the connect node through the item(index) method\n                    Node attr = attrs.item(j);\n                    map.put(attr.getNodeName(), attr.getNodeValue());\n                }\n                connectionMap.put(map.get(\"ConnectionName\") + map.get(\"ConnType\"), map);\n            }\n            configMap.add(connectionMap);\n            log.info(\"insert to db, param:{}\", JSON.toJSONString(configMap));\n            // Get the link imported from navicat and write it into the h2 database of chat2db\n            insertDBConfig(configMap);\n            log.info(\"insert to h2 success\");\n            //Delete temporary files\n            FileUtils.delete(file);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n        return vo;\n    }\n\n    @SneakyThrows\n    @Override\n    public UploadVO dbpUploadFile(File file) {\n        UploadVO vo = new UploadVO();\n        Document metaTree;\n        //Projects waiting to be deleted\n        List<String> projects = new ArrayList<>();\n        try (ZipFile zipFile = new ZipFile(file, ZipFile.OPEN_READ)) {\n            ZipEntry metaEntry = zipFile.getEntry(ExportConstants.META_FILENAME);\n            if (metaEntry == null) {\n                throw new RuntimeException(\"Cannot find meta file\");\n            }\n            try (InputStream metaStream = zipFile.getInputStream(metaEntry)) {\n                metaTree = XMLUtils.parseDocument(metaStream);\n            } catch (Exception e) {\n                throw new RuntimeException(\"Cannot parse meta file: \" + e.getMessage());\n            }\n            Element projectsElement = XMLUtils.getChildElement(metaTree.getDocumentElement(), ExportConstants.TAG_PROJECTS);\n            if (projectsElement != null) {\n                final Collection<Element> projectList = XMLUtils.getChildElementList(projectsElement, ExportConstants.TAG_PROJECT);\n                for (Element projectElement : projectList) {\n                    //Get project name\n                    String projectName = projectElement.getAttribute(ExportConstants.ATTR_NAME);\n                    //Import matching file directory\n                    String config = ConfigUtils.CONFIG_BASE_PATH + File.separator + projectName + File.separator + ExportConstants.CONFIG_FILE;\n                    importDbeaverConfig(new File(config),\n                            projectElement,\n                            //Cannot be replaced by File.separator\n                            ExportConstants.DIR_PROJECTS + \"/\" + projectName + \"/\",\n                            zipFile);\n                    //Add to delete list\n                    projects.add(projectName);\n                    //Configured json file\n                    File json = new File(config + File.separator + ExportConstants.CONFIG_DATASOURCE_FILE);\n                    JSONObject jsonObject = JSON.parseObject(new FileInputStream(json));\n                    JSONObject connections = jsonObject.getJSONObject(ExportConstants.DIR_CONNECTIONS);\n                    Set<String> keys = connections.keySet();\n                    for (String key : keys) {\n                        JSONObject configurations = connections.getJSONObject(key);\n                        JSONObject configuration = configurations.getJSONObject(ExportConstants.DIR_CONFIGURATION);\n                        //Match database type\n                        String provider = configurations.getString(\"provider\");\n                        if (provider.equals(ExportConstants.GENERIC)) {\n                            //Custom driverCustom driver\n                            JSONObject drivers = jsonObject.getJSONObject(ExportConstants.DIR_DRIVERS);\n                            //Get driver id\n                            String driverId = configurations.getString(\"driver\");\n                            //Get all generic\n                            JSONObject generics = drivers.getJSONObject(provider);\n                            //Get your own driver\n                            JSONObject generic = generics.getJSONObject(driverId);\n                            //If it does not exist, it will not be imported.\n                            if (null == generic) {\n                                continue;\n                            }\n                            //Assign driver name to determine the type of database\n                            provider = generic.getString(\"name\");\n                        }\n                        DataBaseType dataBaseType = DataBaseType.matchType(provider.toUpperCase());\n                        DataSourceDO dataSourceDO;\n                        //The database type is not matched. For example: dbeaver supports custom drivers, etc., but chat2DB does not support it yet.\n                        if (null != dataBaseType) {\n                            //Password information\n                            File credentials = new File(config + File.separator + ExportConstants.CONFIG_CREDENTIALS_FILE);\n                            DefaultValueEncryptor defaultValueEncryptor = new DefaultValueEncryptor(DefaultValueEncryptor.getLocalSecretKey());\n                            JSONObject credentialsJson = JSON.parseObject(defaultValueEncryptor.decryptValue(Files.readAllBytes(credentials.toPath())));\n                            dataSourceDO = new DataSourceDO();\n                            Date dateTime = new Date();\n                            dataSourceDO.setGmtCreate(dateTime);\n                            dataSourceDO.setGmtModified(dateTime);\n                            //Insert user id\n                            dataSourceDO.setUserId(ContextUtils.getUserId());\n                            dataSourceDO.setAlias(configurations.getString(\"name\"));\n                            dataSourceDO.setHost(configuration.getString(\"host\"));\n                            dataSourceDO.setPort(configuration.getString(\"port\"));\n                            dataSourceDO.setUrl(configuration.getString(\"url\"));\n                            //ssh is set to false\n                            SSHInfo sshInfo = new SSHInfo();\n                            sshInfo.setUse(false);\n                            dataSourceDO.setSsh(JSON.toJSONString(sshInfo));\n                            if (null != credentialsJson) {\n                                JSONObject userInfo = credentialsJson.getJSONObject(key);\n                                if (null != userInfo) {\n                                    JSONObject userPassword = userInfo.getJSONObject(connection);\n                                    dataSourceDO.setUserName(userPassword.getString(\"user\"));\n                                    DesUtil desUtil = new DesUtil(DesUtil.DES_KEY);\n                                    String password = userPassword.getString(\"password\");\n                                    String encryptStr = desUtil.encrypt(Optional.ofNullable(password).orElse(\"\"), \"CBC\");\n                                    dataSourceDO.setPassword(encryptStr);\n                                }\n                            }\n                            dataSourceDO.setType(dataBaseType.name());\n                            getDataSourceMapper().insert(dataSourceDO);\n                        }\n                    }\n                }\n            }\n        }\n        //Delete temporary files\n        FileUtils.delete(file);\n        //Delete the temporary configuration file generated by dbp when importing dbeaver\n        projects.forEach(v -> FileUtils.delete(new File(ConfigUtils.CONFIG_BASE_PATH + File.separator + v)));\n        return vo;\n    }\n\n    @SneakyThrows\n    private static void importDbeaverConfig(File resource, Element resourceElement, String containerPath, ZipFile zipFile) {\n        for (Element childElement : XMLUtils.getChildElementList(resourceElement, ExportConstants.TAG_RESOURCE)) {\n            String childName = childElement.getAttribute(ExportConstants.ATTR_NAME);\n            String entryPath = containerPath + childName;\n            ZipEntry resourceEntry = zipFile.getEntry(entryPath);\n            if (resourceEntry == null) {\n                continue;\n            }\n            boolean isDirectory = resourceEntry.isDirectory();\n            if (isDirectory) {\n                File folder = new File(resource.getPath());\n                if (!folder.exists()) {\n                    FileUtil.mkdir(folder);\n                }\n                importDbeaverConfig(folder, childElement, entryPath + \"/\", zipFile);\n            } else {\n                File file = new File(resource.getPath() + File.separator + childName);\n                FileUtil.writeFromStream(zipFile.getInputStream(resourceEntry), file, true);\n            }\n        }\n    }\n\n    @SneakyThrows\n    @Override\n    public UploadVO datagripUploadFile(String text) {\n        UploadVO vo = new UploadVO();\n        if (!text.startsWith(DATASOURCE_SETTINGS)) {\n            throw new RuntimeException(\"连接信息的头部不正确！\");\n        }\n        String[] items = text.split(\"\\n\");\n        List<String> configs = new ArrayList<>();\n        for (int i = 0; i < items.length; i++) {\n            if (items[i].equals(BEGIN)) {\n                configs.add(XML_HEADER + items[i + 1]);\n            }\n        }\n        for (String config : configs) {\n            //1、Create a DocumentBuilderFactory object\n            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\n            //2、Create a DocumentBuilder object\n            //Create DocumentBuilder object\n            DocumentBuilder db = dbf.newDocumentBuilder();\n            //3、Load the xml file into the current project through the parser method of the DocumentBuilder object\n            try (InputStream inputStream = new ByteArrayInputStream(config.getBytes(StandardCharsets.UTF_8))) {\n                Document document = db.parse(inputStream);\n                // Get the root element\n                Element rootElement = document.getDocumentElement();\n                //Create datasource\n                DataSourceDO dataSourceDO = new DataSourceDO();\n                Date dateTime = new Date();\n                dataSourceDO.setGmtCreate(dateTime);\n                dataSourceDO.setGmtModified(dateTime);\n                dataSourceDO.setAlias(rootElement.getAttribute(\"name\"));\n                //Insert user id\n                dataSourceDO.setUserId(ContextUtils.getUserId());\n                // Get child elements database-info\n                Element databaseInfoElement = (Element) rootElement.getElementsByTagName(\"database-info\").item(0);\n\n                // Get connection related information\n                String type = databaseInfoElement.getAttribute(\"dbms\");\n                String jdbcUrl = rootElement.getElementsByTagName(\"jdbc-url\").item(0).getTextContent();\n                String username = rootElement.getElementsByTagName(\"user-name\").item(0).getTextContent();\n                String driverName = rootElement.getElementsByTagName(\"jdbc-driver\").item(0).getTextContent();\n                String host = \"\";\n                String port = \"\";\n                if (type.equals(DataBaseType.ORACLE.name())) {\n                    // Create Matcher object\n                    Matcher matcher = ORACLE_IP_PORT.matcher(jdbcUrl);\n                    // Find matching IP address and port number\n                    if (matcher.find()) {\n                        host = matcher.group(\"host\");\n                        port = matcher.group(\"port\");\n                    }\n                } else {\n                    // Create Matcher object\n                    Matcher matcher = IP_PORT.matcher(jdbcUrl);\n                    // Find matching IP address and port number\n                    if (matcher.find()) {\n                        host = matcher.group(\"host\");\n                        port = matcher.group(\"port\");\n\n                    }\n                }\n                //ssh is set to false\n                SSHInfo sshInfo = new SSHInfo();\n                sshInfo.setUse(false);\n                dataSourceDO.setSsh(JSON.toJSONString(sshInfo));\n                dataSourceDO.setHost(host);\n                dataSourceDO.setPort(port);\n                dataSourceDO.setUrl(jdbcUrl);\n                dataSourceDO.setUserName(username);\n                dataSourceDO.setDriver(driverName);\n                dataSourceDO.setType(type);\n                getDataSourceMapper().insert(dataSourceDO);\n            }\n        }\n        return vo;\n    }\n\n    /**\n     * Write to database\n     *\n     * @param list Read data from ncx file\n     */\n    @SneakyThrows\n    public void insertDBConfig(List<Map<String, Map<String, String>>> list) {\n        for (Map<String, Map<String, String>> map : list) {\n            for (Map.Entry<String, Map<String, String>> valueMap : map.entrySet()) {\n                Map<String, String> resultMap = valueMap.getValue();\n                // The version of mysql cannot be distinguished yet\n                DataBaseType dataBaseType = DataBaseType.matchType(resultMap.get(\"ConnType\"));\n                DataSourceDO dataSourceDO;\n                if (null == dataBaseType) {\n                    //The database type is not matched. For example: navicat supports MongoDB, etc., but chat2DB does not support it yet.\n                    continue;\n                } else {\n                    dataSourceDO = new DataSourceDO();\n                    dataSourceDO.setHost(resultMap.get(\"Host\"));\n                    dataSourceDO.setPort(resultMap.get(\"Port\"));\n                    dataSourceDO.setUrl(String.format(dataBaseType.getUrlString(), dataSourceDO.getHost(), dataSourceDO.getPort()));\n                }\n                // Decrypt password\n                String password = cipher.decryptString(resultMap.getOrDefault(\"Password\", \"\"));\n                Date dateTime =new Date();\n                dataSourceDO.setGmtCreate(dateTime);\n                dataSourceDO.setGmtModified(dateTime);\n                dataSourceDO.setAlias(resultMap.get(\"ConnectionName\"));\n                dataSourceDO.setUserName(resultMap.get(\"UserName\"));\n                dataSourceDO.setType(resultMap.get(\"ConnType\"));\n                //Insert user id\n                dataSourceDO.setUserId(ContextUtils.getUserId());\n                //Password is the decrypted ciphertext, and then uses chat2db for encryption.\n                DesUtil desUtil = new DesUtil(DesUtil.DES_KEY);\n                String encryptStr = desUtil.encrypt(password, \"CBC\");\n                dataSourceDO.setPassword(encryptStr);\n                SSHInfo sshInfo = new SSHInfo();\n                if (\"false\".equals(resultMap.get(\"SSH\"))) {\n                    sshInfo.setUse(false);\n                } else {\n                    sshInfo.setUse(true);\n                    sshInfo.setHostName(resultMap.get(\"SSH_Host\"));\n                    sshInfo.setPort(resultMap.get(\"SSH_Port\"));\n                    sshInfo.setUserName(resultMap.get(\"SSH_UserName\"));\n                    // Currently chat2DB only supports password and Private key\n                    boolean passwordType = \"password\".equalsIgnoreCase(resultMap.get(\"SSH_AuthenMethod\"));\n                    sshInfo.setAuthenticationType(passwordType ? \"password\" : \"Private key\");\n                    if (passwordType) {\n                        // Decrypt password\n                        String ssh_password = cipher.decryptString(resultMap.getOrDefault(\"SSH_Password\", \"\"));\n                        sshInfo.setPassword(ssh_password);\n                    } else {\n                        sshInfo.setKeyFile(resultMap.get(\"SSH_PrivateKey\"));\n                        sshInfo.setPassphrase(resultMap.get(\"SSH_Passphrase\"));\n                    }\n                }\n                dataSourceDO.setSsh(JSON.toJSONString(sshInfo));\n                log.info(\"begin insert:{}\", JSON.toJSONString(dataSourceDO));\n                getDataSourceMapper().insert(dataSourceDO);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ncx/vo/UploadVO.java",
    "content": "package ai.chat2db.server.web.api.controller.ncx.vo;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * UploadVO\n *\n * @author lzy\n **/\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UploadVO {\n    private String result;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/log/OperationLogController.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.log;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.OperationLog;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogPageQueryParam;\nimport ai.chat2db.server.domain.api.service.OperationLogService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.controller.operation.log.converter.OperationLogWebConverter;\nimport ai.chat2db.server.web.api.controller.operation.log.request.OperationLogCreateRequest;\nimport ai.chat2db.server.web.api.controller.operation.log.request.OperationLogQueryRequest;\nimport ai.chat2db.server.web.api.controller.operation.log.vo.OperationLogVO;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * History service class\n *\n * @author moji\n * @version HistoryManageController.java, v 0.1 September 18, 2022 10:55 moji Exp $\n * @date 2022/09/18\n */\n@RequestMapping(\"/api/operation/log\")\n@RestController\npublic class OperationLogController {\n\n    @Autowired\n    private OperationLogService operationLogService;\n\n    @Autowired\n    private OperationLogWebConverter operationLogWebConverter;\n\n    /**\n     * Query execution records\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public WebPageResult<OperationLogVO> list(OperationLogQueryRequest request) {\n        OperationLogPageQueryParam param = operationLogWebConverter.req2param(request);\n        param.setUserId(ContextUtils.getUserId());\n        PageResult<OperationLog> result = operationLogService.queryPage(param);\n        List<OperationLogVO> operationLogVOList = operationLogWebConverter.dto2vo(result.getData());\n        return WebPageResult.of(operationLogVOList, result.getTotal(), result.getPageNo(), result.getPageSize());\n    }\n\n    /**\n     * Add history\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/create\")\n    public DataResult<Long> create(@RequestBody OperationLogCreateRequest request) {\n        OperationLogCreateParam param = operationLogWebConverter.createReq2param(request);\n        return operationLogService.create(param);\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/log/converter/OperationLogWebConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.log.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.OperationLog;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationLogPageQueryParam;\nimport ai.chat2db.server.web.api.controller.operation.log.request.OperationLogCreateRequest;\nimport ai.chat2db.server.web.api.controller.operation.log.request.OperationLogQueryRequest;\nimport ai.chat2db.server.web.api.controller.operation.log.vo.OperationLogVO;\n\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * @author moji\n * @version HistoryWebConverter.java, v 0.1 September 25, 2022 16:53 moji Exp $\n * @date 2022/09/25\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class OperationLogWebConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract OperationLogCreateParam createReq2param(OperationLogCreateRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract OperationLogPageQueryParam req2param(OperationLogQueryRequest request);\n\n    /**\n     * Model conversion\n     *\n     * @param ddlDTO\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"ddl\", target = \"name\"),\n        @Mapping(target = \"connectable\", expression = \"java(ddlDTO.getDataSourceName() != null)\"),\n    })\n    public abstract OperationLogVO dto2vo(OperationLog ddlDTO);\n\n    /**\n     * Model conversion\n     *\n     * @param ddlDTOS\n     * @return\n     */\n    public abstract List<OperationLogVO> dto2vo(List<OperationLog> ddlDTOS);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/log/request/OperationLogCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.log.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DdlCreateRequest.java, v 0.1 September 18, 2022 11:13 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class OperationLogCreateRequest extends DataSourceBaseRequest {\n\n    /**\n     * file alias\n     */\n    private String name;\n\n    /**\n     * ddl type\n     */\n    @NotNull\n    private String type;\n\n    /**\n     * ddl content\n     */\n    @NotNull\n    private String ddl;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/log/request/OperationLogQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.log.request;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DdlCreateRequest.java, v 0.1 September 18, 2022 11:13 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class OperationLogQueryRequest extends PageQueryRequest {\n\n    /**\n     * Fuzzy word search\n     */\n    private String searchKey;\n\n    /**\n     * Data source id\n     */\n    private Long dataSourceId;\n\n    /**\n     * Name database\n     */\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/log/vo/OperationLogVO.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.log.vo;\n\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n/**\n * @author moji\n * @version DdlVO.java, v 0.1 September 18, 2022 11:06 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class OperationLogVO {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * creation time\n     */\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    private LocalDateTime gmtModified;\n\n    /**\n     * file alias\n     */\n    private String name;\n\n    /**\n     * Data source id\n     */\n    private Long dataSourceId;\n\n    /**\n     * Data source name\n     */\n    private String dataSourceName;\n\n    /**\n     * Is it connectable?\n     */\n    private Boolean connectable;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * ddl language type\n     */\n    private String type;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * state\n     */\n    private String status;\n\n    /**\n     * Number of operation lines\n     */\n    private Long operationRows;\n\n    /**\n     * Length of use\n     */\n    private Long useTime;\n\n    /**\n     * Extended Information\n     */\n    private String extendInfo;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/saved/OperationSavedController.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.saved;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Operation;\nimport ai.chat2db.server.domain.api.param.operation.OperationPageQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationSavedParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationUpdateParam;\nimport ai.chat2db.server.domain.api.service.OperationService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.controller.operation.saved.converter.OperationWebConverter;\nimport ai.chat2db.server.web.api.controller.operation.saved.request.BatchTabCloseRequest;\nimport ai.chat2db.server.web.api.controller.operation.saved.request.OperationCreateRequest;\nimport ai.chat2db.server.web.api.controller.operation.saved.request.OperationQueryRequest;\nimport ai.chat2db.server.web.api.controller.operation.saved.request.OperationUpdateRequest;\nimport ai.chat2db.server.web.api.controller.operation.saved.vo.OperationVO;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * My save service class\n *\n * @author moji\n * @version DdlManageController.java, v 0.1 September 16, 2022 19:59 moji Exp $\n * @date 2022/09/16\n */\n@RequestMapping(\"/api/operation/saved\")\n@RestController\npublic class OperationSavedController {\n\n    @Autowired\n    private OperationService operationService;\n\n    @Autowired\n    private OperationWebConverter operationWebConverter;\n\n    /**\n     * Check my saves\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public WebPageResult<OperationVO> list(OperationQueryRequest request) {\n        OperationPageQueryParam param = operationWebConverter.queryReq2param(request,ContextUtils.getUserId());\n        param.setUserId(ContextUtils.getUserId());\n        PageResult<Operation> dtoPageResult = operationService.queryPage(param);\n        List<OperationVO> operationVOS = operationWebConverter.dto2vo(dtoPageResult.getData());\n        return WebPageResult.of(operationVOS, dtoPageResult.getTotal(), request.getPageNo(), request.getPageSize());\n    }\n\n    /**\n     * Query console based on id\n     *\n     * @param id\n     * @return\n     */\n    @GetMapping(\"/{id}\")\n    public DataResult<OperationVO> get(@PathVariable(\"id\") Long id) {\n        OperationQueryParam param = new OperationQueryParam();\n        param.setId(id);\n        param.setUserId(ContextUtils.getUserId());\n        return operationService.queryExistent(param).map(operationWebConverter::dto2vo);\n    }\n\n    /**\n     * Add my save\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/create\")\n    public DataResult<Long> create(@RequestBody OperationCreateRequest request) {\n        OperationSavedParam param = operationWebConverter.req2param(request);\n        param.setTabOpened(\"y\");\n        return operationService.createWithPermission(param);\n    }\n\n    /**\n     * Update my save\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/update\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public ActionResult update(@RequestBody OperationUpdateRequest request) {\n        OperationUpdateParam param = operationWebConverter.updateReq2param(request);\n        return operationService.updateWithPermission(param);\n    }\n\n    /**\n     * Close tags in batches\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/batch_tab_close\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public ActionResult batchTabClose(@RequestBody BatchTabCloseRequest request) {\n        if (CollectionUtils.isEmpty(request.getIdList())) {\n            return ActionResult.isSuccess();\n        }\n        request.getIdList().forEach(id -> {\n            OperationUpdateParam param = new OperationUpdateParam();\n            param.setId(id);\n            param.setTabOpened(\"n\");\n            operationService.updateWithPermission(param);\n        });\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * delete my save\n     *\n     * @param id\n     * @return\n     */\n    @DeleteMapping(\"/{id}\")\n    public ActionResult delete(@PathVariable(\"id\") Long id) {\n        return operationService.deleteWithPermission(id);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/saved/converter/OperationWebConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.saved.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.Operation;\nimport ai.chat2db.server.domain.api.param.operation.OperationPageQueryParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationSavedParam;\nimport ai.chat2db.server.domain.api.param.operation.OperationUpdateParam;\nimport ai.chat2db.server.web.api.controller.operation.saved.request.OperationCreateRequest;\nimport ai.chat2db.server.web.api.controller.operation.saved.request.OperationQueryRequest;\nimport ai.chat2db.server.web.api.controller.operation.saved.request.OperationUpdateRequest;\nimport ai.chat2db.server.web.api.controller.operation.saved.vo.OperationVO;\n\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * @author moji\n * @version DdlManageWebConverter.java, v 0.1 September 26, 2022 10:08 moji Exp $\n * @date 2022/09/26\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class OperationWebConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract OperationSavedParam req2param(OperationCreateRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract OperationUpdateParam updateReq2param(OperationUpdateRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract OperationPageQueryParam queryReq2param(OperationQueryRequest request, Long userId);\n\n    /**\n     * Model conversion\n     *\n     * @param ddlDTO\n     * @return\n     */\n    @Mappings({\n        @Mapping(target = \"connectable\", expression = \"java(ddlDTO.getDataSourceName() != null)\"),\n    })\n    public abstract OperationVO dto2vo(Operation ddlDTO);\n\n    /**\n     * Model conversion\n     *\n     * @param ddlDTOS\n     * @return\n     */\n    public abstract List<OperationVO> dto2vo(List<Operation> ddlDTOS);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/saved/request/BatchTabCloseRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.saved.request;\n\nimport java.util.List;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * close tab\n * @author Jiaju Zhuang\n */\n@Data\npublic class BatchTabCloseRequest {\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private List<Long> idList;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/saved/request/OperationCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.saved.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.tools.base.enums.StatusEnum;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DdlCreateRequest.java, v 0.1 September 18, 2022 11:13 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class OperationCreateRequest extends DataSourceBaseRequest {\n\n    /**\n     * file alias\n     */\n    private String name;\n\n    /**\n     * Save state\n     * @see StatusEnum\n     */\n    @NotNull\n    private String status;\n\n    /**\n     * DB TYPE\n     */\n    @NotNull\n    private String type;\n\n    /**\n     * ddl content\n     */\n    @NotNull\n    private String ddl;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/saved/request/OperationQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.saved.request;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DdlCreateRequest.java, v 0.1 September 18, 2022 11:13 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class OperationQueryRequest extends PageQueryRequest {\n\n    /**\n     * Data source id\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * Fuzzy search terms\n     */\n    private String searchKey;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * ddl statement status: DRAFT/RELEASE\n     */\n    private String status;\n\n    /**\n     * orderBy modify time desc\n     */\n    private Boolean orderByDesc;\n\n    /**\n     * orderBy create time desc\n     */\n    private Boolean orderByCreateDesc;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/saved/request/OperationUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.saved.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DdlCreateRequest.java, v 0.1 September 18, 2022 11:13 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class OperationUpdateRequest {\n\n    /**\n     * primary key\n     */\n    @NotNull\n    private Long id;\n\n    /**\n     * file alias\n     */\n    private String name;\n\n    /**\n     * Data source connection ID\n     */\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located\n     */\n    private String schemaName;\n\n    /**\n     * Database type\n     */\n    private String type;\n\n    /**\n     * ddl content\n     */\n    @NotNull\n    private String ddl;\n\n    /**\n     * Update status DRAFT/RELEASE\n     */\n    private String status;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n\n    /**\n     * user id\n     */\n    private Long userId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/operation/saved/vo/OperationVO.java",
    "content": "package ai.chat2db.server.web.api.controller.operation.saved.vo;\n\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version DdlVO.java, v 0.1 September 18, 2022 11:06 moji Exp $\n * @date 2022/09/18\n */\n@Data\npublic class OperationVO {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * file alias\n     */\n    private String name;\n\n    /**\n     * Data source id\n     */\n    private Long dataSourceId;\n\n    /**\n     * Data source name\n     */\n    private String dataSourceName;\n\n    /**\n     * Is it connectable?\n     */\n    private Boolean connectable;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located\n     */\n    private String schemaName;\n\n    /**\n     * ddl language type\n     */\n    private String type;\n\n    /**\n     * ddl content\n     */\n    private String ddl;\n\n    /**\n     * ddl statement status: DRAFT/RELEASE\n     */\n    private String status;\n\n    /**\n     * Whether it is opened in the tab, y means open, n means not opened\n     */\n    private String tabOpened;\n\n    /**\n     * operation type\n     */\n    private String operationType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/pin/PinController.java",
    "content": "package ai.chat2db.server.web.api.controller.pin;\n\nimport ai.chat2db.server.domain.api.service.PinService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.web.api.controller.pin.converter.PinWebConverter;\nimport ai.chat2db.server.web.api.controller.pin.request.PinTableRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;\nimport jakarta.validation.Valid;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RequestMapping(\"/api/pin\")\n@RestController\npublic class PinController {\n\n    @Autowired\n    private PinService pinService;\n\n    @Autowired\n    private PinWebConverter pinWebConverter;\n\n    @PostMapping(\"/table/add\")\n    public ActionResult add(@Valid @RequestBody PinTableRequest request) {\n        return pinService.pinTable(pinWebConverter.req2param(request));\n    }\n\n    @PostMapping(\"/table/delete\")\n    public ActionResult delete(@Valid @RequestBody PinTableRequest request) {\n        return pinService.deletePinTable(pinWebConverter.req2param(request));\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/pin/converter/PinWebConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.pin.converter;\n\nimport ai.chat2db.server.domain.api.param.PinTableParam;\nimport ai.chat2db.server.web.api.controller.pin.request.PinTableRequest;\nimport org.mapstruct.Mapper;\n\n@Mapper(componentModel = \"spring\")\npublic abstract class PinWebConverter {\n\n\n    public abstract PinTableParam req2param(PinTableRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/pin/request/PinTableRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.pin.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class PinTableRequest {\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located\n     */\n    private String schemaName;\n    /**\n     * Pin table name\n     */\n    private String tableName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/DatabaseController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport ai.chat2db.server.domain.api.enums.TaskStatusEnum;\nimport ai.chat2db.server.domain.api.param.MetaDataQueryParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseCreateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseQueryAllParam;\nimport ai.chat2db.server.domain.api.service.DatabaseService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO;\nimport ai.chat2db.server.web.api.controller.rdb.converter.DatabaseConverter;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.data.service.DatabaseDataService;\nimport ai.chat2db.server.web.api.controller.rdb.data.task.TaskManager;\nimport ai.chat2db.server.web.api.controller.rdb.data.task.TaskState;\nimport ai.chat2db.server.web.api.controller.rdb.request.DatabaseCreateRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.DatabaseExportDataRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.DatabaseExportRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.UpdateDatabaseRequest;\nimport ai.chat2db.server.web.api.controller.rdb.vo.MetaSchemaVO;\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.MetaSchema;\nimport ai.chat2db.spi.model.Sql;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.io.PrintWriter;\nimport java.util.Objects;\n\n/**\n * database controller\n */\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/database\")\n@RestController\n@Slf4j\npublic class DatabaseController {\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    @Autowired\n    private DatabaseService databaseService;\n\n    @Autowired\n    public DatabaseConverter databaseConverter;\n    @Autowired\n    private DatabaseDataService databaseDataService;\n\n    /**\n     * Query the database_schema_list contained in the database\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/database_schema_list\")\n    public DataResult<MetaSchemaVO> databaseSchemaList(@Valid DataSourceBaseRequest request) {\n        MetaDataQueryParam queryParam = MetaDataQueryParam.builder().dataSourceId(request.getDataSourceId())\n                .refresh(\n                        request.isRefresh()).build();\n        DataResult<MetaSchema> result = databaseService.queryDatabaseSchema(queryParam);\n        MetaSchemaVO schemaDto2vo = rdbWebConverter.metaSchemaDto2vo(result.getData());\n        return DataResult.of(schemaDto2vo);\n    }\n\n    @GetMapping(\"list\")\n    public ListResult<DatabaseVO> databaseList(@Valid DataSourceBaseRequest request) {\n        DatabaseQueryAllParam queryParam = DatabaseQueryAllParam.builder().dataSourceId(request.getDataSourceId())\n                .refresh(\n                        request.isRefresh()).build();\n        ListResult<Database> result = databaseService.queryAll(queryParam);\n        return ListResult.of(rdbWebConverter.databaseDto2vo(result.getData()));\n    }\n\n    /**\n     * Delete database\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/delete_database\")\n    public ActionResult deleteDatabase(@Valid @RequestBody DataSourceBaseRequest request) {\n        DatabaseCreateParam param = DatabaseCreateParam.builder().name(request.getDatabaseName()).build();\n        return databaseService.deleteDatabase(param);\n    }\n\n    /**\n     * create database\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/create_database_sql\")\n    public DataResult<Sql> createDatabase(@Valid @RequestBody DatabaseCreateRequest request) {\n        if (StringUtils.isBlank(request.getName())) {\n            request.setName(request.getDatabaseName());\n        }\n        Database database = databaseConverter.request2param(request);\n        return databaseService.createDatabase(database);\n    }\n\n    /**\n     * Modify database\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/modify_database\")\n    public ActionResult modifyDatabase(@Valid @RequestBody UpdateDatabaseRequest request) {\n        DatabaseCreateParam param = DatabaseCreateParam.builder().name(request.getDatabaseName())\n                .name(request.getNewDatabaseName()).build();\n        return databaseService.modifyDatabase(param);\n    }\n\n    @PostMapping(\"/export\")\n    public void exportDatabase(@Valid @RequestBody DatabaseExportRequest request, HttpServletResponse response) {\n        String fileName = Objects.isNull(request.getSchemaName()) ? request.getDatabaseName() : request.getSchemaName();\n        response.setContentType(\"text/sql\");\n        response.setHeader(\"Content-disposition\", \"attachment;filename*=utf-8''\" + fileName + \".sql\");\n        response.setCharacterEncoding(\"utf-8\");\n        DatabaseExportParam param = databaseConverter.request2param(request);\n        try (PrintWriter printWriter = response.getWriter()) {\n            String sql = databaseService.exportDatabase(param);\n            printWriter.println(sql);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @PostMapping(\"/export_data\")\n    public DataResult<Long> exportData(@Valid @RequestBody DatabaseExportDataRequest request) {\n        DatabaseExportDataParam databaseExportDataParam = databaseConverter.request2param(request);\n        return databaseDataService.doExportAsync(databaseExportDataParam);\n    }\n\n    @GetMapping(\"/export_data_status/{taskId}\")\n    public DataResult<String> exportDataStatus(@PathVariable(\"taskId\") Long taskId) {\n        TaskState task = TaskManager.getTask(taskId);\n        String state = task.getState();\n        if (Objects.equals(state, TaskStatusEnum.FINISH.name()) || Objects.equals(state, TaskStatusEnum.ERROR.name())) {\n            TaskManager.removeTask(taskId);\n        }\n        return DataResult.of(task.getExportStatus());\n    }\n\n    /**\n     * Query the database_user_list contained in the database\n     *\n     * @return username list\n     */\n    @GetMapping(\"/database_username_list\")\n    public ListResult<String> databaseUsernameList(@Valid DataSourceBaseRequest dataSourceBaseRequest) {\n        return databaseService.getUsernameList();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/FunctionController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport ai.chat2db.server.domain.api.service.FunctionService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.rdb.converter.FunctionConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.FunctionDetailRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.FunctionPageRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.FunctionUpdateRequest;\nimport ai.chat2db.spi.model.Function;\nimport jakarta.validation.Valid;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/function\")\n@RestController\npublic class FunctionController {\n\n    @Autowired\n    private FunctionService functionService;\n\n    @Autowired\n    private FunctionConverter functionConverter;\n\n    @GetMapping(\"/list\")\n    public WebPageResult<Function> list(@Valid FunctionPageRequest request) {\n        ListResult<Function> functionListResult = functionService.functions(request.getDatabaseName(),\n                request.getSchemaName());\n        return WebPageResult.of(functionListResult.getData(), Long.valueOf(functionListResult.getData().size()), 1,\n                functionListResult.getData().size());\n    }\n\n    @GetMapping(\"/detail\")\n    public DataResult<Function> detail(@Valid FunctionDetailRequest request) {\n        return functionService.detail(request.getDatabaseName(), request.getSchemaName(), request.getFunctionName());\n    }\n\n    @PostMapping(\"/delete\")\n    public ActionResult delete(@Valid FunctionUpdateRequest request) {\n        Function function = functionConverter.request2param(request);\n        return functionService.delete(request.getDatabaseName(), request.getSchemaName(), function);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/ProcedureController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport ai.chat2db.server.domain.api.service.ProcedureService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.rdb.converter.ProcedureConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.ProcedureDetailRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.ProcedurePageRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.ProcedureUpdateRequest;\nimport ai.chat2db.spi.model.Procedure;\nimport jakarta.validation.Valid;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.sql.SQLException;\n\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/procedure\")\n@RestController\npublic class ProcedureController {\n\n    @Autowired\n    private ProcedureService procedureService;\n\n    @Autowired\n    private ProcedureConverter procedureConverter;\n    @GetMapping(\"/list\")\n    public WebPageResult<Procedure> list(@Valid ProcedurePageRequest request) {\n        ListResult<Procedure> procedureListResult = procedureService.procedures(request.getDatabaseName(),\n            request.getSchemaName());\n        return WebPageResult.of(procedureListResult.getData(), Long.valueOf(procedureListResult.getData().size()), 1,\n            procedureListResult.getData().size());\n    }\n\n    @GetMapping(\"/detail\")\n    public DataResult<Procedure> detail(@Valid ProcedureDetailRequest request) {\n        return procedureService.detail(request.getDatabaseName(), request.getSchemaName(), request.getProcedureName());\n    }\n\n    @PostMapping(\"/update\")\n    public ActionResult update(@Valid ProcedureUpdateRequest request) throws SQLException {\n        Procedure procedure = procedureConverter.request2param(request);\n        return procedureService.update(request.getDatabaseName(), request.getSchemaName(), procedure);\n    }\n\n    @PostMapping(\"/delete\")\n    public ActionResult delete(@Valid ProcedureUpdateRequest request) {\n        Procedure procedure = procedureConverter.request2param(request);\n        return procedureService.delete(request.getDatabaseName(), request.getSchemaName(), procedure);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/RdbDdlController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseCreateParam;\nimport ai.chat2db.server.domain.api.service.DatabaseService;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.EmbeddingController;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.*;\nimport ai.chat2db.server.web.api.controller.rdb.vo.*;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport com.google.common.collect.Lists;\nimport jakarta.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * mysql table operation and maintenance class\n *\n * @author moji\n * @version MysqlTableManageController.java, v 0.1 September 16, 2022 17:41 moji Exp $\n * @date 2022/09/16\n */\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/ddl\")\n@RestController\n@Slf4j\n@Deprecated\npublic class RdbDdlController extends EmbeddingController {\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private DlTemplateService dlTemplateService;\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    @Autowired\n    private DatabaseService databaseService;\n\n    public static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();\n\n    /**\n     * Query the table list under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public WebPageResult<TableVO> list(@Valid TableBriefQueryRequest request) {\n        TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);\n        TableSelector tableSelector = new TableSelector();\n        tableSelector.setColumnList(false);\n        tableSelector.setIndexList(false);\n\n        PageResult<Table> tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);\n        List<TableVO> tableVOS = rdbWebConverter.tableDto2vo(tableDTOPageResult.getData());\n\n//        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n//        singleThreadExecutor.submit(() -> {\n//            try {\n//                Chat2DBContext.putContext(connectInfo);\n//                syncTableVector(request);\n////                syncTableEs(request);\n//            } catch (Exception e) {\n//                log.error(\"sync table vector error\", e);\n//            } finally {\n//                Chat2DBContext.removeContext();\n//            }\n//            log.info(\"sync table vector finish\");\n//        });\n        return WebPageResult.of(tableVOS, tableDTOPageResult.getTotal(), request.getPageNo(),\n            request.getPageSize());\n    }\n\n    /**\n     * Query the schema_list contained in the database\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/schema_list\")\n    public ListResult<SchemaVO> schemaList(@Valid DataSourceBaseRequest request) {\n        SchemaQueryParam queryParam = SchemaQueryParam.builder().dataBaseName(request.getDatabaseName()).build();\n        ListResult<Schema> tableColumns = databaseService.querySchema(queryParam);\n        List<SchemaVO> tableVOS = rdbWebConverter.schemaDto2vo(tableColumns.getData());\n        return ListResult.of(tableVOS);\n    }\n\n    /**\n     * Query the database_schema_list contained in the database\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/database_schema_list\")\n    public DataResult<MetaSchemaVO> databaseSchemaList(@Valid DataSourceBaseRequest request) {\n        MetaDataQueryParam queryParam = MetaDataQueryParam.builder().dataSourceId(request.getDataSourceId()).refresh(\n            request.isRefresh()).build();\n        DataResult<MetaSchema> result = databaseService.queryDatabaseSchema(queryParam);\n        MetaSchemaVO schemaDto2vo = rdbWebConverter.metaSchemaDto2vo(result.getData());\n        return DataResult.of(schemaDto2vo);\n    }\n\n\n    /**\n     * Query the table columns under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/column_list\")\n    public ListResult<ColumnVO> columnList(@Valid TableDetailQueryRequest request) {\n        TableQueryParam queryParam = rdbWebConverter.tableRequest2param(request);\n        List<TableColumn> tableColumns = tableService.queryColumns(queryParam);\n        List<ColumnVO> tableVOS = rdbWebConverter.columnDto2vo(tableColumns);\n        return ListResult.of(tableVOS);\n    }\n\n    /**\n     * Query the table index under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/index_list\")\n    public ListResult<IndexVO> indexList(@Valid TableDetailQueryRequest request) {\n        TableQueryParam queryParam = rdbWebConverter.tableRequest2param(request);\n        List<TableIndex> tableIndices = tableService.queryIndexes(queryParam);\n        List<IndexVO> indexVOS = rdbWebConverter.indexDto2vo(tableIndices);\n        return ListResult.of(indexVOS);\n    }\n\n    /**\n     * Query the table key under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/key_list\")\n    public ListResult<IndexVO> keyList(@Valid TableDetailQueryRequest request) {\n        // TODO Add query key implementation\n        return ListResult.of(Lists.newArrayList());\n    }\n\n    /**\n     * Export table creation statement\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/export\")\n    public DataResult<String> export(@Valid DdlExportRequest request) {\n        ShowCreateTableParam param = rdbWebConverter.ddlExport2showTableCreate(request);\n        return tableService.showCreateTable(param);\n    }\n\n    /**\n     * Table creation statement example\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/create/example\")\n    public DataResult<String> createExample(@Valid TableCreateDdlQueryRequest request) {\n        return tableService.createTableExample(request.getDbType());\n    }\n\n    /**\n     * Update table statement example\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/update/example\")\n    public DataResult<String> updateExample(@Valid TableUpdateDdlQueryRequest request) {\n        return tableService.alterTableExample(request.getDbType());\n    }\n\n    /**\n     * Get information such as table columns and indexes\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/query\")\n    public DataResult<TableVO> query(@Valid TableDetailQueryRequest request) {\n        TableQueryParam queryParam = rdbWebConverter.tableRequest2param(request);\n        TableSelector tableSelector = new TableSelector();\n        tableSelector.setColumnList(true);\n        tableSelector.setIndexList(true);\n        DataResult<Table> tableDTODataResult = tableService.query(queryParam, tableSelector);\n        TableVO tableVO = rdbWebConverter.tableDto2vo(tableDTODataResult.getData());\n        return DataResult.of(tableVO);\n    }\n\n    /**\n     * Get the sql statement that modifies the table\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/modify/sql\")\n    public ListResult<SqlVO> modifySql(@Valid TableModifySqlRequest request) {\n        return tableService.buildSql(\n                rdbWebConverter.tableRequest2param(request.getOldTable()),\n                rdbWebConverter.tableRequest2param(request.getNewTable()))\n            .map(rdbWebConverter::dto2vo);\n    }\n\n    /**\n     * Delete table\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/delete\")\n    public ActionResult delete(@Valid @RequestBody TableDeleteRequest request) {\n        DropParam dropParam = rdbWebConverter.tableDelete2dropParam(request);\n        return tableService.drop(dropParam);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/RdbDmlController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport java.sql.Connection;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.DlExecuteParam;\nimport ai.chat2db.server.domain.api.param.GroupByParam;\nimport ai.chat2db.server.domain.api.param.OrderByParam;\nimport ai.chat2db.server.domain.api.param.UpdateSelectResultParam;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.tools.base.enums.DataSourceTypeEnum;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.*;\nimport ai.chat2db.server.web.api.controller.rdb.vo.ExecuteResultVO;\nimport ai.chat2db.server.web.api.http.GatewayClientService;\nimport ai.chat2db.server.web.api.http.request.SqlExecuteHistoryCreateRequest;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport com.google.common.collect.Lists;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * mysql data operation and maintenance class\n *\n * @author moji\n * @version MysqlDataManageController.java, v 0.1 September 16, 2022 17:37 moji Exp $\n * @date 2022/09/16\n */\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/dml\")\n@RestController\npublic class RdbDmlController {\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    @Autowired\n    private DlTemplateService dlTemplateService;\n\n    @Autowired\n    private GatewayClientService gatewayClientService;\n\n    public static ExecutorService executorService = Executors.newFixedThreadPool(10);\n\n    /**\n     * Data operation and maintenance such as addition, deletion, modification and query\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/execute\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public ListResult<ExecuteResultVO> manage(@RequestBody DmlRequest request) {\n        DlExecuteParam param = rdbWebConverter.request2param(request);\n        ListResult<ExecuteResult> resultDTOListResult = dlTemplateService.execute(param);\n        List<ExecuteResultVO> resultVOS = rdbWebConverter.dto2vo(resultDTOListResult.getData());\n        return ListResult.of(resultVOS);\n    }\n\n\n    /**\n     * query chat2db apikey\n     *\n     * @return\n     */\n    private String getClientId() {\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();\n        if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {\n            return ConfigUtils.getClientId();\n        }\n        return keyConfig.getContent();\n    }\n\n    /**\n     * Query table structure information\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/execute_table\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public ListResult<ExecuteResultVO> executeTable(@RequestBody DmlTableRequest request) {\n        DlExecuteParam param = rdbWebConverter.request2param(request);\n        return dlTemplateService.executeSelectTable(param)\n                .map(rdbWebConverter::dto2vo);\n    }\n\n    /**\n     * update search result\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/execute_update\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public DataResult<ExecuteResultVO> executeSelectResultUpdate(@RequestBody DmlRequest request) {\n        DlExecuteParam param = rdbWebConverter.request2param(request);\n        DataResult<ExecuteResult> result = dlTemplateService.executeUpdate(param);\n        if (!result.success()) {\n            return DataResult.error(result.getErrorCode(), result.getErrorMessage());\n        }\n        ExecuteResultVO executeResultVO = rdbWebConverter.dto2vo(result.getData());\n        return DataResult.of(executeResultVO);\n\n    }\n\n    @RequestMapping(value = \"/get_update_sql\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public DataResult<String> getUpdateSelectResultSql(@RequestBody SelectResultUpdateRequest request) {\n        UpdateSelectResultParam param = rdbWebConverter.request2param(request);\n        return dlTemplateService.updateSelectResult(param);\n    }\n\n\n    @RequestMapping(value = \"/get_group_by_sql\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public DataResult<String> getGroupBySql(@RequestBody GroupByRequest request) {\n\n        GroupByParam param = rdbWebConverter.request2param(request);\n\n        return dlTemplateService.getGroupBySql(param);\n    }\n\n    @RequestMapping(value = \"/get_order_by_sql\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public DataResult<String> getOrderBySql(@RequestBody OrderByRequest request) {\n\n        OrderByParam param = rdbWebConverter.request2param(request);\n\n        return dlTemplateService.getOrderBySql(param);\n    }\n\n    /**\n     * Data operation and maintenance such as addition, deletion, modification and query\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/execute_ddl\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public DataResult<ExecuteResultVO> executeDDL(@RequestBody DmlRequest request) {\n        DlExecuteParam param = rdbWebConverter.request2param(request);\n        Connection connection = Chat2DBContext.getConnection();\n        if (connection != null) {\n            try {\n                boolean flag = true;\n                ExecuteResultVO executeResult = null;\n                //connection.setAutoCommit(false);\n                ListResult<ExecuteResult> resultDTOListResult = dlTemplateService.execute(param);\n                List<ExecuteResultVO> resultVOS = rdbWebConverter.dto2vo(resultDTOListResult.getData());\n                if (!CollectionUtils.isEmpty(resultVOS)) {\n                    for (ExecuteResultVO resultVO : resultVOS) {\n                        if (!resultVO.getSuccess()) {\n                            flag = false;\n                            executeResult = resultVO;\n                            break;\n\n                        }\n                    }\n                }\n                if (flag) {\n                    //connection.commit();\n                    return DataResult.of(resultVOS.get(0));\n                } else {\n                    //connection.rollback();\n                    return DataResult.of(executeResult);\n                }\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n\n        } else {\n            return DataResult.error(\"connection error\", \"\");\n        }\n    }\n\n    /**\n     * Number of statistics rows\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/count\", method = {RequestMethod.POST, RequestMethod.PUT})\n    public DataResult<Long> count(@RequestBody DdlCountRequest request) {\n        return dlTemplateService.count(rdbWebConverter.request2param(request));\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/RdbDmlExportController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.ValueProcessor;\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.SQLUtils.FormatOption;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.visitor.VisitorFeature;\nimport com.alibaba.excel.EasyExcel;\nimport com.alibaba.excel.ExcelWriter;\nimport com.alibaba.excel.support.ExcelTypeEnum;\nimport com.alibaba.excel.write.builder.ExcelWriterBuilder;\nimport com.alibaba.excel.write.metadata.WriteSheet;\n\nimport ai.chat2db.server.domain.api.enums.ExportSizeEnum;\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport ai.chat2db.server.tools.common.util.EasyEnumUtils;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.JdbcUtils;\nimport ai.chat2db.spi.util.SqlUtils;\nimport cn.hutool.core.date.DatePattern;\nimport com.google.common.collect.Lists;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n/**\n * Export Database Exclusive\n *\n * @author Jiaju Zhuang\n */\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/dml\")\n@Controller\n@Slf4j\npublic class RdbDmlExportController {\n\n    /**\n     * Format insert statement\n     */\n    private static final FormatOption INSERT_FORMAT_OPTION = new FormatOption(true, false);\n\n    static {\n        INSERT_FORMAT_OPTION.config(VisitorFeature.OutputNameQuote, true);\n    }\n\n    /**\n     * export data\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/export\")\n    public void export(@Valid @RequestBody DataExportRequest request, HttpServletResponse response) throws IOException {\n        ExportSizeEnum exportSize = EasyEnumUtils.getEnum(ExportSizeEnum.class, request.getExportSize());\n        ExportTypeEnum exportType = EasyEnumUtils.getEnum(ExportTypeEnum.class, request.getExportType());\n        String sql;\n        if (exportSize == ExportSizeEnum.CURRENT_PAGE) {\n            sql = request.getOriginalSql() + \" LIMIT \" + request.getPageSize() + \" OFFSET \" + (request.getPageNo() - 1) * request.getPageSize();\n        } else {\n            sql = request.getOriginalSql();\n        }\n        if (StringUtils.isBlank(sql)) {\n            throw new ParamBusinessException(\"exportSize\");\n        }\n        DbType dbType = JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());\n        String tableName;\n        if (dbType != null) {\n            SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);\n            if (!(sqlStatement instanceof SQLSelectStatement)) {\n                throw new BusinessException(\"dataSource.sqlAnalysisError\");\n            }\n            tableName = SqlUtils.getTableName(sql, dbType);\n        } else {\n            tableName = StringUtils.join(Lists.newArrayList(request.getDatabaseName(), request.getSchemaName()), \"_\");\n        }\n\n        response.setCharacterEncoding(\"utf-8\");\n        String fileName = URLEncoder.encode(\n                        tableName + \"_\" + LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER),\n                        StandardCharsets.UTF_8)\n                .replaceAll(\"\\\\+\", \"%20\");\n\n        if (exportType == ExportTypeEnum.CSV) {\n            doExportCsv(sql, response, fileName);\n        } else {\n            doExportInsert(sql, response, fileName, dbType, tableName);\n        }\n    }\n\n    private void doExportCsv(String sql, HttpServletResponse response, String fileName)\n            throws IOException {\n        response.setContentType(\"text/csv\");\n        response.setHeader(\"Content-disposition\", \"attachment;filename*=utf-8''\" + fileName + \".csv\");\n\n        ExcelWrapper excelWrapper = new ExcelWrapper();\n        try {\n            ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n            ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream())\n                    .charset(StandardCharsets.UTF_8)\n                    .excelType(ExcelTypeEnum.CSV);\n            excelWrapper.setExcelWriterBuilder(excelWriterBuilder);\n            SQLExecutor.getInstance().execute(Chat2DBContext.getConnection(), sql, headerList -> {\n                excelWriterBuilder.head(\n                        EasyCollectionUtils.toList(headerList, header -> Lists.newArrayList(header.getName())));\n                excelWrapper.setExcelWriter(excelWriterBuilder.build());\n                excelWrapper.setWriteSheet(EasyExcel.writerSheet(0).build());\n            }, dataList -> {\n                List<List<String>> writeDataList = Lists.newArrayList();\n                writeDataList.add(dataList);\n                excelWrapper.getExcelWriter().write(writeDataList, excelWrapper.getWriteSheet());\n            }, jdbcDataValue -> valueProcessor.getJdbcValue(jdbcDataValue), false);\n        } finally {\n            if (excelWrapper.getExcelWriter() != null) {\n                excelWrapper.getExcelWriter().finish();\n            }\n        }\n    }\n\n    private void doExportInsert(String sql, HttpServletResponse response, String fileName, DbType dbType,\n                                String tableName)\n            throws IOException {\n        response.setContentType(\"text/sql\");\n        response.setHeader(\"Content-disposition\", \"attachment;filename*=utf-8''\" + fileName + \".sql\");\n        ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n        SqlBuilder sqlBuilder = Chat2DBContext.getMetaData().getSqlBuilder();\n        try (PrintWriter printWriter = response.getWriter()) {\n            List<String> headerColumns = Lists.newArrayList();\n            List<SQLIdentifierExpr> headers = new ArrayList<>();\n            InsertWrapper insertWrapper = new InsertWrapper();\n            SQLExecutor.getInstance().execute(Chat2DBContext.getConnection(), sql,\n                    headerList -> {\n                        headerList.forEach(sqlIdentifierExpr -> headerColumns.add(sqlIdentifierExpr.getName()));\n                    }\n                    , dataList -> {\n                        for (String header : headerColumns) {\n                            SQLIdentifierExpr expr = new SQLIdentifierExpr(header);\n                            expr.setName(header);\n                            headers.add(expr);\n                        }\n                        insertWrapper.setHeaderList(headers);\n\n                        SQLInsertStatement sqlInsertStatement = new SQLInsertStatement();\n                        sqlInsertStatement.setDbType(dbType);\n                        sqlInsertStatement.setTableSource(new SQLExprTableSource(tableName));\n                        sqlInsertStatement.getColumns().addAll(insertWrapper.getHeaderList());\n                        ValuesClause valuesClause = new ValuesClause();\n                        for (String s : dataList) {\n                            valuesClause.addValue(s);\n                        }\n                        sqlInsertStatement.setValues(valuesClause);\n                        String sqls = sqlBuilder.buildSingleInsertSql(null, null, tableName, headerColumns, dataList);\n                        printWriter.println(sqls + \";\");\n                    }, jdbcDataValue -> valueProcessor.getJdbcSqlValueString(jdbcDataValue), false);\n        }\n    }\n\n    @Data\n    @SuperBuilder\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class InsertWrapper {\n        private List<SQLIdentifierExpr> headerList;\n    }\n\n    @Data\n    @SuperBuilder\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class ExcelWrapper {\n        private ExcelWriterBuilder excelWriterBuilder;\n        private ExcelWriter excelWriter;\n        private WriteSheet writeSheet;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/RdbDocController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.domain.api.param.TablePageQueryParam;\nimport ai.chat2db.server.domain.api.param.TableQueryParam;\nimport ai.chat2db.server.domain.api.param.TableSelector;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.util.EasyEnumUtils;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;\nimport ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;\nimport ai.chat2db.server.web.api.controller.rdb.doc.event.TemplateEvent;\nimport ai.chat2db.server.web.api.controller.rdb.factory.ExportServiceFactory;\nimport ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;\nimport ai.chat2db.server.web.api.controller.rdb.vo.TableVO;\nimport ai.chat2db.spi.model.Table;\nimport cn.hutool.core.date.DatePattern;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\nimport java.lang.reflect.Constructor;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n/**\n * RdbDocController\n *\n * @author lzy\n **/\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/doc\")\n@Controller\n@Slf4j\npublic class RdbDocController {\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n\n    /**\n     * export data\n     *\n     * @param request\n     */\n    @PostMapping(\"/export\")\n    public void export(@Valid @RequestBody DataExportRequest request, HttpServletResponse response) throws Exception {\n        //Copy template\n        ExportTypeEnum exportType = EasyEnumUtils.getEnum(ExportTypeEnum.class, request.getExportType());\n        response.setCharacterEncoding(\"utf-8\");\n        String fileName = URLEncoder.encode(\n                        request.getDatabaseName() + \"_\" + LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER),\n                        StandardCharsets.UTF_8)\n                .replaceAll(\"\\\\+\", \"%20\");\n        TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);\n        queryParam.setPageNo(1);\n        queryParam.setPageSize(Integer.MAX_VALUE);\n        TableSelector tableSelector = new TableSelector();\n        tableSelector.setColumnList(true);\n        tableSelector.setIndexList(true);\n        PageResult<Table> tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);\n        List<TableVO> tableVOS = rdbWebConverter.tableDto2vo(tableDTOPageResult.getData());\n        TableQueryParam param = rdbWebConverter.tableRequest2param(request);\n        for (TableVO tableVO: tableVOS) {\n            param.setTableName(tableVO.getName());\n            tableVO.setColumnList(tableService.queryColumns(param));\n            tableVO.setIndexList(tableService.queryIndexes(param));\n        }\n        Class<?> targetClass = ExportServiceFactory.get(exportType.getCode());\n        Constructor<?> constructor = targetClass.getDeclaredConstructor();\n        DatabaseExportService databaseExportService = (DatabaseExportService) constructor.newInstance();\n        response.setHeader(\"Content-disposition\", \"attachment;filename*=utf-8''\" + fileName + databaseExportService.getSuffix());\n        response.setContentType(databaseExportService.getContentType());\n        // Set up data collection\n        databaseExportService.setExportList(tableVOS);\n        databaseExportService.generate(request.getDatabaseName(), response.getOutputStream(), new ExportOptions());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/SchemaController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.param.SchemaOperationParam;\nimport ai.chat2db.server.domain.api.param.SchemaQueryParam;\nimport ai.chat2db.server.domain.api.service.DatabaseService;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.SchemaCreateRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.UpdateSchemaRequest;\nimport ai.chat2db.server.web.api.controller.rdb.vo.SchemaVO;\nimport ai.chat2db.spi.model.Schema;\nimport ai.chat2db.spi.model.Sql;\nimport jakarta.validation.Valid;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * shema controller\n */\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/schema\")\n@RestController\npublic class SchemaController {\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private DlTemplateService dlTemplateService;\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    @Autowired\n    private DatabaseService databaseService;\n\n    /**\n     * Query the schema_list contained in the database\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public ListResult<SchemaVO> list(@Valid DataSourceBaseRequest request) {\n        SchemaQueryParam queryParam = SchemaQueryParam.builder().dataSourceId(request.getDataSourceId()).dataBaseName(\n                request.getDatabaseName()).refresh(request.isRefresh()).build();\n        ListResult<Schema> tableColumns = databaseService.querySchema(queryParam);\n        List<SchemaVO> tableVOS = rdbWebConverter.schemaDto2vo(tableColumns.getData());\n        return ListResult.of(tableVOS);\n    }\n\n    /**\n     * Delete schema\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/delete_schema\")\n    public ActionResult deleteSchema(@Valid @RequestBody DataSourceBaseRequest request) {\n        SchemaOperationParam param = SchemaOperationParam.builder().databaseName(request.getDatabaseName())\n                .schemaName(request.getSchemaName()).build();\n        return databaseService.deleteSchema(param);\n    }\n\n    /**\n     * Create schema\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/create_schema_sql\")\n    public DataResult<Sql> createSchema(@Valid @RequestBody SchemaCreateRequest request) {\n        Schema schema = Schema.builder().databaseName(request.getDatabaseName())\n                .name(request.getSchemaName())\n                .owner(request.getOwner())\n                .comment(request.getComment())\n                .build();\n        return databaseService.createSchema(schema);\n    }\n\n    /**\n     * Modify schema\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/modify_schema\")\n    public ActionResult modifySchema(@Valid @RequestBody UpdateSchemaRequest request) {\n        SchemaOperationParam param = SchemaOperationParam.builder().databaseName(request.getDatabaseName())\n                .schemaName(request.getSchemaName()).newSchemaName(request.getNewSchemaName()).build();\n        return databaseService.modifySchema(param);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/SequenceController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\n\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.server.domain.api.service.SequenceService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.*;\nimport ai.chat2db.server.web.api.controller.rdb.vo.SequenceVO;\nimport ai.chat2db.server.web.api.controller.rdb.vo.SqlVO;\nimport ai.chat2db.spi.model.*;\nimport jakarta.validation.Valid;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.bind.annotation.*;\n\n/**\n * sequence controller\n *\n * @author Sylphy\n */\n@Slf4j\n@RestController\n@ConnectionInfoAspect\n@RequiredArgsConstructor\n@RequestMapping(\"/api/rdb/sequence\")\npublic class SequenceController {\n    private final RdbWebConverter rdbWebConverter;\n    private final SequenceService sequenceService;\n\n    /**\n     * Query the sequence list under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public ListResult<SimpleSequence> list(@Valid SequenceBriefQueryRequest request) {\n        SequencePageQueryParam queryParam = rdbWebConverter.sequencePageRequest2param(request);\n        return sequenceService.pageQuery(queryParam);\n    }\n\n    /**\n     * Export sequence creation statement\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/export\")\n    public DataResult<String> export(@Valid DdlExportRequest request) {\n        ShowCreateSequenceParam param = rdbWebConverter.ddlExport2showSequenceCreate(request);\n        return sequenceService.showCreateSequence(param);\n    }\n\n    /**\n     * Get a SQL statement that modifies or creates a new sequence\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/modify/sql\")\n    public ListResult<SqlVO> modifySql(@Valid @RequestBody SequenceModifySqlRequest request) {\n        Sequence sequence = rdbWebConverter.sequenceRequest2param(request.getNewSequence());\n        return sequenceService.buildSql(rdbWebConverter.sequenceRequest2param(request.getOldSequence()), sequence)\n                .map(rdbWebConverter::dto2vo);\n    }\n\n    /**\n     * Delete sequence\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/delete\")\n    public ActionResult delete(@Valid @RequestBody SequenceDeleteRequest request){\n        DropParam dropParam = rdbWebConverter.sequenceDelete2dropParam(request);\n        return sequenceService.drop(dropParam);\n    }\n\n    /**\n     * Get information such as table columns and indexes\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/query\")\n    public DataResult<SequenceVO> query(@Valid SequenceDetailQueryRequest request) {\n        SequenceQueryParam queryParam = rdbWebConverter.sequenceRequest2param(request);\n        DataResult<Sequence> sequenceDTODataResult = sequenceService.query(queryParam);\n        SequenceVO sequenceVO = rdbWebConverter.sequenceDto2vo(sequenceDTODataResult.getData());\n        return DataResult.of(sequenceVO);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.server.domain.api.service.DatabaseService;\nimport ai.chat2db.server.domain.api.service.DlTemplateService;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.ai.EmbeddingController;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.*;\nimport ai.chat2db.server.web.api.controller.rdb.vo.ColumnVO;\nimport ai.chat2db.server.web.api.controller.rdb.vo.IndexVO;\nimport ai.chat2db.server.web.api.controller.rdb.vo.SqlVO;\nimport ai.chat2db.server.web.api.controller.rdb.vo.TableVO;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport com.google.common.collect.Lists;\nimport jakarta.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n@Slf4j\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/table\")\n@RestController\npublic class TableController extends EmbeddingController {\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private DlTemplateService dlTemplateService;\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    @Autowired\n    private DatabaseService databaseService;\n\n    public static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();\n\n    /**\n     * Query the table list under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public WebPageResult<TableVO> list(@Valid TableBriefQueryRequest request) {\n        TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);\n        TableSelector tableSelector = new TableSelector();\n        tableSelector.setColumnList(false);\n        tableSelector.setIndexList(false);\n        PageResult<Table> tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);\n        List<TableVO> tableVOS = rdbWebConverter.tableDto2vo(tableDTOPageResult.getData());\n//        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();\n//        singleThreadExecutor.submit(() -> {\n//            try {\n//                Chat2DBContext.putContext(connectInfo);\n//                syncTableVector(request);\n////                syncTableEs(request);\n//            } catch (Exception e) {\n//                log.error(\"sync table vector error\", e);\n//            } finally {\n//                Chat2DBContext.removeContext();\n//            }\n//            log.info(\"sync table vector finish\");\n//        });\n        return WebPageResult.of(tableVOS, tableDTOPageResult.getTotal(), request.getPageNo(),\n                request.getPageSize());\n    }\n\n    /**\n     * Query the table list under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/table_list\")\n    public ListResult<SimpleTable> tableList(@Valid TableBriefQueryRequest request) {\n        TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);\n        return tableService.queryTables(queryParam);\n\n    }\n\n\n\n\n\n    /**\n     * Query the table columns under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/column_list\")\n    public ListResult<ColumnVO> columnList(@Valid TableDetailQueryRequest request) {\n        TableQueryParam queryParam = rdbWebConverter.tableRequest2param(request);\n        List<TableColumn> tableColumns = tableService.queryColumns(queryParam);\n        List<ColumnVO> tableVOS = rdbWebConverter.columnDto2vo(tableColumns);\n        return ListResult.of(tableVOS);\n    }\n\n    @GetMapping(\"/copy_dml_sql\")\n    public DataResult<String> copyDmlSql(@Valid DmlSqlCopyRequest request) {\n        DmlSqlCopyParam queryParam = rdbWebConverter.dmlRequest2param(request);\n        return tableService.copyDmlSql(queryParam);\n    }\n\n    /**\n     * Query the table index under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/index_list\")\n    public ListResult<IndexVO> indexList(@Valid TableDetailQueryRequest request) {\n        TableQueryParam queryParam = rdbWebConverter.tableRequest2param(request);\n        List<TableIndex> tableIndices = tableService.queryIndexes(queryParam);\n        List<IndexVO> indexVOS = rdbWebConverter.indexDto2vo(tableIndices);\n        return ListResult.of(indexVOS);\n    }\n\n    /**\n     * Query the table key under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/key_list\")\n    public ListResult<IndexVO> keyList(@Valid TableDetailQueryRequest request) {\n        // TODO Add query key implementation\n        return ListResult.of(Lists.newArrayList());\n    }\n\n    /**\n     * Export table creation statement\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/export\")\n    public DataResult<String> export(@Valid DdlExportRequest request) {\n        ShowCreateTableParam param = rdbWebConverter.ddlExport2showTableCreate(request);\n        return tableService.showCreateTable(param);\n    }\n\n    /**\n     * Table creation statement example\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/create/example\")\n    public DataResult<String> createExample(@Valid TableCreateDdlQueryRequest request) {\n        return tableService.createTableExample(request.getDbType());\n    }\n\n    /**\n     * Update table statement example\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/update/example\")\n    public DataResult<String> updateExample(@Valid TableUpdateDdlQueryRequest request) {\n        return tableService.alterTableExample(request.getDbType());\n    }\n\n    /**\n     * Get information such as table columns and indexes\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/query\")\n    public DataResult<Table> query(@Valid TableDetailQueryRequest request) {\n        TableQueryParam queryParam = rdbWebConverter.tableRequest2param(request);\n        TableSelector tableSelector = new TableSelector();\n        tableSelector.setColumnList(true);\n        tableSelector.setIndexList(true);\n        return tableService.query(queryParam, tableSelector);\n        //TableVO tableVO = rdbWebConverter.tableDto2vo(tableDTODataResult.getData());\n        //return DataResult.of(tableVO);\n    }\n\n    /**\n     * Get the sql statement that modifies the table\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/modify/sql\")\n    public ListResult<SqlVO> modifySql(@Valid @RequestBody TableModifySqlRequest request) {\n        Table table =  rdbWebConverter.tableRequest2param(request.getNewTable());\n        table.setSchemaName(request.getSchemaName());\n        table.setDatabaseName(request.getDatabaseName());\n        for (TableColumn tableColumn : table.getColumnList()) {\n            tableColumn.setSchemaName(request.getSchemaName());\n            tableColumn.setTableName(table.getName());\n            tableColumn.setDatabaseName(request.getDatabaseName());\n        }\n        for (TableIndex tableIndex : table.getIndexList()) {\n            tableIndex.setSchemaName(request.getSchemaName());\n            tableIndex.setTableName(table.getName());\n            tableIndex.setDatabaseName(request.getDatabaseName());\n        }\n\n        return tableService.buildSql(rdbWebConverter.tableRequest2param(request.getOldTable()),table)\n                .map(rdbWebConverter::dto2vo);\n    }\n\n\n\n    /**\n     * Data types supported by the database\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/type_list\")\n    public ListResult<Type> types(@Valid TypeQueryRequest request) {\n        TypeQueryParam typeQueryParam = TypeQueryParam.builder().dataSourceId(request.getDataSourceId()).build();\n        List<Type> types = tableService.queryTypes(typeQueryParam);\n        return ListResult.of(types);\n    }\n\n\n    @GetMapping(\"/table_meta\")\n    public DataResult<TableMeta> tableMeta(@Valid TypeQueryRequest request) {\n        TypeQueryParam typeQueryParam = TypeQueryParam.builder().dataSourceId(request.getDataSourceId()).build();\n        TableMeta tableMeta = tableService.queryTableMeta(typeQueryParam);\n        return DataResult.of(tableMeta);\n    }\n\n    /**\n     * Delete table\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/delete\")\n    public ActionResult delete(@Valid @RequestBody TableDeleteRequest request) {\n        DropParam dropParam = rdbWebConverter.tableDelete2dropParam(request);\n        return tableService.drop(dropParam);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TriggerController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport ai.chat2db.server.domain.api.service.TriggerService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.rdb.request.TriggerDetailRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.TriggerPageRequest;\nimport ai.chat2db.spi.model.Trigger;\nimport jakarta.validation.Valid;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/trigger\")\n@RestController\npublic class TriggerController {\n\n    @Autowired\n    private TriggerService triggerService;\n\n    @GetMapping(\"/list\")\n    public WebPageResult<Trigger> list(@Valid TriggerPageRequest request) {\n        ListResult<Trigger> listResult = triggerService.triggers(request.getDatabaseName(), request.getSchemaName());\n        Long total = CollectionUtils.isNotEmpty(listResult.getData()) ? Long.valueOf(listResult.getData().size()) : 0L;\n        Integer pageSize = listResult.getData() != null ? listResult.getData().size() : 0;\n        return WebPageResult.of(listResult.getData(), total, 1, pageSize);\n    }\n\n    @GetMapping(\"/detail\")\n    public DataResult<Trigger> detail(@Valid TriggerDetailRequest request) {\n        return triggerService.detail(request.getDatabaseName(), request.getSchemaName(), request.getTriggerName());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/ViewController.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.param.DropParam;\nimport ai.chat2db.server.domain.api.param.TableQueryParam;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.domain.api.service.ViewService;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.TableBriefQueryRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.TableDeleteRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.TableDetailQueryRequest;\nimport ai.chat2db.server.web.api.controller.rdb.vo.ColumnVO;\nimport ai.chat2db.server.web.api.controller.rdb.vo.TableVO;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport jakarta.validation.Valid;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@ConnectionInfoAspect\n@RequestMapping(\"/api/rdb/view\")\n@RestController\npublic class ViewController {\n    @Autowired\n    private ViewService viewService;\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    @GetMapping(\"/list\")\n    public WebPageResult<TableVO> list(@Valid TableBriefQueryRequest request) {\n        ListResult<Table> tableDTOPageResult = viewService.views(request.getDatabaseName(), request.getSchemaName());\n        List<TableVO> tableVOS = rdbWebConverter.tableDto2vo(tableDTOPageResult.getData());\n        Integer pageSize = tableDTOPageResult.getData() != null ? tableDTOPageResult.getData().size() : 0;\n        return WebPageResult.of(tableVOS, Long.valueOf(tableVOS.size()), 1, pageSize);\n    }\n\n\n    @GetMapping(\"/column_list\")\n    public ListResult<ColumnVO> columnList(@Valid TableDetailQueryRequest request) {\n        TableQueryParam queryParam = rdbWebConverter.tableRequest2param(request);\n        List<TableColumn> tableColumns = tableService.queryColumns(queryParam);\n        List<ColumnVO> tableVOS = rdbWebConverter.columnDto2vo(tableColumns);\n        return ListResult.of(tableVOS);\n    }\n\n\n    @GetMapping(\"/detail\")\n    public DataResult<TableVO> detail(@Valid TableDetailQueryRequest request) {\n        DataResult<Table> dataResult = viewService.detail(request.getDatabaseName(),request.getSchemaName(),request.getTableName());\n        TableVO tableVO = rdbWebConverter.tableDto2vo(dataResult.getData());\n        return DataResult.of(tableVO);\n    }\n    @PostMapping(\"/delete\")\n    public ActionResult delete(@Valid TableDeleteRequest request) {\n        DropParam dropParam = rdbWebConverter.tableDelete2dropParam(request);\n       return tableService.drop(dropParam);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/DatabaseConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.converter;\n\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportParam;\nimport ai.chat2db.server.web.api.controller.rdb.request.DatabaseCreateRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.DatabaseExportDataRequest;\nimport ai.chat2db.server.web.api.controller.rdb.request.DatabaseExportRequest;\nimport ai.chat2db.spi.model.Database;\nimport org.mapstruct.Mapper;\n\n@Mapper(componentModel = \"spring\")\npublic abstract class DatabaseConverter {\n\n    public abstract Database request2param(DatabaseCreateRequest request);\n\n    public abstract DatabaseExportParam request2param(DatabaseExportRequest request);\n\n    public abstract DatabaseExportDataParam request2param(DatabaseExportDataRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/FunctionConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.converter;\n\nimport ai.chat2db.server.web.api.controller.rdb.request.FunctionUpdateRequest;\nimport ai.chat2db.spi.model.Function;\nimport org.mapstruct.Mapper;\n\n/**\n * @author Juechen\n * @version : FunctionConverter.java\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class FunctionConverter {\n    public abstract Function request2param(FunctionUpdateRequest request);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/ProcedureConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.converter;\n\nimport ai.chat2db.server.web.api.controller.rdb.request.ProcedureUpdateRequest;\nimport ai.chat2db.spi.model.Procedure;\nimport org.mapstruct.Mapper;\n\n/**\n * @author: zgq\n * @date: February 24, 2024 13:39\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class ProcedureConverter {\n\n    public abstract Procedure request2param(ProcedureUpdateRequest request);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO;\nimport ai.chat2db.server.web.api.controller.rdb.request.*;\nimport ai.chat2db.server.web.api.controller.rdb.vo.*;\nimport ai.chat2db.server.web.api.http.request.EsTableSchemaRequest;\nimport ai.chat2db.spi.model.*;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\n\n/**\n * @author moji\n * @version MysqlDataConverter.java, v 0.1 October 14, 2022 14:04 moji Exp $\n * @date 2022/10/14\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class RdbWebConverter {\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract DlExecuteParam request2param(DmlRequest request);\n\n\n\n    public abstract GroupByParam request2param(GroupByRequest request);\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract OrderByParam request2param(OrderByRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract DlExecuteParam request2param(DmlTableRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract DlExecuteParam tableManageRequest2param(DdlRequest request);\n\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract DlCountParam request2param(DdlCountRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract TableQueryParam tableRequest2param(TableDetailQueryRequest request);\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract SequenceQueryParam sequenceRequest2param(SequenceDetailQueryRequest request);\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract Table tableRequest2param(TableRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract Sequence sequenceRequest2param(SequenceRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract SqlVO dto2vo(Sql dto);\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract TablePageQueryParam tablePageRequest2param(TableBriefQueryRequest request);\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract TablePageQueryParam tablePageRequest2param(DataExportRequest request);\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract SequencePageQueryParam sequencePageRequest2param(SequenceBriefQueryRequest request);\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    public abstract TableQueryParam tableRequest2param(DataExportRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mapping(source = \"name\", target = \"tableName\")\n    public abstract ShowCreateTableParam ddlExport2showTableCreate(DdlExportRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mapping(source = \"name\", target = \"sequenceName\")\n    public abstract ShowCreateSequenceParam ddlExport2showSequenceCreate(DdlExportRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n            @Mapping(source = \"tableName\", target = \"name\"),\n            @Mapping(source = \"schemaName\", target = \"schema\")\n    })\n    public abstract DropParam tableDelete2dropParam(TableDeleteRequest request);\n\n    /**\n     * Parameter conversion\n     *\n     * @param request\n     * @return\n     */\n    @Mappings({\n            @Mapping(source = \"sequenceName\", target = \"name\"),\n            @Mapping(source = \"schemaName\", target = \"schema\")\n    })\n    public abstract DropParam sequenceDelete2dropParam(SequenceDeleteRequest request);\n\n\n    /**\n     * Model conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract ExecuteResultVO dto2vo(ExecuteResult dto);\n\n    /**\n     * Model conversion\n     *\n     * @param dtos\n     * @return\n     */\n    public abstract List<ExecuteResultVO> dto2vo(List<ExecuteResult> dtos);\n\n    /**\n     * Model conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract ColumnVO columnDto2vo(TableColumn dto);\n\n    /**\n     * Model conversion\n     *\n     * @param dtos\n     * @return\n     */\n    public abstract List<ColumnVO> columnDto2vo(List<TableColumn> dtos);\n\n    /**\n     * Model conversion\n     *\n     * @param dto\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"columnList\", target = \"columnList\")\n    })\n    public abstract IndexVO indexDto2vo(TableIndex dto);\n\n    /**\n     * Model conversion\n     *\n     * @param dtos\n     * @return\n     */\n    public abstract List<IndexVO> indexDto2vo(List<TableIndex> dtos);\n\n    /**\n     * Model conversion\n     *\n     * @param dto\n     * @return\n     */\n    @Mappings({\n        @Mapping(source = \"columnList\", target = \"columnList\"),\n        @Mapping(source = \"indexList\", target = \"indexList\"),\n    })\n    public abstract TableVO tableDto2vo(Table dto);\n\n    /**\n     * Model conversion\n     *\n     * @param dto\n     * @return\n     */\n    public abstract SequenceVO sequenceDto2vo(Sequence dto);\n\n    /**\n     * Model conversion\n     *\n     * @param dtos\n     * @return\n     */\n    public abstract List<TableVO> tableDto2vo(List<Table> dtos);\n    /**\n     * Model conversion\n     * @param tableColumns\n     * @return\n     */\n    public abstract List<SchemaVO> schemaDto2vo(List<Schema> tableColumns);\n\n    /**\n     * Model conversion\n     * @param dto\n     * @return\n     */\n    public abstract SchemaVO schemaDto2vo(Schema dto);\n\n    /**\n     * Model conversion\n     * @param dto\n     * @return\n     */\n    public abstract DatabaseVO databaseDto2vo(Database dto);\n\n\n    /**\n     * Model conversion\n     * @param dto\n     * @return\n     */\n    public abstract List<DatabaseVO> databaseDto2vo(List<Database> dto);\n\n    public abstract MetaSchemaVO metaSchemaDto2vo(MetaSchema data);\n\n\n    public abstract UpdateSelectResultParam request2param(SelectResultUpdateRequest request);\n\n    public abstract TableMilvusQueryRequest request2request(TableBriefQueryRequest request);\n\n    @Mappings({\n            @Mapping(source = \"databaseName\", target = \"database\"),\n            @Mapping(source = \"schemaName\", target = \"schema\"),\n    })\n    public abstract TableVectorParam param2param(TableBriefQueryRequest request);\n\n    public abstract EsTableSchemaRequest req2req(TableBriefQueryRequest request);\n\n    public abstract TablePageQueryParam schemaReq2page(EsTableSchemaRequest request);\n\n    public abstract DmlSqlCopyParam dmlRequest2param(DmlSqlCopyRequest request) ;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/BaseDataExporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data;\n\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\nimport ai.chat2db.server.web.api.util.StringUtils;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.io.*;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:51\n */\n@Slf4j\npublic abstract class BaseDataExporter implements DataExportStrategy {\n\n    protected String contentType;\n    protected String suffix;\n\n    public static int BATCH_SIZE = 1000;\n\n    @Override\n    public void doExport(DatabaseExportDataParam databaseExportDataParam, File file) throws IOException, SQLException {\n        List<String> tableNames = databaseExportDataParam.getTableNames();\n        if (CollectionUtils.isEmpty(tableNames)) {\n            throw new IllegalArgumentException(\"tableNames should not be null or empty\");\n        }\n        try (Connection connection = Chat2DBContext.getConnection()) {\n            if (tableNames.size() == 1) {\n                String tableName = tableNames.get(0);\n                if (StringUtils.isEmpty(tableName)) {\n                    throw new IllegalArgumentException(\"tableName should not be null or empty\");\n                }\n                singleExport(connection, databaseExportDataParam,file);\n            } else {\n                multiExport(databaseExportDataParam, connection, file);\n            }\n        }\n\n    }\n\n\n    private void multiExport(DatabaseExportDataParam databaseExportDataParam,\n                             Connection connection, File file) throws IOException {\n        try (OutputStream outputStream = new FileOutputStream(file);\n             ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {\n            List<String> tableNames = databaseExportDataParam.getTableNames();\n            for (String tableName : tableNames) {\n                String fileName = tableName + suffix;\n                zipOutputStream.putNextEntry(new ZipEntry(fileName));\n                try (ByteArrayOutputStream byteArrayOutputStream = multiExport(connection, databaseExportDataParam, tableName)) {\n                    byteArrayOutputStream.writeTo(zipOutputStream);\n                    zipOutputStream.closeEntry();\n                }\n            }\n        }\n    }\n\n\n    protected abstract void singleExport(Connection connectionInfo, DatabaseExportDataParam databaseExportDataParam, File file) throws IOException, SQLException;\n\n\n    protected abstract ByteArrayOutputStream multiExport(Connection connection, DatabaseExportDataParam databaseExportDataParam, String tableName);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/BaseDataImporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:52\n */\npublic abstract class BaseDataImporter implements DataImportStrategy{\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/BaseExcelExporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data;\n\nimport ai.chat2db.server.domain.api.enums.TaskStatusEnum;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\nimport ai.chat2db.server.web.api.controller.rdb.data.task.TaskManager;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.ResultSetUtils;\nimport com.alibaba.excel.EasyExcel;\nimport com.alibaba.excel.support.ExcelTypeEnum;\nimport com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.*;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:56\n */\n@Slf4j\npublic abstract class BaseExcelExporter extends BaseDataExporter {\n    @Override\n    protected void singleExport(Connection connection, DatabaseExportDataParam exportParam, File outputFile) {\n        ExcelTypeEnum excelType = getExcelType();\n        try (OutputStream outputStream = new FileOutputStream(outputFile)) {\n\n\n            String tableName = exportParam.getTableNames().get(0);\n            String querySql = getQuerySql(exportParam, tableName);\n\n            log.info(\"开始导出：{}表数据，导出类型：{}\", tableName, excelType);\n\n            SQLExecutor.getInstance().execute(connection, querySql, BATCH_SIZE, resultSet ->\n                    writeExcelData(resultSet, excelType, outputStream, tableName, exportParam.getContainsHeader()));\n\n        } catch (IOException e) {\n            TaskManager.updateStatus(TaskStatusEnum.ERROR);\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    protected ByteArrayOutputStream multiExport(Connection connection, DatabaseExportDataParam databaseExportDataParam, String tableName) {\n        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n        ExcelTypeEnum excelType = getExcelType();\n\n        log.info(\"开始导出：{}表数据，导出类型：{}\", tableName, excelType);\n\n        String querySql = getQuerySql(databaseExportDataParam, tableName);\n        SQLExecutor.getInstance().execute(connection, querySql, BATCH_SIZE, resultSet -> {\n            writeExcelData(resultSet, excelType, byteArrayOutputStream, tableName, databaseExportDataParam.getContainsHeader());\n        });\n        return byteArrayOutputStream;\n    }\n\n    private void writeExcelData(ResultSet resultSet, ExcelTypeEnum excelType, OutputStream outputStream, String sheetName, Boolean containsHeader) {\n        try {\n            ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.write(outputStream).excelType(excelType).sheet(sheetName);\n            ResultSetMetaData metaData = resultSet.getMetaData();\n            int columnCount = metaData.getColumnCount();\n            ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n            List<List<Object>> dataList = new ArrayList<>();\n\n            if (containsHeader) {\n                List<String> header = ResultSetUtils.getRsHeader(resultSet);\n                excelWriterSheetBuilder.head(header.stream().map(Collections::singletonList).collect(Collectors.toList()));\n            }\n\n            while (resultSet.next()) {\n                List<Object> rowDataList = new ArrayList<>();\n                for (int i = 1; i <= columnCount; i++) {\n                    JDBCDataValue jdbcDataValue = new JDBCDataValue(resultSet, metaData, i, false);\n                    rowDataList.add(valueProcessor.getJdbcValue(jdbcDataValue));\n                }\n                dataList.add(rowDataList);\n            }\n\n            excelWriterSheetBuilder.doWrite(dataList);\n            TaskManager.increaseCurrent();\n        } catch (SQLException e) {\n            TaskManager.updateStatus(TaskStatusEnum.ERROR);\n            log.error(\"Error writing Excel data\", e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    private String getQuerySql(DatabaseExportDataParam databaseExportDataParam, String tableName) {\n        String databaseName = databaseExportDataParam.getDatabaseName();\n        String schemaName = databaseExportDataParam.getSchemaName();\n        return Chat2DBContext.getSqlBuilder().buildTableQuerySql(databaseName, schemaName, tableName);\n    }\n\n    protected abstract ExcelTypeEnum getExcelType();\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/BaseExcelImporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data;\n\n/**\n * 功能描述\n *\n * @author: zgq\n * @date: 2024年06月04日 10:57\n */\npublic abstract class BaseExcelImporter extends BaseDataImporter{\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/DataExportStrategy.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data;\n\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.sql.SQLException;\n\npublic interface DataExportStrategy {\n\n\n    void doExport(DatabaseExportDataParam databaseExportDataParam, File file) throws IOException, SQLException;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/DataImportStrategy.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data;\n\npublic interface DataImportStrategy {\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/csv/CsvDataExporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.csv;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseExcelExporter;\nimport com.alibaba.excel.support.ExcelTypeEnum;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:05\n */\n@Component(\"csvExporter\")\npublic class CsvDataExporter extends BaseExcelExporter {\n\n\n    public CsvDataExporter() {\n        this.contentType = \"text/csv\";\n        this.suffix = ExportFileSuffix.CSV.getSuffix();\n    }\n\n\n    @Override\n    protected ExcelTypeEnum getExcelType() {\n        return ExcelTypeEnum.CSV;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/csv/CsvDataImporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.csv;\n\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseExcelImporter;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:04\n */\n@Component(\"csvImporter\")\npublic class CsvDataImporter extends BaseExcelImporter {\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/factory/DataExportFactory.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.factory;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.rdb.data.DataExportStrategy;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:26\n */\n@Component\npublic class DataExportFactory {\n\n    public static final String BEAN_SUFFIX = \"Exporter\";\n    private final Map<String, DataExportStrategy> exports;\n\n    @Autowired\n    public DataExportFactory(Map<String, DataExportStrategy> exports) {\n        this.exports = exports;\n    }\n\n    public DataExportStrategy getExporter(String type) {\n        DataExportStrategy dataExportStrategy = exports.get(type.toLowerCase() + BEAN_SUFFIX);\n        if (Objects.isNull(dataExportStrategy)) {\n            throw new ParamBusinessException(type);\n        }\n        return dataExportStrategy;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/factory/DataImportFactory.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.factory;\n\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.web.api.controller.rdb.data.DataImportStrategy;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:07\n */\n@Component\npublic class DataImportFactory {\n\n\n    private static final String BEAN_SUFFIX = \"Importer\";\n    private final Map<String, DataImportStrategy> imports;\n\n    @Autowired\n    public DataImportFactory(Map<String, DataImportStrategy> imports) {\n        this.imports = imports;\n    }\n\n    public DataImportStrategy getImporter(String type) {\n        DataImportStrategy dataImportStrategy = imports.get(type.toLowerCase() + BEAN_SUFFIX);\n        if (Objects.isNull(dataImportStrategy)) {\n            throw new ParamBusinessException(type);\n        }\n        return dataImportStrategy;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/json/JsonDataExporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.json;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseDataExporter;\nimport ai.chat2db.server.web.api.controller.rdb.data.task.TaskManager;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.SerializationFeature;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\nimport java.io.*;\nimport java.nio.charset.StandardCharsets;\nimport java.sql.Connection;\nimport java.sql.ResultSetMetaData;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:33\n */\n@Component(\"jsonExporter\")\n@Slf4j\npublic class JsonDataExporter extends BaseDataExporter {\n\n    public JsonDataExporter() {\n        this.suffix = ExportFileSuffix.JSON.getSuffix();\n        this.contentType = \"application/json\";\n    }\n\n\n    @Override\n    protected void singleExport(Connection connection, DatabaseExportDataParam databaseExportDataParam, File file) {\n        String tableName = databaseExportDataParam.getTableNames().get(0);\n        String querySql = getQuerySql(databaseExportDataParam, tableName);\n        log.info(\"开始导出：{}表数据，导出类型：json\", tableName);\n        try (PrintWriter writer = new PrintWriter(file, StandardCharsets.UTF_8);) {\n            writeJsonData(connection, querySql, writer);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    protected ByteArrayOutputStream multiExport(Connection connection, DatabaseExportDataParam databaseExportDataParam, String tableName) {\n        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n        log.info(\"开始导出：{}表数据，导出类型：json\", tableName);\n        try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, StandardCharsets.UTF_8))) {\n            String querySql = getQuerySql(databaseExportDataParam, tableName);\n            writeJsonData(connection, querySql, writer);\n        }\n        return byteArrayOutputStream;\n    }\n\n    private void writeJsonData(Connection connection, String querySql, PrintWriter writer) {\n        SQLExecutor.getInstance().execute(connection, querySql, BATCH_SIZE, resultSet -> {\n            List<Map<String, Object>> dataBatch = new ArrayList<>();\n            ResultSetMetaData metaData = resultSet.getMetaData();\n            ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n            ObjectMapper objectMapper = new ObjectMapper();\n            objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);\n\n            writer.println(\"[\");\n            boolean firstBatch = true;\n            while (resultSet.next()) {\n                Map<String, Object> row = new LinkedHashMap<>();\n                for (int i = 1; i <= metaData.getColumnCount(); i++) {\n                    row.put(metaData.getColumnName(i), valueProcessor.getJdbcValue(new JDBCDataValue(resultSet, metaData, i, false)));\n                }\n                dataBatch.add(row);\n\n                if (dataBatch.size() >= BATCH_SIZE || resultSet.isLast()) {\n                    if (!firstBatch) {\n                        writer.println(\",\");\n                    }\n                    writeBatch(writer, objectMapper, dataBatch);\n                    firstBatch = false;\n                }\n            }\n            writer.println(\"]\");\n        });\n        TaskManager.increaseCurrent();\n    }\n\n    private void writeBatch(PrintWriter writer, ObjectMapper objectMapper, List<Map<String, Object>> dataBatch) {\n        try {\n            String jsonBatch = objectMapper.writeValueAsString(dataBatch);\n            writer.println(jsonBatch.substring(1, jsonBatch.length() - 1));\n            writer.flush();\n            dataBatch.clear();\n        } catch (JsonProcessingException e) {\n            throw new BusinessException(\"data.export.json.error\", null, e);\n        }\n    }\n\n    private String getQuerySql(DatabaseExportDataParam databaseExportDataParam, String tableName) {\n        String databaseName = databaseExportDataParam.getDatabaseName();\n        String schemaName = databaseExportDataParam.getSchemaName();\n        return Chat2DBContext.getSqlBuilder().buildTableQuerySql(databaseName, schemaName, tableName);\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/json/JsonDataImporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.json;\n\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseDataImporter;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:33\n */\n@Component(\"jsonImporter\")\npublic class JsonDataImporter extends BaseDataImporter {\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/service/DatabaseDataService.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.service;\n\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\n\n/**\n * @author: zgq\n * @date: 2024年06月08日 10:32\n */\npublic interface DatabaseDataService {\n\n    DataResult<Long> doExportAsync(DatabaseExportDataParam databaseExportDataParam);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/service/impl/DatabaseDataImpl.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.service.impl;\n\nimport ai.chat2db.server.domain.api.enums.TaskStatusEnum;\nimport ai.chat2db.server.domain.api.enums.TaskTypeEnum;\nimport ai.chat2db.server.domain.api.param.TaskCreateParam;\nimport ai.chat2db.server.domain.api.param.TaskUpdateParam;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\nimport ai.chat2db.server.domain.api.service.TaskService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.controller.rdb.data.factory.DataExportFactory;\nimport ai.chat2db.server.web.api.controller.rdb.data.factory.DataImportFactory;\nimport ai.chat2db.server.web.api.controller.rdb.data.service.DatabaseDataService;\nimport ai.chat2db.server.web.api.controller.rdb.data.task.TaskManager;\nimport ai.chat2db.server.web.api.controller.rdb.data.task.TaskState;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.io.FileUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.sql.SQLException;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.concurrent.CompletableFuture;\n\n/**\n * @author: zgq\n * @date: 2024年06月08日 10:32\n */\n@Service\n@Slf4j\npublic class DatabaseDataImpl implements DatabaseDataService {\n\n    public static final String EXPORT_DATA_TASK_TEMPLATE = \"export_%s_data\";\n    public static final String IMPORT_DATA_TASK_TEMPLATE = \"import_%s_data\";\n    @Autowired\n    private DataExportFactory dataExportFactory;\n    @Autowired\n    private DataImportFactory dataImportFactory;\n    @Autowired\n    private TaskService taskService;\n\n    @Override\n    public DataResult<Long> doExportAsync(DatabaseExportDataParam databaseExportDataParam) {\n        List<String> tableNames = databaseExportDataParam.getTableNames();\n        String databaseName = databaseExportDataParam.getDatabaseName();\n        String schemaName = databaseExportDataParam.getSchemaName();\n        Long dataSourceId = databaseExportDataParam.getDataSourceId();\n        String taskName = buildTaskName(tableNames, databaseName, schemaName);\n        String fileName = URLEncoder.encode(\n                taskName + \"_\" + LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER),\n                StandardCharsets.UTF_8);\n        String suffix = \".\";\n        int size = tableNames.size();\n        if (size > 1) {\n            suffix += \"zip\";\n        } else {\n            suffix += databaseExportDataParam.getExportType().toLowerCase();\n        }\n        File file = FileUtil.createTempFile(fileName, suffix, true);\n        file.deleteOnExit();\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo().copy();\n        DataResult<Long> dataResult = createTask(tableNames.get(0), databaseName, schemaName, dataSourceId, taskName);\n        Long taskId = dataResult.getData();\n        CompletableFuture.runAsync(() -> {\n            buildContext(loginUser, connectInfo);\n            TaskManager.addTask(taskId, TaskState.builder().state(TaskStatusEnum.PROCESSING.name()).total(size)\n                    .current(0).build());\n            try {\n                dataExportFactory.getExporter(databaseExportDataParam.getExportType()).doExport(databaseExportDataParam, file);\n            } catch (IOException | SQLException e) {\n                throw new RuntimeException(e);\n            }\n        }).whenComplete((v, ex) -> {\n            updateStatus(taskId, file, ex);\n            removeContext();\n            TaskManager.removeTaskId();\n        });\n        return dataResult;\n\n    }\n\n    private void updateStatus(Long id, File file, Throwable throwable) {\n        TaskUpdateParam updateParam = new TaskUpdateParam();\n        updateParam.setId(id);\n        updateParam.setTaskProgress(\"1\");\n        updateParam.setDownloadUrl(file.getAbsolutePath());\n        if (throwable != null) {\n            log.error(\"export error\", throwable);\n            updateParam.setTaskStatus(TaskStatusEnum.ERROR.name());\n        } else {\n            updateParam.setTaskStatus(TaskStatusEnum.FINISH.name());\n        }\n        taskService.updateStatus(updateParam);\n    }\n\n    private void removeContext() {\n        Dbutils.removeSession();\n        ContextUtils.removeContext();\n        Chat2DBContext.removeContext();\n    }\n\n    private DataResult<Long> createTask(String tableName, String databaseName, String schemaName, Long datasourceId, String taskName) {\n        TaskCreateParam param = new TaskCreateParam();\n        param.setTaskName(taskName);\n        param.setTaskType(TaskTypeEnum.DOWNLOAD_TABLE_DATA.name());\n        param.setDatabaseName(databaseName);\n        param.setSchemaName(schemaName);\n        param.setTableName(tableName);\n        param.setDataSourceId(datasourceId);\n        param.setUserId(ContextUtils.getUserId());\n        param.setTaskProgress(\"0.1\");\n        return taskService.create(param);\n    }\n\n    private void buildContext(LoginUser loginUser, ConnectInfo connectInfo) {\n        ContextUtils.setContext(Context.builder()\n                                        .loginUser(loginUser)\n                                        .build());\n        Dbutils.setSession();\n        Chat2DBContext.putContext(connectInfo);\n    }\n\n    private String buildTaskName(List<String> tableNames, String databaseName, String schemaName) {\n        StringBuilder taskNameBuilder = new StringBuilder();\n        if (StringUtils.isNotBlank(databaseName)) {\n            taskNameBuilder.append(databaseName).append(\"_\");\n        }\n        if (StringUtils.isNotBlank(schemaName)) {\n            taskNameBuilder.append(schemaName).append(\"_\");\n        }\n        if (tableNames.size() == 1) {\n            taskNameBuilder.append(StringUtils.join(tableNames, \"_\"));\n        }\n        return String.format(EXPORT_DATA_TASK_TEMPLATE, taskNameBuilder);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/sql/SqlDataExporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.sql;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam;\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseDataExporter;\nimport ai.chat2db.server.web.api.controller.rdb.data.task.TaskManager;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.ResultSetUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.springframework.stereotype.Component;\n\nimport java.io.*;\nimport java.nio.charset.StandardCharsets;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:33\n */\n@Component(\"sqlExporter\")\n@Slf4j\npublic class SqlDataExporter extends BaseDataExporter {\n\n    public SqlDataExporter() {\n        this.suffix = ExportFileSuffix.SQL.getSuffix();\n        this.contentType = \"text/sql\";\n    }\n\n    /**\n     * @param connection\n     * @param databaseExportDataParam\n     * @param file\n     */\n    @Override\n    protected void singleExport(Connection connection, DatabaseExportDataParam databaseExportDataParam, File file) {\n        String tableName = databaseExportDataParam.getTableNames().get(0);\n        log.info(\"开始导出：{}表数据，导出类型：sql\", tableName);\n        try (PrintWriter writer = new PrintWriter(file);) {\n            exportSql(connection, databaseExportDataParam, tableName, writer);\n        } catch (FileNotFoundException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    protected ByteArrayOutputStream multiExport(Connection connection, DatabaseExportDataParam databaseExportDataParam, String tableName) {\n        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n        log.info(\"开始导出：{}表数据，导出类型：sql\", tableName);\n        try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, StandardCharsets.UTF_8))) {\n            exportSql(connection, databaseExportDataParam, tableName, writer);\n        }\n        return byteArrayOutputStream;\n    }\n\n    private void exportSql(Connection connection, DatabaseExportDataParam databaseExportDataParam, String tableName, PrintWriter writer) {\n        String databaseName = databaseExportDataParam.getDatabaseName();\n        String schemaName = databaseExportDataParam.getSchemaName();\n        Boolean containsHeader = databaseExportDataParam.getContainsHeader();\n        MetaData metaData = Chat2DBContext.getMetaData();\n        String querySql = metaData.getSqlBuilder().buildTableQuerySql(databaseName, schemaName, tableName);\n        SqlBuilder sqlBuilder = metaData.getSqlBuilder();\n        ValueProcessor valueProcessor = metaData.getValueProcessor();\n        String sqyType = databaseExportDataParam.getSqyType();\n\n        switch (sqyType) {\n            case \"single\" -> exportSingleInsert(connection, querySql, containsHeader, sqlBuilder,\n                                                valueProcessor, databaseName, schemaName, tableName, writer);\n            case \"multi\" -> exportMultiInsert(connection, querySql, containsHeader, sqlBuilder,\n                                              valueProcessor, databaseName, schemaName, tableName, writer);\n            case \"update\" -> exportUpdate(connection, querySql, sqlBuilder, valueProcessor,\n                                          databaseName, schemaName, tableName, writer);\n            default -> throw new IllegalArgumentException(\"Unsupported sqyType: \" + sqyType);\n        }\n    }\n\n    private void exportSingleInsert(Connection connection, String querySql, Boolean containsHeader,\n                                    SqlBuilder sqlBuilder, ValueProcessor valueProcessor,\n                                    String databaseName, String schemaName, String tableName, PrintWriter writer) {\n        List<String> sqlList = new ArrayList<>(BATCH_SIZE);\n        SQLExecutor.getInstance().execute(connection, querySql, BATCH_SIZE, resultSet -> {\n            List<String> header = containsHeader ? ResultSetUtils.getRsHeader(resultSet) : null;\n            while (resultSet.next()) {\n                List<String> rowData = extractRowData(resultSet, valueProcessor);\n                String sql = sqlBuilder.buildSingleInsertSql(databaseName, schemaName, tableName, header, rowData);\n                sqlList.add(sql+\";\");\n                if (sqlList.size() >= BATCH_SIZE) {\n                    writeSqlList(writer, sqlList);\n                }\n            }\n            if(CollectionUtils.isNotEmpty(sqlList)){\n                writeSqlList(writer, sqlList);\n            }\n        });\n        TaskManager.increaseCurrent();\n    }\n\n    private void exportMultiInsert(Connection connection, String querySql, Boolean containsHeader,\n                                   SqlBuilder sqlBuilder, ValueProcessor valueProcessor,\n                                   String databaseName, String schemaName, String tableName, PrintWriter writer) {\n        SQLExecutor.getInstance().execute(connection, querySql, BATCH_SIZE, resultSet -> {\n            List<List<String>> dataList = new ArrayList<>(BATCH_SIZE);\n            List<String> header = containsHeader ? ResultSetUtils.getRsHeader(resultSet) : null;\n            while (resultSet.next()) {\n                dataList.add(extractRowData(resultSet, valueProcessor));\n            }\n            String sql = sqlBuilder.buildMultiInsertSql(databaseName, schemaName, tableName, header, dataList);\n            writer.println(sql+\";\");\n            writer.flush();\n        });\n        TaskManager.increaseCurrent();\n    }\n\n    private void exportUpdate(Connection connection, String querySql, SqlBuilder sqlBuilder,\n                              ValueProcessor valueProcessor,\n                              String databaseName, String schemaName, String tableName, PrintWriter writer) {\n        List<String> sqlList = new ArrayList<>(BATCH_SIZE);\n        SQLExecutor.getInstance().execute(connection, querySql, BATCH_SIZE, resultSet -> {\n            Map<String, String> primaryKeyMap = getPrimaryKeyMap(connection, databaseName, schemaName, tableName);\n            while (resultSet.next()) {\n                Map<String, String> row = extractRowDataAsMap(resultSet, valueProcessor, primaryKeyMap);\n                String sql = sqlBuilder.buildUpdateSql(databaseName, schemaName, tableName, row, primaryKeyMap);\n                sqlList.add(sql);\n                if (sqlList.size() >= BATCH_SIZE || resultSet.isLast()) {\n                    writeSqlList(writer, sqlList);\n                }\n            }\n        });\n        TaskManager.increaseCurrent();\n    }\n\n    private List<String> extractRowData(ResultSet resultSet, ValueProcessor valueProcessor) throws SQLException {\n        ResultSetMetaData metaData = resultSet.getMetaData();\n        List<String> rowData = new ArrayList<>(metaData.getColumnCount());\n        for (int i = 1; i <= metaData.getColumnCount(); i++) {\n            JDBCDataValue jdbcDataValue = new JDBCDataValue(resultSet, metaData, i, false);\n            rowData.add(valueProcessor.getJdbcSqlValueString(jdbcDataValue));\n        }\n        return rowData;\n    }\n\n    private Map<String, String> extractRowDataAsMap(ResultSet resultSet, ValueProcessor valueProcessor,\n                                                    Map<String, String> primaryKeyMap) throws SQLException {\n        ResultSetMetaData metaData = resultSet.getMetaData();\n        Map<String, String> row = new HashMap<>(metaData.getColumnCount());\n        for (int i = 1; i <= metaData.getColumnCount(); i++) {\n            JDBCDataValue jdbcDataValue = new JDBCDataValue(resultSet, metaData, i, false);\n            String columnName = metaData.getColumnName(i);\n            String jdbcValueString = valueProcessor.getJdbcSqlValueString(jdbcDataValue);\n            if (primaryKeyMap.containsKey(columnName)) {\n                primaryKeyMap.put(columnName, jdbcValueString);\n            } else {\n                row.put(columnName, jdbcValueString);\n            }\n        }\n        return row;\n    }\n\n    private Map<String, String> getPrimaryKeyMap(Connection connection, String databaseName,\n                                                 String schemaName, String tableName) throws SQLException {\n        Map<String, String> primaryKeyMap = new HashMap<>();\n        try (ResultSet primaryKeys = connection.getMetaData().getPrimaryKeys(databaseName, schemaName, tableName)) {\n            while (primaryKeys.next()) {\n                primaryKeyMap.put(primaryKeys.getString(\"COLUMN_NAME\"), \"\");\n            }\n        }\n        return primaryKeyMap;\n    }\n\n    private void writeSqlList(PrintWriter writer, List<String> sqlList) {\n        sqlList.forEach(writer::println);\n        sqlList.clear();\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/task/TaskManager.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.task;\n\nimport ai.chat2db.server.domain.api.enums.TaskStatusEnum;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\n\n\npublic class TaskManager {\n    public static final ThreadLocal<Long> TASK_ID = new ThreadLocal<>();\n    public static final Map<Long, TaskState> taskMap = new ConcurrentHashMap<>();\n\n\n    public static void increaseCurrent(int current) {\n        TaskState task = getTask();\n        task.setCurrent(task.getCurrent() + current);\n        if (task.getCurrent() >= task.getTotal()) {\n            task.setState(TaskStatusEnum.FINISH.name());\n        }\n    }\n\n    public static void increaseCurrent() {\n        TaskState task = getTask();\n        task.setCurrent(task.getCurrent() +1);\n        if (task.getCurrent() >= task.getTotal()) {\n            task.setState(TaskStatusEnum.FINISH.name());\n        }\n    }\n\n    public static void updateStatus(TaskStatusEnum status) {\n        TaskState task = getTask();\n        task.setState(status.name());\n    }\n\n\n    public static void addTask(Long taskId, TaskState taskState) {\n        setTaskId(taskId);\n        taskMap.put(taskId, taskState);\n    }\n\n    public static TaskState getTask(Long taskId) {\n        TaskState taskState = taskMap.get(taskId);\n        if (Objects.isNull(taskState)) {\n            throw new IllegalArgumentException(\"taskId is not valid\");\n        }\n        return taskState;\n    }\n\n    public static TaskState getTask() {\n        return getTask(getTaskId());\n    }\n\n    public static void removeTask(Long taskId) {\n        taskMap.remove(taskId);\n    }\n\n    public static void setTaskId(Long taskId) {\n        TASK_ID.set(taskId);\n    }\n\n    public static Long getTaskId() {\n        return TASK_ID.get();\n    }\n\n    public static void removeTaskId() {\n        TASK_ID.remove();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/task/TaskState.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.task;\n\nimport lombok.Builder;\nimport lombok.Data;\n\n/**\n * @author: zgq\n * @date: 2024年06月10日 15:51\n */\n@Data\n@Builder\npublic class TaskState {\n    private String taskId;\n    private String state;\n    private int total;\n    private int current;\n\n\n    public String getExportStatus() {\n        StringBuilder statusBuilder = new StringBuilder();\n        statusBuilder.append(\"导出状态: \").append(state)\n                .append(\" 导出进度: \")\n                .append(current).append(\"/\")\n                .append(total);\n        return statusBuilder.toString();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/xls/XlsDataExporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.xls;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseExcelExporter;\nimport com.alibaba.excel.support.ExcelTypeEnum;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:34\n */\n@Component(\"xlsExporter\")\npublic class XlsDataExporter extends BaseExcelExporter {\n\n    public XlsDataExporter() {\n        this.suffix = ExportFileSuffix.XLS.getSuffix();\n        this.contentType=\"application/vnd.ms-excel\";\n    }\n\n    @Override\n    protected ExcelTypeEnum getExcelType() {\n        return ExcelTypeEnum.XLS;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/xls/XlsDataImporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.xls;\n\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseExcelImporter;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:34\n */\n@Component(\"xlsImporter\")\npublic class XlsDataImporter extends BaseExcelImporter {\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/xlsx/XlsxDataExporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.xlsx;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseExcelExporter;\nimport com.alibaba.excel.support.ExcelTypeEnum;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:34\n */\n@Component(\"xlsxExporter\")\npublic class XlsxDataExporter extends BaseExcelExporter {\n\n    public XlsxDataExporter() {\n        this.suffix = ExportFileSuffix.EXCEL.getSuffix();\n        this.contentType=\"application/vnd.ms-excel\";\n    }\n\n\n    @Override\n    protected ExcelTypeEnum getExcelType() {\n        return ExcelTypeEnum.XLSX;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/xlsx/XlsxDataImporter.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.data.xlsx;\n\nimport ai.chat2db.server.web.api.controller.rdb.data.BaseExcelImporter;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author: zgq\n * @date: 2024年06月04日 10:34\n */\n@Component(\"xlsxImporter\")\npublic class XlsxDataImporter extends BaseExcelImporter {\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/DatabaseExportService.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc;\n\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.domain.api.model.IndexInfo;\nimport ai.chat2db.server.domain.api.model.TableParameter;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;\nimport ai.chat2db.server.web.api.controller.rdb.doc.constant.CommonConstant;\nimport ai.chat2db.server.web.api.controller.rdb.doc.constant.PatternConstant;\nimport ai.chat2db.server.web.api.controller.rdb.vo.ColumnVO;\nimport ai.chat2db.server.web.api.controller.rdb.vo.IndexVO;\nimport ai.chat2db.server.web.api.controller.rdb.vo.TableVO;\nimport ai.chat2db.server.web.api.util.StringUtils;\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.SneakyThrows;\nimport lombok.val;\n\nimport java.io.OutputStream;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\n/**\n * DatabaseExportService\n *\n * @author lzy\n **/\npublic class DatabaseExportService {\n    protected ExportTypeEnum exportTypeEnum;\n    @Getter\n    public String suffix;\n\n    @Getter\n    public String contentType;\n    /**\n     * Export excel collection\n     **/\n    @Setter\n    @Getter\n    public List<TableVO> exportList;\n    /**\n     * Export word and excel table information collection\n     **/\n    public static Map<String, List<TableParameter>> listMap = new LinkedHashMap<>();\n    /**\n     * Export word index collection\n     **/\n    public static Map<String, List<IndexInfo>> indexMap = new HashMap<>(0);\n    /**\n     * Joiner\n     **/\n    public final static String JOINER = \"---\";\n\n    private void init() {\n        CommonConstant.INDEX_HEAD_NAMES = new String[]{I18nUtils.getMessage(\"main.indexName\"),\n                I18nUtils.getMessage(\"main.indexFieldName\"),\n                I18nUtils.getMessage(\"main.indexType\"),\n                I18nUtils.getMessage(\"main.indexMethod\"),\n                I18nUtils.getMessage(\"main.indexNote\")};\n        CommonConstant.COLUMN_HEAD_NAMES = new String[]{I18nUtils.getMessage(\"main.fieldNo\"),\n                I18nUtils.getMessage(\"main.fieldName\"),\n                I18nUtils.getMessage(\"main.fieldType\"),\n                I18nUtils.getMessage(\"main.fieldLength\"),\n                I18nUtils.getMessage(\"main.fieldIfEmpty\"),\n                I18nUtils.getMessage(\"main.fieldDefault\"),\n                I18nUtils.getMessage(\"main.fieldDecimalPlaces\"),\n                I18nUtils.getMessage(\"main.fieldNote\")};\n        // index header\n        StringBuilder mdIndex = new StringBuilder(PatternConstant.MD_SPLIT);\n        StringBuilder htmlIndex = new StringBuilder(\"<tr><th>\");\n        for (int i = 0; i < CommonConstant.INDEX_HEAD_NAMES.length; i++) {\n            mdIndex.append(CommonConstant.INDEX_HEAD_NAMES[i]).append(i == CommonConstant.INDEX_HEAD_NAMES.length - 1 ? \"\" : PatternConstant.MD_SPLIT);\n            htmlIndex.append(CommonConstant.INDEX_HEAD_NAMES[i]).append(i == CommonConstant.INDEX_HEAD_NAMES.length - 1 ? \"\" : \"</th><th>\");\n        }\n        mdIndex.append(PatternConstant.MD_SPLIT);\n        htmlIndex.append(\"</th></tr>\");\n        // column header\n        StringBuilder mdColumn = new StringBuilder(PatternConstant.MD_SPLIT);\n        StringBuilder htmlColumn = new StringBuilder(\"<tr><th>\");\n        for (int i = 0; i < CommonConstant.COLUMN_HEAD_NAMES.length; i++) {\n            mdColumn.append(CommonConstant.COLUMN_HEAD_NAMES[i]).append(i == CommonConstant.COLUMN_HEAD_NAMES.length - 1 ? \"\" : PatternConstant.MD_SPLIT);\n            htmlColumn.append(CommonConstant.COLUMN_HEAD_NAMES[i]).append(i == CommonConstant.COLUMN_HEAD_NAMES.length - 1 ? \"\" : \"</th><th>\");\n        }\n        mdColumn.append(PatternConstant.MD_SPLIT);\n        htmlColumn.append(\"</th></tr>\");\n        PatternConstant.ALL_INDEX_TABLE_HEADER = mdIndex.toString();\n        PatternConstant.HTML_INDEX_TABLE_HEADER = htmlIndex.toString();\n        PatternConstant.ALL_TABLE_HEADER = mdColumn.toString();\n        PatternConstant.HTML_TABLE_HEADER = htmlColumn.toString();\n        listMap.clear();\n        indexMap.clear();\n    }\n\n    public void generate(String databaseName, OutputStream outputStream, ExportOptions exportOptions) {\n        init();\n        exportList.forEach(item -> {\n            dataAssemble(databaseName, exportOptions, item);\n        });\n        try {\n            export(outputStream, exportOptions);\n        } catch (Exception e) {\n            throw new RuntimeException(\"Export failed! Please contact the developer\" + e);\n        }\n        init();\n    }\n\n    /**\n     * data processing\n     *\n     * @param exportOptions Configuration information\n     **/\n    public void dataAssemble(String databaseName, ExportOptions exportOptions, TableVO item) {\n        boolean isExportIndex = Optional.ofNullable(exportOptions.getIsExportIndex()).orElse(false);\n        val t = new TableParameter();\n        t.setFieldName(item.getName() + \"[\" + StringUtils.isNull(item.getComment()) + \"]\");\n        List<TableParameter> colForTable = new LinkedList<>();\n        for (TableColumn info : item.getColumnList()) {\n            val p = new TableParameter();\n            p.setFieldName(info.getName()).setColumnDefault(info.getDefaultValue())\n                    .setColumnComment(info.getComment())\n                    .setColumnType(info.getColumnType())\n                    .setLength(String.valueOf(info.getColumnSize())).setIsNullAble(String.valueOf(info.getNullable()))\n                    .setDecimalPlaces(String.valueOf(info.getDecimalDigits()));\n            colForTable.add(p);\n        }\n        String key = databaseName + JOINER + t.getFieldName();\n        listMap.put(key, colForTable);\n        if (isExportIndex) {\n            int index = key.lastIndexOf(\"[\");\n            String str = key.substring(0, index);\n            indexMap.put(str, vo2Info(item.getIndexList()));\n        }\n        //Assignment serial number\n        for (Map.Entry<String, List<TableParameter>> map : listMap.entrySet()) {\n            //Assignment serial number\n            List<TableParameter> list = map.getValue();\n            IntStream.range(0, list.size()).forEach(x -> {\n                list.get(x).setNo(String.valueOf(x + 1));\n            });\n        }\n    }\n\n    private List<IndexInfo> vo2Info(List<TableIndex> indexList) {\n        return indexList.stream().map(v -> {\n            IndexInfo info = new IndexInfo();\n            info.setName(v.getName());\n            List<TableIndexColumn> columnList = v.getColumnList();\n            info.setColumnName(columnList.stream().map(TableIndexColumn::getColumnName).collect(Collectors.joining(\",\")));\n            info.setIndexType(v.getType());\n            info.setComment(v.getComment());\n            return info;\n        }).collect(Collectors.toList());\n    }\n\n    /**\n     * Export\n     *\n     * @param outputStream file stream\n     **/\n    public void export(OutputStream outputStream, ExportOptions exportOptions) {\n\n    }\n\n    /**\n     * Handle empty string or null character\n     *\n     * @param source source character\n     * @return java.lang.String\n     **/\n    public String dealWith(String source) {\n        return StringUtils.isNullOrEmpty(source);\n    }\n\n    @SneakyThrows\n    public Object[] getIndexValues(IndexInfo indexInfoVO) {\n        Object[] values = new Object[IndexInfo.class.getDeclaredFields().length];\n        values[0] = dealWith(indexInfoVO.getName());\n        values[1] = dealWith(indexInfoVO.getColumnName());\n        values[2] = dealWith(indexInfoVO.getIndexType());\n        values[3] = dealWith(indexInfoVO.getIndexMethod());\n        values[4] = dealWith(indexInfoVO.getComment());\n        return values;\n    }\n\n    @SneakyThrows\n    public Object[] getColumnValues(TableParameter tableParameter) {\n        Object[] values = new Object[TableParameter.class.getDeclaredFields().length];\n        values[0] = StringUtils.isNull(tableParameter.getNo());\n        values[1] = StringUtils.isNull(tableParameter.getFieldName());\n        values[2] = StringUtils.isNull(tableParameter.getColumnType());\n        values[3] = StringUtils.isNull(tableParameter.getLength());\n        values[4] = StringUtils.isNull(tableParameter.getIsNullAble());\n        values[5] = StringUtils.isNull(tableParameter.getColumnDefault());\n        values[6] = StringUtils.isNull(tableParameter.getDecimalPlaces());\n        values[7] = StringUtils.isNull(tableParameter.getColumnComment());\n        return values;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/adaptive/CustomCellWriteHeightConfig.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.adaptive;\n\nimport com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.CellType;\nimport org.apache.poi.ss.usermodel.Row;\n\nimport java.util.Iterator;\n\n/**\n * CustomCellWriteHeightConfig\n *\n * @author lzy\n **/\npublic class CustomCellWriteHeightConfig  extends AbstractRowHeightStyleStrategy {\n    /**\n     * Default height\n     */\n    private static final Integer DEFAULT_HEIGHT = 300;\n\n    @Override\n    protected void setHeadColumnHeight(Row row, int relativeRowIndex) {\n    }\n\n    @Override\n    protected void setContentColumnHeight(Row row, int relativeRowIndex) {\n        Iterator<Cell> cellIterator = row.cellIterator();\n        if (!cellIterator.hasNext()) {\n            return;\n        }\n\n        // Default is 1 row height\n        int maxHeight = 1;\n        while (cellIterator.hasNext()) {\n            Cell cell = cellIterator.next();\n            if (cell.getCellType() == CellType.STRING) {\n                if (cell.getStringCellValue().contains(\"\\n\")) {\n                    int length = cell.getStringCellValue().split(\"\\n\").length;\n                    maxHeight = Math.max(maxHeight, length);\n                }\n            }\n        }\n\n        row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/adaptive/CustomCellWriteWidthConfig.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.adaptive;\n\nimport com.alibaba.excel.enums.CellDataTypeEnum;\nimport com.alibaba.excel.metadata.Head;\nimport com.alibaba.excel.metadata.data.CellData;\nimport com.alibaba.excel.metadata.data.WriteCellData;\nimport com.alibaba.excel.write.metadata.holder.WriteSheetHolder;\nimport com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.Sheet;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * CustomCellWriteWidthConfig\n *\n * @author lzy\n **/\npublic class CustomCellWriteWidthConfig extends AbstractColumnWidthStyleStrategy {\n    private Map<String, Map<Integer, Integer>> CACHE = new HashMap<>();\n\n    @Override\n    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {\n        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);\n        if (needSetWidth) {\n            Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetName(), k -> new HashMap<>(0));\n\n            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);\n            if (columnWidth >= 0) {\n                if (columnWidth > 119) {\n                    columnWidth = 120;\n                }\n\n                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());\n                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {\n                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);\n                    Sheet sheet = writeSheetHolder.getSheet();\n                    sheet.setColumnWidth(cell.getColumnIndex(), Math.min(Double.valueOf(columnWidth * 256 * 1.8).intValue(), 16384));\n                }\n            }\n        }\n    }\n\n    /**\n     * Calculate length\n     * @param cellDataList cell data\n     * @param cell cell\n     * @param isHead whether it is the title\n     * @return\n     */\n    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {\n        if (isHead) {\n            return cell.getStringCellValue().getBytes().length;\n        } else {\n            CellData<?> cellData = cellDataList.get(0);\n            CellDataTypeEnum type = cellData.getType();\n            if (type == null) {\n                return -1;\n            } else {\n                switch (type) {\n                    case STRING:\n                        // Newline character (data needs to be parsed in advance)\n                        int index = cellData.getStringValue().indexOf(\"\\n\");\n                        return index != -1 ?\n                                cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1;\n                    case BOOLEAN:\n                        return cellData.getBooleanValue().toString().getBytes().length;\n                    case NUMBER:\n                        return cellData.getNumberValue().toString().getBytes().length;\n                    default:\n                        return -1;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/conf/ExportOptions.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.conf;\n\nimport lombok.Data;\n\n/**\n * Build options\n *\n * @author lzy\n */\n@Data\npublic class ExportOptions {\n    /**\n     * Whether to export multiple sheets\n     */\n    private Boolean isExportMoreSheet = Boolean.FALSE;\n    /**\n     * Whether to export the index\n     */\n    private Boolean isExportIndex = Boolean.FALSE;\n\n    /**\n     * Export file suffix\n     **/\n    private String fileSuffix;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/constant/CommonConstant.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.constant;\n\n/**\n * CommonConstant\n *\n * @author lzy\n **/\npublic final class CommonConstant {\n    /**\n     * Table head\n     **/\n    public static String[] INDEX_HEAD_NAMES = {};\n    public static String[] COLUMN_HEAD_NAMES = {};\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/constant/PatternConstant.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.constant;\n\n\n/**\n * PatternConstant\n *\n * @author lzy\n **/\npublic final class PatternConstant {\n\n    /**\n     * public\n     */\n    public static final String MD_SPLIT = \"|\";\n\n    /**\n     * Markdown\n     */\n    public static final String TITLE = \"# %s\";\n    public static final String CATALOG = \"## %s\";\n    public static String ALL_TABLE_HEADER = \"\";\n    public static String TABLE_BODY = \"|%s|%s|%s|%s|%s|%s|%s|%s|\";\n    public static String TABLE_SEPARATOR = \"|:----:|----|----|----|----|----|----|----|\";\n    public static String ALL_INDEX_TABLE_HEADER = \"\";\n    public static String INDEX_TABLE_BODY = \"|%s|%s|%s|%s|\";\n    public static String INDEX_TABLE_SEPARATOR = \"|:----:|----|----|----|\";\n\n    /**\n     * Html\n     */\n    public static final String HTML_TITLE = \"<h1 id=\\\"{0}\\\">{0}</h1>\";\n    public static final String HTML_CATALOG = \"<h2 id=\\\"{0}\\\">{1}</h2>\";\n    public static final String HTML_INDEX_ITEM = \"<a href=\\\"#{0}\\\" title=\\\"{0}\\\">{1}</a>\";\n    public static String HTML_TABLE_HEADER = \"\";\n    public static String HTML_TABLE_BODY = \"<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\";\n    public static String HTML_INDEX_TABLE_HEADER = \"\";\n    public static String HTML_INDEX_TABLE_BODY = \"<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\";\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/event/TemplateEvent.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.event;\n\nimport org.springframework.context.ApplicationEvent;\n\n/**\n * TemplateEvent\n *\n * @author lzy\n **/\npublic class TemplateEvent extends ApplicationEvent {\n    public TemplateEvent(String key) {\n        super(key);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/export/ExportExcelService.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.export;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.domain.api.model.TableParameter;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;\nimport ai.chat2db.server.web.api.controller.rdb.doc.adaptive.CustomCellWriteHeightConfig;\nimport ai.chat2db.server.web.api.controller.rdb.doc.adaptive.CustomCellWriteWidthConfig;\nimport ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;\nimport ai.chat2db.server.web.api.controller.rdb.doc.merge.MyMergeExcel;\nimport ai.chat2db.server.web.api.controller.rdb.doc.style.CustomExcelStyle;\nimport com.alibaba.excel.EasyExcel;\nimport com.alibaba.excel.write.style.HorizontalCellStyleStrategy;\nimport lombok.SneakyThrows;\nimport lombok.val;\n\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * ExportExcelService\n *\n * @author lzy\n **/\npublic class ExportExcelService extends DatabaseExportService {\n\n    public ExportExcelService() {\n        exportTypeEnum = ExportTypeEnum.EXCEL;\n        suffix = ExportFileSuffix.EXCEL.getSuffix();\n        contentType = \"text/csv\";\n    }\n\n    @SneakyThrows\n    @Override\n    public void export(OutputStream outputStream, ExportOptions exportOptions) {\n        List<TableParameter> export = new ArrayList<>();\n        for (Map.Entry<String, List<TableParameter>> item : listMap.entrySet()) {\n            val t = new TableParameter();\n            t.setNo(item.getKey()).setColumnComment(MyMergeExcel.NAME);\n            export.add(t);\n            export.addAll(item.getValue());\n        }\n        EasyExcel.write(outputStream)\n                .registerWriteHandler(new HorizontalCellStyleStrategy(CustomExcelStyle.getHeadStyle(), CustomExcelStyle.getContentWriteCellStyle()))\n                .registerWriteHandler(new CustomCellWriteHeightConfig())\n                .registerWriteHandler(new CustomCellWriteWidthConfig())\n                .registerWriteHandler(new MyMergeExcel())\n                .sheet(I18nUtils.getMessage(\"main.sheetName\"))\n                .doWrite(export);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/export/ExportHtmlService.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.export;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.domain.api.model.IndexInfo;\nimport ai.chat2db.server.domain.api.model.TableParameter;\nimport ai.chat2db.server.tools.common.config.GlobalDict;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;\nimport ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;\nimport ai.chat2db.server.web.api.controller.rdb.doc.constant.PatternConstant;\nimport ai.chat2db.server.web.api.util.StringUtils;\nimport lombok.SneakyThrows;\n\nimport java.io.BufferedWriter;\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.text.MessageFormat;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * ExportHtmlService\n *\n * @author lzy\n **/\npublic class ExportHtmlService extends DatabaseExportService {\n\n    public ExportHtmlService() {\n        exportTypeEnum = ExportTypeEnum.HTML;\n        suffix = ExportFileSuffix.HTML.getSuffix();\n        contentType = \"text/html\";\n    }\n\n    @SneakyThrows\n    @Override\n    public void export(OutputStream outputStream, ExportOptions exportOptions) {\n        Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()\n                .stream().collect(Collectors.groupingBy(v -> v.getKey().split(\"---\")[0]));\n        StringBuilder htmlText = new StringBuilder();\n        StringBuilder catalogue = new StringBuilder();\n        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {\n            for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {\n                //Database name\n                String database = myMap.getKey();\n                String title = MessageFormat.format(PatternConstant.HTML_TITLE, I18nUtils.getMessage(\"main.databaseText\") + database);\n                //Database name-directory\n                catalogue.append(\"<li>\").append(MessageFormat.format(PatternConstant.HTML_INDEX_ITEM, I18nUtils.getMessage(\"main.databaseText\")\n                        + database, I18nUtils.getMessage(\"main.databaseText\") + database)).append(\"<ol>\");\n                htmlText.append(title).append(\"\\n\");\n                for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {\n                    //Table Name\n                    String tableName = parameterMap.getKey().split(\"---\")[1];\n                    //Table name-directory\n                    catalogue.append(\"<li>\").append(MessageFormat.format(PatternConstant.HTML_INDEX_ITEM, database + tableName, tableName));\n                    htmlText.append(MessageFormat.format(PatternConstant.HTML_CATALOG, database + tableName, tableName)).append(\"\\n<p></p>\");\n                    //IndexTable\n                    if (!indexMap.isEmpty()) {\n                        htmlText.append(\"<table>\\n\");\n                        htmlText.append(PatternConstant.HTML_INDEX_TABLE_HEADER);\n                        String name = parameterMap.getKey().split(\"\\\\[\")[0];\n                        List<IndexInfo> indexInfoVOList = indexMap.get(name);\n                        for (IndexInfo indexInfo : indexInfoVOList) {\n                            htmlText.append(String.format(PatternConstant.HTML_INDEX_TABLE_BODY, getIndexValues(indexInfo)));\n                        }\n                        htmlText.append(\"</table>\\n\");\n                        htmlText.append(\"\\n<p></p>\");\n                    } else {\n                        htmlText.append(String.format(PatternConstant.HTML_INDEX_TABLE_BODY, getIndexValues(new IndexInfo())));\n                    }\n                    //FieldTable\n                    htmlText.append(\"<table>\\n\");\n                    htmlText.append(PatternConstant.HTML_TABLE_HEADER);\n                    List<TableParameter> exportList = parameterMap.getValue();\n                    for (TableParameter tableParameter : exportList) {\n                        htmlText.append(String.format(PatternConstant.HTML_TABLE_BODY, getColumnValues(tableParameter)));\n                    }\n                    htmlText.append(\"</table>\\n\");\n                }\n                htmlText.append(\"<p></p>\");\n                catalogue.append(\"</ol>\");\n            }\n            catalogue.append(\"</li>\");\n\n            try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(\"template/\" + GlobalDict.TEMPLATE_FILE.get(0));\n                 ByteArrayOutputStream result = new ByteArrayOutputStream()) {\n                byte[] buffer = new byte[1024];\n                int length;\n                while ((length = inputStream.read(buffer)) != -1) {\n                    result.write(buffer, 0, length);\n                }\n                String str = result.toString(String.valueOf(StandardCharsets.UTF_8));\n\n                str = str.replace(\"${data}\", htmlText).replace(\"${catalogue}\", catalogue);\n                writer.write(str);\n            }\n        }\n    }\n\n    @Override\n    public String dealWith(String source) {\n        return StringUtils.isNullForHtml(source);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/export/ExportMarkdownService.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.export;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.domain.api.model.IndexInfo;\nimport ai.chat2db.server.domain.api.model.TableParameter;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;\nimport ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;\nimport ai.chat2db.server.web.api.controller.rdb.doc.constant.PatternConstant;\nimport ai.chat2db.server.web.api.util.StringUtils;\nimport lombok.SneakyThrows;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * ExportMarkdownService\n *\n * @author lzy\n **/\npublic class ExportMarkdownService extends DatabaseExportService {\n\n    public ExportMarkdownService() {\n        exportTypeEnum = ExportTypeEnum.MARKDOWN;\n        suffix = ExportFileSuffix.MARKDOWN.getSuffix();\n        contentType = \"text/plain\";\n    }\n\n    @SneakyThrows\n    @Override\n    public void export(OutputStream outputStream, ExportOptions exportOptions) {\n        Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()\n                .stream().collect(Collectors.groupingBy(v -> v.getKey().split(\"---\")[0]));\n        try (BufferedWriter fileWriter = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {\n            for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {\n                //Database name\n                String database = myMap.getKey();\n                String title = String.format(PatternConstant.TITLE, I18nUtils.getMessage(\"main.databaseText\") + database);\n                fileWriter.write(title);\n                writeLineSeparator(fileWriter, 2);\n                for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {\n                    //Table Name\n                    String tableName = parameterMap.getKey().split(\"---\")[1];\n                    fileWriter.write(String.format(PatternConstant.CATALOG, tableName));\n                    writeLineSeparator(fileWriter, 1);\n                    //IndexTable\n                    if (!indexMap.isEmpty()) {\n                        fileWriter.write(PatternConstant.ALL_INDEX_TABLE_HEADER);\n                        writeLineSeparator(fileWriter, 1);\n                        fileWriter.write(PatternConstant.INDEX_TABLE_SEPARATOR);\n                        writeLineSeparator(fileWriter, 1);\n                        String name = parameterMap.getKey().split(\"\\\\[\")[0];\n                        List<IndexInfo> indexInfoVOList = indexMap.get(name);\n                        for (int j = 0; j < indexInfoVOList.size(); j++) {\n                            fileWriter.write(String.format(PatternConstant.INDEX_TABLE_BODY, getIndexValues(indexInfoVOList.get(j))));\n                            writeLineSeparator(fileWriter, 1);\n                        }\n                        writeLineSeparator(fileWriter, 1);\n                    }\n                    writeLineSeparator(fileWriter, 2);\n                    fileWriter.write(PatternConstant.ALL_TABLE_HEADER);\n                    writeLineSeparator(fileWriter, 1);\n                    fileWriter.write(PatternConstant.TABLE_SEPARATOR);\n                    writeLineSeparator(fileWriter, 1);\n                    //FieldTable\n                    List<TableParameter> exportList = parameterMap.getValue();\n                    for (TableParameter tableParameter : exportList) {\n                        fileWriter.write(String.format(PatternConstant.TABLE_BODY, getColumnValues(tableParameter)));\n                        writeLineSeparator(fileWriter, 1);\n                    }\n                    writeLineSeparator(fileWriter, 2);\n                }\n            }\n        }\n    }\n\n    private void writeLineSeparator(BufferedWriter fileWriter, int number) throws IOException {\n        for (int i = 0; i < number; i++) {\n            fileWriter.write(System.lineSeparator());\n        }\n    }\n\n    @Override\n    public String dealWith(String source) {\n        return StringUtils.isNullForHtml(source);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/export/ExportPdfService.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.export;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.domain.api.model.IndexInfo;\nimport ai.chat2db.server.domain.api.model.TableParameter;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;\nimport ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;\nimport ai.chat2db.server.web.api.controller.rdb.doc.constant.CommonConstant;\nimport com.itextpdf.text.Document;\nimport com.itextpdf.text.Font;\nimport com.itextpdf.text.Paragraph;\nimport com.itextpdf.text.pdf.BaseFont;\nimport com.itextpdf.text.pdf.PdfPCell;\nimport com.itextpdf.text.pdf.PdfPTable;\nimport com.itextpdf.text.pdf.PdfWriter;\nimport lombok.SneakyThrows;\n\nimport java.io.OutputStream;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\n/**\n * ExportPdfService\n *\n * @author lzy\n **/\npublic class ExportPdfService extends DatabaseExportService {\n\n    public ExportPdfService() {\n        exportTypeEnum = ExportTypeEnum.PDF;\n        suffix = ExportFileSuffix.PDF.getSuffix();\n        contentType = \"application/pdf\";\n    }\n\n    @SneakyThrows\n    @Override\n    public void export(OutputStream outputStream, ExportOptions exportOptions) {\n        boolean isExportIndex = exportOptions.getIsExportIndex();\n        Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()\n                .stream().collect(Collectors.groupingBy(v -> v.getKey().split(\"---\")[0]));\n        Document document = new Document();\n        PdfWriter pdfWriter = PdfWriter.getInstance(document, outputStream);\n        pdfWriter.setStrictImageSequence(true);\n        // Font settings\n        BaseFont baseFont =BaseFont.createFont(\"STSong-Light\", \"UniGB-UCS2-H\", BaseFont.NOT_EMBEDDED);\n        // Create font object\n        Font font = new Font(baseFont, 10, Font.NORMAL);\n        Font headFont = new Font(baseFont, 12, Font.NORMAL);\n        Font titleFont = new Font(baseFont, 14, Font.BOLD);\n        document.open();\n        //Iterate over data\n        for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {\n            //Database name\n            String database = myMap.getKey();\n            String title = I18nUtils.getMessage(\"main.databaseText\") + database;\n            Paragraph p = new Paragraph(title, titleFont);\n            document.add(p);\n            for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {\n                //Table Name\n                String tableName = parameterMap.getKey().split(\"---\")[1];\n                Paragraph tableParagraph = new Paragraph(tableName, font);\n                document.add(tableParagraph);\n                //IndexTable\n                if (isExportIndex && !indexMap.isEmpty()) {\n                    PdfPTable table = new PdfPTable(CommonConstant.INDEX_HEAD_NAMES.length);\n                    process(table, CommonConstant.INDEX_HEAD_NAMES, font);\n                    String name = parameterMap.getKey().split(\"\\\\[\")[0];\n                    List<IndexInfo> indexInfoVOList = indexMap.get(name);\n                    for (IndexInfo indexInfo : indexInfoVOList) {\n                        process(table, getIndexValues(indexInfo), font);\n                    }\n                    table.setPaddingTop(5);\n                    document.add(table);\n                }\n                document.add(new Paragraph());\n                //FieldTable\n                List<TableParameter> exportList = parameterMap.getValue();\n                PdfPTable table = new PdfPTable(CommonConstant.COLUMN_HEAD_NAMES.length);\n                //title content\n                process(table, CommonConstant.COLUMN_HEAD_NAMES, headFont);\n                for (TableParameter tableParameter : exportList) {\n                    process(table, getColumnValues(tableParameter), font);\n                }\n                // Set the blank space above the table, that is, the effect of moving downwards\n                table.setSpacingBefore(10f);\n                table.setSpacingAfter(20f);\n                //Align left\n                table.setHorizontalAlignment(PdfPTable.ALIGN_LEFT);\n                document.add(table);\n                //Pagination\n                //document.newPage();\n            }\n        }\n        document.close();\n    }\n\n    //Set table content\n    public static <T> void process(PdfPTable table, T[] line, Font font) {\n        for (T s : line) {\n            if (Objects.isNull(s)) {\n                return;\n            }\n            PdfPCell cell = new PdfPCell(new Paragraph(s.toString(), font));\n            cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);\n            cell.setVerticalAlignment(PdfPCell.ALIGN_CENTER);\n            cell.setPaddingTop(5);\n            cell.setPaddingBottom(5);\n            table.addCell(cell);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/export/ExportWordSuperService.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.export;\n\nimport ai.chat2db.server.domain.api.enums.ExportFileSuffix;\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.domain.api.model.IndexInfo;\nimport ai.chat2db.server.domain.api.model.TableParameter;\nimport ai.chat2db.server.tools.common.config.GlobalDict;\nimport ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;\nimport ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;\nimport ai.chat2db.server.web.api.controller.rdb.doc.constant.CommonConstant;\nimport ai.chat2db.server.web.api.util.AddToTopic;\nimport com.deepoove.poi.XWPFTemplate;\nimport com.deepoove.poi.data.Includes;\nimport com.deepoove.poi.data.RowRenderData;\nimport com.deepoove.poi.data.Rows;\nimport com.deepoove.poi.data.Tables;\nimport lombok.SneakyThrows;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * WordSuperActionListener\n *\n * @author lzy\n **/\npublic class ExportWordSuperService extends DatabaseExportService {\n\n    public ExportWordSuperService() {\n        exportTypeEnum = ExportTypeEnum.WORD;\n        suffix = ExportFileSuffix.WORD.getSuffix();\n        contentType = \"application/msword\";\n    }\n\n    /**\n     * Word export\n     **/\n    @SneakyThrows\n    @Override\n    public void export(OutputStream outputStream, ExportOptions exportOptions) {\n        boolean isExportIndex = exportOptions.getIsExportIndex();\n        InputStream filePath = this.getClass().getClassLoader().getResourceAsStream(\"template/\" + GlobalDict.TEMPLATE_FILE.get(1));\n        InputStream subFile = this.getClass().getClassLoader().getResourceAsStream(\"template/\" + GlobalDict.TEMPLATE_FILE.get(2));\n        Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()\n                .stream().collect(Collectors.groupingBy(v -> v.getKey().split(\"---\")[0]));\n        List<Map<String, Object>> list = new ArrayList<>();\n        Map<String, Object> myDataMap = new HashMap<>(2);\n        //Index header\n        RowRenderData indexHeaderRow = Rows.of(CommonConstant.INDEX_HEAD_NAMES).center().textBold().textColor(\"000000\").bgColor(\"bfbfbf\").create();\n        //Field header\n        RowRenderData tableHeaderRow = Rows.of(CommonConstant.COLUMN_HEAD_NAMES).center().textBold().textColor(\"000000\").bgColor(\"bfbfbf\").create();\n        for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {\n            //Database name\n            String database = myMap.getKey();\n            int i = 1;\n            for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {\n                //Initial capacity 3/0.75 + 1\n                Map<String, Object> tableData = new HashMap<>(8);\n                //IndexTable\n                if (isExportIndex) {\n                    String name = parameterMap.getKey().split(\"\\\\[\")[0];\n                    List<IndexInfo> indexInfoVOList = indexMap.get(name);\n                    List<RowRenderData> rowList = getIndexValues(indexInfoVOList, indexHeaderRow);\n                    tableData.put(\"indexTable\", Tables.create(rowList.toArray(new RowRenderData[0])));\n                }\n                if (i == 1) {\n                    Map<String, String> map = new HashMap<>(2);\n                    map.put(\"dataBase\", database);\n                    tableData.put(\"ifDatabase\", map);\n                }\n                //Table Name\n                String tableName = parameterMap.getKey().split(\"---\")[1];\n                tableData.put(\"number\", i);\n                tableData.put(\"name\", tableName);\n                List<TableParameter> tableParameterList = parameterMap.getValue();\n                List<RowRenderData> rowList = getColumnValues(tableParameterList, tableHeaderRow);\n                tableData.put(\"table\", Tables.create(rowList.toArray(new RowRenderData[0])));\n                i++;\n                list.add(tableData);\n            }\n        }\n        myDataMap.put(\"mydata\", Includes.ofStream(subFile).setRenderModel(list).create());\n        /*Generate documents based on template*/\n        XWPFTemplate template = XWPFTemplate.compile(filePath).render(myDataMap);\n        AddToTopic.generateTOC(template.getXWPFDocument(), outputStream);\n    }\n\n    @SneakyThrows\n    public List<RowRenderData> getColumnValues(List<TableParameter> list, RowRenderData tableHeaderRow) {\n        List<RowRenderData> rowRenderDataList = new ArrayList<>();\n        rowRenderDataList.add(tableHeaderRow);\n        for (TableParameter tableParameter : list) {\n            String[] values = Arrays.stream(getColumnValues(tableParameter)).toArray(String[]::new);\n            rowRenderDataList.add(Rows.of(values).center().create());\n        }\n        return rowRenderDataList;\n    }\n\n\n    @SneakyThrows\n    public List<RowRenderData> getIndexValues(List<IndexInfo> list, RowRenderData tableHeaderRow) {\n        List<RowRenderData> rowRenderDataList = new ArrayList<>();\n        rowRenderDataList.add(tableHeaderRow);\n        if (list.isEmpty()) {\n            String[] values = Arrays.stream(getIndexValues(new IndexInfo())).toArray(String[]::new);\n            rowRenderDataList.add(Rows.of(values).center().create());\n            return rowRenderDataList;\n        }\n        for (IndexInfo indexInfo : list) {\n            String[] values = Arrays.stream(getIndexValues(indexInfo)).toArray(String[]::new);\n            rowRenderDataList.add(Rows.of(values).center().create());\n        }\n        return rowRenderDataList;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/merge/MyMergeExcel.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.merge;\n\nimport com.alibaba.excel.metadata.Head;\nimport com.alibaba.excel.write.merge.AbstractMergeStrategy;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.ss.util.CellRangeAddress;\n\n/**\n * MyMergeExcel\n *\n * @author lzy\n **/\npublic class MyMergeExcel extends AbstractMergeStrategy {\n\n    public static final String NAME = \"isTableNameBlank\";\n\n    @Override\n    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {\n        if (NAME.equals(cell.getStringCellValue())) {\n            CellRangeAddress region = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(), 0, 7);\n            sheet.addMergedRegion(region);\n            Row row = sheet.getRow(cell.getRowIndex());\n            cell = row.getCell(0);\n            Workbook workbook = sheet.getWorkbook();\n            // generate a style\n            CellStyle style = workbook.createCellStyle();\n            // Set these styles\n            style.setBorderBottom(BorderStyle.THIN);\n            style.setBorderLeft(BorderStyle.THIN);\n            style.setBorderRight(BorderStyle.THIN);\n            style.setBorderTop(BorderStyle.THIN);\n            style.setAlignment(HorizontalAlignment.CENTER);\n            style.setVerticalAlignment(VerticalAlignment.CENTER);\n            //Set up filling scheme\n            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);\n            //Set custom fill color\n            style.setFillForegroundColor(IndexedColors.GREEN.getIndex());\n            // generate a font\n            Font font = workbook.createFont();\n            font.setBold(true);\n            font.setFontHeightInPoints((short) 14);\n            // Apply font to current style\n            style.setFont(font);\n            cell.setCellStyle(style);\n        }\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/doc/style/CustomExcelStyle.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.doc.style;\n\nimport com.alibaba.excel.write.metadata.style.WriteCellStyle;\nimport com.alibaba.excel.write.metadata.style.WriteFont;\nimport org.apache.poi.ss.usermodel.BorderStyle;\nimport org.apache.poi.ss.usermodel.HorizontalAlignment;\nimport org.apache.poi.ss.usermodel.IndexedColors;\nimport org.apache.poi.ss.usermodel.VerticalAlignment;\n\n/**\n * CustomExcelStyle\n *\n * @author lzy\n **/\npublic class CustomExcelStyle {\n    public static WriteCellStyle getContentWriteCellStyle() {\n        //Content style strategy\n        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();\n        //Center vertically, center horizontally\n        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);\n        contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());\n        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);\n        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);\n        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);\n        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);\n        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);\n        //Set automatic line wrapping\n        contentWriteCellStyle.setWrapped(true);\n        // Font strategy\n        WriteFont contentWriteFont = new WriteFont();\n        // font size\n        contentWriteFont.setFontHeightInPoints((short) 11);\n        contentWriteFont.setFontName(\"宋体\");\n        contentWriteFont.setColor(IndexedColors.BLACK.getIndex());\n        contentWriteCellStyle.setWriteFont(contentWriteFont);\n        return contentWriteCellStyle;\n    }\n\n    public static WriteCellStyle getHeadStyle() {\n        //Header policy uses default settings for font size\n        WriteCellStyle headWriteCellStyle = new WriteCellStyle();\n\n        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);\n        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);\n        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);\n        headWriteCellStyle.setBorderTop(BorderStyle.THIN);\n        headWriteCellStyle.setBorderRight(BorderStyle.THIN);\n        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);\n\n        headWriteCellStyle.setWrapped(false);\n\n        WriteFont headWriteFont = new WriteFont();\n        headWriteFont.setFontHeightInPoints((short) 14);\n        headWriteFont.setBold(true);\n        headWriteFont.setFontName(\"宋体\");\n        headWriteCellStyle.setWriteFont(headWriteFont);\n        headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());\n        return headWriteCellStyle;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/factory/ExportServiceFactory.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.factory;\n\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.web.api.controller.rdb.doc.export.*;\nimport lombok.SneakyThrows;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * ExportServiceFactory\n *\n * @author lzy\n **/\npublic class ExportServiceFactory {\n\n    /**\n     * Export implementation class cache pool\n     */\n    private static final Map<String, Class<?>> REPORT_POOL = new ConcurrentHashMap<>(8);\n\n\n    static {\n        REPORT_POOL.put(ExportTypeEnum.EXCEL.name(), ExportExcelService.class);\n        REPORT_POOL.put(ExportTypeEnum.WORD.name(), ExportWordSuperService.class);\n        REPORT_POOL.put(ExportTypeEnum.MARKDOWN.name(), ExportMarkdownService.class);\n        REPORT_POOL.put(ExportTypeEnum.HTML.name(), ExportHtmlService.class);\n        REPORT_POOL.put(ExportTypeEnum.PDF.name(), ExportPdfService.class);\n    }\n\n    /**\n     * Get the corresponding interface\n     *\n     * @param type dashboard type\n     * @return Class\n     */\n    @SneakyThrows\n    public static Class<?> get(String type) {\n        Class<?> dataResult = REPORT_POOL.get(type);\n        if (dataResult == null) {\n            throw new ClassNotFoundException(\"no ExportUI was found\");\n        } else {\n            return dataResult;\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/ColumnRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Column\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ColumnRequest {\n\n    /**\n     * The old column name, this parameter is needed when modifying the column\n     * You can send it without modification\n     */\n    private String oldName;\n    /**\n     * name\n     */\n    private String name;\n\n    /**\n     * Column type\n     * For example, varchar(100), double(10,6)\n     */\n    private String columnType;\n\n    /**\n     * Is it empty\n     */\n    private Integer nullable;\n\n    /**\n     * Is it a primary key?\n     */\n    private Boolean primaryKey;\n\n    /**\n     * default value\n     */\n    private String defaultValue;\n\n    /**\n     * Whether to increment automatically\n     */\n    private Boolean autoIncrement;\n\n    /**\n     * comment\n     */\n    private String comment;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DataExportRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.domain.api.enums.ExportSizeEnum;\nimport ai.chat2db.server.domain.api.enums.ExportTypeEnum;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DataExportRequest extends DataSourceBaseRequest {\n    /**\n     * Executed SQL\n     */\n    private String sql;\n\n    /**\n     * Original SQL without pagination\n     */\n    private String originalSql;\n\n    private Integer pageNo;\n    private Integer pageSize;\n\n    /**\n     * export type\n     *\n     * @see ExportTypeEnum\n     */\n    @NotNull\n    private String exportType;\n\n    /**\n     * How much data is currently needed at the beginning\n     *\n     * @see ExportSizeEnum\n     */\n    @NotNull\n    private String exportSize;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DatabaseCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport lombok.Data;\n\n@Data\npublic class DatabaseCreateRequest extends DataSourceBaseRequest {\n\n    private String name;\n\n    private String comment;\n\n    private String charset;\n\n    private String collation;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DatabaseExportDataRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n/**\n * @author: zgq\n * @date: 2024年03月24日 12:36\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class DatabaseExportDataRequest extends DataSourceBaseRequest {\n    @NotNull\n    private String exportType;\n    @NotEmpty\n    private List<String> tableNames;\n    /**\n     * single：单行插入，multi：多行插入，update：更新语句\n     */\n    private String sqyType;\n    private Boolean containsHeader;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DatabaseExportRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author: zgq\n * @date: February 27, 2024 22:03\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class DatabaseExportRequest extends DataSourceBaseRequest {\n    private Boolean containData;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DdlCountRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceConsoleRequestInfo;\n\nimport lombok.Data;\n\n/**\n * total number\n *\n * @author Jiaju Zhuang\n */\n@Data\npublic class DdlCountRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {\n\n    /**\n     * sql statement\n     */\n    @NotNull\n    private String sql;\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DdlExportRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DdlExportRequest extends DataSourceBaseRequest {\n\n    /**\n     * Name\n     */\n    @NotNull\n    private String name;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DdlRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceConsoleRequestInfo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableManageRequest.java, v 0.1 September 16, 2022 17:55 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DdlRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {\n\n    /**\n     * sql statement\n     */\n    @NotNull\n    private String sql;\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     * Page coding\n     * Only available for select statements\n     */\n    private Integer pageNo;\n\n    /**\n     * Paging Size\n     * Only available for select statements\n     */\n    private Integer pageSize;\n\n    /**\n     * Return all data\n     * Only available for select statements\n     */\n    private Boolean pageSizeAll;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DmlRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceConsoleRequestInfo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableManageRequest.java, v 0.1 September 16, 2022 17:55 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DmlRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {\n\n    /**\n     * sql statement\n     */\n    @NotNull\n    private String sql;\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     *Page coding\n     * Only available for select statements\n     */\n    private Integer pageNo;\n\n    /**\n     * Paging Size\n     * Only available for select statements\n     */\n    private Integer pageSize;\n\n    /**\n     * Return all data\n     * Only available for select statements\n     */\n    private Boolean pageSizeAll;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DmlSqlCopyRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class DmlSqlCopyRequest extends DataSourceBaseRequest {\n\n    @NotNull\n    private String tableName;\n\n    private String type;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DmlTableRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceConsoleRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableManageRequest.java, v 0.1 September 16, 2022 17:55 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class DmlTableRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {\n\n    /**\n     * table noun\n     */\n    @NotNull\n    private String tableName;\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     *Page coding\n     * Only available for select statements\n     */\n    private Integer pageNo;\n\n    /**\n     * Paging Size\n     * Only available for select statements\n     */\n    private Integer pageSize;\n\n    /**\n     * Return all data\n     * Only available for select statements\n     */\n    private Boolean pageSizeAll;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/FunctionDetailRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class FunctionDetailRequest implements DataSourceBaseRequestInfo {\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located is required by pg and oracle, but not by mysql.\n     */\n    private String schemaName;\n\n    /**\n     * function name\n     */\n    private String functionName;\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/FunctionPageRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class FunctionPageRequest extends PageQueryRequest implements DataSourceBaseRequestInfo {\n\n    @Serial\n    private static final long serialVersionUID = -364547173428396332L;\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located is required by pg and oracle, but not by mysql.\n     */\n    private String schemaName;\n\n    /**\n     * Fuzzy search terms\n     */\n    private String searchKey;\n\n    /**\n     * function name\n     */\n    private String functionName;\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/FunctionUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Juechen\n * @version : FunctionUpdateRequest.java\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class FunctionUpdateRequest extends DataSourceBaseRequest {\n\n    private String functionName;\n    private String functionBody;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/GroupByRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport ai.chat2db.spi.model.OrderBy;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class GroupByRequest extends DataSourceBaseRequest implements DataSourceBaseRequestInfo {\n\n    /**\n     * origin sql\n     */\n    private String originSql;\n\n     /**\n     * group by field\n     */\n\n    private List<String> groupByList;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/IndexRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.enums.IndexTypeEnum;\n\nimport ai.chat2db.spi.model.TableIndexColumn;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * index\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IndexRequest {\n\n    /**\n     * Index name\n     */\n    private String name;\n\n    /**\n     * all types\n     *\n     * @see IndexTypeEnum\n     */\n    private String type;\n\n    /**\n     * Comment\n     */\n    private String comment;\n\n    /**\n     * Columns included in the index\n     */\n    private List<TableIndexColumn> columnList;\n\n\n    private String editStatus;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/NewTableSqlRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class NewTableSqlRequest extends DataSourceBaseRequest {\n\n    /**\n     * new table structure\n     */\n    @NotNull\n    private TableRequest newTable;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/OrderByRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport ai.chat2db.spi.model.OrderBy;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class OrderByRequest extends DataSourceBaseRequest implements DataSourceBaseRequestInfo {\n\n    /**\n     * origin sql\n     */\n    private String originSql;\n\n    /**\n     * sort field\n     */\n    private List<OrderBy> orderByList;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/ProcedureDetailRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class ProcedureDetailRequest implements DataSourceBaseRequestInfo {\n\n    @Serial\n    private static final long serialVersionUID = -364547173428396332L;\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located is required by pg and oracle, but not by mysql.\n     */\n    private String schemaName;\n\n    /**\n     * procedure name\n     */\n    private String procedureName;\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/ProcedurePageRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class ProcedurePageRequest extends PageQueryRequest implements DataSourceBaseRequestInfo {\n\n    @Serial\n    private static final long serialVersionUID = -364547173428396332L;\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located is required by pg and oracle, but not by mysql.\n     */\n    private String schemaName;\n\n    /**\n     * Fuzzy search terms\n     */\n    private String searchKey;\n\n    /**\n     * procedure name\n     */\n    private String procedureName;\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/ProcedureUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author: zgq\n * @date: February 24, 2024 13:21\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ProcedureUpdateRequest extends DataSourceBaseRequest {\n\n    private String procedureName;\n    private String procedureBody;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/SchemaCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.Data;\n\n@Data\npublic class SchemaCreateRequest extends DataSourceBaseRequest {\n\n    /**\n     * Data name\n     */\n    @JsonAlias({\"TABLE_SCHEM\"})\n    private String name;\n\n\n    private String comment;\n\n\n    private String owner;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/SchemaQueryRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : SchemaQueryRequest.java\n */\n@Data\npublic class SchemaQueryRequest extends DataSourceBaseRequest {\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/SelectResultUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.domain.api.param.SelectResultOperation;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceConsoleRequestInfo;\nimport ai.chat2db.spi.model.Header;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.util.List;\n\n@Data\npublic class SelectResultUpdateRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {\n\n    /**\n     * List of display headers\n     */\n    private List<Header> headerList;\n\n    /**\n     * List of modified data\n     */\n    @NotEmpty\n    private List<SelectResultOperation> operations;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n    @Override\n    public Long getConsoleId() {\n        return consoleId;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/SequenceBriefQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.io.Serial;\n\n/**\n * Query sequence brief request\n *\n * @author Sylphy\n */\n@Data\npublic class SequenceBriefQueryRequest extends PageQueryRequest implements DataSourceBaseRequestInfo {\n\n    @Serial\n    private static final long serialVersionUID = -1324577112324436332L;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the sequence is located is required by pg and oracle, but not by mysql.\n     */\n    private String schemaName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/SequenceDeleteRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Delete sequence sql request\n *\n * @author Sylphy\n */\n@Data\npublic class SequenceDeleteRequest extends DataSourceBaseRequest {\n    /**\n     * Sequence Name\n     */\n    @NotNull\n    private String sequenceName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/SequenceDetailQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Query sequence detail request\n *\n * @author Sylphy\n */\n@Data\npublic class SequenceDetailQueryRequest extends DataSourceBaseRequest {\n\n    /**\n     * Sequence Name\n     */\n    @NotNull\n    private String sequenceName;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/SequenceModifySqlRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * Modify sequence sql request\n *\n * @author Sylphy\n */\n@Data\npublic class SequenceModifySqlRequest extends DataSourceBaseRequest {\n    /**\n     * Old sequence structure\n     * Empty means creating a new sequence\n     */\n    private SequenceRequest oldSequence;\n    /**\n     * new sequence structure\n     */\n    @NotNull\n    private SequenceRequest newSequence;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/SequenceRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Modify sequence sql request\n *\n * @author Sylphy\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SequenceRequest {\n    /**\n     * Schema name\n     */\n    private String nspname;\n    /**\n     * Sequence name\n     */\n    private String relname;\n    /**\n     * Sequence data type\n     */\n    private String typname;\n    /**\n     * Sequence cache\n     */\n    private String seqcache;\n    /**\n     * Sequence owner\n     */\n    private String rolname;\n    /**\n     * Sequence comment\n     */\n    private String comment;\n    /**\n     * Sequence start value\n     */\n    private String seqstart;\n    /**\n     * Sequence step value\n     */\n    private String seqincrement;\n    /**\n     * Sequence max value\n     */\n    private String seqmax;\n    /**\n     * Sequence min value\n     */\n    private String seqmin;\n    /**\n     * Sequence cycle\n     */\n    private Boolean seqcycle;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableBriefQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.io.Serial;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class TableBriefQueryRequest extends PageQueryRequest implements DataSourceBaseRequestInfo {\n\n    @Serial\n    private static final long serialVersionUID = -364547173428396332L;\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located is required by pg and oracle, but not by mysql.\n     */\n    private String schemaName;\n\n    /**\n     * Fuzzy search terms\n     */\n    private String searchKey;\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableCreateDdlQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableCreateDdlQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class TableCreateDdlQueryRequest {\n\n    /**\n     * DB type\n     * @see ai.chat2db.server.domain.support.enums.DbTypeEnum\n     */\n    @NotNull\n    private String dbType;\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableDeleteRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableManageRequest.java, v 0.1 September 16, 2022 17:55 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class TableDeleteRequest extends DataSourceBaseRequest {\n\n    /**\n     * Table Name\n     */\n    @NotNull\n    private String tableName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableDetailQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class TableDetailQueryRequest extends DataSourceBaseRequest {\n\n    /**\n     * Table Name\n     */\n    @NotNull\n    private String tableName;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableMilvusQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\n\nimport lombok.Data;\n\n@Data\npublic class TableMilvusQueryRequest extends TableBriefQueryRequest {\n\n    private String apikey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableModifySqlRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * Modify table sql request\n *\n * @author Shi Yi\n */\n@Data\npublic class TableModifySqlRequest extends DataSourceBaseRequest {\n\n    /**\n     * Old table structure\n     * Empty means creating a new table\n     */\n    private TableRequest oldTable;\n\n    /**\n     * new table structure\n     */\n    @NotNull\n    private TableRequest newTable;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableQueryRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.io.Serial;\nimport java.util.List;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : TableColumnQueryRequest.java\n */\n@Data\npublic class TableQueryRequest extends PageQueryRequest implements DataSourceBaseRequestInfo {\n\n    @Serial\n    private static final long serialVersionUID = 5794716286491282784L;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * 修改表sql请求\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableRequest {\n    /**\n     * Table Name\n     */\n    private String name;\n\n    /**\n     * Table description\n     */\n    private String comment;\n\n    /**\n     * Column\n     */\n    private List<TableColumn> columnList;\n\n    /**\n     * index\n     */\n    private List<TableIndex> indexList;\n\n\n    /**\n     * Space name\n     */\n    private String schemaName;\n\n    /**\n     * Database name\n     */\n    private String databaseName;\n\n\n    private String engine;\n\n\n    private String charset;\n\n\n    private String collate;\n\n    private Long incrementValue;\n\n    private String partition;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TableUpdateDdlQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableUpdateDdlQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class TableUpdateDdlQueryRequest {\n\n    /**\n     * DB type\n     * @see ai.chat2db.server.domain.support.enums.DbTypeEnum\n     */\n    @NotNull\n    private String dbType;\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TriggerDetailRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class TriggerDetailRequest implements DataSourceBaseRequestInfo {\n    @Serial\n    private static final long serialVersionUID = -364547173428396332L;\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located is required by pg and oracle, but not by mysql.\n     */\n    private String schemaName;\n\n    /**\n     * trigger name\n     */\n    private String triggerName;\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TriggerPageRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.wrapper.request.PageQueryRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class TriggerPageRequest extends PageQueryRequest implements DataSourceBaseRequestInfo {\n    @Serial\n    private static final long serialVersionUID = -364547173428396332L;\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n    /**\n     * DB name\n     */\n    private String databaseName;\n\n    /**\n     * The space where the table is located is required by pg and oracle, but not by mysql.\n     */\n    private String schemaName;\n\n    /**\n     * Fuzzy search terms\n     */\n    private String searchKey;\n\n    /**\n     * trigger name\n     */\n    private String triggerName;\n\n    /**\n     * if true, refresh the cache\n     */\n    private boolean refresh;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/TypeQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequestInfo;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\n@Data\npublic class TypeQueryRequest implements DataSourceBaseRequestInfo {\n\n    @NotNull\n    private Long dataSourceId;\n    /**\n     * DB name\n     */\n    private String databaseName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/UpdateDatabaseRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : UpdateDatasourceRequest.java\n */\n@Data\npublic class UpdateDatabaseRequest extends DataSourceBaseRequest {\n\n    private String newDatabaseName;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/UpdateSchemaRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.rdb.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : UpdateSchemaRequest.java\n */\n@Data\npublic class UpdateSchemaRequest extends DataSourceBaseRequest {\n\n    private String newSchemaName;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/ColumnVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author moji\n * @version TableVO.java, v 0.1 September 16, 2022 17:16 moji Exp $\n * @date 2022/09/16\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ColumnVO {\n    /**\n     * The old column name, this parameter is needed when modifying the column\n     * oldName=name when returning\n     */\n    private String oldName;\n\n    /**\n     * Column name\n     */\n    private String name;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n\n    /**\n     * Column type\n     * For example, varchar(100), double(10,6)\n     */\n\n    private String columnType;\n\n    /**\n     * Column data type\n     * For example, varchar, double\n     */\n\n    private Integer dataType;\n\n\n    /**\n     * default value\n     */\n\n    private String defaultValue;\n\n    /**\n     * Whether to increase automatically\n     * Empty means there is no value. The actual semantics of the database are false.\n     */\n    private Boolean autoIncrement;\n\n    /**\n     * Comment\n     */\n    private String comment;\n\n    /**\n     * Is it a primary key?\n     */\n    private Boolean primaryKey;\n\n    /**\n     * Space name\n     */\n    private String schemaName;\n\n    /**\n     * Database name\n     */\n    private String databaseName;\n\n    /**\n     *  Data source dependent type name, for a UDT the type name is fully qualified\n     */\n    private String typeName;\n\n    /**\n     * column size.\n     */\n\n    private Integer columnSize;\n\n    /**\n     * is not used.\n     */\n    private Integer bufferLength;\n\n    /**\n     * the number of fractional digits. Null is returned for data types where DECIMAL_DIGITS is not applicable.\n     */\n\n    private Integer decimalDigits;\n\n    /**\n     * Radix (typically either 10 or 2)\n     */\n\n    private Integer numPrecRadix;\n\n    /**\n     * is NULL allowed.\n     * columnNoNulls - might not allow NULL values\n     * columnNullable - definitely allows NULL values\n     * columnNullableUnknown - nullability unknown\n     */\n    private Integer nullableInt;\n\n    /**\n     * unused\n     */\n    private Integer sqlDataType;\n\n\n    /**\n     * unused\n     */\n    private Integer sqlDatetimeSub;\n\n    /**\n     * for char types the maximum number of bytes in the column\n     */\n    private Integer charOctetLength;\n\n    /**\n     * index of column in table (starting at 1)\n     */\n\n    private Integer ordinalPosition;\n\n    /**\n     * ISO rules are used to determine the nullability for a column.\n     */\n\n    private Integer nullable;\n\n    /**\n     * String => Indicates whether this is a generated column\n     *      * YES --- if this a generated column\n     *      * NO --- if this not a generated column\n     */\n    private Boolean generatedColumn;\n\n\n    private String extent;\n\n\n    private String editStatus;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/ExecuteResultVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\nimport java.util.List;\n\n\nimport ai.chat2db.spi.model.Header;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ExecuteResultVO.java, v 0.1 October 23, 2022 11:20 moji Exp $\n * @date 2022/10/23\n */\n@Data\npublic class ExecuteResultVO {\n\n    /**\n     * executed sql\n     */\n    private String sql;\n\n    /**\n     * Original SQL without pagination\n     */\n    private String originalSql;\n\n    /**\n     * description\n     */\n    private String description;\n\n    /**\n     * Failure message prompt\n     */\n    private String message;\n\n    /**\n     * success flag\n     */\n    private Boolean success;\n\n    /**\n     * Modify the number of rows and query sql will not return\n     */\n    private Integer updateCount;\n\n    /**\n     * List of display headers\n     */\n    private List<Header> headerList;\n\n    /**\n     * list of data\n     */\n    private List<List<String>> dataList;\n\n    /**\n     * sql type\n     *\n     */\n    private String sqlType;\n\n    /**\n     * Whether there is a next page\n     * Only available for select statements\n     */\n    private Boolean hasNextPage;\n\n    /**\n     * Page coding\n     * Only available for select statements\n     */\n    private Integer pageNo;\n\n    /**\n     * Paging Size\n     * Only available for select statements\n     */\n    private Integer pageSize;\n\n    /**\n     * Total number of fuzzy rows\n     * Only select statements have\n     */\n    private String fuzzyTotal;\n\n    /**\n     * execution duration\n     */\n    private Long duration;\n\n    /**\n     * Whether the returned result can be edited\n     */\n    private boolean canEdit;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/IndexColumnVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Column information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class IndexColumnVO {\n\n    /**\n     * Index name\n     */\n    private String indexName;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n\n    /**\n     * index type\n     *\n     * @see\n     */\n    private String type;\n\n    /**\n     * Comment\n     */\n    private String comment;\n\n    /**\n     * Column name\n     */\n    private String columnName;\n\n    /**\n     * order\n     */\n    private Short ordinalPosition;\n\n    /**\n     * sort\n     *\n     */\n    private String collation;\n\n\n    /**\n     * The schema to which the index belongs\n     */\n    private String schemaName;\n\n    /**\n     * Database name\n     */\n    private String databaseName;\n\n    /**\n     * Is it unique?\n     */\n    private Boolean nonUnique;\n\n    /**\n     *  index catalog (may be null); null when TYPE is tableIndexStatistic\n     */\n    private String indexQualifier;\n\n    /**\n     * ASC_OR_DESC String => column sort sequence, \"A\" => ascending, \"D\" => descending, may be null if sort sequence is not supported; null when TYPE is tableIndexStatistic\n     */\n    private String ascOrDesc;\n\n    /**\n     * CARDINALITY long => When TYPE is tableIndexStatistic, then this is the number of rows in the table; otherwise, it is the number of unique values in the index.\n     */\n    private Long cardinality;\n\n    /**\n     * When TYPE is tableIndexStatistic then this is the number of pages used for the table, otherwise it is the number of pages used for the current index.\n     */\n    private Long pages;\n\n    /**\n     * Filter condition, if any. (may be null)\n     */\n    private String filterCondition;\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/IndexVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.enums.IndexTypeEnum;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version IndexVO.java, v 0.1 September 16, 2022 17:47 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class IndexVO {\n\n    /**\n     * Contains columns\n     */\n    private String columns;\n\n    /**\n     * Index name\n     */\n    private String name;\n\n    /**\n     * all types\n     *\n     * @see IndexTypeEnum\n     */\n    private String type;\n\n    /**\n     * Comment\n     */\n    private String comment;\n\n    /**\n     * Columns included in the index\n     */\n    private List<IndexColumnVO> columnList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/KeyVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version IndexVO.java, v 0.1 September 16, 2022 17:47 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class KeyVO {\n\n    /**\n     * Contains columns\n     */\n    private String columns;\n\n    /**\n     * Index name\n     */\n    private String name;\n\n    /**\n     * Comment\n     */\n    private String comment;\n\n    /**\n     * Columns included in the index\n     */\n    private List<ColumnVO> columnList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/MetaSchemaVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.Schema;\nimport lombok.Data;\n\nimport java.util.List;\n@Data\npublic class MetaSchemaVO {\n    /**\n     * database list\n     */\n    private List<Database> databases;\n\n    /**\n     * schema list\n     */\n    private List<Schema> schemas;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/SchemaVO.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.rdb.vo;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : SchemaVO.java\n */\n@Data\npublic class SchemaVO {\n    /**\n     * Data name\n     */\n    private String name;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/SequenceVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\n\nimport lombok.Data;\n\n/**\n * Sequence detail VO\n *\n * @author Sylphy\n */\n@Data\npublic class SequenceVO {\n    /**\n     * Schema name\n     */\n    private String nspname;\n    /**\n     * Sequence name\n     */\n    private String relname;\n    /**\n     * Sequence data type\n     */\n    private String typname;\n    /**\n     * Sequence cache\n     */\n    private String seqcache;\n    /**\n     * Sequence owner\n     */\n    private String rolname;\n    /**\n     * Sequence comment\n     */\n    private String comment;\n    /**\n     * Sequence start value\n     */\n    private String seqstart;\n    /**\n     * Sequence step value\n     */\n    private String seqincrement;\n    /**\n     * Sequence max value\n     */\n    private String seqmax;\n    /**\n     * Sequence min value\n     */\n    private String seqmin;\n    /**\n     * Sequence cycle\n     */\n    private Boolean seqcycle;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/SqlVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * sql object\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SqlVO {\n\n    /**\n     * sql\n     */\n    private String sql;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/TableVO.java",
    "content": "package ai.chat2db.server.web.api.controller.rdb.vo;\n\nimport java.util.List;\n\nimport ai.chat2db.spi.model.TableColumn;\nimport ai.chat2db.spi.model.TableIndex;\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableVO.java, v 0.1 September 16, 2022 17:16 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class TableVO {\n\n    /**\n     * Table Name\n     */\n    private String name;\n\n    /**\n     * Table description\n     */\n    private String comment;\n\n    /**\n     * Column\n     */\n    private List<TableColumn> columnList;\n\n    /**\n     * index\n     */\n    private List<TableIndex> indexList;\n\n    /**\n     * Has it been fixed?\n     */\n    private boolean pinned;\n\n    /**\n     * ddl\n     */\n    private String ddl;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/ViewVO.java",
    "content": "//package ai.chat2db.server.web.api.controller.rdb.vo;\n//\n//public class ViewVO extends TableVO{\n//\n//    /**\n//     * view script\n//     */\n//    private String script;\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/RedisKeyManageController.java",
    "content": "package ai.chat2db.server.web.api.controller.redis;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.web.api.controller.redis.request.KeyCreateRequest;\nimport ai.chat2db.server.web.api.controller.redis.request.KeyDeleteRequest;\nimport ai.chat2db.server.web.api.controller.redis.request.KeyQueryRequest;\nimport ai.chat2db.server.web.api.controller.redis.request.KeyUpdateRequest;\nimport ai.chat2db.server.web.api.controller.redis.vo.KeyVO;\n\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * redis key operation and maintenance class\n *\n * @author moji\n * @version MysqlTableManageController.java, v 0.1 September 16, 2022 17:41 moji Exp $\n * @date 2022/09/16\n */\n@RequestMapping(\"/api/redis/key\")\n@RestController\npublic class RedisKeyManageController {\n\n    /**\n     * Query the key list under the current DB\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/list\")\n    public ListResult<KeyVO> list(KeyQueryRequest request) {\n        return null;\n    }\n\n    /**\n     * Add Key\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/create\")\n    public ActionResult create(@RequestBody KeyCreateRequest request) {\n        return null;\n    }\n\n    /**\n     * Modify key information\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/update\",method = {RequestMethod.POST, RequestMethod.PUT})\n    public ActionResult update(@RequestBody KeyUpdateRequest request) {\n        return null;\n    }\n\n\n    /**\n     * Delete key\n     *\n     * @param request\n     * @return\n     */\n    @DeleteMapping(\"/delete\")\n    public ActionResult delete(@RequestBody KeyDeleteRequest request) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/RedisKeyValueManageController.java",
    "content": "package ai.chat2db.server.web.api.controller.redis;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.web.api.controller.redis.request.KeyQueryRequest;\nimport ai.chat2db.server.web.api.controller.redis.request.KeyValueManageRequest;\nimport ai.chat2db.server.web.api.controller.redis.request.ValueUpdateRequest;\nimport ai.chat2db.server.web.api.controller.redis.vo.KeyVO;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * redis data operation and maintenance class\n *\n * @author moji\n * @version MysqlDataManageController.java, v 0.1 September 16, 2022 17:37 moji Exp $\n * @date 2022/09/16\n */\n@RequestMapping(\"/api/redis/kv\")\n@RestController\npublic class RedisKeyValueManageController {\n\n    /**\n     * redis ddl command execution\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/manage\",method = {RequestMethod.POST, RequestMethod.PUT})\n    public DataResult<Object> manage(@RequestBody KeyValueManageRequest request) {\n        return null;\n    }\n\n    /**\n     * Get cache key details\n     *\n     * @param request\n     * @return\n     */\n    @GetMapping(\"/query\")\n    public DataResult<KeyVO> query(KeyQueryRequest request) {\n        return null;\n    }\n\n    /**\n     * Update key value\n     *\n     * @param request\n     * @return\n     */\n    @RequestMapping(value = \"/update\",method = {RequestMethod.POST, RequestMethod.PUT})\n    public ActionResult update(@RequestBody ValueUpdateRequest request) {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/request/KeyCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.redis.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableVO.java, v 0.1 September 16, 2022 17:16 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class KeyCreateRequest extends DataSourceBaseRequest {\n\n    /**\n     * key name\n     */\n    @NotNull\n    private String name;\n\n    /**\n     * key value\n     */\n    private Object value;\n\n    /**\n     * Expiration\n     */\n    private Long ttl;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/request/KeyDeleteRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.redis.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class KeyDeleteRequest extends DataSourceBaseRequest {\n\n    /**\n     * key name\n     */\n    private String keyName;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/request/KeyQueryRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.redis.request;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class KeyQueryRequest extends DataSourceBaseRequest {\n\n    /**\n     * Cache key name\n     */\n    private String keyName;\n\n    /**\n     * search keyword\n     */\n    private String searchKey;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/request/KeyUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.redis.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class KeyUpdateRequest extends DataSourceBaseRequest {\n\n    /**\n     * key name\n     */\n    @NotNull\n    private String originalKey;\n\n    /**\n     * Key name after update\n     */\n    private String updateKey;\n\n    /**\n     * Original ttl value\n     */\n    private Long originalTtl;\n\n    /**\n     * ttl value after update\n     */\n    private Object updateTtl;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/request/KeyValueManageRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.redis.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version TableManageRequest.java, v 0.1 September 16, 2022 17:55 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class KeyValueManageRequest extends DataSourceBaseRequest {\n\n    /**\n     * redis ddl statement\n     */\n    @NotNull\n    private String ddl;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/request/ValueUpdateRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.redis.request;\n\nimport jakarta.validation.constraints.NotNull;\n\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\nimport ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version ConnectionQueryRequest.java, v 0.1 September 16, 2022 14:23 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class ValueUpdateRequest extends DataSourceBaseRequest {\n\n    /**\n     * key name\n     */\n    @NotNull\n    private String key;\n\n    /**\n     * Original key value\n     */\n    @NotNull\n    private Object originalValue;\n\n    /**\n     * Key value after update\n     */\n    @NotNull\n    private Object updateValue;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/redis/vo/KeyVO.java",
    "content": "package ai.chat2db.server.web.api.controller.redis.vo;\n\nimport lombok.Data;\n\n/**\n * @author moji\n * @version Table.java, v 0.1 September 16, 2022 17:16 moji Exp $\n * @date 2022/09/16\n */\n@Data\npublic class KeyVO {\n\n    /**\n     * key name\n     */\n    private String name;\n\n    /**\n     * key value\n     */\n    private Object value;\n\n    /**\n     * key type\n     */\n    private String type;\n\n    /**\n     * Expiration\n     */\n    private Long ttl;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/sql/SqlController.java",
    "content": "package ai.chat2db.server.web.api.controller.sql;\n\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.sql.request.SqlFormatRequest;\nimport com.github.vertical_blank.sqlformatter.SqlFormatter;\nimport jakarta.validation.Valid;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * SQL Controller\n */\n@ConnectionInfoAspect\n@RequestMapping(\"/api/sql\")\n@RestController\npublic class SqlController {\n\n    /**\n     * SQL Format\n     *\n     * @param sqlFormatRequest\n     * @return\n     */\n    @GetMapping(\"/format\")\n    public DataResult<String> list(@Valid SqlFormatRequest sqlFormatRequest) {\n        String sql = sqlFormatRequest.getSql();\n        try {\n            sql = SqlFormatter.format(sql);\n        } catch (Exception e) {\n            // ignore\n        }\n        return DataResult.of(sql);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/sql/request/SqlFormatRequest.java",
    "content": "package ai.chat2db.server.web.api.controller.sql.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SqlFormatRequest {\n\n    private String sql;\n\n    private String dbType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/SystemController.java",
    "content": "/**\n * Alipay.com Inc.\n * Copyright (c) 2004-2022 All Rights Reserved.\n */\npackage ai.chat2db.server.web.api.controller.system;\n\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.param.SystemConfigParam;\nimport ai.chat2db.server.domain.api.service.ConfigService;\nimport ai.chat2db.server.domain.core.cache.CacheManage;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.config.Chat2dbProperties;\nimport ai.chat2db.server.tools.common.enums.ModeEnum;\nimport ai.chat2db.server.tools.common.model.ConfigJson;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport ai.chat2db.server.tools.common.util.EasyEnumUtils;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;\nimport ai.chat2db.server.web.api.controller.system.util.SystemUtils;\nimport ai.chat2db.server.web.api.controller.system.vo.AppVersionVO;\nimport ai.chat2db.server.web.api.controller.system.vo.SystemVO;\nimport ai.chat2db.spi.ssh.SSHManager;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.web.bind.annotation.*;\n\n\n/**\n * @author jipengfei\n * @version : HomeController.java, v 0.1 September 18, 2022 14:52 jipengfei Exp $\n */\n@RestController\n@RequestMapping(\"/api/system\")\n@Slf4j\npublic class SystemController {\n\n    @Autowired\n    private ApplicationContext applicationContext;\n\n    @Autowired\n    private Chat2dbProperties chat2dbProperties;\n\n    @Autowired\n    private ConfigService configService;\n\n    /**\n     * Check if the test is successful\n     *\n     * @return\n     */\n    @GetMapping\n    public DataResult<SystemVO> get() {\n        String clientVersion = System.getProperty(\"client.version\");\n        String version = ConfigUtils.getLatestLocalVersion();\n        log.error(\"clientVersion:{},version:{}\", clientVersion, version);\n        if (!StringUtils.equals(clientVersion, version) && !StringUtils.isEmpty(clientVersion)) {\n            stop();\n            return null;\n        } else {\n            ConfigJson configJson = ConfigUtils.getConfig();\n            return DataResult.of(SystemVO.builder()\n                    .systemUuid(configJson.getSystemUuid())\n                    .build());\n        }\n    }\n\n    private static final String UPDATE_TYPE = \"client_update_type\";\n\n    @GetMapping(\"/get_latest_version\")\n    public DataResult<AppVersionVO> getLatestVersion(String currentVersion) {\n        ModeEnum mode = EasyEnumUtils.getEnum(ModeEnum.class, System.getProperty(\"chat2db.mode\"));\n        if (mode != ModeEnum.DESKTOP) {\n            // In this mode, no user login is required, so only local access is available\n            return DataResult.of(null);\n        }\n        String user = \"\";\n        DataResult<Config> dataResult = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY);\n        if (dataResult.getData() != null) {\n            user = dataResult.getData().getContent();\n        }\n        AppVersionVO appVersionVO = SystemUtils.getLatestVersion(currentVersion, \"manual\", user);\n        if (appVersionVO == null) {\n            appVersionVO = new AppVersionVO();\n            appVersionVO.setVersion(currentVersion);\n            appVersionVO.setType(\"manual\");\n        }\n        DataResult<Config> updateType = configService.find(UPDATE_TYPE);\n        if (updateType.getData() != null) {\n            appVersionVO.setType(updateType.getData().getContent());\n        }\n        // In this mode, no user login is required, so only local access is available\n        appVersionVO.setDesktop(true);\n        return DataResult.of(appVersionVO);\n    }\n\n    @PostMapping(\"/update_desktop_version\")\n    public DataResult<String> updateDesktopVersion(@RequestBody AppVersionVO version) {\n        new Thread(() -> {\n            SystemUtils.upgrade(version);\n        }).start();\n        return DataResult.of(version.getVersion());\n    }\n\n    @GetMapping(\"/is_update_success\")\n    public DataResult<Boolean> isUpdateSuccess(String version) {\n        String localVersion = ConfigUtils.getLocalVersion();\n        if (StringUtils.isEmpty(localVersion)) {\n            return DataResult.of(false);\n        }\n        return DataResult.of(localVersion.equals(version));\n    }\n\n    @PostMapping(\"/set_update_type\")\n    public ActionResult setUpdateType(@RequestBody String updateType) {\n        SystemConfigParam systemConfigParam = new SystemConfigParam();\n        systemConfigParam.setCode(UPDATE_TYPE);\n        systemConfigParam.setContent(updateType);\n        systemConfigParam.setSummary(\"client update type\");\n        configService.createOrUpdate(systemConfigParam);\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * Get the current version number\n     *\n     * @return\n     */\n    @GetMapping(\"/get-version-a\")\n    public DataResult<String> getVersion() {\n        return DataResult.of(chat2dbProperties.getVersion());\n    }\n\n    /**\n     * Exit service\n     */\n    @RequestMapping(\"/stop\")\n    public DataResult<String> stop(boolean forceQuit) {\n        log.info(\"Exit application\");\n        if (forceQuit) {\n            stop();\n        } else {\n//            String clientVersion = System.getProperty(\"client.version\");\n//            String version = ConfigUtils.getLatestLocalVersion();\n//            log.error(\"clientVersion:{},version:{}\", clientVersion, version);\n//            if (!StringUtils.equals(clientVersion, version)) {\n            stop();\n            //}\n        }\n        return DataResult.of(\"ok\");\n    }\n\n    private void stop() {\n        new Thread(() -> {\n            //  Will exit the background after 100ms\n            try {\n                Thread.sleep(200L);\n            } catch (InterruptedException e) {\n                throw new RuntimeException(e);\n            }\n            log.info(\"Start exiting Spring application\");\n            SSHManager.close();\n            try {\n                SpringApplication.exit(applicationContext);\n            } catch (Exception ignore) {\n            }\n            // It is possible that SpringApplication.exit will fail to exit\n            // Direct system exit\n            log.info(\"Start exiting system applications\");\n            CacheManage.close();\n            try {\n                System.exit(0);\n            } catch (Exception ignore) {\n            }\n\n        }).start();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/util/SystemUtils.java",
    "content": "package ai.chat2db.server.web.api.controller.system.util;\n\n\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport ai.chat2db.server.web.api.controller.system.vo.AppVersionVO;\nimport ai.chat2db.spi.ssh.SSHManager;\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.util.ZipUtil;\nimport cn.hutool.http.HttpUtil;\nimport com.dtflys.forest.Forest;\nimport com.dtflys.forest.utils.TypeReference;\nimport lombok.extern.slf4j.Slf4j;\nimport okhttp3.OkHttpClient;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.File;\nimport java.time.Duration;\n\n/**\n * System Toolkit\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class SystemUtils {\n\n    /**\n     * Stop current application\n     */\n    public static void stop() {\n        new Thread(() -> {\n            log.info(\"Exit the application after 1 second\");\n            // Automatically exit the application after 1 second\n            try {\n                Thread.sleep(1000L);\n            } catch (InterruptedException e) {\n                throw new RuntimeException(e);\n            }\n            // Direct system exit\n            log.info(\"Start exiting system applications\");\n            SSHManager.close();\n            try {\n                System.exit(0);\n            } catch (Exception ignore) {\n            }\n        }).start();\n    }\n\n    private static final OkHttpClient client = new OkHttpClient();\n\n    private static final String VERSION_URL = \"https://sqlgpt.cn/api/version.json\";\n\n    private static final String ZIP_FILE_PATH = ConfigUtils.APP_PATH + File.separator + \"versions\" + File.separator;\n\n    public static void upgrade(AppVersionVO appVersion) {\n\n        String appPath = ConfigUtils.APP_PATH;\n\n        log.info(\"appPath: {}\", appPath);\n        if (StringUtils.isBlank(appPath) || !appPath.contains(\"app\")) {\n            return;\n        }\n        try {\n            String zipPath = ZIP_FILE_PATH + appVersion.getVersion() + \".zip\";\n\n            HttpUtil.downloadFile(appVersion.getHotUpgradeUrl(), ZIP_FILE_PATH + appVersion.getVersion() + \".zip\");\n\n            ZipUtil.unzip(zipPath);\n\n            FileUtil.del(zipPath);\n\n            ConfigUtils.updateVersion(appVersion.getVersion());\n        } catch (Exception e) {\n            log.error(\"checkVersionUpdates error\", e);\n        }\n    }\n\n    private static final String LATEST_VERSION_URL = \"http://test.sqlgpt.cn/gateway/api/client/version/check/v3?version=%s&type=%s&userId=%s\";\n\n    public static AppVersionVO getLatestVersion(String version, String type, String userId) {\n        String url = String.format(LATEST_VERSION_URL, version, type, userId);\n        DataResult<AppVersionVO> result = Forest.get(url)\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .execute(new TypeReference<>() {\n                });\n        return result.getData();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/vo/AppVersionVO.java",
    "content": "package ai.chat2db.server.web.api.controller.system.vo;\n\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\n\n@Data\npublic class AppVersionVO {\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * new version\n     */\n    private String version;\n\n//    /**\n//     * Which versions can be upgraded to this version\n//     */\n//    private String versionUse;\n\n//    /**\n//     * state\n//     */\n//    private String status;\n\n    /**\n     * downloadLink\n     */\n    private String downloadLink;\n\n    /**\n     * Manual update, automatic forced update\n     */\n    private String type;\n\n//    /**\n//     * Whitelist, for testing\n//     */\n//    private String whiteList;\n\n    /**\n     * Hot update package address\n     */\n    private String hotUpgradeUrl;\n\n    /**\n     * updateLog\n     */\n    private String updateLog;\n\n    /**\n     * desktop\n     */\n    private boolean desktop;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/vo/SystemVO.java",
    "content": "package ai.chat2db.server.web.api.controller.system.vo;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * system\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SystemVO {\n    /**\n     * The unique ID of the  system\n     */\n    private String systemUuid;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/task/ExportController.java",
    "content": "package ai.chat2db.server.web.api.controller.task;\n\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;\nimport ai.chat2db.server.web.api.controller.task.biz.TaskBizService;\nimport jakarta.validation.Valid;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@ConnectionInfoAspect\n@RequestMapping(\"/api/export\")\n@Controller\n@Slf4j\npublic class ExportController {\n\n    @Autowired\n    private TaskBizService taskBizService;\n\n\n    /**\n     * export data\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"/export_data\")\n    public DataResult<Long> export(@Valid @RequestBody DataExportRequest request) {\n        return taskBizService.exportResultData(request);\n    }\n\n    @PostMapping(\"/export_doc\")\n    public DataResult<Long> exportDoc(@Valid @RequestBody DataExportRequest request) {\n        return taskBizService.exportSchemaDoc(request);\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/task/TaskController.java",
    "content": "package ai.chat2db.server.web.api.controller.task;\n\nimport ai.chat2db.server.domain.api.model.Task;\nimport ai.chat2db.server.domain.api.param.TaskPageParam;\nimport ai.chat2db.server.domain.api.service.TaskService;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.base.wrapper.result.web.WebPageResult;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;\nimport jakarta.servlet.http.HttpServletResponse;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\nimport java.io.*;\n\n@ConnectionInfoAspect\n@RequestMapping(\"/api/task\")\n@Controller\n@Slf4j\npublic class TaskController {\n\n    @Autowired\n    private TaskService taskService;\n\n\n    @GetMapping(\"/list\")\n    public WebPageResult<Task> list() {\n        TaskPageParam taskPageParam = new TaskPageParam();\n        taskPageParam.setPageNo(1);\n        taskPageParam.setPageSize(10);\n        taskPageParam.setUserId(ContextUtils.getUserId());\n        PageResult<Task> task = taskService.page(taskPageParam);\n        return WebPageResult.of(task.getData(), 100L, 1, 10);\n    }\n\n    @GetMapping(\"/download/{id}\")\n    public void download(@PathVariable Long id, HttpServletResponse response) {\n        DataResult<Task> task = taskService.get(id);\n        Task data = task.getData();\n        if (data == null) {\n            log.error(\"task is null\");\n            throw new RuntimeException(\"task is null\");\n        }\n        if (!ContextUtils.getUserId().equals(data.getUserId())) {\n            log.error(\"task is not belong to user\");\n            throw new RuntimeException(\"task is not belong to user\");\n        }\n\n        File file = new File(data.getDownloadUrl());\n\n        if (!file.exists() || !file.isFile()) {\n            log.error(\"File not found or is not a file: {}\", file.getAbsolutePath());\n            throw new RuntimeException(\"File not found or accessible\");\n        }\n\n\n        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, \"attachment; filename=\\\"\" + file.getName() + \"\\\"\");\n        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);\n\n        try (InputStream inputStream = new FileInputStream(file)) {\n            OutputStream outputStream = response.getOutputStream();\n            byte[] buffer = new byte[8192];\n            int bytesRead;\n            while ((bytesRead = inputStream.read(buffer)) != -1) {\n                outputStream.write(buffer, 0, bytesRead);\n                outputStream.flush();\n            }\n        } catch (IOException e) {\n            log.error(\"Error occurred while processing file download\", e);\n            throw new RuntimeException(\"Error in file download\", e);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/task/biz/TaskBizService.java",
    "content": "package ai.chat2db.server.web.api.controller.task.biz;\n\nimport ai.chat2db.server.domain.api.enums.*;\nimport ai.chat2db.server.domain.api.param.*;\nimport ai.chat2db.server.domain.api.service.TableService;\nimport ai.chat2db.server.domain.api.service.TaskService;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport ai.chat2db.server.web.api.controller.rdb.RdbDmlExportController;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;\nimport ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;\nimport ai.chat2db.server.web.api.controller.rdb.factory.ExportServiceFactory;\nimport ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;\nimport ai.chat2db.server.web.api.controller.rdb.vo.TableVO;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.JdbcUtils;\nimport ai.chat2db.spi.util.SqlUtils;\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.core.lang.Assert;\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.visitor.VisitorFeature;\nimport com.alibaba.excel.EasyExcel;\nimport com.alibaba.excel.ExcelWriter;\nimport com.alibaba.excel.support.ExcelTypeEnum;\nimport com.alibaba.excel.write.builder.ExcelWriterBuilder;\nimport com.alibaba.excel.write.metadata.WriteSheet;\nimport com.google.common.collect.Lists;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.lang.reflect.Constructor;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.LocalDateTime;\nimport java.util.List;\nimport java.util.concurrent.CompletableFuture;\n\n@Slf4j\n@Component\npublic class TaskBizService {\n\n    /**\n     * Format insert statement\n     */\n    private static final SQLUtils.FormatOption INSERT_FORMAT_OPTION = new SQLUtils.FormatOption(true, false);\n\n    static {\n        INSERT_FORMAT_OPTION.config(VisitorFeature.OutputNameQuote, true);\n    }\n\n\n    @Autowired\n    private TaskService taskService;\n\n\n    @Autowired\n    private TableService tableService;\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    public DataResult<Long> exportResultData(DataExportRequest request) {\n        String sql = ExportSizeEnum.CURRENT_PAGE.getCode().equals(request.getExportSize()) ? request.getSql() : request.getOriginalSql();\n        Assert.notBlank(sql, \"dataSource.sqlEmpty\");\n        DbType dbType = JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());\n        String tableName = getTableName(request, sql, dbType);\n        File file = createTempFile(tableName, request.getExportType());\n        DataResult<Long> dataResult = createTask(tableName, request.getDatabaseName(), request.getSchemaName(), request.getDataSourceId(), tableName);\n\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo().copy();\n        CompletableFuture.runAsync(() -> {\n            buildContext(loginUser, connectInfo);\n            doExport(sql, file, dbType, tableName, request.getExportType());\n        }).whenComplete((aVoid, throwable) -> {\n            updateStatus(dataResult.getData(), file, throwable);\n            removeContext();\n        });\n        return dataResult;\n    }\n\n    public DataResult<Long> exportSchemaDoc(DataExportRequest request) {\n        File file = createTempFile(request.getDatabaseName(), request.getExportType());\n        DataResult<Long> dataResult = createTask(null, request.getDatabaseName(), request.getSchemaName(), request.getDataSourceId(), \"schema_doc\");\n        LoginUser loginUser = ContextUtils.getLoginUser();\n        ConnectInfo connectInfo = Chat2DBContext.getConnectInfo().copy();\n        CompletableFuture.runAsync(() -> {\n            buildContext(loginUser, connectInfo);\n            doExportDoc(request, file);\n        }).whenComplete((aVoid, throwable) -> {\n            updateStatus(dataResult.getData(), file, throwable);\n            removeContext();\n        });\n        return dataResult;\n    }\n\n    private void doExportDoc(DataExportRequest request, File file) {\n        try {\n            TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);\n            queryParam.setPageNo(1);\n            queryParam.setPageSize(Integer.MAX_VALUE);\n            TableSelector tableSelector = new TableSelector();\n            tableSelector.setColumnList(true);\n            tableSelector.setIndexList(true);\n            PageResult<Table> tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);\n            List<TableVO> tableVOS = rdbWebConverter.tableDto2vo(tableDTOPageResult.getData());\n            TableQueryParam param = rdbWebConverter.tableRequest2param(request);\n            for (TableVO tableVO : tableVOS) {\n                param.setTableName(tableVO.getName());\n                tableVO.setColumnList(tableService.queryColumns(param));\n                tableVO.setIndexList(tableService.queryIndexes(param));\n            }\n            Class<?> targetClass = ExportServiceFactory.get(request.getExportType());\n            Constructor<?> constructor = targetClass.getDeclaredConstructor();\n            DatabaseExportService databaseExportService = (DatabaseExportService) constructor.newInstance();\n            // Set up data collection\n            databaseExportService.setExportList(tableVOS);\n            databaseExportService.generate(request.getDatabaseName(), new FileOutputStream(file), new ExportOptions());\n        } catch (Exception e) {\n            log.error(\"export error\", e);\n            throw new BusinessException(\"dataSource.exportError\");\n        }\n    }\n\n\n    private void removeContext() {\n        Dbutils.removeSession();\n        ContextUtils.removeContext();\n        Chat2DBContext.removeContext();\n    }\n\n    private void buildContext(LoginUser loginUser, ConnectInfo connectInfo) {\n        ContextUtils.setContext(Context.builder()\n                .loginUser(loginUser)\n                .build());\n        Dbutils.setSession();\n        Chat2DBContext.putContext(connectInfo);\n    }\n\n    private DataResult<Long> createTask(String tableName, String databaseName, String schemaName, Long datasourceId, String taskName) {\n        TaskCreateParam param = new TaskCreateParam();\n        param.setTaskName(\"export_\" + taskName);\n        param.setTaskType(TaskTypeEnum.DOWNLOAD_TABLE_DATA.name());\n        param.setDatabaseName(databaseName);\n        param.setSchemaName(schemaName);\n        param.setTableName(tableName);\n        param.setDataSourceId(datasourceId);\n        param.setUserId(ContextUtils.getUserId());\n        param.setTaskProgress(\"0.1\");\n        return taskService.create(param);\n    }\n\n    private void updateStatus(Long id, File file, Throwable throwable) {\n        TaskUpdateParam updateParam = new TaskUpdateParam();\n        updateParam.setId(id);\n        updateParam.setTaskProgress(\"1\");\n        updateParam.setDownloadUrl(file.getAbsolutePath());\n        if (throwable != null) {\n            log.error(\"export error\", throwable);\n            updateParam.setTaskStatus(TaskStatusEnum.ERROR.name());\n        } else {\n            updateParam.setTaskStatus(TaskStatusEnum.FINISH.name());\n        }\n        taskService.updateStatus(updateParam);\n    }\n\n    private void doExport(String sql, File file, DbType dbType, String tableName, String exportType) {\n        try {\n            if (ExportTypeEnum.CSV.getCode().equals(exportType)) {\n                doExportCsv(sql, file);\n            } else {\n                doExportInsert(sql, file, dbType, tableName);\n            }\n        } catch (Exception e) {\n            log.error(\"export error\", e);\n            throw new BusinessException(\"dataSource.exportError\");\n        }\n    }\n\n\n    private File createTempFile(String tableName, String exportType) {\n        String fileName = URLEncoder.encode(\n                        tableName + \"_\" + LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER),\n                        StandardCharsets.UTF_8)\n                .replaceAll(\"\\\\+\", \"%20\");\n\n        if (ExportTypeEnum.CSV.getCode().equals(exportType)) {\n            return FileUtil.createTempFile(fileName, \".csv\", true);\n        } else if (ExportTypeEnum.INSERT.getCode().equals(exportType)) {\n            return FileUtil.createTempFile(fileName, \".sql\", true);\n        } else if (ExportTypeEnum.EXCEL.getCode().equals(exportType)) {\n            return FileUtil.createTempFile(fileName, ExportFileSuffix.EXCEL.getSuffix(), true);\n        } else if (ExportTypeEnum.MARKDOWN.getCode().equals(exportType)) {\n            return FileUtil.createTempFile(fileName, ExportFileSuffix.MARKDOWN.getSuffix(), true);\n        } else if (ExportTypeEnum.WORD.getCode().equals(exportType)) {\n            return FileUtil.createTempFile(fileName, ExportFileSuffix.WORD.getSuffix(), true);\n        } else if (ExportTypeEnum.PDF.getCode().equals(exportType)) {\n            return FileUtil.createTempFile(fileName, ExportFileSuffix.PDF.getSuffix(), true);\n        } else if (ExportTypeEnum.HTML.getCode().equals(exportType)) {\n            return FileUtil.createTempFile(fileName, ExportFileSuffix.HTML.getSuffix(), true);\n        }\n        return FileUtil.createTempFile(fileName, \".txt\", true);\n    }\n\n    private String getTableName(DataExportRequest request, String sql, DbType dbType) {\n        String tableName = null;\n        if (dbType != null) {\n            SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);\n            if (!(sqlStatement instanceof SQLSelectStatement)) {\n                throw new BusinessException(\"dataSource.sqlAnalysisError\");\n            }\n            tableName = SqlUtils.getTableName(sql, dbType);\n        } else {\n            tableName = StringUtils.join(Lists.newArrayList(request.getDatabaseName(), request.getSchemaName()), \"_\");\n        }\n        return tableName;\n    }\n\n    private void doExportCsv(String sql, File file) {\n        RdbDmlExportController.ExcelWrapper excelWrapper = new RdbDmlExportController.ExcelWrapper();\n        try {\n            ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(file)\n                    .charset(StandardCharsets.UTF_8)\n                    .excelType(ExcelTypeEnum.CSV);\n            excelWrapper.setExcelWriterBuilder(excelWriterBuilder);\n            ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n            SQLExecutor.getInstance().execute(Chat2DBContext.getConnection(), sql, headerList -> {\n                excelWriterBuilder.head(\n                        EasyCollectionUtils.toList(headerList, header -> Lists.newArrayList(header.getName())));\n                excelWrapper.setExcelWriter(excelWriterBuilder.build());\n                excelWrapper.setWriteSheet(EasyExcel.writerSheet(0).build());\n            }, dataList -> {\n                List<List<String>> writeDataList = Lists.newArrayList();\n                writeDataList.add(dataList);\n                excelWrapper.getExcelWriter().write(writeDataList, excelWrapper.getWriteSheet());\n            }, jdbcDataValue -> valueProcessor.getJdbcValue(jdbcDataValue),false);\n        } finally {\n            if (excelWrapper.getExcelWriter() != null) {\n                excelWrapper.getExcelWriter().finish();\n            }\n        }\n    }\n\n    private void doExportInsert(String sql, File file, DbType dbType,\n                                String tableName)\n            throws IOException {\n        try (PrintWriter printWriter = new PrintWriter(file, StandardCharsets.UTF_8.name())) {\n            RdbDmlExportController.InsertWrapper insertWrapper = new RdbDmlExportController.InsertWrapper();\n            ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n            SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();\n            String databaseName = Chat2DBContext.getConnectInfo().getDatabaseName();\n            String schemaName = Chat2DBContext.getConnectInfo().getSchemaName();\n            List<String> headerColumns = Lists.newArrayList();\n            SQLExecutor.getInstance().execute(Chat2DBContext.getConnection(), sql,\n                    headerList -> {\n                        headerList.forEach(header -> headerColumns.add(header.getName()));\n                    }\n                    , dataList -> {\n                        String insertSql = sqlBuilder.buildSingleInsertSql(databaseName, schemaName, tableName, headerColumns, dataList);\n                        printWriter.println(insertSql + \";\");\n                    }, jdbcDataValue -> valueProcessor.getJdbcSqlValueString(jdbcDataValue), false);\n        }\n    }\n\n    @Data\n    @SuperBuilder\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class InsertWrapper {\n        private List<SQLIdentifierExpr> headerList;\n    }\n\n    @Data\n    @SuperBuilder\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class ExcelWrapper {\n        private ExcelWriterBuilder excelWriterBuilder;\n        private ExcelWriter excelWriter;\n        private WriteSheet writeSheet;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/user/UserController.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.user;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author jipengfei\n * @version : UserController.java\n */\n@RequestMapping(\"/api/user\")\n@RestController\npublic class UserController {\n    //\n    //@Autowired\n    //private UserService userService;\n    //\n    //@Autowired\n    //private UserWebConverter userWebConverter;\n    //\n    //@GetMapping(\"/{id}\")\n    //public DataResult<UserVO> query(@PathVariable(\"id\") Long id) {\n    //    return DataResult.of(userWebConverter.dto2vo(userService.query(id).getData()));\n    //}\n    //\n    //@GetMapping(\"/list\")\n    //public WebPageResult<UserVO> list(UserQueryRequest request) {\n    //    UserQueryParam userQueryParam = new UserQueryParam();\n    //    userQueryParam.setKeyWord(request.getKeyWord());\n    //    userQueryParam.setPageNo(request.getPageNo());\n    //    userQueryParam.setPageSize(request.getPageSize());\n    //    PageResult<User> pageResult = userService.queryPage(userQueryParam);\n    //    return WebPageResult.of(userWebConverter.dto2vo(pageResult.getData()), pageResult.getTotal(), request);\n    //}\n    //\n    ///**\n    // * Add Key\n    // *\n    // * @param request\n    // * @return\n    // */\n    //@PostMapping(\"/create\")\n    //public DataResult<Long> create(@RequestBody UserCreateRequest request) {\n    //    return userService.create(userWebConverter.createRequest2dto(request));\n    //}\n    //\n    ///**\n    // * Update my save\n    // *\n    // * @param request\n    // * @return\n    // */\n    //@RequestMapping(value = \"/update\",method = {RequestMethod.POST, RequestMethod.PUT})\n    //public ActionResult update(@RequestBody UserUpdateRequest request) {\n    //    DataResult<Boolean> result = userService.update(userWebConverter.updateRequest2dto(request));\n    //    return ActionResult.isSuccess();\n    //}\n    //\n    ///**\n    // * delete my save\n    // *\n    // * @param id\n    // * @return\n    // */\n    //@DeleteMapping(\"/{id}\")\n    //public ActionResult delete(@PathVariable(\"id\") Long id) {\n    //    userService.delete(id);\n    //    return ActionResult.isSuccess();\n    //}\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/user/converter/UserWebConverter.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.user.converter;\n\nimport java.util.List;\n\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.web.api.controller.user.request.UserCreateRequest;\nimport ai.chat2db.server.web.api.controller.user.request.UserUpdateRequest;\nimport ai.chat2db.server.web.api.controller.user.vo.UserVO;\n\nimport org.mapstruct.Mapper;\n\n/**\n * @author jipengfei\n * @version : UserWebConverter.java\n */\n@Mapper(componentModel = \"spring\")\npublic abstract class UserWebConverter {\n    /**\n     * Convert\n     *\n     * @param user\n     * @return\n     */\n    public abstract UserVO dto2vo(User user);\n\n    /**\n     *\n     * @param user\n     * @return\n     */\n    public abstract List<UserVO> dto2vo(List<User> user);\n\n    /**\n     *\n     * @param createRequest\n     * @return\n     */\n    public abstract User createRequest2dto(UserCreateRequest createRequest);\n\n    /**\n     *\n     * @param updateRequest\n     * @return\n     */\n    public abstract User updateRequest2dto(UserUpdateRequest updateRequest);\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/user/request/UserCreateRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.user.request;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author jipengfei\n * @version : UserCreateRequest.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserCreateRequest implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = 353710386092262213L;\n    /**\n     * userName\n     */\n    private String userName;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * nickName\n     */\n    private String nickName;\n\n\n    /**\n     * email\n     */\n    private String email;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/user/request/UserQueryRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.user.request;\n\nimport java.io.Serial;\n\nimport ai.chat2db.server.tools.base.wrapper.param.PageQueryParam;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author jipengfei\n * @version : UserQueryRequest.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserQueryRequest extends PageQueryParam {\n\n    @Serial\n    private static final long serialVersionUID = 5663790872812326134L;\n    /**\n     * Username magic search\n     */\n    private String keyWord;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/user/request/UserUpdateRequest.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.user.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author jipengfei\n * @version : UserUpdateRequest.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserUpdateRequest {\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * userName\n     */\n    private String userName;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * nickName\n     */\n    private String nickName;\n\n\n    /**\n     * email\n     */\n    private String email;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/user/vo/UserVO.java",
    "content": "\npackage ai.chat2db.server.web.api.controller.user.vo;\n\nimport java.io.Serializable;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author jipengfei\n * @version : UserVO.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserVO implements Serializable {\n    private static final long serialVersionUID = 502943167829222727L;\n\n    /**\n     * primary key\n     */\n    private Long id;\n\n    /**\n     * userName\n     */\n    private String userName;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * nickName\n     */\n    private String nickName;\n\n\n    /**\n     * email\n     */\n    private String email;\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/GatewayClientService.java",
    "content": "package ai.chat2db.server.web.api.http;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.config.Chat2dbProperties;\nimport ai.chat2db.server.web.api.http.request.*;\nimport ai.chat2db.server.web.api.http.response.*;\nimport com.dtflys.forest.Forest;\nimport com.dtflys.forest.utils.TypeReference;\nimport jakarta.annotation.Resource;\nimport org.springframework.stereotype.Service;\n\nimport java.time.Duration;\n\n\n/**\n * Gateway http service\n *\n * @author Jiaju Zhuang\n */\n//@BaseRequest(\n//    baseURL = \"{gatewayBaseUrl}\"\n//)\n@Service\npublic class GatewayClientService {\n\n    @Resource\n    private Chat2dbProperties chat2dbProperties;\n\n    /**\n     * Get the QR code of the official account\n     *\n     * @return\n     */\n    public DataResult<QrCodeResponse> getLoginQrCode() {\n        DataResult<QrCodeResponse> result = Forest.get(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/loginQrCode\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .execute(new TypeReference<>() {\n                });\n        return result;\n    }\n\n    /**\n     * Refresh login\n     *\n     * @param token\n     * @return\n     */\n    public DataResult<QrCodeResponse> getLoginStatus(String token) {\n        DataResult<QrCodeResponse> result = Forest.get(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/loginStatus\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .addQuery(\"token\", token)\n                .readTimeout(Duration.ofMillis(10000))\n                .execute(new TypeReference<>() {\n                });\n        return result;\n\n    }\n\n    /**\n     * Return the remaining times\n     *\n     * @param key\n     * @return\n     */\n    public DataResult<ApiKeyResponse> remaininguses(String key) {\n        DataResult<ApiKeyResponse> result = Forest.get(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/remaininguses/\" + key)\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .execute(new TypeReference<>() {\n                });\n        return result;\n\n    }\n\n\n    /**\n     * Obtain invitation QR code\n     *\n     * @param apiKey\n     * @return\n     */\n    public DataResult<InviteQrCodeResponse> getInviteQrCode(String apiKey) {\n        DataResult<InviteQrCodeResponse> result = Forest.get(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/inviteQrCode\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .addQuery(\"apiKey\", apiKey)\n                .readTimeout(Duration.ofMillis(10000))\n                .execute(new TypeReference<>() {\n                });\n        return result;\n\n\n    }\n\n    /**\n     * save knowledge vector\n     *\n     * @param request\n     * @return\n     */\n    public ActionResult knowledgeVectorSave(KnowledgeRequest request) {\n\n        ActionResult result = Forest.post(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/milvus/knowledge/save\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .contentType(\"application/json\")\n                .addBody(request)\n                .execute(new TypeReference<>() {\n                });\n        return result;\n\n    }\n\n    /**\n     * save table schema vector\n     *\n     * @param request\n     * @return\n     */\n    public ActionResult schemaVectorSave(TableSchemaRequest request) {\n        ActionResult result = Forest.post(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/milvus/schema/save\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .contentType(\"application/json\")\n                .addBody(request)\n                .execute(new TypeReference<>() {\n                });\n        return result;\n    }\n\n    /**\n     * save table schema vector\n     *\n     * @param request\n     * @return\n     */\n    public ActionResult schemaEsSave(EsTableSchemaRequest request) {\n        ActionResult result = Forest.post(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/es/schema/save\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .contentType(\"application/json\")\n                .addBody(request)\n                .execute(new TypeReference<>() {\n                });\n        return result;\n    }\n\n    /**\n     * save knowledge vector\n     *\n     * @param searchVectors\n     * @return\n     */\n    public DataResult<KnowledgeResponse> knowledgeVectorSearch(KnowledgeRequest searchVectors) {\n        DataResult<KnowledgeResponse> result = Forest.post(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/milvus/knowledge/search\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .contentType(\"application/json\")\n                .addBody(searchVectors)\n                .execute(new TypeReference<>() {\n                });\n        return result;\n    }\n\n    /**\n     * save table schema vector\n     *\n     * @param request\n     * @return\n     */\n    public DataResult<TableSchemaResponse> schemaVectorSearch(TableSchemaRequest request) {\n        DataResult<TableSchemaResponse> result = Forest.post(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/milvus/schema/search\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .contentType(\"application/json\")\n                .addBody(request)\n                .execute(new TypeReference<>() {\n                });\n        return result;\n    }\n\n    /**\n     * save table schema vector\n     *\n     * @param request\n     * @return\n     */\n    public DataResult<EsTableSchemaResponse> schemaEsSearch(EsTableSchemaRequest request) {\n        DataResult<EsTableSchemaResponse> result = Forest.post(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/es/schema/search\")\n                .connectTimeout(Duration.ofMillis(5000))\n                .readTimeout(Duration.ofMillis(10000))\n                .contentType(\"application/json\")\n                .addBody(request)\n                .execute(new TypeReference<>() {\n                });\n        return result;\n    }\n\n    /**\n     * check in white list\n     *\n     * @param whiteListRequest\n     * @return\n     */\n    public DataResult<Boolean> checkInWhite(WhiteListRequest whiteListRequest) {\n        // Remove whitelist\n        return DataResult.of(false);\n//        DataResult<Boolean> result = Forest.get(chat2dbProperties.getGateway().getBaseUrl() + \"/api/client/whitelist/check\")\n//                .connectTimeout(Duration.ofMillis(5000))\n//                .readTimeout(Duration.ofMillis(10000))\n//                .addQuery(whiteListRequest)\n//                .execute(new TypeReference<>() {\n//                });\n//        return result;\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/model/EsTableSchema.java",
    "content": "package ai.chat2db.server.web.api.http.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class EsTableSchema {\n\n    private String dataSourceId;\n\n    private String databaseName;\n\n    private String apiKey;\n\n    private String schemaName;\n\n    private String tableName;\n\n    private String tableSchemaContent;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/model/Knowledge.java",
    "content": "package ai.chat2db.server.web.api.http.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Knowledge {\n\n    private Long id;\n\n    private String content;\n\n    private String contentVector;\n\n    private Integer wordCount;\n\n    public Knowledge(Long id, String content, Integer wordCount) {\n        this.id = id;\n        this.content = content;\n        this.wordCount = wordCount;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/model/TableSchema.java",
    "content": "package ai.chat2db.server.web.api.http.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableSchema {\n\n    private Long id;\n\n    private Long dataSourceId;\n\n    private String tableSchema;\n\n    private String tableSchemaVector;\n\n    private Integer wordCount;\n\n    public TableSchema(Long id, Long dataSourceId, String tableSchema, Integer wordCount) {\n        this.id = id;\n        this.dataSourceId = dataSourceId;\n        this.tableSchema = tableSchema;\n        this.wordCount = wordCount;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/request/EsTableSchemaRequest.java",
    "content": "package ai.chat2db.server.web.api.http.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class EsTableSchemaRequest {\n\n    private Long dataSourceId;\n\n    private String databaseName;\n\n    private String apiKey;\n\n    private String schemaName;\n\n    private String tableName;\n\n    private String tableSchemaContent;\n\n    private String searchKey;\n\n    private Boolean deleteBeforeInsert;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/request/KnowledgeRequest.java",
    "content": "package ai.chat2db.server.web.api.http.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class KnowledgeRequest {\n\n    private List<List<BigDecimal>> contentVector;\n\n    private List<String> sentenceList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/request/SqlExecuteHistoryCreateRequest.java",
    "content": "package ai.chat2db.server.web.api.http.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * <p>\n * sql execution history\n * </p>\n *\n * @author chat2db\n * @since 2023-12-25\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@SuperBuilder\npublic class SqlExecuteHistoryCreateRequest implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n\n    /**\n     * Database type\n     */\n    private String databaseType;\n\n    /**\n     * Execute SQL\n     */\n    private String sqlContent;\n\n    /**\n     * Client ID\n     */\n    private String clientId;\n\n    /**\n     * state\n     */\n    private String executeStatus;\n\n    /**\n     * wrong information\n     */\n    private String errorMessage;\n\n    /**\n     * sql type\n     */\n    private String sqlType;\n\n    /**\n     * execution duration\n     */\n    private Long duration;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/request/TableSchemaRequest.java",
    "content": "package ai.chat2db.server.web.api.http.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableSchemaRequest {\n\n    private Long dataSourceId;\n\n    private String databaseName;\n\n    private String apiKey;\n\n    private String dataSourceSchema;\n\n    private List<java.util.List<BigDecimal>> schemaVector;\n\n    private List<String> schemaList;\n\n    private Boolean deleteBeforeInsert = false;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/request/WhiteListRequest.java",
    "content": "package ai.chat2db.server.web.api.http.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class WhiteListRequest {\n\n    /**\n     * api key\n     */\n    private String apiKey;\n\n    /**\n     * Whitelist type, such as vector\n     * @see ai.chat2db.server.tools.base.enums.WhiteListTypeEnum\n     */\n    private String whiteType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/response/ApiKeyResponse.java",
    "content": "package ai.chat2db.server.web.api.http.response;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * apikey\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ApiKeyResponse {\n    /**\n     * key\n     */\n    private String key;\n\n    /**\n     * Expiration\n     */\n    private Long expiry;\n\n    /**\n     * return\n     */\n    private Long remainingUses;\n\n    /**\n     * WeChat public account url\n     */\n    private String wechatMpUrl;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/response/EsTableSchemaResponse.java",
    "content": "package ai.chat2db.server.web.api.http.response;\n\nimport ai.chat2db.server.web.api.http.model.EsTableSchema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.util.List;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class EsTableSchemaResponse {\n\n    private List<EsTableSchema> tableSchemas;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/response/InviteQrCodeResponse.java",
    "content": "package ai.chat2db.server.web.api.http.response;\n\nimport lombok.Data;\n\n@Data\npublic class InviteQrCodeResponse {\n\n    private String wechatQrCodeUrl;\n\n    private String tip;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/response/KnowledgeResponse.java",
    "content": "package ai.chat2db.server.web.api.http.response;\n\nimport ai.chat2db.server.web.api.http.model.Knowledge;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.util.List;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class KnowledgeResponse {\n\n    private List<Knowledge> knowledgeList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/response/QrCodeResponse.java",
    "content": "package ai.chat2db.server.web.api.http.response;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class QrCodeResponse {\n    /**\n     * When logging in for the first time, the token will be returned, and subsequent services need to poll the token to\n     * check if the user is logged in\n     */\n    private String token;\n\n    /**\n     * Return the QR code used by the user to log in\n     */\n    private String wechatQrCodeUrl;\n\n    /**\n     * If the user logs in successfully, it will return apiKey. If the front-end detects the presence of apiKey, it\n     * indicates successful login\n     */\n    private String apiKey;\n\n    private String tip;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/response/TableSchemaResponse.java",
    "content": "package ai.chat2db.server.web.api.http.response;\n\nimport ai.chat2db.server.web.api.http.model.TableSchema;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.util.List;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableSchemaResponse {\n\n    private List<TableSchema> tableSchemas;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/util/AddToTopic.java",
    "content": "package ai.chat2db.server.web.api.util;\n\nimport org.apache.poi.xwpf.usermodel.XWPFDocument;\nimport org.apache.poi.xwpf.usermodel.XWPFParagraph;\nimport org.apache.poi.xwpf.usermodel.XWPFRun;\nimport org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;\nimport org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\n\n/**\n * AddToTopic\n *\n * @author lzy\n **/\npublic class AddToTopic {\n\n    public static void generateTOC(XWPFDocument document, OutputStream out) throws IOException {\n        String findText = \"directory\";\n        String replaceText = \"\";\n        for (XWPFParagraph p : document.getParagraphs()) {\n            for (XWPFRun r : p.getRuns()) {\n                int pos = r.getTextPosition();\n                String text = r.getText(pos);\n                if (text != null && text.contains(findText)) {\n                    text = text.replace(findText, replaceText);\n                    r.setText(text, 0);\n                    addField(p);\n                    // addField(p, \"TOC \\\\h\");\n                    break;\n                }\n            }\n        }\n        document.write(out);\n    }\n\n    private static void addField(XWPFParagraph paragraph) {\n        CTSimpleField ctSimpleField = paragraph.getCTP().addNewFldSimple();\n        ctSimpleField.setInstr(\"TOC \\\\o \\\"1-3\\\" \\\\h \\\\z \\\\u\");\n        ctSimpleField.setDirty(STOnOff.TRUE);\n        ctSimpleField.addNewR().addNewT().setStringValue(\"<>\");\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/util/ApplicationContextUtil.java",
    "content": "\npackage ai.chat2db.server.web.api.util;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author jipengfei\n * @version : ApplicationContextUtil.java\n */\n@Component\n@Lazy(false)\npublic class ApplicationContextUtil implements ApplicationContextAware {\n\n    /**\n     * context object instance\n     */\n    private static ApplicationContext applicationContext;\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        ApplicationContextUtil.applicationContext = applicationContext;\n    }\n\n    /**\n     * Get applicationContext\n     *\n     * @return\n     */\n    public static ApplicationContext getApplicationContext() {\n        return applicationContext;\n    }\n\n    /**\n     *  Get bean by name\n     *\n     * @param name\n     * @return\n     */\n    public static Object getBean(String name) {\n        return getApplicationContext().getBean(name);\n    }\n\n    /**\n     * Get Bean through class.\n     *\n     * @param clazz\n     * @param <T>\n     * @return\n     */\n    public static <T> T getBean(Class<T> clazz) {\n        return getApplicationContext().getBean(clazz);\n    }\n\n    /**\n     * Return the specified Bean through name and Clazz\n     *\n     * @param name\n     * @param clazz\n     * @param <T>\n     * @return\n     */\n    public static <T> T getBean(String name, Class<T> clazz) {\n        return getApplicationContext().getBean(name, clazz);\n    }\n\n    /**\n     * Get the value in the configuration file\n     * @param key\n     * @return\n     */\n    public static String getProperty(String key) {\n        return getApplicationContext().getEnvironment().getProperty(key);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/util/FileUtils.java",
    "content": "package ai.chat2db.server.web.api.util;\n\n/**\n * FileUtil\n *\n * @author lzy\n **/\npublic class FileUtils {\n\n    public enum ConfigFile {\n        // navicat connection information file\n        NCX,\n        //dbeaver connection information file\n        DBP\n    }\n\n    public static String getFileExtension(String fileName) {\n        int dotIndex = fileName.lastIndexOf(\".\");\n        if (dotIndex > 0) {\n            return fileName.substring(dotIndex + 1).toLowerCase();\n        } else {\n            return \"\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/util/StringUtils.java",
    "content": "package ai.chat2db.server.web.api.util;\n\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\n/**\n * Add string tool class. In order to be compatible with various JB products, try not to use third-party toolkits.\n *\n * @author lzy\n */\n@SuppressWarnings(\"WeakerAccess\")\npublic class StringUtils {\n\n    private static final String EMPTY_STR = \"null\";\n\n    /**\n     * How to deal with initial letters\n     */\n    private static final BiFunction<String, Function<Integer, Integer>, String> FIRST_CHAR_HANDLER_FUN = (str, firstCharFun) -> {\n        int strLen;\n        if (str == null || (strLen = str.length()) == 0) {\n            return str;\n        }\n\n        final int firstCodepoint = str.codePointAt(0);\n        final int newCodePoint = firstCharFun.apply(firstCodepoint);\n        if (firstCodepoint == newCodePoint) {\n            // already capitalized\n            return str;\n        }\n\n        // cannot be longer than the char array\n        final int[] newCodePoints = new int[strLen];\n        int outOffset = 0;\n        // copy the first codepoint\n        newCodePoints[outOffset++] = newCodePoint;\n        for (int inOffset = Character.charCount(firstCodepoint); inOffset < strLen; ) {\n            final int codepoint = str.codePointAt(inOffset);\n            // copy the remaining ones\n            newCodePoints[outOffset++] = codepoint;\n            inOffset += Character.charCount(codepoint);\n        }\n        return new String(newCodePoints, 0, outOffset);\n    };\n\n    public static String isNullOrEmpty(String str) {\n        if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {\n            return \"\";\n        }\n        return str;\n    }\n\n    public static String isNull(String str) {\n        if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {\n            return \"--\";\n        }\n        return str;\n    }\n\n    public static String isNullForHtml(String str) {\n        if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {\n            return \"<br>\";\n        }\n        return str;\n    }\n\n    /**\n     * Determine if it is an empty string\n     *\n     * @param cs string\n     * @return whether it is empty\n     */\n    public static boolean isEmpty(final CharSequence cs) {\n        return cs == null || cs.length() == 0;\n    }\n\n    /**\n     * Capitalize the first letter\n     *\n     * @param str string\n     * @return capitalize the first letter of the result\n     */\n    public static String capitalize(final String str) {\n        return FIRST_CHAR_HANDLER_FUN.apply(str, Character::toTitleCase);\n    }\n\n    /**\n     * Lowercase first letter\n     *\n     * @param str string\n     * @return the first letter of the result is lowercase\n     */\n    public static String uncapitalize(final String str) {\n        return FIRST_CHAR_HANDLER_FUN.apply(str, Character::toLowerCase);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/util/XMLUtils.java",
    "content": "/*\n * DBeaver - Universal Database Manager\n * Copyright (C) 2010-2023 DBeaver Corp and others\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ai.chat2db.server.web.api.util;\n\nimport org.apache.batik.xml.XMLException;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.xml.sax.InputSource;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * Common XML utils\n */\npublic class XMLUtils {\n\n    public static Document parseDocument(String fileName)\n        throws XMLException {\n        return parseDocument(new java.io.File(fileName));\n    }\n\n    public static Document parseDocument(java.io.File file) throws XMLException {\n        try (InputStream is = new FileInputStream(file)) {\n            return parseDocument(new InputSource(is));\n        } catch (IOException e) {\n            throw new XMLException(\"Error opening file '\" + file + \"'\", e);\n        }\n    }\n\n    public static Document parseDocument(InputStream is) throws XMLException {\n        return parseDocument(new InputSource(is));\n    }\n\n    public static Document parseDocument(java.io.Reader is) throws XMLException {\n        return parseDocument(new InputSource(is));\n    }\n\n    public static Document parseDocument(InputSource source) throws XMLException {\n        try {\n            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\n            dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);\n            dbf.setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true);\n            DocumentBuilder xmlBuilder = dbf.newDocumentBuilder();\n            return xmlBuilder.parse(source);\n        } catch (Exception er) {\n            throw new XMLException(\"Error parsing XML document\", er);\n        }\n    }\n\n    public static Document createDocument()\n        throws XMLException {\n        try {\n            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\n            DocumentBuilder xmlBuilder = dbf.newDocumentBuilder();\n            return xmlBuilder.newDocument();\n        } catch (Exception er) {\n            throw new XMLException(\"Error creating XML document\", er);\n        }\n    }\n\n    public static Element getChildElement(Element element,  String childName) {\n        if (element == null) {\n            return null;\n        }\n        for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {\n            if (node.getNodeType() == Node.ELEMENT_NODE &&\n                ((Element) node).getTagName().equals(childName)) {\n                return (Element) node;\n            }\n        }\n        return null;\n    }\n\n    public static String getChildElementBody(Element element,  String childName) {\n        if (element == null) {\n            return null;\n        }\n        for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {\n            if (node.getNodeType() == Node.ELEMENT_NODE &&\n                ((Element) node).getTagName().equals(childName)) {\n                return getElementBody((Element) node);\n            }\n        }\n        return null;\n    }\n\n    public static String getElementBody( Element element) {\n        return element.getTextContent();\n    }\n\n    // Get list of all child elements of specified node\n\n    public static List<Element> getChildElementList(\n        Element parent,\n        String nodeName) {\n        List<Element> list = new ArrayList<>();\n        if (parent != null) {\n            for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {\n                if (node.getNodeType() == Node.ELEMENT_NODE &&\n                    nodeName.equals(node.getNodeName())) {\n                    list.add((Element) node);\n                }\n            }\n        }\n        return list;\n    }\n\n    // Get list of all child elements of specified node\n\n    public static Collection<Element> getChildElementListNS(\n        Element parent,\n        String nsURI) {\n        List<Element> list = new ArrayList<>();\n        if (parent != null) {\n            for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {\n                if (node.getNodeType() == Node.ELEMENT_NODE &&\n                    node.getNamespaceURI().equals(nsURI)) {\n                    list.add((Element) node);\n                }\n            }\n        }\n        return list;\n    }\n\n    // Get list of all child elements of specified node\n    public static Collection<Element> getChildElementListNS(\n        Element parent,\n        String nodeName,\n        String nsURI) {\n        List<Element> list = new ArrayList<>();\n        for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {\n            if (node.getNodeType() == Node.ELEMENT_NODE &&\n                node.getLocalName().equals(nodeName) &&\n                node.getNamespaceURI().equals(nsURI)) {\n                list.add((Element) node);\n            }\n        }\n        return list;\n    }\n\n    // Get list of all child elements of specified node\n\n    public static Collection<Element> getChildElementList(\n        Element parent,\n        String[] nodeNameList) {\n        List<Element> list = new ArrayList<>();\n        for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {\n            if (node.getNodeType() == Node.ELEMENT_NODE) {\n                for (int i = 0; i < nodeNameList.length; i++) {\n                    if (node.getNodeName().equals(nodeNameList[i])) {\n                        list.add((Element) node);\n                    }\n                }\n            }\n        }\n        return list;\n    }\n\n    // Find one child element with specified name\n    public static Element findChildElement(\n        Element parent) {\n        for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {\n            if (node.getNodeType() == Node.ELEMENT_NODE) {\n                return (Element) node;\n            }\n        }\n        return null;\n    }\n\n    public static Object escapeXml(Object obj) {\n        if (obj == null) {\n            return null;\n        } else if (obj instanceof CharSequence) {\n            return escapeXml((CharSequence) obj);\n        } else {\n            return obj;\n        }\n    }\n\n    public static String escapeXml(CharSequence str) {\n        if (str == null) {\n            return null;\n        }\n        StringBuilder res = null;\n        int strLength = str.length();\n        for (int i = 0; i < strLength; i++) {\n            char c = str.charAt(i);\n            String repl = encodeXMLChar(c);\n            if (repl == null) {\n                if (res != null) {\n                    res.append(c);\n                }\n            } else {\n                if (res == null) {\n                    res = new StringBuilder(str.length() + 5);\n                    for (int k = 0; k < i; k++) {\n                        res.append(str.charAt(k));\n                    }\n                }\n                res.append(repl);\n            }\n        }\n        return res == null ? str.toString() : res.toString();\n    }\n\n    public static boolean isValidXMLChar(char c) {\n        return (c >= 32 || c == '\\n' || c == '\\r' || c == '\\t');\n    }\n\n    /**\n     * Encodes a char to XML-valid form replacing &,',\",<,> with special XML encoding.\n     *\n     * @param ch char to convert\n     * @return XML-encoded text\n     */\n    public static String encodeXMLChar(char ch) {\n        switch (ch) {\n            case '&':\n                return \"&amp;\";\n            case '\\\"':\n                return \"&quot;\";\n            case '\\'':\n                return \"&#39;\";\n            case '<':\n                return \"&lt;\";\n            case '>':\n                return \"&gt;\";\n            default:\n                return null;\n        }\n    }\n\n    public static XMLException adaptSAXException(Exception toCatch) {\n        if (toCatch instanceof XMLException) {\n            return (XMLException) toCatch;\n        } else if (toCatch instanceof org.xml.sax.SAXException) {\n            String message = toCatch.getMessage();\n            Exception embedded = ((org.xml.sax.SAXException) toCatch).getException();\n            if (embedded != null && embedded.getMessage() != null && embedded.getMessage().equals(message)) {\n                // Just SAX wrapper - skip it\n                return adaptSAXException(embedded);\n            } else {\n                return new XMLException(\n                    message,\n                    embedded != null ? adaptSAXException(embedded) : null);\n            }\n        } else {\n            return new XMLException(toCatch.getMessage(), toCatch);\n        }\n    }\n\n    public static Collection<Element> getChildElementList(Element element) {\n        List<Element> children = new ArrayList<>();\n        if (element != null) {\n            for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {\n                if (node.getNodeType() == Node.ELEMENT_NODE) {\n                    children.add((Element) node);\n                }\n            }\n        }\n        return children;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/ws/WsConfig.java",
    "content": "package ai.chat2db.server.web.api.ws;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.socket.server.standard.ServerEndpointExporter;\n\n@Configuration\npublic class WsConfig {\n\n    @Bean\n    public ServerEndpointExporter serverEndpointExporter() {\n        return new ServerEndpointExporter();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/ws/WsMessage.java",
    "content": "package ai.chat2db.server.web.api.ws;\n\nimport com.alibaba.fastjson2.JSONObject;\nimport lombok.Data;\n\n@Data\npublic class WsMessage {\n\n    /**\n     * message id\n     */\n    private String uuid;\n\n    /**\n     * message content\n     */\n    private JSONObject message;\n\n    /**\n     * message type\n     */\n    private String actionType;\n\n\n    public static class ActionType {\n        public static final String EXECUTE = \"execute\";\n        public static final String LOGIN = \"login\";\n        public static final String PING = \"ping\";\n        public static final String OPEN_SESSION = \"open_session\";\n        public static final String ERROR = \"error\";\n        public static final String MESSAGE = \"message\";\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/ws/WsResult.java",
    "content": "package ai.chat2db.server.web.api.ws;\n\n\nimport ai.chat2db.server.tools.base.wrapper.Result;\nimport lombok.Data;\n\n@Data\npublic class WsResult {\n    /**\n     * message id\n     */\n    private String uuid;\n\n    /**\n     * message content\n     */\n    private Result message;\n\n    /**\n     * message type\n     */\n    private String actionType;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/ws/WsServer.java",
    "content": "package ai.chat2db.server.web.api.ws;\n\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.web.api.controller.rdb.request.DmlRequest;\nimport ai.chat2db.server.web.api.controller.rdb.vo.ExecuteResultVO;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport com.alibaba.fastjson2.JSONObject;\nimport com.jcraft.jsch.JSchException;\nimport jakarta.websocket.*;\nimport jakarta.websocket.server.PathParam;\nimport jakarta.websocket.server.ServerEndpoint;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.context.support.SpringBeanAutowiringSupport;\n\nimport java.io.IOException;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Map;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n@Slf4j\n@Component\n@ServerEndpoint(\"/api/ws/{token}\")\npublic class WsServer {\n    private Session session;\n\n    private static final AtomicInteger OnlineCount = new AtomicInteger(0);\n\n    // The thread-safe Set of the concurrent package is used to store the Session object corresponding to each client.\n    private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();\n\n    private static int num = 0;\n\n    private Timer timer = new Timer();\n\n\n    private Map<String, ConnectInfo> connectInfoMap = new ConcurrentHashMap<>();\n\n\n    private LoginUser loginUser;\n\n    private WsService wsService;\n\n    /**\n     * Method called when connection is established successfully\n     */\n    @OnOpen\n    public void onOpen(Session session, @PathParam(\"token\") String token) throws IOException {\n        SessionSet.add(session);\n        this.session = session;\n        int cnt = OnlineCount.incrementAndGet(); // Add 1 to the online number\n        log.info(\"There are connections added, and the current number of connections is: {}\", cnt);\n\n        heartBeat(session);\n        this.wsService = ApplicationContextUtil.getBean(WsService.class);\n        Dbutils.setSession();\n        this.loginUser = wsService.doLogin(token);\n        if (this.loginUser == null) {\n            ActionResult actionResult = new ActionResult();\n            actionResult.setSuccess(false);\n            actionResult.setErrorCode(\"LOGIN_FAIL\");\n            WsResult wsMessage = new WsResult();\n            wsMessage.setActionType(WsMessage.ActionType.OPEN_SESSION);\n            wsMessage.setUuid(token);\n            wsMessage.setMessage(actionResult);\n            SendMessage(this.session, wsMessage);\n            onClose();\n        }else {\n            ActionResult actionResult = new ActionResult();\n            actionResult.setSuccess(true);\n            WsResult wsMessage = new WsResult();\n            wsMessage.setActionType(WsMessage.ActionType.OPEN_SESSION);\n            wsMessage.setUuid(token);\n            wsMessage.setMessage(actionResult);\n            SendMessage(this.session, wsMessage);\n        }\n        Dbutils.removeSession();\n    }\n\n\n    /**\n     * Method called on connection close\n     */\n    @OnClose\n    public void onClose() throws IOException {\n        if (SessionSet.contains(session)) {\n            SessionSet.remove(this.session);\n            session.close();\n            for (Map.Entry<String, ConnectInfo> entry : connectInfoMap.entrySet()) {\n                ConnectInfo connectInfo = entry.getValue();\n                if (connectInfo != null) {\n                    Connection connection = connectInfo.getConnection();\n                    try {\n                        if (connection != null && !connection.isClosed()) {\n                            connection.close();\n                        }\n                    } catch (SQLException e) {\n                        log.error(\"close connection error\", e);\n                    }\n\n                    com.jcraft.jsch.Session session = connectInfo.getSession();\n                    if (session != null && session.isConnected() && connectInfo.getSsh() != null\n                            && connectInfo.getSsh().isUse()) {\n                        try {\n                            session.delPortForwardingL(Integer.parseInt(connectInfo.getSsh().getLocalPort()));\n                        } catch (JSchException e) {\n                        }\n                    }\n                }\n            }\n            int cnt = OnlineCount.decrementAndGet();\n            log.info(\"A connection was closed, session:{},{}\", session, this);\n            log.info(\"A connection is closed, the current number of connections is: {}\", cnt);\n        }\n    }\n\n    /**\n     * Method called after receiving client message\n     *\n     * @param message message sent by the client\n     */\n    @OnMessage(maxMessageSize = 1024000)\n    public void onMessage(String message, Session session) {\n        CompletableFuture.runAsync(() -> {\n            WsMessage wsMessage = JSONObject.parseObject(message, WsMessage.class);\n            // Process your messages here\n            try {\n                String actionType = wsMessage.getActionType();\n                if (WsMessage.ActionType.PING.equalsIgnoreCase(actionType)) {\n                    WsResult wsResult = new WsResult();\n                    ActionResult actionResult = new ActionResult();\n                    actionResult.setSuccess(true);\n                    wsResult.setActionType(WsMessage.ActionType.PING);\n                    wsResult.setUuid(wsMessage.getUuid());\n                    wsResult.setMessage(actionResult);\n                    SendMessage(session, wsResult);\n                    timer.cancel();\n                    heartBeat(session);\n                } else {\n                    ContextUtils.setContext(Context.builder()\n                            .loginUser(loginUser)\n                            .build());\n                    Dbutils.setSession();\n                    JSONObject jsonObject = wsMessage.getMessage();\n                    Long dataSourceId = jsonObject.getLong(\"dataSourceId\");\n                    String databaseName = jsonObject.getString(\"databaseName\");\n                    String schemaName = jsonObject.getString(\"schemaName\");\n                    Long consoleId = jsonObject.getLong(\"consoleId\");\n                    String key = connectInfoKey(dataSourceId, databaseName, schemaName, consoleId);\n                    ConnectInfo connectInfo = connectInfoMap.get(key);\n                    if (connectInfo == null) {\n                        connectInfo = wsService.toInfo(dataSourceId, databaseName, consoleId, schemaName);\n                        connectInfoMap.put(key, connectInfo);\n                    }\n                    Chat2DBContext.putContext(connectInfo);\n                    if (WsMessage.ActionType.EXECUTE.equalsIgnoreCase(actionType)) {\n                        DmlRequest request = jsonObject.toJavaObject(DmlRequest.class);\n                        ListResult<ExecuteResultVO> result = wsService.execute(request);\n                        WsResult resultMessage = new WsResult();\n                        resultMessage.setUuid(wsMessage.getUuid());\n                        resultMessage.setActionType(wsMessage.getActionType());\n                        resultMessage.setMessage(result);\n                        SendMessage(session, resultMessage);\n                    }\n                }\n            } catch (Exception e) {\n                WsResult wsResult = new WsResult();\n                ActionResult actionResult = new ActionResult();\n                actionResult.setSuccess(false);\n                actionResult.setErrorCode(e.getMessage());\n                wsResult.setActionType(WsMessage.ActionType.ERROR);\n                wsResult.setUuid(wsMessage.getUuid());\n                wsResult.setMessage(actionResult);\n                SendMessage(session, wsResult);\n            } finally {\n                Chat2DBContext.removeContext();\n                ContextUtils.removeContext();\n                Dbutils.removeSession();\n            }\n        });\n\n    }\n\n\n    private String connectInfoKey(Long dataSourceId, String databaseName, String schemaName, Long consoleId) {\n        return dataSourceId + \"_\" + databaseName + \"_\" + schemaName + \"_\" + consoleId;\n    }\n\n\n    /**\n     * An error occurred\n     *\n     * @param session\n     * @param error\n     */\n    @OnError\n    public void onError(Session session, Throwable error) {\n        log.error(\"An error occurred:{}，Session ID： {}\", error.getMessage(), session.getId(), error);\n    }\n\n    /**\n     * heartbeat\n     *\n     * @param session\n     */\n    private void heartBeat(Session session) {\n        timer = new Timer();\n        timer.schedule(new TimerTask() {\n            @Override\n            public void run() {\n                try {\n                    onClose();\n                } catch (IOException e) {\n                    log.error(\"Error sending message：{}\", e.getMessage(), e);\n                }\n            }\n        }, 600000);\n    }\n\n    /**\n     * Sending a message, practice shows that every time the browser refreshes, the session will change.\n     *\n     * @param session\n     * @param wsResult\n     */\n    public static void SendMessage(Session session, WsResult wsResult) {\n        try {\n            if (session.isOpen()) {\n                session.getBasicRemote().sendText(JSONObject.toJSONString(wsResult));\n            }\n        } catch (IOException e) {\n            log.error(\"Error sending message：{}\", e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/ws/WsService.java",
    "content": "package ai.chat2db.server.web.api.ws;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.model.Config;\nimport ai.chat2db.server.domain.api.model.DataSource;\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.param.DlExecuteParam;\nimport ai.chat2db.server.domain.api.service.*;\nimport ai.chat2db.server.domain.core.cache.CacheKey;\nimport ai.chat2db.server.domain.core.cache.MemoryCacheManage;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.base.wrapper.result.ListResult;\nimport ai.chat2db.server.tools.common.exception.ParamBusinessException;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;\nimport ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;\nimport ai.chat2db.server.web.api.controller.rdb.request.DmlRequest;\nimport ai.chat2db.server.web.api.controller.rdb.vo.ExecuteResultVO;\nimport ai.chat2db.server.web.api.util.ApplicationContextUtil;\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.ExecuteResult;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n@Component\npublic class WsService {\n\n    @Autowired\n    private UserService userService;\n\n\n    @Autowired\n    private DataSourceService dataSourceService;\n\n    @Autowired\n    private DataSourceAccessBusinessService dataSourceAccessBusinessService;\n\n\n    @Autowired\n    private RdbWebConverter rdbWebConverter;\n\n    @Autowired\n    private DlTemplateService dlTemplateService;\n\n    public static ExecutorService executorService = Executors.newFixedThreadPool(10);\n\n    public ListResult<ExecuteResultVO> execute(DmlRequest request) {\n        DlExecuteParam param = rdbWebConverter.request2param(request);\n        ListResult<ExecuteResult> resultDTOListResult = dlTemplateService.execute(param);\n        List<ExecuteResultVO> resultVOS = rdbWebConverter.dto2vo(resultDTOListResult.getData());\n        return ListResult.of(resultVOS);\n    }\n\n\n    public LoginUser doLogin(String token) {\n        Long userId = RoleCodeEnum.DESKTOP.getDefaultUserId();\n        LoginUser loginUser = MemoryCacheManage.computeIfAbsent(CacheKey.getLoginUserKey(userId), () -> {\n            User user = userService.query(userId).getData();\n            if (user == null) {\n                return null;\n            }\n            boolean admin = RoleCodeEnum.ADMIN.getCode().equals(user.getRoleCode());\n\n            return LoginUser.builder()\n                    .id(user.getId())\n                    .nickName(user.getNickName())\n                    .admin(admin)\n                    .roleCode(user.getRoleCode())\n                    .build();\n        });\n\n\n        loginUser.setToken(userId.toString());\n        return loginUser;\n    }\n\n    public ConnectInfo toInfo(Long dataSourceId, String database, Long consoleId, String schemaName) {\n        DataResult<DataSource> result = dataSourceService.queryById(dataSourceId);\n        DataSource dataSource = result.getData();\n        if (!result.success() || dataSource == null) {\n            throw new ParamBusinessException(\"dataSourceId\");\n        }\n        // Verify permissions\n        dataSourceAccessBusinessService.checkPermission(dataSource);\n        ConnectInfo connectInfo = new ConnectInfo();\n        connectInfo.setAlias(dataSource.getAlias());\n        connectInfo.setUser(dataSource.getUserName());\n        connectInfo.setConsoleId(consoleId);\n        connectInfo.setDataSourceId(dataSourceId);\n        connectInfo.setPassword(dataSource.getPassword());\n        connectInfo.setDbType(dataSource.getType());\n        connectInfo.setUrl(dataSource.getUrl());\n        connectInfo.setDatabase(database);\n        connectInfo.setSchemaName(schemaName);\n        connectInfo.setConsoleOwn(false);\n        connectInfo.setDriver(dataSource.getDriver());\n        connectInfo.setSsh(dataSource.getSsh());\n        connectInfo.setSsl(dataSource.getSsl());\n        connectInfo.setJdbc(dataSource.getJdbc());\n        connectInfo.setExtendInfo(dataSource.getExtendInfo());\n        connectInfo.setUrl(dataSource.getUrl());\n        connectInfo.setPort(StringUtils.isNotBlank(dataSource.getPort()) ? Integer.parseInt(dataSource.getPort()) : null);\n        connectInfo.setHost(dataSource.getHost());\n        DriverConfig driverConfig = dataSource.getDriverConfig();\n        if (driverConfig != null && driverConfig.notEmpty()) {\n            connectInfo.setDriverConfig(driverConfig);\n        }\n        return connectInfo;\n    }\n\n\n    private String getApiKey() {\n        ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);\n        Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();\n        if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {\n            return null;\n        }\n        return keyConfig.getContent();\n    }\n\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/resources/template/template.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title></title>\n<style>\nbody{font-family:Arial,serif;font-size:15px;line-height:180%;margin-top:0;margin-left:0;padding-bottom: 20px} /*Total control, you can ignore this line*/\ntable tr:first-child{background:#38a4ed; color:#fff;font-weight:bold;} /*First line title blue background*/\ntable{border-top:1pt solid #C1DAD7;border-left:1pt solid #C1DAD7;width: 70%;}\ntable td {word-wrap: break-word;max-width:500px;}\ntd{ padding:5px 10px; text-align:center;border-right:1pt solid #C1DAD7;border-bottom:1pt solid #C1DAD7;}\ntr:nth-of-type(odd){ background:#F5FAFA;} /* odd identifies odd-numbered rows, even identifies even-numbered rows */\ntr:hover{ background:#E0F0F0;} /*Table background color after mouseover*/\nul{position:fixed;float:left; width: 20%; height: 100%; overflow: auto; margin-top: 0; background: rgba(255, 255, 255, 0.2); color: rgba(199, 199, 199, 0.5); box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);}\nli{line-height: 30px;text-decoration:none; color: #000;padding: 8px 8px;}\nli a{text-decoration:none;color: #000;font-size: 1.1rem;text-overflow: ellipsis;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 1;overflow: hidden;}\nli > a:hover{color: #38a4ed;-webkit-transition: .12s;transition: .12s;}\nul > li::marker {content: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjY4MTcyNzkzMzI3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjY0MyIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHBhdGggZD0iTTUxMiA4MDBjLTI0Ny40MiAwLTQ0OC03MS42My00NDgtMTYwdjE2MGMwIDg4LjM3IDIwMC41OCAxNjAgNDQ4IDE2MHM0NDgtNzEuNjMgNDQ4LTE2MFY2NDBjMCA4OC4zNy0yMDAuNTggMTYwLTQ0OCAxNjB6IiBwLWlkPSI2NDQiIGZpbGw9IiMwMmI0MjciPjwvcGF0aD48cGF0aCBkPSJNNTEyIDYwOGMtMjQ3LjQyIDAtNDQ4LTcxLjYzLTQ0OC0xNjB2MTYwYzAgODguMzcgMjAwLjU4IDE2MCA0NDggMTYwczQ0OC03MS42MyA0NDgtMTYwVjQ0OGMwIDg4LjM3LTIwMC41OCAxNjAtNDQ4IDE2MHoiIHAtaWQ9IjY0NSIgZmlsbD0iIzAyYjQyNyI+PC9wYXRoPjxwYXRoIGQ9Ik01MTIgNDE2Yy0yNDcuNDIgMC00NDgtNzEuNjMtNDQ4LTE2MHYxNjBjMCA4OC4zNyAyMDAuNTggMTYwIDQ0OCAxNjBzNDQ4LTcxLjYzIDQ0OC0xNjBWMjU2YzAgODguMzctMjAwLjU4IDE2MC00NDggMTYweiIgcC1pZD0iNjQ2IiBmaWxsPSIjMDJiNDI3Ij48L3BhdGg+PHBhdGggZD0iTTY0IDIyNGE0NDggMTYwIDAgMSAwIDg5NiAwIDQ0OCAxNjAgMCAxIDAtODk2IDBaIiBwLWlkPSI2NDciIGZpbGw9IiMwMmI0MjciPjwvcGF0aD48L3N2Zz4=');}\nol > li::marker {content: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjY4MTcxMDYxNzUyIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQ3Nzc0IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ij48cGF0aCBkPSJNMCAwaDEwMjR2MjkyLjU3MTQyOUgweiIgZmlsbD0iIzQxODVGNCIgcC1pZD0iNDc3NzUiPjwvcGF0aD48cGF0aCBkPSJNMCAzNjUuNzE0Mjg2aDI5Mi41NzE0Mjl2MjkyLjU3MTQyOEgweiIgZmlsbD0iI0EwQzJGOSIgcC1pZD0iNDc3NzYiPjwvcGF0aD48cGF0aCBkPSJNMCA3MzEuNDI4NTcxaDI5Mi41NzE0Mjl2MjkyLjU3MTQyOUgweiIgZmlsbD0iI0EwQzJGOSIgcC1pZD0iNDc3NzciPjwvcGF0aD48cGF0aCBkPSJNMzY1LjcxNDI4NiAzNjUuNzE0Mjg2aDI5Mi41NzE0Mjh2MjkyLjU3MTQyOEgzNjUuNzE0Mjg2ek0zNjUuNzE0Mjg2IDczMS40Mjg1NzFoMjkyLjU3MTQyOHYyOTIuNTcxNDI5SDM2NS43MTQyODZ6IiBmaWxsPSIjQTBDMkY5IiBwLWlkPSI0Nzc3OCI+PC9wYXRoPjxwYXRoIGQ9Ik03MzEuNDI4NTcxIDM2NS43MTQyODZoMjkyLjU3MTQyOXYyOTIuNTcxNDI4aC0yOTIuNTcxNDI5eiIgZmlsbD0iI0EwQzJGOSIgcC1pZD0iNDc3NzkiPjwvcGF0aD48cGF0aCBkPSJNNzMxLjQyODU3MSA3MzEuNDI4NTcxaDI5Mi41NzE0Mjl2MjkyLjU3MTQyOWgtMjkyLjU3MTQyOXoiIGZpbGw9IiM0MTg1RjQiIHAtaWQ9IjQ3NzgwIj48L3BhdGg+PC9zdmc+');}\nol {padding-inline-start: 30px;}\nmy{float: right;width:-moz-calc(80% - 60px);width:-webkit-calc(80% - 60px);width: calc(80% - 60px);padding-bottom: 20px;}\n/*Define the height, width and background of the scroll bar. The height and width correspond to the size of the horizontal and vertical scroll bars respectively*/\n::-webkit-scrollbar {width: 7px;height: 7px;background-color: #f5f5f5;}\n/*Define scroll bar track inner shadow + rounded corners*/\n::-webkit-scrollbar-track {box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); border-radius: 10px;background-color: #f5f5f5;}\n/*Define slider inner shadow + rounded corners*/\n::-webkit-scrollbar-thumb {border-radius: 10px;box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);background-color: #c8c8c8;}\n</style>\n</head>\n<body>\n<ul>\n${catalogue}\n</ul>\n<my>\n${data}\n</my>\n</div>\n</body>\n</html>"
  },
  {
    "path": "chat2db-server/chat2db-server-web/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-parent</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-web</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>chat2db-server-web-api</module>\n        <module>chat2db-server-admin-api</module>\n        <module>chat2db-server-common-api</module>\n    </modules>\n\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <parent>\n        <groupId>ai.chat2db</groupId>\n        <artifactId>chat2db-server-parent</artifactId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>chat2db-server-web-start</artifactId>\n    <packaging>jar</packaging>\n    <name>chat2db-server-web-start</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <exclusions>\n                <exclusion>\n                    <artifactId>log4j-api</artifactId>\n                    <groupId>org.apache.logging.log4j</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-web-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-admin-api</artifactId>\n        </dependency>\n        <!-- Services that need to be loaded -->\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-domain-core</artifactId>\n        </dependency>\n\n        <!-- Log using logback -->\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>jcl-over-slf4j</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>log4j-over-slf4j</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ch.qos.logback</groupId>\n            <artifactId>logback-classic</artifactId>\n        </dependency>\n\n        <!-- Start database -->\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n        </dependency>\n\n        <!-- Database version management -->\n        <dependency>\n            <groupId>org.flywaydb</groupId>\n            <artifactId>flyway-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.flywaydb</groupId>\n            <artifactId>flyway-mysql</artifactId>\n        </dependency>\n\n        <!-- Login authentication -->\n        <dependency>\n            <groupId>cn.dev33</groupId>\n            <artifactId>sa-token-spring-boot3-starter</artifactId>\n        </dependency>\n        <!-- Sa-Token matching jwt -->\n        <dependency>\n            <groupId>cn.dev33</groupId>\n            <artifactId>sa-token-jwt</artifactId>\n        </dependency>\n\n        <!-- template engine -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-thymeleaf</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.freemarker</groupId>\n            <artifactId>freemarker</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-generator</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- http -->\n        <dependency>\n            <groupId>com.dtflys.forest</groupId>\n            <artifactId>forest-spring</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.dtflys.forest</groupId>\n            <artifactId>forest-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.zalando</groupId>\n            <artifactId>logbook-spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n    <build>\n        <finalName>chat2db-server-web-start</finalName>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n    <!--    <build>-->\n    <!--        <finalName>chat2db-server-start</finalName>-->\n    <!--        <plugins>-->\n    <!--            <plugin>-->\n    <!--                &lt;!&ndash;Remove third-party dependencies when packaging&ndash;&gt;-->\n    <!--                <groupId>org.springframework.boot</groupId>-->\n    <!--                <artifactId>spring-boot-maven-plugin</artifactId>-->\n    <!--                <configuration>-->\n    <!--                    <layout>ZIP</layout>-->\n    <!--                    <includes>-->\n    <!--                        <include>-->\n    <!--                            <groupId>non-exists</groupId>-->\n    <!--                            <artifactId>non-exists</artifactId>-->\n    <!--                        </include>-->\n    <!--                    </includes>-->\n    <!--                </configuration>-->\n    <!--            </plugin>-->\n    <!--            &lt;!&ndash;Copy third-party dependency files to the specified directory&ndash;&gt;-->\n    <!--            <plugin>-->\n    <!--                <groupId>org.apache.maven.plugins</groupId>-->\n    <!--                <artifactId>maven-dependency-plugin</artifactId>-->\n    <!--                <executions>-->\n    <!--                    <execution>-->\n    <!--                        <id>copy-dependencies</id>-->\n    <!--                        <phase>package</phase>-->\n    <!--                        <goals>-->\n    <!--                            <goal>copy-dependencies</goal>-->\n    <!--                        </goals>-->\n    <!--                        <configuration>-->\n    <!--                            &lt;!&ndash;target/lib is the output directory that depends on the jar package and can be configured according to your preferences.&ndash;&gt;-->\n    <!--                            <outputDirectory>target/lib</outputDirectory>-->\n    <!--                            <excludeTransitive>false</excludeTransitive>-->\n    <!--                            <stripVersion>false</stripVersion>-->\n    <!--                            <includeScope>runtime</includeScope>-->\n    <!--                        </configuration>-->\n    <!--                    </execution>-->\n    <!--                </executions>-->\n    <!--            </plugin>-->\n    <!--        </plugins>-->\n    <!--    </build>-->\n</project>\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/Application.java",
    "content": "package ai.chat2db.server.web.start;\n\nimport ai.chat2db.server.tools.common.enums.ModeEnum;\nimport ai.chat2db.server.tools.common.model.ConfigJson;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport ai.chat2db.server.tools.common.util.EasyEnumUtils;\nimport cn.hutool.core.lang.UUID;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cache.annotation.EnableCaching;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.scheduling.annotation.EnableAsync;\nimport org.springframework.scheduling.annotation.EnableScheduling;\nimport org.springframework.stereotype.Indexed;\n\n/**\n * Startup class\n *\n * @author Jiaju Zhuang\n */\n@SpringBootApplication\n@ComponentScan(value = {\"ai.chat2db.server\"})\n@Indexed\n@EnableCaching\n@EnableScheduling\n@EnableAsync\n@Slf4j\npublic class Application {\n\n    public static void main(String[] args) {\n        long s1 = System.currentTimeMillis();\n        String currentVersion = ConfigUtils.getLocalVersion();\n        ConfigJson configJson = ConfigUtils.getConfig();\n\n        // The unique ID of the entire system will not change after multiple starts\n        if (StringUtils.isBlank(configJson.getSystemUuid())) {\n            configJson.setSystemUuid(UUID.fastUUID().toString(true));\n            ConfigUtils.setConfig(configJson);\n        }\n\n        // Represents that the current version has been successfully launched\n        if (StringUtils.isNotBlank(currentVersion) && StringUtils.equals(currentVersion,\n            configJson.getLatestStartupSuccessVersion())) {\n            // Flyway doesn't need to start every time to increase startup speed\n            //args = ArrayUtils.add(args, \"--spring.flyway.enabled=false\");\n            log.info(\"The current version {} has been successfully launched once and will no longer load Flyway.\",\n                currentVersion);\n        }\n        ModeEnum mode = EasyEnumUtils.getEnum(ModeEnum.class, System.getProperty(\"chat2db.mode\"));\n        if (mode == ModeEnum.DESKTOP) {\n            // In this mode, no user login is required, so only local access is available\n            args = ArrayUtils.add(args, \"--server.address=0.0.0.0\");\n        }\n\n        String jwtSecretKey = System.getProperty(\"sa-token.jwt-secret-key\");\n        // The user did not specify the jws key\n        if (StringUtils.isBlank(jwtSecretKey)) {\n            if (StringUtils.isBlank(configJson.getJwtSecretKey())) {\n                configJson.setJwtSecretKey(UUID.fastUUID().toString(true));\n                ConfigUtils.setConfig(configJson);\n            }\n            // Ensure that the jwt Secret Key for each application is unique\n            args = ArrayUtils.add(args, \"--sa-token.jwt-secret-key=\" + configJson.getJwtSecretKey());\n        }\n        System.out.println(\"启动耗时：\" + (System.currentTimeMillis() - s1) + \"ms\");\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/StdinReader.java",
    "content": "package ai.chat2db.server.web.start.config;//package ai.chat2db.server.start.config;\n//\n//import jakarta.annotation.PostConstruct;\n//import org.springframework.boot.context.event.ApplicationReadyEvent;\n//import org.springframework.context.ApplicationListener;\n//import org.springframework.stereotype.Component;\n//\n//import java.util.Scanner;\n//\n//@Component\n//public class StdinReader implements ApplicationListener<ApplicationReadyEvent> {\n//\n//\n//    @Override\n//    public void onApplicationEvent(ApplicationReadyEvent event) {\n//        // Start a thread that reads stdin\n//        new Thread(() -> readStdin()).start();\n//    }\n//\n//    private void readStdin() {\n//        Scanner scanner = new Scanner(System.in);\n//        while (scanner.hasNextLine()) {\n//            String line = scanner.nextLine();\n//            // Process the received data\n//            System.out.println(\"data received: \" + line);\n//            // Call other services or logic here\n//        }\n//    }\n//\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/config/AsyncContextRefreshedListener.java",
    "content": "package ai.chat2db.server.web.start.config.config;\n\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.tools.common.model.ConfigJson;\nimport ai.chat2db.server.tools.common.util.ConfigUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextRefreshedEvent;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Component;\n\n/**\n * Execute tasks after startup is completed\n *\n * @author Jiaju Zhuang\n */\n@Component\n@Slf4j\npublic class AsyncContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {\n    @Override\n    @Async\n    public void onApplicationEvent(ContextRefreshedEvent event) {\n        // Successfully set up startup\n        String currentVersion = ConfigUtils.getLocalVersion();\n        ConfigJson configJson = ConfigUtils.getConfig();\n        if (StringUtils.isNotBlank(currentVersion) && !StringUtils.equals(currentVersion,\n            configJson.getLatestStartupSuccessVersion())) {\n            configJson.setLatestStartupSuccessVersion(currentVersion);\n            ConfigUtils.setConfig(configJson);\n        }\n        Dbutils.init();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/config/Chat2dbForestConfiguration.java",
    "content": "package ai.chat2db.server.web.start.config.config;\n\nimport ai.chat2db.server.tools.common.config.Chat2dbProperties;\nimport com.dtflys.forest.Forest;\nimport jakarta.annotation.Resource;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * forest config\n *\n * @author Jiaju Zhuang\n */\n@Configuration\npublic class Chat2dbForestConfiguration implements InitializingBean {\n\n    @Resource\n    private Chat2dbProperties chat2dbProperties;\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        Forest.config()\n            .setVariableValue(\"gatewayBaseUrl\", chat2dbProperties.getGateway().getBaseUrl());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/config/Chat2dbWebMvcConfigurer.java",
    "content": "package ai.chat2db.server.web.start.config.config;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.service.TeamUserService;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.domain.core.cache.CacheKey;\nimport ai.chat2db.server.domain.core.cache.MemoryCacheManage;\nimport ai.chat2db.server.domain.repository.Dbutils;\nimport ai.chat2db.server.tools.base.constant.SymbolConstant;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.config.Chat2dbProperties;\nimport ai.chat2db.server.tools.common.enums.ModeEnum;\nimport ai.chat2db.server.tools.common.exception.PermissionDeniedBusinessException;\nimport ai.chat2db.server.tools.common.exception.RedirectBusinessException;\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport cn.dev33.satoken.context.SaHolder;\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.dev33.satoken.util.SaFoxUtil;\nimport com.alibaba.fastjson2.JSON;\nimport jakarta.annotation.Resource;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.AsyncHandlerInterceptor;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport java.io.IOException;\nimport java.util.Enumeration;\n\n/**\n * web project configuration\n *\n * @author Shi Yi\n */\n@Configuration\n@Slf4j\npublic class Chat2dbWebMvcConfigurer implements WebMvcConfigurer {\n\n    /**\n     * api prefix\n     */\n    private static final String API_PREFIX = \"/api/\";\n\n    /**\n     * Globally released url\n     */\n    private static final String[] FRONT_PERMIT_ALL = new String[] {\"/favicon.ico\", \"/error\", \"/static/**\",\n        \"/api/system\", \"/login\", \"/api/system/get_latest_version\"};\n\n    @Resource\n    private UserService userService;\n    @Resource\n    private TeamUserService teamUserService;\n    @Resource\n    private Chat2dbProperties chat2dbProperties;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n\n        // All requests try to add user information\n        registry.addInterceptor(new AsyncHandlerInterceptor() {\n                @Override\n                public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,\n                    @NotNull Object handler) {\n                    Dbutils.setSession();\n                    String userIdString = (String)StpUtil.getLoginIdDefaultNull();\n                    Long userId;\n                    // Not logged in\n                    if (!StringUtils.isNumeric(userIdString)) {\n                        if (chat2dbProperties.getMode() == ModeEnum.DESKTOP) {\n                            userId = RoleCodeEnum.DESKTOP.getDefaultUserId();\n                        } else {\n                            return true;\n                        }\n                    } else {\n                        userId = Long.parseLong(userIdString);\n                    }\n                    Long finalUserId = userId;\n                    LoginUser loginUser = MemoryCacheManage.computeIfAbsent(CacheKey.getLoginUserKey(userId), () -> {\n                        User user = userService.query(finalUserId).getData();\n                        if (user == null) {\n                            return null;\n                        }\n                        if (!ValidStatusEnum.VALID.getCode().equals(user.getStatus())) {\n                            StpUtil.logout();\n                            throw new BusinessException(\"oauth.invalidUserName\");\n                        }\n                        boolean admin = RoleCodeEnum.ADMIN.getCode().equals(user.getRoleCode());\n\n                        return LoginUser.builder()\n                            .id(user.getId())\n                            .nickName(user.getNickName())\n                            .admin(admin)\n                            .roleCode(user.getRoleCode())\n                            .build();\n                    });\n\n                    if (loginUser == null) {\n                        // Indicates that the user may have been deleted\n                        return true;\n                    }\n\n                    loginUser.setToken(StpUtil.getTokenValue());\n                    ContextUtils.setContext(Context.builder()\n                        .loginUser(loginUser)\n                        .build());\n                    return true;\n                }\n\n                @Override\n                public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,\n                    Exception ex) throws Exception {\n                    // Remove login information\n                    ContextUtils.removeContext();\n                    Dbutils.removeSession();\n                }\n            })\n            .order(1)\n            .addPathPatterns(\"/**\")\n            .excludePathPatterns(FRONT_PERMIT_ALL);\n\n        // Verify login information\n        registry.addInterceptor(new AsyncHandlerInterceptor() {\n                @Override\n                public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,\n                    @NotNull Object handler) throws IOException {\n                    Context context = ContextUtils.queryContext();\n                    // Verify login information\n                    if (context == null) {\n                        log.info(\"Login is required to access {},{}\", buildHeaderString(request), SaHolder.getRequest().getUrl());\n\n                        String path = SaHolder.getRequest().getRequestPath();\n//                        if(path.startsWith(\"/login\")){\n//                            return true;\n//                        }\n                        if (path.startsWith(API_PREFIX)) {\n                            response.getWriter().println(JSON.toJSONString(\n                                ActionResult.fail(\"common.needLoggedIn\", I18nUtils.getMessage(\"common.needLoggedIn\"),\n                                    \"\")));\n                            return false;\n                        } else {\n                            throw new RedirectBusinessException(\n                                \"/login?callback=\" + SaFoxUtil.joinParam(request.getRequestURI(),\n                                    request.getQueryString()));\n                        }\n                    }\n                    return true;\n                }\n            })\n            .order(2)\n            .addPathPatterns(\"/**\")\n            // Links that need to be released on the front end\n            .excludePathPatterns(FRONT_PERMIT_ALL)\n            // Uniform release ending in -a\n            .excludePathPatterns(\"/**/*-a\")\n            // Uniform release of endings in _a\n            .excludePathPatterns(\"/**/*_a\");\n\n        // Verify permissions\n        registry.addInterceptor(new AsyncHandlerInterceptor() {\n                @Override\n                public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,\n                    @NotNull Object handler) throws IOException {\n                    LoginUser loginUser = ContextUtils.getLoginUser();\n                    if (BooleanUtils.isNotTrue(loginUser.getAdmin())) {\n                        throw new PermissionDeniedBusinessException();\n                    }\n                    return true;\n                }\n            })\n            .order(3)\n            .addPathPatterns(\"/api/admin/**\")\n            .addPathPatterns(\"/admin/**\")\n        ;\n\n    }\n\n    private String buildHeaderString(HttpServletRequest request) {\n        StringBuilder stringBuilder = new StringBuilder();\n        Enumeration<String> headerNames = request.getHeaderNames();\n        while (headerNames.hasMoreElements()) {\n            String headerName = headerNames.nextElement();\n            stringBuilder.append(headerName);\n            stringBuilder.append(SymbolConstant.COLON);\n            stringBuilder.append(request.getHeader(headerName));\n            stringBuilder.append(SymbolConstant.COMMA);\n        }\n        return stringBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/config/JarDownloadTask.java",
    "content": "\npackage ai.chat2db.server.web.start.config.config;\n\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.util.JdbcJarUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author jipengfei\n * @version : JarDownloadTask.java\n */\n@Component\n@Slf4j\npublic class JarDownloadTask implements CommandLineRunner {\n\n    @Override\n    public void run(String... args) throws Exception {\n        List<String> urls = new ArrayList<>();\n        Chat2DBContext.PLUGIN_MAP.forEach((k, v) -> {\n            v.getDBConfig().getDriverConfigList().forEach(driverConfig -> {\n                if (driverConfig != null && !CollectionUtils.isEmpty(driverConfig.getDownloadJdbcDriverUrls()) && (\n                    \"MYSQL\".equals(driverConfig.getDbType()))) {\n                    urls.addAll(driverConfig.getDownloadJdbcDriverUrls());\n                }\n            });\n        });\n        JdbcJarUtils.asyncDownload(urls);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/config/WebLogConfiguration.java",
    "content": "package ai.chat2db.server.web.start.config.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.zalando.logbook.BodyFilter;\n\n/**\n * log config\n *\n * @author Jiaju Zhuang\n */\n@Configuration\npublic class WebLogConfiguration {\n\n    @Bean\n    public BodyFilter bodyFilter() {\n        return BodyFilter.none();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/i18n/I18nConfig.java",
    "content": "package ai.chat2db.server.web.start.config.i18n;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.i18n.CookieLocaleResolver;\n\nimport java.util.Locale;\n\n/**\n * Internationalized configuration\n *\n * @author Jiaju Zhuang\n */\n@Configuration\npublic class I18nConfig {\n    @Bean\n    public CookieLocaleResolver localeResolver() {\n        CookieLocaleResolver resolver = new CookieLocaleResolver(\"CHAT2DB.LOCALE\");\n        resolver.setDefaultLocale(Locale.US);\n        return resolver;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/interceptor/CorsFilter.java",
    "content": "package ai.chat2db.server.web.start.config.interceptor;\n\nimport jakarta.servlet.*;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\n\n/**\n * Cors cross-domain interceptor, allowing cross-domain under any circumstances\n *\n * There will be a problem when logging in across domains through the CorsRegistry policy, but it does not happen locally. The possible reason is: the loading order of the beans.\n * Temporarily solved through CorsFilter, you can study it later: CorsRegistry\n *\n * @author Shi Yi\n */\n@Component\npublic class CorsFilter implements Filter {\n\n    @Override\n    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)\n        throws IOException, ServletException {\n        HttpServletResponse response = (HttpServletResponse)res;\n        HttpServletRequest request = (HttpServletRequest)req;\n\n        response.setHeader(\"Access-Control-Allow-Origin\", request.getHeader(HttpHeaders.ORIGIN));\n        response.setHeader(\"Access-Control-Allow-Credentials\", \"true\");\n        response.setHeader(\"Access-Control-Allow-Methods\", \"POST, GET, PUT, OPTIONS, DELETE\");\n        response.setHeader(\"Access-Control-Max-Age\", \"3600\");\n        response.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, DBHUB, uid\");\n        chain.doFilter(req, res);\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/listener/DbhubTomcatConnectorCustomizer.java",
    "content": "package ai.chat2db.server.web.start.config.listener;//package ai.chat2db.server.start.config.listener;\n//\n//import ai.chat2db.server.web.api.controller.system.util.SystemUtils;\n//import lombok.extern.slf4j.Slf4j;\n//import org.apache.catalina.LifecycleState;\n//import org.apache.catalina.connector.Connector;\n//import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;\n//import org.springframework.stereotype.Component;\n//\n///**\n// * Custom tomcat parameters\n// *\n// * @author Jiaju Zhuang\n// */\n//@Component\n//@Slf4j\n//public class DbhubTomcatConnectorCustomizer implements TomcatConnectorCustomizer {\n//    @Override\n//    public void customize(Connector connector) {\n//        connector.addLifecycleListener(event -> {\n//            // Exit the system directly after receiving the shutdown event, because sometimes the system will not exit.\n//            if (LifecycleState.STOPPING == event.getLifecycle().getState()) {\n//                SystemUtils.stop();\n//            }\n//        });\n//    }\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/listener/FailedEventApplicationListener.java",
    "content": "package ai.chat2db.server.web.start.config.listener;\n\nimport ai.chat2db.server.web.api.controller.system.util.SystemUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.context.event.ApplicationFailedEvent;\nimport org.springframework.context.ApplicationListener;\n\n/**\n * Listener for application startup failure\n * The application failed to start. It just stopped tomcat and did not stop the application. Stop xia here.\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class FailedEventApplicationListener implements ApplicationListener<ApplicationFailedEvent> {\n\n    @Override\n    public void onApplicationEvent(ApplicationFailedEvent event) {\n        log.error(\"Failed to start, stop application\", event.getException());\n        SystemUtils.stop();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/listener/ManageApplicationListener.java",
    "content": "package ai.chat2db.server.web.start.config.listener;//package ai.chat2db.server.start.config.listener;\n//\n//import com.alibaba.fastjson2.JSON;\n//import com.alibaba.fastjson2.TypeReference;\n//\n//import ai.chat2db.server.tools.base.enums.SystemEnvironmentEnum;\n//import ai.chat2db.server.tools.base.wrapper.result.DataResult;\n//import cn.hutool.http.HttpUtil;\n//import lombok.extern.slf4j.Slf4j;\n//import org.apache.commons.lang3.BooleanUtils;\n//import org.apache.commons.lang3.StringUtils;\n//import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;\n//import org.springframework.context.ApplicationListener;\n//import org.springframework.util.Assert;\n//\n///**\n// * Used to manage startup\n// * Prevent starting multiple\n// *\n// * @author zhuangjiaju\n// * @date 2023/05/08\n// */\n//@Slf4j\n//public class ManageApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {\n//\n//    @Override\n//    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {\n//        Integer serverPort = event.getEnvironment().getProperty(\"server.port\", Integer.class);\n//        Assert.notNull(serverPort, \"server.port configuration information\");\n//        log.info(\"The startup port is: {}\", serverPort);\n//        String environment = event.getEnvironment().getProperty(\"spring.profiles.active\", String.class);\n//\n//        // Try to access to confirm whether the application has been started\n//        DataResult<String> dataResult;\n//        try {\n//            String body = HttpUtil.get(\"http://127.0.0.1:\" + serverPort + \"/api/system/get-version-a\", 10);\n//            dataResult = JSON.parseObject(body, new TypeReference<>() {});\n//        } catch (Exception e) {\n//            // Throwing an exception means that there is no old startup or the old one is unreliable.\n//            log.info(\"Attempts to access old applications failed. This exception is not important. It will be output during normal startup, so please ignore it.\" + e.getMessage());\n//\n//            // Try killing the old process\n//            killOldIfNecessary(environment);\n//            return;\n//        }\n//\n//        if (dataResult == null || BooleanUtils.isNotTrue(dataResult.getSuccess())) {\n//            // Try killing the old process\n//            killOldIfNecessary(environment);\n//            return;\n//        }\n//\n//        // Indicates that the old process is available\n//        log.info(\"There is already a started application on the current interface, and this application is no longer started.\");\n//        System.exit(0);\n//    }\n//\n//    private void killOldIfNecessary(String environment) {\n//        try {\n//            ProcessHandle.allProcesses().forEach(process -> {\n//                String command = process.info().command().orElse(null);\n//                // Not a java application\n//                boolean isJava = StringUtils.endsWithIgnoreCase(command, \"java\") || StringUtils.endsWithIgnoreCase(\n//                    command,\n//                    \"java.exe\");\n//                if (!isJava) {\n//                    return;\n//                }\n//                String[] arguments = process.info().arguments().orElse(null);\n//                // no parameters\n//                if (arguments == null) {\n//                    return;\n//                }\n//                // Is it dbhub?\n//                boolean isDbhub = false;\n//                String environmentArgument = null;\n//                for (String argument : arguments) {\n//                    if (StringUtils.equals(\"chat2db-server-start.jar\", argument)) {\n//                        isDbhub = true;\n//                    }\n//                    if (StringUtils.startsWith(argument, \"-Dspring.profiles.active=\")) {\n//                        environmentArgument = StringUtils.substringAfter(argument, \"-Dspring.profiles.active=\");\n//                    }\n//                }\n//                // Not dbhub\n//                if (!isDbhub) {\n//                    return;\n//                }\n//                // Determine whether it is a formal environment\n//                if (StringUtils.equals(SystemEnvironmentEnum.RELEASE.getCode(), environment) && StringUtils.equals(\n//                    SystemEnvironmentEnum.RELEASE.getCode(), environmentArgument)) {\n//                    log.info(\"The formal environment requires closing the process\");\n//                    destroyProcess(process, command, arguments);\n//                    return;\n//                }\n//\n//                // Determine whether it is a test environment\n//                if (StringUtils.equals(SystemEnvironmentEnum.TEST.getCode(), environment) && StringUtils.equals(\n//                    SystemEnvironmentEnum.TEST.getCode(), environmentArgument)) {\n//                    log.info(\"The test environment needs to shut down the process\");\n//                    destroyProcess(process, command, arguments);\n//                    return;\n//                }\n//\n//                // Determine whether it is a local environment\n//                boolean devDestroy = StringUtils.equals(SystemEnvironmentEnum.DEV.getCode(), environment) && (\n//                    environmentArgument == null\n//                        || StringUtils.equals(SystemEnvironmentEnum.DEV.getCode(), environmentArgument));\n//                if (devDestroy) {\n//                    log.info(\"The local environment needs to close the process\");\n//                    destroyProcess(process, command, arguments);\n//                }\n//            });\n//        } catch (Throwable t) {\n//            log.warn(\"Attempts to close redundant processes failed and did not affect normal startup.\", t);\n//        }\n//\n//    }\n//\n//    private void destroyProcess(ProcessHandle process, String command, String[] arguments) {\n//        log.info(\"Checked that there are processes that need to be shut down:{},{}\", JSON.toJSONString(command), JSON.toJSONString(arguments));\n//        try {\n//            process.destroy();\n//        } catch (Exception e) {\n//            log.error(\"Failed to end process\", e);\n//        }\n//    }\n//}"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/listener/manage/ManageMessage.java",
    "content": "package ai.chat2db.server.web.start.config.listener.manage;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * Administrative messages\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ManageMessage implements Serializable {\n    @Serial\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * Message type\n     *\n     * @see MessageTypeEnum\n     */\n    private MessageTypeEnum messageTypeEnum;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/listener/manage/MessageTypeEnum.java",
    "content": "package ai.chat2db.server.web.start.config.listener.manage;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * Message type enum\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum MessageTypeEnum implements BaseEnum<String> {\n    /**\n     * Check if it works properly\n     */\n    HEARTBEAT,\n\n\n    ;\n\n\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n    @Override\n    public String getDescription() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/mybatis/MyBatisPlusConfig.java",
    "content": "package ai.chat2db.server.web.start.config.mybatis;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author moji\n * @version MyBatisPlusConfig.java, v 0.1 September 29, 2022 17:38 moji Exp $\n * @date 2022/09/29\n */\n@Configuration\npublic class MyBatisPlusConfig {\n\n    /**\n     * myBatisPlus Pagination plugin\n     */\n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));\n        return mybatisPlusInterceptor;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/oauth/SaLogForSlf4j.java",
    "content": "package ai.chat2db.server.web.start.config.oauth;\n\nimport cn.dev33.satoken.log.SaLog;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * satoken Log printing\n *\n * @author Shi Yi\n */\n@Slf4j\n@Component\npublic class SaLogForSlf4j implements SaLog {\n    @Override\n    public void trace(String str, Object... args) {\n        log.trace(str, args);\n    }\n\n    @Override\n    public void debug(String str, Object... args) {\n        log.debug(str, args);\n    }\n\n    @Override\n    public void info(String str, Object... args) {\n        log.info(str, args);\n    }\n\n    @Override\n    public void warn(String str, Object... args) {\n        log.trace(str, args);\n    }\n\n    @Override\n    public void error(String str, Object... args) {\n        log.error(str, args);\n    }\n\n    @Override\n    public void fatal(String str, Object... args) {\n        log.error(str, args);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/oauth/SaTokenConfigure.java",
    "content": "package ai.chat2db.server.web.start.config.oauth;\n\nimport cn.dev33.satoken.jwt.StpLogicJwtForStateless;\nimport cn.dev33.satoken.stp.StpLogic;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * satoken placement\n *\n * @author Shi Yi\n */\n@Configuration\npublic class SaTokenConfigure {\n    @Bean\n    public StpLogic ttpLogic() {\n        // Login display is stateless, so there is no need to rely on redis or the like.\n        // Can it be changed to ehcahe storage disk later?\n        return new StpLogicJwtForStateless();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/controller/oauth/OauthController.java",
    "content": "package ai.chat2db.server.web.start.controller.oauth;\n\nimport ai.chat2db.server.domain.api.enums.RoleCodeEnum;\nimport ai.chat2db.server.domain.api.enums.ValidStatusEnum;\nimport ai.chat2db.server.domain.api.model.User;\nimport ai.chat2db.server.domain.api.service.UserService;\nimport ai.chat2db.server.web.start.controller.oauth.request.LoginRequest;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.base.wrapper.result.DataResult;\nimport ai.chat2db.server.tools.common.model.LoginUser;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport cn.dev33.satoken.context.SaHolder;\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.dev33.satoken.util.SaTokenConsts;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.jetbrains.annotations.NotNull;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.Objects;\n\n/**\n * Login authorization service\n *\n * @author Jiaju Zhuang\n */\n@RestController\n@RequestMapping(\"/api/oauth\")\n@Slf4j\npublic class OauthController {\n\n    @Resource\n    private UserService userService;\n\n    /**\n     * Login with username and password\n     *\n     * @param request\n     * @return\n     */\n    @PostMapping(\"login_a\")\n    public DataResult login(@Validated @RequestBody LoginRequest request) {\n        //   Query user\n        User user = userService.query(request.getUserName()).getData();\n        this.validateUser(user);\n\n        // Successfully logged in without modifying the administrator password\n        if (this.validateAdmin(user)) {\n            return DataResult.of(doLogin(user));\n        }\n\n        if (!DigestUtil.bcryptCheck(request.getPassword(), user.getPassword())) {\n            throw new BusinessException(\"oauth.passwordIncorrect\");\n        }\n\n        return DataResult.of(doLogin(user));\n    }\n\n    private boolean validateAdmin(final @NotNull User user) {\n        return RoleCodeEnum.ADMIN.getDefaultUserId().equals(user.getId()) && RoleCodeEnum.ADMIN.getPassword().equals(\n                user.getPassword());\n    }\n\n    private void validateUser(final User user) {\n        if (Objects.isNull(user)) {\n            throw new BusinessException(\"oauth.userNameNotExits\");\n        }\n        if (!ValidStatusEnum.VALID.getCode().equals(user.getStatus())) {\n            throw new BusinessException(\"oauth.invalidUserName\");\n        }\n        if (RoleCodeEnum.DESKTOP.getDefaultUserId().equals(user.getId())) {\n            throw new BusinessException(\"oauth.IllegalUserName\");\n        }\n    }\n\n    private Object doLogin(User user) {\n        StpUtil.login(user.getId());\n        return SaHolder.getStorage().get(SaTokenConsts.JUST_CREATED_NOT_PREFIX);\n    }\n\n    /**\n     * Sign out\n     *\n     * @return\n     */\n    @PostMapping(\"logout_a\")\n    public ActionResult logout() {\n        StpUtil.logout();\n        return ActionResult.isSuccess();\n    }\n\n    /**\n     * user\n     *\n     * @return\n     */\n    @GetMapping(\"user\")\n    public DataResult<LoginUser> user() {\n        return DataResult.of(getLoginUser());\n    }\n\n    /**\n     * user\n     *\n     * @return\n     */\n    @GetMapping(\"user_a\")\n    public DataResult<LoginUser> usera() {\n        return DataResult.of(getLoginUser());\n    }\n\n    private LoginUser getLoginUser() {\n        return ContextUtils.queryLoginUser();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/controller/oauth/request/LoginRequest.java",
    "content": "package ai.chat2db.server.web.start.controller.oauth.request;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Log in\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class LoginRequest {\n    /**\n     * userName\n     */\n    @NotNull(message = \"Username can not be empty\")\n    private String userName;\n\n    /**\n     * password\n     */\n    @NotNull(message = \"password can not be blank\")\n    private String password;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/controller/thymeleaf/ThymeleafController.java",
    "content": "package ai.chat2db.server.web.start.controller.thymeleaf;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\n\n/**\n * Template engine configuration\n *\n * @author Jiaju Zhuang\n */\n@Controller\n@Slf4j\n@Order(Integer.MIN_VALUE)\npublic class ThymeleafController {\n\n    /**\n     * Front-end template settings\n     *\n     * @return\n     */\n    @GetMapping(value = {\"/\", \"/web/\", \"/web/**\",\"/login\",\"/workspace\",\"/dashboard\",\"/connections\",\"/team\"})\n    public String index() {\n        return \"index\";\n    }\n\n    @RequestMapping(value = \"/chat.html\", method={RequestMethod.GET}, produces=\"text/html;charset=utf-8\")\n    public String chat(){\n\n        return \"chat\";\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/EasyControllerExceptionHandler.java",
    "content": "package ai.chat2db.server.web.start.exception;\n\nimport ai.chat2db.server.web.start.exception.convertor.*;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.excption.SystemException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.exception.NeedLoggedInBusinessException;\nimport ai.chat2db.server.tools.common.exception.RedirectBusinessException;\nimport com.alibaba.fastjson2.JSON;\nimport com.google.common.collect.Maps;\nimport jakarta.servlet.http.HttpServletRequest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.validation.BindException;\nimport org.springframework.web.HttpMediaTypeNotAcceptableException;\nimport org.springframework.web.HttpMediaTypeNotSupportedException;\nimport org.springframework.web.HttpRequestMethodNotSupportedException;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.MissingRequestHeaderException;\nimport org.springframework.web.bind.MissingServletRequestParameterException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\nimport org.springframework.web.multipart.MultipartException;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport java.util.Map;\n\n/**\n * Intercepting Controller exceptions\n *\n * @author Shi Yi\n */\n@ControllerAdvice\n@Slf4j\n@Order(Ordered.HIGHEST_PRECEDENCE)\npublic class EasyControllerExceptionHandler {\n\n    /**\n     * All exception handling converters\n     */\n    public static final Map<Class<?>, ExceptionConvertor> EXCEPTION_CONVERTOR_MAP = Maps.newHashMap();\n\n    static {\n        EXCEPTION_CONVERTOR_MAP.put(MethodArgumentNotValidException.class,\n            new MethodArgumentNotValidExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(BindException.class, new BindExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(BusinessException.class, new BusinessExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(NeedLoggedInBusinessException.class, new BusinessExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(MissingServletRequestParameterException.class, new ParamExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(IllegalArgumentException.class, new ParamExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(MethodArgumentTypeMismatchException.class,\n            new MethodArgumentTypeMismatchExceptionConvertor());\n        EXCEPTION_CONVERTOR_MAP.put(MaxUploadSizeExceededException.class,\n            new MaxUploadSizeExceededExceptionConvertor());\n    }\n\n    /**\n     * Default converter\n     */\n    public static ExceptionConvertor DEFAULT_EXCEPTION_CONVERTOR = new DefaultExceptionConvertor();\n\n    /**\n     * Business abnormality\n     *\n     * @param request   request\n     * @param exception exception\n     * @return return\n     */\n    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class, IllegalArgumentException.class,\n        MissingServletRequestParameterException.class, MethodArgumentTypeMismatchException.class,\n        BusinessException.class, MaxUploadSizeExceededException.class,\n        HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotAcceptableException.class,\n        MultipartException.class, MissingRequestHeaderException.class, HttpMediaTypeNotSupportedException.class,\n        NeedLoggedInBusinessException.class})\n    @ResponseStatus(value = HttpStatus.OK)\n    @ResponseBody\n    public ActionResult handleBusinessException(HttpServletRequest request, Exception exception) {\n        ActionResult result = convert(exception);\n        log.info(\"Business exception occurred{}:{}\", request.getRequestURI(), result, exception);\n        return result;\n    }\n\n    /**\n     * Business abnormality\n     *\n     * @param request   request\n     * @param exception exception\n     * @return return\n     */\n    @ExceptionHandler({RedirectBusinessException.class})\n    public ModelAndView handleModelAndViewBizException(HttpServletRequest request, Exception exception) {\n        ModelAndView result = translateModelAndView(exception);\n        log.info(\"ModelAndView business exception occurred{}:{}\", request.getRequestURI(), result, exception);\n        return result;\n    }\n\n    public ModelAndView translateModelAndView(Throwable exception) {\n        // Parameter exception\n        if (exception instanceof RedirectBusinessException) {\n            RedirectBusinessException e = (RedirectBusinessException)exception;\n            return dealResponseModelAndView(null, e.getMessage(), e.getRedirect(), null, null);\n        }\n        // Jump to homepage by default\n        return new ModelAndView(\"redirect:/\");\n    }\n\n    private ModelAndView dealResponseModelAndView(String title, String errorMessage, String redirect, String href,\n        String buttonText) {\n        // If there is redirection information, jump\n        if (StringUtils.isNotBlank(redirect)) {\n            return new ModelAndView(\"redirect:\" + redirect);\n        }\n        // Jump to homepage by default\n        return new ModelAndView(\"redirect:/\");\n\n        // synchronous request\n        //return ModelAndViewUtils.error(title, errorMessage,href,buttonText);\n    }\n\n    /**\n     * System exception\n     *\n     * @param request   request\n     * @param exception exception\n     * @return return\n     */\n    @ExceptionHandler({SystemException.class})\n    @ResponseStatus(value = HttpStatus.OK)\n    @ResponseBody\n    public ActionResult handleSystemException(HttpServletRequest request, Exception exception) {\n        ActionResult result = convert(exception);\n        log.error(\"Business exception occurred{}:{}\", request.getRequestURI(), result, exception);\n        return result;\n    }\n\n    /**\n     * Unknown exception requires manual intervention to view logs\n     *\n     * @param request   request\n     * @param exception exception\n     * @return return\n     */\n    @ExceptionHandler(Exception.class)\n    @ResponseStatus(value = HttpStatus.OK)\n    @ResponseBody\n    public ActionResult handledException(HttpServletRequest request, Exception exception) {\n        ActionResult result = convert(exception);\n        log.error(\"An unknown exception occurred {}:{}, request parameters:{}\", request.getRequestURI(), result,\n            JSON.toJSONString(request.getParameterMap()),\n            exception);\n        return result;\n    }\n\n    public ActionResult convert(Throwable exception) {\n        ExceptionConvertor exceptionConvertor = EXCEPTION_CONVERTOR_MAP.get(exception.getClass());\n        if (exceptionConvertor == null) {\n            if (exception instanceof BusinessException) {\n                exceptionConvertor = EXCEPTION_CONVERTOR_MAP.get(BusinessException.class);\n            } else if (exception instanceof SystemException) {\n                exceptionConvertor = EXCEPTION_CONVERTOR_MAP.get(SystemException.class);\n            } else {\n                exceptionConvertor = DEFAULT_EXCEPTION_CONVERTOR;\n            }\n        }\n        return exceptionConvertor.convert(exception);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/BindExceptionConvertor.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.spi.util.ExceptionUtils;\nimport org.springframework.validation.BindException;\n\n/**\n * BindException\n *\n * @author Shi Yi\n */\npublic class BindExceptionConvertor implements ExceptionConvertor<BindException> {\n\n    @Override\n    public ActionResult convert(BindException exception) {\n        String message = ExceptionConvertorUtils.buildMessage(exception.getBindingResult());\n        return ActionResult.fail(\"common.paramError\", message, ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/BusinessExceptionConvertor.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.util.ExceptionUtils;\n\n/**\n * BusinessException\n *\n * @author Shi Yi\n */\npublic class BusinessExceptionConvertor implements ExceptionConvertor<BusinessException> {\n\n    @Override\n    public ActionResult convert(BusinessException exception) {\n        return ActionResult.fail(exception.getCode(), I18nUtils.getMessage(exception.getCode(), exception.getArgs()),\n            ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/DefaultExceptionConvertor.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.util.ExceptionUtils;\n\n/**\n * Default exception handling\n * Throw system exception directly\n *\n * @author Shi Yi\n */\npublic class DefaultExceptionConvertor implements ExceptionConvertor<Throwable> {\n\n    @Override\n    public ActionResult convert(Throwable exception) {\n        return ActionResult.fail(\"common.systemError\", I18nUtils.getMessage(\"common.systemError\"), ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/ExceptionConvertor.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\n\n/**\n * exception converter\n *\n * @author Shi Yi\n */\npublic interface ExceptionConvertor<T extends Throwable> {\n\n    /**\n     * Conversion exception\n     *\n     * @param exception\n     * @return\n     */\n    ActionResult convert(T exception);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/ExceptionConvertorUtils.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.constant.SymbolConstant;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.validation.FieldError;\nimport org.springframework.validation.ObjectError;\n\nimport java.util.List;\n\n/**\n * Conversion tool class\n *\n * @author Shi Yi\n */\npublic class ExceptionConvertorUtils {\n\n    /**\n     * Extract error message from BindingResult\n     *\n     * @param result\n     * @return\n     */\n    public static String buildMessage(BindingResult result) {\n        List<ObjectError> errors = result.getAllErrors();\n        if (CollectionUtils.isEmpty(errors)) {\n            return null;\n        }\n\n        int index = 1;\n        StringBuilder msg = new StringBuilder();\n        msg.append(I18nUtils.getMessage(\"common.paramCheckError\"));\n        for (ObjectError e : errors) {\n            msg.append(index++);\n            // got error message\n            msg.append(SymbolConstant.DOT);\n            if (e instanceof FieldError fieldError) {\n                msg.append(fieldError.getField());\n                msg.append(\" : \");\n            }\n            msg.append(e.getDefaultMessage());\n            msg.append(SymbolConstant.SEMICOLON);\n        }\n        return msg.toString();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/MaxUploadSizeExceededExceptionConvertor.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.util.ExceptionUtils;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\n\n/**\n * MaxUploadSizeExceededException\n *\n * @author Shi Yi\n */\npublic class MaxUploadSizeExceededExceptionConvertor implements ExceptionConvertor<MaxUploadSizeExceededException> {\n\n    @Override\n    public ActionResult convert(MaxUploadSizeExceededException exception) {\n        return ActionResult.fail(\"common.maxUploadSize\", I18nUtils.getMessage(\"common.maxUploadSize\"), ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/MethodArgumentNotValidExceptionConvertor.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.spi.util.ExceptionUtils;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\n\n/**\n * MethodArgumentNotValidException\n *\n * @author Shi Yi\n */\npublic class MethodArgumentNotValidExceptionConvertor implements ExceptionConvertor<MethodArgumentNotValidException> {\n\n    @Override\n    public ActionResult convert(MethodArgumentNotValidException exception) {\n        String message = ExceptionConvertorUtils.buildMessage(exception.getBindingResult());\n        return ActionResult.fail(\"common.paramError\", message, ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/MethodArgumentTypeMismatchExceptionConvertor.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.util.ExceptionUtils;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\n\n/**\n * MethodArgumentTypeMismatchException\n *\n * @author Shi Yi\n */\npublic class MethodArgumentTypeMismatchExceptionConvertor\n    implements ExceptionConvertor<MethodArgumentTypeMismatchException> {\n\n    @Override\n    public ActionResult convert(MethodArgumentTypeMismatchException exception) {\n        return ActionResult.fail(\"common.paramError\", I18nUtils.getMessage(\"common.paramError\"), ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/exception/convertor/ParamExceptionConvertor.java",
    "content": "package ai.chat2db.server.web.start.exception.convertor;\n\nimport ai.chat2db.server.tools.base.wrapper.result.ActionResult;\nimport ai.chat2db.spi.util.ExceptionUtils;\n\n/**\n * Parameter exceptions currently include：\n * ConstraintViolationException\n * MissingServletRequestParameterException\n * IllegalArgumentException\n *\n * @author Shi Yi\n */\npublic class ParamExceptionConvertor implements ExceptionConvertor<Throwable> {\n\n    @Override\n    public ActionResult convert(Throwable exception) {\n        return ActionResult.fail(\"common.paramError\", exception.getMessage(), ExceptionUtils.getErrorInfoFromException(exception));\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/log/EasyLogSink.java",
    "content": "package ai.chat2db.server.web.start.log;\n\nimport ai.chat2db.server.tools.common.util.LogUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\nimport org.thymeleaf.util.ContentTypeUtils;\nimport org.zalando.logbook.*;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\n\n/**\n * log\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\n@Component\npublic class EasyLogSink implements Sink {\n\n    @Override\n    public void write(final Precorrelation precorrelation, final HttpRequest request) {\n    }\n\n    @Override\n    public void write(final Correlation correlation, final HttpRequest request, final HttpResponse response) {\n        try {\n            printLog(correlation, request, response);\n        } catch (Exception e) {\n            log.error(\"Log exceptions\", e);\n        }\n    }\n\n    public void printLog(final Correlation correlation, final HttpRequest request, final HttpResponse response)\n        throws IOException {\n        // Encapsulate log object\n        WebLog webLog = new WebLog();\n\n        String method = request.getMethod();\n        // path\n        String path = request.getPath();\n\n        webLog.setMethod(method);\n        webLog.setPath(LogUtils.cutLog(path));\n        webLog.setQuery(LogUtils.cutLog(request.getQuery()));\n        webLog.setDuration(correlation.getDuration().toMillis());\n        webLog.setStartTime(LocalDateTime.ofInstant(correlation.getStart(), ZoneId.systemDefault()));\n        webLog.setEndTime(LocalDateTime.ofInstant(correlation.getEnd(), ZoneId.systemDefault()));\n        try {\n            webLog.setRequest(LogUtils.maskString(LogUtils.cutLog(new String(request.getBody(), StandardCharsets.UTF_8))));\n            if (ContentTypeUtils.isContentTypeJSON(response.getContentType()) || ContentTypeUtils.isContentTypeHTML(\n                response.getContentType())) {\n                webLog.setResponse(LogUtils.maskString(LogUtils.cutLog(new String(response.getBody(), StandardCharsets.UTF_8))));\n            } else {\n                webLog.setResponse(response.getContentType() + \":[\" + response.getBody().length + \"]\");\n            }\n        } catch (IOException e) {\n            log.warn(\"The request to obtain the log & returns an exception. Most likely, the user has closed the stream.\", e);\n        }\n        webLog.setIp(LogUtils.getClientIp(request));\n\n        String pathAndQuery = path;\n        if (StringUtils.isNotBlank(webLog.getQuery())) {\n            pathAndQuery += \"?\" + webLog.getQuery();\n        }\n        log.info(\"http : {}|{}|{}|{}|{}\", webLog.getMethod(), pathAndQuery, webLog.getDuration(),\n            webLog.getRequest(), webLog.getResponse());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/log/LogOncePerRequestFilter.java",
    "content": "package ai.chat2db.server.web.start.log;\n\nimport ai.chat2db.server.tools.common.util.LogUtils;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport lombok.extern.slf4j.Slf4j;\nimport org.slf4j.MDC;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport java.io.IOException;\n\n/**\n * Intercept the log and put in the trace id\n *\n * @author Jiaju Zhuang\n */\n@Order(Ordered.HIGHEST_PRECEDENCE)\n@Component\n@Slf4j\npublic class LogOncePerRequestFilter extends OncePerRequestFilter {\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n        throws\n        ServletException, IOException {\n        try {\n            MDC.put(LogUtils.TRACE_ID, LogUtils.generateTraceId());\n            filterChain.doFilter(request, response);\n        } finally {\n            MDC.remove(LogUtils.TRACE_ID);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/log/WebLog.java",
    "content": "package ai.chat2db.server.web.start.log;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.time.LocalDateTime;\n\n/**\n * log object\n *\n * @author Shi Yi\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class WebLog {\n    /**\n     * call method\n     */\n    private String method;\n\n    /**\n     * path\n     */\n    private String path;\n\n    /**\n     * Query conditions\n     */\n    private String query;\n\n    /**\n     * Time consuming ms\n     */\n    private Long duration;\n\n    /**\n     * Time consuming ms\n     */\n    private LocalDateTime startTime;\n\n    /**\n     * Time consuming ms\n     */\n    private LocalDateTime endTime;\n\n    /**\n     * request\n     */\n    private String request;\n\n    /**\n     * response\n     */\n    private String response;\n\n    /**\n     * IP address\n     */\n    private String ip;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/META-INF/spring.factories",
    "content": ""
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/application-dev.yml",
    "content": "#spring:\n#  datasource:\n#    # Configure the relative path of the built-in database\n#    url: jdbc:h2:~/.chat2db/db/chat2db_dev;FILE_LOCK=NO;MODE=MYSQL\n#    driver-class-name: org.h2.Driver\n# The port number\nserver:\n  port: 10821\n\nchat2db:\n  gateway:\n    base-url: http://test.sqlgpt.cn/gateway\n    model-base-url: http://test.sqlgpt.cn/gateway"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/application-release.yml",
    "content": "#spring:\n#  datasource:\n#    # Configure the relative path of the built-in database\n#    url: jdbc:h2:~/.chat2db/db/chat2db;MODE=MYSQL;FILE_LOCK=NO\n#    driver-class-name: org.h2.Driver\n# The port number\nserver:\n  port: 10824\n\nchat2db:\n  gateway:\n    base-url: http://test.sqlgpt.cn/gateway\n    model-base-url: http://test.sqlgpt.cn/gateway"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/application-test.yml",
    "content": "\n# port\nserver:\n  port: 10822\n\nchat2db:\n  gateway:\n    base-url: http://test.sqlgpt.cn/gateway\n    model-base-url: http://test.sqlgpt.cn/gateway"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/application.yml",
    "content": "spring:\n  # Default development environment\n  profiles:\n    active: dev\n  main:\n    allow-bean-definition-overriding: true\n  messages:\n    basename: i18n/messages\n    encoding: UTF-8\n    fallbackToSystemLocale: true\n  jmx:\n    enabled: false\n  # thymeleaf\n  thymeleaf:\n    prefix: classpath:/thymeleaf/\n    check-template-location: true\n    suffix: .html\n    servlet:\n      content-type: text/html\n    mode: HTML5\n  # static files\n  mvc:\n    static-path-pattern: /static/**\n  web:\n    resources:\n      static-locations[0]: classpath:/static/\n  #  Used for database table structure version management\n  flyway:\n    enabled: false\n  servlet:\n    multipart:\n      max-file-size: -1\n      max-request-size: -1\n  jackson:\n    serialization:\n      write-dates-as-timestamps: true\n\nchat2db:\n  version: 1.0.0\n\n# flywaydb outputs the log of executing sql\nlogging:\n  level:\n    org:\n      flywaydb: debug\n    ai:\n      chat2db:\n        server:\n          domain:\n            repository:\n              mapper: debug\n# Login function\nsa-token:\n  # token name (also cookie name)\n  token-name: CHAT2DB\n  timeout: 2592000\n  # Whether to allow concurrent logins with the same account (when true, simultaneous logins are allowed, when false, new logins crowd out old logins)\n  is-concurrent: true\n  # When multiple people log in to the same account, whether to share a token (when true, all logins share a token, when false, a new token is created for each login)\n  is-share: true\n  # token style\n  token-style: uuid\n  # Whether to output operation logs\n  is-log: true\n  is-write-header: true\n\nchatgpt:\n  apiKey: sk-xxxx\n  apiHost: https://api.openai.com/\n  # You can choose GPT3 or GPT35\n  version: GPT35\n  context:\n    length: 1\n\n# Print the HTTP log\nlogbook:\n  include:\n    - /api/**\n\n# Http Client\nforest:\n  connect-timeout: 30000\n  log-response-content: true\n  read-timeout: 30000\n  timeout: 30000"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/i18n/messages.properties",
    "content": "# common\ncommon.businessError=Please try resubmitting or refreshing the page later\ncommon.systemError=An exception occurs, you can view the exception details in the log in the help menu.\ncommon.needLoggedIn=Login required\ncommon.redirect=Redirect\ncommon.paramError=The parameter is incorrect\ncommon.paramDetailError=The parameter: {0} is incorrect\ncommon.paramCheckError=The following parameters are not valid:\ncommon.maxUploadSize=The file exceeds the maximum limit\ncommon.permissionDenied=Permission denied\ncommon.dataNotFound=Data not found\ncommon.dataAlreadyExists=The data already exists in the database\ncommon.dataAlreadyExistsWithParam=The data already exists in the database,{0}:{1}\n\noauth.userNameNotExits=The current account does not exist\noauth.IllegalUserName=The current account cannot be logged in. Please change your account\noauth.passwordIncorrect=The password you entered is incorrect\noauth.invalidUserName=The current account is invalid\n\n# dataSource\ndataSource.sqlAnalysisError=Invalid statements\n# connection\nconnection.error=Connection failed, please check the connection information\nconnection.ssh.error=SSH connection failed, please check the connection information\nconnection.driver.load.error=Failed to load driver class, please check the driver jar package\n# sqlResult\nsqlResult.rowNumber=Row Number\nsqlResult.success=Execution successful\n\nuser.canNotOperateSystemAccount=System accounts cannot be operated\nexecute.exportCsv=For more data, please click on Export CSV\n\nsettings.permissionDeniedForAiConfig=Please contact the administrator to set ApiKey in \"Settings ->Custom Ai\""
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/i18n/messages_en_US.properties",
    "content": "common.businessError=Please try resubmitting or refreshing the page later\ncommon.systemError=An exception occurs, you can view the exception details in the log in the help menu.\ncommon.needLoggedIn=Login required\ncommon.redirect=Redirect\ncommon.paramError=The parameter is incorrect\ncommon.paramDetailError=The parameter: {0} is incorrect\ncommon.paramCheckError=The following parameters are not valid\ncommon.maxUploadSize=The file exceeds the maximum limit\ncommon.permissionDenied=Permission denied\ncommon.dataNotFound=Data not found\ncommon.dataAlreadyExists=The data already exists in the database\ncommon.dataAlreadyExistsWithParam=The data already exists in the database,{0}:{1}\n\noauth.userNameNotExits=The current account does not exist\noauth.IllegalUserName=The current account cannot be logged in. Please change your account\noauth.passwordError=The password you entered is incorrect\noauth.invalidUserName=The current account is invalid\n\n\ndataSource.sqlAnalysisError=Invalid statements\nconnection.error=Connection failed, please check the connection information\nconnection.ssh.error=SSH connection failed, please check the connection information\nconnection.driver.load.error=Failed to load driver class, please check the driver jar package\n# sqlResult\nsqlResult.rowNumber=Row Number\nsqlResult.success=Execution successful\n\nuser.canNotOperateSystemAccount=System accounts cannot be operated\nexecute.exportCsv=For more data, please click on Export CSV\n\nsettings.permissionDeniedForAiConfig=Please contact the administrator to set ApiKey in \"Settings ->Custom Ai\"\nmain.indexName=Name\nmain.indexFieldName=Column Name\nmain.indexType=Index Type\nmain.indexMethod=Index Method\nmain.indexNote=Index Comment\nmain.fieldNo=No\nmain.fieldName=Field Name\nmain.fieldType=Column Type\nmain.fieldLength=Length\nmain.fieldIfEmpty=Nullable\nmain.fieldDefault=Column Default\nmain.fieldDecimalPlaces=Decimal Places\nmain.fieldNote=Column Comment\nmain.databaseText=Database:\nmain.sheetName=Table Structure\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/i18n/messages_zh_CN.properties",
    "content": "common.businessError=请尝试重新提交或者刷新页面\ncommon.systemError=系统发生异常，可在帮助中点击日志查看异常详情。\ncommon.needLoggedIn=需要登陆页面\ncommon.redirect=重定向页面\ncommon.paramError=您输入的参数异常\ncommon.paramDetailError=您输入的参数:{0},存在异常\ncommon.paramCheckError=请检查以下参数:\ncommon.maxUploadSize=您输入的文件超过最大限制\ncommon.permissionDenied=您没有权限访问该页面\ncommon.dataNotFound=您访问的数据不存在\ncommon.dataAlreadyExists=数据库总已经存在该数据\ncommon.dataAlreadyExistsWithParam=数据库总已经存在该数据,{0}:{1}\n\noauth.userNameNotExits=当前账号不存在\noauth.IllegalUserName=当前账号无法登录，请换一个账号\noauth.passwordIncorrect=您输入的密码有误\noauth.invalidUserName=您输入的账号已经失效\n\ndataSource.sqlAnalysisError=不合法的执行语句\nconnection.error=数据库链接异常，请检查数据库配置\nconnection.ssh.error=SSH 链接异常，请检查SSH配置\nconnection.driver.load.error=数据库驱动加载异常，请检查驱动配置\n# sqlResult\nsqlResult.rowNumber=行号\nsqlResult.success=执行成功\n\nuser.canNotOperateSystemAccount=不能操作系统账号\nexecute.exportCsv=更多数据请点击导出csv\n\nsettings.permissionDeniedForAiConfig=请联系管理员在 “设置->自定义AI” 里面设置ApiKey\nmain.indexName=名称\nmain.indexFieldName=字段\nmain.indexType=索引类型\nmain.indexMethod=索引方法\nmain.indexNote=注释\nmain.fieldNo=序号\nmain.fieldName=字段名\nmain.fieldType=数据类型\nmain.fieldLength=长度\nmain.fieldIfEmpty=不是null\nmain.fieldDefault=默认值\nmain.fieldDecimalPlaces=小数位\nmain.fieldNote=备注\nmain.databaseText=数据库：\nmain.sheetName=表结构\n"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <springProperty scope=\"context\" name=\"APP_NAME\" source=\"project.name\"/>\n    <property name=\"LOG_PATH\" value=\"${user.home}/.chat2db/logs\"/>\n    <property name=\"LOG_FILE\" value=\"${LOG_PATH}/application.log\"/>\n\n    <property name=\"EASY_FILE_LOG_PATTERN\"\n              value=\"${EASY_FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39}.%line : %X{TRACE_ID} | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}\"/>\n    <property name=\"EASY_CONSOLE_LOG_PATTERN\"\n              value=\"${EASY_CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}.%line){cyan} %clr(:){faint} %X{TRACE_ID} | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}\"/>\n\n    <appender name=\"APPLICATION\"\n              class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${LOG_FILE}</file>\n        <encoder>\n            <pattern>${EASY_FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>1GB</maxFileSize>\n            <totalSizeCap>10GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${EASY_CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"APPLICATION\"/>\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n</configuration>"
  },
  {
    "path": "chat2db-server/chat2db-server-web-start/src/main/resources/thymeleaf/template.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Chat2DB</title>\n</head>\n<body>\n  \n</body>\n</html>"
  },
  {
    "path": "chat2db-server/chat2db-spi/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>chat2db-server-parent</artifactId>\n        <groupId>ai.chat2db</groupId>\n        <version>${revision}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>chat2db-spi</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>ai.chat2db</groupId>\n            <artifactId>chat2db-server-tools-common</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid</artifactId>\n        </dependency>\n\n        <!-- Supported connected databases -->\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.github.mwiede</groupId>\n            <artifactId>jsch</artifactId>\n            <version>0.2.9</version>\n        </dependency>\n        <dependency>\n            <groupId>org.bouncycastle</groupId>\n            <artifactId>bcprov-jdk18on</artifactId>\n            <version>1.71</version>\n        </dependency>\n        <dependency>\n            <groupId>com.oracle.ojdbc</groupId>\n            <artifactId>orai18n</artifactId>\n            <version>19.3.0.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>8.0.30</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>okhttp</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.zaxxer</groupId>\n            <artifactId>HikariCP</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>commons-beanutils</groupId>\n            <artifactId>commons-beanutils</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.github.jsqlparser</groupId>\n            <artifactId>jsqlparser</artifactId>\n            <version>4.6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.locationtech.jts</groupId>\n            <artifactId>jts-core</artifactId>\n            <version>1.19.0</version> <!-- Make sure to use the latest version -->\n        </dependency>\n\n        <dependency>\n            <groupId>org.antlr</groupId>\n            <artifactId>antlr4</artifactId>\n            <version>4.9.1</version>\n        </dependency>\n        <dependency>\n            <groupId>com.oceanbase</groupId>\n            <artifactId>ob-sql-parser</artifactId>\n            <version>1.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.tika</groupId>\n            <artifactId>tika-core</artifactId>\n            <version>2.7.0</version>\n        </dependency>\n    </dependencies>\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>3.2.1</version> <!-- 使用最新版本 -->\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <goals>\n                            <goal>jar</goal> <!-- 打包源代码成jar -->\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/ColumnBuilder.java",
    "content": "package ai.chat2db.spi;\n\nimport ai.chat2db.spi.model.TableColumn;\n\npublic interface ColumnBuilder {\n\n    /**\n     * Generate column sql\n     * @param column\n     * @return\n     */\n    String buildCreateColumnSql(TableColumn column);\n\n\n    /**\n     * Build modify column sql\n     * @param tableColumn\n     * @return\n     */\n    String buildModifyColumn(TableColumn tableColumn);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/CommandExecutor.java",
    "content": "package ai.chat2db.spi;\n\nimport ai.chat2db.spi.model.Command;\nimport ai.chat2db.spi.model.ExecuteResult;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * Command executor\n * <p>\n * The command executor is used to execute the command.\n * <br>\n */\npublic interface CommandExecutor {\n\n    /**\n     * Execute command\n     */\n    List<ExecuteResult> execute(Command command);\n\n\n    /**\n     * Execute command\n     */\n    ExecuteResult executeUpdate(String sql, Connection connection, int n)throws SQLException;\n\n\n    /**\n     * Execute command\n     */\n    List<ExecuteResult> executeSelectTable(Command command);\n\n\n    /**\n     *\n     *\n     */\n     ExecuteResult execute(final String sql, Connection connection, boolean limitRowSize, Integer offset,\n                           Integer count)\n            throws SQLException;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/DBManage.java",
    "content": "package ai.chat2db.spi;\n\nimport ai.chat2db.spi.model.AsyncContext;\nimport ai.chat2db.spi.model.Function;\nimport ai.chat2db.spi.model.Procedure;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport jakarta.validation.constraints.NotEmpty;\nimport jakarta.validation.constraints.NotNull;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\n/**\n * @author jipengfei\n * @version : DBManage.java\n */\npublic interface DBManage {\n\n    /**\n     * Create connection\n     *\n     * @param connectInfo\n     */\n    Connection getConnection(ConnectInfo connectInfo);\n\n    /**\n     * @param database\n     */\n    void connectDatabase(Connection connection, String database);\n\n    /**\n     * Modify database name\n     *\n     * @param databaseName\n     * @param newDatabaseName\n     */\n    void modifyDatabase(Connection connection, String databaseName, String newDatabaseName);\n\n    /**\n     * Create database\n     *\n     * @param databaseName\n     */\n    void createDatabase(Connection connection, String databaseName);\n\n    /**\n     * Delete database\n     *\n     * @param databaseName\n     */\n    void dropDatabase(Connection connection, String databaseName);\n\n    /**\n     * Create schema\n     *\n     * @param databaseName\n     * @param schemaName\n     */\n    void createSchema(Connection connection, String databaseName, String schemaName);\n\n    /**\n     * Delete schema\n     *\n     * @param databaseName\n     * @param schemaName\n     */\n    void dropSchema(Connection connection, String databaseName, String schemaName);\n\n    /**\n     * Modify schema\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param newSchemaName\n     */\n    void modifySchema(Connection connection, String databaseName, String schemaName, String newSchemaName);\n\n    /**\n     * Delete table structure\n     *\n     * @param databaseName\n     * @param tableName\n     * @return\n     */\n    void dropTable(Connection connection, @NotEmpty String databaseName, String schemaName, @NotEmpty String tableName);\n\n    /**\n     * Delete sequence structure\n     *\n     * @param databaseName\n     * @param sequenceName\n     * @return\n     */\n    void dropSequence(Connection connection, @NotEmpty String databaseName, String schemaName, @NotEmpty String sequenceName);\n\n    /**\n     * delete function\n     *\n     * @param databaseName\n     * @param functionName\n     * @return\n     */\n    void dropFunction(Connection connection, @NotEmpty String databaseName, String schemaName,\n                      @NotEmpty String functionName);\n\n    /**\n     * delete trigger\n     *\n     * @param databaseName\n     * @param triggerName\n     * @return\n     */\n    void dropTrigger(Connection connection, @NotEmpty String databaseName, String schemaName,\n                     @NotEmpty String triggerName);\n\n    /**\n     * Delete stored procedure\n     *\n     * @param databaseName\n     * @param triggerName\n     * @return\n     */\n    void dropProcedure(Connection connection, @NotEmpty String databaseName, String schemaName,\n                       @NotEmpty String triggerName);\n\n    /**\n     * Update stored procedure\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param procedure\n     */\n    void updateProcedure(Connection connection, @NotEmpty String databaseName, String schemaName, @NotNull Procedure procedure) throws SQLException;\n\n    /**\n     * Export database\n     *\n     * @param databaseName\n     * @param schemaName\n     * @return\n     */\n    void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException;\n\n    /**\n     * Export database data\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @return\n     */\n    void exportTable(Connection connection, String databaseName, String schemaName,String tableName,AsyncContext asyncContext) throws SQLException;\n\n\n    /**\n     * truncate table\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @throws SQLException\n     */\n    void truncateTable(Connection connection, String databaseName, String schemaName, String tableName)throws SQLException;\n\n\n    /**\n     * copy table\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @param newTableName\n     * @return\n     */\n    void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName,boolean copyData) throws SQLException;\n\n    /**\n     * delete procedure\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param procedure\n     */\n    void deleteProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure);\n\n    /**\n     * delete function\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param function\n     */\n    void deleteFunction(Connection connection, String databaseName, String schemaName, Function function);\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/MetaData.java",
    "content": "package ai.chat2db.spi;\n\nimport java.sql.Connection;\nimport java.util.List;\n\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.spi.model.*;\nimport jakarta.validation.constraints.NotEmpty;\n\n/**\n * Get database metadata information.\n *\n * @author jipengfei\n * @version : MetaData.java\n */\npublic interface MetaData {\n    /**\n     * Query all databases.\n     *\n     * @param connection\n     * @return\n     */\n    List<Database> databases(Connection connection);\n\n    /**\n     * Querying all schemas under a database\n     *\n     * @param connection\n     * @param databaseName\n     * @return\n     */\n    List<Schema> schemas(Connection connection, String databaseName);\n\n    /**\n     * Querying DDL information\n     *\n     * @param connection\n     * @param databaseName\n     * @param tableName\n     * @return\n     */\n    String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,\n                    @NotEmpty String tableName);\n\n    /**\n     * Querying all table under a schema.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @return\n     */\n    List<Table> tables(Connection connection, @NotEmpty String databaseName, String schemaName, String tableName);\n\n    /**\n     * Querying all table name under a schema.\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @return\n     */\n    List<String> tableNames(Connection connection, @NotEmpty String databaseName, String schemaName, String tableName);\n\n\n    /**\n     * Querying all table under a schema.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableNamePattern\n     * @param pageNo\n     * @param pageSize\n     * @return\n     */\n    PageResult<Table> tables(Connection connection, String databaseName, String schemaName, String tableNamePattern, int pageNo, int pageSize);\n    /**\n     * Querying view information.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param viewName\n     * @return\n     */\n    Table view(Connection connection, @NotEmpty String databaseName, String schemaName, String viewName);\n\n\n    /** query view names\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @return\n     */\n    List<String> viewNames(Connection connection, @NotEmpty String databaseName, String schemaName);\n\n    /**\n     * Querying all views under a schema.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @return\n     */\n    List<Table> views(Connection connection, @NotEmpty String databaseName, String schemaName);\n\n    /**\n     * Querying all functions under a schema.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @return\n     */\n    List<Function> functions(Connection connection, @NotEmpty String databaseName, String schemaName);\n\n    /**\n     * Querying all triggers under a schema.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @return\n     */\n    List<Trigger> triggers(Connection connection, @NotEmpty String databaseName, String schemaName);\n\n    /**\n     * Querying all procedures under a schema.\n     *\n     * @param connection\n     * @param schemaName\n     * @param databaseName\n     * @return\n     */\n    List<Procedure> procedures(Connection connection, @NotEmpty String databaseName, String schemaName);\n\n    /**\n     * Querying all columns under a table.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @return\n     */\n    List<TableColumn> columns(Connection connection, @NotEmpty String databaseName, String schemaName,\n                              @NotEmpty String tableName);\n\n    /**\n     * Querying all columns under a table.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @param columnName\n     * @return\n     */\n    List<TableColumn> columns(Connection connection, @NotEmpty String databaseName, String schemaName, String tableName,\n                              String columnName);\n\n    /**\n     * Querying all indexes under a table.\n     *\n     * @param connection\n     * @param databaseName\n     * @param databaseName\n     * @return\n     */\n    List<TableIndex> indexes(Connection connection, @NotEmpty String databaseName, String schemaName,\n                             @NotEmpty String tableName);\n\n    /**\n     * Querying function detail under a schema.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param functionName\n     * @return\n     */\n    Function function(Connection connection, @NotEmpty String databaseName, String schemaName, String functionName);\n\n    /**\n     * Querying  trigger  under a schema.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param triggerName\n     * @return\n     */\n    Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName, String triggerName);\n\n    /**\n     * Querying all procedures under a schema.\n     *\n     * @param connection\n     * @param schemaName\n     * @param databaseName\n     * @param procedureName\n     * @return\n     */\n    Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName, String procedureName);\n\n\n    /**\n     * @param connection\n     * @return\n     */\n    List<Type> types(Connection connection);\n\n\n    /**\n     * Get sql builder.\n     *\n     * @return\n     */\n    SqlBuilder getSqlBuilder();\n\n\n    /**\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @return\n     */\n    TableMeta getTableMeta(String databaseName, String schemaName, String tableName);\n\n\n    /**\n     * Get meta data name.\n     *\n     */\n    String getMetaDataName(String ...names);\n\n\n    /**\n     * Get column builder.\n     *\n     */\n    ValueProcessor getValueProcessor();\n\n\n    /**\n     * Get command executor.\n     */\n    CommandExecutor getCommandExecutor();\n\n    /**\n     * Get system databases.\n     * @return\n     */\n    List<String> getSystemDatabases();\n\n    /**\n     * Get system schemas.\n     * @return\n     */\n    List<String> getSystemSchemas();\n\n    /**\n     * Querying DDL information\n     *\n     * @param connection\n     * @param databaseName\n     * @param tableName\n     * @return\n     */\n    String sequenceDDL(Connection connection, @NotEmpty String databaseName, String schemaName,\n                       @NotEmpty String tableName);\n\n    /**\n     * Querying sequences simple information\n     *\n     * @param connection\n     * @param databaseName\n     * @return\n     */\n    List<SimpleSequence> sequences(Connection connection, String databaseName, String schemaName);\n\n    /**\n     * Querying all sequence under a schema.\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param sequenceName\n     * @return\n     */\n    Sequence sequences(Connection connection, @NotEmpty String databaseName, String schemaName, String sequenceName);\n\n    List<String> usernames(Connection connection);\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/Plugin.java",
    "content": "\npackage ai.chat2db.spi;\n\nimport ai.chat2db.spi.config.DBConfig;\n\n/**\n * @author jipengfei\n * @version : Plugin.java\n */\npublic interface Plugin {\n\n    /**\n     * Get DB configuration information.\n     *\n     * @return\n     */\n    DBConfig getDBConfig();\n\n    /**\n     * Query db metadata information.\n     *\n     * @return\n     */\n    MetaData getMetaData();\n\n    /**\n     *\n     * @return\n     */\n    DBManage getDBManage();\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/SqlBuilder.java",
    "content": "package ai.chat2db.spi;\n\nimport ai.chat2db.spi.model.*;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface SqlBuilder<T> {\n\n    /**\n     * Generate create table sql\n     *\n     * @param table\n     * @return\n     */\n    String buildCreateTableSql(T table);\n\n\n    /**\n     * Generate modify table sql\n     *\n     * @param newTable\n     * @param oldTable\n     * @return\n     */\n    String buildModifyTaleSql(T oldTable, T newTable);\n\n\n    /**\n     * Generate page limit sql\n     *\n     * @param sql\n     * @param offset\n     * @param pageNo\n     * @param pageSize\n     * @return\n     */\n    String pageLimit(String sql, int offset, int pageNo, int pageSize);\n\n\n    /**\n     * Generate create database sql\n     *\n     * @param database\n     * @return\n     */\n    String buildCreateDatabaseSql(Database database);\n\n\n    /**\n     * @param oldDatabase\n     * @param newDatabase\n     * @return\n     */\n    String buildModifyDatabaseSql(Database oldDatabase, Database newDatabase);\n\n\n    /**\n     * @param schemaName\n     * @return\n     */\n    String buildCreateSchemaSql(Schema schemaName);\n\n\n    /**\n     * @param oldSchemaName\n     * @param newSchemaName\n     * @return\n     */\n    String buildModifySchemaSql(String oldSchemaName, String newSchemaName);\n\n    /**\n     * @param originSql\n     * @param orderByList\n     * @return\n     */\n    String buildOrderBySql(String originSql, List<OrderBy> orderByList);\n\n    /**\n     * @param originSql\n     * @param groupByList\n     * @return\n     */\n    String buildGroupBySql(String originSql, List<String> groupByList);\n\n\n    /**\n     * generate sql based on results\n     */\n    String buildSqlByQuery(QueryResult queryResult);\n\n    /**\n     * DML SQL\n     *\n     * @param table\n     * @param type\n     * @return\n     */\n    String getTableDmlSql(T table, String type);\n\n    /**\n     *\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @return\n     */\n    String buildTableQuerySql(String databaseName, String schemaName, String tableName);\n\n    /**\n     * Generate create sequence sql\n     *\n     * @param sequence\n     * @return\n     */\n    String buildCreateSequenceSql(Sequence sequence);\n\n\n    /**\n     * Generate modify sequence sql\n     *\n     * @param newSequence\n     * @param oldSequence\n     * @return\n     */\n    String buildModifySequenceSql(Sequence oldSequence, Sequence newSequence);\n\n    String buildSingleInsertSql(String databaseName, String schemaName, String tableName, List<String> columnList, List<String> valueList);\n\n    String buildMultiInsertSql(String databaseName, String schemaName, String tableName, List<String> columnList, List<List<String>> valueLists);\n\n    String buildUpdateSql(String databaseName, String schemaName, String tableName, Map<String, String> row,Map<String,String> primaryKeyMap);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/ValueHandler.java",
    "content": "//package ai.chat2db.spi;\n//\n//import java.sql.ResultSet;\n//import java.sql.SQLException;\n//\n//public interface ValueHandler {\n//\n//    /**\n//     * Process column values in the result set\n//     * @param rs\n//     * @param index\n//     * @param limitSize\n//     * @return\n//     * @throws SQLException\n//     */\n//    String getString(ResultSet rs, int index, boolean limitSize)throws SQLException;\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/ValueProcessor.java",
    "content": "package ai.chat2db.spi;\n\n\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\n\npublic interface ValueProcessor {\n\n    /**\n     * Converts a given value into a format suitable for use in an SQL statement\n     * <br>\n     * Example:\n     * <br>\n     * Input oracle DATE : '2024-05-29 11:35:20.0'\n     * <br>\n     * Output for Oracle DATE: TO_DATE('2024-05-29 14:25:00', 'YYYY-MM-DD HH24:MI:SS')\n     */\n    String getSqlValueString(SQLDataValue dataValue);\n\n\n    /**\n     * 将JDBC数据值对象转换为适合前端展示的字符串格式。\n     * <p>\n     * 它旨在处理包括但不限于数字、日期、字符串以及特殊的空数据，确保这些数据\n     * 在传递到前端用户界面时是格式化良好且可理解的。\n     *\n     * @param dataValue ResultSetMetaData, ResultSet, columnIndex的组合对象，用于获取数据值。\n     * @return 一个格式化后的字符串，适配于前端展示。例如，日期可能会转换为\"YYYY-MM-DD\"格式，以方便用户直观理解。\n     */\n    String getJdbcValue(JDBCDataValue dataValue);\n\n    /**\n     * 将从JDBC ResultSet中获取的数据值转换并构造为适合DML语句的格式。\n     *\n     * @param dataValue JDBC数据源中检索出的数据值对象，用于准备DML操作的值。\n     *\n     * @return 一个格式化后的字符串，可以直接用于DML语句中，确保数据的正确插入或更新。\n     */\n    String getJdbcSqlValueString(JDBCDataValue dataValue);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/config/DBConfig.java",
    "content": "\npackage ai.chat2db.spi.config;\n\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.util.List;\n\n/**\n * @author jipengfei\n * @version : DBConfig.java\n */\npublic class DBConfig {\n\n    /**\n     * MYSQL POSTGRESQL ...\n     */\n    private String dbType;\n\n    /**\n     * Mysql PostgreSQL ...\n     */\n    private String name;\n\n    /**\n     * defaultDriverConfig\n     */\n    private DriverConfig defaultDriverConfig;\n\n    /**\n     * List of supported drivers\n     */\n    private List<DriverConfig> driverConfigList;\n\n    /**\n     * Create table statement\n     */\n    private String simpleCreateTable;\n\n    /**\n     * Modify table structure\n     */\n    private String simpleAlterTable;\n\n\n    private boolean supportDatabase;\n\n\n    private boolean supportSchema;\n\n    public boolean isSupportDatabase() {\n        return supportDatabase;\n    }\n\n    public void setSupportDatabase(boolean supportDatabase) {\n        this.supportDatabase = supportDatabase;\n    }\n\n    public boolean isSupportSchema() {\n        return supportSchema;\n    }\n\n    public void setSupportSchema(boolean supportSchema) {\n        this.supportSchema = supportSchema;\n    }\n\n    public String getDbType() {\n        return dbType;\n    }\n\n    public void setDbType(String dbType) {\n        this.dbType = dbType;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public DriverConfig getDefaultDriverConfig() {\n        if (this.defaultDriverConfig != null) {\n            return this.defaultDriverConfig;\n        } else {\n            if (!CollectionUtils.isEmpty(driverConfigList)) {\n                for (DriverConfig driverConfig : driverConfigList) {\n                    if (driverConfig.isDefaultDriver()) {\n                        return driverConfig;\n                    }\n                }\n                return driverConfigList.get(0);\n            }\n        }\n        return null;\n    }\n\n    public void setDefaultDriverConfig(DriverConfig defaultDriverConfig) {\n        this.defaultDriverConfig = defaultDriverConfig;\n    }\n\n    public List<DriverConfig> getDriverConfigList() {\n        return driverConfigList;\n    }\n\n    public void setDriverConfigList(List<DriverConfig> driverConfigList) {\n        this.driverConfigList = driverConfigList;\n        if (!CollectionUtils.isEmpty(driverConfigList)) {\n            for (DriverConfig driverConfig : driverConfigList) {\n                if (driverConfig.isDefaultDriver()) {\n                    this.defaultDriverConfig = driverConfig;\n                    break;\n                }\n            }\n        }\n    }\n\n    public String getSimpleCreateTable() {\n        return simpleCreateTable;\n    }\n\n    public void setSimpleCreateTable(String simpleCreateTable) {\n        this.simpleCreateTable = simpleCreateTable;\n    }\n\n    public String getSimpleAlterTable() {\n        return simpleAlterTable;\n    }\n\n    public void setSimpleAlterTable(String simpleAlterTable) {\n        this.simpleAlterTable = simpleAlterTable;\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/config/DriverConfig.java",
    "content": "\npackage ai.chat2db.spi.config;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport ai.chat2db.spi.model.KeyValue;\nimport lombok.Data;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author jipengfei\n * @version : DriverConfig.java\n */\n@Data\npublic class DriverConfig  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * url\n     */\n    private String url;\n    /**\n     * jdbcDriver\n     */\n    private String jdbcDriver;\n\n    /**\n     * jdbcDriverClass\n     */\n    private String jdbcDriverClass;\n\n    /**\n     * downloadJdbcDriverUrls\n     */\n    private List<String> downloadJdbcDriverUrls;\n\n    /**\n     * dbType\n     */\n    private String dbType;\n\n    /**\n     * customize\n     */\n    private boolean custom;\n\n    /**\n     * properties\n     */\n    private List<KeyValue> extendInfo;\n\n\n    private boolean defaultDriver;\n\n    public boolean notEmpty() {\n       return StringUtils.isNotBlank(getJdbcDriver()) && StringUtils.isNotBlank(\n            getJdbcDriverClass());\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/enums/CellTypeEnum.java",
    "content": "package ai.chat2db.spi.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * Driver class enumeration\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum CellTypeEnum implements BaseEnum<String> {\n    /**\n     * string\n     */\n    STRING(\"string\"),\n\n    /**\n     * number\n     */\n    BIG_DECIMAL(\"number\"),\n\n    /**\n     * date\n     */\n    DATE(\"date\"),\n\n    /**\n     * binary stream\n     */\n    BYTE(\"binary stream\"),\n\n    /**\n     * empty data\n     */\n    EMPTY(\"empty data\"),\n    ;\n\n    final String description;\n\n    CellTypeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/enums/CollationEnum.java",
    "content": "package ai.chat2db.spi.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport ai.chat2db.server.tools.common.util.EasyEnumUtils;\nimport com.alibaba.druid.sql.ast.SQLOrderingSpecification;\nimport lombok.Getter;\n\n/**\n * Sorted enumeration\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum CollationEnum implements BaseEnum<String> {\n    /**\n     * ASC\n     */\n    ASC(\"asc\", SQLOrderingSpecification.ASC),\n\n    /**\n     * DESC\n     */\n    DESC(\"desc\", SQLOrderingSpecification.DESC),\n\n    ;\n\n    final String description;\n\n    final SQLOrderingSpecification sqlOrderingSpecification;\n\n    CollationEnum(String description, SQLOrderingSpecification sqlOrderingSpecification) {\n        this.description = description;\n        this.sqlOrderingSpecification = sqlOrderingSpecification;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n    public static boolean equals(String collation1, String collation2) {\n        return equals(EasyEnumUtils.getEnum(CollationEnum.class, collation1),\n            EasyEnumUtils.getEnum(CollationEnum.class, collation2));\n    }\n\n    public static boolean equals(CollationEnum collation1, CollationEnum collation2) {\n        // The same returns directly\n        if (collation1 == collation2) {\n            return true;\n        }\n        // One of them is in reverse order, which means they are different. The others are the same.\n        return !(collation1 == CollationEnum.DESC || collation2 == CollationEnum.DESC);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/enums/DataTypeEnum.java",
    "content": "package ai.chat2db.spi.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * Driver class enumeration\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum DataTypeEnum implements BaseEnum<String> {\n    /**\n     * Boolean value\n     */\n    BOOLEAN(\"Boolean value\"),\n\n    /**\n     * number\n     */\n    NUMERIC(\"number\"),\n\n    /**\n     * string\n     */\n    STRING(\"string\"),\n\n    /**\n     * date\n     */\n    DATETIME(\"date\"),\n\n    /**\n     * binary\n     */\n    BINARY(\"binary\"),\n\n    /**\n     * content\n     */\n    CONTENT(\"content\"),\n\n    /**\n     * structure\n     */\n    STRUCT(\"structure\"),\n\n    /**\n     * document\n     */\n    DOCUMENT(\"document\"),\n\n    /**\n     * array\n     */\n    ARRAY(\"array\"),\n\n    /**\n     * object\n     */\n    OBJECT(\"object\"),\n\n    /**\n     * reference\n     */\n    REFERENCE(\"reference\"),\n\n    /**\n     * rowid\n     */\n    ROWID(\"rowid\"),\n\n    /**\n     * any\n     */\n    ANY(\"any\"),\n\n    /**\n     * unknow\n     */\n    UNKNOWN(\"unknow\"),\n\n    /**\n     * Row number\n     */\n    CHAT2DB_ROW_NUMBER(\"Row number\"),\n    ;\n\n    final String description;\n\n    DataTypeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n\n    public static DataTypeEnum getByCode(String code) {\n        for (DataTypeEnum value : DataTypeEnum.values()) {\n            if (value.getCode().equals(code)) {\n                return value;\n            }\n        }\n        return DataTypeEnum.UNKNOWN;\n    }\n\n    public String getSqlValue(String value) {\n        if (this == DataTypeEnum.BOOLEAN) {\n            if (\"true\".equalsIgnoreCase(value) || \"false\".equalsIgnoreCase(value)) {\n                return value;\n            } else {\n                return \"'\" + value + \"'\";\n            }\n        }\n        if (this == DataTypeEnum.NUMERIC) {\n            return value;\n        }\n        if (this == DataTypeEnum.STRING) {\n            return getStringValue(value);\n        }\n        if (this == DataTypeEnum.DATETIME) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.BINARY) {\n            return \"''\";\n        }\n        if (this == DataTypeEnum.CONTENT) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.STRUCT) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.DOCUMENT) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.ARRAY) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.OBJECT) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.REFERENCE) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.ROWID) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.ANY) {\n            return \"'\" + value + \"'\";\n        }\n        if (this == DataTypeEnum.UNKNOWN) {\n            return \"'\" + value + \"'\";\n        }\n        return \"'\" + value + \"'\";\n    }\n\n    public static String getStringValue(String value) {\n        if (StringUtils.isBlank(value)) {\n            return \"'\" + value + \"'\";\n        }\n        value = value.replace(\"\\\\\", \"\\\\\\\\\");\n        value = value.replace(\"'\", \"\\\\'\");\n        value = value.replace(\"\\\"\", \"\\\\\\\"\");\n        return \"'\" + value + \"'\";\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/enums/DmlType.java",
    "content": "package ai.chat2db.spi.enums;\n\npublic enum DmlType {\n    INSERT, UPDATE, DELETE, SELECT\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/enums/EditStatus.java",
    "content": "package ai.chat2db.spi.enums;\n\npublic enum EditStatus {\n\n    DELETE,\n\n    ADD,\n\n    MODIFY;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/enums/IndexTypeEnum.java",
    "content": "package ai.chat2db.spi.enums;\n\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * Index type\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum IndexTypeEnum implements BaseEnum<String> {\n    /**\n     * primary key\n     */\n    PRIMARY_KEY(\"primary key\"),\n\n    /**\n     * Ordinary index\n     */\n    NORMAL(\"Ordinary index\"),\n\n    /**\n     * unique index\n     */\n    UNIQUE(\"unique index\"),\n    ;\n\n    final String description;\n\n    IndexTypeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/enums/SqlTypeEnum.java",
    "content": "package ai.chat2db.spi.enums;\n\nimport ai.chat2db.server.tools.base.enums.BaseEnum;\nimport lombok.Getter;\n\n/**\n * sql type\n *\n * @author Jiaju Zhuang\n */\n@Getter\npublic enum SqlTypeEnum implements BaseEnum<String> {\n    /**\n     * Check for phrases\n     */\n    SELECT(\"Check for phrases\"),\n\n    /**\n     * unknow\n     */\n    UNKNOWN(\"unknow\"),\n\n    ;\n\n    final String description;\n\n    SqlTypeEnum(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getCode() {\n        return this.name();\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/BaseValueProcessor.java",
    "content": "//package ai.chat2db.spi.jdbc;\n//\n//import ai.chat2db.server.tools.common.util.EasyStringUtils;\n//import ai.chat2db.spi.ValueProcessor;\n//import ai.chat2db.spi.model.JDBCDataValue;\n//import ai.chat2db.spi.model.SQLDataValue;\n//import org.apache.commons.lang3.StringUtils;\n//\n//import java.util.Objects;\n//\n///**\n// * @author: zgq\n// * @date: 2024年05月30日 15:33\n// */\n//public abstract class BaseValueProcessor implements ValueProcessor {\n//\n//\n//\n//    public abstract String convertSQLValueByType(SQLDataValue dataValue);\n//\n//    public abstract String convertJDBCValueByType(JDBCDataValue dataValue);\n//\n//    public abstract String convertJDBCValueStrByType(JDBCDataValue dataValue);\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultDBManage.java",
    "content": "package ai.chat2db.spi.jdbc;\n\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.common.exception.ConnectionException;\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.sql.ConnectInfo;\nimport ai.chat2db.spi.sql.IDriverManager;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.ssh.SSHManager;\nimport ai.chat2db.spi.util.ResultSetUtils;\nimport com.jcraft.jsch.Session;\nimport jakarta.validation.constraints.NotEmpty;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static ai.chat2db.server.tools.base.constant.SymbolConstant.DOT;\n\n/**\n * @author jipengfei\n * @version : DefaultDBManage.java\n */\npublic class DefaultDBManage implements DBManage {\n\n    protected static final String DIVIDING_LINE = \"-- ----------------------------\";\n\n    protected static final String NEW_LINE = \"\\n\";\n\n    protected static final String EXPORT_TITLE = DIVIDING_LINE + NEW_LINE + \"-- Chat2DB export data , export time: %s\" + NEW_LINE + DIVIDING_LINE;\n\n    protected static final String TABLE_TITLE = DIVIDING_LINE + NEW_LINE + \"-- Table structure for table %s\" + NEW_LINE + DIVIDING_LINE;\n\n    protected static final String VIEW_TITLE = DIVIDING_LINE + NEW_LINE + \"-- View structure for view %s\" + NEW_LINE + DIVIDING_LINE;\n\n    protected static final String FUNCTION_TITLE = DIVIDING_LINE + NEW_LINE + \"-- Function structure for function %s\" + NEW_LINE + DIVIDING_LINE;\n\n    protected static final String TRIGGER_TITLE = DIVIDING_LINE + NEW_LINE + \"-- Trigger structure for trigger %s\" + NEW_LINE + DIVIDING_LINE;\n\n    protected static final String PROCEDURE_TITLE = DIVIDING_LINE + NEW_LINE + \"-- Procedure structure for procedure %s\" + NEW_LINE + DIVIDING_LINE;\n\n    private static final String RECORD_TITLE = DIVIDING_LINE + NEW_LINE + \"-- Records of %s\" + NEW_LINE + DIVIDING_LINE;\n\n\n    @Override\n    public Connection getConnection(ConnectInfo connectInfo) {\n        Connection connection = connectInfo.getConnection();\n        SSHInfo ssh = connectInfo.getSsh();\n        String url = connectInfo.getUrl();\n        String host = connectInfo.getHost();\n        String port = connectInfo.getPort() + \"\";\n        Session session = null;\n        try {\n            if (connection != null && !connection.isClosed()) {\n                return connection;\n            }\n            ssh.setRHost(host);\n            ssh.setRPort(port);\n            session = getSession(ssh);\n            if (session != null) {\n                url = url.replace(host, \"127.0.0.1\").replace(port, ssh.getLocalPort());\n            }\n        } catch (Exception e) {\n\n            throw new ConnectionException(\"connection.ssh.error\", null, e);\n        }\n        try {\n            connection = IDriverManager.getConnection(url, connectInfo.getUser(), connectInfo.getPassword(),\n                    connectInfo.getDriverConfig(), connectInfo.getExtendMap());\n\n        } catch (Exception e1) {\n            close(connection, session, ssh);\n            throw new BusinessException(\"connection.error\", null, e1);\n        }\n        connectInfo.setSession(session);\n        connectInfo.setConnection(connection);\n        if (StringUtils.isNotBlank(connectInfo.getDatabaseName()) || StringUtils.isNotBlank(connectInfo.getSchemaName())) {\n            connectDatabase(connection, connectInfo.getDatabaseName());\n        }\n        return connection;\n    }\n\n    private void close(Connection connection, Session session, SSHInfo ssh) {\n        if (connection != null) {\n            try {\n                connection.close();\n            } catch (Exception e) {\n            }\n        }\n        if (session != null) {\n            try {\n                session.delPortForwardingL(Integer.parseInt(ssh.getLocalPort()));\n            } catch (Exception e) {\n            }\n            try {\n                session.disconnect();\n            } catch (Exception e) {\n            }\n        }\n    }\n\n    private Session getSession(SSHInfo ssh) {\n        Session session = null;\n        if (ssh != null && ssh.isUse()) {\n            session = SSHManager.getSSHSession(ssh);\n        }\n        return session;\n    }\n\n    @Override\n    public void connectDatabase(Connection connection, String database) {\n\n    }\n\n    @Override\n    public void modifyDatabase(Connection connection, String databaseName, String newDatabaseName) {\n\n    }\n\n    @Override\n    public void createDatabase(Connection connection, String databaseName) {\n\n    }\n\n    @Override\n    public void dropDatabase(Connection connection, String databaseName) {\n\n    }\n\n    @Override\n    public void createSchema(Connection connection, String databaseName, String schemaName) {\n\n    }\n\n    @Override\n    public void dropSchema(Connection connection, String databaseName, String schemaName) {\n\n    }\n\n    @Override\n    public void modifySchema(Connection connection, String databaseName, String schemaName, String newSchemaName) {\n\n    }\n\n    @Override\n    public void dropFunction(Connection connection, String databaseName, String schemaName, String functionName) {\n\n    }\n\n    @Override\n    public void dropTrigger(Connection connection, String databaseName, String schemaName, String triggerName) {\n\n    }\n\n    @Override\n    public void dropProcedure(Connection connection, String databaseName, String schemaName, String triggerName) {\n\n    }\n\n    @Override\n    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {\n\n    }\n\n    @Override\n    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {\n\n    }\n\n    @Override\n    public void exportTable(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {\n\n    }\n\n    @Override\n    public void truncateTable(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException {\n        String sql = \"TRUNCATE TABLE \" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName, boolean copyData) throws SQLException {\n        String sql = \"\";\n        if (copyData) {\n            sql = \"CREATE TABLE \" + newTableName + \" AS SELECT * FROM \" + tableName;\n        } else {\n            sql = \"CREATE TABLE \" + newTableName + \" AS SELECT * FROM \" + tableName + \" WHERE 1=0\";\n        }\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void deleteProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) {\n        String procedureNewName = getSchemaOrProcedureName(procedure.getProcedureBody(), schemaName, procedure);\n        String sql = \"DROP PROCEDURE \" + procedureNewName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void deleteFunction(Connection connection, String databaseName, String schemaName, Function function) {\n        String functionNewName = getSchemaOrFunctionName(function.getFunctionBody(), schemaName, function);\n        String sql = \"DROP FUNCTION \" + functionNewName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {\n        String sql = \"DROP TABLE \" + tableName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    @Override\n    public void dropSequence(Connection connection, @NotEmpty String databaseName, String schemaName, @NotEmpty String sequenceName){\n        String sql = \"DROP SEQUENCE \" + schemaName + DOT + sequenceName;\n        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);\n    }\n\n    public void exportTableData(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) {\n        SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();\n        String tableQuerySql = sqlBuilder.buildTableQuerySql(databaseName, schemaName, tableName);\n        asyncContext.info(\"export table data sql: \" + tableQuerySql);\n        SQLExecutor.getInstance().execute(connection, tableQuerySql, 1000, resultSet -> {\n            ResultSetMetaData metaData = resultSet.getMetaData();\n            List<String> columnList = ResultSetUtils.getRsHeader(resultSet);\n            List<String> valueList = new ArrayList<>();\n            asyncContext.write(String.format(RECORD_TITLE, tableName));\n            while (resultSet.next()) {\n                for (int i = 1; i <= metaData.getColumnCount(); i++) {\n                    ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n                    JDBCDataValue jdbcDataValue = new JDBCDataValue(resultSet, metaData, i, false);\n                    String valueString = valueProcessor.getJdbcSqlValueString(jdbcDataValue);\n                    valueList.add(valueString);\n                }\n                String insertSql = sqlBuilder.buildSingleInsertSql(null, null, tableName, columnList, valueList);\n                asyncContext.write(insertSql + \";\");\n                valueList.clear();\n            }\n        });\n\n    }\n\n    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {\n        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return procedure.getProcedureName();\n        } else {\n            return schemaName + \".\" + procedure.getProcedureName();\n        }\n    }\n\n    private static String getSchemaOrFunctionName(String functionBody, String schemaName, Function function) {\n        if (functionBody.toLowerCase().contains(schemaName.toLowerCase())) {\n            return function.getFunctionName();\n        } else {\n            return schemaName + \".\" + function.getFunctionName();\n        }\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java",
    "content": "package ai.chat2db.spi.jdbc;\n\nimport ai.chat2db.server.tools.base.wrapper.result.PageResult;\nimport ai.chat2db.spi.*;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.SQLExecutor;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Lists;\nimport jakarta.validation.constraints.NotEmpty;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * @author jipengfei\n * @version : DefaultMetaService.java\n */\npublic class DefaultMetaService implements MetaData {\n    @Override\n    public List<Database> databases(Connection connection) {\n        return SQLExecutor.getInstance().databases(connection);\n    }\n\n    @Override\n    public List<Schema> schemas(Connection connection, String databaseName) {\n        List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);\n        if (StringUtils.isNotBlank(databaseName) && CollectionUtils.isNotEmpty(schemas)) {\n            for (Schema schema : schemas) {\n                if (StringUtils.isBlank(schema.getDatabaseName())) {\n                    schema.setDatabaseName(databaseName);\n                }\n            }\n        }\n        return schemas;\n    }\n\n    @Override\n    public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {\n        return null;\n    }\n\n    @Override\n    public List<Table> tables(Connection connection, String databaseName, String schemaName, String tableName) {\n        return SQLExecutor.getInstance().tables(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, new String[]{\"TABLE\", \"SYSTEM TABLE\"});\n    }\n\n    @Override\n    public List<String> tableNames(Connection connection, String databaseName, String schemaName, String tableName) {\n        return SQLExecutor.getInstance().tableNames(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, new String[]{\"TABLE\", \"SYSTEM TABLE\"});\n    }\n\n    @Override\n    public PageResult<Table> tables(Connection connection, String databaseName, String schemaName, String tableNamePattern, int pageNo, int pageSize) {\n        List<Table> tables = tables(connection, databaseName, schemaName, tableNamePattern);\n        if (CollectionUtils.isEmpty(tables)) {\n            return PageResult.of(tables, 0L, pageNo, pageSize);\n        }\n        List result = tables.stream().skip((pageNo - 1) * pageSize).limit(pageSize).collect(Collectors.toList());\n        return PageResult.of(result, (long) tables.size(), pageNo, pageSize);\n    }\n\n    @Override\n    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {\n        return null;\n    }\n\n    @Override\n    public List<Table> views(Connection connection, String databaseName, String schemaName) {\n        return SQLExecutor.getInstance().tables(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, null, new String[]{\"VIEW\"});\n    }\n\n    @Override\n    public List<String> viewNames(Connection connection, String databaseName, String schemaName) {\n        return SQLExecutor.getInstance().tableNames(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, null, new String[]{\"VIEW\"});\n    }\n\n    @Override\n    public List<Function> functions(Connection connection, String databaseName, String schemaName) {\n        List<Function> functions = SQLExecutor.getInstance().functions(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName);\n        if (CollectionUtils.isEmpty(functions)) {\n            return functions;\n        }\n        return functions.stream().filter(function -> StringUtils.isNotBlank(function.getFunctionName())).map(function -> {\n            String functionName = function.getFunctionName();\n            function.setFunctionName(functionName.trim());\n            return function;\n        }).collect(Collectors.toList());\n    }\n\n    @Override\n    public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {\n        return null;\n    }\n\n    @Override\n    public List<Procedure> procedures(Connection connection, String databaseName, String schemaName) {\n        List<Procedure> procedures = SQLExecutor.getInstance().procedures(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName);\n\n        if (CollectionUtils.isEmpty(procedures)) {\n            return procedures;\n        }\n        return procedures.stream().filter(function -> StringUtils.isNotBlank(function.getProcedureName())).map(procedure -> {\n            String procedureName = procedure.getProcedureName();\n            procedure.setProcedureName(procedureName.trim());\n            return procedure;\n        }).collect(Collectors.toList());\n    }\n\n    @Override\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {\n        List<TableColumn> columns = SQLExecutor.getInstance().columns(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, null);\n        if (CollectionUtils.isNotEmpty(columns)) {\n            for (TableColumn column : columns) {\n                String columnType = SqlUtils.removeDigits(column.getColumnType());\n                column.setColumnType(columnType);\n            }\n        }\n        return columns;\n    }\n\n    @Override\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName,\n                                     String columnName) {\n        return SQLExecutor.getInstance().columns(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, columnName);\n    }\n\n    @Override\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        return SQLExecutor.getInstance().indexes(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName);\n    }\n\n    @Override\n    public Function function(Connection connection, String databaseName, String schemaName, String functionName) {\n        return null;\n    }\n\n    @Override\n    public Trigger trigger(Connection connection, String databaseName, String schemaName, String triggerName) {\n        return null;\n    }\n\n    @Override\n    public Procedure procedure(Connection connection, String databaseName, String schemaName, String procedureName) {\n        return null;\n    }\n\n    @Override\n    public List<Type> types(Connection connection) {\n        return SQLExecutor.getInstance().types(connection);\n    }\n\n    @Override\n    public SqlBuilder getSqlBuilder() {\n        return new DefaultSqlBuilder();\n    }\n\n    @Override\n    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {\n        return null;\n    }\n\n    @Override\n    public String getMetaDataName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).collect(Collectors.joining(\".\"));\n    }\n\n    @Override\n    public ValueProcessor getValueProcessor() {\n        return new DefaultValueProcessor();\n    }\n\n    @Override\n    public CommandExecutor getCommandExecutor() {\n        return SQLExecutor.getInstance();\n    }\n\n    @Override\n    public List<String> getSystemDatabases() {\n        return Lists.newArrayList();\n    }\n\n    @Override\n    public List<String> getSystemSchemas() {\n        return Lists.newArrayList();\n    }\n\n    @Override\n    public String sequenceDDL(Connection connection, @NotEmpty String databaseName, String schemaName,\n                              @NotEmpty String tableName){\n        return null;\n    }\n\n    @Override\n    public List<SimpleSequence> sequences(Connection connection, String databaseName, String schemaName){\n        return Collections.emptyList();\n    }\n\n    @Override\n    public Sequence sequences(Connection connection, @NotEmpty String databaseName, String schemaName, String sequenceName){\n        return null;\n    }\n\n    @Override\n    public List<String> usernames(Connection connection){\n        return Collections.emptyList();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultSqlBuilder.java",
    "content": "package ai.chat2db.spi.jdbc;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.enums.DmlType;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.sql.Chat2DBContext;\nimport ai.chat2db.spi.util.SqlUtils;\nimport com.google.common.collect.Lists;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.select.OrderByElement;\nimport net.sf.jsqlparser.statement.select.GroupByElement;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.collections4.MapUtils;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\npublic class DefaultSqlBuilder implements SqlBuilder<Table> {\n\n\n    @Override\n    public String buildTableQuerySql(String databaseName, String schemaName, String tableName) {\n        StringBuilder sqlBuilder = new StringBuilder();\n        sqlBuilder.append(\"SELECT * FROM \");\n        buildTableName(databaseName, schemaName, tableName, sqlBuilder);\n        return sqlBuilder.toString();\n    }\n\n    @Override\n    public String buildCreateTableSql(Table table) {\n        return null;\n    }\n\n    @Override\n    public String buildModifyTaleSql(Table oldTable, Table newTable) {\n        return null;\n    }\n\n    @Override\n    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {\n        return null;\n    }\n\n    public static String CREATE_DATABASE_SQL = \"CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET %s COLLATE %s\";\n\n    @Override\n    public String buildCreateDatabaseSql(Database database) {\n        return null;\n    }\n\n    @Override\n    public String buildModifyDatabaseSql(Database oldDatabase, Database newDatabase) {\n        return null;\n    }\n\n    @Override\n    public String buildCreateSchemaSql(Schema schema) {\n        return null;\n    }\n\n    @Override\n    public String buildModifySchemaSql(String oldSchemaName, String newSchemaName) {\n        return null;\n    }\n\n    @Override\n    public String buildOrderBySql(String originSql, List<OrderBy> orderByList) {\n        if (CollectionUtils.isEmpty(orderByList)) {\n            return originSql;\n        }\n        try {\n            Statement statement = CCJSqlParserUtil.parse(originSql);\n            if (statement instanceof Select) {\n                Select selectStatement = (Select) statement;\n                PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();\n\n                // Create a new ORDER BY clause\n                List<OrderByElement> orderByElements = new ArrayList<>();\n\n                for (OrderBy orderBy : orderByList) {\n                    OrderByElement orderByElement = new OrderByElement();\n                    orderByElement.setExpression(CCJSqlParserUtil.parseExpression(orderBy.getColumnName()));\n                    orderByElement.setAsc(orderBy.isAsc()); // Set to ascending order, use setAsc(false) to set to descending order\n                    orderByElements.add(orderByElement);\n                }\n                // Replace the original ORDER BY clause\n                plainSelect.setOrderByElements(orderByElements);\n                // Output the modified SQL\n                return plainSelect.toString();\n            }\n        } catch (Exception e) {\n        }\n        return originSql;\n    }\n\n    @Override\n\tpublic String buildGroupBySql(String originSql, List<String> groupByList) {\n\t\tif (CollectionUtils.isEmpty(groupByList)) {\n\t\t\treturn originSql;\n\t\t}\n\t\ttry {\n\t\t\tStatement statement = CCJSqlParserUtil.parse(originSql);\n\t\t\tif (statement instanceof Select) {\n\t\t\t\tSelect selectStatement = (Select) statement;\n\t\t\t\tPlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();\n\n\t\t\t\t// Create a new GROUP BY clause\n\t\t\t\t// Replace the original GROUP BY clause\n\t\t\t\tGroupByElement grouByElement = new GroupByElement();\n\t\t\t\tgrouByElement.setGroupingSets(groupByList);\n\t\t\t\tplainSelect.setGroupByElement(grouByElement);\n\t\t\t\t// Output the modified SQL\n\t\t\t\treturn plainSelect.toString();\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t}\n\n\t\treturn originSql;\n\t}\n\n\n    @Override\n    public String buildSqlByQuery(QueryResult queryResult) {\n        List<Header> headerList = queryResult.getHeaderList();\n        List<ResultOperation> operations = queryResult.getOperations();\n        String tableName = queryResult.getTableName();\n        StringBuilder stringBuilder = new StringBuilder();\n        MetaData metaSchema = Chat2DBContext.getMetaData();\n        String dbType = Chat2DBContext.getDBConfig().getDbType();\n        List<String> keyColumns = getPrimaryColumns(headerList);\n        for (int i = 0; i < operations.size(); i++) {\n            ResultOperation operation = operations.get(i);\n            List<String> row = operation.getDataList();\n            List<String> odlRow = operation.getOldDataList();\n            String sql = \"\";\n            if (\"UPDATE\".equalsIgnoreCase(operation.getType())) {\n                sql = getUpdateSql(tableName, headerList, row, odlRow, metaSchema, keyColumns, false);\n                if(\"MYSQL\".equalsIgnoreCase(dbType)){\n                    sql = sql + \" LIMIT 1\";\n                }\n            } else if (\"CREATE\".equalsIgnoreCase(operation.getType())) {\n                sql = getInsertSql(tableName, headerList, row, metaSchema);\n            } else if (\"DELETE\".equalsIgnoreCase(operation.getType())) {\n                sql = getDeleteSql(tableName, headerList, odlRow, metaSchema, keyColumns);\n                if(\"MYSQL\".equalsIgnoreCase(dbType)){\n                    sql = sql + \" LIMIT 1\";\n                }\n            } else if (\"UPDATE_COPY\".equalsIgnoreCase(operation.getType())) {\n                sql = getUpdateSql(tableName, headerList, row, row, metaSchema, keyColumns, true);\n            }\n            stringBuilder.append(sql + \";\\n\");\n        }\n        return stringBuilder.toString();\n    }\n\n    @Override\n    public String getTableDmlSql(Table table, String type) {\n        if (table == null || CollectionUtils.isEmpty(table.getColumnList()) || StringUtils.isBlank(type)) {\n            return \"\";\n        }\n        if (DmlType.INSERT.name().equalsIgnoreCase(type)) {\n            return getInsertSql(table.getName(), table.getColumnList());\n        } else if (DmlType.UPDATE.name().equalsIgnoreCase(type)) {\n            return getUpdateSql(table.getName(), table.getColumnList());\n        } else if (DmlType.DELETE.name().equalsIgnoreCase(type)) {\n            return getDeleteSql(table.getName(), table.getColumnList());\n        } else if (DmlType.SELECT.name().equalsIgnoreCase(type)) {\n            return getSelectSql(table.getName(), table.getColumnList());\n        }\n        return \"\";\n    }\n\n    private String getSelectSql(String name, List<TableColumn> columnList) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"SELECT \");\n        for (TableColumn column : columnList) {\n            script.append(column.getName())\n                    .append(\",\");\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\" FROM where\").append(name);\n        return script.toString();\n    }\n\n    private String getDeleteSql(String name, List<TableColumn> columnList) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"DELETE FROM \").append(name)\n                .append(\" where \");\n        return script.toString();\n    }\n\n    private String getUpdateSql(String name, List<TableColumn> columnList) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"UPDATE \").append(name)\n                .append(\" set \");\n        for (TableColumn column : columnList) {\n            script.append(column.getName())\n                    .append(\" = \")\n                    .append(\" \")\n                    .append(\",\");\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\" where \");\n        return script.toString();\n    }\n\n    private String getInsertSql(String name, List<TableColumn> columnList) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"INSERT INTO \").append(name)\n                .append(\" (\");\n        for (TableColumn column : columnList) {\n            script.append(column.getName())\n                    .append(\",\");\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\") VALUES (\");\n        for (TableColumn column : columnList) {\n            script.append(\" \")\n                    .append(\",\");\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n    }\n\n    /**\n     * Generates the base part of the INSERT SQL statement.\n     * Optionally includes column names if provided.\n     *\n     * @param databaseName\n     * @param schemaName   Name of the database schema.\n     * @param tableName    Name of the table to insert into.\n     * @param columnList   Optional list of column names.\n     * @return The base part of the INSERT SQL statement.\n     */\n    protected String buildBaseInsertSql(String databaseName, String schemaName, String tableName, List<String> columnList) {\n        StringBuilder script = new StringBuilder();\n\n        script.append(\"INSERT INTO \");\n\n        buildTableName(databaseName, schemaName, tableName, script);\n\n        buildColumns(columnList, script);\n\n        script.append(\" VALUES \");\n        return script.toString();\n    }\n\n    protected void buildColumns(List<String> columnList, StringBuilder script) {\n        if (CollectionUtils.isNotEmpty(columnList)) {\n            script.append(\" (\")\n                    .append(String.join(\",\", columnList))\n                    .append(\") \");\n        }\n    }\n\n    protected void buildTableName(String databaseName, String schemaName, String tableName, StringBuilder script) {\n        if (StringUtils.isNotBlank(databaseName)) {\n            script.append(databaseName).append('.');\n        }\n        if (StringUtils.isNotBlank(schemaName)) {\n            script.append(schemaName).append('.');\n        }\n\n        script.append(tableName);\n    }\n\n    /**\n     * Generates a single INSERT SQL statement for one record.\n     *\n     * @param schemaName Name of the database schema.\n     * @param tableName  Name of the table to insert into.\n     * @param columnList Optional list of column names.\n     * @param valueList  List of values to be inserted.\n     * @return The complete INSERT SQL statement for a single record.\n     */\n    public String buildSingleInsertSql(String databaseName, String schemaName, String tableName, List<String> columnList, List<String> valueList) {\n        String baseSql = buildBaseInsertSql(databaseName, schemaName, tableName, columnList);\n        List<String> list = valueList.stream().map(EasyStringUtils::escapeLineString).toList();\n        return baseSql + \"(\" + String.join(\",\", list) + \")\";\n    }\n\n    /**\n     * Generates a multi-row INSERT SQL statement.\n     *\n     * @param schemaName Name of the database schema.\n     * @param tableName  Name of the table to insert into.\n     * @param columnList Optional list of column names.\n     * @param valueLists List of lists, each inner list represents values for a row.\n     * @return The complete multi-row INSERT SQL statement.\n     */\n    public String buildMultiInsertSql(String databaseName, String schemaName, String tableName, List<String> columnList, List<List<String>> valueLists) {\n        String baseSql = buildBaseInsertSql(databaseName, schemaName, tableName, columnList);\n        String valuesPart = valueLists.stream()\n                .map(values -> \"(\" + String.join(\",\", values.stream().map(EasyStringUtils::escapeLineString).toList()) + \")\")\n                .collect(Collectors.joining(\",\\n\"));\n        return baseSql + valuesPart;\n    }\n\n\n    @Override\n    public String buildUpdateSql(String databaseName, String schemaName, String tableName, Map<String, String> row, Map<String, String> primaryKeyMap) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"UPDATE \");\n        buildTableName(databaseName, schemaName, tableName, script);\n\n        script.append(\" SET \");\n        List<String> setClauses = row.entrySet().stream()\n                .map(entry -> entry.getKey() + \" = \" + entry.getValue())\n                .collect(Collectors.toList());\n        script.append(String.join(\",\", setClauses));\n\n        if (MapUtils.isNotEmpty(primaryKeyMap)) {\n            script.append(\" WHERE \");\n            List<String> whereClauses = primaryKeyMap.entrySet().stream()\n                    .map(entry -> entry.getKey() + \" = \" + entry.getValue())\n                    .collect(Collectors.toList());\n            script.append(String.join(\" AND \", whereClauses));\n        }\n        return script.toString();\n    }\n\n    @Override\n    public String buildCreateSequenceSql(Sequence sequence) {\n        return null;\n    }\n\n    @Override\n    public String buildModifySequenceSql(Sequence oldSequence, Sequence newSequence) {\n        return null;\n    }\n\n\n    private List<String> getPrimaryColumns(List<Header> headerList) {\n        if (CollectionUtils.isEmpty(headerList)) {\n            return Lists.newArrayList();\n        }\n        List<String> keyColumns = Lists.newArrayList();\n        for (Header header : headerList) {\n            if (header.getPrimaryKey() != null && header.getPrimaryKey()) {\n                keyColumns.add(header.getName());\n            }\n        }\n        return keyColumns;\n    }\n\n    private String getDeleteSql(String tableName, List<Header> headerList, List<String> row, MetaData metaSchema,\n                                List<String> keyColumns) {\n        StringBuilder script = new StringBuilder();\n        script.append(\"DELETE FROM \").append(tableName).append(\"\");\n        script.append(buildWhere(headerList, row, metaSchema, keyColumns));\n        return script.toString();\n    }\n\n    private String buildWhere(List<Header> headerList, List<String> row, MetaData metaSchema, List<String> keyColumns) {\n        StringBuilder script = new StringBuilder();\n        script.append(\" where \");\n        if (CollectionUtils.isEmpty(keyColumns)) {\n            for (int i = 1; i < row.size(); i++) {\n                String oldValue = row.get(i);\n                Header header = headerList.get(i);\n                String value = SqlUtils.getSqlValue(oldValue, header.getDataType());\n                if (value == null) {\n                    script.append(metaSchema.getMetaDataName(header.getName()))\n                            .append(\" is null and \");\n                } else {\n                    script.append(metaSchema.getMetaDataName(header.getName()))\n                            .append(\" = \")\n                            .append(value)\n                            .append(\" and \");\n                }\n            }\n        } else {\n            for (int i = 1; i < row.size(); i++) {\n                String oldValue = row.get(i);\n                Header header = headerList.get(i);\n                String columnName = header.getName();\n                if (keyColumns.contains(columnName)) {\n                    String value = SqlUtils.getSqlValue(oldValue, header.getDataType());\n                    if (value == null) {\n                        script.append(metaSchema.getMetaDataName(columnName))\n                                .append(\" is null and \");\n                    } else {\n                        script.append(metaSchema.getMetaDataName(columnName))\n                                .append(\" = \")\n                                .append(value)\n                                .append(\" and \");\n                    }\n                }\n            }\n        }\n        script.delete(script.length() - 4, script.length());\n        return script.toString();\n    }\n\n    private String getInsertSql(String tableName, List<Header> headerList, List<String> row, MetaData metaSchema) {\n        if (CollectionUtils.isEmpty(row) || ObjectUtils.allNull(row.toArray())) {\n            return \"\";\n        }\n        StringBuilder script = new StringBuilder();\n        script.append(\"INSERT INTO \").append(tableName)\n                .append(\" (\");\n\n        ValueProcessor valueProcessor = metaSchema.getValueProcessor();\n        for (int i = 1; i < row.size(); i++) {\n            Header header = headerList.get(i);\n            //String newValue = row.get(i);\n            //if (newValue != null) {\n            script.append(metaSchema.getMetaDataName(header.getName()))\n                    .append(\",\");\n            // }\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\") VALUES (\");\n        for (int i = 1; i < row.size(); i++) {\n            String newValue = row.get(i);\n            //if (newValue != null) {\n            Header header = headerList.get(i);\n            SQLDataValue sqlDataValue = new SQLDataValue();\n            DataType dataType = new DataType();\n            dataType.setDataTypeName(header.getDataType());\n            dataType.setScale(header.getDecimalDigits());\n            dataType.setPrecision(header.getColumnSize());\n            sqlDataValue.setValue(newValue);\n            sqlDataValue.setDataType(dataType);\n            String value =  valueProcessor.getSqlValueString(sqlDataValue);\n            script.append(value)\n                    .append(\",\");\n            //}\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(\")\");\n        return script.toString();\n\n    }\n\n    private String getUpdateSql(String tableName, List<Header> headerList, List<String> row, List<String> odlRow,\n                                MetaData metaSchema,\n                                List<String> keyColumns, boolean copy) {\n        StringBuilder script = new StringBuilder();\n        if (CollectionUtils.isEmpty(row) || CollectionUtils.isEmpty(odlRow)) {\n            return \"\";\n        }\n        script.append(\"UPDATE \").append(tableName).append(\" set \");\n        for (int i = 1; i < row.size(); i++) {\n            String newValue = row.get(i);\n            String oldValue = odlRow.get(i);\n            if (StringUtils.equals(newValue, oldValue) && !copy) {\n                continue;\n            }\n            Header header = headerList.get(i);\n            String newSqlValue = SqlUtils.getSqlValue(newValue, header.getDataType());\n            script.append(metaSchema.getMetaDataName(header.getName()))\n                    .append(\" = \")\n                    .append(newSqlValue)\n                    .append(\",\");\n        }\n        script.deleteCharAt(script.length() - 1);\n        script.append(buildWhere(headerList, odlRow, metaSchema, keyColumns));\n        return script.toString();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultValueHandler.java",
    "content": "//package ai.chat2db.spi.jdbc;\n//\n//import ai.chat2db.server.tools.common.util.I18nUtils;\n//import ai.chat2db.spi.ValueHandler;\n//import cn.hutool.core.io.unit.DataSizeUtil;\n//import lombok.extern.slf4j.Slf4j;\n//\n//import java.math.BigDecimal;\n//import java.nio.charset.StandardCharsets;\n//import java.sql.*;\n//import java.time.LocalDateTime;\n//import java.time.format.DateTimeFormatter;\n//\n//@Slf4j\n//public class DefaultValueHandler implements ValueHandler {\n//\n//    private static final long MAX_RESULT_SIZE = 10* 1024 * 1024;\n//\n//    @Override\n//    public String getString(ResultSet rs, int index, boolean limitSize) throws SQLException {\n//        try {\n//            Object obj = rs.getObject(index);\n//            if (obj == null) {\n//                return null;\n//            }\n//            if (obj instanceof BigDecimal bigDecimal) {\n//                return bigDecimal.toPlainString();\n//            } else if (obj instanceof Double d) {\n//                return BigDecimal.valueOf(d).toPlainString();\n//            } else if (obj instanceof Float f) {\n//                return BigDecimal.valueOf(f).toPlainString();\n//            } else if (obj instanceof Clob) {\n//                return largeString(rs, index, limitSize);\n//            } else if (obj instanceof byte[]) {\n//                return largeString(rs, index, limitSize);\n//            } else if (obj instanceof Blob blob) {\n//                return largeStringBlob(blob, limitSize);\n//            } else if (obj instanceof Timestamp || obj instanceof LocalDateTime) {\n//                return largeTime(obj);\n//            } else if (obj instanceof SQLXML){\n//                return ((SQLXML) obj).getString();\n//            } else {\n//                return obj.toString();\n//            }\n//        } catch (Exception e) {\n//            log.warn(\"Failed to parse number:{},\", index, e);\n//            return rs.getString(index);\n//        }\n//    }\n//\n//    private String largeStringBlob(Blob blob, boolean limitSize) throws SQLException {\n//        if (blob == null) {\n//            return null;\n//        }\n//        int length = Math.toIntExact(blob.length());\n//        if (limitSize && length > MAX_RESULT_SIZE) {\n//            length = Math.toIntExact(MAX_RESULT_SIZE);\n//        }\n//        byte[] data = blob.getBytes(1, length);\n//        String result = new String(data, StandardCharsets.UTF_8);\n//\n//        if (length > MAX_RESULT_SIZE) {\n//            return \"[ \" + DataSizeUtil.format(MAX_RESULT_SIZE) + \" of \" + DataSizeUtil.format(length)\n//                    + \" ,\"\n//                    + I18nUtils.getMessage(\"execute.exportCsv\") + \" ] \" + result;\n//        }\n//        return result;\n//    }\n//\n//    private String largeTime(Object obj) throws SQLException {\n//        Object timeField = obj; // Assuming a time field of type Object\n//\n//        LocalDateTime localDateTime;\n//\n//        if (obj instanceof Timestamp) {\n//            // Convert a time field of type Object to a LocalDateTime object\n//            localDateTime = ((Timestamp) timeField).toLocalDateTime();\n//        } else if(obj instanceof  LocalDateTime){\n//            localDateTime = (LocalDateTime) timeField;\n//        } else {\n//            try {\n//                localDateTime = LocalDateTime.parse(timeField.toString(), DateTimeFormatter.ofPattern(\"yyyy-MM-dd'T'HH:mm:ss\"));\n//            }catch (Exception e){\n//                localDateTime = LocalDateTime.parse(timeField.toString(), DateTimeFormatter.ofPattern(\"yyyy-MM-dd'T'HH:mm\"));\n//            }\n//        }\n//        // Create a DateTimeFormatter instance and specify the output date and time format\n//        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\");\n//\n//        // Format date time\n//        String formattedDateTime = dtf.format(localDateTime);\n//        return formattedDateTime;\n//    }\n//\n//    private static String largeString(ResultSet rs, int index, boolean limitSize) throws SQLException {\n//        String result = rs.getString(index);\n//        if (result == null) {\n//            return null;\n//\n//        }\n//        if (!limitSize) {\n//            return result;\n//        }\n//\n//        if (result.length() > MAX_RESULT_SIZE) {\n//            return \"[ \" + DataSizeUtil.format(MAX_RESULT_SIZE) + \" of \" + DataSizeUtil.format(result.length()) + \" ,\"\n//                    + I18nUtils.getMessage(\"execute.exportCsv\") + \" ] \" + result.substring(0,\n//                    Math.toIntExact(MAX_RESULT_SIZE));\n//        }\n//        return result;\n//    }\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultValueProcessor.java",
    "content": "package ai.chat2db.spi.jdbc;\n\nimport ai.chat2db.server.tools.common.util.EasyStringUtils;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.model.JDBCDataValue;\nimport ai.chat2db.spi.model.SQLDataValue;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.math.NumberUtils;\n\nimport java.util.Objects;\n\n/**\n * @author: zgq\n * @date: 2024年05月24日 14:30\n */\npublic class DefaultValueProcessor implements ValueProcessor {\n\n    @Override\n    public String getSqlValueString(SQLDataValue dataValue) {\n        if (Objects.isNull(dataValue.getValue())) {\n            return \"NULL\";\n        }\n        return convertSQLValueByType(dataValue);\n\n    }\n\n\n    @Override\n    public String getJdbcValue(JDBCDataValue dataValue) {\n//        Object value = dataValue.getObject();\n//        if (Objects.isNull(dataValue.getObject())) {\n//            return null;\n//        }\n//        if (value instanceof String emptySry) {\n//            if (StringUtils.isBlank(emptySry)) {\n//                return emptySry;\n//            }\n//        }\n        return convertJDBCValueByType(dataValue);\n    }\n\n\n    @Override\n    public String getJdbcSqlValueString(JDBCDataValue dataValue) {\n//        Object value = dataValue.getObject();\n//        if (Objects.isNull(value)) {\n//            return \"NULL\";\n//        }\n//        if (value instanceof String stringValue) {\n//            if (StringUtils.isBlank(stringValue)) {\n//                return EasyStringUtils.quoteString(stringValue);\n//            }\n//        }\n        return convertJDBCValueStrByType(dataValue);\n    }\n\n    public String convertSQLValueByType(SQLDataValue dataValue) {\n        return getString(dataValue.getValue());\n    }\n\n\n    public String convertJDBCValueByType(JDBCDataValue dataValue) {\n        return dataValue.getString();\n    }\n\n\n    public String convertJDBCValueStrByType(JDBCDataValue dataValue) {\n        String value = dataValue.getString();\n        if (value == null) {\n            return \"NULL\";\n        }\n        return getString(value);\n\n    }\n\n    private boolean isNumber(String value) {\n        return NumberUtils.isCreatable(value);\n    }\n\n    private String getString(String value) {\n//        if (isNumber(value)) {\n//            return value;\n//        }\n        return EasyStringUtils.escapeAndQuoteString(value);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/AsyncCall.java",
    "content": "package ai.chat2db.spi.model;\n\nimport java.util.Map;\n\npublic interface AsyncCall {\n\n    void update(Map<String,Object> map);\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/AsyncContext.java",
    "content": "package ai.chat2db.spi.model;\n\nimport ai.chat2db.server.tools.common.model.Context;\nimport ai.chat2db.server.tools.common.util.ContextUtils;\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.io.FileUtil;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;\n\n@Slf4j\npublic class AsyncContext {\n\n    private File writeFile;\n\n    protected PrintWriter writer;\n\n    protected boolean containsData;\n\n    protected AsyncCall call;\n\n    protected boolean finish;\n\n    public File getWriteFile() {\n        return writeFile;\n    }\n\n\n    public AsyncContext(AsyncCall call, Context context, File writeFile, boolean containsData) {\n        this.call = call;\n        this.writeFile = writeFile;\n        this.progress = 5;\n        this.containsData = containsData;\n        createWriter();\n        asyncCallBack(context);\n        info(\"start:\" + DateUtil.format(new Date(), NORM_DATETIME_PATTERN));\n    }\n\n    private void createWriter() {\n        if (writeFile != null) {\n            this.writer = FileUtil.getPrintWriter(writeFile, \"UTF-8\", false);\n        }\n    }\n\n\n    private void asyncCallBack(Context context) {\n        if (call != null && context != null) {\n            new Thread(() -> {\n                try {\n                    ContextUtils.setContext(context);\n                    int n = 1;\n                    while (!finish) {\n                        // 更新时间逐渐变长避免频繁更新\n                        callUpdate();\n                        Thread.sleep(2000 * n);\n                        if (n < 5) {\n                            n++;\n                        }\n                    }\n                } catch (Exception e) {\n                    log.error(\"AsyncContext call error\", e);\n                } finally {\n                    ContextUtils.removeContext();\n                }\n            }).start();\n        }\n    }\n\n    private void callUpdate() {\n        if (call == null) {\n            return;\n        }\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"progress\", progress);\n        map.put(\"info\", info.toString());\n        map.put(\"error\", error.toString());\n        map.put(\"status\", finish ? \"FINISHED\" : \"RUNNING\");\n        if (progress == 100 && writeFile != null) {\n            map.put(\"downloadUrl\", writeFile.getAbsolutePath());\n        }\n        info = new StringBuffer();\n        error = new StringBuffer();\n        call.update(map);\n    }\n\n    public boolean isContainsData() {\n        return containsData;\n    }\n\n\n    public void setProgress(Integer progress) {\n        if (progress == null) {\n            return;\n        }\n        if (progress >= 100) {\n            progress = 99;\n        }\n        this.progress = progress;\n    }\n\n    public void info(String message) {\n        info.append(message + \"\\n\");\n    }\n\n    public void error(String message) {\n        error.append(message + \"\\n\");\n        info.append(message + \"\\n\");\n    }\n\n    public void stop() {\n        this.finish = true;\n    }\n\n    public void finish() {\n        finish = true;\n        this.progress = 100;\n        info(\"finish:\" + DateUtil.format(new Date(), NORM_DATETIME_PATTERN));\n        if (writeFile != null) {\n            info(\"file:\" + writeFile.getAbsolutePath());\n        }\n        if (writer != null) {\n            writer.flush();\n            writer.close();\n        }\n        callUpdate();\n\n    }\n\n    public void write(String message) {\n        if (writer != null) {\n            writer.write(message + \"\\n\");\n        }\n    }\n\n    protected Integer progress;\n\n    private StringBuffer info = new StringBuffer();\n\n    private StringBuffer error = new StringBuffer();\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Cell.java",
    "content": "package ai.chat2db.spi.model;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * cell type\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Cell  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * cell type\n     *\n     * @see CellTypeEnum\n     */\n    private String type;\n\n    /**\n     * string data\n     */\n    private String stringValue;\n\n    /**\n     * number\n     */\n    private BigDecimal bigDecimalValue;\n\n    /**\n     * date data\n     */\n    private Long dateValue;\n\n    /**\n     * binary stream\n     */\n    private byte[] byteValue;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Charset.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\n@AllArgsConstructor\npublic class Charset  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private String charsetName;\n\n    private String defaultCollationName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Collation.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\n@AllArgsConstructor\npublic class Collation  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private String collationName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ColumnType.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\n@AllArgsConstructor\npublic class ColumnType  implements Serializable {\n    private static final long serialVersionUID = 1L;\n    private String typeName;\n    private boolean supportLength;\n    private boolean supportScale;\n    private boolean supportNullable;\n    private boolean supportAutoIncrement;\n    private boolean supportCharset;\n    private boolean supportCollation;\n    private boolean supportComments;\n    private boolean supportDefaultValue;\n    private boolean supportExtent;\n    private boolean supportValue;\n    private boolean supportUnit;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Command.java",
    "content": "package ai.chat2db.spi.model;\n\nimport jakarta.validation.constraints.NotNull;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\npublic class Command  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * sql statement\n     */\n    @NotNull\n    private String script;\n\n    /**\n     * console id\n     */\n    @NotNull\n    private Long consoleId;\n\n    /**\n     * Data source id\n     */\n    @NotNull\n    private Long dataSourceId;\n\n    /**\n     * DB name\n     */\n    @NotNull\n    private String databaseName;\n\n    /**\n     * schema name\n     */\n    private String schemaName;\n\n    /**\n     *\n     */\n    private String tableName;\n\n    /**\n     *Page coding\n      * Only available for select statements\n     */\n    private Integer pageNo;\n\n    /**\n     * Paging Size\n      * Only available for select statements\n     */\n    private Integer pageSize;\n\n    /**\n     * Return all data\n     * Only available for select statements\n     */\n    private Boolean pageSizeAll;\n\n    /**\n     * single SQL\n     */\n    private boolean single;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/CreateTableSql.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * @author jipengfei\n * @version : CreateTableSql.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CreateTableSql  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    public String tableName;\n\n    public String sql;\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/DataSourceConnect.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * Database connection object\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DataSourceConnect  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * success flag\n     */\n    private Boolean success;\n\n    /**\n     * Failure message prompt\n     * Only in case of failure\n     */\n    private String message;\n\n    /**\n     * description\n     */\n    private String description;\n\n    /**\n     * error detail\n     */\n    private String errorDetail;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/DataType.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.Data;\n\n/**\n * @author: zgq\n * @date: 2024年05月31日 16:47\n */\n@Data\npublic class DataType {\n\n    /**\n     * 数据类型的名称，如 \"VARCHAR\", \"INTEGER\", \"DECIMAL\", \"DATE\" 等。\n     * 这个名称反映了数据库中字段的确切数据类型，是根据`ResultSetMetaData.getColumnTypeName()`获取的，\n     * 对理解和转换字段值至关重要，尤其是在处理数据库特定类型（如Oracle的NUMBER，MySQL的DATETIME）时。\n     */\n    private String dataTypeName;\n\n    /**\n     * 精度（Precision），通常用于数值类型和字符串类型，表示该类型能够存储的最大字符数量或数字的总位数。\n     * 对于数值类型，如`DECIMAL(5,2)`，精度5指的是整数部分加上小数部分的总位数。\n     * 在从`ResultSetMetaData.getPrecision()`获取时，它帮助确定如何格式化数值，以确保数据的完整性和准确性。\n     */\n    private Integer precision;\n\n    /**\n     * 小数位数（Scale），仅对数值类型有意义，表示小数点右侧的位数。\n     * 例如，在`DECIMAL(5,2)`中，比例2表示小数点后保留两位数。\n     * 通过`ResultSetMetaData.getScale()`获得，对于构造精确的数值字符串（特别是在财务和科学计算中）非常重要。\n     */\n    private Integer scale;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Database.java",
    "content": "package ai.chat2db.spi.model;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * database\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Database implements Serializable {\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    /**\n     * Database name\n     */\n    @JsonAlias({\"TABLE_CAT\"})\n    private String name;\n\n    /**\n     * schema name\n     */\n    private List<Schema> schemas;\n\n\n    private String comment;\n\n    private String charset;\n\n    private String collation;\n\n    private String owner;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/DefaultValue.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\n@AllArgsConstructor\npublic class DefaultValue implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private String defaultValue;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/DriverEntry.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport java.io.Serializable;\nimport java.sql.Driver;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author jipengfei\n * @version : DriverEntry.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DriverEntry  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private DriverConfig driverConfig;\n\n    private Driver driver;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/EngineType.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\n@AllArgsConstructor\npublic class EngineType  implements Serializable {\n    private static final long serialVersionUID = 1L;\n    private String name;\n    private boolean supportTTL;\n    private boolean supportSortOrder;\n    private boolean supportSkippingIndices;\n    private boolean supportDeduplication;\n    private boolean supportSettings;\n    private boolean supportParallelInsert;\n    private boolean supportProjections;\n    private boolean supportReplication;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ExecuteResult.java",
    "content": "package ai.chat2db.spi.model;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Results of the\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ExecuteResult  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * success flag\n     */\n    private Boolean success;\n\n    /**\n     * Failure message prompt\n     * Only in case of failure\n     */\n    private String message;\n\n    /**\n     * executed sql\n     */\n    private String sql;\n\n    /**\n     * Original SQL without pagination\n     */\n    private String originalSql;\n\n    /**\n     * description\n     */\n    private String description;\n\n    /**\n     * Modify the number of rows and query sql will not return\n     */\n    private Integer updateCount;\n\n    /**\n     * List of display headers\n     */\n    private List<Header> headerList;\n\n    /**\n     * list of data\n     */\n    private List<List<String>> dataList;\n\n    /**\n     * sql type\n     *\n     * @see ai.chat2db.spi.enums.SqlTypeEnum\n     */\n    private String sqlType;\n\n    /**\n     * Whether there is a next page\n     * Only available for select statements\n     */\n    private Boolean hasNextPage;\n\n    /**\n     * Page coding\n     * Only available for select statements\n     */\n    private Integer pageNo;\n\n    /**\n     * Paging Size\n     * Only available for select statements\n     */\n    private Integer pageSize;\n\n    /**\n     * Total number of fuzzy rows\n     * Only select statements have\n     */\n    private String fuzzyTotal;\n\n    /**\n     * execution duration\n     */\n    private Long duration;\n\n\n    /**\n     * Whether the returned result can be edited\n     */\n    private boolean canEdit;\n\n    /**\n     * Table Name for the result\n     */\n    private String tableName;\n\n    /**\n     * Extra information that can be used by the plugin\n     */\n    private Map<String,Object> extra;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Function.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * @author jipengfei\n * @version : Function.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Function  implements Serializable {\n    private static final long serialVersionUID = 1L;\n    //FUNCTION_CAT String => function catalog (may be null)\n    //FUNCTION_SCHEME String => function schema (may be null)\n    //FUNCTION_NAME String => function name. This is the name used to invoke the function\n    //REMARKS String => explanatory comment on the function\n    //FUNCTION_TYPE short => kind of function:\n    //functionResultUnknown - Cannot determine if a return value or table will be returned\n    //functionNoTable- Does not return a table\n    //functionReturnsTable - Returns a table\n    //SPECIFIC_NAME String => the name which uniquely identifies this function within its schema. This is a user specified, or DBMS generated, name that may be different then the FUNCTION_NAME for example with overload functions\n    //\n\n    @JsonAlias({\"FUNCTION_CAT\"})\n    private String databaseName;\n\n    @JsonAlias({\"FUNCTION_SCHEM\"})\n    private String schemaName;\n\n    @JsonAlias({\"FUNCTION_NAME\"})\n    private String functionName;\n\n    @JsonAlias({\"REMARKS\"})\n    private String remarks;\n\n    @JsonAlias({\"FUNCTION_TYPE\"})\n    private Short functionType;\n\n    @JsonAlias({\"SPECIFIC_NAME\"})\n    private String specificName;\n\n    private String functionBody;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Header.java",
    "content": "package ai.chat2db.spi.model;\n\nimport ai.chat2db.spi.enums.DataTypeEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * cell header\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Header implements Serializable {\n    private static final long serialVersionUID = 1L;\n    /**\n     * cell type\n     *\n     * @see DataTypeEnum\n     */\n    private String dataType;\n\n    /**\n     * display name\n     */\n    private String name;\n\n\n    private Boolean primaryKey;\n\n\n    private String comment;\n\n    private String defaultValue;\n\n    private Integer autoIncrement;\n\n    private Integer nullable;\n\n    private Integer columnSize;\n\n    private Integer decimalDigits;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/IndexType.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\n@AllArgsConstructor\npublic class IndexType  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     *\n     */\n    private String typeName;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/JDBCDataValue.java",
    "content": "package ai.chat2db.spi.model;\n\nimport ai.chat2db.spi.util.ResultSetUtils;\nimport com.google.common.io.BaseEncoding;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.Getter;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.tika.Tika;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.imageio.ImageIO;\nimport java.awt.image.BufferedImage;\nimport java.io.*;\nimport java.math.BigDecimal;\nimport java.sql.*;\nimport java.util.Objects;\n\n/**\n * @author: zgq\n * @date: 2024年05月30日 20:48\n */\n@Data\n@AllArgsConstructor\npublic class JDBCDataValue {\n    private static final Logger log = LoggerFactory.getLogger(JDBCDataValue.class);\n    private ResultSet resultSet;\n    private ResultSetMetaData metaData;\n    private int columnIndex;\n    private boolean limitSize;\n\n    public Object getObject() {\n        try {\n            return resultSet.getObject(columnIndex);\n        } catch (Exception e) {\n            log.warn(\"Failed to retrieve object from database\", e);\n            try {\n                return resultSet.getString(columnIndex);\n            } catch (SQLException ex) {\n                throw new RuntimeException(ex);\n            }\n        }\n    }\n\n    public String getString() {\n        return ResultSetUtils.getString(resultSet, columnIndex);\n    }\n\n    public String getType() {\n        return ResultSetUtils.getColumnDataTypeName(metaData, columnIndex);\n    }\n\n    public InputStream getBinaryStream() {\n        return ResultSetUtils.getBinaryStream(resultSet, columnIndex);\n    }\n\n    public int getPrecision() {\n        return ResultSetUtils.getColumnPrecision(metaData, columnIndex);\n    }\n\n    public byte[] getBytes() {\n        return ResultSetUtils.getBytes(resultSet, columnIndex);\n    }\n\n    public boolean getBoolean() {\n        return ResultSetUtils.getBoolean(resultSet, columnIndex);\n    }\n\n    public int getScale() {\n        return ResultSetUtils.getColumnScale(metaData, columnIndex);\n    }\n\n    public int getInt() {\n        return ResultSetUtils.getInt(resultSet, columnIndex);\n    }\n\n    public Date getDate() {\n        return ResultSetUtils.getDate(resultSet, columnIndex);\n    }\n\n    public Timestamp getTimestamp() {\n        return ResultSetUtils.getTimestamp(resultSet, columnIndex);\n    }\n\n    public Clob getClob() {\n        return ResultSetUtils.getClob(resultSet, columnIndex);\n    }\n\n    public Blob getBlob() {\n        return ResultSetUtils.getBlob(resultSet, columnIndex);\n    }\n\n    public String getBlobHexString() {\n        byte[] bytes = getBytes();\n        if (Objects.isNull(bytes)) {\n            return null;\n        }\n        return BaseEncoding.base16().encode(bytes);\n    }\n\n    public BigDecimal getBigDecimal() {\n        return ResultSetUtils.getBigDecimal(resultSet, columnIndex);\n    }\n\n    public String getBigDecimalString() {\n        BigDecimal bigDecimal = getBigDecimal();\n        return bigDecimal == null ? new String(getBytes()) : bigDecimal.toPlainString();\n    }\n\n\n    public String getBlobString() {\n        Blob blob = getBlob();\n        try (InputStream binaryStream = blob.getBinaryStream()) {\n            long length = blob.length();\n            return converterBinaryData(length, binaryStream);\n        } catch (SQLException | IOException e) {\n            log.warn(\"Error while reading binary stream\", e);\n            return getString();\n        }\n    }\n\n\n    public String getClobString() {\n        Clob clob = getClob();\n        try (Reader reader = clob.getCharacterStream()) {\n            long length = clob.length();\n            LOBInfo cLobInfo = getLobInfo(length);\n            double size = cLobInfo.getSize();\n            if (size == 0) {\n                return \"\";\n            }\n            String unit = cLobInfo.getUnit();\n            if (limitSize && isBigSize(unit)) {\n                return String.format(\"[%s] %s\", getType(), cLobInfo);\n            }\n            return IOUtils.toString(reader);\n        } catch (IOException | SQLException e) {\n            log.warn(\"Error while reading clob stream\", e);\n            return getStringValue();\n        }\n    }\n\n    private String handleImageType(InputStream imageStream, LOBInfo lobInfo) {\n        if (limitSize) {\n            try {\n                BufferedImage bufferedImage = ImageIO.read(imageStream);\n                return String.format(\"[%s] %dx%d JPEG image  %s\", getType(), bufferedImage.getWidth(), bufferedImage.getHeight(), lobInfo);\n            } catch (IOException e) {\n                log.warn(\"Error while reading image stream\", e);\n                return getStringValue();\n            }\n        } else {\n            return \"0x\" + getBlobHexString();\n        }\n    }\n\n    private String handleStringType(InputStream binaryStream, LOBInfo lobInfo) throws IOException {\n        if (isBigSize(lobInfo.getUnit()) && limitSize) {\n            return String.format(\"[%s] %s\", getType(), lobInfo);\n        } else {\n            return new String(binaryStream.readAllBytes());\n        }\n    }\n\n    private boolean isBigSize(String unit) {\n        return LobUnit.G.unit.equals(unit) || LobUnit.M.unit.equals(unit);\n    }\n\n\n    @NotNull\n    private LOBInfo getLobInfo(long size) {\n        if (size == 0) {\n            return new LOBInfo(LobUnit.B.unit, 0);\n        }\n        return new LOBInfo(size);\n    }\n\n    public String getStringValue() {\n        return ResultSetUtils.getStringValue(resultSet, columnIndex);\n    }\n\n    public String getBinaryDataString() {\n        InputStream binaryStream = null;\n        try {\n            binaryStream = getBinaryStream();\n            if (Objects.isNull(binaryStream)) {\n                return null;\n            }\n            // 检查流是否支持 mark 操作，不支持则用 BufferedInputStream 包装\n            if (!binaryStream.markSupported()) {\n                binaryStream = new BufferedInputStream(binaryStream);\n            }\n\n            binaryStream.mark(Integer.MAX_VALUE);\n\n            long size = 0;\n            byte[] buffer = new byte[8192]; // 缓冲区\n            int bytesRead;\n            while ((bytesRead = binaryStream.read(buffer)) != -1) {\n                size += bytesRead;\n            }\n            binaryStream.reset(); // 重置流到标记的位置\n            return converterBinaryData(size, binaryStream);\n        } catch (SQLException | IOException e) {\n            log.warn(\"Error while reading binary stream\", e);\n            return getStringValue();\n        } finally {\n            // 关闭流\n            if (binaryStream != null) {\n                try {\n                    binaryStream.close();\n                } catch (IOException e) {\n                    log.warn(\"Error while closing binary stream\", e);\n                }\n            }\n        }\n    }\n\n    private String converterBinaryData(long size, InputStream binaryStream) throws IOException, SQLException {\n        LOBInfo lobInfo = getLobInfo(size);\n        String unit = lobInfo.unit;\n        if (size == 0) {\n            return \"\";\n        }\n        Tika tika = new Tika();\n        String contentType = tika.detect(binaryStream);\n        FileTypeEnum fileTypeEnum = FileTypeEnum.fromDescription(contentType);\n        if (Objects.isNull(fileTypeEnum)) {\n            if (isBigSize(unit) && limitSize) {\n                return String.format(\"[%s] %s\", getType(), lobInfo);\n            }\n            return \"0x\" + BaseEncoding.base16().encode(binaryStream.readAllBytes());\n        }\n\n        return switch (fileTypeEnum) {\n            case IMAGE -> handleImageType(binaryStream, lobInfo);\n            case STRING -> handleStringType(binaryStream, lobInfo);\n            default -> \"\";\n        };\n    }\n\n\n    @Getter\n    public enum LobUnit {\n        B(\"B\", 1L),\n        K(\"KB\", 1024L),\n        M(\"MB\", 1024L * 1024L),\n        G(\"GB\", 1024L * 1024L * 1024L);\n\n        private final String unit;\n        private final long size;\n\n        LobUnit(String unit, long size) {\n            this.unit = unit;\n            this.size = size;\n        }\n\n    }\n\n    @Getter\n    public static class LOBInfo {\n        private final String unit;\n        private final double size;\n\n        public LOBInfo(String unit, double size) {\n            this.unit = unit;\n            this.size = size;\n        }\n\n        public LOBInfo(long size) {\n            if (size >= LobUnit.G.size) {\n                this.unit = LobUnit.G.unit;\n                this.size = (double) size / LobUnit.G.size;\n            } else if (size >= LobUnit.M.size) {\n                this.unit = LobUnit.M.unit;\n                this.size = (double) size / LobUnit.M.size;\n            } else if (size >= LobUnit.K.size) {\n                this.unit = LobUnit.K.unit;\n                this.size = (double) size / LobUnit.K.size;\n            } else {\n                this.unit = LobUnit.B.unit;\n                this.size = (double) size;\n            }\n        }\n\n        @Override\n        public String toString() {\n            return String.format(\"%.2f %s\", size, unit);\n        }\n    }\n\n    @Getter\n    public enum FileTypeEnum {\n        IMAGE(\"image/jpeg\"),\n        STRING(\"text/plain\"),\n        ;\n        private final String description;\n\n        FileTypeEnum(String description) {\n            this.description = description;\n        }\n\n        public static FileTypeEnum fromDescription(String description) {\n            for (FileTypeEnum fileType : values()) {\n                if (fileType.description.equals(description)) {\n                    return fileType;\n                }\n            }\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/KeyValue.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.google.common.collect.Maps;\nimport lombok.Data;\nimport org.apache.commons.collections4.CollectionUtils;\n\n/**\n * @author jipengfei\n * @version : KeyValue.java\n */\n@Data\npublic class KeyValue implements Serializable {\n    /**\n     * attribute name\n     */\n    private String key;\n\n    /**\n     * attribute value\n     */\n    private String value;\n\n    /**\n     * Is it required?\n     */\n    private boolean required;\n\n    /**\n     * Options\n     */\n    private List<String> choices;\n\n    public static Map<String, Object> toMap(List<KeyValue> keyValues) {\n        if (CollectionUtils.isEmpty(keyValues)) {\n            return Maps.newHashMap();\n        } else {\n            Map<String, Object> map = Maps.newHashMap();\n            keyValues.forEach(keyValue -> map.put(keyValue.getKey(), String.valueOf(keyValue.getValue())));\n            return map;\n        }\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/MetaSchema.java",
    "content": "package ai.chat2db.spi.model;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class MetaSchema implements Serializable {\n\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n    /**\n     * database list\n     */\n    private List<Database> databases;\n\n    /**\n     * schema list\n     */\n    private List<Schema> schemas;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/OrderBy.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\npublic class OrderBy  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * sort field\n     */\n    private String columnName;\n\n    /**\n     * sort by\n     */\n    private boolean asc;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Procedure.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * @author jipengfei\n * @version : Procedure.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Procedure implements Serializable {\n    private static final long serialVersionUID = 1L;\n    //PROCEDURE_CAT String => procedure catalog (may be null)\n    //PROCEDURE_SCHEME String => procedure schema (may be null)\n    //PROCEDURE_NAME String => procedure name\n    //REMARKS String => explanatory comment on the procedure\n    //PROCEDURE_TYPE short => kind of procedure:\n    //procedureResultUnknown - Cannot determine if a return value will be returned\n    //procedureNoResult - Does not return a return value\n    //procedureReturnsResult - Returns a return value\n    //SPECIFIC_NAME String => the name which uniquely identifies this procedure within its schema. This is a user specified, or DBMS generated, name that may be different then the PROCEDURE_NAME for example with overload procedures\n    //\n\n    @JsonAlias({\"PROCEDURE_CAT\"})\n    private String databaseName;\n\n    @JsonAlias({\"PROCEDURE_SCHEM\"})\n    private String schemaName;\n\n    @JsonAlias({\"PROCEDURE_NAME\"})\n    private String procedureName;\n\n    @JsonAlias({\"REMARKS\"})\n    private String remarks;\n\n    @JsonAlias({\"PROCEDURE_TYPE\"})\n    private Short procedureType;\n\n    @JsonAlias({\"SPECIFIC_NAME\"})\n    private String specificName;\n\n    private String procedureBody;\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/QueryResult.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\n@Data\npublic class QueryResult implements Serializable {\n\n    private String tableName;\n    private List<Header> headerList;\n    private List<ResultOperation> operations;\n    private Map<String, Object> extra;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ResultOperation.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n@Data\npublic class ResultOperation  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private String type;\n\n    private List<String> dataList;\n\n    private List<String> oldDataList;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/SQLDataValue.java",
    "content": "package ai.chat2db.spi.model;\n\nimport com.google.common.io.BaseEncoding;\nimport lombok.Data;\n\n/**\n * @author: zgq\n * @date: 2024年05月30日 15:01\n */\n@Data\npublic class SQLDataValue {\n    private String value;\n    private DataType dataType;\n\n    public String getDateTypeName() {\n        return dataType.getDataTypeName();\n    }\n\n    public int getPrecision() {\n        return dataType.getPrecision();\n    }\n\n    public int getScale() {\n        return dataType.getScale();\n    }\n\n    public String getBlobHexString() {\n        return BaseEncoding.base16().encode(value.getBytes());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/SSHInfo.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\nimport lombok.Data;\n\n/**\n * @author jipengfei\n * @version : SSHInfo.java\n */\n@Data\npublic class SSHInfo  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Whether to use ssh\n     */\n    private boolean use;\n\n    /**\n     * ssh hostname\n     */\n    private String hostName;\n\n    /**\n     * ssh port\n     */\n    private String port;\n\n    /**\n     * ssh username\n     */\n    private String userName;\n\n    /**\n     * ssh local port\n     */\n    private String localPort;\n\n    /**\n     * ssh Certification type\n     */\n    private String authenticationType;\n\n    /**\n     * ssh password\n     */\n    private String password;\n\n    /**\n     * ssh key file\n     */\n    private String keyFile;\n\n    /**\n     * ssh key file password\n     */\n    private String passphrase;\n\n    /**\n     * ssh springboard target host\n     */\n    private String rHost;\n\n    /**\n     * ssh springboard target port\n     */\n    private String rPort;\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {return true;}\n        if (o == null || getClass() != o.getClass()) {return false;}\n        SSHInfo sshInfo = (SSHInfo)o;\n        return use == sshInfo.use && Objects.equals(hostName, sshInfo.hostName) && Objects.equals(port,\n            sshInfo.port) && Objects.equals(userName, sshInfo.userName) && Objects.equals(localPort,\n            sshInfo.localPort) && Objects.equals(authenticationType, sshInfo.authenticationType)\n            && Objects.equals(password, sshInfo.password) && Objects.equals(keyFile, sshInfo.keyFile)\n            && Objects.equals(passphrase, sshInfo.passphrase) && Objects.equals(rHost, sshInfo.rHost)\n            && Objects.equals(rPort, sshInfo.rPort);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(use, hostName, port, userName, localPort, authenticationType, password, keyFile, passphrase,\n            rHost, rPort);\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/SSLInfo.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author jipengfei\n * @version : SSLInfo.java\n */\n@Data\npublic class SSLInfo  implements Serializable {\n    private static final long serialVersionUID = 1L;\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Schema.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport java.io.Serializable;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author jipengfei\n * @version : TableSchema.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Schema implements Serializable {\n    private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;\n\n    /**\n     * databaseName\n     */\n    @JsonAlias({\"TABLE_CATALOG\",\"table_catalog\"})\n    private String databaseName;\n    /**\n     * Data name\n     */\n    @JsonAlias({\"TABLE_SCHEM\",\"table_schem\"})\n    private String name;\n\n\n    private String comment;\n\n\n    private String owner;\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Sequence.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * Sequence information\n *\n * @author Sylphy\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Sequence {\n    /**\n     * Schema name\n     */\n    private String nspname;\n    /**\n     * Sequence name\n     */\n    private String relname;\n    /**\n     * Sequence data type\n     */\n    private String typname;\n    /**\n     * Sequence cache\n     */\n    private String seqcache;\n    /**\n     * Sequence owner\n     */\n    private String rolname;\n    /**\n     * Sequence comment\n     */\n    private String comment;\n    /**\n     * Sequence start value\n     */\n    private String seqstart;\n    /**\n     * Sequence step value\n     */\n    private String seqincrement;\n    /**\n     * Sequence max value\n     */\n    private String seqmax;\n    /**\n     * Sequence min value\n     */\n    private String seqmin;\n    /**\n     * Sequence cycle\n     */\n    private Boolean seqcycle;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ShowDatabaseResult.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * @author jipengfei\n * @version : ShowDatabaseResult.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ShowDatabaseResult implements Serializable {\n    private static final long serialVersionUID = 1L;\n    String database;\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/SimpleColumn.java",
    "content": "package ai.chat2db.spi.model;\n\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SimpleColumn  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Column name\n     */\n    @JsonAlias({\"COLUMN_NAME\"})\n    private String name;\n\n\n    @JsonAlias({\"TYPE_NAME\"})\n    private String columnType;\n\n    /**\n     * Comment\n     */\n    @JsonAlias({\"REMARKS\"})\n    private String comment;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/SimpleSequence.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * Simple sequence information\n *\n * @author Sylphy\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SimpleSequence implements Serializable {\n    private static final long serialVersionUID = 1L;\n    /**\n     * Sequence Name\n     */\n    private String name;\n\n    /**\n     * description\n     */\n    private String comment;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/SimpleTable.java",
    "content": "package ai.chat2db.spi.model;\n\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SimpleTable implements Serializable {\n    private static final long serialVersionUID = 1L;\n    /**\n     * Table Name\n     */\n    @JsonAlias({\"TABLE_NAME\"})\n    private String name;\n\n    /**\n     * description\n     */\n    @JsonAlias({\"REMARKS\"})\n\n    private String comment;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Sql.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * sql object\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Sql  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * sql\n     */\n    private String sql;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Table.java",
    "content": "package ai.chat2db.spi.model;\n\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * Table information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Table implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Table Name\n     */\n    @JsonAlias({\"TABLE_NAME\"})\n    private String name;\n\n    /**\n     * description\n     */\n    @JsonAlias({\"REMARKS\"})\n\n    private String comment;\n\n    /**\n     * DB name\n     */\n    @JsonAlias({\"TABLE_SCHEM\"})\n\n    private String schemaName;\n\n    /**\n     * columnList\n     */\n    private List<TableColumn> columnList;\n\n    /**\n     * indexList\n     */\n    private List<TableIndex> indexList;\n\n    /**\n     * DB type\n     */\n    private String dbType;\n\n    /**\n     * Database name\n     */\n    @JsonAlias(\"TABLE_CAT\")\n    private String databaseName;\n\n    /**\n     * table type\n     */\n    @JsonAlias(\"TABLE_TYPE\")\n    private String type;\n\n    /**\n     * Whether to pin it to the top\n     */\n    private boolean pinned;\n\n    /**\n     * ddl\n     */\n    private String ddl;\n\n    /**\n     * engine\n     */\n    @JsonAlias(\"TYPE_NAME\")\n    private String engine;\n\n\n    private String charset;\n\n\n    private String collate;\n\n\n    private Long incrementValue;\n\n\n    private String partition;\n\n\n    private String tablespace;\n\n    private Long rows;\n\n    private Long dataLength;\n\n    private String createTime;\n\n    private String updateTime;\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java",
    "content": "package ai.chat2db.spi.model;\n\nimport com.alibaba.fastjson2.annotation.JSONField;\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\n/**\n * Column information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableColumn  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Old column, when modifying a column, you need this parameter\n     */\n    private TableColumn oldColumn;\n    /**\n     * The old column name, this parameter is needed when modifying the column\n      * oldName=name when returning\n     */\n    private String oldName;\n\n    /**\n     * Column name\n     */\n    @JsonAlias({\"COLUMN_NAME\",\"column_name\"})\n    private String name;\n\n    /**\n     * Table Name\n     */\n    @JsonAlias({\"TABLE_NAME\",\"table_name\"})\n    private String tableName;\n\n    /**\n     * Column type\n     * For example, varchar(100), double(10,6)\n     */\n\n    @JsonAlias({\"TYPE_NAME\",\"type_name\"})\n    private String columnType;\n\n    /**\n     * Column data type\n     * For example, varchar, double\n     */\n\n    @JsonAlias({\"DATA_TYPE\",\"data_type\"})\n    private Integer dataType;\n\n\n    /**\n     * default value\n     */\n\n    @JsonAlias({\"COLUMN_DEF\",\"column_def\"})\n    private String defaultValue;\n\n\n\n    /**\n     * Whether to increase automatically\n     * Empty means there is no value. The actual semantics of the database are false.\n     */\n    private Boolean autoIncrement;\n\n    /**\n     * Comment\n     */\n    @JsonAlias({\"REMARKS\",\"remarks\"})\n    private String comment;\n\n    /**\n     * Is it a primary key?\n     */\n    private Boolean primaryKey;\n\n\n    /**\n     * primary key name\n     */\n    private String primaryKeyName;\n\n\n    /**\n     * primaryKeyOrder\n     */\n    private int primaryKeyOrder;\n\n    /**\n     * Space name\n     */\n    @JsonAlias({\"TABLE_SCHEM\",\"table_schem\"})\n    private String schemaName;\n\n    /**\n     * Database name\n     */\n    @JsonAlias({\"TABLE_CAT\",\"table_cat\"})\n    private String databaseName;\n\n//    /**\n//     * Data source dependent type name, for a UDT the type name is fully qualified\n//     */\n//    private String typeName;\n\n    /**\n     * column size.\n     */\n\n    @JsonAlias({\"COLUMN_SIZE\",\"column_size\"})\n    private Integer columnSize;\n\n    /**\n     * is not used.\n     */\n    private Integer bufferLength;\n\n    /**\n     * the number of fractional digits. Null is returned for data types where DECIMAL_DIGITS is not applicable.\n     */\n\n    @JsonAlias({\"DECIMAL_DIGITS\",\"decimal_digits\"})\n    private Integer decimalDigits;\n\n    /**\n     * Radix (typically either 10 or 2)\n     */\n\n    @JsonAlias({\"NUM_PREC_RADIX\",\"num_prec_radix\"})\n    private Integer numPrecRadix;\n\n\n    /**\n     * unused\n     */\n    private Integer sqlDataType;\n\n\n    /**\n     * unused\n     */\n    private Integer sqlDatetimeSub;\n\n    /**\n     * for char types the maximum number of bytes in the column\n     */\n    private Integer charOctetLength;\n\n    /**\n     * index of column in table (starting at 1)\n     */\n\n    @JsonAlias({\"ORDINAL_POSITION\",\"ordinal_position\"})\n    private Integer ordinalPosition;\n\n    /**\n     * ISO rules are used to determine the nullability for a column.\n     */\n\n    @JsonAlias({\"nullable\",\"NULLABLE\"})\n    @JSONField(name = \"nullable\")\n    private Integer nullable;\n\n    /**\n     * String => Indicates whether this is a generated column\n     * * YES --- if this a generated column\n     * * NO --- if this not a generated column\n     */\n    private Boolean generatedColumn;\n\n\n    private String extent;\n\n\n    private String editStatus;\n\n    private String charSetName;\n\n    private String collationName;\n\n    //Mysql\n    private String value;\n\n    //ORACLE\n    private String unit;\n\n    // sqlserver\n    private Boolean sparse;\n\n    // sqlserver\n    private String defaultConstraintName;\n\n    // DM seed\n    private Integer seed;\n\n    // DM increment\n    private Integer increment;\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) { return true;}\n        if (o == null || getClass() != o.getClass()) { return false;}\n        TableColumn that = (TableColumn) o;\n        return Objects.equals(name, that.name) && Objects.equals(tableName, that.tableName) && Objects.equals(columnType, that.columnType) && Objects.equals(defaultValue, that.defaultValue) && Objects.equals(autoIncrement, that.autoIncrement) && Objects.equals(comment, that.comment) && Objects.equals(columnSize, that.columnSize) && Objects.equals(decimalDigits, that.decimalDigits) && Objects.equals(numPrecRadix, that.numPrecRadix) && Objects.equals(sqlDataType, that.sqlDataType) && Objects.equals(ordinalPosition, that.ordinalPosition) && Objects.equals(nullable, that.nullable) && Objects.equals(extent, that.extent) && Objects.equals(charSetName, that.charSetName) && Objects.equals(collationName, that.collationName) && Objects.equals(value, that.value) && Objects.equals(unit, that.unit) && Objects.equals(sparse, that.sparse) && Objects.equals(defaultConstraintName, that.defaultConstraintName) && Objects.equals(seed, that.seed) && Objects.equals(increment, that.increment);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(name, tableName, columnType, defaultValue, autoIncrement, comment, columnSize, decimalDigits, numPrecRadix, sqlDataType, ordinalPosition, nullable, extent, charSetName, collationName, value, unit, sparse, defaultConstraintName, seed, increment);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableIndex.java",
    "content": "package ai.chat2db.spi.model;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Index information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableIndex  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private String oldName;\n\n    /**\n     * Index name\n     */\n    private String name;\n\n    /**\n     * Table Name\n     */\n    private String tableName;\n\n    /**\n     * Index type\n     *\n     * @see IndexTypeEnum\n     */\n    private String type;\n\n    /**\n     * Is it unique?\n     */\n    private Boolean unique;\n\n    /**\n     * Comment\n     */\n    private String comment;\n\n    /**\n     * The schema to which the index belongs\n     */\n    private String schemaName;\n\n    /**\n     * Database name\n     */\n    private String databaseName;\n\n    /**\n     * Columns included in the index\n     */\n    private List<TableIndexColumn> columnList;\n\n\n    private String editStatus;\n\n    /**\n     * Is it concurrent?\n     */\n    private Boolean concurrently;\n\n    /**\n     * Index method\n     */\n    private String method;\n\n\n    /**\n     * Foreign key points to schema\n     */\n    private String foreignSchemaName;\n\n    /**\n     * Foreign key points to table name\n     */\n    private String foreignTableName;\n\n    /**\n     * The column name pointed to by the foreign key\n     */\n    private List<String> foreignColumnNamelist;\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableIndexColumn.java",
    "content": "package ai.chat2db.spi.model;\n\n\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * Column information\n *\n * @author Jiaju Zhuang\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TableIndexColumn implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Index name\n     */\n    @JsonAlias({\"INDEX_NAME\"})\n    private String indexName;\n\n    /**\n     * Table Name\n     */\n    @JsonAlias ({\"TABLE_NAME\"})\n    private String tableName;\n\n    /**\n     * Index type\n     *\n     * @see\n     */\n    private String type;\n\n    /**\n     * Comment\n     */\n    private String comment;\n\n    /**\n     * columnName\n     */\n    @JsonAlias({\"COLUMN_NAME\"})\n    private String columnName;\n\n    /**\n     * ordinalPosition\n     */\n    @JsonAlias({\"ORDINAL_POSITION\"})\n    private Short ordinalPosition;\n\n    /**\n     * collation\n     *\n     */\n    private String collation;\n\n\n    /**\n     * The schema to which the index belongs\n     */\n    @JsonAlias({\"TABLE_SCHEM\"})\n    private String schemaName;\n\n    /**\n     * Database name\n     */\n    @JsonAlias({\"TABLE_CAT\"})\n    private String databaseName;\n\n    /**\n     * Is it unique?\n     */\n    @JsonAlias({\"NON_UNIQUE\"})\n    private Boolean nonUnique;\n\n    /**\n     *  index catalog (may be null); null when TYPE is tableIndexStatistic\n     */\n    @JsonAlias({\"INDEX_QUALIFIER\"})\n    private String indexQualifier;\n\n    /**\n     * ASC_OR_DESC String => column sort sequence, \"A\" => ascending, \"D\" => descending, may be null if sort sequence is not supported; null when TYPE is tableIndexStatistic\n     */\n    @JsonAlias({\"ASC_OR_DESC\"})\n    private String ascOrDesc;\n\n    /**\n     * CARDINALITY long => When TYPE is tableIndexStatistic, then this is the number of rows in the table; otherwise, it is the number of unique values in the index.\n     */\n    @JsonAlias({\"CARDINALITY\"})\n    private Long cardinality;\n\n    /**\n     * When TYPE is tableIndexStatistic then this is the number of pages used for the table, otherwise it is the number of pages used for the current index.\n     */\n    @JsonAlias({\"PAGES\"})\n    private Long pages;\n\n    /**\n     * Filter condition, if any. (may be null)\n     */\n    @JsonAlias({\"FILTER_CONDITION\"})\n    private String filterCondition;\n\n\n    private Long subPart;\n\n\n    private String editStatus;\n}\n\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableMeta.java",
    "content": "package ai.chat2db.spi.model;\n\nimport lombok.Builder;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n@Data\n@Builder\npublic class TableMeta  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private List<ColumnType> columnTypes;\n\n\n    private List<Charset> charsets;\n\n\n    private List<Collation> collations;\n\n\n    private List<IndexType> indexTypes;\n\n    private List<DefaultValue> defaultValues;\n\n    private List<EngineType> engineTypes;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Trigger.java",
    "content": "\npackage ai.chat2db.spi.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n/**\n * @author jipengfei\n * @version : Trigger.java\n */\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Trigger  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    private String databaseName;\n\n    private String schemaName;\n\n    private String triggerName;\n\n    private String eventManipulation;\n\n    private String triggerBody;\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Type.java",
    "content": "package ai.chat2db.spi.model;\n\nimport com.fasterxml.jackson.annotation.JsonAlias;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\nimport java.io.Serializable;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Type  implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    @JsonAlias(\"TYPE_NAME\")\n    private String typeName;\n\n    @JsonAlias(\"DATA_TYPE\")\n    private Integer dataType;\n\n    @JsonAlias(\"PRECISION\")\n    private Integer precision;\n\n    @JsonAlias(\"LITERAL_PREFIX\")\n    private String literalPrefix;\n\n    @JsonAlias(\"LITERAL_SUFFIX\")\n    private String literalSuffix;\n\n    @JsonAlias(\"CREATE_PARAMS\")\n    private String createParams;\n\n    @JsonAlias(\"NULLABLE\")\n    private Short nullable;\n\n\n    @JsonAlias(\"CASE_SENSITIVE\")\n    private Boolean caseSensitive;\n\n    @JsonAlias(\"SEARCHABLE\")\n    private Short searchable;\n\n    @JsonAlias(\"UNSIGNED_ATTRIBUTE\")\n    private Boolean unsignedAttribute;\n\n\n    @JsonAlias(\"FIXED_PREC_SCALE\")\n    private Boolean fixedPrecScale;\n\n    @JsonAlias(\"AUTO_INCREMENT\")\n    private Boolean autoIncrement;\n\n    @JsonAlias(\"LOCAL_TYPE_NAME\")\n    private String localTypeName;\n\n    @JsonAlias(\"MINIMUM_SCALE\")\n    private Short minimumScale;\n\n    @JsonAlias(\"MAXIMUM_SCALE\")\n    private Short maximumScale;\n\n    @JsonAlias(\"SQL_DATA_TYPE\")\n    private Integer sqlDataType;\n\n    @JsonAlias(\"SQL_DATETIME_SUB\")\n    private Integer sqlDatetimeSub;\n\n\n    @JsonAlias(\"NUM_PREC_RADIX\")\n    private Integer numPrecRadix;\n\n\n\n//    TYPE_NAME String => Type name\n//    DATA_TYPE int => SQL data type from java.sql.Types\n//    PRECISION int => maximum precision\n//    LITERAL_PREFIX String => prefix used to quote a literal (may be null)\n//    LITERAL_SUFFIX String => suffix used to quote a literal (may be null)\n//    CREATE_PARAMS String => parameters used in creating the type (may be null)\n//    NULLABLE short => can you use NULL for this type.\n//            typeNoNulls - does not allow NULL values\n//    typeNullable - allows NULL values\n//    typeNullableUnknown - nullability unknown\n//    CASE_SENSITIVE boolean=> is it case sensitive.\n//            SEARCHABLE short => can you use \"WHERE\" based on this type:\n//    typePredNone - No support\n//    typePredChar - Only supported with WHERE .. LIKE\n//    typePredBasic - Supported except for WHERE .. LIKE\n//    typeSearchable - Supported for all WHERE ..\n//    UNSIGNED_ATTRIBUTE boolean => is it unsigned.\n//            FIXED_PREC_SCALE boolean => can it be a money value.\n//    AUTO_INCREMENT boolean => can it be used for an auto-increment value.\n//    LOCAL_TYPE_NAME String => localized version of type name (may be null)\n//    MINIMUM_SCALE short => minimum scale supported\n//    MAXIMUM_SCALE short => maximum scale supported\n//    SQL_DATA_TYPE int => unused\n//    SQL_DATETIME_SUB int => unused\n//    NUM_PREC_RADIX int => usually 2 or 10\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/Chat2DBContext.java",
    "content": "\npackage ai.chat2db.spi.sql;\n\nimport ai.chat2db.spi.DBManage;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.Plugin;\nimport ai.chat2db.spi.SqlBuilder;\nimport ai.chat2db.spi.config.DBConfig;\nimport ai.chat2db.spi.config.DriverConfig;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.ServiceLoader;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author jipengfei\n * @version : Chat2DBContext.java\n */\n@Slf4j\npublic class Chat2DBContext {\n    private static final ThreadLocal<ConnectInfo> CONNECT_INFO_THREAD_LOCAL = new ThreadLocal<>();\n\n\n\n    public static Map<String, Plugin> PLUGIN_MAP = new ConcurrentHashMap<>();\n\n    static {\n        ServiceLoader<Plugin> s = ServiceLoader.load(Plugin.class);\n        Iterator<Plugin> iterator = s.iterator();\n        while (iterator.hasNext()) {\n            Plugin plugin = iterator.next();\n            PLUGIN_MAP.put(plugin.getDBConfig().getDbType(), plugin);\n        }\n    }\n\n    public static DriverConfig getDefaultDriverConfig(String dbType) {\n        return PLUGIN_MAP.get(dbType).getDBConfig().getDefaultDriverConfig();\n    }\n\n    public static SqlBuilder getSqlBuilder() {\n        return PLUGIN_MAP.get(getConnectInfo().getDbType()).getMetaData().getSqlBuilder();\n    }\n\n    /**\n     * Get the ContentContext of the current thread\n     *\n     * @return\n     */\n    public static ConnectInfo getConnectInfo() {\n        return CONNECT_INFO_THREAD_LOCAL.get();\n    }\n\n    public static MetaData getMetaData() {\n        return PLUGIN_MAP.get(getConnectInfo().getDbType()).getMetaData();\n    }\n\n    public static MetaData getMetaData(String dbType) {\n        if (StringUtils.isBlank(dbType)) {\n            return getMetaData();\n        }\n        return PLUGIN_MAP.get(dbType).getMetaData();\n    }\n\n    public static DBConfig getDBConfig(String dbType) {\n        return PLUGIN_MAP.get(dbType).getDBConfig();\n    }\n\n    public static DBConfig getDBConfig() {\n        return PLUGIN_MAP.get(getConnectInfo().getDbType()).getDBConfig();\n    }\n\n    public static DBManage getDBManage() {\n        return PLUGIN_MAP.get(getConnectInfo().getDbType()).getDBManage();\n    }\n\n    public static Connection getConnection() {\n//        ConnectInfo connectInfo = getConnectInfo();\n//        Connection connection = connectInfo.getConnection();\n//        try {\n//            if (connection == null || connection.isClosed()) {\n//                synchronized (connectInfo) {\n//                    connection = connectInfo.getConnection();\n//                    try {\n//                        if (connection != null && !connection.isClosed()) {\n//                            log.info(\"get connection from cache\");\n//                            return connection;\n//                        } else {\n//                            log.info(\"get connection from db begin\");\n//                            connection = getDBManage().getConnection(connectInfo);\n//                            log.info(\"get connection from db end\");\n//                        }\n//                    } catch (SQLException e) {\n//                        log.error(\"get connection error\", e);\n//                        log.info(\"get connection from db begin2\");\n//                        connection = getDBManage().getConnection(connectInfo);\n//                        log.info(\"get connection from db end2\");\n//                    }\n//                    connectInfo.setConnection(connection);\n//                }\n//            }\n//        } catch (SQLException e) {\n//            log.error(\"get connection error\", e);\n//        }\n        return ConnectionPool.getConnection(getConnectInfo());\n    }\n\n\n    public static String getDbVersion() {\n        ConnectInfo connectInfo = getConnectInfo();\n        String dbVersion = connectInfo.getDbVersion();\n        if (dbVersion == null) {\n            synchronized (connectInfo) {\n                if (connectInfo.getDbVersion() != null) {\n                    return connectInfo.getDbVersion();\n                } else {\n                    dbVersion = SQLExecutor.getInstance().getDbVersion(getConnection());\n                    connectInfo.setDbVersion(dbVersion);\n                    return connectInfo.getDbVersion();\n                }\n            }\n        } else {\n            return dbVersion;\n        }\n\n    }\n\n\n    /**\n     * Set context\n     *\n     * @param info\n     */\n    public static void putContext(ConnectInfo info) {\n        DriverConfig config = info.getDriverConfig();\n        if (config == null) {\n            config = getDefaultDriverConfig(info.getDbType());\n            info.setDriverConfig(config);\n        }\n        CONNECT_INFO_THREAD_LOCAL.set(info);\n    }\n\n    /**\n     * Set context\n     */\n    public static void removeContext() {\n        ConnectInfo connectInfo = CONNECT_INFO_THREAD_LOCAL.get();\n        if (connectInfo != null) {\n//            connectInfo.close();\n            CONNECT_INFO_THREAD_LOCAL.remove();\n            ConnectionPool.close(connectInfo);\n        }\n    }\n\n    public static void close() {\n        removeContext();\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/ConnectInfo.java",
    "content": "\npackage ai.chat2db.spi.sql;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.time.LocalDateTime;\nimport java.util.Date;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.KeyValue;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.model.SSLInfo;\nimport com.jcraft.jsch.JSchException;\nimport com.jcraft.jsch.Session;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * @author jipengfei\n * @version : ConnectInfo.java\n */\n@Slf4j\npublic class ConnectInfo {\n\n    private String loginUser;\n    /**\n     * alias\n     */\n    private String alias;\n    /**\n     * dataSourceId\n     */\n    private Long dataSourceId;\n\n\n    /**\n     * creation time\n     */\n    private LocalDateTime gmtCreate;\n\n    /**\n     * modified time\n     */\n    private LocalDateTime gmtModified;\n    /**\n     * database\n     */\n    private String databaseName;\n\n\n    /**\n     * schema\n     */\n    private String schemaName;\n\n    /**\n     * console id\n     */\n    private Long consoleId;\n\n    /**\n     * Database URL\n     */\n    private String url;\n\n    /**\n     * user\n     */\n    private String user;\n\n    /**\n     * password\n     */\n    private String password;\n\n    /**\n     * The console independently owns the connection\n     */\n    private Boolean consoleOwn = Boolean.FALSE;\n\n    /**\n     * Database type\n     */\n    private String dbType;\n\n    /**\n     * port\n     */\n    private Integer port;\n\n    /**\n     *\n     */\n    private String urlWithOutDatabase;\n\n    /**\n     * host\n     */\n    private String host;\n\n    /**\n     * ssh\n     */\n    private SSHInfo ssh;\n\n    /**\n     * ssh\n     */\n    private SSLInfo ssl;\n\n    /**\n     * sid\n     */\n    private String sid;\n\n    /**\n     * driver\n     */\n    private String driver;\n\n    /**\n     * jdbc version\n     */\n    private String jdbc;\n\n    /**\n     * Extended Information\n     */\n    private List<KeyValue> extendInfo;\n\n\n    public Connection connection;\n\n    /**\n     * Database version used for different database\n     */\n    private String dbVersion;\n\n\n    private DriverConfig driverConfig;\n\n\n    private Date lastAccessTime;\n\n\n    public String getDbVersion() {\n        return dbVersion;\n    }\n\n    public void setDbVersion(String dbVersion) {\n        this.dbVersion = dbVersion;\n    }\n\n    public DriverConfig getDriverConfig() {\n        return driverConfig;\n    }\n\n\n    public void setDriverConfig(DriverConfig driverConfig) {\n        this.driverConfig = driverConfig;\n    }\n\n    public Session getSession() {\n        return session;\n    }\n\n    public void setSession(Session session) {\n        this.session = session;\n    }\n\n    public Session session;\n\n\n\n    public LinkedHashMap<String, Object> getExtendMap() {\n\n        if (ObjectUtils.isEmpty(extendInfo)) {\n            if (driverConfig != null) {\n                extendInfo = driverConfig.getExtendInfo();\n            } else {\n                return new LinkedHashMap<>();\n            }\n        }\n        if (ObjectUtils.isEmpty(extendInfo)) {\n            return new LinkedHashMap<>();\n        }\n        LinkedHashMap<String, Object> map = new LinkedHashMap<>();\n        for (KeyValue keyValue : extendInfo) {\n            map.put(keyValue.getKey(), keyValue.getValue());\n        }\n        return map;\n    }\n\n\n    public void setDatabase(String database) {\n        this.databaseName = database;\n    }\n\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (!(o instanceof ConnectInfo)) {\n            return false;\n        }\n        ConnectInfo that = (ConnectInfo) o;\n        return Objects.equals(dataSourceId, that.dataSourceId)\n                && Objects.equals(gmtModified, that.gmtModified)\n                ;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(dataSourceId, consoleId, databaseName);\n    }\n\n    public Long getDataSourceId() {\n        return dataSourceId;\n    }\n\n    public void setDataSourceId(Long dataSourceId) {\n        this.dataSourceId = dataSourceId;\n    }\n\n    public String getDatabaseName() {\n        return databaseName;\n    }\n\n    public void setDatabaseName(String databaseName) {\n        this.databaseName = databaseName;\n    }\n\n    public Long getConsoleId() {\n        return consoleId;\n    }\n\n    public void setConsoleId(Long consoleId) {\n        this.consoleId = consoleId;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public String getUser() {\n        return user;\n    }\n\n    public void setUser(String user) {\n        this.user = user;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    /**\n     * Setter method for property <tt>password</tt>.\n     *\n     * @param password value to be assigned to property password\n     */\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    /**\n     * Getter method for property <tt>consoleOwn</tt>.\n     *\n     * @return property value of consoleOwn\n     */\n    public Boolean getConsoleOwn() {\n        return consoleOwn;\n    }\n\n    /**\n     * Setter method for property <tt>consoleOwn</tt>.\n     *\n     * @param consoleOwn value to be assigned to property consoleOwn\n     */\n    public void setConsoleOwn(Boolean consoleOwn) {\n        this.consoleOwn = consoleOwn;\n    }\n\n    /**\n     * Getter method for property <tt>dbType</tt>.\n     *\n     * @return property value of dbType\n     */\n    public String getDbType() {\n        return dbType;\n    }\n\n    /**\n     * Setter method for property <tt>dbType</tt>.\n     *\n     * @param dbType value to be assigned to property dbType\n     */\n    public void setDbType(String dbType) {\n        this.dbType = dbType;\n    }\n\n    /**\n     * Getter method for property <tt>port</tt>.\n     *\n     * @return property value of port\n     */\n    public Integer getPort() {\n        return port;\n    }\n\n    /**\n     * Setter method for property <tt>port</tt>.\n     *\n     * @param port value to be assigned to property port\n     */\n    public void setPort(Integer port) {\n        this.port = port;\n    }\n\n    /**\n     * Getter method for property <tt>urlWithOutDatabase</tt>.\n     *\n     * @return property value of urlWithOutDatabase\n     */\n    public String getUrlWithOutDatabase() {\n        return urlWithOutDatabase;\n    }\n\n    /**\n     * Setter method for property <tt>urlWithOutDatabase</tt>.\n     *\n     * @param urlWithOutDatabase value to be assigned to property urlWithOutDatabase\n     */\n    public void setUrlWithOutDatabase(String urlWithOutDatabase) {\n        this.urlWithOutDatabase = urlWithOutDatabase;\n    }\n\n    /**\n     * Getter method for property <tt>host</tt>.\n     *\n     * @return property value of host\n     */\n    public String getHost() {\n        return host;\n    }\n\n    /**\n     * Setter method for property <tt>host</tt>.\n     *\n     * @param host value to be assigned to property host\n     */\n    public void setHost(String host) {\n        this.host = host;\n    }\n\n    /**\n     * Getter method for property <tt>ssh</tt>.\n     *\n     * @return property value of ssh\n     */\n    public SSHInfo getSsh() {\n        return ssh;\n    }\n\n    /**\n     * Setter method for property <tt>ssh</tt>.\n     *\n     * @param ssh value to be assigned to property ssh\n     */\n    public void setSsh(SSHInfo ssh) {\n        this.ssh = ssh;\n    }\n\n    /**\n     * Getter method for property <tt>ssl</tt>.\n     *\n     * @return property value of ssl\n     */\n    public SSLInfo getSsl() {\n        return ssl;\n    }\n\n    /**\n     * Setter method for property <tt>ssl</tt>.\n     *\n     * @param ssl value to be assigned to property ssl\n     */\n    public void setSsl(SSLInfo ssl) {\n        this.ssl = ssl;\n    }\n\n    /**\n     * Getter method for property <tt>sid</tt>.\n     *\n     * @return property value of sid\n     */\n    public String getSid() {\n        return sid;\n    }\n\n    /**\n     * Setter method for property <tt>sid</tt>.\n     *\n     * @param sid value to be assigned to property sid\n     */\n    public void setSid(String sid) {\n        this.sid = sid;\n    }\n\n    /**\n     * Getter method for property <tt>driver</tt>.\n     *\n     * @return property value of driver\n     */\n    public String getDriver() {\n        return driver;\n    }\n\n    /**\n     * Setter method for property <tt>driver</tt>.\n     *\n     * @param driver value to be assigned to property driver\n     */\n    public void setDriver(String driver) {\n        this.driver = driver;\n    }\n\n    /**\n     * Getter method for property <tt>jdbc</tt>.\n     *\n     * @return property value of jdbc\n     */\n    public String getJdbc() {\n        return jdbc;\n    }\n\n    /**\n     * Setter method for property <tt>jdbc</tt>.\n     *\n     * @param jdbc value to be assigned to property jdbc\n     */\n    public void setJdbc(String jdbc) {\n        this.jdbc = jdbc;\n    }\n\n    /**\n     * Getter method for property <tt>extendInfo</tt>.\n     *\n     * @return property value of extendInfo\n     */\n    public List<KeyValue> getExtendInfo() {\n        return extendInfo;\n    }\n\n\n    /**\n     * Setter method for property <tt>extendInfo</tt>.\n     *\n     * @param extendInfo value to be assigned to property extendInfo\n     */\n    public void setExtendInfo(List<KeyValue> extendInfo) {\n        this.extendInfo = extendInfo;\n    }\n\n    /**\n     * Getter method for property <tt>connection</tt>.\n     *\n     * @return property value of connection\n     */\n    public Connection getConnection() {\n        return connection;\n    }\n\n    /**\n     * Setter method for property <tt>connection</tt>.\n     *\n     * @param connection value to be assigned to property connection\n     */\n    public void setConnection(Connection connection) {\n        this.connection = connection;\n    }\n\n\n    /**\n     * Getter method for property <tt>alias</tt>.\n     *\n     * @return property value of alias\n     */\n    public String getAlias() {\n        return alias;\n    }\n\n    /**\n     * Setter method for property <tt>alias</tt>.\n     *\n     * @param alias value to be assigned to property alias\n     */\n    public void setAlias(String alias) {\n        this.alias = alias;\n    }\n\n    public LocalDateTime getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(LocalDateTime gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public LocalDateTime getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(LocalDateTime gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getSchemaName() {\n        return schemaName;\n    }\n\n    public void setSchemaName(String schemaName) {\n        this.schemaName = schemaName;\n    }\n\n    public ConnectInfo copy() {\n        ConnectInfo copy = new ConnectInfo();\n        copy.setDbVersion(this.getDbVersion());\n        copy.setDbType(this.getDbType());\n        copy.setHost(this.getHost());\n        copy.setPort(this.getPort());\n        copy.setDatabaseName(this.getDatabaseName());\n        copy.setSchemaName(this.getSchemaName());\n        copy.setUser(this.getUser());\n        copy.setPassword(this.getPassword());\n        copy.setUrl(this.getUrl());\n        copy.setAlias(this.getAlias());\n        copy.setDataSourceId(this.getDataSourceId());\n        copy.setConsoleId(this.getConsoleId());\n        copy.setConsoleOwn(this.getConsoleOwn());\n        copy.setDriver(this.getDriver());\n        copy.setSsh(this.getSsh());\n        copy.setSsl(this.getSsl());\n        copy.setJdbc(this.getJdbc());\n        copy.setExtendInfo(this.getExtendInfo());\n        copy.setDriverConfig(this.getDriverConfig());\n        copy.setSid(this.getSid());\n        copy.setUrlWithOutDatabase(this.getUrlWithOutDatabase());\n        copy.setLastAccessTime(new Date());\n        return copy;\n    }\n\n    public void close() {\n        if (this != null) {\n            Connection connection = this.getConnection();\n            try {\n                if (connection != null && !connection.isClosed()) {\n                    connection.close();\n                    log.info(\"connection close success\");\n                }\n            } catch (SQLException e) {\n                log.error(\"connection close error\",e);\n            }\n            com.jcraft.jsch.Session session = this.getSession();\n            if (session != null && session.isConnected() && this.getSsh() != null\n                    && this.getSsh().isUse()) {\n                try {\n                    session.delPortForwardingL(Integer.parseInt(this.getSsh().getLocalPort()));\n                } catch (JSchException e) {\n                    log.error(\"ssh close error\",e);\n                }\n            }\n        }\n    }\n\n\n    public String getKey() {\n        return \"loginUser:\"+loginUser + \"_dataSourceId:\" + dataSourceId + \"_databaseName:\" + databaseName + \"_schemaName:\" + schemaName + \"_consoleId:\" + consoleId;\n    }\n\n    public String getLoginUser() {\n        return loginUser;\n    }\n\n    public void setLoginUser(String loginUser) {\n        this.loginUser = loginUser;\n    }\n\n    public Date getLastAccessTime() {\n        return lastAccessTime;\n    }\n\n    public void setLastAccessTime(Date lastAccessTime) {\n        this.lastAccessTime = lastAccessTime;\n    }\n\n    private final AtomicInteger refCount = new AtomicInteger(0);\n\n    public int incrementRefCount() {\n       return refCount.incrementAndGet();\n    }\n\n    public int decrementRefCount() {\n       return refCount.decrementAndGet();\n    }\n\n    public int getRefCount() {\n        return refCount.get();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/ConnectionPool.java",
    "content": "package ai.chat2db.spi.sql;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.h2.engine.ConnectionInfo;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n@Slf4j\npublic class ConnectionPool {\n\n    private static ConcurrentHashMap<String, ConnectInfo> CONNECTION_MAP = new ConcurrentHashMap<>();\n\n    static {\n        new Thread(() -> {\n            while (true) {\n                try {\n                    Thread.sleep(1000 * 60 * 10);\n                    log.info(\"CONNECTION_MAP size:{}\",CONNECTION_MAP.size());\n                    CONNECTION_MAP.forEach((k, v) -> {\n                        log.info(\"CONNECTION_key:{},value:{}\",k,v.getRefCount());\n                        if (v.getLastAccessTime().getTime() + 1000 * 60 * 20 < System.currentTimeMillis() && v.getRefCount() == 0) {\n                            try {\n                                Connection connection = v.getConnection();\n                                if (connection != null) {\n                                    connection.close();\n                                    CONNECTION_MAP.remove(k);\n                                }\n                            } catch (SQLException e) {\n                                log.error(\"close connection error\", e);\n                            }\n                        }\n                    });\n                } catch (InterruptedException e) {\n                    log.error(\"close connection error\", e);\n                }\n            }\n        }).start();\n\n    }\n\n    public static synchronized void removeConnection(Long datasourceId) {\n        CONNECTION_MAP.forEach((k, v) -> {\n            if (k.contains(String.valueOf(datasourceId))) {\n                try {\n                    Connection connection = v.getConnection();\n                    if (connection != null) {\n                        connection.close();\n                        CONNECTION_MAP.remove(k);\n                    }\n                } catch (SQLException e) {\n                    log.error(\"close connection error\", e);\n                }\n            }\n        });\n    }\n\n\n    public static Connection getConnection(ConnectInfo connectInfo) {\n        Connection connection = connectInfo.getConnection();\n        try {\n            if (connection != null && !connection.isClosed()) {\n                log.info(\"get connection from local\");\n                return connection;\n            }\n            String key = connectInfo.getKey();\n            ConnectInfo lock = CONNECTION_MAP.computeIfAbsent(key, k -> connectInfo.copy());\n            try {\n                synchronized (lock) {\n                    connection = connectInfo.getConnection();\n                    if (connection != null && !connection.isClosed()) {\n                        log.info(\"get connection from local\");\n                        return connection;\n                    }\n\n                    int n = lock.incrementRefCount();\n                    if (n == 1) {\n                        connection = lock.getConnection();\n                        if (connection != null && !connection.isClosed()) {\n                            log.info(\"get connection from cache\");\n                            connectInfo.setConnection(connection);\n                            lock.setLastAccessTime(new Date());\n                            return connection;\n                        } else {\n                            log.info(\"get connection from db begin\");\n                            connection = Chat2DBContext.getDBManage().getConnection(connectInfo);\n                            lock.setConnection(connection);\n                            lock.setLastAccessTime(new Date());\n                            log.info(\"get connection from db end\");\n                        }\n                        connectInfo.setConnection(connection);\n                        return connection;\n                    } else {\n                        connection = Chat2DBContext.getDBManage().getConnection(connectInfo);\n                        connectInfo.setConnection(connection);\n                        return connection;\n                    }\n\n                }\n            } catch (SQLException e) {\n                log.error(\"get connection error\", e);\n                if (connection != null && !connection.isClosed()) {\n                    connection.close();\n                }\n            }\n        } catch (SQLException e) {\n            log.error(\"get connection error\", e);\n            try {\n                if (connection != null && !connection.isClosed()) {\n                    connection.close();\n                }\n            } catch (Exception e1) {\n                log.error(\"\", e1);\n            }\n        }\n        return null;\n    }\n\n    public static void close(ConnectInfo connectInfo) {\n        String key = connectInfo.getKey();\n        try {\n            Connection currentConnection = connectInfo.getConnection();\n            // 如果当前连接已经关闭，则不需要重复关闭\n            if (currentConnection == null || currentConnection.isClosed()) {\n                log.info(\"connection is already closed, key:{}, n:{}\", connectInfo.getKey(), connectInfo.getRefCount());\n                return;\n            }\n        } catch (SQLException e) {\n            log.error(\"connection close error\",e);\n        }\n        ConnectInfo lock = CONNECTION_MAP.get(key);\n        if (lock != null) {\n            synchronized (lock) {\n                int n = lock.decrementRefCount();\n                if (n == 0) {\n                    lock.setLastAccessTime(new Date());\n                    lock.setConnection(connectInfo.getConnection());\n                } else {\n                    connectInfo.close();\n                }\n            }\n        } else {\n            connectInfo.close();\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/DocumentUtils.java",
    "content": "package ai.chat2db.spi.sql;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.ClassUtils;\n\n/**\n * @author luojun\n * @version 1.0\n * @description: 接口定义\n * @date 2024/5/31 19:05\n **/\npublic class DocumentUtils {\n\n    public static LinkedHashMap<String, Object> convertToMap(Object obj) {\n        LinkedHashMap<String, Object> map = new LinkedHashMap<>();\n        if (obj == null) {\n            return map;\n        }\n        if (ClassUtils.isPrimitiveOrWrapper(obj.getClass()) || String.class.equals(obj.getClass())) {\n            map.put(\"result\", obj);\n            return map;\n        }\n        for (Map.Entry<String, Object> entry : ((Map<String, Object>) obj).entrySet()) {\n            Object value = entry.getValue();\n            if (value == null) {\n                map.put(entry.getKey(), null);\n            } else if (ClassUtils.isPrimitiveOrWrapper(value.getClass()) || String.class.equals(value.getClass())) {\n                map.put(entry.getKey(), value);\n            } else if (entry.getValue() instanceof Map) {\n                LinkedHashMap<String, Object> mmp = convertToMap(entry.getValue());\n                map.put(entry.getKey(), mmp);\n            } else {\n                map.put(entry.getKey(), entry.getValue().toString());\n            }\n        }\n        return map;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/IDriverManager.java",
    "content": "\npackage ai.chat2db.spi.sql;\n\nimport ai.chat2db.server.tools.common.exception.ConnectionException;\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.model.DriverEntry;\nimport ai.chat2db.spi.util.JdbcJarUtils;\nimport com.alibaba.fastjson2.JSON;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.sql.Connection;\nimport java.sql.Driver;\nimport java.sql.DriverPropertyInfo;\nimport java.sql.SQLException;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Properties;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static ai.chat2db.spi.util.JdbcJarUtils.getFullPath;\n\n/**\n * @author jipengfei\n * @version : IsolationDriverManager.java\n */\npublic class IDriverManager {\n    private static final Logger log = LoggerFactory.getLogger(IDriverManager.class);\n    private static final Map<String, ClassLoader> CLASS_LOADER_MAP = new ConcurrentHashMap();\n    private static final Map<String, DriverEntry> DRIVER_ENTRY_MAP = new ConcurrentHashMap();\n    private static final String SQL_STATE_CODE = \"08001\";\n\n    public static Connection getConnection(String url, DriverConfig driver) throws SQLException {\n        Properties info = new Properties();\n        return getConnection(url, info, driver);\n    }\n\n    public static Connection getConnection(String url, String user, String password, DriverConfig driver)\n            throws SQLException {\n        Properties info = new Properties();\n        if (user != null) {\n            info.put(\"user\", user);\n        }\n\n        if (password != null) {\n            info.put(\"password\", password);\n        }\n\n        return getConnection(url, info, driver);\n    }\n\n    public static Connection getConnection(String url, String user, String password, DriverConfig driver,\n                                           Map<String, Object> properties)\n            throws SQLException {\n        Properties info = new Properties();\n        if (StringUtils.isNotEmpty(user)) {\n            info.put(\"user\", user);\n        }\n\n        if (StringUtils.isNotEmpty(password)) {\n            info.put(\"password\", password);\n        }\n        if (properties != null && !properties.isEmpty()) {\n            for (Map.Entry<String, Object> entry : properties.entrySet()) {\n                if (entry.getKey() != null && entry.getValue() != null) {\n                    info.put(entry.getKey(), entry.getValue());\n                }\n            }\n        }\n        return getConnection(url, info, driver);\n    }\n\n    public static Connection getConnection(String url, Properties info, DriverConfig driver)\n            throws SQLException {\n        if (Objects.isNull(url)) {\n            throw new SQLException(\"The url cannot be null\", SQL_STATE_CODE);\n        }\n\n        DriverEntry driverEntry = DRIVER_ENTRY_MAP.get(driver.getJdbcDriver());\n        if (Objects.isNull(driverEntry)) {\n            driverEntry = getJDBCDriver(driver);\n        }\n        Connection connection;\n        try {\n            connection = driverEntry.getDriver().connect(url, info);\n            if (Objects.isNull(connection)) {\n                throw new SQLException(String.format(\"driver.connect return null , No suitable driver found for url %s\", url), SQL_STATE_CODE);\n\n            }\n            return connection;\n        } catch (SQLException sqlException) {\n            Connection con = tryConnectionAgain(driverEntry, url, info);\n\n            if (Objects.isNull(con)) {\n                throw new SQLException(String.format(\"Cannot create connection (%s)\", sqlException.getMessage()), SQL_STATE_CODE,\n                        sqlException);\n            }\n\n            return con;\n        }\n    }\n\n    public static DriverPropertyInfo[] getProperty(DriverConfig driver)\n            throws SQLException {\n        if (Objects.isNull(driver)) {\n            return null;\n        }\n        DriverEntry driverEntry = DRIVER_ENTRY_MAP.get(driver.getJdbcDriver());\n        try {\n            if (driverEntry == null) {\n                driverEntry = getJDBCDriver(driver);\n            }\n            String url = Objects.isNull(driver.getUrl()) ? \"\" : driver.getUrl();\n            return driverEntry.getDriver().getPropertyInfo(url, null);\n        } catch (Exception var7) {\n            return null;\n        }\n    }\n\n\n    private static Connection tryConnectionAgain(DriverEntry driverEntry, String url,\n                                                 Properties info) throws SQLException {\n        if (url.contains(\"mysql\")) {\n            if (!info.containsKey(\"useSSL\")) {\n                info.put(\"useSSL\", \"false\");\n            }\n            return driverEntry.getDriver().connect(url, info);\n        }\n        return null;\n    }\n\n    private static DriverEntry getJDBCDriver(DriverConfig driver)\n            throws SQLException {\n        synchronized (driver) {\n            try {\n                if (DRIVER_ENTRY_MAP.containsKey(driver.getJdbcDriver())) {\n                    return DRIVER_ENTRY_MAP.get(driver.getJdbcDriver());\n                }\n                ClassLoader cl = getClassLoader(driver);\n                Driver d = (Driver) cl.loadClass(driver.getJdbcDriverClass()).newInstance();\n                DriverEntry driverEntry = DriverEntry.builder().driverConfig(driver).driver(d).build();\n                DRIVER_ENTRY_MAP.put(driver.getJdbcDriver(), driverEntry);\n                return driverEntry;\n            } catch (Exception e) {\n                throw new ConnectionException(\"connection.driver.load.error\", null, e);\n            }\n        }\n\n    }\n\n    public static ClassLoader getClassLoader(DriverConfig driverConfig) throws MalformedURLException, ClassNotFoundException {\n        String jarPath = driverConfig.getJdbcDriver();\n        if (CLASS_LOADER_MAP.containsKey(jarPath)) {\n            return CLASS_LOADER_MAP.get(jarPath);\n        } else {\n            synchronized (jarPath) {\n                if (CLASS_LOADER_MAP.containsKey(jarPath)) {\n                    return CLASS_LOADER_MAP.get(jarPath);\n                }\n                String[] jarPaths = jarPath.split(\",\");\n                URL[] urls = new URL[jarPaths.length];\n                for (int i = 0; i < jarPaths.length; i++) {\n                    File driverFile = new File(getFullPath(jarPaths[i]));\n                    urls[i] = driverFile.toURI().toURL();\n                }\n                //urls[jarPaths.length] = new File(JdbcJarUtils.getFullPath(\"HikariCP-4.0.3.jar\")).toURI().toURL();\n\n                URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());\n                log.info(\"ClassLoader class:{}\", cl.hashCode());\n                log.info(\"ClassLoader URLs:{}\", JSON.toJSONString(cl.getURLs()));\n\n                try {\n                    cl.loadClass(driverConfig.getJdbcDriverClass());\n                } catch (Exception e) {\n                    //If an error is reported, delete the directory and try again.\n                    for (int i = 0; i < jarPaths.length; i++) {\n                        File driverFile = new File(JdbcJarUtils.getNewFullPath(jarPaths[i]));\n                        urls[i] = driverFile.toURI().toURL();\n                    }\n                    //urls[jarPaths.length] = new File(JdbcJarUtils.getFullPath(\"HikariCP-4.0.3.jar\")).toURI().toURL();\n                    cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());\n                    cl.loadClass(driverConfig.getJdbcDriverClass());\n                }\n                CLASS_LOADER_MAP.put(jarPath, cl);\n                return cl;\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/MongExtendedJsonObjectIdConverter.java",
    "content": "//package ai.chat2db.spi.sql;\n//\n//import org.bson.json.Converter;\n//import org.bson.json.StrictJsonWriter;\n//import org.bson.types.ObjectId;\n//\n//public class MongExtendedJsonObjectIdConverter implements Converter<ObjectId> {\n//    @Override\n//    public void convert(final ObjectId value, final StrictJsonWriter writer) {\n//        writer.writeStartObject();\n//        writer.writeString(\"\", value.toHexString());\n//        writer.writeEndObject();\n//    }\n//}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/ResultSetConsumer.java",
    "content": "package ai.chat2db.spi.sql;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n@FunctionalInterface\npublic interface ResultSetConsumer {\n\n    void accept(ResultSet resultSet) throws SQLException;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/ResultSetFunction.java",
    "content": "package ai.chat2db.spi.sql;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\npublic interface ResultSetFunction<R> {\n\n    R apply(ResultSet t) throws SQLException;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java",
    "content": "package ai.chat2db.spi.sql;\n\nimport ai.chat2db.server.tools.base.constant.EasyToolsConstant;\nimport ai.chat2db.server.tools.base.enums.DataSourceTypeEnum;\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.server.tools.common.util.EasyCollectionUtils;\nimport ai.chat2db.server.tools.common.util.I18nUtils;\nimport ai.chat2db.spi.CommandExecutor;\nimport ai.chat2db.spi.MetaData;\nimport ai.chat2db.spi.ValueProcessor;\nimport ai.chat2db.spi.enums.DataTypeEnum;\nimport ai.chat2db.spi.enums.SqlTypeEnum;\nimport ai.chat2db.spi.model.*;\nimport ai.chat2db.spi.util.JdbcUtils;\nimport ai.chat2db.spi.util.ResultSetUtils;\nimport ai.chat2db.spi.util.SqlUtils;\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.date.TimeInterval;\nimport cn.hutool.core.util.ArrayUtil;\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.google.common.collect.Lists;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.util.Assert;\n\nimport java.sql.*;\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\n/**\n * Dbhub unified database connection management\n *\n * @author jipengfei\n */\n@Slf4j\npublic class SQLExecutor implements CommandExecutor {\n\n    /**\n     * Singleton instance of SQLExecutor.\n     */\n    private static final SQLExecutor INSTANCE = new SQLExecutor();\n\n    public SQLExecutor() {\n    }\n\n    public static SQLExecutor getInstance() {\n        return INSTANCE;\n    }\n\n\n    public <R> R execute(Connection connection, String sql, ResultSetFunction<R> function) {\n        try (Statement stmt = connection.createStatement();) {\n            boolean query = stmt.execute(sql);\n            // Represents the query\n            if (query) {\n                try (ResultSet rs = stmt.getResultSet();) {\n                    return function.apply(rs);\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"execute:{}\", sql, e);\n            throw new RuntimeException(e);\n        }\n        return null;\n    }\n\n    public void execute(Connection connection, String sql, ResultSetConsumer consumer) {\n        try (Statement stmt = connection.createStatement()) {\n            boolean query = stmt.execute(sql);\n            // Represents the query\n            if (query) {\n                try (ResultSet rs = stmt.getResultSet();) {\n                    consumer.accept(rs);\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"execute:{}\", sql, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n\n    public void execute(\n            Connection connection, String sql,\n            Consumer<List<Header>> headerConsumer,\n            Consumer<List<String>> rowConsumer,\n            java.util.function.Function<JDBCDataValue,\n                    String> valueFunction,\n            boolean limitSize) {\n        Assert.notNull(sql, \"SQL must not be null\");\n        try (Statement stmt = connection.createStatement();) {\n            boolean query = stmt.execute(sql);\n            // Represents the query\n            if (query) {\n                ResultSet rs = null;\n                try {\n                    rs = stmt.getResultSet();\n                    // Get how many columns\n                    ResultSetMetaData resultSetMetaData = rs.getMetaData();\n                    int col = resultSetMetaData.getColumnCount();\n\n                    // Get header information\n                    List<Header> headerList = generateHeaderList(resultSetMetaData);\n                    headerConsumer.accept(headerList);\n\n                    while (rs.next()) {\n                        List<String> row = Lists.newArrayListWithExpectedSize(col);\n                        for (int i = 1; i <= col; i++) {\n                            JDBCDataValue jdbcDataValue = new JDBCDataValue(rs, resultSetMetaData, i, limitSize);\n                            row.add(valueFunction.apply(jdbcDataValue));\n                        }\n                        rowConsumer.accept(row);\n                    }\n                } finally {\n                    JdbcUtils.closeResultSet(rs);\n                }\n            }\n        } catch (SQLException e) {\n            log.error(\"execute:{}\", sql, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public <R> R preExecute(Connection connection, String sql, String[] args, ResultSetFunction<R> function) {\n        log.info(\"preExecute:{}\", sql);\n        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {\n            if (ArrayUtil.isNotEmpty(args)) {\n                for (int i = 0; i < args.length; i++) {\n                    preparedStatement.setObject(i + 1, args[i]);\n                }\n            }\n            boolean query = preparedStatement.execute();\n            // Represents the query\n            if (query) {\n                try (ResultSet rs = preparedStatement.getResultSet()) {\n                    return function.apply(rs);\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"execute:{}\", sql, e);\n            throw new RuntimeException(e);\n        }\n        return null;\n    }\n\n//    /**\n//     * Execute SQL\n//     *\n//     * @param sql\n//     * @return\n//     * @throws SQLException\n//     */\n//    public ExecuteResult execute(final String sql, Connection connection, ValueHandler valueHandler)\n//        throws SQLException {\n//        return execute(sql, connection, true, null, null, valueHandler);\n//    }\n\n    @Override\n    public ExecuteResult executeUpdate(String sql, Connection connection, int n)\n            throws SQLException {\n        Assert.notNull(sql, \"SQL must not be null\");\n        ExecuteResult executeResult = ExecuteResult.builder().sql(sql).success(Boolean.TRUE).build();\n        try (Statement stmt = connection.createStatement()) {\n            int affectedRows = stmt.executeUpdate(sql);\n            if (affectedRows != n) {\n                log.info(\"Update error {} update affectedRows = {}\", sql, affectedRows);\n            }\n        }\n        return executeResult;\n    }\n\n    @Override\n    public List<ExecuteResult> executeSelectTable(Command command) {\n        MetaData metaData = Chat2DBContext.getMetaData();\n        String tableName = metaData.getMetaDataName(command.getDatabaseName(), command.getSchemaName(),\n                command.getTableName());\n        String sql = \"select * from \" + tableName;\n        command.setScript(sql);\n        return execute(command);\n    }\n\n\n    /**\n     * Executes the given SQL query using the provided connection.\n     *\n     * @param sql          The SQL query to be executed.\n     * @param connection   The database connection to use for the query.\n     * @param limitRowSize Flag to indicate if row size should be limited.\n     * @param offset       The starting point of rows to fetch in the result set.\n     * @param count        The number of rows to fetch from the result set.\n     * @return ExecuteResult containing the result of the execution.\n     * @throws SQLException If there is any SQL related error.\n     */\n    public ExecuteResult execute(final String sql, Connection connection, boolean limitRowSize, Integer offset, Integer count)\n            throws SQLException {\n        Assert.notNull(sql, \"SQL must not be null\");\n        ExecuteResult executeResult = ExecuteResult.builder().sql(sql).success(Boolean.TRUE).build();\n        try (Statement stmt = connection.createStatement()) {\n            stmt.setFetchSize(EasyToolsConstant.MAX_PAGE_SIZE);\n            if (offset != null && count != null) {\n                stmt.setMaxRows(offset + count);\n            }\n            TimeInterval timeInterval = new TimeInterval();\n            boolean query = stmt.execute(sql);\n            executeResult.setDescription(I18nUtils.getMessage(\"sqlResult.success\"));\n            // Represents the query\n            if (query) {\n                executeResult = generateQueryExecuteResult(stmt, limitRowSize, offset, count);\n            } else {\n                // Modification or other\n                executeResult.setUpdateCount(stmt.getUpdateCount());\n            }\n            executeResult.setDuration(timeInterval.interval());\n        }\n        return executeResult;\n    }\n\n    private ExecuteResult generateQueryExecuteResult(Statement stmt, boolean limitRowSize, Integer offset,\n                                                     Integer count) throws SQLException {\n        ExecuteResult executeResult = ExecuteResult.builder().success(Boolean.TRUE).build();\n        executeResult.setDescription(I18nUtils.getMessage(\"sqlResult.success\"));\n        ResultSet rs = null;\n        try {\n            rs = stmt.getResultSet();\n            // Get how many columns\n            ResultSetMetaData resultSetMetaData = rs.getMetaData();\n            int col = resultSetMetaData.getColumnCount();\n            // Get header information\n            List<Header> headerList = generateHeaderList(resultSetMetaData);\n\n\n            int chat2dbAutoRowIdIndex = getChat2dbAutoRowIdIndex(headerList);\n            // Get data information\n            List<List<String>> dataList = generateDataList(rs, col, chat2dbAutoRowIdIndex, limitRowSize,\n                    offset, count);\n\n            executeResult.setHeaderList(headerList);\n            executeResult.setDataList(dataList);\n        } finally {\n            JdbcUtils.closeResultSet(rs);\n        }\n        return executeResult;\n    }\n\n    private List<List<String>> generateDataList(ResultSet rs, int col, int chat2dbAutoRowIdIndex,\n                                                boolean limitRowSize, Integer offset, Integer count) throws SQLException {\n        List<List<String>> dataList = Lists.newArrayList();\n\n        if (offset == null || offset < 0) {\n            offset = 0;\n        }\n        int rowNumber = 0;\n        int rowCount = 1;\n        while (rs.next()) {\n            if (rowNumber++ < offset) {\n                continue;\n            }\n            List<String> row = Lists.newArrayListWithExpectedSize(col);\n            dataList.add(row);\n            for (int i = 1; i <= col; i++) {\n                if (chat2dbAutoRowIdIndex == i) {\n                    continue;\n                }\n                ValueProcessor valueProcessor = Chat2DBContext.getMetaData().getValueProcessor();\n                row.add(valueProcessor.getJdbcValue(new JDBCDataValue(rs, rs.getMetaData(), i, limitRowSize)));\n            }\n            if (count != null && count > 0 && rowCount++ >= count) {\n                break;\n            }\n        }\n        return dataList;\n    }\n\n    private int getChat2dbAutoRowIdIndex(List<Header> headerList) {\n\n        for (int i = 0; i < headerList.size(); i++) {\n            Header header = headerList.get(i);\n            if (\"CAHT2DB_AUTO_ROW_ID\".equals(header.getName())) {\n                headerList.remove(i);\n                return i + 1;\n            }\n        }\n        return -1;\n    }\n\n\n    private List<Header> generateHeaderList(ResultSetMetaData resultSetMetaData) throws SQLException {\n        int col = resultSetMetaData.getColumnCount();\n        List<Header> headerList = Lists.newArrayListWithExpectedSize(col);\n        for (int i = 1; i <= col; i++) {\n            headerList.add(Header.builder()\n                    .dataType(JdbcUtils.resolveDataType(\n                            resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode())\n                    .name(ResultSetUtils.getColumnName(resultSetMetaData, i))\n                    .build());\n        }\n        return headerList;\n    }\n\n\n    public ExecuteResult execute(Connection connection, String sql) throws SQLException {\n        return execute(sql, connection, true, null, null);\n    }\n\n    /**\n     * Get all databases\n     *\n     * @param connection\n     * @return\n     */\n    public List<Database> databases(Connection connection) {\n        try (ResultSet resultSet = connection.getMetaData().getCatalogs();) {\n            List<Database> databases = ResultSetUtils.toObjectList(resultSet, Database.class);\n            if (CollectionUtils.isEmpty(databases)) {\n                return databases;\n            }\n            return databases.stream().filter(database -> database.getName() != null).collect(Collectors.toList());\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * Retrieves the schema names available in this database. The results are ordered by TABLE_CATALOG and TABLE_SCHEM.\n     * The schema columns are: TABLE_SCHEM String => schema name TABLE_CATALOG String => catalog name (may be null)\n     * Params: catalog – a catalog name; must match the catalog name as it is stored in the database;\"\" retrieves those\n     * without a catalog; null means catalog name should not be used to narrow down the search. schemaPattern – a schema\n     * name; must match the schema name as it is stored in the database; null means schema name should not be used to\n     * narrow down the search. Returns: a ResultSet object in which each row is a schema description Throws:\n     * SQLException – if a database access error occurs Since: 1.6 See Also: getSearchStringEscape\n     */\n    public List<Schema> schemas(Connection connection, String databaseName, String schemaName) {\n        if (StringUtils.isEmpty(databaseName) && StringUtils.isEmpty(schemaName)) {\n            try (ResultSet resultSet = connection.getMetaData().getSchemas()) {\n                return ResultSetUtils.toObjectList(resultSet, Schema.class);\n            } catch (SQLException e) {\n                throw new RuntimeException(\"Get schemas error\", e);\n            }\n        }\n        try (ResultSet resultSet = connection.getMetaData().getSchemas(databaseName, schemaName)) {\n            return ResultSetUtils.toObjectList(resultSet, Schema.class);\n        } catch (SQLException e) {\n            throw new RuntimeException(\"Get schemas error\", e);\n        }\n    }\n\n    /**\n     * Get all database tables\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @param types\n     * @return\n     */\n    public List<Table> tables(Connection connection, String databaseName, String schemaName, String tableName,\n                              String types[]) {\n        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, tableName, types)) {\n            return ResultSetUtils.toObjectList(resultSet, Table.class);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * query table names\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @param types\n     * @return\n     */\n    public List<String> tableNames(Connection connection, String databaseName, String schemaName, String tableName,\n                                   String[] types) {\n        List<String> tableNames = new ArrayList<>();\n        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, tableName, types)) {\n            while (resultSet.next()) {\n                tableNames.add(resultSet.getString(\"TABLE_NAME\"));\n            }\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n        return tableNames;\n    }\n\n    /**\n     * Get all database table columns\n     *\n     * @param connection\n     * @param databaseName\n     * @param schemaName\n     * @param tableName\n     * @param columnName\n     * @return\n     */\n    public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String\n            tableName,\n                                     String columnName) {\n        try (ResultSet resultSet = connection.getMetaData().getColumns(databaseName, schemaName, tableName,\n                columnName)) {\n            return ResultSetUtils.toObjectList(resultSet, TableColumn.class);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * get all table index info\n     *\n     * @param connection   connection\n     * @param databaseName databaseName of the index\n     * @param schemaName   schemaName of the index\n     * @param tableName    tableName of the index\n     * @return List<TableIndex> table index list\n     */\n    public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {\n        List<TableIndex> tableIndices = Lists.newArrayList();\n        try (ResultSet resultSet = connection.getMetaData().getIndexInfo(databaseName, schemaName, tableName,\n                false,\n                false)) {\n            List<TableIndexColumn> tableIndexColumns = ResultSetUtils.toObjectList(resultSet, TableIndexColumn.class);\n            tableIndexColumns.stream().filter(c -> c.getIndexName() != null).collect(\n                            Collectors.groupingBy(TableIndexColumn::getIndexName)).entrySet()\n                    .stream().forEach(entry -> {\n                        TableIndex tableIndex = new TableIndex();\n                        TableIndexColumn column = entry.getValue().get(0);\n                        tableIndex.setName(entry.getKey());\n                        tableIndex.setTableName(column.getTableName());\n                        tableIndex.setSchemaName(column.getSchemaName());\n                        tableIndex.setDatabaseName(column.getDatabaseName());\n                        tableIndex.setUnique(!column.getNonUnique());\n                        tableIndex.setColumnList(entry.getValue());\n                        tableIndices.add(tableIndex);\n                    });\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n        return tableIndices;\n    }\n\n\n    /**\n     * Get all functions available in a catalog.\n     *\n     * @param connection   connection\n     * @param databaseName databaseName of the function\n     * @param schemaName   schemaName of the function\n     * @return List<Function>\n     */\n    public List<ai.chat2db.spi.model.Function> functions(Connection connection, String databaseName,\n                                                         String schemaName) {\n        try (ResultSet resultSet = connection.getMetaData().getFunctions(databaseName, schemaName, null);) {\n            return ResultSetUtils.toObjectList(resultSet, ai.chat2db.spi.model.Function.class);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * Retrieves a description of all the data types supported by this database. They are ordered by DATA_TYPE and then\n     * by how closely the data type maps to the corresponding JDBC SQL type. If the database supports SQL distinct\n     * types, then getTypeInfo() will return a single row with a TYPE_NAME of DISTINCT and a DATA_TYPE of\n     * Types.DISTINCT. If the database supports SQL structured types, then getTypeInfo() will return a single row with a\n     * TYPE_NAME of STRUCT and a DATA_TYPE of Types.STRUCT. If SQL distinct or structured types are supported, then\n     * information on the individual types may be obtained from the getUDTs() method.\n     *\n     * @param connection connection\n     * @return List<Function>\n     */\n    public List<Type> types(Connection connection) {\n        try (ResultSet resultSet = connection.getMetaData().getTypeInfo();) {\n            return ResultSetUtils.toObjectList(resultSet, ai.chat2db.spi.model.Type.class);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * procedure list\n     *\n     * @param connection   connection\n     * @param databaseName databaseName\n     * @param schemaName   schemaName\n     * @return List<Procedure>\n     */\n    public List<Procedure> procedures(Connection connection, String databaseName, String schemaName) {\n        try (ResultSet resultSet = connection.getMetaData().getProcedures(databaseName, schemaName, null)) {\n            return ResultSetUtils.toObjectList(resultSet, Procedure.class);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public String getDbVersion(Connection connection) {\n        try {\n            String dbVersion = connection.getMetaData().getDatabaseProductVersion();\n            return dbVersion;\n        } catch (Exception e) {\n            log.error(\"get db version error\", e);\n        }\n        return \"\";\n    }\n\n    @Override\n    public List<ExecuteResult> execute(Command command) {\n        if (StringUtils.isBlank(command.getScript())) {\n            return Collections.emptyList();\n        }\n        // parse sql\n        String type = Chat2DBContext.getConnectInfo().getDbType();\n        DbType dbType = JdbcUtils.parse2DruidDbType(type);\n        List<String> sqlList = Lists.newArrayList(command.getScript());\n        if(!command.isSingle()) {\n            sqlList = SqlUtils.parse(command.getScript(), dbType, true);\n        }\n        if (CollectionUtils.isEmpty(sqlList)) {\n            throw new BusinessException(\"dataSource.sqlAnalysisError\");\n        }\n        List<ExecuteResult> result = new ArrayList<>();\n        // Execute SQL\n        for (String originalSql : sqlList) {\n            ExecuteResult executeResult = executeSQL(originalSql, dbType, command);\n            result.add(executeResult);\n        }\n        return result;\n    }\n\n    private ExecuteResult executeSQL(String originalSql, DbType dbType, Command param) {\n        int pageNo = Optional.ofNullable(param.getPageNo()).orElse(1);\n        int pageSize = Optional.ofNullable(param.getPageSize()).orElse(EasyToolsConstant.MAX_PAGE_SIZE);\n        Integer offset = (pageNo - 1) * pageSize;\n        Integer count = pageSize;\n        SqlTypeEnum sqlType = getSqlType(dbType, originalSql);\n        ExecuteResult executeResult = null;\n\n        if (SqlTypeEnum.SELECT.equals(sqlType) && !SqlUtils.hasPageLimit(originalSql, dbType)) {\n            String pageLimit = Chat2DBContext.getSqlBuilder().pageLimit(originalSql, offset, pageNo, pageSize);\n            if (StringUtils.isNotBlank(pageLimit)) {\n                executeResult = execute(pageLimit, 0, count);\n            }\n        }\n        if (executeResult == null || !executeResult.getSuccess()) {\n            executeResult = execute(originalSql, offset, count);\n        }\n\n        executeResult.setSqlType(sqlType.getCode());\n        executeResult.setOriginalSql(originalSql);\n\n        SqlUtils.buildCanEditResult(originalSql, dbType, executeResult);\n        // Add row number\n        addRowNumber(executeResult, pageNo, pageSize);\n        //  Total number of fuzzy rows\n        setPageInfo(executeResult, sqlType, pageNo, pageSize);\n        return executeResult;\n    }\n\n    private SqlTypeEnum getSqlType(DbType dbType, String originalSql) {\n        SqlTypeEnum sqlType = SqlTypeEnum.UNKNOWN;\n        // parse sql\n        String type = Chat2DBContext.getConnectInfo().getDbType();\n        boolean supportDruid = !DataSourceTypeEnum.MONGODB.getCode().equals(type);\n        SQLStatement sqlStatement = null;\n        if (supportDruid) {\n            try {\n                sqlStatement = SQLUtils.parseSingleStatement(originalSql, dbType);\n            } catch (Exception e) {\n                log.warn(\"Failed to parse sql: {}\", originalSql, e);\n            }\n        }\n\n        // Mongodb is currently unable to recognize it, so every time a page is transmitted\n        if (!supportDruid || (sqlStatement instanceof SQLSelectStatement)) {\n            sqlType = SqlTypeEnum.SELECT;\n        }\n        return sqlType;\n    }\n\n    private void setPageInfo(ExecuteResult executeResult, SqlTypeEnum sqlType, int pageNo, int pageSize) {\n        if (SqlTypeEnum.SELECT.equals(sqlType)) {\n            executeResult.setPageNo(pageNo);\n            executeResult.setPageSize(pageSize);\n            executeResult.setHasNextPage(\n                    CollectionUtils.size(executeResult.getDataList()) >= executeResult.getPageSize());\n        } else {\n            executeResult.setPageNo(pageNo);\n            executeResult.setPageSize(CollectionUtils.size(executeResult.getDataList()));\n            executeResult.setHasNextPage(Boolean.FALSE);\n        }\n        executeResult.setFuzzyTotal(calculateFuzzyTotal(pageNo, pageSize, executeResult));\n    }\n\n\n    private void addRowNumber(ExecuteResult executeResult, int pageNo, int pageSize) {\n        List<Header> headers = executeResult.getHeaderList();\n        Header rowNumberHeader = Header.builder()\n                .name(I18nUtils.getMessage(\"sqlResult.rowNumber\"))\n                .dataType(DataTypeEnum.CHAT2DB_ROW_NUMBER\n                        .getCode()).build();\n        executeResult.setHeaderList(EasyCollectionUtils.union(Arrays.asList(rowNumberHeader), headers));\n\n        // Add row number\n        if (executeResult.getDataList() != null) {\n            int rowNumberIncrement = 1 + Math.max(pageNo - 1, 0) * pageSize;\n            for (int i = 0; i < executeResult.getDataList().size(); i++) {\n                List<String> row = executeResult.getDataList().get(i);\n                List<String> newRow = Lists.newArrayListWithExpectedSize(row.size() + 1);\n                newRow.add(Integer.toString(i + rowNumberIncrement));\n                newRow.addAll(row);\n                executeResult.getDataList().set(i, newRow);\n            }\n        }\n    }\n\n\n    private String calculateFuzzyTotal(int pageNo, int pageSize, ExecuteResult executeResult) {\n        int dataSize = CollectionUtils.size(executeResult.getDataList());\n        if (pageSize <= 0) {\n            return Integer.toString(dataSize);\n        }\n        int fuzzyTotal = Math.max(pageNo - 1, 0) * pageSize + dataSize;\n        if (dataSize < pageSize) {\n            return Integer.toString(fuzzyTotal);\n        }\n        return fuzzyTotal + \"+\";\n    }\n\n    private ExecuteResult execute(String sql, Integer offset, Integer count) {\n        ExecuteResult executeResult;\n        try {\n            executeResult = SQLExecutor.getInstance().execute(sql, Chat2DBContext.getConnection(), true, offset, count);\n        } catch (SQLException e) {\n            log.error(\"Execute sql: {} exception\", sql, e);\n            executeResult = ExecuteResult.builder()\n                    .sql(sql)\n                    .success(Boolean.FALSE)\n                    .message(e.getMessage())\n                    .build();\n        }\n        return executeResult;\n    }\n\n    /**\n     * Formats the given table name by stripping off any schema or catalog prefixes.\n     * If the table name contains a dot ('.'), it splits the string by the dot\n     * and returns the last part, which is generally the actual table name.\n     * If the table name is blank (null, empty, or only whitespace), it returns the original table name.\n     *\n     * @param tableName the original table name, potentially including schema or catalog prefixes.\n     * @return the formatted table name, or the original table name if it's blank or contains no dot.\n     */\n    public static String formatTableName(String tableName) {\n        // Check if the table name is blank (null, empty, or only whitespace)\n        if (StringUtils.isBlank(tableName)) {\n            return tableName;\n        }\n\n        // Check if the table name contains a dot ('.')\n        if (tableName.contains(\".\")) {\n            // Split the table name by the dot and return the last part\n            String[] split = tableName.split(\"\\\\.\");\n            return split[split.length - 1];\n        }\n\n        // Return the original table name if it contains no dot\n        return tableName;\n    }\n\n    public void execute(Connection connection, String sql, int batchSize, ResultSetConsumer consumer) {\n        try (Statement stmt = connection.createStatement()) {\n            stmt.setFetchSize(batchSize);\n            boolean query = stmt.execute(sql);\n            // Represents the query\n            if (query) {\n                try (ResultSet rs = stmt.getResultSet()) {\n                    consumer.accept(rs);\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"execute error:{}\", sql, e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void executeBatchInsert(Connection connection, List<String> sqlCacheList) {\n        try (Statement stmt = connection.createStatement()) {\n            for (String sql : sqlCacheList) {\n                stmt.addBatch(sql);\n            }\n            stmt.executeBatch();\n            stmt.clearBatch();\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SqlParseUtils.java",
    "content": "package ai.chat2db.spi.sql;\n\nimport java.util.List;\n\nimport com.alibaba.druid.sql.parser.SQLParserUtils;\nimport com.google.common.collect.Lists;\n\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * @author luojun\n * @version 1.0\n * @description: 用于解析sql，输出语句不会改变关键字的大小写\n * @date 2024/6/3 10:57\n **/\n@Slf4j\npublic class SqlParseUtils {\n\n    public static List<String> parseSql(String sql) {\n        List<String> list = Lists.newArrayList();\n        try {\n            return SQLParserUtils.splitAndRemoveComment(sql, null);\n        } catch (Exception e) {\n            list.add(SQLParserUtils.removeComment(sql, null));\n            log.error(\"parse sql error\", e);\n        }\n        return list;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/ssh/MyUserInfo.java",
    "content": "package ai.chat2db.spi.ssh;\n\nimport com.jcraft.jsch.UserInfo;\n\npublic class MyUserInfo implements UserInfo {\n\n    private String passphrase;\n\n    public MyUserInfo(String passphrase) {\n        this.passphrase = passphrase;\n    }\n    @Override\n    public String getPassphrase() {\n        return passphrase;\n    }\n\n    @Override\n    public String getPassword() {\n        return null;\n    }\n\n    @Override\n    public boolean promptPassword(String s) {\n        return true;\n    }\n\n    @Override\n    public boolean promptPassphrase(String s) {\n        return true;\n    }\n\n    @Override\n    public boolean promptYesNo(String s) {\n        return true;\n    }\n\n    @Override\n    public void showMessage(String s) {\n        System.out.println(s);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/ssh/SSHManager.java",
    "content": "\npackage ai.chat2db.spi.ssh;\n\nimport java.security.Security;\n\nimport ai.chat2db.server.tools.common.exception.ConnectionException;\nimport ai.chat2db.spi.model.SSHInfo;\nimport cn.hutool.core.net.NetUtil;\nimport cn.hutool.extra.ssh.JschUtil;\nimport com.jcraft.jsch.JSch;\nimport com.jcraft.jsch.Session;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\n\n/**\n * @author jipengfei\n * @version : SSHSessionManager.java\n */\n@Slf4j\npublic class SSHManager {\n\n    static {\n        try {\n            Security.insertProviderAt(new BouncyCastleProvider(), 1);\n            JSch.setConfig(\"kex\", JSch.getConfig(\"kex\") + \",diffie-hellman-group1-sha1\");\n            JSch.setConfig(\"server_host_key\", JSch.getConfig(\"server_host_key\") + \",ssh-rsa,ssh-dss\");\n        }catch (Exception e){\n            log.error(\"SSHManager init error\",e);\n        }\n    }\n\n    public static Session getSSHSession(SSHInfo ssh) {\n        Session session = null;\n        try {\n            if (StringUtils.isNotBlank(ssh.getKeyFile())) {\n                byte[] passphrase = StringUtils.isNotBlank(ssh.getPassphrase()) ? StringUtils.getBytes(\n                    ssh.getPassphrase(),\n                    \"UTF-8\") : null;\n                session = JschUtil.getSession(ssh.getHostName(), Integer.parseInt(ssh.getPort()), ssh.getUserName(),\n                    ssh.getKeyFile(), passphrase);\n            } else if (StringUtils.isNotBlank(ssh.getUserName())) {\n                session = JschUtil.getSession(ssh.getHostName(), Integer.parseInt(ssh.getPort()), ssh.getUserName(),\n                    ssh.getPassword());\n            }\n\n        } catch (Exception e) {\n            log.info(\"getSSHSession error,sshinfo:{}\",ssh.toString(), e);\n            throw new ConnectionException(\"connection.ssh.error\", null, e);\n        }\n        if (session != null && StringUtils.isNotBlank(ssh.getRHost()) && StringUtils.isNotBlank(ssh.getRPort())) {\n            try {\n                String[] portForwardingL = session.getPortForwardingL();\n                if (portForwardingL != null && portForwardingL.length > 0) {\n                    return session;\n                }\n                int localPort = !StringUtils.isBlank(ssh.getLocalPort()) ? Integer.parseInt(ssh.getLocalPort())\n                    : NetUtil.getUsableLocalPort();\n                ssh.setLocalPort(String.valueOf(localPort));\n                session.setPortForwardingL(localPort, ssh.getRHost(),\n                    Integer.parseInt(ssh.getRPort()));\n            } catch (Exception e) {\n                log.info(\"getSSHSession setPortForwardingL error,sshinfo:{}\",ssh.toString(), e);\n                if (session != null && session.isConnected()) {\n                    session.disconnect();\n                }\n                throw new ConnectionException(\"connection.ssh.error\", null, e);\n            }\n        }\n        return session;\n    }\n\n    public static void close() {\n        JschUtil.closeAll();\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ExceptionUtils.java",
    "content": "package ai.chat2db.spi.util;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * exception utils\n */\n@Slf4j\npublic class ExceptionUtils {\n\n    /**\n     * print stack trace\n     *\n     * @param throwable\n     * @return\n     */\n    public static String getErrorInfoFromException(Throwable throwable) {\n        try (StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter)) {\n            throwable.printStackTrace(printWriter);\n            return stringWriter.toString();\n        } catch (Exception e) {\n            log.error(\"ErrorInfoFromException\", e);\n            return \"ErrorInfoFromException\";\n        }\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/FileUtils.java",
    "content": "package ai.chat2db.spi.util;\n\nimport ai.chat2db.spi.config.DBConfig;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\nimport java.io.IOException;\n\npublic class FileUtils {\n\n    public static <T> T readJsonValue(Class<?> loaderClass, String path, Class<T> clazz) {\n        ObjectMapper mapper = new ObjectMapper();\n        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n        T value = null;\n        try {\n            value = mapper.readValue(loaderClass.getResourceAsStream(path), clazz);\n            // Use data in obj\n        } catch (IOException e) {\n            return null;\n        }\n        return value;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/Holder.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\npublic class Holder<T> {\n    private T value;\n\n    public Holder() {}\n\n    public Holder(T value) {\n        this.value = value;\n    }\n\n    public T getValue() {\n        return value;\n    }\n\n    public void setValue(T value) {\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/JdbcJarUtils.java",
    "content": "\npackage ai.chat2db.spi.util;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.concurrent.Executors;\n\nimport okhttp3.Call;\nimport okhttp3.Callback;\nimport okhttp3.Dispatcher;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\n/**\n * @author jipengfei\n * @version : JdbcJarUtils.java\n */\npublic class JdbcJarUtils {\n\n    private static final OkHttpClient async_client = new OkHttpClient.Builder()\n        .dispatcher(new Dispatcher(Executors.newFixedThreadPool(20))) // 设定线程池大小\n        .build();\n\n    private static final OkHttpClient client = new OkHttpClient();\n\n    public static final String PATH = System.getProperty(\"user.home\") + File.separator + \".chat2db\" + File.separator\n        + \"jdbc-lib\" + File.separator;\n\n    static {\n        File file = new File(PATH);\n        if (!file.exists()) {\n            file.mkdirs();\n        }\n    }\n\n    public static void asyncDownload(List<String> urls) throws Exception {\n        for (String url : urls) {\n            String outputPath = PATH + url.substring(url.lastIndexOf(\"/\") + 1);\n            File file = new File(outputPath);\n            if (file.exists()) {\n                continue;\n            }\n            asyncDownload(url);\n        }\n    }\n\n    public static void asyncDownload(String url) throws Exception {\n        String outputPath = PATH + url.substring(url.lastIndexOf(\"/\") + 1);\n        File file = new File(outputPath);\n        if (file.exists()) {\n            file.delete();\n        }\n        Request request = new Request.Builder()\n            .url(url)\n            .build();\n        async_client.newCall(request).enqueue(new Callback() {\n            @Override\n            public void onFailure(Call call, IOException e) {\n            }\n\n            @Override\n            public void onResponse(Call call, Response response) throws IOException {\n                if (!response.isSuccessful()) {\n                    throw new IOException(\"Unexpected code \" + response);\n                }\n                try (InputStream is = response.body().byteStream();\n                     FileOutputStream fos = new FileOutputStream(outputPath)) {\n                    byte[] buffer = new byte[2048];\n                    int length;\n                    while ((length = is.read(buffer)) != -1) {\n                        fos.write(buffer, 0, length);\n                    }\n                    fos.flush();\n                }\n            }\n        });\n    }\n\n    public static void download(String url) throws IOException {\n        String outputPath = PATH + url.substring(url.lastIndexOf(\"/\") + 1);\n        File file = new File(outputPath);\n        if (file.exists()) {\n            file.delete();\n        }\n        Request request = new Request.Builder()\n            .url(url)\n            .build();\n        try (Response response = client.newCall(request).execute()) {\n            if (!response.isSuccessful()) {\n                throw new IOException(\"Unexpected code \" + response);\n            }\n            try (InputStream is = response.body().byteStream();\n                 FileOutputStream fos = new FileOutputStream(outputPath)) {\n\n                byte[] buffer = new byte[2048];\n                int length;\n                while ((length = is.read(buffer)) != -1) {\n                    fos.write(buffer, 0, length);\n                }\n                fos.flush();\n            }\n        }\n    }\n\n    public static String getNewFullPath(String jarPath) {\n        String path = PATH + jarPath;\n        File file = new File(path);\n        if (file.exists()) {\n            file.delete();\n        }\n        return getFullPath(jarPath);\n    }\n\n    public static String getFullPath(String jarPath) {\n        String path = PATH + jarPath;\n        File file = new File(path);\n        if (!file.exists()) {\n            String url = getDownloadUrl(jarPath);\n            try {\n                download(url);\n            } catch (IOException e) {\n                try {\n                    download(url);\n                } catch (IOException ex) {\n                    throw new RuntimeException(ex);\n                }\n            }\n        }\n        return path;\n    }\n\n    public static final String DOWNLOAD_URL_HOST = \"https://cdn.chat2db-ai.com/lib/\";\n    private static String getDownloadUrl(String jarPath) {\n       return   DOWNLOAD_URL_HOST+jarPath;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/JdbcUtils.java",
    "content": "package ai.chat2db.spi.util;\n\nimport java.sql.*;\nimport java.text.Collator;\nimport java.util.*;\n\nimport ai.chat2db.spi.model.KeyValue;\nimport com.alibaba.druid.DbType;\n\nimport ai.chat2db.spi.config.DriverConfig;\nimport ai.chat2db.spi.enums.DataTypeEnum;\nimport ai.chat2db.spi.model.DataSourceConnect;\nimport ai.chat2db.spi.model.SSHInfo;\nimport ai.chat2db.spi.sql.IDriverManager;\nimport ai.chat2db.spi.ssh.SSHManager;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.jcraft.jsch.Session;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.lang.Nullable;\n\n/**\n * jdbc tool class\n *\n * @author Jiaju Zhuang\n */\n@Slf4j\npublic class JdbcUtils {\n\n    private static final long MAX_RESULT_SIZE = 256 * 1024;\n\n    /**\n     * Get the database type of Druid\n     *\n     * @param dbType\n     * @return\n     */\n    public static DbType parse2DruidDbType(String dbType) {\n        if (dbType == null) {\n            return null;\n        }\n        if(\"SUNDB\".equalsIgnoreCase(dbType)){\n            return DbType.oracle;\n        }\n        try {\n            return DbType.valueOf(dbType.toLowerCase());\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    /**\n     * Parse field type\n     *\n     * @param typeName\n     * @param type\n     * @return\n     */\n    public static DataTypeEnum resolveDataType(String typeName, int type) {\n        switch (getTypeByTypeName(typeName, type)) {\n            case Types.BOOLEAN:\n                return DataTypeEnum.BOOLEAN;\n            case Types.CHAR:\n            case Types.VARCHAR:\n            case Types.NVARCHAR:\n            case Types.LONGVARCHAR:\n            case Types.LONGNVARCHAR:\n                return DataTypeEnum.STRING;\n            case Types.BIGINT:\n            case Types.DECIMAL:\n            case Types.DOUBLE:\n            case Types.FLOAT:\n            case Types.INTEGER:\n            case Types.NUMERIC:\n            case Types.REAL:\n            case Types.SMALLINT:\n                return DataTypeEnum.NUMERIC;\n            case Types.BIT:\n            case Types.TINYINT:\n                if (typeName.toLowerCase().contains(\"bool\")) {\n                    // Declared as numeric but actually it's a boolean\n                    return DataTypeEnum.BOOLEAN;\n                }\n                return DataTypeEnum.NUMERIC;\n            case Types.DATE:\n            case Types.TIME:\n            case Types.TIME_WITH_TIMEZONE:\n            case Types.TIMESTAMP:\n            case Types.TIMESTAMP_WITH_TIMEZONE:\n                return DataTypeEnum.DATETIME;\n            case Types.BINARY:\n            case Types.VARBINARY:\n            case Types.LONGVARBINARY:\n                return DataTypeEnum.BINARY;\n            case Types.BLOB:\n            case Types.CLOB:\n            case Types.NCLOB:\n            case Types.SQLXML:\n                return DataTypeEnum.CONTENT;\n            case Types.STRUCT:\n                return DataTypeEnum.STRUCT;\n            case Types.ARRAY:\n                return DataTypeEnum.ARRAY;\n            case Types.ROWID:\n                return DataTypeEnum.ROWID;\n            case Types.REF:\n                return DataTypeEnum.REFERENCE;\n            case Types.OTHER:\n                return DataTypeEnum.OBJECT;\n            default:\n                return DataTypeEnum.UNKNOWN;\n        }\n    }\n\n    private static int getTypeByTypeName(String typeName, int type) {\n        // [JDBC: SQLite driver uses VARCHAR value type for all LOBs]\n        if (type == Types.OTHER || type == Types.VARCHAR) {\n            if (\"BLOB\".equalsIgnoreCase(typeName)) {\n                return Types.BLOB;\n            } else if (\"CLOB\".equalsIgnoreCase(typeName)) {\n                return Types.CLOB;\n            } else if (\"NCLOB\".equalsIgnoreCase(typeName)) {\n                return Types.NCLOB;\n            }\n        } else if (type == Types.BIT) {\n            // Workaround for MySQL (and maybe others) when TINYINT(1) == BOOLEAN\n            if (\"TINYINT\".equalsIgnoreCase(typeName)) {\n                return Types.TINYINT;\n            }\n        }\n        return type;\n    }\n\n    /**\n     * Test database connection\n     *\n     * @param url database connection\n     * @param userName username\n     * @param password password\n     * @param dbType database type\n     * @return\n     */\n    public static DataSourceConnect testConnect(String url, String host, String port,\n                                                String userName, String password, String dbType,\n                                                DriverConfig driverConfig, SSHInfo ssh, Map<String, Object> properties) {\n        DataSourceConnect dataSourceConnect = DataSourceConnect.builder()\n                .success(Boolean.TRUE)\n                .build();\n        Session session = null;\n        Connection connection = null;\n        // Load driver\n        try {\n            if (ssh.isUse()) {\n                ssh.setRHost(host);\n                ssh.setRPort(port);\n                session = SSHManager.getSSHSession(ssh);\n                url = url.replace(host, \"127.0.0.1\").replace(port, ssh.getLocalPort());\n            }\n            // Create connection\n            connection = IDriverManager.getConnection(url, userName, password,\n                    driverConfig, properties);\n        } catch (Exception e) {\n            log.error(\"connection fail:\", e);\n            dataSourceConnect.setSuccess(Boolean.FALSE);\n            // Get the last exception information to the front end\n            Throwable t = e;\n            while (t.getCause() != null) {\n                t = t.getCause();\n            }\n            dataSourceConnect.setMessage(t.getMessage());\n            dataSourceConnect.setErrorDetail(ExceptionUtils.getErrorInfoFromException(t));\n            return dataSourceConnect;\n        } finally {\n            if (connection != null) {\n                try {\n                    connection.close();\n                } catch (SQLException e) {\n                    // ignore\n                }\n            }\n            if (session != null) {\n                try {\n                    if (StringUtils.isNotBlank(ssh.getLocalPort())) {\n                        session.delPortForwardingL(Integer.parseInt(ssh.getLocalPort()));\n                    }\n                    session.disconnect();\n                } catch (Exception e) {\n\n                }\n            }\n        }\n        dataSourceConnect.setDescription(\"成功\");\n        return dataSourceConnect;\n    }\n\n    public static void closeResultSet(@Nullable ResultSet rs) {\n        if (rs != null) {\n            try {\n                rs.close();\n            } catch (SQLException var2) {\n                log.trace(\"Could not close JDBC ResultSet\", var2);\n            } catch (Throwable var3) {\n                log.trace(\"Unexpected exception on closing JDBC ResultSet\", var3);\n            }\n        }\n\n    }\n\n    public static void setDriverDefaultProperty(DriverConfig driverConfig) {\n        if(driverConfig == null){\n            return;\n        }\n        List<KeyValue> defaultKeyValues = driverConfig.getExtendInfo();\n        Map<String, KeyValue> valueMap = Maps.newHashMap();\n        if (!CollectionUtils.isEmpty(defaultKeyValues)) {\n            for (KeyValue keyValue : defaultKeyValues) {\n                if (keyValue == null || StringUtils.isBlank(keyValue.getKey())) {\n                    continue;\n                }\n                valueMap.put(keyValue.getKey(), keyValue);\n            }\n        }\n        try {\n            DriverPropertyInfo[] propertyInfos = IDriverManager.getProperty(driverConfig);\n            if (propertyInfos == null) {\n                return;\n            }\n            for (int i = 0; i < propertyInfos.length; i++) {\n                DriverPropertyInfo propertyInfo = propertyInfos[i];\n                if (propertyInfo == null) {\n                    continue;\n                }\n                KeyValue keyValue = valueMap.get(propertyInfo.name);\n                if (keyValue != null) {\n                    String[] choices = propertyInfo.choices;\n                    if (CollectionUtils.isEmpty(keyValue.getChoices()) && choices != null && choices.length > 0) {\n                        keyValue.setChoices(Lists.newArrayList(choices));\n                    }\n                } else {\n                    keyValue = new KeyValue();\n                    keyValue.setKey(propertyInfo.name);\n                    keyValue.setValue(propertyInfo.value);\n                    keyValue.setRequired(propertyInfo.required);\n                    String[] choices = propertyInfo.choices;\n                    if (choices != null && choices.length > 0) {\n                        keyValue.setChoices(Lists.newArrayList(choices));\n                    }\n                    valueMap.put(keyValue.getKey(), keyValue);\n                }\n            }\n            if (!valueMap.isEmpty()) {\n                Comparator comparator = Collator.getInstance(Locale.ENGLISH);\n                List<KeyValue> result = new ArrayList<>(valueMap.values());\n                Collections.sort(result, (o1, o2) -> comparator.compare(o1.getKey(), o2.getKey()));\n                driverConfig.setExtendInfo(result);\n            }\n        } catch (SQLException e) {\n            log.error(\"get property error:\", e);\n        }\n    }\n\n    public static void removePropertySameAsDefault(DriverConfig driverConfig) {\n        if(driverConfig == null){\n            return;\n        }\n        List<KeyValue> customValue = driverConfig.getExtendInfo();\n        if (CollectionUtils.isEmpty(customValue)) {\n            return ;\n        }\n        Map<String, String> map = Maps.newHashMap();\n        List<KeyValue> result = new ArrayList<>();\n        try {\n            DriverPropertyInfo[] propertyInfos = IDriverManager.getProperty(driverConfig);\n            if (propertyInfos == null) {\n                return ;\n            }\n            for (int i = 0; i < propertyInfos.length; i++) {\n                DriverPropertyInfo propertyInfo = propertyInfos[i];\n                if (propertyInfo == null) {\n                    continue;\n                }\n                map.put(propertyInfo.name, propertyInfo.value);\n            }\n            for (KeyValue keyValue : customValue) {\n                if (keyValue == null || StringUtils.isBlank(keyValue.getKey())) {\n                    continue;\n                }\n                String value = map.get(keyValue.getKey());\n                if (!StringUtils.equals(value, keyValue.getValue())) {\n                    result.add(keyValue);\n                }\n            }\n            Comparator comparator = Collator.getInstance(Locale.ENGLISH);\n            Collections.sort(result, (o1, o2) -> comparator.compare(o1.getKey(), o2.getKey()));\n            driverConfig.setExtendInfo(result);\n        } catch (SQLException e) {\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/LexerFactories.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport com.oceanbase.tools.sqlparser.oboracle.PLLexer;\nimport com.oceanbase.tools.sqlparser.oracle.PlSqlLexer;\nimport org.antlr.v4.runtime.Lexer;\n\npublic class LexerFactories {\n    private static final LexerFactory OB_ORACLE_LEXER_FACTORY = new OBOraclePLLexerFactory();\n    private static final LexerFactory ORACLE_LEXER_FACTORY = new OracleLexerFactory();\n\n    public static LexerFactory of(Class<? extends Lexer> lexerType) {\n        if (PLLexer.class == lexerType) {\n            return OB_ORACLE_LEXER_FACTORY;\n        }\n        if (PlSqlLexer.class == lexerType) {\n            return ORACLE_LEXER_FACTORY;\n        }\n        throw new RuntimeException(\"LexerType not supported, lexerType=\" + lexerType.getName());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/LexerFactory.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport org.antlr.v4.runtime.CharStream;\nimport org.antlr.v4.runtime.Lexer;\n\ninterface LexerFactory {\n    Lexer create(CharStream input);\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/LexerTokenDefinition.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\n/**\n * SQL 语法 Token 定义。<br>\n * 如果某个 Token 不支持，对应方法返回 1 {@link org.antlr.v4.runtime.Token#MIN_USER_TOKEN_TYPE}\n */\ninterface LexerTokenDefinition {\n    /**\n     * 除号\n     */\n    int DIV();\n\n    /**\n     * 空白字符，包括空格、制表符、换行符等\n     */\n    int SPACES();\n\n    /**\n     * OB 语法文件不区分单行注释和多行注释，统一为 ANTLR_SKIP\n     */\n    int ANTLR_SKIP();\n\n    /**\n     * 单行注释\n     */\n    int SINGLE_LINE_COMMENT();\n\n    /**\n     * 多行注释\n     */\n    int MULTI_LINE_COMMENT();\n\n    /**\n     * DECLARE 关键字\n     */\n    int DECLARE();\n\n    /**\n     * BEGIN 关键字\n     */\n    int BEGIN();\n\n    /**\n     * END 关键字\n     */\n    int END();\n\n    /**\n     * CREATE 关键字\n     */\n    int CREATE();\n\n    /**\n     * OR 关键字\n     */\n    int OR();\n\n    /**\n     * REPLACE 关键字\n     */\n    int REPLACE();\n\n    /**\n     * EDITIONABLE 关键字\n     */\n    int EDITIONABLE();\n\n    /**\n     * NONEDITIONABLE 关键字\n     */\n    int NONEDITIONABLE();\n\n    /**\n     * PROCEDURE 关键字\n     */\n    int PROCEDURE();\n\n    /**\n     * FUNCTION 关键字\n     */\n    int FUNCTION();\n\n    /**\n     * PACKAGE 关键字\n     */\n    int PACKAGE();\n\n    /**\n     * TYPE 关键字\n     */\n    int TYPE();\n\n    /**\n     * TRIGGER 关键字\n     */\n    int TRIGGER();\n\n    /**\n     * BODY 关键字\n     */\n    int BODY();\n\n    /**\n     * IDENT, may {@link #REGULAR_ID} OR {@link #DELIMITED_ID}\n     */\n    int IDENT();\n\n    int REGULAR_ID();\n\n    int DELIMITED_ID();\n\n    int FOR();\n\n    /**\n     * LOOP 关键字\n     */\n    int LOOP();\n\n    /**\n     * IF 关键字\n     */\n    int IF();\n\n    /**\n     * CASE 关键字\n     */\n    int CASE();\n\n    /**\n     * LANGUAGE 关键字\n     */\n    int LANGUAGE();\n\n    /**\n     * EXTERNAL 关键字\n     */\n    int EXTERNAL();\n\n    /**\n     * IS 关键字\n     */\n    int IS();\n\n    /**\n     * AS 关键字\n     */\n    int AS();\n\n    /**\n     * MEMBER 关键字\n     */\n    int MEMBER();\n\n    /**\n     * STATIC 关键字\n     */\n    int STATIC();\n\n    int SEMICOLON();\n\n    int ELSE();\n\n    int THEN();\n\n    int RIGHTBRACKET();\n\n    int LEFTBRACKET();\n\n    int GREATER_THAN_OP();\n\n    int WHILE();\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/LexerTokenDefinitions.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport com.oceanbase.tools.sqlparser.oboracle.PLLexer;\nimport com.oceanbase.tools.sqlparser.oracle.PlSqlLexer;\nimport org.antlr.v4.runtime.Lexer;\n\nclass LexerTokenDefinitions {\n    private static final LexerTokenDefinition OB_PL_LEXER_DEFINITION = new OBOraclePLLexerDefinition();\n    private static final LexerTokenDefinition ORACLE_LEXER_DEFINITION = new OracleLexerDefinition();\n\n    public static LexerTokenDefinition of(Class<? extends Lexer> lexerType) {\n        if (PLLexer.class == lexerType) {\n            return OB_PL_LEXER_DEFINITION;\n        }\n        if (PlSqlLexer.class == lexerType) {\n            return ORACLE_LEXER_DEFINITION;\n        }\n        throw new RuntimeException(\"LexerType not supported, lexerType=\" + lexerType.getName());\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/OBOraclePLLexerDefinition.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport com.oceanbase.tools.sqlparser.oboracle.PLLexer;\nimport org.antlr.v4.runtime.Token;\n\nclass OBOraclePLLexerDefinition implements LexerTokenDefinition {\n\n    @Override\n    public int DIV() {\n        return PLLexer.Div;\n    }\n\n    @Override\n    public int SPACES() {\n        return PLLexer.Blank;\n    }\n\n    @Override\n    public int ANTLR_SKIP() {\n        return PLLexer.ANTLR_SKIP;\n    }\n\n    @Override\n    public int SINGLE_LINE_COMMENT() {\n        return Token.MIN_USER_TOKEN_TYPE;\n    }\n\n    @Override\n    public int MULTI_LINE_COMMENT() {\n        return Token.MIN_USER_TOKEN_TYPE;\n    }\n\n    @Override\n    public int DECLARE() {\n        return PLLexer.DECLARE;\n    }\n\n    @Override\n    public int BEGIN() {\n        return PLLexer.BEGIN_KEY;\n    }\n\n    @Override\n    public int END() {\n        return PLLexer.END_KEY;\n    }\n\n    @Override\n    public int CREATE() {\n        return PLLexer.CREATE;\n    }\n\n    @Override\n    public int OR() {\n        return PLLexer.OR;\n    }\n\n    @Override\n    public int REPLACE() {\n        return PLLexer.REPLACE;\n    }\n\n    @Override\n    public int EDITIONABLE() {\n        return PLLexer.EDITIONABLE;\n    }\n\n    @Override\n    public int NONEDITIONABLE() {\n        return PLLexer.NONEDITIONABLE;\n    }\n\n    @Override\n    public int PROCEDURE() {\n        return PLLexer.PROCEDURE;\n    }\n\n    @Override\n    public int FUNCTION() {\n        return PLLexer.FUNCTION;\n    }\n\n    @Override\n    public int PACKAGE() {\n        return PLLexer.PACKAGE_P;\n    }\n\n    @Override\n    public int TYPE() {\n        return PLLexer.TYPE;\n    }\n\n    @Override\n    public int TRIGGER() {\n        return PLLexer.TRIGGER;\n    }\n\n    @Override\n    public int BODY() {\n        return PLLexer.BODY;\n    }\n\n    @Override\n    public int IDENT() {\n        return PLLexer.IDENT;\n    }\n\n    @Override\n    public int REGULAR_ID() {\n        return Token.MIN_USER_TOKEN_TYPE;\n    }\n\n    @Override\n    public int DELIMITED_ID() {\n        return Token.MIN_USER_TOKEN_TYPE;\n    }\n\n    @Override\n    public int FOR() {\n        return PLLexer.FOR;\n    }\n\n    @Override\n    public int LOOP() {\n        return PLLexer.LOOP;\n    }\n\n    @Override\n    public int IF() {\n        return PLLexer.IF;\n    }\n\n    @Override\n    public int CASE() {\n        return PLLexer.CASE;\n    }\n\n    @Override\n    public int LANGUAGE() {\n        return PLLexer.LANGUAGE;\n    }\n\n    @Override\n    public int EXTERNAL() {\n        return PLLexer.EXTERNAL;\n    }\n\n    @Override\n    public int IS() {\n        return PLLexer.IS;\n    }\n\n    @Override\n    public int AS() {\n        return PLLexer.AS;\n    }\n\n    @Override\n    public int MEMBER() {\n        return PLLexer.MEMBER;\n    }\n\n    @Override\n    public int STATIC() {\n        return PLLexer.STATIC;\n    }\n\n    @Override\n    public int SEMICOLON() {\n        return PLLexer.DELIMITER;\n    }\n\n    @Override\n    public int ELSE() {\n        return PLLexer.ELSE;\n    }\n\n    @Override\n    public int THEN() {\n        return PLLexer.THEN;\n    }\n\n    @Override\n    public int RIGHTBRACKET() {\n        return PLLexer.RightParen;\n    }\n\n    @Override\n    public int LEFTBRACKET() {\n        return PLLexer.LeftParen;\n    }\n\n    @Override\n    public int GREATER_THAN_OP() {\n        return PLLexer.LABEL_RIGHT;\n    }\n\n    @Override\n    public int WHILE() {\n        return PLLexer.WHILE;\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/OBOraclePLLexerFactory.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport com.oceanbase.tools.sqlparser.oboracle.PLLexer;\nimport org.antlr.v4.runtime.CharStream;\nimport org.antlr.v4.runtime.Lexer;\n\nclass OBOraclePLLexerFactory implements LexerFactory {\n    @Override\n    public Lexer create(CharStream input) {\n        return new PLLexer(input);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/OracleLexerDefinition.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport com.oceanbase.tools.sqlparser.oracle.PlSqlLexer;\nimport org.antlr.v4.runtime.Token;\n\nclass OracleLexerDefinition implements LexerTokenDefinition {\n\n    @Override\n    public int DIV() {\n        return PlSqlLexer.SOLIDUS;\n    }\n\n    @Override\n    public int SPACES() {\n        return PlSqlLexer.SPACES;\n    }\n\n    @Override\n    public int ANTLR_SKIP() {\n        return Token.MIN_USER_TOKEN_TYPE;\n    }\n\n    @Override\n    public int SINGLE_LINE_COMMENT() {\n        return PlSqlLexer.SINGLE_LINE_COMMENT;\n    }\n\n    @Override\n    public int MULTI_LINE_COMMENT() {\n        return PlSqlLexer.MULTI_LINE_COMMENT;\n    }\n\n    @Override\n    public int DECLARE() {\n        return PlSqlLexer.DECLARE;\n    }\n\n    @Override\n    public int BEGIN() {\n        return PlSqlLexer.BEGIN;\n    }\n\n    @Override\n    public int END() {\n        return PlSqlLexer.END;\n    }\n\n    @Override\n    public int CREATE() {\n        return PlSqlLexer.CREATE;\n    }\n\n    @Override\n    public int OR() {\n        return PlSqlLexer.OR;\n    }\n\n    @Override\n    public int REPLACE() {\n        return PlSqlLexer.REPLACE;\n    }\n\n    @Override\n    public int EDITIONABLE() {\n        return PlSqlLexer.EDITIONABLE;\n    }\n\n    @Override\n    public int NONEDITIONABLE() {\n        return PlSqlLexer.NONEDITIONABLE;\n    }\n\n    @Override\n    public int PROCEDURE() {\n        return PlSqlLexer.PROCEDURE;\n    }\n\n    @Override\n    public int FUNCTION() {\n        return PlSqlLexer.FUNCTION;\n    }\n\n    @Override\n    public int PACKAGE() {\n        return PlSqlLexer.PACKAGE;\n    }\n\n    @Override\n    public int TYPE() {\n        return PlSqlLexer.TYPE;\n    }\n\n    @Override\n    public int TRIGGER() {\n        return PlSqlLexer.TRIGGER;\n    }\n\n    @Override\n    public int BODY() {\n        return PlSqlLexer.BODY;\n    }\n\n    @Override\n    public int IDENT() {\n        return Token.MIN_USER_TOKEN_TYPE;\n    }\n\n    @Override\n    public int REGULAR_ID() {\n        return PlSqlLexer.REGULAR_ID;\n    }\n\n    @Override\n    public int DELIMITED_ID() {\n        return PlSqlLexer.DELIMITED_ID;\n    }\n\n    @Override\n    public int FOR() {\n        return PlSqlLexer.FOR;\n    }\n\n    @Override\n    public int LOOP() {\n        return PlSqlLexer.LOOP;\n    }\n\n    @Override\n    public int IF() {\n        return PlSqlLexer.IF;\n    }\n\n    @Override\n    public int CASE() {\n        return PlSqlLexer.CASE;\n    }\n\n    @Override\n    public int LANGUAGE() {\n        return PlSqlLexer.LANGUAGE;\n    }\n\n    @Override\n    public int EXTERNAL() {\n        return PlSqlLexer.EXTERNAL;\n    }\n\n    @Override\n    public int IS() {\n        return PlSqlLexer.IS;\n    }\n\n    @Override\n    public int AS() {\n        return PlSqlLexer.AS;\n    }\n\n    @Override\n    public int MEMBER() {\n        return PlSqlLexer.MEMBER;\n    }\n\n    @Override\n    public int STATIC() {\n        return PlSqlLexer.STATIC;\n    }\n\n    @Override\n    public int SEMICOLON() {\n        return PlSqlLexer.SEMICOLON;\n    }\n\n    @Override\n    public int ELSE() {\n        return PlSqlLexer.ELSE;\n    }\n\n    @Override\n    public int THEN() {\n        return PlSqlLexer.THEN;\n    }\n\n    @Override\n    public int RIGHTBRACKET() {\n        return PlSqlLexer.RIGHT_PAREN;\n    }\n\n    @Override\n    public int LEFTBRACKET() {\n        return PlSqlLexer.LEFT_PAREN;\n    }\n\n    @Override\n    public int GREATER_THAN_OP() {\n        return PlSqlLexer.GREATER_THAN_OP;\n    }\n\n    @Override\n    public int WHILE() {\n        return PlSqlLexer.WHILE;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/OracleLexerFactory.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport com.oceanbase.tools.sqlparser.oracle.PlSqlLexer;\nimport com.oceanbase.tools.sqlparser.util.CaseChangingCharStream;\nimport org.antlr.v4.runtime.CharStream;\nimport org.antlr.v4.runtime.Lexer;\n\npublic class OracleLexerFactory implements LexerFactory {\n    @Override\n    public Lexer create(CharStream input) {\n        CaseChangingCharStream caseChangingCharStream = new CaseChangingCharStream(input, true);\n        return new PlSqlLexer(caseChangingCharStream);\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java",
    "content": "\npackage ai.chat2db.spi.util;\n\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.MapperFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.PropertyNamingStrategies;\nimport com.google.common.collect.Lists;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.InputStream;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.sql.*;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n\n/**\n * @author jipengfei\n * @version : ResultSetUtils.java\n */\n@Slf4j\npublic class ResultSetUtils {\n\n\n    public static List<String> getRsHeader(ResultSet rs) {\n        try {\n            ResultSetMetaData resultSetMetaData = rs.getMetaData();\n            int col = resultSetMetaData.getColumnCount();\n            List<String> headerList = Lists.newArrayListWithExpectedSize(col);\n            for (int i = 1; i <= col; i++) {\n                headerList.add(getColumnName(resultSetMetaData, i));\n            }\n            return headerList;\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     *\n     * @param rs\n     * @param clazz\n     * @return\n     * @param <T>\n     */\n    public static <T> List<T> toObjectList(ResultSet rs, Class<T> clazz) {\n        try {\n            if (rs == null || clazz == null) {\n                return Lists.newArrayList();\n            }\n            List<T> list = Lists.newArrayList();\n            ResultSetMetaData rsMetaData = rs.getMetaData();\n            int col = rsMetaData.getColumnCount();\n            List<String> headerList = getRsHeader(rs);\n            ObjectMapper mapper = new ObjectMapper();\n            mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);\n            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n            mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);\n            while (rs.next()) {\n                Map<String, Object> map = new HashMap<>();\n                for (int i = 1; i <= col; i++) {\n                    map.put(headerList.get(i-1), rs.getObject(i));\n                }\n                T obj = mapper.convertValue(map, clazz);\n\n                list.add(obj);\n            }\n            return list;\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n\n    public static String getColumnName(ResultSetMetaData resultSetMetaData, int column) throws SQLException {\n        String columnLabel = resultSetMetaData.getColumnLabel(column);\n        if (columnLabel != null) {\n            return columnLabel;\n        }\n        return resultSetMetaData.getColumnName(column);\n    }\n\n    public static String getColumnDataTypeName(ResultSetMetaData resultSetMetaData, int columnIndex) {\n        try {\n            return resultSetMetaData.getColumnTypeName(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static int getColumnPrecision(ResultSetMetaData resultSetMetaData, int columnIndex){\n        try {\n            return resultSetMetaData.getPrecision(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static int getColumnScale(ResultSetMetaData resultSetMetaData, int columnIndex){\n        try {\n            return resultSetMetaData.getScale(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static String getString(ResultSet rs, int columnIndex){\n        try {\n            Object obj = rs.getObject(columnIndex);\n            if (obj == null) {\n                return null;\n            }\n            if(obj instanceof String){\n                return (String) obj;\n            }else if (obj instanceof BigDecimal bigDecimal) {\n                return bigDecimal.toPlainString();\n            } else if (obj instanceof Double d) {\n                return BigDecimal.valueOf(d).toPlainString();\n            } else if (obj instanceof Float f) {\n                return BigDecimal.valueOf(f).toPlainString();\n            } else if (obj instanceof Clob) {\n                return largeString(rs, columnIndex);\n            } else if (obj instanceof byte[]) {\n                return largeString(rs, columnIndex);\n            } else if (obj instanceof Blob blob) {\n                return largeStringBlob(blob);\n            } else if (obj instanceof Timestamp || obj instanceof LocalDateTime) {\n                return largeTime(obj);\n            } else if (obj instanceof SQLXML){\n                return ((SQLXML) obj).getString();\n            } else {\n                return obj.toString();\n            }\n        } catch (Exception e) {\n            log.warn(\"Failed to parse number:{},\", columnIndex, e);\n            try {\n                return rs.getString(columnIndex);\n            } catch (SQLException ex) {\n                throw new RuntimeException(ex);\n            }\n        }\n    }\n\n    private static String largeStringBlob(Blob blob) throws SQLException {\n        if (blob == null) {\n            return null;\n        }\n        int length = Math.toIntExact(blob.length());\n        byte[] data = blob.getBytes(1, length);\n        String result = new String(data, StandardCharsets.UTF_8);\n        return result;\n    }\n\n    private static String largeTime(Object obj) throws SQLException {\n        Object timeField = obj; // Assuming a time field of type Object\n\n        LocalDateTime localDateTime;\n\n        if (obj instanceof Timestamp) {\n            // Convert a time field of type Object to a LocalDateTime object\n            localDateTime = ((Timestamp) timeField).toLocalDateTime();\n        } else if(obj instanceof  LocalDateTime){\n            localDateTime = (LocalDateTime) timeField;\n        } else {\n            try {\n                localDateTime = LocalDateTime.parse(timeField.toString(), DateTimeFormatter.ofPattern(\"yyyy-MM-dd'T'HH:mm:ss\"));\n            }catch (Exception e){\n                localDateTime = LocalDateTime.parse(timeField.toString(), DateTimeFormatter.ofPattern(\"yyyy-MM-dd'T'HH:mm\"));\n            }\n        }\n        // Create a DateTimeFormatter instance and specify the output date and time format\n        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\");\n\n        // Format date time\n        String formattedDateTime = dtf.format(localDateTime);\n        return formattedDateTime;\n    }\n\n    private static String largeString(ResultSet rs, int index) throws SQLException {\n        String result = rs.getString(index);\n        if (result == null) {\n            return null;\n\n        }\n        return result;\n    }\n\n    public static InputStream getBinaryStream(ResultSet rs, int columnIndex) {\n        try {\n            return rs.getBinaryStream(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static byte[] getBytes(ResultSet rs, int columnIndex) {\n        try {\n            return rs.getBytes(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static boolean getBoolean(ResultSet rs, int columnIndex) {\n        try {\n            return rs.getBoolean(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static int getInt(ResultSet resultSet, int columnIndex) {\n        try {\n            return resultSet.getInt(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static Date getDate(ResultSet resultSet, int columnIndex) {\n        try {\n            return resultSet.getDate(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static Timestamp getTimestamp(ResultSet resultSet, int columnIndex) {\n        try {\n            return resultSet.getTimestamp(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static Clob getClob(ResultSet resultSet, int columnIndex) {\n        try {\n            return resultSet.getClob(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static Blob getBlob(ResultSet resultSet, int columnIndex) {\n        try {\n            return resultSet.getBlob(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static BigDecimal getBigDecimal(ResultSet resultSet, int columnIndex) {\n        try {\n            return resultSet.getBigDecimal(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static String getStringValue(ResultSet resultSet, int columnIndex) {\n        try {\n            return resultSet.getString(columnIndex);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SortUtils.java",
    "content": "package ai.chat2db.spi.util;\n\nimport ai.chat2db.spi.model.Database;\nimport ai.chat2db.spi.model.Schema;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\npublic class SortUtils {\n\n    public static List<Database> sortDatabase(List<Database> databases, List<String> list, Connection connection) {\n        if (CollectionUtils.isEmpty(databases)) {\n            return databases;\n        }\n        List<Database> databaseList = new ArrayList<>();\n        List<Database> systemDatabases = databases.stream()\n                .filter(database -> list.contains(database.getName())).collect(Collectors.toList());\n        List<Database> userDatabases = databases.stream()\n                .filter(database -> !list.contains(database.getName())).collect(Collectors.toList());\n\n        if (CollectionUtils.isEmpty(userDatabases)) {\n            databaseList = databases;\n        }else if (CollectionUtils.isEmpty(systemDatabases)) {\n            databaseList = userDatabases;\n        }else {\n            databaseList = Stream.concat(userDatabases.stream(), systemDatabases.stream())\n                    .collect(Collectors.toList());\n        }\n        // If the database name contains the name of the current database, the current database is placed in the first place\n\n        String ulr;\n        try {\n            ulr = connection.getMetaData().getURL();\n        } catch (SQLException e) {\n            return databaseList;\n        }\n        // If the database name contains the name of the current database, the current database is placed in the first place\n        int no = -1;\n        for (int i = 0; i < databases.size(); i++) {\n            if (StringUtils.isNotBlank(ulr)\n                    && StringUtils.isNotBlank(databases.get(i).getName())\n                    && ulr.contains(databases.get(i).getName())\n                    && !\"mysql\".equalsIgnoreCase(databases.get(i).getName())) {\n                no = i;\n                break;\n            }\n        }\n        if (no != -1 && no != 0) {\n            Collections.swap(databaseList, no, 0);\n        }\n        return databaseList;\n    }\n\n    public static List<Schema> sortSchema(List<Schema> schemas, List<String> systemSchemas) {\n        if (CollectionUtils.isEmpty(schemas)) {\n            return schemas;\n        }\n        List<Schema> systemSchema = schemas.stream()\n                .filter(schema -> systemSchemas.contains(schema.getName()) || \"APEX_\".startsWith(schema.getName())).collect(Collectors.toList());\n        List<Schema> userSchema = schemas.stream()\n                .filter(schema -> !systemSchemas.contains(schema.getName()) && !\"APEX_\".startsWith(schema.getName())).collect(Collectors.toList());\n\n        if (CollectionUtils.isEmpty(userSchema)) {\n            return schemas;\n        }\n        if (CollectionUtils.isEmpty(systemSchema)) {\n            return userSchema;\n        }\n        return Stream.concat(userSchema.stream(), systemSchema.stream())\n                .collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SplitSqlString.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ai.chat2db.spi.util;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class SplitSqlString {\n    private int offset;\n    private String str;\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SqlSplitProcessor.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport com.alibaba.druid.DbType;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\n/**\n */\npublic class SqlSplitProcessor {\n\n    private static final String DELIMITER_NAME = \"delimiter\";\n    /**\n     * 是否保留格式\n     */\n    private boolean preserveFormat = false;\n    private String delimiter = \";\";\n    @Getter\n    private boolean mlComment = false;\n    private char inString = '\\0';\n    private DbType dialectType;\n    private char escapeString = '\\0';\n    private boolean inNormalSql = false;\n    /**\n     * 是否保留单行注释\n     */\n    @Getter\n    private boolean preserveSingleComments = false;\n    /**\n     * 是否保留多行注释\n     */\n    @Getter\n    private boolean preserveMultiComments = false;\n\n    private static Pattern pattern = Pattern.compile(\"\\\\r\\\\n|\\\\r|\\\\n\");\n\n\n    public SqlSplitProcessor(boolean preserveFormat, String delimiter) {\n        this.delimiter = delimiter;\n        this.preserveFormat = preserveFormat;\n    }\n\n    public SqlSplitProcessor(DbType dialectType,\n                             boolean preserveSingleComments,\n                             boolean preserveMultiComments) {\n        this.preserveFormat = true;\n        this.dialectType = dialectType;\n        this.preserveSingleComments = preserveSingleComments;\n        this.preserveMultiComments = preserveMultiComments;\n    }\n\n    public SqlSplitProcessor(DbType dialectType,\n                             boolean preserveFormat,\n                             boolean preserveSingleComments,\n                             boolean preserveMultiComments) {\n        this.preserveFormat = preserveFormat;\n        this.dialectType = dialectType;\n        this.preserveSingleComments = preserveSingleComments;\n        this.preserveMultiComments = preserveMultiComments;\n    }\n\n    public SqlSplitProcessor(DbType dialectType, String delimiter) {\n        this.preserveFormat = true;\n        this.dialectType = dialectType;\n        this.delimiter = delimiter;\n    }\n\n    public SqlSplitProcessor() {}\n\n    public static SqlStatementIterator iterator(InputStream in, Charset charset, SqlSplitProcessor processor) {\n        return new SqlCommentProcessorIterator(in, charset, processor);\n    }\n\n    public static List<SplitSqlString> removeSqlComments(String originalSql,\n                                                         String delimiter, DbType dbMode, boolean preserveFormat) {\n        SqlSplitProcessor sqlCommentProcessor = new SqlSplitProcessor(preserveFormat, delimiter);\n        StringBuffer buffer = new StringBuffer();\n        List<SplitSqlString> offsetStrings = new ArrayList<>();\n        List<List<OrderChar>> lines = splitLine(originalSql);\n        Holder<Integer> bufferOrder = new Holder<>(0);\n        for (List<OrderChar> item : lines) {\n            if (Objects.nonNull(dbMode) && DbType.mysql.equals(dbMode)) {\n                sqlCommentProcessor.addLineMysql(offsetStrings, buffer, bufferOrder, item);\n            } else {\n                sqlCommentProcessor.addLineOracle(offsetStrings, buffer, bufferOrder, item);\n            }\n        }\n\n        String bufferStr = buffer.toString();\n        if (bufferStr.trim().length() != 0) {\n            while (true) {\n                if (bufferStr.endsWith(\"\\n\")) {\n                    /**\n                     * remove all <code>\\n</code> from sqls\n                     */\n                    bufferStr = bufferStr.substring(0, bufferStr.length() - 1);\n                } else {\n                    break;\n                }\n            }\n            if (offsetStrings.size() == 0) {\n                offsetStrings.add(new SplitSqlString(0, bufferStr));\n            } else {\n                offsetStrings.add(new SplitSqlString(\n                        offsetStrings.get(offsetStrings.size() - 1).getOffset()\n                                + offsetStrings.get(offsetStrings.size() - 1).getStr().length(),\n                        bufferStr));\n            }\n        }\n        return offsetStrings;\n    }\n\n    public synchronized List<SplitSqlString> split(StringBuffer buffer, String sqlScript) {\n        if (StringUtils.isBlank(sqlScript)) {\n            return new ArrayList<>();\n        }\n        try {\n            List<SplitSqlString> offsetStrings = new ArrayList<>();\n\n            List<List<OrderChar>> lines = splitLine(sqlScript);\n            Holder<Integer> bufferOrder = new Holder<>(0);\n            int i = 0;\n            for (List<OrderChar> item : lines) {\n                if (Objects.nonNull(this.dialectType) && DbType.mysql.equals(this.dialectType)) {\n                    addLineMysql(offsetStrings, buffer, bufferOrder, item);\n                } else if (Objects.nonNull(this.dialectType) && DbType.mariadb.equals(this.dialectType)) {\n                    addLineMysql(offsetStrings, buffer, bufferOrder, item);\n                } else if (Objects.nonNull(this.dialectType) && DbType.oracle.equals(this.dialectType)) {\n                    addLineOracle(offsetStrings, buffer, bufferOrder, item);\n                } else if (Objects.nonNull(this.dialectType) && DbType.oceanbase.equals(this.dialectType)) {\n                    addLineMysql(offsetStrings, buffer, bufferOrder, item);\n                } else {\n                    throw new IllegalArgumentException(\"dialect type is illegal\");\n                }\n                i++;\n            }\n            return offsetStrings;\n        } finally {\n            mlComment = false;\n            inString = '\\0';\n            inNormalSql = false;\n        }\n    }\n\n    private synchronized void addLineMysql(List<SplitSqlString> sqls, StringBuffer buffer, Holder<Integer> bufferOrder,\n                                           List<OrderChar> line) {\n        int pos, out;\n        boolean needSpace = false;\n        // 标识量，用于标识当前是否处于HINT，CONDITIONAL中\n        SSC ssComment = SSC.NONE;\n        boolean isSameLine = false;\n        int lineLength = line.size();\n        OrderChar[] lines = line.toArray(new OrderChar[lineLength + 1]);\n        if ((lines.length == 0 || lines[0] == null || lines[0].getCh() == 0) && buffer.length() == 0) {\n            return;\n        }\n        lines[lineLength] = new OrderChar((char) 0, lineLength);\n        for (pos = out = 0; pos < lineLength; pos++) {\n            OrderChar inOrderChar = lines[pos];\n            char inChar = inOrderChar.getCh();\n            // 去掉每一行SQL语句最开始的空格\n            if (inChar == ' ' && out == 0 && buffer.length() == 0 && !preserveFormat) {\n                continue;\n            }\n            int delimiterBegin = 0;\n            if (preserveFormat) {\n                for (; delimiterBegin < out\n                        && (lines[delimiterBegin].getCh() == ' '\n                                || lines[delimiterBegin].getCh() == '\\t'); delimiterBegin++) {\n                }\n            }\n            if (equalsIgnoreCase((DELIMITER_NAME + \" \").toCharArray(), lines, delimiterBegin, (out - delimiterBegin))) {\n                // 检测到\"delimiter \"字符串，且不在多行注释以及多行字符串中，说明有设定分隔符的语句\n                StringBuilder newDelimiter = new StringBuilder();\n                for (; pos < lineLength; pos++) {\n                    char tempChar = lines[pos].getCh();\n                    if (tempChar != ' ') {\n                        newDelimiter.append(tempChar);\n                    } else if (newDelimiter.length() != 0) {\n                        break;\n                    }\n                }\n                out = 0;\n                this.delimiter = newDelimiter.toString();\n                continue;\n            }\n            // 扫描到转义字符，可能出现指令\n            if ((!mlComment && inChar == '\\\\')) {\n                inOrderChar = lines[++pos];\n                inChar = inOrderChar.getCh();\n                if (inChar == 0) {\n                    break;\n                }\n                if (inString != '\\0' || inChar == 'N') {\n                    lines[out++] = OrderChar.newOrderChar(lines[pos - 1]);\n                    if (inChar == '`' && inString == inChar) {\n                        pos--;\n                    } else {\n                        lines[out++] = OrderChar.newOrderChar(lines[pos]);\n                    }\n                    continue;\n                }\n                // 非mysql model或没有检索到正确的命令，直接将转义符号及转义字符放入缓冲\n                lines[out++] = OrderChar.newOrderChar(lines[pos - 1]);\n                lines[out++] = OrderChar.newOrderChar(lines[pos]);\n            } else if (!mlComment && inString == '\\0' && ssComment != SSC.HINT\n                    && isPrefix(lines, pos, delimiter)) {\n                // 不是多行注释，未在字符串中，不是hint且以delimiter开头，通常是扫描到了sql的末尾\n                pos += delimiter.length();\n                if (out != 0) {\n                    if (buffer.length() == 0) {\n                        bufferOrder.setValue(lines[0].getOrder());\n                    }\n                    append(buffer, lines, 0, out);\n                    out = 0;\n                }\n                // buffer.append(\";\").append('\\n');\n                sqls.add(new SplitSqlString(bufferOrder.getValue(), buffer.toString()));\n                bufferOrder.setValue(bufferOrder.getValue() + buffer.length());\n                pos--;\n                buffer.setLength(0);\n                isSameLine = true;\n                inNormalSql = false;\n            } else if (!mlComment\n                    && (inString == '\\0' && (inChar == '#' || (inChar == '-' && lines[pos + 1].getCh() == '-'\n                            && ((lines[pos + 2].getCh() == ' ' || lines[pos + 2].getCh() == '\\0')))))) {\n                // 处于单行注释中\n                if (buffer.length() == 0) {\n                    bufferOrder.setValue(lines[0].getOrder());\n                }\n                append(buffer, lines, 0, out);\n                out = 0;\n                if (preserveSingleComments) {\n                    // 如果保留单行注释则需要将注释完整地拷贝到缓冲中不能丢弃\n                    for (; pos < lineLength; pos++) {\n                        lines[out++] = OrderChar.newOrderChar(lines[pos]);\n                    }\n                    if (isOnlyWhiteSpace(buffer)) {\n                        // 缓冲中全部是空格，或者缓冲为空说明注释要么处于第一行要么处于个已经完结的sql语句之后\n                        if (sqls.size() != 0) {\n                            if (buffer.length() == 0) {\n                                bufferOrder.setValue(lines[0].getOrder());\n                            }\n                            // 说明注释处于一个已经完结的sql之后，且该sql已经被加入到sql集合中，此处的注释需要追加到最后一句sql中\n                            append(buffer, lines, 0, out);\n                            int lastIndex = sqls.size() - 1;\n                            String lastSql = sqls.get(lastIndex).getStr();\n                            if (!isSameLine) {\n                                lastSql += '\\n';\n                            }\n                            lastSql += buffer + \"\\n\";\n                            sqls.set(lastIndex, new SplitSqlString(sqls.get(lastIndex).getOffset(), lastSql));\n                            buffer.setLength(0);\n                        } else {\n                            lines[out++].setCh('\\n');\n                            if (buffer.length() == 0) {\n                                bufferOrder.setValue(lines[0].getOrder());\n                            }\n                            append(buffer, lines, 0, out - 1);\n                        }\n                    } else {\n                        lines[out++].setCh('\\n');\n                        if (buffer.length() == 0) {\n                            bufferOrder.setValue(lines[0].getOrder());\n                        }\n                        append(buffer, lines, 0, out - 1);\n                    }\n                    out = 0;\n                }\n                break;\n            } else if (inString == '\\0' && (inChar == '/' && lines[pos + 1].getCh() == '*')\n            // 此处注意，Oracle模式下没有Conditional，故这里要做规避。Mysql模式下的Conditional在Oracle模式在要识别为注释去掉\n                    && lines[pos + 2].getCh() != '!'\n                    && lines[pos + 2].getCh() != '+' && ssComment != SSC.HINT) {\n                // 处于多行注释中，注意规避了HINT和CONDITIONAL，Oracle模式下没有conditional\n                if (preserveMultiComments) {\n                    lines[out++].setCh('/');\n                    lines[out++].setCh('*');\n                }\n                pos++;\n                mlComment = true;\n            } else if (mlComment && ssComment == SSC.NONE && inChar == '*' && lines[pos + 1].getCh() == '/') {\n                // 多行注释结束\n                pos++;\n                mlComment = false;\n                if (buffer.length() == 0) {\n                    bufferOrder.setValue(lines[0].getOrder());\n                }\n                append(buffer, lines, 0, out);\n                out = 0;\n                if (preserveMultiComments) {\n                    lines[out++].setCh('*');\n                    lines[out++].setCh('/');\n                    if (buffer.length() == 0) {\n                        bufferOrder.setValue(lines[0].getOrder());\n                    }\n                    append(buffer, lines, 0, out);\n                    out = 0;\n                    if (sqls.size() != 0 && !inNormalSql) {\n                        int lastIndex = sqls.size() - 1;\n                        String lastSql = sqls.get(lastIndex).getStr() + buffer;\n                        sqls.set(lastIndex, new SplitSqlString(sqls.get(lastIndex).getOffset(), lastSql));\n                        buffer.setLength(0);\n                    }\n                }\n                needSpace = true;\n            } else {\n                if (inString == '\\0' && inChar == '/' && lines[pos + 1].getCh() == '*') {\n                    if (lines[pos + 2].getCh() == '!') {\n                        // 处于CONDITIONAL中\n                        ssComment = SSC.CONDITIONAL;\n                    } else if (lines[pos + 2].getCh() == '+') {\n                        // 处于HINT中\n                        ssComment = SSC.HINT;\n                    }\n                } else if (inString == '\\0' && ssComment != SSC.NONE && inChar == '*'\n                        && lines[pos + 1].getCh() == '/') {\n                    // HINT或CONDITIONAL结束\n                    ssComment = SSC.NONE;\n                }\n                if (inChar == inString) {\n                    // 字符指针出字符串或表达式\n                    inString = '\\0';\n                } else if (!mlComment && inString == '\\0' && ssComment != SSC.HINT\n                        && (inChar == '\\'' || inChar == '\"' || inChar == '`')) {\n                    // 字符指针进入字符串或者表达式\n                    inString = inChar;\n                }\n                if (!mlComment) {\n                    if (needSpace && inChar == ' ') {\n                        lines[out++].setCh(' ');\n                    }\n                    needSpace = false;\n                    // 正常的SQL语句，将其放入line缓冲当中，在合适的实际flush如buffer缓存\n                    lines[out++] = OrderChar.newOrderChar(inOrderChar);\n                    if (inChar != ' ') {\n                        inNormalSql = true;\n                    }\n                } else if (preserveMultiComments) {\n                    // 保留多行注释\n                    lines[out++] = OrderChar.newOrderChar(inOrderChar);\n                }\n            }\n        }\n        // 拦截性的处理，如果out指针没有为0，说明lines中还有内容没有被刷入到buffer，在这里进行flush\n        if (out != 0 || buffer.length() != 0) {\n            lines[out++].setCh('\\n');\n            if (buffer.length() == 0) {\n                bufferOrder.setValue(lines[0].getOrder());\n            }\n            append(buffer, lines, 0, out);\n        }\n    }\n\n    private boolean isOnlyWhiteSpace(StringBuffer buffer) {\n        if (buffer == null) {\n            return false;\n        }\n        int length = buffer.length();\n        for (int i = 0; i < length; i++) {\n            if (buffer.charAt(i) != ' ') {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public synchronized void addLineOracle(List<SplitSqlString> sqls, StringBuffer buffer, Holder<Integer> bufferOrder,\n                                           List<OrderChar> line) {\n        int pos, out;\n        boolean needSpace = false;\n        // 标识量，用于标识当前是否处于HINT，CONDITIONAL中\n        SSC ssComment = SSC.NONE;\n\n        boolean isSameLine = false;\n        int lineLength = line.size();\n        OrderChar[] lines = line.toArray(new OrderChar[lineLength + 1]);\n        if ((lines.length == 0 || lines[0] == null || lines[0].getCh() == 0) && buffer.length() == 0) {\n            return;\n        }\n        lines[lineLength] = new OrderChar((char) 0, lineLength);\n        for (pos = out = 0; pos < lineLength; pos++) {\n            OrderChar inOrderChar = lines[pos];\n            char inChar = inOrderChar.getCh();\n            // 去掉每一行SQL语句最开始的空格\n            if (inChar == ' ' && out == 0 && buffer.length() == 0 && !preserveFormat) {\n                continue;\n            }\n            int delimiterBegin = 0;\n            if (preserveFormat) {\n                for (; delimiterBegin < out\n                        && (lines[delimiterBegin].getCh() == ' '\n                                || lines[delimiterBegin].getCh() == '\\t'); delimiterBegin++) {\n                }\n            }\n            if (equalsIgnoreCase((DELIMITER_NAME + \" \").toCharArray(), lines, delimiterBegin, (out - delimiterBegin))) {\n                // 检测到\"delimiter \"字符串，且不在多行注释以及多行字符串中，说明有设定分隔符的语句\n                StringBuilder newDelimiter = new StringBuilder();\n                for (; pos < lineLength; pos++) {\n                    char tempChar = lines[pos].getCh();\n                    if (tempChar != ' ') {\n                        newDelimiter.append(tempChar);\n                    } else if (newDelimiter.length() != 0) {\n                        break;\n                    }\n                }\n                out = 0;\n                this.delimiter = newDelimiter.toString();\n                continue;\n            }\n            if (!mlComment && inString == '\\0' && ssComment != SSC.HINT && isPrefix(lines, pos, delimiter)) {\n                // 不是多行注释，未在字符串中，不是hint且以delimiter开头，通常是扫描到了sql的末尾\n                pos += delimiter.length();\n                if (out != 0) {\n                    if (buffer.length() == 0) {\n                        bufferOrder.setValue(lines[0].getOrder());\n                    }\n                    append(buffer, lines, 0, out);\n                    out = 0;\n                }\n                // buffer.append(\";\").append('\\n');\n                sqls.add(new SplitSqlString(bufferOrder.getValue(), buffer.toString()));\n                bufferOrder.setValue(bufferOrder.getValue() + buffer.length());\n                pos--;\n                buffer.setLength(0);\n                isSameLine = true;\n                inNormalSql = false;\n            } else if (!mlComment && (inString == '\\0' && (inChar == '-' && lines[pos + 1].getCh() == '-'\n                    && (lines[pos + 2].getCh() != '+' || (lines[pos + 2].getCh() == ' '\n                            || lines[pos + 2].getCh() == '\\0'))))) {\n                // 处于单行注释中，注意规避单行HINT\n                if (buffer.length() == 0) {\n                    bufferOrder.setValue(lines[0].getOrder());\n                }\n                append(buffer, lines, 0, out);\n                out = 0;\n                if (preserveSingleComments) {\n                    // 如果保留单行注释则需要将注释完整地拷贝到缓冲中不能丢弃\n                    for (; pos < lineLength; pos++) {\n                        lines[out++] = OrderChar.newOrderChar(lines[pos]);\n                    }\n                    if (isOnlyWhiteSpace(buffer)) {\n                        // 缓冲中全部是空格，或者缓冲为空说明注释要么处于第一行要么处于个已经完结的sql语句之后\n                        if (sqls.size() != 0) {\n                            if (buffer.length() == 0) {\n                                bufferOrder.setValue(lines[0].getOrder());\n                            }\n                            // 说明注释处于一个已经完结的sql之后，且该sql已经被加入到sql集合中，此处的注释需要追加到最后一句sql中\n                            append(buffer, lines, 0, out);\n                            int lastIndex = sqls.size() - 1;\n                            String lastSql = sqls.get(lastIndex).getStr();\n                            if (!isSameLine) {\n                                lastSql += '\\n';\n                            }\n                            lastSql += buffer + \"\\n\";\n                            sqls.set(lastIndex, new SplitSqlString(sqls.get(lastIndex).getOffset(), lastSql));\n                            buffer.setLength(0);\n                        } else {\n                            lines[out++].setCh('\\n');\n                            if (buffer.length() == 0) {\n                                bufferOrder.setValue(lines[0].getOrder());\n                            }\n                            append(buffer, lines, 0, out - 1);\n                        }\n                    } else {\n                        lines[out++].setCh('\\n');\n                        if (buffer.length() == 0) {\n                            bufferOrder.setValue(lines[0].getOrder());\n                        }\n                        append(buffer, lines, 0, out - 1);\n                    }\n                    out = 0;\n                }\n                break;\n            } else if (inString == '\\0' && (inChar == '/' && lines[pos + 1].getCh() == '*')\n                    && lines[pos + 2].getCh() != '+'\n                    && ssComment != SSC.HINT) {\n                // 处于多行注释中，注意规避了HINT和CONDITIONAL，Oracle模式下没有conditional\n                if (preserveMultiComments) {\n                    lines[out++].setCh('/');\n                    lines[out++].setCh('*');\n                }\n                pos++;\n                mlComment = true;\n            } else if (mlComment && ssComment == SSC.NONE && inChar == '*' && lines[pos + 1].getCh() == '/') {\n                // 多行注释结束\n                pos++;\n                mlComment = false;\n                if (buffer.length() == 0) {\n                    bufferOrder.setValue(lines[0].getOrder());\n                }\n                append(buffer, lines, 0, out);\n                out = 0;\n                if (preserveMultiComments) {\n                    lines[out++].setCh('*');\n                    lines[out++].setCh('/');\n                    if (buffer.length() == 0) {\n                        bufferOrder.setValue(lines[0].getOrder());\n                    }\n                    append(buffer, lines, 0, out);\n                    out = 0;\n                    if (sqls.size() != 0 && !inNormalSql) {\n                        int lastIndex = sqls.size() - 1;\n                        String lastSql = sqls.get(lastIndex).getStr() + buffer;\n                        sqls.set(lastIndex, new SplitSqlString(sqls.get(lastIndex).getOffset(), lastSql));\n                        buffer.setLength(0);\n                    }\n                }\n                needSpace = true;\n            } else {\n                if (inString == '\\0' && inChar == '/' && lines[pos + 1].getCh() == '*') {\n                    if (lines[pos + 2].getCh() == '+') {\n                        // 处于HINT中\n                        ssComment = SSC.HINT;\n                    }\n                } else if (inString == '\\0' && ssComment != SSC.NONE && inChar == '*'\n                        && lines[pos + 1].getCh() == '/') {\n                    // HINT或CONDITIONAL结束\n                    ssComment = SSC.NONE;\n                } else if (inString == '\\0' && inChar == '-' && lines[pos + 1].getCh() == '-'\n                        && lines[pos + 2].getCh() == '+') {\n                    // 在Oracle模式下Hint有单行Hint和多行Hint之分，这里处理Oracle模式下的单行Hint\n                    ssComment = SSC.HINT;\n                }\n                if (inChar == inString) {\n                    // 字符指针出字符串或表达式\n                    if (escapeString == '\\0') {\n                        inString = '\\0';\n                    } else if (pos >= 1 && matchQEscape(lines[pos - 1].getCh())) {\n                        inString = '\\0';\n                        escapeString = '\\0';\n                    }\n                } else if (!mlComment && inString == '\\0' && ssComment != SSC.HINT\n                        && (inChar == '\\'' || inChar == '\"' || inChar == '`')) {\n                    // 字符指针进入字符串或者表达式\n                    inString = inChar;\n                    if (pos >= 1 && (lines[pos - 1].getCh() == 'q' || lines[pos - 1].getCh() == 'Q')) {\n                        // oracle 特有语法，Q 转义\n                        escapeString = lines[pos + 1].getCh();\n                    }\n                }\n                if (!mlComment) {\n                    if (needSpace && inChar == ' ') {\n                        lines[out++].setCh(' ');\n                    }\n                    needSpace = false;\n                    // 正常的SQL语句，将其放入line缓冲当中，在合适的实际flush如buffer缓存\n                    lines[out++] = new OrderChar(inOrderChar.getCh(), inOrderChar.getOrder());\n                    if (inChar != ' ') {\n                        inNormalSql = true;\n                    }\n                } else if (preserveMultiComments) {\n                    // 保留多行注释\n                    lines[out++] = new OrderChar(inOrderChar.getCh(), inOrderChar.getOrder());\n                }\n            }\n        }\n        // 拦截性的处理，如果out指针没有为0，说明lines中还有内容没有被刷入到buffer，在这里进行flush\n        if (out != 0 || buffer.length() != 0) {\n            lines[out++].setCh('\\n');\n            if (buffer.length() == 0) {\n                bufferOrder.setValue(lines[0].getOrder());\n            }\n            append(buffer, lines, 0, out);\n        }\n    }\n\n    private boolean equalsIgnoreCase(char[] src, OrderChar[] dest, int begin, int count) {\n        if (src == null && dest == null) {\n            return true;\n        } else if (src != null && dest != null) {\n            if (src.length != count) {\n                return false;\n            }\n            for (int i = 0; i < count; i++) {\n                char c1 = src[i];\n                char c2 = dest[begin + i].getCh();\n                if (c1 == c2) {\n                    continue;\n                }\n                char u1 = Character.toUpperCase(c1);\n                char u2 = Character.toUpperCase(c2);\n                if (u1 == u2) {\n                    continue;\n                }\n                if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {\n                    continue;\n                }\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 当前SQL是否是以分隔符开头\n     */\n    private boolean isPrefix(OrderChar[] line, int pos, String delim) {\n        StringBuilder builder = new StringBuilder();\n        for (int i = 0; i < line.length - pos; i++) {\n            builder.append(line[pos + i].getCh());\n        }\n        boolean res = builder.toString().startsWith(delim);\n        if (!res || !\"/\".equals(delim) || line.length <= 1) {\n            return res;\n        }\n        // 匹配到分隔符，分隔符为正斜杠且当前行的大小大于 1，需要注意规避多行注释\n        if (pos == 0) {\n            return !(line[pos + 1].getCh() == '*');\n        } else if (line.length - 1 == pos) {\n            return !(line[pos - 1].getCh() == '*');\n        }\n        return !(line[pos + 1].getCh() == '*' || line[pos - 1].getCh() == '*');\n    }\n\n    private boolean matchQEscape(char escapeChar) {\n        if (this.escapeString == '\\0') {\n            return false;\n        }\n        switch (this.escapeString) {\n            case '<':\n                return escapeChar == '>';\n            case '{':\n                return escapeChar == '}';\n            case '[':\n                return escapeChar == ']';\n            case '(':\n                return escapeChar == ')';\n            default:\n                return this.escapeString == escapeChar;\n        }\n    }\n\n    private void append(StringBuffer buffer, OrderChar[] chars, int begin, int count) {\n        for (int i = begin; i < count; i++) {\n            buffer.append(chars[i].getCh());\n        }\n    }\n\n    private static List<List<OrderChar>> splitLine(String sqlScript) {\n        List<List<OrderChar>> lines = new ArrayList<>();\n        List<OrderChar> currentList = new ArrayList<>();\n        Matcher matcher = pattern.matcher(sqlScript);\n        int start = 0;\n        while (matcher.find()) {\n            int end = matcher.start();\n            for (int i = start; i < end; i++) {\n                OrderChar orderChar = new OrderChar(sqlScript.charAt(i), i);\n                currentList.add(orderChar);\n            }\n            lines.add(currentList);\n            currentList = new ArrayList<>();\n            start = matcher.end();\n        }\n        if (start < sqlScript.length()) {\n            for (int i = start; i < sqlScript.length(); i++) {\n                OrderChar orderChar = new OrderChar(sqlScript.charAt(i), i);\n                currentList.add(orderChar);\n            }\n        }\n        if (!currentList.isEmpty()) {\n            lines.add(currentList);\n        }\n        return lines;\n    }\n\n    public String getDelimiter() {\n        return delimiter;\n    }\n\n    public void setDelimiter(String delimiter) {\n        this.delimiter = delimiter;\n    }\n\n    private enum SSC {\n        /**\n         * 不处于HINT或CONDITIONAL中\n         */\n        NONE(0),\n        /**\n         * 当前SQL字符指针处于CONDITIONAL中\n         */\n        CONDITIONAL(1),\n        /**\n         * 当前处于HINT中\n         */\n        HINT(2);\n\n        private final int value;\n\n        SSC(int value) {\n            this.value = value;\n        }\n\n        public int getValue() {\n            return value;\n        }\n    }\n\n    private static class SqlCommentProcessorIterator implements SqlStatementIterator {\n\n        private final BufferedReader reader;\n        private final StringBuffer buffer = new StringBuffer();\n        private final LinkedList<SplitSqlString> holder = new LinkedList<>();\n        private final Holder<Integer> bufferOrder = new Holder<>(0);\n        private final SqlSplitProcessor processor;\n\n        private SplitSqlString current;\n        private int lastLineOrder = 0;\n        private long iteratedBytes = 0;\n\n        public SqlCommentProcessorIterator(InputStream input, Charset charset, SqlSplitProcessor processor) {\n            this.reader = new BufferedReader(new InputStreamReader(input, charset));\n            this.processor = processor;\n        }\n\n        @Override\n        public boolean hasNext() {\n            if (current == null) {\n                current = parseNext();\n            }\n            return current != null;\n        }\n\n        @Override\n        public SplitSqlString next() {\n            SplitSqlString next = current;\n            current = null;\n            if (next == null) {\n                next = parseNext();\n                if (next == null) {\n                    throw new NoSuchElementException(\"No more available sql.\");\n                }\n            }\n            return next;\n        }\n\n        @Override\n        public long iteratedBytes() {\n            return iteratedBytes;\n        }\n\n        private SplitSqlString parseNext() {\n            try {\n                if (!holder.isEmpty()) {\n                    return holder.poll();\n                }\n                String line;\n                while (holder.isEmpty() && (line = reader.readLine()) != null) {\n                    if ( DbType.mysql.equals(processor.dialectType)) {\n                        processor.addLineMysql(holder, buffer, bufferOrder, line.chars()\n                                .mapToObj(c -> new OrderChar((char) c, lastLineOrder++))\n                                .collect(Collectors.toList()));\n                    } else if (DbType.oracle.equals(processor.dialectType)) {\n                        processor.addLineOracle(holder, buffer, bufferOrder, line.chars()\n                                .mapToObj(c -> new OrderChar((char) c, lastLineOrder++))\n                                .collect(Collectors.toList()));\n                    } else if (DbType.oceanbase.equals(processor.dialectType)) {\n                        processor.addLineMysql(holder, buffer, bufferOrder, line.chars()\n                                .mapToObj(c -> new OrderChar((char) c, lastLineOrder++))\n                                .collect(Collectors.toList()));\n                    }\n                    // consider \\n in the end of each line\n                    lastLineOrder++;\n                    iteratedBytes += line.getBytes(StandardCharsets.UTF_8).length + 1;\n                }\n                if (!holder.isEmpty()) {\n                    return holder.poll();\n                }\n                if (buffer.toString().trim().isEmpty()) {\n                    return null;\n                }\n                String sql = buffer.toString();\n                buffer.setLength(0);\n                return new SplitSqlString(0, sql);\n            } catch (Exception e) {\n                throw new RuntimeException(\"Failed to parse input. reason: \" + e.getMessage(), e);\n            }\n        }\n\n    }\n\n    @Data\n    @AllArgsConstructor\n    @NoArgsConstructor\n    static class OrderChar {\n        private char ch;\n        private int order;\n\n        static OrderChar newOrderChar(OrderChar orderChar) {\n            return new OrderChar(orderChar.getCh(), orderChar.getOrder());\n        }\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SqlSplitter.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage ai.chat2db.spi.util;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.oceanbase.tools.sqlparser.oracle.PlSqlLexer;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.antlr.v4.runtime.*;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.Charset;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * currently only for oracle mode <br>\n * TODO: support mysql mode\n */\n@Slf4j\npublic class SqlSplitter {\n\n    private static final int MAX_PL_PATTEN_TYPE_SIZE = 3;\n    private static final String DEFAULT_SQL_DELIMITER = \";\";\n    private static final char[] SPACES_CHARS = \"\\r\\n\\t \".toCharArray();\n    private final LexerTokenDefinition tokenDefinition;\n    private final LexerFactory lexerFactory;\n    private final InnerUtils innerUtils;\n    private final int DEFAULT_PL_END_DELIMITER;\n    private final int SQL_DELIMITER;\n    private final int PL_ELSE;\n    private final int PL_THEN;\n    private final int PL_RIGHTPAREN;\n    private final int PL_LEFTPAREN;\n    private final int PL_GREATER_THAN_OP;\n\n    /**\n     * PL 块开始判断 token 清单\n     */\n    private final int[][] ALL_PL_START_PATTERNS;\n\n    /**\n     * 用于移除 SQL 语句前缀注释部分\n     */\n    private final int[] BLANK_OR_COMMENT_TYPES;\n\n    /**\n     * 用于判断 PL 块开始的 token type 忽略清单\n     */\n    private final int[] PL_START_PATTERN_IGNORE_TYPES;\n\n    /**\n     * 用于判断 IDENT Token\n     */\n    private final int[] PL_IDENT_TYPES;\n\n    /**\n     * 缓存 Token 类型 List， 用于判断是否进入 PL Block <br>\n     * 为了保证性能，最多只使用 MAX_PL_PATTEN_TYPE_SIZE 个 token 判断，超出后则肯定不是 PL Block\n     */\n    private final List<Integer> cacheTokenTypes = new ArrayList<>();\n\n    /**\n     * 当前语句缓冲区，确定一个语句结束时会清空缓冲区用于下一个语句\n     */\n    private final StringBuilder currentStmtBuilder = new StringBuilder();\n\n    /**\n     * 拆句结果\n     */\n    private final List<SplitSqlString> stmts = new ArrayList<>();\n\n    private final boolean addDelimiter;\n\n    /**\n     * 当前语句状态，初始为 SQL_STMT，进去 PL BLock 后切换到 PL_STMT 状态\n     */\n    private State state = State.SQL_STMT;\n\n    /**\n     * 当前拆句关键字，初始为连接会话的 delimiter，拆词结束后回写到连接会话的 delimiter\n     */\n    private String delimiter;\n\n    /**\n     * 为避免每次匹配拆句关键字重复解析，每次 delimiter 变化时同步计算对应的 tokens，和 delimiter 一致\n     */\n    private Token[] delimiterTokens;\n\n    /**\n     * 是否移除 注释前缀，用于绕过部分 OB 版本 PL 语句带注释前缀报错的问（OB 3.1.x）<br>\n     */\n    @Getter\n    @Setter\n    private boolean removeCommentPrefix = false;\n\n    /**\n     * 用于记录在PL块中的子代码块嵌套情况\n     */\n    private Stack<SubPLLevel> subPLStack = new Stack();\n\n    /**\n     * 用于缓存PL块中 Token 类型 List， 用于判断是否进入子 PL Block <br>\n     * 为了保证性能，最多只使用 MAX_PL_PATTEN_TYPE_SIZE 个 token 判断，超出后则肯定不是 PL Block\n     */\n    private List<Integer> plCacheTokenTypes = new ArrayList<>();\n\n    /**\n     * 用于记录 `ALL_PL_START_PATTERNS` 中PL块开始标志index和其枚举类型的映射关系\n     */\n    private Map<Integer, PLStartSymbol> INDEX_2_START_SYMBOL;\n\n    private String sql;\n    private Boolean whileForLoopFlag = false;\n\n    private Holder<Integer> currentOffset = new Holder<>(0);\n\n\n    public SqlSplitter(Class<? extends Lexer> lexerType) {\n        this(lexerType, DEFAULT_SQL_DELIMITER);\n    }\n\n    public SqlSplitter(Class<? extends Lexer> lexerType, String delimiter) {\n        this(lexerType, delimiter, true);\n    }\n\n    public SqlSplitter(Class<? extends Lexer> lexerType, String delimiter, boolean addDelimiter) {\n        this.tokenDefinition = LexerTokenDefinitions.of(lexerType);\n        this.lexerFactory = LexerFactories.of(lexerType);\n        this.innerUtils = new InnerUtils();\n        this.delimiter = delimiter;\n        this.addDelimiter = addDelimiter;\n\n        LexerTokenDefinition definition = this.tokenDefinition;\n        this.DEFAULT_PL_END_DELIMITER = definition.DIV();\n        this.SQL_DELIMITER = definition.SEMICOLON();\n        this.PL_ELSE = definition.ELSE();\n        this.PL_THEN = definition.THEN();\n        this.PL_RIGHTPAREN = definition.RIGHTBRACKET();\n        this.PL_LEFTPAREN = definition.LEFTBRACKET();\n        this.PL_GREATER_THAN_OP = definition.GREATER_THAN_OP();\n\n        int[] ANONYMOUS_BLOCK_DECLARE_START = new int[] {definition.DECLARE()};\n        int[] ANONYMOUS_BLOCK_BEGIN_START = new int[] {definition.BEGIN()};\n        int[] CREATE_FUNCTION_START = new int[] {definition.CREATE(), definition.FUNCTION()};\n        int[] CREATE_PROCEDURE_START = new int[] {definition.CREATE(), definition.PROCEDURE()};\n        int[] CREATE_TRIGGER_START = new int[] {definition.CREATE(), definition.TRIGGER()};\n        int[] CREATE_PACKAGE_START = new int[] {definition.CREATE(), definition.PACKAGE()};\n        int[] CREATE_PACKAGE_BODY_START = new int[] {definition.CREATE(), definition.PACKAGE(), definition.BODY()};\n        int[] CREATE_TYPE_START = new int[] {definition.CREATE(), definition.TYPE()};\n        int[] CREATE_TYPE_BODY_START = new int[] {definition.CREATE(), definition.TYPE(), definition.BODY()};\n\n        int[] FUNCTION_START = new int[] {definition.FUNCTION()};\n        int[] PROCEDURE_START = new int[] {definition.PROCEDURE()};\n        int[] TRIGGER_START = new int[] {definition.TRIGGER()};\n        int[] PACKAGE_START = new int[] {definition.PACKAGE()};\n        int[] PACKAGE_BODY_START = new int[] {definition.PACKAGE(), definition.BODY()};\n        int[] LOOP_START = new int[] {definition.LOOP()};\n        int[] FOR_START = new int[] {definition.FOR()};\n        int[] IF_START = new int[] {definition.IF()};\n        int[] CASE_START = new int[] {definition.CASE()};\n        int[] WHILE_START = new int[] {definition.WHILE()};\n\n        this.ALL_PL_START_PATTERNS = new int[][] {ANONYMOUS_BLOCK_DECLARE_START, ANONYMOUS_BLOCK_BEGIN_START,\n                CREATE_FUNCTION_START, CREATE_PROCEDURE_START, CREATE_TRIGGER_START, CREATE_PACKAGE_START,\n                CREATE_PACKAGE_BODY_START, CREATE_TYPE_START, CREATE_TYPE_BODY_START,\n                FUNCTION_START, PROCEDURE_START, TRIGGER_START, PACKAGE_START, PACKAGE_BODY_START,\n                LOOP_START, FOR_START, IF_START, CASE_START, WHILE_START};\n\n        this.INDEX_2_START_SYMBOL = ImmutableMap.<Integer, PLStartSymbol>builder().put(0, PLStartSymbol.DECLARE)\n                .put(1, PLStartSymbol.BEGIN)\n                .put(2, PLStartSymbol.CREATE_FUNCTION)\n                .put(3, PLStartSymbol.CREATE_PROCEDURE)\n                .put(4, PLStartSymbol.CREATE_TRIGGER)\n                .put(5, PLStartSymbol.CREATE_PACKAGE)\n                .put(6, PLStartSymbol.CREATE_PACKAGE_BODY)\n                .put(7, PLStartSymbol.CREATE_TYPE)\n                .put(8, PLStartSymbol.CREATE_TYPE_BODY)\n                .put(9, PLStartSymbol.FUNCTION)\n                .put(10, PLStartSymbol.PROCEDURE)\n                .put(11, PLStartSymbol.TRIGGER)\n                .put(12, PLStartSymbol.PACKAGE)\n                .put(13, PLStartSymbol.PACKAGE_BODY)\n                .put(14, PLStartSymbol.LOOP)\n                .put(15, PLStartSymbol.FOR)\n                .put(16, PLStartSymbol.IF)\n                .put(17, PLStartSymbol.CASE)\n                .put(18, PLStartSymbol.WHILE).build();\n\n        this.BLANK_OR_COMMENT_TYPES = definition.ANTLR_SKIP() > Token.MIN_USER_TOKEN_TYPE\n                ? new int[] {definition.SPACES(), definition.ANTLR_SKIP()}\n                : new int[] {definition.SPACES(), definition.SINGLE_LINE_COMMENT(),\n                        definition.MULTI_LINE_COMMENT()};\n        this.PL_START_PATTERN_IGNORE_TYPES = new int[] {definition.OR(), definition.REPLACE(), definition.EDITIONABLE(),\n                definition.NONEDITIONABLE(), definition.BODY(), definition.AS(), definition.IS()};\n\n        this.PL_IDENT_TYPES = definition.IDENT() > Token.MIN_USER_TOKEN_TYPE ? new int[] {definition.IDENT()}\n                : new int[] {definition.REGULAR_ID(), definition.DELIMITED_ID()};\n\n        this.delimiterTokens = innerUtils.extractDelimiterTokens(delimiter);\n    }\n\n    public String getDelimiter() {\n        return delimiter;\n    }\n\n    public List<SplitSqlString> split(String sql) {\n        if (StringUtils.isBlank(sql)) {\n            return new ArrayList<>();\n        }\n        clear();\n        this.sql = sql;\n\n        /**\n         * Antlr Lexer 拆词后的 token 列表\n         */\n        Token[] tokens = innerUtils.initTokens(sql);\n\n        int tokenCount = tokens.length;\n        int labelRightCount = 0;\n        for (int pos = 0; pos < tokenCount; pos++) {\n            Token token = tokens[pos];\n            int type = token.getType();\n            if (type < Token.MIN_USER_TOKEN_TYPE) {\n                // invalid token type\n                continue;\n            }\n\n            String text = token.getText();\n            int offset = token.getStartIndex();\n            if (\">\".equals(text)) {\n                labelRightCount++;\n            } else {\n                labelRightCount = 0;\n            }\n            if (this.removeCommentPrefix\n                    && 0 == currentStmtBuilder.length()\n                    && innerUtils.isBlankOrComment(type)) {\n                continue;\n            }\n            if (innerUtils.isPLStartPatternIgnoreTypes(type)) {\n                if (StringUtils.isBlank(currentStmtBuilder.toString()) && type != tokenDefinition.SPACES()) {\n                    currentOffset.setValue(offset);\n                }\n                // skip analysis blank, comment and other PL block start math pattern ignore types\n                currentStmtBuilder.append(text);\n                continue;\n            }\n\n            if (this.state == State.SQL_STMT) {\n                if (cacheTokenTypes.size() < MAX_PL_PATTEN_TYPE_SIZE) {\n                    cacheTokenTypes.add(type);\n                }\n                if (cacheTokenTypes.size() == 1 && innerUtils.isDelimiterCommand(token)) {\n                    pos = executeDelimiterCommand(tokens, pos);\n                    continue;\n                }\n                if (isPLBlockStart()) {\n                    pushToStack(cacheTokenTypes);\n                    if (StringUtils.isBlank(currentStmtBuilder.toString()) && type != tokenDefinition.SPACES()) {\n                        currentOffset.setValue(offset);\n                    }\n                    currentStmtBuilder.append(text);\n                    this.state = State.PL_STMT;\n                    cacheTokenTypes.clear();\n                } else if (isStmtEnd(tokens, pos)) {\n                    pos = addStmtWhileStmtEnd(tokens, pos);\n                } else {\n                    if (StringUtils.isBlank(currentStmtBuilder.toString()) && type != tokenDefinition.SPACES()) {\n                        currentOffset.setValue(offset);\n                    }\n                    currentStmtBuilder.append(text);\n                }\n            } else if (this.state == State.PL_STMT) {\n                // sql statement inside PL block end\n                if (SQL_DELIMITER == type || PL_ELSE == type || PL_THEN == type || PL_RIGHTPAREN == type\n                        || (labelRightCount == 2 && PL_GREATER_THAN_OP == type) || PL_LEFTPAREN == type) {\n                    plCacheTokenTypes.clear();\n                    labelRightCount = 0;\n                } else if (plCacheTokenTypes.size() < MAX_PL_PATTEN_TYPE_SIZE) {\n                    plCacheTokenTypes.add(type);\n                }\n                if (!subPLStack.empty() && (type == tokenDefinition.EXTERNAL() || type == tokenDefinition.LANGUAGE())) {\n                    subPLStack.peek().matchExternalOrLanguage = true;\n                } else if (!subPLStack.empty() && (type == tokenDefinition.IS() || type == tokenDefinition.AS())) {\n                    // `IS` may run into case like `cursor cur1 is select col from for_loop_cursor_t;`\n                    // in this case, it does not have parent pl block\n                    subPLStack.peek().matchIsOrAs = true;\n                } else if (!subPLStack.empty() && subPLStack.peek().startSymbol == PLStartSymbol.CREATE_TYPE\n                        && (type == tokenDefinition.MEMBER() || type == tokenDefinition.STATIC())) {\n                    // temporarily set matchMemberOrStatic in parent subPLLevel\n                    // when encounters sub Function / Procedure in create type\n                    // set sub Function / Procedure's matchMemberOrStatic\n                    // and recover parent subPLLevel matchMemberOrStatic value to false\n                    subPLStack.peek().matchMemberOrStatic = true;\n                }\n\n                if (isStmtEnd(tokens, pos)) {\n                    pos = addStmtWhileStmtEnd(tokens, pos);\n                    this.state = State.SQL_STMT;\n                } else {\n                    if (isSubPLBlockStart()) {\n                        pushToStack(plCacheTokenTypes);\n                        plCacheTokenTypes.clear();\n                    }\n                    int posShift = isPLBlockEnd(tokens, pos);\n                    if (posShift >= 0) {\n                        pos += posShift;\n                        subPLStack.pop();\n                        plCacheTokenTypes.clear();\n                    }\n                    if (StringUtils.isBlank(currentStmtBuilder.toString()) && type != tokenDefinition.SPACES()) {\n                        currentOffset.setValue(offset);\n                    }\n                    currentStmtBuilder.append(text);\n                    // add additional tokens in which may contains in pl block ending tokens\n                    // like end[;] / end [object_name;] / end [loop;] / end [if;] / end [case;]\n                    if (posShift > 0) {\n                        for (int index = 1; index <= posShift; index++) {\n                            if (StringUtils.isBlank(currentStmtBuilder.toString())\n                                    && type != tokenDefinition.SPACES()) {\n                                currentOffset.setValue(offset);\n                            }\n                            currentStmtBuilder.append(tokens[pos - posShift + index].getText());\n                        }\n                    }\n                }\n            }\n        }\n        addStmtWhileStmtEnd(tokens, tokenCount);\n        return stmts;\n    }\n\n    public static SqlStatementIterator iterator(InputStream in, Charset charset, String delimiter) {\n        return iterator(in, charset, delimiter, true);\n    }\n\n    public static SqlStatementIterator iterator(InputStream in, Charset charset, String delimiter,\n            boolean addDelimiter) {\n        return new SqlSplitterIterator(in, charset, delimiter, addDelimiter);\n    }\n\n    private void clear() {\n        this.stmts.clear();\n        this.cacheTokenTypes.clear();\n        this.currentStmtBuilder.setLength(0);\n        this.state = State.SQL_STMT;\n    }\n\n    private int addStmtWhileStmtEnd(Token[] tokens, int pos) {\n        String currentStmt = currentStmtBuilder.toString();\n        boolean notDefaultSqlDelimiter = false;\n        if (StringUtils.isNotBlank(currentStmt)) {\n            if (addDelimiter) {\n                for (int cursor = pos - 1; cursor > 0; cursor--) {\n                    Token token = tokens[cursor];\n                    if (innerUtils.isEOF(token.getType()) || innerUtils.isBlankOrComment(token.getType())) {\n                        continue;\n                    }\n                    notDefaultSqlDelimiter = !DEFAULT_SQL_DELIMITER.equals(token.getText());\n                    if (notDefaultSqlDelimiter) {\n                        currentStmt += DEFAULT_SQL_DELIMITER;\n                    }\n                    break;\n                }\n            }\n            this.stmts.add(new SplitSqlString(currentOffset.getValue(), currentStmt.trim()));\n            if (notDefaultSqlDelimiter) {\n                this.currentOffset.setValue(this.currentOffset.getValue() + DEFAULT_SQL_DELIMITER.length());\n            }\n        }\n        this.cacheTokenTypes.clear();\n        this.currentStmtBuilder.setLength(0);\n        return pos + delimiterTokens.length - 1;\n    }\n\n    private int executeDelimiterCommand(Token[] tokens, int pos) {\n        // delimiter command identified, will ignore built-in pl delimiter logic,\n        // examples:\n        // - delimiter $$\n        // - delimiter /\n        if (pos + 2 >= tokens.length) {\n            // invalid syntax\n            throw new IllegalArgumentException(\"Invalid delimiter command syntax\");\n        }\n        pos++;\n        Token expectBlank = tokens[pos];\n        if (expectBlank.getType() != tokenDefinition.SPACES()) {\n            throw new IllegalArgumentException(\n                    \"Invalid delimiter command syntax, expect blank after 'delimiter'\");\n        }\n        List<Token> delimiterTokensToSet = new ArrayList<>();\n        StringBuilder delimiterBuilder = new StringBuilder();\n\n        // ignore multiple blanks between delimiter keyword and value of delimiter\n        boolean hasDelimiterValue = false;\n        while (++pos < tokens.length) {\n            Token delimiterToken = tokens[pos];\n            int delimiterTokenType = delimiterToken.getType();\n            if (delimiterTokenType > Token.MIN_USER_TOKEN_TYPE && delimiterTokenType != tokenDefinition.SPACES()) {\n                hasDelimiterValue = true;\n                break;\n            }\n        }\n        if (hasDelimiterValue) {\n            --pos;\n        } else {\n            throw new IllegalArgumentException(\n                    \"Invalid delimiter command syntax, no delimiter value set\");\n        }\n\n        // extract value of delimiter, may multiple tokens\n        while (++pos < tokens.length) {\n            Token delimiterToken = tokens[pos];\n            int delimiterTokenType = delimiterToken.getType();\n            if (delimiterTokenType > Token.MIN_USER_TOKEN_TYPE && delimiterTokenType != tokenDefinition.SPACES()) {\n                delimiterTokensToSet.add(delimiterToken);\n                delimiterBuilder.append(delimiterToken.getText());\n            } else {\n                break;\n            }\n        }\n        if (delimiterTokensToSet.isEmpty()) {\n            throw new IllegalArgumentException(\n                    \"Invalid delimiter command syntax, no delimiter value set\");\n        }\n        this.delimiterTokens = delimiterTokensToSet.toArray(new Token[0]);\n        this.delimiter = delimiterBuilder.toString();\n        cacheTokenTypes.clear();\n        return pos;\n    }\n\n    private boolean isPLBlockStart() {\n        if (cacheTokenTypes.size() > MAX_PL_PATTEN_TYPE_SIZE) {\n            return false;\n        }\n        int[] cacheTokenTypeArray = cacheTokenTypes.stream().mapToInt(i -> i).toArray();\n        for (int[] pattern : ALL_PL_START_PATTERNS) {\n            if (Arrays.equals(pattern, cacheTokenTypeArray)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * <pre>\n     * sub PL block start rule is different from the first PL block start\n     * cache token types are stored in a queue because when we run into a new symbol,\n     * we always add to the tail of queue and try to remove head of queue when it it full\n     *\n     * when trying to match ALL_PL_START_PATTERNS, we always try to match the longer start pattern,\n     * for example, match `create type body` but not `create type`\n     * </pre>\n     */\n    private boolean isSubPLBlockStart() {\n        if (plCacheTokenTypes.size() > MAX_PL_PATTEN_TYPE_SIZE) {\n            return false;\n        }\n        int[] plCacheTokenTypeArray = plCacheTokenTypes.stream().mapToInt(i -> i).toArray();\n        for (int[] pattern : ALL_PL_START_PATTERNS) {\n            if (Arrays.equals(pattern, plCacheTokenTypeArray)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private void pushToStack(Collection<Integer> sourceCache) {\n        PLStartSymbol currentSymbol = recognizeStartSymbol(sourceCache);\n        if (Objects.nonNull(currentSymbol)\n                && (currentSymbol == PLStartSymbol.WHILE || currentSymbol == PLStartSymbol.FOR)) {\n            whileForLoopFlag = true;\n        }\n        if (Objects.nonNull(currentSymbol) && whileForLoopFlag && currentSymbol == PLStartSymbol.LOOP) {\n            whileForLoopFlag = false;\n            return;\n        }\n        // declare does not need to push into stack\n        // because declare ... begin ... end block can be recognized by begin ... end\n        // declare does not have an explicit ending\n        if (Objects.nonNull(currentSymbol) && currentSymbol != PLStartSymbol.DECLARE) {\n            SubPLLevel subPLLevel = new SubPLLevel(currentSymbol);\n            // begin symbol does not need to push into stack when its wrapped in [create] function / procedure /\n            // trigger / package [body] / type [body]\n            // because in this case, it is always like `create function ... begin ... end` which can be\n            // recognized by create function ... end\n            if (!subPLStack.empty() && currentSymbol == PLStartSymbol.BEGIN) {\n                switch (subPLStack.peek().startSymbol) {\n                    case FUNCTION:\n                    case PROCEDURE:\n                    case TRIGGER:\n                    case PACKAGE:\n                    case PACKAGE_BODY:\n                    case CREATE_FUNCTION:\n                    case CREATE_PROCEDURE:\n                    case CREATE_TRIGGER:\n                    case CREATE_PACKAGE:\n                    case CREATE_PACKAGE_BODY:\n                    case CREATE_TYPE:\n                    case CREATE_TYPE_BODY:\n                        return;\n                }\n            } else if (!subPLStack.empty() && subPLStack.peek().startSymbol == PLStartSymbol.CREATE_TYPE\n                    && (currentSymbol == PLStartSymbol.FUNCTION || currentSymbol == PLStartSymbol.PROCEDURE)) {\n                // inherit matchMemberOrStatic from parent `CREATE_TYPE` sentence to sub Function or procedure\n                // declare\n                subPLLevel.matchMemberOrStatic = subPLStack.peek().matchMemberOrStatic;\n                subPLStack.peek().matchMemberOrStatic = false;\n            }\n            subPLStack.push(subPLLevel);\n        }\n    }\n\n    private PLStartSymbol recognizeStartSymbol(Collection<Integer> sourceCache) {\n        if (sourceCache.size() > MAX_PL_PATTEN_TYPE_SIZE) {\n            return null;\n        }\n        int[] cacheTokenTypeArray = sourceCache.stream().mapToInt(i -> i).toArray();\n        for (int i = 0; i < ALL_PL_START_PATTERNS.length; i++) {\n            int[] pattern = ALL_PL_START_PATTERNS[i];\n            if (Arrays.equals(pattern, cacheTokenTypeArray)) {\n                return INDEX_2_START_SYMBOL.get(i);\n            }\n        }\n        return PLStartSymbol.UNKNOWN;\n    }\n\n    /**\n     * <pre>\n     * PL block ending judgement may involve pos moving forward.\n     * For example in case LOOP / IF / CASE, ending push pos to pos + 2.\n     * The result of this function means position shift\n     * 0 means is end and pos does not need any move\n     * value < 0 means is not end\n     * value > 0 means is end and pos needs moving forward according to value\n     * </pre>\n     */\n    private int isPLBlockEnd(Token[] tokens, int pos) {\n        if (pos >= tokens.length || subPLStack.empty()) {\n            return -1;\n        }\n\n        boolean isEnd;\n        int posShift = 0;\n        /**\n         * <pre>\n         * `subPLStack` in this function must not be empty because this function must be called when state in PL_STMT\n         * `subPLStack` must have been pushed at least once before enter PL_STMT\n         * </pre>\n         */\n        SubPLLevel peekLevel = subPLStack.peek();\n        switch (peekLevel.startSymbol) {\n            case CREATE_TYPE:\n                isEnd = matchDelimiterTokens(tokens, pos);\n                break;\n            case BEGIN:\n            case CREATE_TYPE_BODY:\n            case TRIGGER:\n            case CREATE_TRIGGER:\n                isEnd = tokens[pos].getType() == this.tokenDefinition.END();\n                break;\n            case FUNCTION:\n            case PROCEDURE:\n                // member or static function && procedure declare in type which does not contain IS or AS\n                // should end with `)` or `,`\n                if (peekLevel.matchMemberOrStatic && !peekLevel.matchIsOrAs) {\n                    isEnd = tokens[pos].getText().equals(\")\") || tokens[pos].getText().equals(\",\");\n                    break;\n                }\n            case PACKAGE:\n            case PACKAGE_BODY:\n            case CREATE_FUNCTION:\n            case CREATE_PROCEDURE:\n            case CREATE_PACKAGE:\n            case CREATE_PACKAGE_BODY:\n                boolean matchDelimiter = matchDelimiterTokens(tokens, pos);\n                if (matchDelimiter) {\n                    // in only two cases, delimiter can be the end of pl create sentence\n                    // 1. `IS` or `AS` is not matched\n                    // 2. `IS` or `AS` is matched but `EXTERNAL` or `LANGUAGE` is also matched\n                    isEnd = !peekLevel.matchIsOrAs || peekLevel.matchExternalOrLanguage;\n                } else {\n                    isEnd = tokens[pos].getType() == this.tokenDefinition.END();\n                }\n                break;\n            case FOR:\n            case LOOP:\n            case WHILE:\n                isEnd = matchPLBlockEnd(tokens, pos, this.tokenDefinition.LOOP());\n                posShift = isEnd ? 2 : 0;\n                break;\n            case IF:\n                isEnd = matchPLBlockEnd(tokens, pos, this.tokenDefinition.IF());\n                posShift = isEnd ? 2 : 0;\n                break;\n            case CASE:\n                isEnd = matchPLBlockEnd(tokens, pos, this.tokenDefinition.CASE());\n                posShift = isEnd ? 2 : 0;\n                break;\n            case UNKNOWN:\n            default:\n                throw new RuntimeException(\n                        String.format(\"Unsupported pl start symbol: %s\", peekLevel.startSymbol));\n        }\n        if (isEnd) {\n            return posShift;\n        }\n        return -1;\n    }\n\n    private boolean matchPLBlockEnd(Token[] tokens, int pos, Integer endObjectType) {\n        boolean match = tokens[pos].getType() == this.tokenDefinition.END();\n        int tokensLength = tokens.length;\n        if (Objects.nonNull(endObjectType)) {\n            if (endObjectType != Token.MIN_USER_TOKEN_TYPE) {\n                // use MIN_USER_TOKEN_TYPE means place holder here\n                // in which we can recognize `end object_name;` as pl block ending\n                match &= (pos + 2 < tokensLength) && tokens[pos + 2].getType() == endObjectType;\n            }\n        }\n        return match;\n    }\n\n    private boolean isStmtEnd(Token[] tokens, int pos) {\n        // only use Div `/` as while in PL stmt and use `;` as delimiter\n        if (pos >= tokens.length) {\n            return false;\n        }\n        if (!subPLStack.empty()) {\n            return false;\n        }\n\n        if (this.state == State.PL_STMT) {\n            if (!DEFAULT_SQL_DELIMITER.equals(delimiter)) {\n                return matchDelimiterTokens(tokens, pos);\n            }\n            return tokens[pos].getType() == DEFAULT_PL_END_DELIMITER;\n        }\n        return matchDelimiterTokens(tokens, pos);\n    }\n\n    private boolean matchDelimiterTokens(Token[] tokens, int pos) {\n        Token[] dt = delimiterTokens;\n        if (this.state == State.PL_STMT && !subPLStack.empty()) {\n            dt = innerUtils.extractDelimiterTokens(DEFAULT_SQL_DELIMITER);\n        }\n        int delimiterLength = dt.length;\n        int tokensLength = tokens.length;\n        if (pos + delimiterLength > tokensLength) {\n            return false;\n        }\n        for (int i = 0; i < delimiterLength; i++) {\n            if (!innerUtils.isTokenEquals(dt[i], tokens[pos + i])) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    enum State {\n        SQL_STMT,\n        PL_STMT\n    }\n\n    enum PLStartSymbol {\n        BEGIN,\n        DECLARE,\n        CREATE_FUNCTION,\n        CREATE_PROCEDURE,\n        CREATE_TRIGGER,\n        CREATE_PACKAGE,\n        CREATE_PACKAGE_BODY,\n        CREATE_TYPE,\n        CREATE_TYPE_BODY,\n        FUNCTION,\n        PROCEDURE,\n        TRIGGER,\n        PACKAGE,\n        PACKAGE_BODY,\n        FOR,\n        LOOP,\n        IF,\n        CASE,\n        WHILE,\n        UNKNOWN\n    }\n\n    class SubPLLevel {\n        private PLStartSymbol startSymbol;\n        /**\n         * 用于记录PL对象DDL语句[如create package / function 等]中是否出现了 EXTERNAL 或者 LANGUAGE 关键字\n         * 如果有，则当前DDL语句的结束符只能为delimiter\n         */\n        private boolean matchExternalOrLanguage = false;\n        /**\n         * 用于记录PL对象DDL语句[如create package / function 等]中是否出现了 IS 或者 AS 关键字\n         */\n        private boolean matchIsOrAs = false;\n        /**\n         * 用于记录Type对象中是否出现了 MEMBER 或者 STATIC 关键字\n         */\n        private boolean matchMemberOrStatic = false;\n\n        private SubPLLevel(PLStartSymbol startSymbol) {\n            this.startSymbol = startSymbol;\n        }\n    }\n\n    class InnerUtils {\n        Token[] initTokens(String sql) {\n            return tokens(sql).toArray(new Token[0]);\n        }\n\n        Token[] extractDelimiterTokens(String delimiter) {\n            return tokens(delimiter).stream()\n                    .filter(token -> token.getType() > Token.MIN_USER_TOKEN_TYPE)\n                    .toArray(Token[]::new);\n        }\n\n        /**\n         * Oracle 词法文件识别 IDENT 关键字必须是字母开头，对于形如 delimiter $$ 语句，其中的 $$ 会被认为是错误的词法，<br>\n         * 这里对不识别的词法转换为 IDENT 和 SPACES 类型的 Token，使得上层可以一致化处理\n         */\n        private List<Token> tokens(String sql) {\n            List<Token> tokens = initTokenStream(sql).getTokens();\n            int length = sql.codePointCount(0, sql.length());\n            int size = tokens.size();\n            Token firstToken = size > 0 ? tokens.get(0) : null;\n            Token lastToken = size > 0 ? tokens.get(size - 1) : null;\n            List<Token> allTokens = new ArrayList<>(tokens.size());\n            if (firstToken != null) {\n                if (firstToken.getStartIndex() > 0) {\n                    allTokens.addAll(generateInvalidTokens(sql, 0, firstToken.getStartIndex()));\n                }\n                allTokens.add(firstToken);\n            }\n            for (int i = 1; i < size; i++) {\n                Token current = tokens.get(i);\n                Token previous = tokens.get(i - 1);\n                if (current.getStartIndex() - previous.getStopIndex() > 1) {\n                    allTokens.addAll(generateInvalidTokens(sql, previous.getStopIndex() + 1, current.getStartIndex()));\n                }\n                allTokens.add(current);\n            }\n            if (lastToken != null && lastToken.getStopIndex() < length - 1) {\n                allTokens.addAll(generateInvalidTokens(sql, lastToken.getStopIndex() + 1, length));\n            }\n            return allTokens;\n        }\n\n        private List<Token> generateInvalidTokens(String sql, int start, int end) {\n            String invalidStr = StringUtils.substring(sql, start, end);\n            char[] chars = invalidStr.toCharArray();\n            if (chars.length == 1) {\n                Token token = new CommonToken(PL_IDENT_TYPES[0], invalidStr);\n                return Collections.singletonList(token);\n            }\n            List<Token> invalidTokens = new ArrayList<>();\n            boolean lastCharSpace = ArrayUtils.contains(SPACES_CHARS, chars[0]);\n            boolean currentCharSpace;\n            int charProcessPos = 0;\n            for (int i = 1; i < chars.length; i++) {\n                currentCharSpace = ArrayUtils.contains(SPACES_CHARS, chars[i]);\n                if (lastCharSpace != currentCharSpace) {\n                    invalidTokens.add(invalidToken(lastCharSpace, invalidStr, charProcessPos, i));\n                    charProcessPos = i;\n                }\n                lastCharSpace = currentCharSpace;\n            }\n            if (charProcessPos <= chars.length - 1) {\n                invalidTokens.add(invalidToken(lastCharSpace, invalidStr, charProcessPos, chars.length));\n            }\n            return invalidTokens;\n        }\n\n        private Token invalidToken(boolean spaces, String str, int start, int end) {\n            String text = StringUtils.substring(str, start, end);\n            Token token = spaces ? new CommonToken(tokenDefinition.SPACES(), text)\n                    : new CommonToken(PL_IDENT_TYPES[0], text);\n            return token;\n        }\n\n        private CommonTokenStream initTokenStream(String sql) {\n            CharStream input = CharStreams.fromString(sql);\n            Lexer lexer = lexerFactory.create(input);\n            CommonTokenStream tokenStream = new CommonTokenStream(lexer);\n            tokenStream.fill();\n            return tokenStream;\n        }\n\n        boolean isDelimiterCommand(Token token) {\n            int type = token.getType();\n            String text = token.getText();\n            return isIdent(type) && StringUtils.equalsIgnoreCase(\"delimiter\", text);\n        }\n\n        boolean isTokenEquals(Token left, Token right) {\n            return left.getType() == right.getType() && StringUtils.equalsIgnoreCase(left.getText(), right.getText());\n        }\n\n        boolean isPLStartPatternIgnoreTypes(int tokenType) {\n            if (isBlankOrComment(tokenType)) {\n                return true;\n            }\n            return ArrayUtils.contains(PL_START_PATTERN_IGNORE_TYPES, tokenType);\n        }\n\n        boolean isBlankOrComment(int tokenType) {\n            return ArrayUtils.contains(BLANK_OR_COMMENT_TYPES, tokenType);\n        }\n\n        boolean isEOF(int tokenType) {\n            return tokenType == -1;\n        }\n\n        private boolean isIdent(int tokenType) {\n            return ArrayUtils.contains(PL_IDENT_TYPES, tokenType);\n        }\n\n    }\n\n    private static class SqlSplitterIterator implements SqlStatementIterator {\n\n        private final BufferedReader reader;\n        private final StringBuilder buffer = new StringBuilder();\n        private final LinkedList<SplitSqlString> holder = new LinkedList<>();\n        private final boolean addDelimiter;\n\n        private SplitSqlString current;\n        private String delimiter;\n        private boolean firstLine = true;\n        private List<String> sqls = new ArrayList<>();\n        private long iteratedBytes = 0;\n        private int offset = 0;\n\n        private static final Character SQL_SEPARATOR_CHAR = '/';\n        private static final Character LINE_SEPARATOR_CHAR = '\\n';\n        private static final String SQL_MULTI_LINE_COMMENT_PREFIX = \"/*\";\n        private static final Set<Character> DELIMITER_CHARACTERS = new HashSet<>(Arrays.asList(';', '/', '$'));\n\n        public SqlSplitterIterator(InputStream input, Charset charset, String delimiter, boolean addDelimiter) {\n            this.reader = new BufferedReader(new InputStreamReader(input, charset));\n            this.delimiter = delimiter;\n            this.addDelimiter = addDelimiter;\n        }\n\n        @Override\n        public boolean hasNext() {\n            if (this.current == null) {\n                this.current = parseNext();\n            }\n            return this.current != null;\n        }\n\n        @Override\n        public SplitSqlString next() {\n            SplitSqlString next = this.current;\n            this.current = null;\n            if (next == null) {\n                next = parseNext();\n                if (next == null) {\n                    throw new NoSuchElementException(\"No more available sql.\");\n                }\n            }\n            return next;\n        }\n\n        @Override\n        public long iteratedBytes() {\n            return this.iteratedBytes;\n        }\n\n        private SplitSqlString parseNext() {\n            try {\n                if (!this.holder.isEmpty()) {\n                    return this.holder.poll();\n                }\n                String line;\n                while (this.holder.isEmpty() && (line = this.reader.readLine()) != null) {\n                    this.iteratedBytes += line.getBytes(Charset.defaultCharset()).length + 1;\n                    addLineToBuffer(line);\n                    SqlSplitProcessor processor = new SqlSplitProcessor();\n                    LinkedList<SplitSqlString> innerHolder = new LinkedList<>();\n                    StringBuffer innerBuffer = new StringBuffer();\n                    Holder<Integer> bufferOrder = new Holder<>(0);\n                    processor.addLineOracle(innerHolder, innerBuffer, bufferOrder,\n                            line.chars().mapToObj(c -> new SqlSplitProcessor.OrderChar((char) c, -1)).collect(Collectors.toList()));\n                    while (processor.isMlComment() && (line = reader.readLine()) != null) {\n                        this.iteratedBytes += line.getBytes(Charset.defaultCharset()).length + 1;\n                        addLineToBuffer(line);\n                        processor.addLineOracle(innerHolder, innerBuffer, bufferOrder,\n                                line.chars().mapToObj(c -> new SqlSplitProcessor.OrderChar((char) c, -1)).collect(Collectors.toList()));\n                    }\n                    // SqlSplitter is non-reentrant, so we need to create a new one for each loop\n                    SqlSplitter splitter = createSplitter();\n                    this.sqls = splitter.split(this.buffer.toString()).stream().map(SplitSqlString::getStr)\n                            .collect(Collectors.toList());\n                    while (this.sqls.size() > 1) {\n                        String sql = this.sqls.remove(0);\n                        int index = this.buffer.indexOf(sql.substring(0, sql.length() - 1));\n                        this.holder.addLast(new SplitSqlString(this.offset + index, sql));\n                        this.buffer.delete(0, index + sql.length());\n                        this.offset += index + sql.length();\n                        clearUselessPrefix();\n                        this.delimiter = splitter.getDelimiter();\n                    }\n                }\n                if (!this.holder.isEmpty()) {\n                    return this.holder.poll();\n                }\n                if (this.sqls.isEmpty()) {\n                    return null;\n                }\n                return new SplitSqlString(this.offset, this.sqls.remove(0));\n            } catch (Exception e) {\n                throw new RuntimeException(\"Failed to parse input. reason: \" + e.getMessage(), e);\n            }\n        }\n\n        private void addLineToBuffer(String line) {\n            if (this.firstLine) {\n                this.buffer.append(line);\n                this.firstLine = false;\n            } else {\n                this.buffer.append(LINE_SEPARATOR_CHAR).append(line);\n            }\n        }\n\n        private void clearUselessPrefix() {\n            while (this.buffer.length() > 0 && DELIMITER_CHARACTERS.contains(this.buffer.charAt(0))\n                    && !(this.buffer.toString().startsWith(SQL_MULTI_LINE_COMMENT_PREFIX))) {\n                this.buffer.deleteCharAt(0);\n                this.offset++;\n            }\n            while ((this.buffer.length() > 0 && (Character.isWhitespace(this.buffer.charAt(0)))) ||\n                    (this.buffer.toString().startsWith(SQL_SEPARATOR_CHAR.toString())\n                            && !this.buffer.toString().startsWith(SQL_MULTI_LINE_COMMENT_PREFIX))) {\n                this.buffer.deleteCharAt(0);\n                this.offset++;\n            }\n        }\n\n        private SqlSplitter createSplitter() {\n            return new SqlSplitter(PlSqlLexer.class, this.delimiter, addDelimiter);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SqlStatementIterator.java",
    "content": "/*\n * Copyright (c) 2023 OceanBase.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage ai.chat2db.spi.util;\n\nimport java.util.Iterator;\n\npublic interface SqlStatementIterator extends Iterator<SplitSqlString> {\n\n    /**\n     * This method is used to get the current number of bytes of the SQL file stream that has been\n     * traversed. The reason for placing it in the interface is that the exact value can only be\n     * computed when it is processed inner the Iterator, whereas the value computed on the call side\n     * based on the contents of the processed SQL is inaccurate. This is because there may be large\n     * blank characters in the SQL which are removed during the SQL splitting process.\n     *\n     * @return bytes size that has been iterated\n     */\n    long iteratedBytes();\n\n}\n"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SqlUtils.java",
    "content": "\npackage ai.chat2db.spi.util;\n\nimport ai.chat2db.server.tools.base.excption.BusinessException;\nimport ai.chat2db.spi.enums.DataTypeEnum;\nimport ai.chat2db.spi.model.ExecuteResult;\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\nimport com.alibaba.druid.sql.parser.SQLParserUtils;\nimport com.oceanbase.tools.sqlparser.oracle.PlSqlLexer;\nimport lombok.extern.slf4j.Slf4j;\nimport net.sf.jsqlparser.expression.Function;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\nimport net.sf.jsqlparser.statement.create.procedure.CreateProcedure;\nimport net.sf.jsqlparser.statement.select.*;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\n/**\n * @author jipengfei\n * @version : SqlUtils.java\n */\n@Slf4j\npublic class SqlUtils {\n\n    public static final String DEFAULT_TABLE_NAME = \"table1\";\n\n    public static void buildCanEditResult(String sql, DbType dbType, ExecuteResult executeResult) {\n        try {\n            Statement statement;\n            if (DbType.sqlserver.equals(dbType)) {\n                statement = CCJSqlParserUtil.parse(sql, ccjSqlParser -> ccjSqlParser.withSquareBracketQuotation(true));\n            } else {\n                statement = CCJSqlParserUtil.parse(sql);\n            }\n            if (statement instanceof Select) {\n                Select select = (Select) statement;\n                PlainSelect plainSelect = (PlainSelect) select.getSelectBody();\n                if (plainSelect.getJoins() == null && plainSelect.getFromItem() != null) {\n                    for (SelectItem item : plainSelect.getSelectItems()) {\n                        if (item instanceof SelectExpressionItem) {\n                            SelectExpressionItem expressionItem = (SelectExpressionItem) item;\n                            if (expressionItem.getAlias() != null) {\n                                //canEdit = false; // 找到了一个别名\n                                executeResult.setCanEdit(false);\n                                return;\n                            }\n                            if (item instanceof SelectExpressionItem) {\n                                SelectExpressionItem selectExpressionItem = (SelectExpressionItem) item;\n                                // if the expression is a function\n                                if (selectExpressionItem.getExpression() instanceof Function) {\n                                    Function function = (Function) selectExpressionItem.getExpression();\n                                    // Check if the function is \"COUNT\"\n                                    if (\"COUNT\".equalsIgnoreCase(function.getName())) {\n                                        executeResult.setCanEdit(false);\n                                        return;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    executeResult.setCanEdit(true);\n                    SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);\n                    if ((sqlStatement instanceof SQLSelectStatement sqlSelectStatement)) {\n                        SQLExprTableSource sqlExprTableSource = (SQLExprTableSource) getSQLExprTableSource(\n                                sqlSelectStatement.getSelect().getFirstQueryBlock().getFrom());\n                        executeResult.setTableName(getMetaDataTableName(sqlExprTableSource.getCatalog(), sqlExprTableSource.getSchema(), sqlExprTableSource.getTableName()));\n                    }\n                } else {\n                    executeResult.setCanEdit(false);\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"buildCanEditResult error\", e);\n            executeResult.setCanEdit(false);\n        }\n    }\n\n    private static String getMetaDataTableName(String... names) {\n        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> name).collect(Collectors.joining(\".\"));\n    }\n\n    public static String formatSQLString(Object para) {\n        return para != null ? \" '\" + para + \"' \" : null;\n    }\n\n    public static String getTableName(String sql, DbType dbType) {\n        SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);\n        if (!(sqlStatement instanceof SQLSelectStatement sqlSelectStatement)) {\n            throw new BusinessException(\"dataSource.sqlAnalysisError\");\n        }\n        SQLExprTableSource sqlExprTableSource = (SQLExprTableSource) getSQLExprTableSource(\n                sqlSelectStatement.getSelect().getFirstQueryBlock().getFrom());\n        if (sqlExprTableSource == null) {\n            return DEFAULT_TABLE_NAME;\n        }\n        return sqlExprTableSource.getTableName();\n    }\n\n    private static SQLTableSource getSQLExprTableSource(SQLTableSource sqlTableSource) {\n        if (sqlTableSource instanceof SQLExprTableSource sqlExprTableSource) {\n            return sqlExprTableSource;\n        } else if (sqlTableSource instanceof SQLJoinTableSource sqlJoinTableSource) {\n            return getSQLExprTableSource(sqlJoinTableSource.getLeft());\n        }\n        return null;\n    }\n\n    private static final String DELIMITER_AFTER_REGEX = \"^\\\\s*(?i)delimiter\\\\s+(\\\\S+)\";\n    private static final String DELIMITER_REGEX = \"(?mi)^\\\\s*delimiter\\\\s*;?\";\n\n    private static final String EVENT_REGEX = \"(?i)\\\\bcreate\\\\s+event\\\\b.*?\\\\bend\\\\b\";\n\n    public static List<String> parse(String sql, DbType dbType, boolean removeComment) {\n        List<String> list = new ArrayList<>();\n        try {\n            if (StringUtils.isBlank(sql)) {\n                return list;\n            }\n            if (removeComment) {\n                sql = SQLParserUtils.removeComment(sql, dbType);\n            }\n            try {\n                if (DbType.oracle.equals(dbType)) {\n                    SqlSplitter sqlSplitter = new SqlSplitter(PlSqlLexer.class, \";\", false);\n                    sqlSplitter.setRemoveCommentPrefix(true);\n                    List<SplitSqlString> sqls = sqlSplitter.split(sql);\n                    return sqls.stream().map(splitSqlString -> removeComment ? SQLParserUtils.removeComment(splitSqlString.getStr(), dbType) : splitSqlString.getStr()).collect(Collectors.toList());\n                }\n            } catch (Exception e) {\n                log.error(\"sqlSplitter error\", e);\n            }\n            try {\n                if (DbType.mysql.equals(dbType) ||\n                        DbType.mariadb.equals(dbType) ||\n                        DbType.oceanbase.equals(dbType)) {\n                    sql = updateNow(sql, dbType);\n                    SqlSplitProcessor sqlSplitProcessor = new SqlSplitProcessor(dbType, true, true);\n                    sqlSplitProcessor.setDelimiter(\";\");\n                    return split(sqlSplitProcessor, sql, dbType, removeComment);\n                }\n            } catch (Exception e) {\n                log.error(\"sqlSplitProcessor error\", e);\n            }\n//            sql = removeDelimiter(sql);\n            if (StringUtils.isBlank(sql)) {\n                return list;\n            }\n            Statements statements = CCJSqlParserUtil.parseStatements(sql);\n            // Iterate through each statement\n            for (Statement stmt : statements.getStatements()) {\n                if (!(stmt instanceof CreateProcedure)) {\n                    list.add(stmt.toString());\n                }\n            }\n            if (CollectionUtils.isEmpty(list)) {\n                list.add(sql);\n            }\n        } catch (Exception e) {\n            try {\n                return splitWithCreateEvent(sql, dbType);\n            } catch (Exception e1) {\n                if (removeComment) {\n                    return SQLParserUtils.splitAndRemoveComment(sql, dbType);\n                }{\n                    return SQLParserUtils.split(sql, dbType);\n                }\n            }\n        }\n        return list;\n    }\n\n    private static String removeDelimiter(String str) {\n        try {\n            if (str.toUpperCase().contains(\"DELIMITER\")) {\n                Pattern pattern = Pattern.compile(DELIMITER_AFTER_REGEX, Pattern.MULTILINE);\n                Matcher matcher = pattern.matcher(str);\n                while (matcher.find()) {\n                    // 获取并打印 \"DELIMITER\" 后的第一个字符串\n                    String mm = matcher.group(1);\n                    if (!\";\".equals(mm)) {\n                        str = str.replace(mm, \"\");\n                    }\n                }\n            }\n            return str.replaceAll(DELIMITER_REGEX, \"\");\n        } catch (Exception e) {\n            return str;\n        }\n    }\n\n    private static List<String> splitWithCreateEvent(String str, DbType dbType) {\n        List<String> list = new ArrayList<>();\n        String sql = SQLParserUtils.removeComment(str, dbType).trim();\n        Pattern pattern = Pattern.compile(EVENT_REGEX, Pattern.DOTALL);\n        Matcher matcher = pattern.matcher(sql);\n        StringBuilder stringBuilder = new StringBuilder();\n        int lastEnd = 0; // 用于跟踪上一个匹配的结束位置\n        while (matcher.find()) {\n            if (matcher.start() > lastEnd) {\n                List<String> l = SQLParserUtils.split(sql.substring(lastEnd, matcher.start()), dbType);\n                list.addAll(l);\n            }\n            list.add(matcher.group());\n            lastEnd = matcher.end(); // 更新上一个匹配的结束位置\n        }\n        if (lastEnd < sql.length()) {\n            List<String> l = SQLParserUtils.split(sql.substring(lastEnd), dbType);\n            list.addAll(l);\n        }\n        return list;\n    }\n\n\n    private static String updateNow(String sql, DbType dbType) {\n        if (StringUtils.isBlank(sql) || !DbType.mysql.equals(dbType)) {\n            return sql;\n        }\n        if (sql.contains(\"default now()\")) {\n            return sql.replace(\"default now()\", \"default CURRENT_TIMESTAMP\");\n        }\n        if (sql.contains(\"DEFAULT now()\")) {\n            return sql.replace(\"DEFAULT now()\", \"default CURRENT_TIMESTAMP\");\n        }\n        if (sql.contains(\"default now ()\")) {\n            return sql.replace(\"default now ()\", \"default CURRENT_TIMESTAMP\");\n        }\n        if (sql.contains(\"DEFAULT now ()\")) {\n            return sql.replace(\"DEFAULT now ()\", \"DEFAULT CURRENT_TIMESTAMP\");\n        }\n        return sql;\n    }\n\n    private static final String DEFAULT_VALUE = \"CHAT2DB_UPDATE_TABLE_DATA_USER_FILLED_DEFAULT\";\n\n    public static String getSqlValue(String value, String dataType) {\n        if (value == null) {\n            return null;\n        }\n        if (\"\".equals(value)) {\n            return \"''\";\n        }\n        if (DEFAULT_VALUE.equals(value)) {\n            return \"DEFAULT\";\n        }\n        DataTypeEnum dataTypeEnum = DataTypeEnum.getByCode(dataType);\n        return dataTypeEnum.getSqlValue(value);\n    }\n\n\n    public static boolean hasPageLimit(String sql, DbType dbType) {\n        try {\n            Statement statement = CCJSqlParserUtil.parse(sql);\n            if (statement instanceof Select) {\n                Select selectStatement = (Select) statement;\n                SelectBody selectBody = selectStatement.getSelectBody();\n                // Check out common pagination methods\n                if (selectBody instanceof PlainSelect) {\n                    PlainSelect plainSelect = (PlainSelect) selectBody;\n                    // CHECK LIMIT\n                    if (plainSelect.getLimit() != null || plainSelect.getOffset() != null || plainSelect.getTop() != null || plainSelect.getFetch() != null) {\n                        return true;\n                    }\n                    if (DbType.oracle.equals(dbType)) {\n                        return sql.contains(\"ROWNUM\") || sql.contains(\"rownum\");\n                    }\n                }\n            }\n        } catch (Exception e) {\n            return false;\n        }\n        return false;\n    }\n\n    private static List<String> split(SqlSplitProcessor processor, String sql, DbType dbType, boolean removeComment) {\n        StringBuffer buffer = new StringBuffer();\n        List<SplitSqlString> sqls = processor.split(buffer, sql);\n        String bufferStr = buffer.toString();\n        if (bufferStr.trim().length() != 0) {\n            // if buffer is not empty, there will be some errors in syntax\n//            log.info(\"sql processor's buffer is not empty, there may be some errors. buffer={}\", bufferStr);\n            int lastSqlOffset;\n            if (sqls.size() == 0) {\n                int index = sql.indexOf(bufferStr.trim(), 0);\n                lastSqlOffset = index == -1 ? 0 : index;\n            } else {\n                int from = sqls.get(sqls.size() - 1).getOffset() + sqls.get(sqls.size() - 1).getStr().length();\n                int index = sql.indexOf(bufferStr.trim(), from);\n                lastSqlOffset = index == -1 ? from : index;\n            }\n            sqls.add(new SplitSqlString(lastSqlOffset, bufferStr));\n\n//            String sqlstr = SQLParserUtils.removeComment(sql, dbType);\n//            return Lists.newArrayList(sqlstr);\n        }\n        return sqls.stream().map(splitSqlString -> removeComment ? SQLParserUtils.removeComment(splitSqlString.getStr(), dbType) : splitSqlString.getStr()).collect(Collectors.toList());\n    }\n\n    public static void main(String[] args) {\n\n    }\n\n    public static String quoteObjectName(String name) {\n        return quoteObjectName(name, \"\\\"\");\n    }\n\n    public static String quoteObjectName(String name, String quoteSymbol) {\n        if (StringUtils.isNotBlank(name)) {\n            boolean startsWithQuote = name.startsWith(quoteSymbol);\n            boolean endsWithQuote = name.endsWith(quoteSymbol);\n\n            if (!startsWithQuote && !endsWithQuote) {\n                // 如果前后都没有quoteSymbol\n                return quoteSymbol + name + quoteSymbol;\n            } else if (startsWithQuote && !endsWithQuote) {\n                // 如果只有前面有quoteSymbol\n                return quoteSymbol + quoteSymbol + name + quoteSymbol;\n            } else if (!startsWithQuote) {\n                // 如果只有后面有quoteSymbol\n                return quoteSymbol + name + quoteSymbol + quoteSymbol;\n            }\n            // 如果前后都有quoteSymbol，直接返回原字符串\n            return name;\n        }\n        // 如果name为空或仅包含空白字符，返回原字符串\n        return name;\n    }\n\n    /**\n     * String input = \"INTERVAL DAY(2) TO SECOND(6)\";\n     * remove (2) and (6)\n     *\n     * @param input\n     * @return\n     */\n    public static String removeDigits(String input) {\n        if (StringUtils.isBlank(input)) {\n            return input;\n        }\n        return input.replaceAll(\"\\\\(\\\\d+\\\\)\", \"\");\n    }\n\n}"
  },
  {
    "path": "chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/TableUtils.java",
    "content": "package ai.chat2db.spi.util;\n\nimport ai.chat2db.spi.model.Table;\nimport ai.chat2db.spi.model.TableColumn;\nimport org.apache.commons.collections4.CollectionUtils;\n\npublic class TableUtils {\n\n    public static TableColumn getTableColumn(Table table,String columnName) {\n        if(table == null || CollectionUtils.isEmpty(table.getColumnList())){\n            return null ;\n        }\n        for (TableColumn tableColumn : table.getColumnList()) {\n            if(tableColumn.getName().equalsIgnoreCase(columnName)){\n                return tableColumn ;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "chat2db-server/lombok.config",
    "content": "# Convert toString and call the previous layer\nlombok.toString.callSuper = CALL\n# Globally configure the callSuper attribute of equalsAndHashCode to true\nlombok.equalsAndHashCode.callSuper=call"
  },
  {
    "path": "chat2db-server/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <!-- Introduce spring startup project -->\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>3.1.0</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>ai.chat2db</groupId>\n    <artifactId>chat2db-server-parent</artifactId>\n    <packaging>pom</packaging>\n    <version>${revision}</version>\n    <name>chat2db-server-parent</name>\n\n    <properties>\n        <!-- The version of the current project uses flatten and only needs to modify this one place -->\n        <revision>2.0.0-SNAPSHOT</revision>\n        <maven.compiler.target>17</maven.compiler.target>\n        <maven.compiler.source>17</maven.compiler.source>\n        <java.version>17</java.version>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <!-- skip test -->\n        <maven.test.skip>true</maven.test.skip>\n    </properties>\n\n    <modules>\n        <module>chat2db-server-domain</module>\n        <module>chat2db-server-start</module>\n        <module>chat2db-server-test</module>\n        <module>chat2db-server-tools</module>\n        <module>chat2db-server-web</module>\n        <module>chat2db-spi</module>\n        <module>chat2db-plugins</module>\n        <module>chat2db-server-web-start</module>\n    </modules>\n\n    <dependencyManagement>\n        <dependencies>\n            <!-- Comes with package -->\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-tools-base</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-tools-common</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-web-api</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-admin-api</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-common-api</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-domain-api</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-domain-core</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-domain-repository</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <!--            <dependency>-->\n            <!--                <groupId>ai.chat2db</groupId>-->\n            <!--                <artifactId>chat2db-server-domain-support</artifactId>-->\n            <!--                <version>${revision}</version>-->\n            <!--            </dependency>-->\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-server-start</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-spi</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>ai.chat2db</groupId>\n                <artifactId>chat2db-plugins</artifactId>\n                <version>${revision}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-collections4</artifactId>\n                <version>4.4</version>\n            </dependency>\n            <dependency>\n                <groupId>com.google.guava</groupId>\n                <artifactId>guava</artifactId>\n                <version>32.0.1-jre</version>\n            </dependency>\n            <dependency>\n                <groupId>cn.hutool</groupId>\n                <artifactId>hutool-all</artifactId>\n                <version>5.8.20</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.fastjson2</groupId>\n                <artifactId>fastjson2</artifactId>\n                <version>2.0.37</version>\n            </dependency>\n\n            <!-- mapstruct -->\n            <dependency>\n                <groupId>org.mapstruct</groupId>\n                <artifactId>mapstruct</artifactId>\n                <version>1.5.5.Final</version>\n            </dependency>\n            <dependency>\n                <groupId>org.mapstruct</groupId>\n                <artifactId>mapstruct-processor</artifactId>\n                <version>1.5.5.Final</version>\n            </dependency>\n            <!-- Make sure to generate lombok first and then mapstruct -->\n            <dependency>\n                <groupId>org.projectlombok</groupId>\n                <artifactId>lombok-mapstruct-binding</artifactId>\n                <version>0.2.0</version>\n            </dependency>\n\n            <!-- Comes with database -->\n            <dependency>\n                <groupId>com.h2database</groupId>\n                <artifactId>h2</artifactId>\n                <version>2.1.214</version>\n            </dependency>\n\n            <!-- mybatis-plus -->\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus</artifactId>\n                <version>3.5.3.1</version>\n            </dependency>\n            <dependency>\n                <groupId>com.zaxxer</groupId>\n                <artifactId>HikariCP</artifactId>\n                <version>5.0.1</version>\n            </dependency>\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus-generator</artifactId>\n                <version>3.5.3.1</version>\n            </dependency>\n            <dependency>\n                <groupId>org.freemarker</groupId>\n                <artifactId>freemarker</artifactId>\n                <version>2.3.32</version>\n            </dependency>\n\n            <!-- druid -->\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>druid</artifactId>\n                <version>1.2.18</version>\n            </dependency>\n\n            <!-- Sa-Token Permission authentication, online documentation:https://sa-token.cc -->\n            <dependency>\n                <groupId>cn.dev33</groupId>\n                <artifactId>sa-token-spring-boot3-starter</artifactId>\n                <version>1.34.0</version>\n            </dependency>\n            <!-- Sa-Token matching jwt -->\n            <dependency>\n                <groupId>cn.dev33</groupId>\n                <artifactId>sa-token-jwt</artifactId>\n                <version>1.34.0</version>\n                <exclusions>\n                    <exclusion>\n                        <artifactId>hutool-jwt</artifactId>\n                        <groupId>cn.hutool</groupId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <!-- http -->\n            <dependency>\n                <groupId>com.dtflys.forest</groupId>\n                <artifactId>forest-spring</artifactId>\n                <version>1.5.32</version>\n            </dependency>\n            <dependency>\n                <groupId>com.dtflys.forest</groupId>\n                <artifactId>forest-core</artifactId>\n                <version>1.5.32</version>\n            </dependency>\n\n            <!-- Database version management -->\n            <dependency>\n                <groupId>org.flywaydb</groupId>\n                <artifactId>flyway-core</artifactId>\n                <version>9.19.4</version>\n            </dependency>\n            <dependency>\n                <groupId>org.flywaydb</groupId>\n                <artifactId>flyway-mysql</artifactId>\n                <version>9.19.4</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.unfbx</groupId>\n                <artifactId>chatgpt-java</artifactId>\n                <version>1.0.8</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>org.slf4j</groupId>\n                        <artifactId>slf4j-simple</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n            <dependency>\n                <groupId>com.theokanning.openai-gpt3-java</groupId>\n                <artifactId>service</artifactId>\n                <version>0.12.0</version>\n            </dependency>\n\n            <!-- https://mvnrepository.com/artifact/org.zalando/logbook-spring-boot-starter -->\n            <dependency>\n                <groupId>org.zalando</groupId>\n                <artifactId>logbook-spring-boot-starter</artifactId>\n                <version>3.3.0</version>\n            </dependency>\n\n            <!-- https://mvnrepository.com/artifact/org.springframework/spring-context-indexer -->\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-context-indexer</artifactId>\n                <version>6.0.10</version>\n                <scope>optional</scope>\n            </dependency>\n            <dependency>\n                <groupId>commons-beanutils</groupId>\n                <artifactId>commons-beanutils</artifactId>\n                <version>1.9.4</version>\n            </dependency>\n            <dependency>\n                <groupId>org.ehcache</groupId>\n                <artifactId>ehcache</artifactId>\n                <version>3.10.8</version>\n            </dependency>\n            <dependency>\n                <groupId>javax.cache</groupId>\n                <artifactId>cache-api</artifactId>\n                <version>1.1.1</version>\n            </dependency>\n            <dependency>\n                <groupId>commons-io</groupId>\n                <artifactId>commons-io</artifactId>\n                <version>2.7</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>easyexcel</artifactId>\n                <version>3.3.2</version>\n            </dependency>\n\n            <!--poi-tl-->\n            <dependency>\n                <groupId>com.deepoove</groupId>\n                <artifactId>poi-tl</artifactId>\n                <version>1.10.5</version>\n            </dependency>\n            <!--pdf-->\n            <dependency>\n                <groupId>com.itextpdf</groupId>\n                <artifactId>itext-asian</artifactId>\n                <version>5.2.0</version>\n            </dependency>\n            <dependency>\n                <groupId>com.itextpdf</groupId>\n                <artifactId>itextpdf</artifactId>\n                <version>5.5.13</version>\n            </dependency>\n\n            <!--    pdf analysis    -->\n            <dependency>\n                <groupId>org.apache.pdfbox</groupId>\n                <artifactId>pdfbox</artifactId>\n                <version>2.0.24</version>\n            </dependency>\n            <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->\n            <dependency>\n                <groupId>com.auth0</groupId>\n                <artifactId>java-jwt</artifactId>\n                <version>4.4.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.github.vertical-blank</groupId>\n                <artifactId>sql-formatter</artifactId>\n                <version>2.0.4</version>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <!-- Make sure to generate lombok first and then mapstruct -->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok-mapstruct-binding</artifactId>\n            <scope>provided</scope>\n        </dependency>\n    </dependencies>\n\n\n    <build>\n        <plugins>\n            <!-- compile -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.11.0</version>\n                <configuration>\n                    <!-- Configure mapstruct to disable builder -->\n                    <compilerArgs>\n                        <arg>\n                            -Amapstruct.disableBuilders=true\n                        </arg>\n                    </compilerArgs>\n                </configuration>\n            </plugin>\n\n            <!-- Support maven revision to configure the system version -->\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>flatten-maven-plugin</artifactId>\n                <version>1.5.0</version>\n                <configuration>\n                    <updatePomFile>true</updatePomFile>\n                    <flattenMode>oss</flattenMode>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>flatten</id>\n                        <phase>process-resources</phase>\n                        <goals>\n                            <goal>flatten</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>flatten.clean</id>\n                        <phase>clean</phase>\n                        <goals>\n                            <goal>clean</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <!-- Run test case -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>3.1.2</version>\n                <configuration>\n                    <includes>\n                        <include>/ai/chat2db/server/test/**/*.java</include>\n                    </includes>\n                    <excludes>\n                        <include>/ai/chat2db/server/test/temp/**/*.java</include>\n                    </excludes>\n                    <testFailureIgnore>true</testFailureIgnore>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "# Mirror based on jdk17\nFROM openjdk:17\n# Define the default location when entering the container and the working location for subsequent operations.\nWORKDIR /app\n# Copy the jar package in the current directory to the /app directory of the docker container\nADD chat2db-server/chat2db-server-web-start/target/chat2db-server-web-start.jar chat2db-server-web-start.jar\n# Copy the lib package in the current directory to the /app/lib directory of the docker container\n# ADD chat2db-server/chat2db-server-web-start/target/lib lib\n# Let the current container expose 10824\nEXPOSE 10824\n# Run Jar\nENTRYPOINT [\"java\",\"-Dloader.path=lib\",\"-Dspring.profiles.active=release\",\"-jar\",\"chat2db-server-web-start.jar\"]\n"
  },
  {
    "path": "docker/docker-build.sh",
    "content": "# First package it out chat2db-server/chat2db-server-start/target/chat2db-server-start.jar\n# Packing\ndocker build -t chat2db/chat2db:test -f docker/Dockerfile ."
  },
  {
    "path": "docker/docker-compose-start.sh",
    "content": "docker-compose -f docker-compose.yml up -d"
  },
  {
    "path": "docker/docker-compose.yml",
    "content": "version: '3'\nservices:\n  chat2db:\n    image: chat2db/chat2db:latest\n    container_name: chat2db-latest\n    volumes:\n      - ~/.chat2db-docker:/root/.chat2db\n    ports:\n      - \"10824:10824\""
  },
  {
    "path": "docker/docker-start.sh",
    "content": "docker run --name=chat2db -p 10824:10824 -v ~/.chat2db-docker:/root/.chat2db  chat2db/chat2db:latest"
  },
  {
    "path": "docker/test/docker-compose.yml",
    "content": "version: '3'\nservices:\n  # Connection address:jdbc:mysql://localhost:3306 username:root  password：ali_dbhub port：3306 Default database：ali_dbhub_test\n  mysql:\n    image: mysql:latest\n    restart: always\n    container_name: mysql-latest\n    environment:\n      MYSQL_ROOT_PASSWORD: ali_dbhub\n      MYSQL_DATABASE: ali_dbhub_test\n      TZ: Asia/Shanghai\n    ports:\n      - 3306:3306\n  # Connection address:jdbc:postgresql://localhost:5432/ali_dbhub_test, username:ali_dbhub, password:ali_dbhub port：5432 Default database：ali_dbhub_test\n  postgres:\n    image: postgres:12-alpine\n    restart: always\n    container_name: postgres-alpine\n    environment:\n      POSTGRES_USER: ali_dbhub\n      POSTGRES_PASSWORD: ali_dbhub\n      POSTGRES_DB: ali_dbhub_test\n      TZ: Asia/Shanghai\n    ports:\n      - 5431:5431\n  # Connection address:jdbc:oracle:thin:@localhost:1521:XE,  username:system, password:ali_dbhub\n  # The default database XEPDB1 will start much faster\n  oracle:\n    image: gvenzl/oracle-xe:slim-faststart\n    restart: always\n    container_name: oracle-latest\n    environment:\n      ORACLE_DATABASE: XEPDB1\n      ORACLE_PASSWORD: ali_dbhub\n      TZ: Asia/Shanghai\n    ports:\n      - 1521:1521\n  redis:\n    image: redis:7\n    container_name: redis7\n    restart: always\n    # Mount mapping, which allows data or configuration to be persisted\n    volumes:\n      #＜local configuration file＞：\n      - $PWD/redis/redis.conf:/etc/redis/redis. conf:ro\n      - $PWD/redis/data:/data\n      - $PWD/redis/logs:/logs\n   # command: redis-server/etc/redis/redis.conf\n    ports:\n      - 6379:6379\n    #service name\n\n    # jdbc:mariadb://localhost:3303 ? user=root&password=ali_dbhub\n  mariadb:\n    image: mariadb:10.5.5\n    container_name: \"mariadb1\"\n    restart: always\n    environment:\n      MYSQL_USER: \"root\"\n      MYSQL_PASSWORD: \"ali_dbhub\"\n      MYSQL_ROOT_PASSWORD: \"ali_dbhub\"\n      TZ: \"Asia/Shanghai\"\n    ports:\n      - \"3304:3304\"\n\n  clickhouse-server:\n    # image：Specify the image, which can be the image name or image id. If the image does not exist locally, compose will try to pull the image.\n    image: yandex/clickhouse-server\n    # container_name：Specify the container name, which defaults to the format of project name_service name_serial number\n    container_name: clickhouse\n    # hostname：Specify container hostname\n    hostname: clickhouse\n    # networks:Configure the network the container is connected to, specifying the networks defined at the end of the file\n    # ports：Expose port information in the format of host port:container port; when only the container port is specified, the host will randomly select the port, similar to docker run -p\n    ports:\n      - \"8123:8123\"\n      - \"9000:9000\"\n      - \"9004:9004\"\n    # expose：The port is exposed but not mapped to the host, so the port cannot be accessed from the outside and can only be accessed and used within the container.\n    expose:\n      - 9009\n    # volumes：Data volume mounting path setting, similar to docker run --volumn=hostdir:containerDir, file permissions can also be specified"
  },
  {
    "path": "docker/test/redis/redis.conf",
    "content": "#Enable remote connection\n#bind 127.0.0.1\n#Custom password\nrequirepass 12345678\n#Specify Redis listening port (default: 6379)\nport 6379\n#The client closes the connection after being idle for a specified period of time (unit: seconds. 0: close this function)\ntimeout 0\n# If there is at least one write operation within 900s, execute bgsave for RDB persistence operation\nsave 900 1\n# Within 300s, if at least 10 keys are modified, perform persistence operation\nsave 300 10\n#Within 60s, if at least 10,000 keys have been modified, the persistence operation will be performed.\nsave 60 10000\n#Whether to compress data storage (default: yes. Redis uses LZ compression. If you want to save CPU time, you can turn off this option, but it will cause the database file to become huge)\nrdbcompression yes\n#Specify the local data file name (default: dump.rdb)\ndbfilename dump.rdb\n#Specify the local data file storage directory\ndir/data\n#Specify the log file location (if it is a relative path, redis will store the log in the specified dir directory)\nlogfile \"redis.log\"\n"
  },
  {
    "path": "docker/test/start.sh",
    "content": "docker-compose -f docker-compose.yml up -d"
  },
  {
    "path": "docker/test/stop.sh",
    "content": "docker-compose -f docker-compose.yml down"
  },
  {
    "path": "document/git/git.sh",
    "content": "# https://www.zhangbj.com/p/1437.html\n# -r represents recursion\n#\n#\ngit filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch chat2db-client/static' --prune-empty --tag-name-filter cat -- --all\ngit push origin developing  --force\ngit push origin delete_git  --force\n"
  },
  {
    "path": "document/sql/mysql.sql",
    "content": "CREATE TABLE `product` (\n                           `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品ID',\n                           `name` varchar(255) NOT NULL COMMENT '商品名称',\n                           `description` varchar(1000) NOT NULL COMMENT '商品描述',\n                           `price` decimal(10,2) NOT NULL COMMENT '商品单价',\n                           `category_id` int(11) NOT NULL COMMENT '所属类别ID',\n                           `brand_id` int(11) DEFAULT NULL COMMENT '品牌ID',\n                           `origin` varchar(255) DEFAULT NULL COMMENT '商品产地',\n                           `weight` decimal(10,2) DEFAULT NULL COMMENT '商品重量（kg）',\n                           `length` decimal(10,2) DEFAULT NULL COMMENT '商品长度（cm）',\n                           `width` decimal(10,2) DEFAULT NULL COMMENT '商品宽度（cm）',\n                           `height` decimal(10,2) DEFAULT NULL COMMENT '商品高度（cm）',\n                           `thumbnail` varchar(255) DEFAULT NULL COMMENT '商品缩略图URL',\n                           `image` varchar(1000) DEFAULT NULL COMMENT '商品图片URL',\n                           `is_sale` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否上架，0：下架，1：上架',\n                           `stock` int(11) NOT NULL COMMENT '商品库存',\n                           `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                           `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n                           PRIMARY KEY (`id`),\n                           KEY `category_id` (`category_id`),\n                           KEY `brand_id` (`brand_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';\n\nINSERT INTO product (name, description, price, category_id, brand_id, origin, weight, length, width, height, thumbnail, image, is_sale, stock, created_at, updated_at)\nSELECT\n    CONCAT('商品', t1.n),\n    CONCAT('这是商品', t1.n, '的描述'),\n    ROUND(RAND() * 1000, 2),\n    FLOOR(RAND() * 10) + 1,\n    IF(RAND() < 0.5, NULL, FLOOR(RAND() * 10) + 1),\n    CONCAT('产地', t1.n),\n    ROUND(RAND() * 10, 2),\n    ROUND(RAND() * 100, 2),\n    ROUND(RAND() * 100, 2),\n    ROUND(RAND() * 100, 2),\n    CONCAT('http://example.com/thumbnail_', t1.n, '.jpg'),\n    CONCAT('http://example.com/image_', t1.n, '.jpg'),\n    IF(RAND() < 0.5, 0, 1),\n    FLOOR(RAND() * 1000),\n    NOW() - INTERVAL FLOOR(RAND() * 365) DAY,\n    NOW() - INTERVAL FLOOR(RAND() * 365) DAY\nFROM\n    (SELECT @rownum:=0) t0,\n    (SELECT @rownum:=@rownum+1 AS n FROM information_schema.COLUMNS LIMIT 10000) t1 ;\n\n\nCREATE TABLE `order` (\n                         `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单ID',\n                         `user_id` int(11) NOT NULL COMMENT '用户ID',\n                         `total_price` decimal(10,2) NOT NULL COMMENT '订单总价',\n                         `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                         `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n                         PRIMARY KEY (`id`),\n                         KEY `user_id` (`user_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';\n\nINSERT INTO `order` (user_id, total_price, created_at, updated_at)\nSELECT\n        FLOOR(RAND() * 1000) + 1,\n        ROUND(RAND() * 10000, 2),\n        NOW() - INTERVAL FLOOR(RAND() * 365) DAY,\n        NOW() - INTERVAL FLOOR(RAND() * 365) DAY\nFROM\n    (SELECT @rownum:=0) t0,\n    (SELECT @rownum:=@rownum+1 AS n FROM information_schema.COLUMNS LIMIT 10000) t1 ;\n\n\nCREATE TABLE `order_item` (\n                              `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单明细ID',\n                              `order_id` int(11) NOT NULL COMMENT '订单ID',\n                              `product_id` int(11) NOT NULL COMMENT '商品ID',\n                              `quantity` int(11) NOT NULL COMMENT '购买数量',\n                              `price` decimal(10,2) NOT NULL COMMENT '商品单价',\n                              `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n                              `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n                              PRIMARY KEY (`id`),\n                              KEY `order_id` (`order_id`),\n                              KEY `product_id` (`product_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';\n\nINSERT INTO order_item (order_id, product_id, quantity, price, created_at, updated_at)\nSELECT\n        FLOOR(RAND() * 10000) + 1,\n        FLOOR(RAND() * 1000) + 1,\n        FLOOR(RAND() * 10) + 1,\n        ROUND(RAND() * 1000, 2),\n        NOW() - INTERVAL FLOOR(RAND() * 365) DAY,\n        NOW() - INTERVAL FLOOR(RAND() * 365) DAY\nFROM\n    (SELECT @rownum:=0) t0,\n    (SELECT @rownum:=@rownum+1 AS n FROM information_schema.COLUMNS LIMIT 10000) t1 ;"
  },
  {
    "path": "document/style/Alibaba_CodeStyle.xml",
    "content": "<code_scheme name=\"Alibaba-CodeStyle\" version=\"173\">\n  <option name=\"INSERT_INNER_CLASS_IMPORTS\" value=\"true\" />\n  <option name=\"CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"999\" />\n  <option name=\"NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"999\" />\n  <option name=\"IMPORT_LAYOUT_TABLE\">\n    <value>\n      <package name=\"java\" withSubpackages=\"true\" static=\"false\" />\n      <emptyLine />\n      <package name=\"javax\" withSubpackages=\"true\" static=\"false\" />\n      <emptyLine />\n      <package name=\"com.alibaba\" withSubpackages=\"true\" static=\"false\" />\n      <emptyLine />\n      <package name=\"\" withSubpackages=\"true\" static=\"false\" />\n      <emptyLine />\n      <package name=\"\" withSubpackages=\"true\" static=\"true\" />\n    </value>\n  </option>\n  <JavaCodeStyleSettings>\n    <option name=\"ANNOTATION_PARAMETER_WRAP\" value=\"1\" />\n    <option name=\"INSERT_INNER_CLASS_IMPORTS\" value=\"true\" />\n    <option name=\"CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"999\" />\n    <option name=\"NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"999\" />\n    <option name=\"IMPORT_LAYOUT_TABLE\">\n      <value>\n        <package name=\"java\" withSubpackages=\"true\" static=\"false\" />\n        <emptyLine />\n        <package name=\"javax\" withSubpackages=\"true\" static=\"false\" />\n        <emptyLine />\n        <package name=\"com.alibaba\" withSubpackages=\"true\" static=\"false\" />\n        <emptyLine />\n        <package name=\"\" withSubpackages=\"true\" static=\"false\" />\n        <emptyLine />\n        <package name=\"\" withSubpackages=\"true\" static=\"true\" />\n      </value>\n    </option>\n    <option name=\"JD_ALIGN_EXCEPTION_COMMENTS\" value=\"false\" />\n    <option name=\"JD_P_AT_EMPTY_LINES\" value=\"false\" />\n    <option name=\"JD_PRESERVE_LINE_FEEDS\" value=\"true\" />\n  </JavaCodeStyleSettings>\n  <codeStyleSettings language=\"JAVA\">\n    <option name=\"LINE_COMMENT_AT_FIRST_COLUMN\" value=\"false\" />\n    <option name=\"BLOCK_COMMENT_AT_FIRST_COLUMN\" value=\"false\" />\n    <option name=\"KEEP_FIRST_COLUMN_COMMENT\" value=\"false\" />\n    <option name=\"KEEP_CONTROL_STATEMENT_IN_ONE_LINE\" value=\"false\" />\n    <option name=\"KEEP_BLANK_LINES_IN_DECLARATIONS\" value=\"1\" />\n    <option name=\"KEEP_BLANK_LINES_IN_CODE\" value=\"1\" />\n    <option name=\"KEEP_BLANK_LINES_BEFORE_RBRACE\" value=\"1\" />\n    <option name=\"ALIGN_MULTILINE_PARAMETERS\" value=\"false\" />\n    <option name=\"SPACE_AFTER_TYPE_CAST\" value=\"false\" />\n    <option name=\"SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE\" value=\"true\" />\n    <option name=\"CALL_PARAMETERS_WRAP\" value=\"1\" />\n    <option name=\"PREFER_PARAMETERS_WRAP\" value=\"true\" />\n    <option name=\"METHOD_PARAMETERS_WRAP\" value=\"1\" />\n    <option name=\"RESOURCE_LIST_WRAP\" value=\"1\" />\n    <option name=\"EXTENDS_LIST_WRAP\" value=\"1\" />\n    <option name=\"THROWS_LIST_WRAP\" value=\"1\" />\n    <option name=\"EXTENDS_KEYWORD_WRAP\" value=\"1\" />\n    <option name=\"THROWS_KEYWORD_WRAP\" value=\"1\" />\n    <option name=\"METHOD_CALL_CHAIN_WRAP\" value=\"1\" />\n    <option name=\"BINARY_OPERATION_WRAP\" value=\"1\" />\n    <option name=\"BINARY_OPERATION_SIGN_ON_NEXT_LINE\" value=\"true\" />\n    <option name=\"TERNARY_OPERATION_WRAP\" value=\"1\" />\n    <option name=\"TERNARY_OPERATION_SIGNS_ON_NEXT_LINE\" value=\"true\" />\n    <option name=\"KEEP_SIMPLE_BLOCKS_IN_ONE_LINE\" value=\"true\" />\n    <option name=\"KEEP_SIMPLE_METHODS_IN_ONE_LINE\" value=\"true\" />\n    <option name=\"KEEP_SIMPLE_CLASSES_IN_ONE_LINE\" value=\"true\" />\n    <option name=\"FOR_STATEMENT_WRAP\" value=\"1\" />\n    <option name=\"ARRAY_INITIALIZER_WRAP\" value=\"1\" />\n    <option name=\"ASSIGNMENT_WRAP\" value=\"1\" />\n    <option name=\"PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE\" value=\"true\" />\n    <option name=\"WRAP_COMMENTS\" value=\"true\" />\n    <option name=\"ASSERT_STATEMENT_WRAP\" value=\"1\" />\n    <option name=\"ASSERT_STATEMENT_COLON_ON_NEXT_LINE\" value=\"true\" />\n    <option name=\"IF_BRACE_FORCE\" value=\"3\" />\n    <option name=\"DOWHILE_BRACE_FORCE\" value=\"3\" />\n    <option name=\"WHILE_BRACE_FORCE\" value=\"3\" />\n    <option name=\"FOR_BRACE_FORCE\" value=\"3\" />\n    <option name=\"WRAP_LONG_LINES\" value=\"true\" />\n    <option name=\"PARAMETER_ANNOTATION_WRAP\" value=\"1\" />\n    <option name=\"VARIABLE_ANNOTATION_WRAP\" value=\"2\" />\n    <option name=\"ENUM_CONSTANTS_WRAP\" value=\"2\" />\n    <indentOptions>\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\n    </indentOptions>\n  </codeStyleSettings>\n</code_scheme>"
  },
  {
    "path": "script/local-client-build.sh",
    "content": "#!/bin/bash\n\n# JRE_DIR=\"${JAVA_HOME}/jre\"\nJRE_DIR=\"~/Library/Java/JavaVirtualMachines/corretto-17.0.8.1/Contents/Home/\"\nJRE_TARGET_DIR=\"chat2db-client/static/jre\"\nCURRENT_ID=\"123\"\n\n# Clean\necho \"Clean\"\nrm -rf chat2db-client/static\nrm -rf chat2db-client/versions\nrm -rf chat2db-client/release\n\n# Use mkdir to create the directory, and use the -p parameter to ensure that no error is reported if the directory already exists\nmkdir -p \"$JRE_TARGET_DIR\"\n\n# Use the cp command to copy the contents of the JAVA_HOME directory to the target directory\n# -r Parameter means recursively copy the entire directory\ncp -r \"${JRE_DIR}/\" \"$JRE_TARGET_DIR\"\nchmod -R 777 \"$JRE_TARGET_DIR\"\n\n# Packaging backend code\nmvn clean package -B '-Dmaven.test.skip=true' -f chat2db-server/pom.xml\nmkdir -p chat2db-client/versions/99.0.${CURRENT_ID}/static\necho -n 99.0.${CURRENT_ID} > chat2db-client/versions/version\ncp chat2db-server/chat2db-server-start/target/chat2db-server-start.jar chat2db-client/versions/99.0.${CURRENT_ID}/static/\n\n# Packaging front-end code\ncd chat2db-client\nyarn install\nyarn run build:web:desktop --app_port=10822\ncp -r dist ./versions/99.0.${CURRENT_ID}/\n# Packaged client\nyarn run build:main:prod -c.productName=Chat2DB-Test -c.extraMetadata.version=99.0.${CURRENT_ID} --mac --arm64\n"
  }
]