From 98fc7be6799287e0687e9e6bc26dfd963a340a65 Mon Sep 17 00:00:00 2001 From: liwq <122639653@qq.com> Date: Thu, 25 Sep 2025 17:48:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20ai=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/infra-server.log.2025-07-25.0.gz | Bin 4880 -> 0 bytes logs/infra-server.log.2025-07-28.0.gz | Bin 93006 -> 0 bytes logs/system-server.log.2025-07-28.0.gz | Bin 42566 -> 0 bytes sql/db2/README.md | 3 - sql/mysql/ai-manage.sql | 72 ++ .../{ruoyi-vue-pro.sql => tashow-base.sql} | 0 sql/tools/.gitignore | 8 - sql/tools/README.md | 130 --- sql/tools/convertor.py | 844 ------------------ sql/tools/docker-compose.yaml | 134 --- sql/tools/oracle/1_create_user.sql | 3 - sql/tools/oracle/2_create_schema.sh | 1 - sql/tools/sqlserver/create_schema.sh | 5 - tashow-dependencies/pom.xml | 23 + .../cloud/infraapi/api/file/FileApi.java | 82 +- tashow-framework/tashow-framework-web/pom.xml | 9 + .../src/main/resources/application-local.yaml | 4 +- tashow-module/pom.xml | 1 + .../pom.xml | 22 +- .../cloud/ai}/TranslateServerApplication.java | 2 +- .../admin/aisample/AiSampleController.java | 78 ++ .../admin/aisample/AiSampleTagController.java | 74 ++ .../aisample/AiSampleTagGroupController.java | 64 ++ .../admin/aisample/vo/AiSamplePageReqVO.java | 33 + .../aisample/vo/AiSampleRelateTagVO.java | 17 + .../admin/aisample/vo/AiSampleRespVO.java | 45 + .../admin/aisample/vo/AiSampleSaveReqVO.java | 19 + .../vo/AiSampleTagGroupPageReqVO.java | 27 + .../aisample/vo/AiSampleTagGroupRespVO.java | 26 + .../vo/AiSampleTagGroupSaveReqVO.java | 16 + .../aisample/vo/AiSampleTagListRespVO.java | 16 + .../aisample/vo/AiSampleTagPageReqVO.java | 18 + .../admin/aisample/vo/AiSampleTagRespVO.java | 20 + .../aisample/vo/AiSampleTagSaveReqVO.java | 21 + .../admin/aisample/vo/FileUploadReqVO.java | 15 + .../ai/controller/admin/package-info.java | 1 + .../cloud/ai/controller/app/package-info.java | 1 + .../app/translate/TranslateController.java | 17 + .../cloud/ai/controller/package-info.java | 1 + .../dal/dataobject/aisample/AiSampleDO.java | 61 ++ .../dataobject/aisample/AiSampleTagDO.java | 34 + .../aisample/AiSampleTagGroupDO.java | 34 + .../aisample/AiSampleTagGroupRelateDO.java | 38 + .../aisample/AiSampleTagRelateDO.java | 36 + .../cloud/ai/dal/dataobject/package-info.java | 2 + .../ai/dal/mysql/aisample/AiSampleMapper.java | 29 + .../aisample/AiSampleTagGroupMapper.java | 25 + .../AiSampleTagGroupRelateMapper.java | 15 + .../dal/mysql/aisample/AiSampleTagMapper.java | 32 + .../aisample/AiSampleTagRelateMapper.java | 16 + .../cloud/ai/dal/mysql/package-info.java | 1 + .../com/tashow/cloud/ai/dal/package-info.java | 1 + .../config/SecurityConfiguration.java | 2 +- .../cloud/ai/security/core/package-info.java | 4 + .../ai/service/aisample/AiSampleService.java | 73 ++ .../service/aisample/AiSampleServiceImpl.java | 156 ++++ .../AiSampleTagGroupRelateService.java | 11 + .../AiSampleTagGroupRelateServiceImpl.java | 16 + .../aisample/AiSampleTagGroupService.java | 62 ++ .../aisample/AiSampleTagGroupServiceImpl.java | 74 ++ .../aisample/AiSampleTagRelateService.java | 11 + .../AiSampleTagRelateServiceImpl.java | 16 + .../service/aisample/AiSampleTagService.java | 61 ++ .../aisample/AiSampleTagServiceImpl.java | 102 +++ .../tashow/cloud/ai/service/package-info.java | 1 + .../src/main/resources/application-local.yaml | 8 +- .../src/main/resources/application.yaml | 0 .../src/main/resources/mapper/demo.xml | 0 .../core/client/local/LocalFileClient.java | 2 +- .../src/main/resources/application-local.yaml | 4 +- .../src/main/resources/application-local.yaml | 4 +- .../translate/controller/package-info.java | 1 - .../dal/dataobject/package-info.java | 2 - .../cloud/translate/dal/package-info.java | 1 - .../translate/security/core/package-info.java | 4 - .../service/feishu/package-info.java | 1 - .../cloud/translate/service/package-info.java | 1 - tashow-sdk/pom.xml | 1 + 78 files changed, 1600 insertions(+), 1194 deletions(-) delete mode 100644 logs/infra-server.log.2025-07-25.0.gz delete mode 100644 logs/infra-server.log.2025-07-28.0.gz delete mode 100644 logs/system-server.log.2025-07-28.0.gz delete mode 100644 sql/db2/README.md create mode 100644 sql/mysql/ai-manage.sql rename sql/mysql/{ruoyi-vue-pro.sql => tashow-base.sql} (100%) delete mode 100644 sql/tools/.gitignore delete mode 100644 sql/tools/README.md delete mode 100644 sql/tools/convertor.py delete mode 100644 sql/tools/docker-compose.yaml delete mode 100644 sql/tools/oracle/1_create_user.sql delete mode 100644 sql/tools/oracle/2_create_schema.sh delete mode 100644 sql/tools/sqlserver/create_schema.sh rename tashow-module/{tashow-module-translate => tashow-module-ai}/pom.xml (86%) rename tashow-module/{tashow-module-translate/src/main/java/com/tashow/cloud/translate => tashow-module-ai/src/main/java/com/tashow/cloud/ai}/TranslateServerApplication.java (92%) create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleController.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleTagController.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleTagGroupController.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSamplePageReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleRelateTagVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleRespVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleSaveReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupPageReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupRespVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupSaveReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagListRespVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagPageReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagRespVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagSaveReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/FileUploadReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/package-info.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/app/package-info.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/app/translate/TranslateController.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/package-info.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleDO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagDO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagGroupDO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagGroupRelateDO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagRelateDO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/package-info.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleMapper.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagGroupMapper.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagGroupRelateMapper.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagMapper.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagRelateMapper.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/package-info.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/package-info.java rename tashow-module/{tashow-module-translate/src/main/java/com/tashow/cloud/translate => tashow-module-ai/src/main/java/com/tashow/cloud/ai}/security/config/SecurityConfiguration.java (97%) create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/security/core/package-info.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleService.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleServiceImpl.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupRelateService.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupRelateServiceImpl.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupService.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupServiceImpl.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagRelateService.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagRelateServiceImpl.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagService.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagServiceImpl.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/package-info.java rename tashow-module/{tashow-module-translate => tashow-module-ai}/src/main/resources/application-local.yaml (74%) rename tashow-module/{tashow-module-translate => tashow-module-ai}/src/main/resources/application.yaml (100%) create mode 100644 tashow-module/tashow-module-ai/src/main/resources/mapper/demo.xml delete mode 100644 tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/controller/package-info.java delete mode 100644 tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/dal/dataobject/package-info.java delete mode 100644 tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/dal/package-info.java delete mode 100644 tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/security/core/package-info.java delete mode 100644 tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/service/feishu/package-info.java delete mode 100644 tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/service/package-info.java diff --git a/logs/infra-server.log.2025-07-25.0.gz b/logs/infra-server.log.2025-07-25.0.gz deleted file mode 100644 index 16fd04f135c4b9dfaa3cb896f9b6a25bc52994fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4880 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 CJE~Ry diff --git a/logs/infra-server.log.2025-07-28.0.gz b/logs/infra-server.log.2025-07-28.0.gz deleted file mode 100644 index 1aa75732e8e6c20916ae91045a86536cf15b6fbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93006 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 b7+fAX>6LD}Z%EDb_zeHZv}UAkfCBe#*Pb2G diff --git a/sql/db2/README.md b/sql/db2/README.md deleted file mode 100644 index d9a95a8..0000000 --- a/sql/db2/README.md +++ /dev/null @@ -1,3 +0,0 @@ -暂未适配 IBM DB2 数据库,如果你有需要,可以微信联系 wangwenbin-server 一起建设。 - -你需要把表结构与数据导入到 DM 数据库,我a来测试与适配代码。 diff --git a/sql/mysql/ai-manage.sql b/sql/mysql/ai-manage.sql new file mode 100644 index 0000000..b160e05 --- /dev/null +++ b/sql/mysql/ai-manage.sql @@ -0,0 +1,72 @@ +DROP TABLE IF EXISTS `tz_ai_sample`; +CREATE TABLE `tz_ai_sample` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `sample_file_id` bigint NOT NULL COMMENT '样本文件id', + `sample_name` varchar(64) NULL DEFAULT '' COMMENT '样本名称', + `sample_time` varchar(16) NULL DEFAULT '' COMMENT '样本时长', + `sample_mine_type` varchar(16) NULL DEFAULT '' COMMENT '样本格式', + `sample_size` varchar(16) NULL DEFAULT '' COMMENT '样本大小', + `remark` varchar(255) NULL DEFAULT '' COMMENT '样本注释', + `creator` varchar(64) NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_create_time` (`create_time` ASC) USING BTREE +) ENGINE = InnoDB COMMENT = '样本库'; + + +DROP TABLE IF EXISTS `tz_ai_sample_tag_relate`; +CREATE TABLE `tz_ai_sample_tag_relate` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `sample_id` bigint NOT NULL COMMENT '样本id', + `sample_tag_id` bigint NOT NULL COMMENT '样本标签id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT = '样本-标签关联表'; + + + +DROP TABLE IF EXISTS `tz_ai_sample_tag`; +CREATE TABLE `tz_ai_sample_tag` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `tag_name` varchar(64) NULL DEFAULT '' COMMENT '标签名称', + `creator` varchar(64) NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_create_time` (`create_time` ASC) USING BTREE +) ENGINE = InnoDB COMMENT = '样本标签库'; + +DROP TABLE IF EXISTS `tz_ai_sample_tag_group`; +CREATE TABLE `tz_ai_sample_tag_group` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `group_name` varchar(64) NULL DEFAULT '' COMMENT '分组名称', + `creator` varchar(64) NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_create_time` (`create_time` ASC) USING BTREE +) ENGINE = InnoDB COMMENT = '样本标签分组库'; + + + +DROP TABLE IF EXISTS `tz_ai_sample_tag_group_relate`; +CREATE TABLE `tz_ai_sample_tag_group_relate` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `sample_tag_id` bigint NOT NULL COMMENT '样本标签id', + `sample_tag_group_id` bigint NOT NULL COMMENT '样本标签分组id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT = '样本标签-分组关联表'; diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/tashow-base.sql similarity index 100% rename from sql/mysql/ruoyi-vue-pro.sql rename to sql/mysql/tashow-base.sql diff --git a/sql/tools/.gitignore b/sql/tools/.gitignore deleted file mode 100644 index e00c3e7..0000000 --- a/sql/tools/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# 忽略python虚拟环境 -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ diff --git a/sql/tools/README.md b/sql/tools/README.md deleted file mode 100644 index 94c5300..0000000 --- a/sql/tools/README.md +++ /dev/null @@ -1,130 +0,0 @@ -## 0. 友情提示 - -在 `sql/tools` 目录下,我们提供一些数据库相关的工具,包括测试数据库的快速启动、MySQL 转换其它数据库等等。 - -注意!所有的操作,必须在 `sql/tools` 目录下执行。 - -## 1. 测试数据库的快速启动 - -基于 Docker Compose,快速启动 MySQL、Oracle、PostgreSQL、SQL Server 等数据库。 - -注意!使用 Docker Compose 启动完测试数据后,因为会自动导入项目的 SQL 脚本,所以可能需要等待 1-2 分钟。 - -### 1.1 MySQL - -```Bash -docker compose up -d mysql -``` - -#### 1.2 Oracle - -```Bash -## x86 版本 -docker compose up -d oracle - -## MacBook Apple Silicon -docker compose up -d oracle_m1 -``` - -> 注意:如果使用 MacBook Apple Silicon 版本,它的 ORACLE_SID 不是 XE,而是 FREE!!! - -### 1.3 PostgreSQL - -```Bash -docker compose up -d postgres -``` - -### 1.4 SQL Server - -```Bash -docker compose up -d sqlserver -# 注意:启动完 sqlserver 后,需要手动再执行如下命令,因为 SQL Server 不支持初始化脚本 -docker compose exec sqlserver bash /tmp/create_schema.sh -``` - -### 1.5 DM 达梦 - -① 下载达梦 Docker 镜像: 地址,点击“Docker 镜像”选项,进行下载。 - -② 加载镜像文件,在镜像 tar 文件所在目录运行: - -```Bash -docker load -i dm8_20240715_x86_rh6_rq_single.tar -``` - -③ 在项目 `sql/tools` 目录下运行: - -```Bash -docker compose up -d dm8 -# 注意:启动完 dm 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本 -docker compose exec dm8 bash -c '/opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql' -exit -``` - -### 1.6 KingbaseES 人大金仓 - -① 下载人大金仓 Docker 镜像: - -* [x86_64 版本](https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/x86_64/kdb_x86_64_V009R001C001B0025.tar) 【Windows 选择这个】 -* [aarch64 版本](https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/aarch64/kdb_aarch64_V009R001C001B0025.tar) 【MacBook Apple Silicon 选择这个】 - -② 加载镜像文件,在镜像 tar 文件所在目录运行: - -```Bash -docker load -i kdb_x86_64_V009R001C001B0025.tar -``` - -③ 在项目 `sql/tools` 目录下运行: - -```Bash -docker compose up -d kingbase -# 注意:启动完 kingbase 后,需要手动再执行如下命令 -docker compose exec kingbase bash -c 'ksql -U $DB_USER -d test -f /tmp/schema.sql' -``` - -### 1.7 华为 OpenGauss - -```Bash -docker compose up -d opengauss -# 注意:启动完 opengauss 后,需要手动再执行如下命令 -docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql' -``` - -## 1.X 容器的销毁重建 - -开发测试过程中,有时候需要创建全新干净的数据库。由于测试数据 Docker 容器采用数据卷 Volume 挂载数据库实例的数据目录,因此销毁数据需要停止容器后,删除数据卷,然后再重新创建容器。 - -以 postgres 为例,操作如下: - -```Bash -docker compose down postgres -docker volume rm ruoyi-vue-pro_postgres -``` - -## 2. MySQL 转换其它数据库 - -项目提供了 `sql/tools/convertor.py` 脚本,支持将 MySQL 转换为 Oracle、PostgreSQL、SQL Server、达梦、人大金仓、OpenGauss 等数据库的脚本。 - -### 2.1 实现原理 - -通过读取 MySQL 的 `sql/mysql/ruoyi-vue-pro.sql` 数据库文件,转换成对应的数据库脚本。 - -### 2.2 使用方法 - -① 安装依赖库 `simple-ddl-parser` - -```bash -pip install simple-ddl-parser -# pip3 install simple-ddl-parser -``` - -② 在 `sql/tools/` 目录下,执行如下命令打印生成 postgres 的脚本内容,其他可选参数有:`oracle`、`sqlserver`、`dm8`、`kingbase`、`opengauss`: - -```Bash -python3 convertor.py postgres -# python3 convertor.py postgres > tmp.sql -``` - -程序将 SQL 脚本打印到终端,可以重定向到临时文件 `tmp.sql`。 - -确认无误后,可以利用 IDEA 进行格式化。当然,也可以直接导入到数据库中。 diff --git a/sql/tools/convertor.py b/sql/tools/convertor.py deleted file mode 100644 index f672cd7..0000000 --- a/sql/tools/convertor.py +++ /dev/null @@ -1,844 +0,0 @@ -# encoding=utf8 -"""芋道系统数据库迁移工具 - -Author: dhb52 (https://gitee.com/dhb52) - -pip install simple-ddl-parser -""" - -import argparse -import pathlib -import re -import time -from abc import ABC, abstractmethod -from typing import Dict, Generator, Optional, Tuple, Union - -from simple_ddl_parser import DDLParser - -PREAMBLE = """/* - Yudao Database Transfer Tool - - Source Server Type : MySQL - - Target Server Type : {db_type} - - Date: {date} -*/ - -""" - - -def load_and_clean(sql_file: str) -> str: - """加载源 SQL 文件,并清理内容方便下一步 ddl 解析 - - Args: - sql_file (str): sql文件路径 - - Returns: - str: 清理后的sql文件内容 - """ - REPLACE_PAIR_LIST = ( - (" CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ", " "), - (" KEY `", " INDEX `"), - ("UNIQUE INDEX", "UNIQUE KEY"), - ("b'0'", "'0'"), - ("b'1'", "'1'"), - ) - - content = open(sql_file).read() - for replace_pair in REPLACE_PAIR_LIST: - content = content.replace(*replace_pair) - content = re.sub(r"ENGINE.*COMMENT", "COMMENT", content) - content = re.sub(r"ENGINE.*;", ";", content) - return content - - -class Convertor(ABC): - def __init__(self, src: str, db_type) -> None: - self.src = src - self.db_type = db_type - self.content = load_and_clean(self.src) - self.table_script_list = re.findall(r"CREATE TABLE [^;]*;", self.content) - - @abstractmethod - def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]) -> str: - """字段类型转换 - - Args: - type (str): 字段类型 - size (Optional[Union[int, Tuple[int]]]): 字段长度描述, 如varchar(255), decimal(10,2) - - Returns: - str: 类型定义 - """ - pass - - @abstractmethod - def gen_create(self, table_ddl: Dict) -> str: - """生成 create 脚本 - - Args: - table_ddl (Dict): 表DDL - - Returns: - str: 生成脚本 - """ - pass - - @abstractmethod - def gen_pk(self, table_name: str) -> str: - """生成主键定义 - - Args: - table_name (str): 表名 - - Returns: - str: 生成脚本 - """ - pass - - @abstractmethod - def gen_index(self, ddl: Dict) -> str: - """生成索引定义 - - Args: - table_ddl (Dict): 表DDL - - Returns: - str: 生成脚本 - """ - pass - - @abstractmethod - def gen_comment(self, table_sql: str, table_name: str) -> str: - """生成字段/表注释 - - Args: - table_sql (str): 原始表SQL - table_name (str): 表名 - - Returns: - str: 生成脚本 - """ - pass - - @abstractmethod - def gen_insert(self, table_name: str) -> str: - """生成 insert 语句块 - - Args: - table_name (str): 表名 - - Returns: - str: 生成脚本 - """ - pass - - def gen_dual(self) -> str: - """生成虚拟 dual 表 - - Returns: - str: 生成脚本, 默认返回空脚本, 表示当前数据库无需手工创建 - """ - return "" - - @staticmethod - def inserts(table_name: str, script_content: str) -> Generator: - PREFIX = f"INSERT INTO `{table_name}`" - - # 收集 `table_name` 对应的 insert 语句 - for line in script_content.split("\n"): - if line.startswith(PREFIX): - head, tail = line.replace(PREFIX, "").split(" VALUES ", maxsplit=1) - head = head.strip().replace("`", "").lower() - tail = tail.strip().replace(r"\"", '"') - # tail = tail.replace("b'0'", "'0'").replace("b'1'", "'1'") - yield f"INSERT INTO {table_name.lower()} {head} VALUES {tail}" - - @staticmethod - def index(ddl: Dict) -> Generator: - """生成索引定义 - - Args: - ddl (Dict): 表DDL - - Yields: - Generator[str]: create index 语句 - """ - - def generate_columns(columns): - keys = [ - f"{col['name'].lower()}{' ' + col['order'].lower() if col['order'] != 'ASC' else ''}" - for col in columns[0] - ] - return ", ".join(keys) - - for no, index in enumerate(ddl["index"], 1): - columns = generate_columns(index["columns"]) - table_name = ddl["table_name"].lower() - yield f"CREATE INDEX idx_{table_name}_{no:02d} ON {table_name} ({columns})" - - @staticmethod - def filed_comments(table_sql: str) -> Generator: - for line in table_sql.split("\n"): - match = re.match(r"^`([^`]+)`.* COMMENT '([^']+)'", line.strip()) - if match: - field = match.group(1) - comment_string = match.group(2).replace("\\n", "\n") - yield field, comment_string - - def table_comment(self, table_sql: str) -> str: - match = re.search(r"COMMENT \= '([^']+)';", table_sql) - return match.group(1) if match else None - - def print(self): - """打印转换后的sql脚本到终端""" - print( - PREAMBLE.format( - db_type=self.db_type, - date=time.strftime("%Y-%m-%d %H:%M:%S"), - ) - ) - - dual = self.gen_dual() - if dual: - print( - f"""-- ---------------------------- --- Table structure for dual --- ---------------------------- -{dual} -""" - ) - - error_scripts = [] - for table_sql in self.table_script_list: - ddl = DDLParser(table_sql.replace("`", "")).run() - - # 如果parse失败, 需要跟进 - if len(ddl) == 0: - error_scripts.append(table_sql) - continue - - table_ddl = ddl[0] - table_name = table_ddl["table_name"] - - # 忽略 quartz 的内容 - if table_name.lower().startswith("qrtz"): - continue - - # 为每个表生成个5个基本部分 - create = self.gen_create(table_ddl) - pk = self.gen_pk(table_name) - index = self.gen_index(table_ddl) - comment = self.gen_comment(table_sql, table_name) - inserts = self.gen_insert(table_name) - - # 组合当前表的DDL脚本 - script = f"""{create} - -{pk} - -{index} - -{comment} - -{inserts} -""" - - # 清理 - script = re.sub("\n{3,}", "\n\n", script).strip() + "\n" - - print(script) - - # 将parse失败的脚本打印出来 - if error_scripts: - for script in error_scripts: - print(script) - - -class PostgreSQLConvertor(Convertor): - def __init__(self, src): - super().__init__(src, "PostgreSQL") - - def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]): - """类型转换""" - - type = type.lower() - - if type == "varchar": - return f"varchar({size})" - if type == "int": - return "int4" - if type == "bigint" or type == "bigint unsigned": - return "int8" - if type == "datetime": - return "timestamp" - if type == "bit": - return "bool" - if type in ("tinyint", "smallint"): - return "int2" - if type == "text": - return "text" - if type in ("blob", "mediumblob"): - return "bytea" - if type == "decimal": - return ( - f"numeric({','.join(str(s) for s in size)})" if len(size) else "numeric" - ) - - def gen_create(self, ddl: Dict) -> str: - """生成 create""" - - def _generate_column(col): - name = col["name"].lower() - if name == "deleted": - return "deleted int2 NOT NULL DEFAULT 0" - - type = col["type"].lower() - full_type = self.translate_type(type, col["size"]) - nullable = "NULL" if col["nullable"] else "NOT NULL" - default = f"DEFAULT {col['default']}" if col["default"] is not None else "" - return f"{name} {full_type} {nullable} {default}" - - table_name = ddl["table_name"].lower() - columns = [f"{_generate_column(col).strip()}" for col in ddl["columns"]] - filed_def_list = ",\n ".join(columns) - script = f"""-- ---------------------------- --- Table structure for {table_name} --- ---------------------------- -DROP TABLE IF EXISTS {table_name}; -CREATE TABLE {table_name} ( - {filed_def_list} -);""" - - return script - - def gen_index(self, ddl: Dict) -> str: - return "\n".join(f"{script};" for script in self.index(ddl)) - - def gen_comment(self, table_sql: str, table_name: str) -> str: - """生成字段及表的注释""" - - script = "" - for field, comment_string in self.filed_comments(table_sql): - script += ( - f"COMMENT ON COLUMN {table_name}.{field} IS '{comment_string}';" + "\n" - ) - - table_comment = self.table_comment(table_sql) - if table_comment: - script += f"COMMENT ON TABLE {table_name} IS '{table_comment}';\n" - - return script - - def gen_pk(self, table_name) -> str: - """生成主键定义""" - return f"ALTER TABLE {table_name} ADD CONSTRAINT pk_{table_name} PRIMARY KEY (id);\n" - - def gen_insert(self, table_name: str) -> str: - """生成 insert 语句,以及根据最后的 insert id+1 生成 Sequence""" - - inserts = list(Convertor.inserts(table_name, self.content)) - ## 生成 insert 脚本 - script = "" - last_id = 0 - if inserts: - inserts_lines = "\n".join(inserts) - script += f"""\n\n-- ---------------------------- --- Records of {table_name.lower()} --- ---------------------------- --- @formatter:off -BEGIN; -{inserts_lines} -COMMIT; --- @formatter:on""" - match = re.search(r"VALUES \((\d+),", inserts[-1]) - if match: - last_id = int(match.group(1)) - - # 生成 Sequence - script += ( - "\n\n" - + f"""DROP SEQUENCE IF EXISTS {table_name}_seq; -CREATE SEQUENCE {table_name}_seq - START {last_id + 1};""" - ) - - return script - - def gen_dual(self) -> str: - return """DROP TABLE IF EXISTS dual; -CREATE TABLE dual -( - id int2 -); - -COMMENT ON TABLE dual IS '数据库连接的表'; - --- ---------------------------- --- Records of dual --- ---------------------------- --- @formatter:off -INSERT INTO dual VALUES (1); --- @formatter:on""" - - -class OracleConvertor(Convertor): - def __init__(self, src): - super().__init__(src, "Oracle") - - def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]): - """类型转换""" - type = type.lower() - - if type == "varchar": - return f"varchar2({size if size < 4000 else 4000})" - if type == "int": - return "number" - if type == "bigint" or type == "bigint unsigned": - return "number" - if type == "datetime": - return "date" - if type == "bit": - return "number(1,0)" - if type in ("tinyint", "smallint"): - return "smallint" - if type == "text": - return "clob" - if type in ("blob", "mediumblob"): - return "blob" - if type == "decimal": - return ( - f"number({','.join(str(s) for s in size)})" if len(size) else "number" - ) - - def gen_create(self, ddl) -> str: - """生成 CREATE 语句""" - - def generate_column(col): - name = col["name"].lower() - if name == "deleted": - return "deleted number(1,0) DEFAULT 0 NOT NULL" - - type = col["type"].lower() - full_type = self.translate_type(type, col["size"]) - nullable = "NULL" if col["nullable"] else "NOT NULL" - default = f"DEFAULT {col['default']}" if col["default"] is not None else "" - # Oracle 中 size 不能作为字段名 - field_name = '"size"' if name == "size" else name - # Oracle DEFAULT 定义在 NULLABLE 之前 - return f"{field_name} {full_type} {default} {nullable}" - - table_name = ddl["table_name"].lower() - columns = [f"{generate_column(col).strip()}" for col in ddl["columns"]] - field_def_list = ",\n ".join(columns) - script = f"""-- ---------------------------- --- Table structure for {table_name} --- ---------------------------- -CREATE TABLE {table_name} ( - {field_def_list} -);""" - - # oracle INSERT '' 不能通过 NOT NULL 校验 - script = script.replace("DEFAULT '' NOT NULL", "DEFAULT '' NULL") - - return script - - def gen_index(self, ddl: Dict) -> str: - return "\n".join(f"{script};" for script in self.index(ddl)) - - def gen_comment(self, table_sql: str, table_name: str) -> str: - script = "" - for field, comment_string in self.filed_comments(table_sql): - script += ( - f"COMMENT ON COLUMN {table_name}.{field} IS '{comment_string}';" + "\n" - ) - - table_comment = self.table_comment(table_sql) - if table_comment: - script += f"COMMENT ON TABLE {table_name} IS '{table_comment}';\n" - - return script - - def gen_pk(self, table_name: str) -> str: - """生成主键定义""" - return f"ALTER TABLE {table_name} ADD CONSTRAINT pk_{table_name} PRIMARY KEY (id);\n" - - def gen_index(self, ddl: Dict) -> str: - return "\n".join(f"{script};" for script in self.index(ddl)) - - def gen_insert(self, table_name: str) -> str: - """拷贝 INSERT 语句""" - inserts = [] - for insert_script in Convertor.inserts(table_name, self.content): - # 对日期数据添加 TO_DATE 转换 - insert_script = re.sub( - r"('\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}')", - r"to_date(\g<1>, 'SYYYY-MM-DD HH24:MI:SS')", - insert_script, - ) - inserts.append(insert_script) - - ## 生成 insert 脚本 - script = "" - last_id = 0 - if inserts: - inserts_lines = "\n".join(inserts) - script += f"""\n\n-- ---------------------------- --- Records of {table_name.lower()} --- ---------------------------- --- @formatter:off -{inserts_lines} -COMMIT; --- @formatter:on""" - match = re.search(r"VALUES \((\d+),", inserts[-1]) - if match: - last_id = int(match.group(1)) - - # 生成 Sequence - script += f""" - -CREATE SEQUENCE {table_name}_seq - START WITH {last_id + 1};""" - - return script - - -class SQLServerConvertor(Convertor): - """_summary_ - - Args: - Convertor (_type_): _description_ - """ - - def __init__(self, src): - super().__init__(src, "Microsoft SQL Server") - - def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]): - """类型转换""" - - type = type.lower() - - if type == "varchar": - return f"nvarchar({size if size < 4000 else 4000})" - if type == "int": - return "int" - if type == "bigint" or type == "bigint unsigned": - return "bigint" - if type == "datetime": - return "datetime2" - if type == "bit": - return "varchar(1)" - if type in ("tinyint", "smallint"): - return "tinyint" - if type == "text": - return "nvarchar(max)" - if type in ("blob", "mediumblob"): - return "varbinary(max)" - if type == "decimal": - return ( - f"numeric({','.join(str(s) for s in size)})" if len(size) else "numeric" - ) - - def gen_create(self, ddl: Dict) -> str: - """生成 create""" - - def _generate_column(col): - name = col["name"].lower() - if name == "id": - return "id bigint NOT NULL PRIMARY KEY IDENTITY" - if name == "deleted": - return "deleted bit DEFAULT 0 NOT NULL" - - type = col["type"].lower() - full_type = self.translate_type(type, col["size"]) - nullable = "NULL" if col["nullable"] else "NOT NULL" - default = f"DEFAULT {col['default']}" if col["default"] is not None else "" - return f"{name} {full_type} {default} {nullable}" - - table_name = ddl["table_name"].lower() - columns = [f"{_generate_column(col).strip()}" for col in ddl["columns"]] - filed_def_list = ",\n ".join(columns) - script = f"""-- ---------------------------- --- Table structure for {table_name} --- ---------------------------- -DROP TABLE IF EXISTS {table_name} -GO -CREATE TABLE {table_name} ( - {filed_def_list} -) -GO""" - - return script - - def gen_comment(self, table_sql: str, table_name: str) -> str: - """生成字段及表的注释""" - - script = "" - - for field, comment_string in self.filed_comments(table_sql): - script += f"""EXEC sp_addextendedproperty - 'MS_Description', N'{comment_string}', - 'SCHEMA', N'dbo', - 'TABLE', N'{table_name}', - 'COLUMN', N'{field}' -GO - -""" - - table_comment = self.table_comment(table_sql) - if table_comment: - script += f"""EXEC sp_addextendedproperty - 'MS_Description', N'{table_comment}', - 'SCHEMA', N'dbo', - 'TABLE', N'{table_name}' -GO - -""" - return script - - def gen_pk(self, table_name: str) -> str: - """生成主键定义""" - return "" - - def gen_index(self, ddl: Dict) -> str: - """生成 index""" - return "\n".join(f"{script}\nGO" for script in self.index(ddl)) - - def gen_insert(self, table_name: str) -> str: - """生成 insert 语句""" - - # 收集 `table_name` 对应的 insert 语句 - inserts = [] - for insert_script in Convertor.inserts(table_name, self.content): - # SQLServer: 字符串前加N,hack,是否存在替换字符串内容的风险 - insert_script = insert_script.replace(", '", ", N'").replace( - "VALUES ('", "VALUES (N')" - ) - # 删除 insert 的结尾分号 - insert_script = re.sub(";$", r"\nGO", insert_script) - inserts.append(insert_script) - - ## 生成 insert 脚本 - script = "" - if inserts: - inserts_lines = "\n".join(inserts) - script += f"""\n\n-- ---------------------------- --- Records of {table_name.lower()} --- ---------------------------- --- @formatter:off -BEGIN TRANSACTION -GO -SET IDENTITY_INSERT {table_name.lower()} ON -GO -{inserts_lines} -SET IDENTITY_INSERT {table_name.lower()} OFF -GO -COMMIT -GO --- @formatter:on""" - - return script - - def gen_dual(self) -> str: - return """DROP TABLE IF EXISTS dual -GO -CREATE TABLE dual -( - id int -) -GO - -EXEC sp_addextendedproperty - 'MS_Description', N'数据库连接的表', - 'SCHEMA', N'dbo', - 'TABLE', N'dual' -GO - --- ---------------------------- --- Records of dual --- ---------------------------- --- @formatter:off -INSERT INTO dual VALUES (1) -GO --- @formatter:on""" - - -class DM8Convertor(Convertor): - def __init__(self, src): - super().__init__(src, "DM8") - - def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]): - """类型转换""" - type = type.lower() - - if type == "varchar": - return f"varchar({size})" - if type == "int": - return "int" - if type == "bigint" or type == "bigint unsigned": - return "bigint" - if type == "datetime": - return "datetime" - if type == "bit": - return "bit" - if type in ("tinyint", "smallint"): - return "smallint" - if type == "text": - return "text" - if type == "blob": - return "blob" - if type == "mediumblob": - return "varchar(10240)" - if type == "decimal": - return ( - f"decimal({','.join(str(s) for s in size)})" if len(size) else "decimal" - ) - - def gen_create(self, ddl) -> str: - """生成 CREATE 语句""" - - def generate_column(col): - name = col["name"].lower() - if name == "id": - return "id bigint NOT NULL PRIMARY KEY IDENTITY" - - type = col["type"].lower() - full_type = self.translate_type(type, col["size"]) - nullable = "NULL" if col["nullable"] else "NOT NULL" - default = f"DEFAULT {col['default']}" if col["default"] is not None else "" - return f"{name} {full_type} {default} {nullable}" - - table_name = ddl["table_name"].lower() - columns = [f"{generate_column(col).strip()}" for col in ddl["columns"]] - field_def_list = ",\n ".join(columns) - script = f"""-- ---------------------------- --- Table structure for {table_name} --- ---------------------------- -CREATE TABLE {table_name} ( - {field_def_list} -);""" - - # oracle INSERT '' 不能通过 NOT NULL 校验 - script = script.replace("DEFAULT '' NOT NULL", "DEFAULT '' NULL") - - return script - - def gen_index(self, ddl: Dict) -> str: - return "\n".join(f"{script};" for script in self.index(ddl)) - - def gen_comment(self, table_sql: str, table_name: str) -> str: - script = "" - for field, comment_string in self.filed_comments(table_sql): - script += ( - f"COMMENT ON COLUMN {table_name}.{field} IS '{comment_string}';" + "\n" - ) - - table_comment = self.table_comment(table_sql) - if table_comment: - script += f"COMMENT ON TABLE {table_name} IS '{table_comment}';\n" - - return script - - def gen_pk(self, table_name: str) -> str: - """生成主键定义""" - return "" - - def gen_index(self, ddl: Dict) -> str: - return "\n".join(f"{script};" for script in self.index(ddl)) - - def gen_insert(self, table_name: str) -> str: - """拷贝 INSERT 语句""" - inserts = list(Convertor.inserts(table_name, self.content)) - - ## 生成 insert 脚本 - script = "" - if inserts: - inserts_lines = "\n".join(inserts) - script += f"""\n\n-- ---------------------------- --- Records of {table_name.lower()} --- ---------------------------- --- @formatter:off -SET IDENTITY_INSERT {table_name.lower()} ON; -{inserts_lines} -COMMIT; -SET IDENTITY_INSERT {table_name.lower()} OFF; --- @formatter:on""" - - return script - - -class KingbaseConvertor(PostgreSQLConvertor): - def __init__(self, src): - super().__init__(src) - self.db_type = "Kingbase" - - def gen_create(self, ddl: Dict) -> str: - """生成 create""" - - def _generate_column(col): - name = col["name"].lower() - if name == "deleted": - return "deleted int2 NOT NULL DEFAULT 0" - - type = col["type"].lower() - full_type = self.translate_type(type, col["size"]) - nullable = "NULL" if col["nullable"] else "NOT NULL" - default = f"DEFAULT {col['default']}" if col["default"] is not None else "" - return f"{name} {full_type} {nullable} {default}" - - table_name = ddl["table_name"].lower() - columns = [f"{_generate_column(col).strip()}" for col in ddl["columns"]] - filed_def_list = ",\n ".join(columns) - script = f"""-- ---------------------------- --- Table structure for {table_name} --- ---------------------------- -DROP TABLE IF EXISTS {table_name}; -CREATE TABLE {table_name} ( - {filed_def_list} -);""" - - # Kingbase INSERT '' 不能通过 NOT NULL 校验 - script = script.replace("NOT NULL DEFAULT ''", "NULL DEFAULT ''") - - return script - - -class OpengaussConvertor(KingbaseConvertor): - def __init__(self, src): - super().__init__(src) - self.db_type = "OpenGauss" - - -def main(): - parser = argparse.ArgumentParser(description="芋道系统数据库转换工具") - parser.add_argument( - "type", - type=str, - help="目标数据库类型", - choices=["postgres", "oracle", "sqlserver", "dm8", "kingbase", "opengauss"], - ) - args = parser.parse_args() - - sql_file = pathlib.Path("../mysql/ruoyi-vue-pro.sql").resolve().as_posix() - convertor = None - if args.type == "postgres": - convertor = PostgreSQLConvertor(sql_file) - elif args.type == "oracle": - convertor = OracleConvertor(sql_file) - elif args.type == "sqlserver": - convertor = SQLServerConvertor(sql_file) - elif args.type == "dm8": - convertor = DM8Convertor(sql_file) - elif args.type == "kingbase": - convertor = KingbaseConvertor(sql_file) - elif args.type == "opengauss": - convertor = OpengaussConvertor(sql_file) - else: - raise NotImplementedError(f"不支持目标数据库类型: {args.type}") - - convertor.print() - - -if __name__ == "__main__": - main() diff --git a/sql/tools/docker-compose.yaml b/sql/tools/docker-compose.yaml deleted file mode 100644 index 0fa9513..0000000 --- a/sql/tools/docker-compose.yaml +++ /dev/null @@ -1,134 +0,0 @@ -name: ruoyi-vue-pro - -volumes: - mysql: { } - postgres: { } - sqlserver: { } - dm8: { } - kingbase: { } - opengauss: { } - -services: - mysql: - image: mysql:8.0.33 - restart: unless-stopped - environment: - TZ: Asia/Shanghai - MYSQL_ROOT_PASSWORD: 123456 - MYSQL_DATABASE: ruoyi-vue-pro - ports: - - "3306:3306" - volumes: - - mysql:/var/lib/mysql/ - # 注入初始化脚本 - - ./mysql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/init.sql:ro - command: - --default-authentication-plugin=mysql_native_password - --character-set-server=utf8mb4 - --collation-server=utf8mb4_general_ci - --explicit_defaults_for_timestamp=true - --lower_case_table_names=1 - - postgres: - image: postgres:14.2 - restart: unless-stopped - environment: - POSTGRES_USER: root - POSTGRES_PASSWORD: 123456 - POSTGRES_DB: ruoyi-vue-pro - ports: - - "5432:5432" - volumes: - - postgres:/var/lib/postgresql/data - # 注入初始化脚本 - - ../postgresql/quartz.sql:/docker-entrypoint-initdb.d/quartz.sql:ro - - ../postgresql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/ruoyi-vue-pro.sql:ro - - oracle: - image: gvenzl/oracle-xe:18-slim-faststart - restart: unless-stopped - environment: - ## 登录信息 SID: XE user: system password: oracle - ORACLE_PASSWORD: oracle - ports: - - "1521:1521" - volumes: - - ../oracle/ruoyi-vue-pro.sql:/tmp/schema.sql:ro - # 创建app用户: ROOT/123456@//localhost/XEPDB1 - - ./oracle/1_create_user.sql:/docker-entrypoint-initdb.d/1_create_user.sql:ro - - ./oracle/2_create_schema.sh:/docker-entrypoint-initdb.d/2_create_schema.sh:ro - - oracle_m1: - image: einslib/oracle-19c:19.3.0-ee-slim-faststart - restart: unless-stopped - environment: - ## 登录信息 SID: FREE user: system password: oracle - ORACLE_PASSWORD: oracle - ports: - - "1521:1521" - volumes: - - ../oracle/ruoyi-vue-pro.sql:/tmp/schema.sql:ro - # 创建app用户: ROOT/123456@//localhost/XEPDB1 - - ./oracle/1_create_user.sql:/docker-entrypoint-initdb.d/1_create_user.sql:ro - - ./oracle/2_create_schema.sh:/docker-entrypoint-initdb.d/2_create_schema.sh:ro - - sqlserver: - image: mcr.microsoft.com/mssql/server:2017-latest - restart: unless-stopped - environment: - TZ: Asia/Shanghai - ACCEPT_EULA: "Y" - SA_PASSWORD: "Yudao@2024" - ports: - - "1433:1433" - volumes: - - sqlserver:/var/opt/mssql - - ../sqlserver/ruoyi-vue-pro.sql:/tmp/schema.sql:ro - # docker compose exec sqlserver bash /tmp/create_schema.sh - - ./sqlserver/create_schema.sh:/tmp/create_schema.sh:ro - - dm8: - # docker load -i dm8_20240715_x86_rh6_rq_single.tar - image: dm8_single:dm8_20240715_rev232765_x86_rh6_64 - restart: unless-stopped - environment: - PAGE_SIZE: 16 - LD_LIBRARY_PATH: /opt/dmdbms/bin - EXTENT_SIZE: 32 - BLANK_PAD_MODE: 1 - LOG_SIZE: 1024 - UNICODE_FLAG: 1 - LENGTH_IN_CHAR: 1 - INSTANCE_NAME: dm8_test - ports: - - "5236:5236" - volumes: - - dm8:/opt/dmdbms/data - - ../dm/ruoyi-vue-pro-dm8.sql:/tmp/schema.sql:ro - - kingbase: - image: kingbase_v009r001c001b0025_single_x86:v1 -# image: kingbase_v009r001c001b0025_single_arm:v1 - restart: unless-stopped - environment: - DB_USER: root - DB_PASSWORD: 123456 - ports: - - "54321:54321" - volumes: - - kingbase:/home/kingbase/userdata - - ../kingbase/ruoyi-vue-pro.sql:/tmp/schema.sql:ro - - opengauss: - image: opengauss/opengauss:5.0.0 - restart: unless-stopped - environment: - GS_USERNAME: root - GS_PASSWORD: Yudao@2024 - LD_LIBRARY_PATH: /usr/local/opengauss/lib:/usr/lib - ports: - - "5432:5432" - volumes: - - opengauss:/var/lib/opengauss - - ../opengauss/ruoyi-vue-pro.sql:/tmp/schema.sql:ro - # docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql' \ No newline at end of file diff --git a/sql/tools/oracle/1_create_user.sql b/sql/tools/oracle/1_create_user.sql deleted file mode 100644 index 58c9658..0000000 --- a/sql/tools/oracle/1_create_user.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER SESSION SET CONTAINER=XEPDB1; -CREATE USER ROOT IDENTIFIED BY 123456 QUOTA UNLIMITED ON USERS; -GRANT CONNECT, RESOURCE TO ROOT; diff --git a/sql/tools/oracle/2_create_schema.sh b/sql/tools/oracle/2_create_schema.sh deleted file mode 100644 index ce7955d..0000000 --- a/sql/tools/oracle/2_create_schema.sh +++ /dev/null @@ -1 +0,0 @@ -sqlplus -s ROOT/123456@//localhost/XEPDB1 @/tmp/schema.sql diff --git a/sql/tools/sqlserver/create_schema.sh b/sql/tools/sqlserver/create_schema.sh deleted file mode 100644 index 172650b..0000000 --- a/sql/tools/sqlserver/create_schema.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${SA_PASSWORD} -Q "CREATE DATABASE [ruoyi-vue-pro]; -GO" -/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${SA_PASSWORD} -d 'ruoyi-vue-pro' -i /tmp/schema.sql diff --git a/tashow-dependencies/pom.xml b/tashow-dependencies/pom.xml index c427091..a1f0ce2 100644 --- a/tashow-dependencies/pom.xml +++ b/tashow-dependencies/pom.xml @@ -34,6 +34,8 @@ 2.3.1 + 2.7.0 + 4.6.0 2.4.0 @@ -150,6 +152,11 @@ tashow-data-permission ${revision} + + com.tashow.cloud + tashow-sdk-file + ${revision} + @@ -397,6 +404,22 @@ ${spring-boot-admin.version} + + + com.github.xingfudeshi + knife4j-openapi3-jakarta-spring-boot-starter + ${knife4j.version} + + + org.springdoc + springdoc-openapi-starter-webmvc-api + ${springdoc.version} + + + com.github.xiaoymin + knife4j-gateway-spring-boot-starter + 4.5.0 + org.mockito diff --git a/tashow-feign/tashow-infra-api/src/main/java/com/tashow/cloud/infraapi/api/file/FileApi.java b/tashow-feign/tashow-infra-api/src/main/java/com/tashow/cloud/infraapi/api/file/FileApi.java index 2ecf928..cbbcd25 100644 --- a/tashow-feign/tashow-infra-api/src/main/java/com/tashow/cloud/infraapi/api/file/FileApi.java +++ b/tashow-feign/tashow-infra-api/src/main/java/com/tashow/cloud/infraapi/api/file/FileApi.java @@ -9,50 +9,54 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; +/** + * RPC 服务 - 文件 + */ @FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = -/** RPC 服务 - 文件 */ public interface FileApi { - String PREFIX = ApiConstants.PREFIX + "/file"; + String PREFIX = ApiConstants.PREFIX + "/file"; - /** - * 保存文件,并返回文件的访问路径 - * - * @param content 文件内容 - * @return 文件路径 - */ - default String createFile(byte[] content) { - return createFile(null, null, content); - } + /** + * 保存文件,并返回文件的访问路径 + * + * @param content 文件内容 + * @return 文件路径 + */ + default String createFile(byte[] content) { + return createFile(null, null, content); + } - /** - * 保存文件,并返回文件的访问路径 - * - * @param path 文件路径 - * @param content 文件内容 - * @return 文件路径 - */ - default String createFile(String path, byte[] content) { - return createFile(null, path, content); - } + /** + * 保存文件,并返回文件的访问路径 + * + * @param path 文件路径 + * @param content 文件内容 + * @return 文件路径 + */ + default String createFile(String path, byte[] content) { + return createFile(null, path, content); + } - /** - * 保存文件,并返回文件的访问路径 - * - * @param name 原文件名称 - * @param path 文件路径 - * @param content 文件内容 - * @return 文件路径 - */ - default String createFile( - @RequestParam("name") String name, - @RequestParam("path") String path, - @RequestParam("content") byte[] content) { - return createFile(new FileCreateReqDTO().setName(name).setPath(path).setContent(content)) - .getCheckedData(); - } + /** + * 保存文件,并返回文件的访问路径 + * + * @param name 原文件名称 + * @param path 文件路径 + * @param content 文件内容 + * @return 文件路径 + */ + default String createFile( + @RequestParam("name") String name, + @RequestParam("path") String path, + @RequestParam("content") byte[] content) { + return createFile(new FileCreateReqDTO().setName(name).setPath(path).setContent(content)) + .getCheckedData(); + } - @PostMapping(PREFIX + "/create") - /** 保存文件,并返回文件的访问路径 */ - CommonResult createFile(@Valid @RequestBody FileCreateReqDTO createReqDTO); + /** + * 保存文件,并返回文件的访问路径 + */ + @PostMapping(PREFIX + "/create") + CommonResult createFile(@Valid @RequestBody FileCreateReqDTO createReqDTO); } diff --git a/tashow-framework/tashow-framework-web/pom.xml b/tashow-framework/tashow-framework-web/pom.xml index cbe0fa2..0c14595 100644 --- a/tashow-framework/tashow-framework-web/pom.xml +++ b/tashow-framework/tashow-framework-web/pom.xml @@ -27,6 +27,15 @@ true + + com.github.xingfudeshi + knife4j-openapi3-jakarta-spring-boot-starter + + + org.springdoc + springdoc-openapi-starter-webmvc-api + + org.springframework.boot diff --git a/tashow-gateway/src/main/resources/application-local.yaml b/tashow-gateway/src/main/resources/application-local.yaml index 8756703..59b8b16 100644 --- a/tashow-gateway/src/main/resources/application-local.yaml +++ b/tashow-gateway/src/main/resources/application-local.yaml @@ -7,10 +7,10 @@ spring: username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境 + namespace: dev # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP config: # 【注册中心】配置项 - namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境 + namespace: dev # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP # 日志文件配置 diff --git a/tashow-module/pom.xml b/tashow-module/pom.xml index ffdb6c5..0f3180f 100644 --- a/tashow-module/pom.xml +++ b/tashow-module/pom.xml @@ -15,6 +15,7 @@ tashow-module-infra tashow-module-app tashow-module-product + tashow-module-ai diff --git a/tashow-module/tashow-module-translate/pom.xml b/tashow-module/tashow-module-ai/pom.xml similarity index 86% rename from tashow-module/tashow-module-translate/pom.xml rename to tashow-module/tashow-module-ai/pom.xml index 05580eb..d45ca14 100644 --- a/tashow-module/tashow-module-translate/pom.xml +++ b/tashow-module/tashow-module-ai/pom.xml @@ -7,7 +7,7 @@ ${revision} - tashow-module-translate + tashow-module-ai jar @@ -23,6 +23,11 @@ spring-cloud-starter-alibaba-nacos-config + + + com.tashow.cloud + tashow-infra-api + com.tashow.cloud @@ -40,11 +45,6 @@ com.tashow.cloud tashow-framework-env - - com.tashow.cloud - tashow-infra-api - - com.tashow.cloud tashow-framework-websocket @@ -53,6 +53,10 @@ com.tashow.cloud tashow-data-redis + + com.tashow.cloud + tashow-data-excel + com.tashow.cloud tashow-framework-security @@ -61,5 +65,11 @@ org.springframework.boot spring-boot-starter-actuator + + com.tashow.cloud + tashow-sdk-file + + + diff --git a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/TranslateServerApplication.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/TranslateServerApplication.java similarity index 92% rename from tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/TranslateServerApplication.java rename to tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/TranslateServerApplication.java index 8d2c05c..fc775be 100644 --- a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/TranslateServerApplication.java +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/TranslateServerApplication.java @@ -1,4 +1,4 @@ -package com.tashow.cloud.translate; +package com.tashow.cloud.ai; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleController.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleController.java new file mode 100644 index 0000000..a99c39d --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleController.java @@ -0,0 +1,78 @@ +package com.tashow.cloud.ai.controller.admin.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.*; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO; +import com.tashow.cloud.ai.service.aisample.AiSampleService; +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.tashow.cloud.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 样本库") +@RestController +@RequestMapping("/ai/sample") +@Validated +public class AiSampleController { + + @Resource + private AiSampleService aiSampleService; + + @PostMapping("/create") + @Operation(summary = "创建样本库-上传文件") + @PreAuthorize("@ss.hasPermission('ai:sample:create')") + public CommonResult> createAiSample(FileUploadReqVO uploadReqVO) { + return success(aiSampleService.createAiSample(uploadReqVO)); + } + + @PutMapping("/updates") + @Operation(summary = "更新样本库") + @PreAuthorize("@ss.hasPermission('ai:sample:update')") + public CommonResult updateAiSample(@Valid @RequestBody List updateReqVO) { + aiSampleService.updateAiSamples(updateReqVO); + return success(true); + } + + @PutMapping("/relate") + @Operation(summary = "添加关联标签") + @PreAuthorize("@ss.hasPermission('ai:sample:update')") + public CommonResult relate(@Valid @RequestBody AiSampleRelateTagVO relateTagVO) { + aiSampleService.relate(relateTagVO); + return success(true); + } + + @DeleteMapping("/deleteRelate") + @Operation(summary = "删除关联标签") + @PreAuthorize("@ss.hasPermission('ai:sample:delete')") + public CommonResult deleteRelate(@Valid @RequestBody AiSampleRelateTagVO relateTagVO) { + aiSampleService.deleteRelate(relateTagVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除样本库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('ai:sample:delete')") + public CommonResult deleteAiSample(@RequestParam("id") Long id) { + aiSampleService.deleteAiSample(id); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得样本库分页") + @PreAuthorize("@ss.hasPermission('ai:sample:query')") + public CommonResult> getAiSamplePage(@Valid AiSamplePageReqVO pageReqVO) { + PageResult pageResult = aiSampleService.getAiSamplePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiSampleRespVO.class)); + } +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleTagController.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleTagController.java new file mode 100644 index 0000000..8281f67 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleTagController.java @@ -0,0 +1,74 @@ +package com.tashow.cloud.ai.controller.admin.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagListRespVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagPageReqVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagRespVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; +import com.tashow.cloud.ai.service.aisample.AiSampleTagService; +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.tashow.cloud.common.pojo.CommonResult.success; + + +@Tag(name = "管理后台 - 样本标签库") +@RestController +@RequestMapping("/ai/sampleTag") +@Validated +public class AiSampleTagController { + + @Resource + private AiSampleTagService aiSampleTagService; + + @PostMapping("/create") + @Operation(summary = "创建样本标签库") + @PreAuthorize("@ss.hasPermission('ai:sampleTag:create')") + public CommonResult createAiSampleTag(@Valid @RequestBody AiSampleTagSaveReqVO createReqVO) { + return success(aiSampleTagService.createAiSampleTag(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新样本标签库") + @PreAuthorize("@ss.hasPermission('ai:sampleTag:update')") + public CommonResult updateAiSampleTag(@Valid @RequestBody AiSampleTagSaveReqVO updateReqVO) { + aiSampleTagService.updateAiSampleTag(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除样本标签库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('ai:sampleTag:delete')") + public CommonResult deleteAiSampleTag(@RequestParam("id") Long id) { + aiSampleTagService.deleteAiSampleTag(id); + return success(true); + } + + @GetMapping("/list") + @Operation(summary = "获得样本标签库列表") + @PreAuthorize("@ss.hasPermission('ai:sampleTag:query')") + public CommonResult> getAiSampleTagList() { + return success(BeanUtils.toBean(aiSampleTagService.getAiSampleTagList(), AiSampleTagListRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得样本标签库分页") + @PreAuthorize("@ss.hasPermission('ai:sampleTag:query')") + public CommonResult> getAiSampleTagPage(@Valid AiSampleTagPageReqVO pageReqVO) { + PageResult pageResult = aiSampleTagService.getAiSampleTagPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiSampleTagRespVO.class)); + } + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleTagGroupController.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleTagGroupController.java new file mode 100644 index 0000000..1d04f00 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/AiSampleTagGroupController.java @@ -0,0 +1,64 @@ +package com.tashow.cloud.ai.controller.admin.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupRespVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagGroupDO; +import com.tashow.cloud.ai.service.aisample.AiSampleTagGroupService; +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.tashow.cloud.common.pojo.CommonResult.success; + + +@Tag(name = "管理后台 - 样本标签分组库") +@RestController +@RequestMapping("/ai/sampleTagGroup") +@Validated +public class AiSampleTagGroupController { + + @Resource + private AiSampleTagGroupService aiSampleTagGroupService; + + @PostMapping("/create") + @Operation(summary = "创建样本标签分组库") + @PreAuthorize("@ss.hasPermission('ai:sampleTagGroup:create')") + public CommonResult createAiSampleTagGroup(@Valid @RequestBody AiSampleTagGroupSaveReqVO createReqVO) { + return success(aiSampleTagGroupService.createAiSampleTagGroup(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新样本标签分组库") + @PreAuthorize("@ss.hasPermission('ai:sampleTagGroup:update')") + public CommonResult updateAiSampleTagGroup(@Valid @RequestBody AiSampleTagGroupSaveReqVO updateReqVO) { + aiSampleTagGroupService.updateAiSampleTagGroup(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除样本标签分组库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('ai:sampleTagGroup:delete')") + public CommonResult deleteAiSampleTagGroup(@RequestParam("id") Long id) { + aiSampleTagGroupService.deleteAiSampleTagGroup(id); + return success(true); + } + + @GetMapping("/list") + @Operation(summary = "获得样本标签分组库列表") + @PreAuthorize("@ss.hasPermission('ai:sampleTagGroup:query')") + public CommonResult> getAiSampleTagGroupPage() { + List aiSampleTagGroupList = aiSampleTagGroupService.getAiSampleTagGroupList(); + return success(BeanUtils.toBean(aiSampleTagGroupList, AiSampleTagGroupRespVO.class)); + } + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSamplePageReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSamplePageReqVO.java new file mode 100644 index 0000000..d0459cc --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSamplePageReqVO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import com.tashow.cloud.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 样本库分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AiSamplePageReqVO extends PageParam { + + @Schema(description = "标签id", example = "25839") + private Long tagId; + + @Schema(description = "样本名称", example = "张三") + private String sampleName; + + @Schema(description = "样本格式", example = "1") + private String sampleMineType; + + @Schema(description = "排序", example = "正序:asc,倒序:desc") + private String sort; + + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleRelateTagVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleRelateTagVO.java new file mode 100644 index 0000000..cf0d422 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleRelateTagVO.java @@ -0,0 +1,17 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 样本库新增/修改 Request VO") +@Data +public class AiSampleRelateTagVO { + + @Schema(description = "标签id") + private Long tagId; + + @Schema(description = "样本ids") + private List sampleIds; +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleRespVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleRespVO.java new file mode 100644 index 0000000..5adcaa1 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleRespVO.java @@ -0,0 +1,45 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 样本库 Response VO") +@Data +@ExcelIgnoreUnannotated +public class AiSampleRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "7701") + private Long id; + + @Schema(description = "标签列表") + private List tags; + + @Schema(description = "样本文件地址") + private String sampleFilePath; + + @Schema(description = "样本名称", example = "张三") + private String sampleName; + + @Schema(description = "样本时长") + private String sampleTime; + + @Schema(description = "样本格式", example = "1") + private String sampleMineType; + + @Schema(description = "样本大小") + private String sampleSize; + + @Schema(description = "样本注释", example = "随便") + private String remark; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + @Schema(description = "修改时间") + private LocalDateTime updateTime; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleSaveReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleSaveReqVO.java new file mode 100644 index 0000000..e764e90 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleSaveReqVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 样本库新增/修改 Request VO") +@Data +public class AiSampleSaveReqVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "7701") + private Long id; + + @Schema(description = "样本名称", example = "张三") + private String sampleName; + + @Schema(description = "样本注释", example = "随便") + private String remark; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupPageReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupPageReqVO.java new file mode 100644 index 0000000..bc14d11 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupPageReqVO.java @@ -0,0 +1,27 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import com.tashow.cloud.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 样本标签分组库分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AiSampleTagGroupPageReqVO extends PageParam { + + @Schema(description = "分组名称", example = "张三") + private String groupName; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupRespVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupRespVO.java new file mode 100644 index 0000000..5b332fe --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupRespVO.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; + +@Schema(description = "管理后台 - 样本标签分组库 Response VO") +@Data +@ExcelIgnoreUnannotated +public class AiSampleTagGroupRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "18784") + @ExcelProperty("主键") + private Long id; + + @Schema(description = "分组名称", example = "张三") + @ExcelProperty("分组名称") + private String groupName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupSaveReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupSaveReqVO.java new file mode 100644 index 0000000..ef4da7a --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagGroupSaveReqVO.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +@Schema(description = "管理后台 - 样本标签分组库新增/修改 Request VO") +@Data +public class AiSampleTagGroupSaveReqVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "18784") + private Long id; + + @Schema(description = "分组名称", example = "张三") + private String groupName; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagListRespVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagListRespVO.java new file mode 100644 index 0000000..47c452a --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagListRespVO.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 样本标签库 Response VO") +@Data +@ExcelIgnoreUnannotated +public class AiSampleTagListRespVO { + + @Schema(description = "标签id") + private Long id; + @Schema(description = "标签名称") + private String tagName; +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagPageReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagPageReqVO.java new file mode 100644 index 0000000..153087b --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagPageReqVO.java @@ -0,0 +1,18 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import com.tashow.cloud.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 样本标签库分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AiSampleTagPageReqVO extends PageParam { + + @Schema(description = "分组id", example = "张三") + private Long groupId; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagRespVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagRespVO.java new file mode 100644 index 0000000..db35832 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagRespVO.java @@ -0,0 +1,20 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagGroupDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 样本标签库 Response VO") +@Data +@ExcelIgnoreUnannotated +public class AiSampleTagRespVO { + + @Schema(description = "标签列表") + List tags; + @Schema(description = "分组列表") + List tagGroupDOS; +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagSaveReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagSaveReqVO.java new file mode 100644 index 0000000..4fba8a1 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/AiSampleTagSaveReqVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 样本标签库新增/修改 Request VO") +@Data +public class AiSampleTagSaveReqVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32538") + private Long id; + + @Schema(description = "分组ids") + private List groupIds; + + @Schema(description = "标签名称", example = "张三") + private String tagName; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/FileUploadReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/FileUploadReqVO.java new file mode 100644 index 0000000..233bfca --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/aisample/vo/FileUploadReqVO.java @@ -0,0 +1,15 @@ +package com.tashow.cloud.ai.controller.admin.aisample.vo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +/** 管理后台 - 上传文件 Request VO */ +@Data +public class FileUploadReqVO { + + /** 文件附件 */ + @NotNull(message = "文件附件不能为空") + private MultipartFile[] files; + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/package-info.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/package-info.java new file mode 100644 index 0000000..15f00f8 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/package-info.java @@ -0,0 +1 @@ +package com.tashow.cloud.ai.controller.admin; \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/app/package-info.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/app/package-info.java new file mode 100644 index 0000000..80d118b --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/app/package-info.java @@ -0,0 +1 @@ +package com.tashow.cloud.ai.controller.app; \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/app/translate/TranslateController.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/app/translate/TranslateController.java new file mode 100644 index 0000000..3849fda --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/app/translate/TranslateController.java @@ -0,0 +1,17 @@ +package com.tashow.cloud.ai.controller.app.translate; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/ai/sample") +@Validated +@Slf4j +public class TranslateController { + + + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/package-info.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/package-info.java new file mode 100644 index 0000000..4a424d2 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/package-info.java @@ -0,0 +1 @@ +package com.tashow.cloud.ai.controller; \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleDO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleDO.java new file mode 100644 index 0000000..ae22fe3 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleDO.java @@ -0,0 +1,61 @@ +package com.tashow.cloud.ai.dal.dataobject.aisample; + +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO; +import lombok.*; + +import java.util.List; + +/** + * 样本库 DO + * + * @author 芋道源码 + */ +@TableName("tz_ai_sample") +@KeySequence("tz_ai_sample_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiSampleDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + + @TableField(exist = false) + private List tags; + + /** + * 样本文件id + */ + private String sampleFilePath; + /** + * 样本名称 + */ + private String sampleName; + /** + * 样本时长 + */ + private String sampleTime; + /** + * 样本格式 + */ + private String sampleMineType; + /** + * 样本大小 + */ + private Long sampleSize; + /** + * 样本注释 + */ + private String remark; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagDO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagDO.java new file mode 100644 index 0000000..721a716 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagDO.java @@ -0,0 +1,34 @@ +package com.tashow.cloud.ai.dal.dataobject.aisample; + +import lombok.*; +import com.baomidou.mybatisplus.annotation.*; +import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO; + +/** + * 样本标签库 DO + * + * @author 芋道源码 + */ +@TableName("tz_ai_sample_tag") +@KeySequence("tz_ai_sample_tag_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiSampleTagDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + + /** + * 标签名称 + */ + private String tagName; + + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagGroupDO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagGroupDO.java new file mode 100644 index 0000000..ecf0b47 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagGroupDO.java @@ -0,0 +1,34 @@ +package com.tashow.cloud.ai.dal.dataobject.aisample; + +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO; +import lombok.*; + +/** + * 样本标签分组库 DO + * + * @author 芋道源码 + */ +@TableName("tz_ai_sample_tag_group") +@KeySequence("tz_ai_sample_tag_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiSampleTagGroupDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 分组名称 + */ + private String groupName; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagGroupRelateDO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagGroupRelateDO.java new file mode 100644 index 0000000..7a7f5f5 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagGroupRelateDO.java @@ -0,0 +1,38 @@ +package com.tashow.cloud.ai.dal.dataobject.aisample; + +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO; +import lombok.*; + +/** + * 样本标签-分组关联 DO + * + * @author 芋道源码 + */ +@TableName("tz_ai_sample_tag_group_relate") +@KeySequence("tz_ai_sample_tag_group_relate_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiSampleTagGroupRelateDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 样本标签id + */ + private Long sampleTagId; + /** + * 样本标签分组id + */ + private Long sampleTagGroupId; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagRelateDO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagRelateDO.java new file mode 100644 index 0000000..72bf179 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/aisample/AiSampleTagRelateDO.java @@ -0,0 +1,36 @@ +package com.tashow.cloud.ai.dal.dataobject.aisample; + +import lombok.*; +import com.baomidou.mybatisplus.annotation.*; +import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO; + +/** + * 样本-标签关联 DO + * + * @author 芋道源码 + */ +@TableName("tz_ai_sample_tag_relate") +@KeySequence("tz_ai_sample_tag_relate_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiSampleTagRelateDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 样本id + */ + private Long sampleId; + /** + * 样本标签id + */ + private Long sampleTagId; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/package-info.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/package-info.java new file mode 100644 index 0000000..0a55678 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/package-info.java @@ -0,0 +1,2 @@ +package com.tashow.cloud.ai.dal.dataobject; +// 数据库对象 \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleMapper.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleMapper.java new file mode 100644 index 0000000..082ae0e --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleMapper.java @@ -0,0 +1,29 @@ +package com.tashow.cloud.ai.dal.mysql.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSamplePageReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 样本库 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiSampleMapper extends BaseMapperX { + + default PageResult selectPage(AiSamplePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(AiSampleDO::getSampleFilePath, reqVO.getSampleFilePath()) + .likeIfPresent(AiSampleDO::getSampleName, reqVO.getSampleName()) + .betweenIfPresent(AiSampleDO::getSampleTime, reqVO.getSampleTime()) + .eqIfPresent(AiSampleDO::getSampleMineType, reqVO.getSampleMineType()) + .eqIfPresent(AiSampleDO::getSampleSize, reqVO.getSampleSize()) + .eqIfPresent(AiSampleDO::getRemark, reqVO.getRemark()) + .betweenIfPresent(AiSampleDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(AiSampleDO::getId)); + } +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagGroupMapper.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagGroupMapper.java new file mode 100644 index 0000000..9f0404a --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagGroupMapper.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.ai.dal.mysql.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupPageReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagGroupDO; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 样本标签分组库 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiSampleTagGroupMapper extends BaseMapperX { + + default PageResult selectPage(AiSampleTagGroupPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiSampleTagGroupDO::getGroupName, reqVO.getGroupName()) + .betweenIfPresent(AiSampleTagGroupDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(AiSampleTagGroupDO::getId)); + } + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagGroupRelateMapper.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagGroupRelateMapper.java new file mode 100644 index 0000000..cdf2216 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagGroupRelateMapper.java @@ -0,0 +1,15 @@ +package com.tashow.cloud.ai.dal.mysql.aisample; + +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagGroupRelateDO; +import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 样本标签-分组关联 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiSampleTagGroupRelateMapper extends BaseMapperX { + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagMapper.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagMapper.java new file mode 100644 index 0000000..98fa0de --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagMapper.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.ai.dal.mysql.aisample; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagPageReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +/** + * 样本标签库 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiSampleTagMapper extends BaseMapperX { + + default PageResult selectPage(AiSampleTagPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiSampleTagDO::getTagName, reqVO.getTagName()) + .betweenIfPresent(AiSampleTagDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(AiSampleTagDO::getId)); + } + + @Select("SELECT t.* FROM tz_ai_sample_tag t INNER JOIN tz_ai_sample_tag_group_relate r ON t.id = r.sample_tag_id \n" + + " WHERE r.sample_tag_group_id = #{groupId} ") + IPage getAiSampleTagPage(Page page, AiSampleTagPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagRelateMapper.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagRelateMapper.java new file mode 100644 index 0000000..90e0336 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/aisample/AiSampleTagRelateMapper.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.ai.dal.mysql.aisample; + +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagRelateDO; +import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 样本-标签关联 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiSampleTagRelateMapper extends BaseMapperX { + + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/package-info.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/package-info.java new file mode 100644 index 0000000..dc66f15 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/package-info.java @@ -0,0 +1 @@ +package com.tashow.cloud.ai.dal.mysql; \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/package-info.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/package-info.java new file mode 100644 index 0000000..91dc35a --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/package-info.java @@ -0,0 +1 @@ +package com.tashow.cloud.ai.dal; \ No newline at end of file diff --git a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/security/config/SecurityConfiguration.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/security/config/SecurityConfiguration.java similarity index 97% rename from tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/security/config/SecurityConfiguration.java rename to tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/security/config/SecurityConfiguration.java index 006dcbe..eb8b20d 100644 --- a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/security/config/SecurityConfiguration.java +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/security/config/SecurityConfiguration.java @@ -1,4 +1,4 @@ -package com.tashow.cloud.translate.security.config; +package com.tashow.cloud.ai.security.config; import com.tashow.cloud.infraapi.enums.ApiConstants; import com.tashow.cloud.security.security.config.AuthorizeRequestsCustomizer; diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/security/core/package-info.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/security/core/package-info.java new file mode 100644 index 0000000..66da3e4 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package com.tashow.cloud.ai.security.core; diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleService.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleService.java new file mode 100644 index 0000000..c2f788e --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleService.java @@ -0,0 +1,73 @@ +package com.tashow.cloud.ai.service.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.*; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO; +import com.tashow.cloud.common.pojo.PageResult; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * 样本库 Service 接口 + * + * @author 芋道源码 + */ +public interface AiSampleService { + + /** + * 创建样本库 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + List createAiSample(@Valid FileUploadReqVO createReqVO); + + /** + * 更新样本库 + * + * @param updateReqVO 更新信息 + */ + void updateAiSample(@Valid AiSampleSaveReqVO updateReqVO); + + /** + * 批量更新 + * @param updateReqVO + */ + void updateAiSamples(@Valid List updateReqVO); + + /** + * 关联标签 + * @param relateTagVO + */ + void relate(@Valid AiSampleRelateTagVO relateTagVO); + /** + * 删除样本库 + * + * @param id 编号 + */ + void deleteAiSample(Long id); + + /** + * 删除样本库 + * @param relateTagVO 编号 + */ + void deleteRelate(AiSampleRelateTagVO relateTagVO); + + /** + * 获得样本库 + * + * @param id 编号 + * @return 样本库 + */ + AiSampleDO getAiSample(Long id); + + /** + * 获得样本库分页 + * + * @param pageReqVO 分页查询 + * @return 样本库分页 + */ + PageResult getAiSamplePage(AiSamplePageReqVO pageReqVO); + + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleServiceImpl.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleServiceImpl.java new file mode 100644 index 0000000..de8739a --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleServiceImpl.java @@ -0,0 +1,156 @@ +package com.tashow.cloud.ai.service.aisample; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSamplePageReqVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleRelateTagVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleSaveReqVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.FileUploadReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagRelateDO; +import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleMapper; +import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagMapper; +import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagRelateMapper; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import com.tashow.cloud.infraapi.api.file.FileApi; +import jakarta.annotation.Resource; +import lombok.SneakyThrows; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +/** + * 样本库 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AiSampleServiceImpl implements AiSampleService { + + @Resource + private AiSampleMapper aiSampleMapper; + @Resource + private AiSampleTagMapper aiSampleTagMapper; + @Resource + private AiSampleTagRelateMapper aiSampleTagRelateMapper; + @Resource + private FileApi fileApi; + + @Override + @SneakyThrows + public List createAiSample(FileUploadReqVO uploadReqVO) { + //返回图片路径 + List urls = new ArrayList<>(); + /* 调用文件上传服务*/ + for (MultipartFile file : uploadReqVO.getFiles()) { + //返回上传结果 + String file1 ="http://192.168.1.231:48080"+ fileApi.createFile(file.getBytes()); + //保存样本信息 + AiSampleDO aiSampleDO = new AiSampleDO(); + aiSampleDO.setSampleFilePath(file1); + aiSampleDO.setSampleName(file.getOriginalFilename()); + aiSampleDO.setSampleMineType(file.getContentType()); + aiSampleDO.setSampleSize(file.getSize()); + aiSampleMapper.insert(aiSampleDO); + urls.add(file1); + } + // 返回 + return urls; + } + + @Override + public void updateAiSample(AiSampleSaveReqVO updateReqVO) { + // 校验存在 + validateAiSampleExists(updateReqVO.getId()); + // 更新 + AiSampleDO updateObj = BeanUtils.toBean(updateReqVO, AiSampleDO.class); + aiSampleMapper.updateById(updateObj); + } + + @Override + public void updateAiSamples(List updateReqVO) { + aiSampleMapper.updateBatch( BeanUtils.toBean(updateReqVO, AiSampleDO.class)); + } + + @Override + public void relate(AiSampleRelateTagVO relateTagVO) { + List tagRelateDOS = new ArrayList<>(); + for (Long sampleId : relateTagVO.getSampleIds()) { + AiSampleTagRelateDO relateDO = new AiSampleTagRelateDO(); + relateDO.setSampleId(sampleId); + relateDO.setSampleTagId(relateTagVO.getTagId()); + tagRelateDOS.add(relateDO); + } + aiSampleTagRelateMapper.insertBatch(tagRelateDOS); + } + + @Override + public void deleteRelate(AiSampleRelateTagVO relateTagVO) { + List tagRelateIds = new ArrayList<>(); + for (Long sampleId : relateTagVO.getSampleIds()) { + AiSampleTagRelateDO relateDO = aiSampleTagRelateMapper.selectOne( + new LambdaQueryWrapper() + .eq(AiSampleTagRelateDO::getSampleId, sampleId) + .eq(AiSampleTagRelateDO::getSampleTagId, relateTagVO.getTagId()) + ); + tagRelateIds.add(relateDO.getId()); + } + aiSampleTagRelateMapper.deleteBatchIds(tagRelateIds); + } + + @Override + public void deleteAiSample(Long id) { + // 校验存在 + validateAiSampleExists(id); + // 删除 + aiSampleMapper.deleteById(id); + } + + private void validateAiSampleExists(Long id) { + if (aiSampleMapper.selectById(id) == null) { +// throw exception(AI_SAMPLE_NOT_EXISTS); + } + } + + @Override + public AiSampleDO getAiSample(Long id) { + AiSampleDO aiSampleDO = aiSampleMapper.selectById(id); + aiSampleDO.setSampleFilePath("http://192.168.1.231:48080"+aiSampleDO.getSampleFilePath()); + //先获取关联的标签id + List tagRelateDOS = aiSampleTagRelateMapper.selectList(new LambdaQueryWrapper().eq(AiSampleTagRelateDO::getSampleId, id)); + List tagIds = tagRelateDOS.stream().map(AiSampleTagRelateDO::getSampleTagId).toList(); + aiSampleDO.setTags(aiSampleTagMapper.selectList(new LambdaQueryWrapper().in(AiSampleTagDO::getId,tagIds))); + return aiSampleDO; + } + + @Override + public PageResult getAiSamplePage(AiSamplePageReqVO pageReqVO) { + PageResult aiSampleDOPageResult = aiSampleMapper.selectPage(pageReqVO); + //根据样本id获取关联的标签id + List sampleIds = aiSampleDOPageResult.getList().stream().map(AiSampleDO::getId).toList(); + List tagRelateDOS = aiSampleTagRelateMapper.selectList(new LambdaQueryWrapper().in(AiSampleTagRelateDO::getSampleId, sampleIds)); + List tagIds = tagRelateDOS.stream().map(AiSampleTagRelateDO::getSampleTagId).toList(); + //获取标签信息 + List aiSampleTagDOS = aiSampleTagMapper.selectList(new LambdaQueryWrapper().in(AiSampleTagDO::getId, tagIds)); + + //封装标签信息 + for (AiSampleDO aiSampleDO : aiSampleDOPageResult.getList()) { + List list = tagRelateDOS.stream() + .filter(a -> ObjectUtil.equals(aiSampleDO.getId(), a.getSampleId())) + .toList(); + Object[] tagsId = list.stream().map(AiSampleTagRelateDO::getSampleTagId).toArray(); + List list1 = aiSampleTagDOS.stream().filter(a -> ArrayUtil.containsAny(tagsId, a.getId())).toList(); + aiSampleDO.setTags(list1); + aiSampleDO.setSampleFilePath("http://192.168.1.231:48080"+aiSampleDO.getSampleFilePath()); + } + return aiSampleDOPageResult; + } + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupRelateService.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupRelateService.java new file mode 100644 index 0000000..82081a0 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupRelateService.java @@ -0,0 +1,11 @@ +package com.tashow.cloud.ai.service.aisample; + +/** + * 样本标签-分组关联 Service 接口 + * + * @author 芋道源码 + */ +public interface AiSampleTagGroupRelateService { + + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupRelateServiceImpl.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupRelateServiceImpl.java new file mode 100644 index 0000000..4b88f1e --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupRelateServiceImpl.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.ai.service.aisample; + +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +/** + * 样本标签-分组关联 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AiSampleTagGroupRelateServiceImpl implements AiSampleTagGroupRelateService { + + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupService.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupService.java new file mode 100644 index 0000000..fa37ae6 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupService.java @@ -0,0 +1,62 @@ +package com.tashow.cloud.ai.service.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupPageReqVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupRespVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagGroupDO; +import com.tashow.cloud.common.pojo.PageResult; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * 样本标签分组库 Service 接口 + * + * @author 芋道源码 + */ +public interface AiSampleTagGroupService { + + /** + * 创建样本标签分组库 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createAiSampleTagGroup(@Valid AiSampleTagGroupSaveReqVO createReqVO); + + /** + * 更新样本标签分组库 + * + * @param updateReqVO 更新信息 + */ + void updateAiSampleTagGroup(@Valid AiSampleTagGroupSaveReqVO updateReqVO); + + /** + * 删除样本标签分组库 + * + * @param id 编号 + */ + void deleteAiSampleTagGroup(Long id); + + /** + * 获得样本标签分组库 + * + * @param id 编号 + * @return 样本标签分组库 + */ + AiSampleTagGroupDO getAiSampleTagGroup(Long id); + + /** + * 获得样本标签分组库分页 + * + * @param pageReqVO 分页查询 + * @return 样本标签分组库分页 + */ + PageResult getAiSampleTagGroupPage(AiSampleTagGroupPageReqVO pageReqVO); + + /** + * 获取标签分组列表 + * @return + */ + List getAiSampleTagGroupList(); +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupServiceImpl.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupServiceImpl.java new file mode 100644 index 0000000..21ee295 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagGroupServiceImpl.java @@ -0,0 +1,74 @@ +package com.tashow.cloud.ai.service.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupPageReqVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupRespVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagGroupDO; +import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagGroupMapper; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * 样本标签分组库 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AiSampleTagGroupServiceImpl implements AiSampleTagGroupService { + + @Resource + private AiSampleTagGroupMapper aiSampleTagGroupMapper; + + @Override + public Long createAiSampleTagGroup(AiSampleTagGroupSaveReqVO createReqVO) { + // 插入 + AiSampleTagGroupDO aiSampleTagGroup = BeanUtils.toBean(createReqVO, AiSampleTagGroupDO.class); + aiSampleTagGroupMapper.insert(aiSampleTagGroup); + // 返回 + return aiSampleTagGroup.getId(); + } + + @Override + public void updateAiSampleTagGroup(AiSampleTagGroupSaveReqVO updateReqVO) { + // 校验存在 + validateAiSampleTagGroupExists(updateReqVO.getId()); + // 更新 + AiSampleTagGroupDO updateObj = BeanUtils.toBean(updateReqVO, AiSampleTagGroupDO.class); + aiSampleTagGroupMapper.updateById(updateObj); + } + + @Override + public void deleteAiSampleTagGroup(Long id) { + // 校验存在 + validateAiSampleTagGroupExists(id); + // 删除 + aiSampleTagGroupMapper.deleteById(id); + } + + private void validateAiSampleTagGroupExists(Long id) { + if (aiSampleTagGroupMapper.selectById(id) == null) { +// throw exception(AI_SAMPLE_TAG_GROUP_NOT_EXISTS); + } + } + + @Override + public AiSampleTagGroupDO getAiSampleTagGroup(Long id) { + return aiSampleTagGroupMapper.selectById(id); + } + + @Override + public PageResult getAiSampleTagGroupPage(AiSampleTagGroupPageReqVO pageReqVO) { + return aiSampleTagGroupMapper.selectPage(pageReqVO); + } + + @Override + public List getAiSampleTagGroupList() { + return aiSampleTagGroupMapper.selectList(); + } +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagRelateService.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagRelateService.java new file mode 100644 index 0000000..d0432d0 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagRelateService.java @@ -0,0 +1,11 @@ +package com.tashow.cloud.ai.service.aisample; + +/** + * 样本-标签关联 Service 接口 + * + * @author 芋道源码 + */ +public interface AiSampleTagRelateService { + + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagRelateServiceImpl.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagRelateServiceImpl.java new file mode 100644 index 0000000..29f7360 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagRelateServiceImpl.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.ai.service.aisample; + +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +/** + * 样本-标签关联 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AiSampleTagRelateServiceImpl implements AiSampleTagRelateService { + + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagService.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagService.java new file mode 100644 index 0000000..2e2a48c --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagService.java @@ -0,0 +1,61 @@ +package com.tashow.cloud.ai.service.aisample; + +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagPageReqVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; +import com.tashow.cloud.common.pojo.PageResult; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * 样本标签库 Service 接口 + * + * @author 芋道源码 + */ +public interface AiSampleTagService { + + /** + * 创建样本标签库 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createAiSampleTag(@Valid AiSampleTagSaveReqVO createReqVO); + + /** + * 更新样本标签库 + * + * @param updateReqVO 更新信息 + */ + void updateAiSampleTag(@Valid AiSampleTagSaveReqVO updateReqVO); + + /** + * 删除样本标签库 + * + * @param id 编号 + */ + void deleteAiSampleTag(Long id); + + /** + * 获得样本标签库 + * + * @param id 编号 + * @return 样本标签库 + */ + AiSampleTagDO getAiSampleTag(Long id); + + /** + * 获得样本标签库分页 + * + * @param pageReqVO 分页查询 + * @return 样本标签库分页 + */ + PageResult getAiSampleTagPage(AiSampleTagPageReqVO pageReqVO); + + /** + * 获取样本标签列表 + * @return + */ + List getAiSampleTagList(); +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagServiceImpl.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagServiceImpl.java new file mode 100644 index 0000000..d36eece --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/aisample/AiSampleTagServiceImpl.java @@ -0,0 +1,102 @@ +package com.tashow.cloud.ai.service.aisample; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagPageReqVO; +import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; +import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagGroupRelateDO; +import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagGroupRelateMapper; +import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagMapper; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import com.tashow.cloud.mybatis.mybatis.core.util.MyBatisUtils; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.ArrayList; +import java.util.List; + +/** + * 样本标签库 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AiSampleTagServiceImpl implements AiSampleTagService { + + @Resource + private AiSampleTagMapper aiSampleTagMapper; + @Resource + private AiSampleTagGroupRelateMapper tagGroupRelateMapper; + + @Override + public Long createAiSampleTag(AiSampleTagSaveReqVO createReqVO) { + // 插入 + AiSampleTagDO aiSampleTag = BeanUtils.toBean(createReqVO, AiSampleTagDO.class); + aiSampleTagMapper.insert(aiSampleTag); + //插入关联 + List tagGroupRelateDOS = new ArrayList<>(); + for (Long groupId : createReqVO.getGroupIds()) { + AiSampleTagGroupRelateDO tagGroupRelateDO = new AiSampleTagGroupRelateDO(); + tagGroupRelateDO.setSampleTagId(createReqVO.getId()); + tagGroupRelateDO.setSampleTagGroupId(groupId); + tagGroupRelateDOS.add(tagGroupRelateDO); + } + tagGroupRelateMapper.insertBatch(tagGroupRelateDOS); + // 返回 + return aiSampleTag.getId(); + } + + @Override + public void updateAiSampleTag(AiSampleTagSaveReqVO updateReqVO) { + // 校验存在 + validateAiSampleTagExists(updateReqVO.getId()); + // 更新 + AiSampleTagDO updateObj = BeanUtils.toBean(updateReqVO, AiSampleTagDO.class); + aiSampleTagMapper.updateById(updateObj); + //删除所有关联 + tagGroupRelateMapper.delete(new LambdaQueryWrapper().eq(AiSampleTagGroupRelateDO::getSampleTagId, updateReqVO.getId())); + //插入关联 + List tagGroupRelateDOS = new ArrayList<>(); + for (Long groupId : updateReqVO.getGroupIds()) { + AiSampleTagGroupRelateDO tagGroupRelateDO = new AiSampleTagGroupRelateDO(); + tagGroupRelateDO.setSampleTagId(updateReqVO.getId()); + tagGroupRelateDO.setSampleTagGroupId(groupId); + tagGroupRelateDOS.add(tagGroupRelateDO); + } + tagGroupRelateMapper.insertBatch(tagGroupRelateDOS); + } + + @Override + public void deleteAiSampleTag(Long id) { + // 校验存在 + validateAiSampleTagExists(id); + // 删除 + aiSampleTagMapper.deleteById(id); + } + + private void validateAiSampleTagExists(Long id) { + if (aiSampleTagMapper.selectById(id) == null) { +// throw exception(AI_SAMPLE_TAG_NOT_EXISTS); + } + } + + @Override + public AiSampleTagDO getAiSampleTag(Long id) { + return aiSampleTagMapper.selectById(id); + } + + @Override + public PageResult getAiSampleTagPage(AiSampleTagPageReqVO pageReqVO) { + IPage prodPageList = aiSampleTagMapper.getAiSampleTagPage(MyBatisUtils.buildPage(pageReqVO),pageReqVO); + return new PageResult<>(prodPageList.getRecords(), prodPageList.getTotal()); + } + + @Override + public List getAiSampleTagList() { + return aiSampleTagMapper.selectList(); + } +} \ No newline at end of file diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/package-info.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/package-info.java new file mode 100644 index 0000000..72ec8fd --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/package-info.java @@ -0,0 +1 @@ +package com.tashow.cloud.ai.service; \ No newline at end of file diff --git a/tashow-module/tashow-module-translate/src/main/resources/application-local.yaml b/tashow-module/tashow-module-ai/src/main/resources/application-local.yaml similarity index 74% rename from tashow-module/tashow-module-translate/src/main/resources/application-local.yaml rename to tashow-module/tashow-module-ai/src/main/resources/application-local.yaml index 79777cb..4f2c22e 100644 --- a/tashow-module/tashow-module-translate/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-ai/src/main/resources/application-local.yaml @@ -7,10 +7,14 @@ spring: username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: 5c8b8fe6-9a89-4ae3-975e-ef3bf560ff82 # 命名空间。这里使用 dev 开发环境 + namespace: dev # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: 5c8b8fe6-9a89-4ae3-975e-ef3bf560ff82 # 命名空间。这里使用 dev 开发环境 + namespace: dev # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + +ufop: + storage-type: 0 + local-storage-path: D://test \ No newline at end of file diff --git a/tashow-module/tashow-module-translate/src/main/resources/application.yaml b/tashow-module/tashow-module-ai/src/main/resources/application.yaml similarity index 100% rename from tashow-module/tashow-module-translate/src/main/resources/application.yaml rename to tashow-module/tashow-module-ai/src/main/resources/application.yaml diff --git a/tashow-module/tashow-module-ai/src/main/resources/mapper/demo.xml b/tashow-module/tashow-module-ai/src/main/resources/mapper/demo.xml new file mode 100644 index 0000000..e69de29 diff --git a/tashow-module/tashow-module-infra/src/main/java/com/tashow/cloud/infra/framework/file/core/client/local/LocalFileClient.java b/tashow-module/tashow-module-infra/src/main/java/com/tashow/cloud/infra/framework/file/core/client/local/LocalFileClient.java index aa99e0a..422e27a 100644 --- a/tashow-module/tashow-module-infra/src/main/java/com/tashow/cloud/infra/framework/file/core/client/local/LocalFileClient.java +++ b/tashow-module/tashow-module-infra/src/main/java/com/tashow/cloud/infra/framework/file/core/client/local/LocalFileClient.java @@ -30,7 +30,7 @@ public class LocalFileClient extends AbstractFileClient { String filePath = getFilePath(path); FileUtil.writeBytes(content, filePath); // 拼接返回路径 - return super.formatFileUrl(config.getDomain(), path); + return super.formatFileUrl("", path); } @Override diff --git a/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml b/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml index 15b2156..a781954 100644 --- a/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml @@ -7,11 +7,11 @@ spring: username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: 76667956-2ac2-4e05-906b-4bca4ebcc5f0 # 命名空间。这里使用 dev 开发环境 + namespace: dev # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: 76667956-2ac2-4e05-906b-4bca4ebcc5f0 # 命名空间。这里使用 dev 开发环境 + namespace: dev # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP diff --git a/tashow-module/tashow-module-system/src/main/resources/application-local.yaml b/tashow-module/tashow-module-system/src/main/resources/application-local.yaml index a4d2d57..03bada2 100644 --- a/tashow-module/tashow-module-system/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-system/src/main/resources/application-local.yaml @@ -5,11 +5,11 @@ spring: username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境 + namespace: dev # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境 + namespace: dev # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP diff --git a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/controller/package-info.java b/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/controller/package-info.java deleted file mode 100644 index ae958ed..0000000 --- a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/controller/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.tashow.cloud.translate.controller; \ No newline at end of file diff --git a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/dal/dataobject/package-info.java b/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/dal/dataobject/package-info.java deleted file mode 100644 index 0a104a6..0000000 --- a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/dal/dataobject/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -package com.tashow.cloud.translate.dal.dataobject; -// 数据库对象 \ No newline at end of file diff --git a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/dal/package-info.java b/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/dal/package-info.java deleted file mode 100644 index 270fb32..0000000 --- a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/dal/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.tashow.cloud.translate.dal; \ No newline at end of file diff --git a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/security/core/package-info.java b/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/security/core/package-info.java deleted file mode 100644 index 1f91ce3..0000000 --- a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/security/core/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位 - */ -package com.tashow.cloud.translate.security.core; diff --git a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/service/feishu/package-info.java b/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/service/feishu/package-info.java deleted file mode 100644 index b53383d..0000000 --- a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/service/feishu/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.tashow.cloud.translate.service.feishu; \ No newline at end of file diff --git a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/service/package-info.java b/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/service/package-info.java deleted file mode 100644 index 591023a..0000000 --- a/tashow-module/tashow-module-translate/src/main/java/com/tashow/cloud/translate/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.tashow.cloud.translate.service; \ No newline at end of file diff --git a/tashow-sdk/pom.xml b/tashow-sdk/pom.xml index 58ee94c..258e341 100644 --- a/tashow-sdk/pom.xml +++ b/tashow-sdk/pom.xml @@ -12,6 +12,7 @@ tashow-sdk-payment + tashow-sdk-file tashow-feishu-sdk