2 Commits

Author SHA1 Message Date
xuelijun
ef99dbe035 店铺信息 2025-10-16 15:27:17 +08:00
4ae13cf1e6 解决启动问题 2025-09-24 11:36:14 +08:00
108 changed files with 1290 additions and 2632 deletions

48
.cursor/rules/1.mdc Normal file
View File

@@ -0,0 +1,48 @@
---
description:
globs:
alwaysApply: false
---
---
description:
globs:
alwaysApply: false
---
# Your rule content
#角色
你是一名精通开发的高级工程师拥有10年以上的应用开发经验熟悉*等开发工具和技术栈。
你的任务是帮助用户设计和开发易用且易于推护的 *** 应用。始终遵循最佳实践,并坚持干净代码和健壮架构的原则。
#目标
你的目标是以用户容易理解的方式帮助他们完成“应用的设计和开发工作,确保应用功能完善、性能优异、用户体验良好。
#要求
在理解用户需求、设计UI、编写代码、解决问题和项目选代优化时你应该始终遵循以下原则:
##需求理解
-充分理解用户需求,站在用户角度思考,分析需求是否存在缺漏,并与用户讨论完善需求;
-选择最简单的解决方案来满足用户需求,避免过度设计。
##UI和样式设计
-使用现代UI框架进行样式设计(例如***这里可以根据不同开发项目仔纽展开比如使用哪些视觉规范或者UI框架没有的话也可以不用过多展开);
-在不同平台上实现一致的设计和响应式模式
##代码编写
技术选型:根据项目需求选择合适的技术栈(例如***,这里需要仔细展开,比如介招某个技术栈用在什么地方,以及要遵循什么最佳实践)
代码结构:强调代码的清晰性、模块化、可维护性,遵循最佳实践(如DRY原则、最小权限原则、响应式设计等)
-代码安全性:在编写代码时,始终考虑安全性,避免引入漏洞,确保用户输入的安全处理
-性能优化:优化代码的性能,减少资源占用,提升加载速度,确保项目的高效运行
-测试与文档:编写单元测试,确保代码的健壮性,并提供清晰的中文注释和文档。方便后续阅读和维护
##问题解决
-全面阅读相关代码,理解***应用的工作原理
-根据用户的反馈分析问题的原因,提出解决问题的思路
-确保每次代码变更不会破坏现有功能,且尽可能保持最小的改动
##迭代优化
与用户保持密切沟通,根据反读调整功能和设计,确保应用符合用户需求
在不确定需求时,主动询问用户以澄清需求或技术细节
##方法论
-系统2思维:以分析严谨的方式解决问题。将需求分解为更小、可管理的部分,并在实施前仔细考虑每一步
思维树:评估多种可能的解决方案及其后果。使用结构化的方法探索不同的路径。并选择最优的解决方案
-选代改进:在最终确定代码之前,考虑改进、边缘情况和优化。通过潜在增强的迭代,确保最终解决方案是健壮的

2
.gitignore vendored
View File

@@ -31,8 +31,6 @@ target/*
/.nb-gradle/ /.nb-gradle/
/build/ /build/
.idea .idea
.cursor
.lingma

View File

@@ -0,0 +1,2 @@
**添加规则文件可帮助模型精准理解你的编码偏好,如框架、代码风格等**
**规则文件只对当前工程生效单文件限制10000字符。如果无需将该文件提交到远程 Git 仓库,请将其添加到 .gitignore**

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
pom.xml
View File

@@ -47,22 +47,6 @@
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<profiles>
<profile>
<id>prod</id>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>*.yaml</exclude>
</excludes>
</resource>
</resources>
</build>
</profile>
</profiles>
<build> <build>
<pluginManagement> <pluginManagement>
<plugins> <plugins>

3
sql/db2/README.md Normal file
View File

@@ -0,0 +1,3 @@
暂未适配 IBM DB2 数据库,如果你有需要,可以微信联系 wangwenbin-server 一起建设。
你需要把表结构与数据导入到 DM 数据库我a来测试与适配代码。

View File

@@ -1,113 +0,0 @@
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 = '样本标签-分组关联表';
DROP TABLE IF EXISTS `tz_ai_dialog`;
CREATE TABLE `tz_ai_dialog`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`title` varchar(128) DEFAULT '' COMMENT '对话标题',
`user_id` bigint NOT NULL COMMENT '用户id',
`dialog_status` int DEFAULT NULL COMMENT '对话状态1active, 2archived, 3deleted',
`creator` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) 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
) ENGINE = InnoDB COMMENT ='ai-对话表';
DROP TABLE IF EXISTS `tz_ai_dialog_message`;
CREATE TABLE `tz_ai_dialog_message`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`dialog_id` bigint NOT NULL COMMENT '对话id',
`content_text` text COMMENT '内容',
`content_type` int DEFAULT NULL COMMENT '文本类型1text2file',
`message_order` int DEFAULT NULL COMMENT '对话中的顺序',
`message_status` int DEFAULT NULL COMMENT '消息状态 1正常 0删除',
`pet_id` bigint DEFAULT NULL COMMENT '宠物id',
`pet_name` varchar(255) DEFAULT NULL COMMENT '宠物名称',
`pet_avatar` varchar(255) DEFAULT NULL COMMENT '宠物头像',
`pet_type` varchar(64) DEFAULT NULL COMMENT '宠物类型',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`trans_result` text COMMENT '回答结果',
`content_duration` bigint DEFAULT NULL COMMENT '文件时长',
`trans_status` int DEFAULT NULL COMMENT '翻译状态1成功 0失败',
`source_result` text COMMENT '原始结果',
`file_name` varchar(255) DEFAULT NULL COMMENT '文件名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB COMMENT ='ai-对话消息表';

8
sql/tools/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
# 忽略python虚拟环境
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

130
sql/tools/README.md Normal file
View File

@@ -0,0 +1,130 @@
## 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 镜像:<https://eco.dameng.com/download/> 地址点击“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 进行格式化。当然,也可以直接导入到数据库中。

844
sql/tools/convertor.py Normal file
View File

@@ -0,0 +1,844 @@
# 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: 字符串前加Nhack是否存在替换字符串内容的风险
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()

View File

@@ -0,0 +1,134 @@
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'

View File

@@ -0,0 +1,3 @@
ALTER SESSION SET CONTAINER=XEPDB1;
CREATE USER ROOT IDENTIFIED BY 123456 QUOTA UNLIMITED ON USERS;
GRANT CONNECT, RESOURCE TO ROOT;

View File

@@ -0,0 +1 @@
sqlplus -s ROOT/123456@//localhost/XEPDB1 @/tmp/schema.sql

View File

@@ -0,0 +1,5 @@
#!/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

View File

@@ -34,8 +34,6 @@
<rocketmq-spring.version>2.3.1</rocketmq-spring.version> <rocketmq-spring.version>2.3.1</rocketmq-spring.version>
<!-- RPC 相关 --> <!-- RPC 相关 -->
<!-- Config 配置中心相关 --> <!-- Config 配置中心相关 -->
<springdoc.version>2.7.0</springdoc.version>
<knife4j.version>4.6.0</knife4j.version>
<!-- Job 定时任务相关 --> <!-- Job 定时任务相关 -->
<xxl-job.version>2.4.0</xxl-job.version> <xxl-job.version>2.4.0</xxl-job.version>
<!-- 服务保障相关 --> <!-- 服务保障相关 -->
@@ -152,11 +150,6 @@
<artifactId>tashow-data-permission</artifactId> <artifactId>tashow-data-permission</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-sdk-file</artifactId>
<version>${revision}</version>
</dependency>
<!-- Spring 核心 --> <!-- Spring 核心 -->
<dependency> <dependency>
@@ -404,22 +397,6 @@
<version>${spring-boot-admin.version}</version> <version>${spring-boot-admin.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.xingfudeshi</groupId> <!-- TODO 芋艿https://github.com/xiaoymin/knife4j/issues/874 -->
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UIknife4j【网关专属】 -->
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>4.5.0</version> <!-- TODO 芋艿:等 4.5.0 => 4.6.0 -->
</dependency>
<!-- Test 测试相关 --> <!-- Test 测试相关 -->
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>

View File

@@ -9,10 +9,8 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
/**
* RPC 服务 - 文件
*/
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory = @FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
/** RPC 服务 - 文件 */
public interface FileApi { public interface FileApi {
String PREFIX = ApiConstants.PREFIX + "/file"; String PREFIX = ApiConstants.PREFIX + "/file";
@@ -54,9 +52,7 @@ public interface FileApi {
.getCheckedData(); .getCheckedData();
} }
/**
* 保存文件,并返回文件的访问路径
*/
@PostMapping(PREFIX + "/create") @PostMapping(PREFIX + "/create")
/** 保存文件,并返回文件的访问路径 */
CommonResult<String> createFile(@Valid @RequestBody FileCreateReqDTO createReqDTO); CommonResult<String> createFile(@Valid @RequestBody FileCreateReqDTO createReqDTO);
} }

View File

@@ -0,0 +1,21 @@
package com.tashow.cloud.productapi.api.product;
import com.tashow.cloud.productapi.api.product.dto.CategoryDO;
import com.tashow.cloud.productapi.api.product.dto.ShopDetailDO;
import com.tashow.cloud.productapi.enums.ApiConstants;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
/** RPC 服务 - 参数配置 */
public interface ShopDetailApi {
String PREFIX = ApiConstants.PREFIX + "/shop";
@GetMapping(PREFIX + "/getShopInfo")
ShopDetailDO getShopInfo(@RequestParam(value = "id", required = false) Long id);
}

View File

@@ -12,7 +12,6 @@ import java.util.List;
* @author 芋道源码 * @author 芋道源码
*/ */
@Data @Data
public class CategoryDto { public class CategoryDto {
/** /**

View File

@@ -17,7 +17,7 @@ public class ApiConstants {
*/ */
public static final String NAME = "product-server"; public static final String NAME = "product-server";
public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/infra"; public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/product";
public static final String VERSION = "1.0.0"; public static final String VERSION = "1.0.0";

View File

@@ -27,15 +27,6 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>com.github.xingfudeshi</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
</dependency>
<!-- Web 相关 --> <!-- Web 相关 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@@ -61,6 +61,7 @@
</dependencies> </dependencies>
<build> <build>
<!-- 设置构建的 jar 包名 --> <!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName> <finalName>${project.artifactId}</finalName>

View File

@@ -7,10 +7,10 @@ spring:
username: nacos # Nacos 账号 username: nacos # Nacos 账号
password: nacos # Nacos 密码 password: nacos # Nacos 密码
discovery: # 【配置中心】配置项 discovery: # 【配置中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境 namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
config: # 【注册中心】配置项 config: # 【注册中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境 namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
# 日志文件配置 # 日志文件配置

View File

@@ -15,7 +15,6 @@
<module>tashow-module-infra</module> <module>tashow-module-infra</module>
<module>tashow-module-app</module> <module>tashow-module-app</module>
<module>tashow-module-product</module> <module>tashow-module-product</module>
<module>tashow-module-ai</module>
</modules> </modules>
</project> </project>

View File

@@ -1,96 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-module</artifactId>
<version>${revision}</version>
</parent>
<artifactId>tashow-module-ai</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--文件管理-->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-infra-api</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-rpc</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-data-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-web</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-env</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-data-excel</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-framework-monitor</artifactId>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,15 +0,0 @@
package com.tashow.cloud.ai;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 应用服务启动类
*/
@SpringBootApplication
public class AiServerApplication {
public static void main(String[] args) {
SpringApplication.run(AiServerApplication.class, args);
}
}

View File

@@ -1,94 +0,0 @@
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.annotation.security.PermitAll;
import jakarta.validation.Valid;
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 = "创建样本库-上传文件")
@PermitAll
// @PreAuthorize("@ss.hasPermission('ai:sample:create')")
public CommonResult<List<AiSampleFileRespVO>> createAiSample(FileUploadReqVO uploadReqVO) {
return success(aiSampleService.createAiSample(uploadReqVO));
}
@PutMapping("/updates")
@Operation(summary = "更新样本库")
// @PreAuthorize("@ss.hasPermission('ai:sample:update')")
@PermitAll
public CommonResult<Boolean> updateAiSample(@Valid @RequestBody List<AiSampleSaveReqVO> updateReqVO) {
aiSampleService.updateAiSamples(updateReqVO);
return success(true);
}
@PutMapping("/relate")
@Operation(summary = "添加关联标签")
// @PreAuthorize("@ss.hasPermission('ai:sample:update')")
@PermitAll
public CommonResult<Boolean> relate(@Valid @RequestBody AiSampleRelateTagVO relateTagVO) {
aiSampleService.relate(relateTagVO);
return success(true);
}
@DeleteMapping("/deleteRelate")
@Operation(summary = "删除关联标签")
// @PreAuthorize("@ss.hasPermission('ai:sample:delete')")
@PermitAll
public CommonResult<Boolean> 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')")
@PermitAll
public CommonResult<Boolean> deleteAiSample(@RequestParam("id") String ids) {
aiSampleService.deleteAiSample(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得样本库")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
// @PreAuthorize("@ss.hasPermission('ai:sample:query')")
@PermitAll
public CommonResult<AiSampleRespVO> getAiSample(@RequestParam("id") Long id) {
AiSampleDO aiSample = aiSampleService.getAiSample(id);
return success(BeanUtils.toBean(aiSample, AiSampleRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得样本库分页")
// @PreAuthorize("@ss.hasPermission('ai:sample:query')")
@PermitAll
public CommonResult<PageResult<AiSampleRespVO>> getAiSamplePage(@Valid AiSamplePageReqVO pageReqVO) {
PageResult<AiSampleDO> pageResult = aiSampleService.getAiSamplePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiSampleRespVO.class));
}
}

View File

@@ -1,78 +0,0 @@
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.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.annotation.security.PermitAll;
import jakarta.validation.Valid;
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')")
@PermitAll
public CommonResult<Long> createAiSampleTag(@Valid @RequestBody AiSampleTagSaveReqVO createReqVO) {
return success(aiSampleTagService.createAiSampleTag(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新样本标签库")
// @PreAuthorize("@ss.hasPermission('ai:sampleTag:update')")
@PermitAll
public CommonResult<Boolean> 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')")
@PermitAll
public CommonResult<Boolean> deleteAiSampleTag(@RequestParam("id") Long id) {
aiSampleTagService.deleteAiSampleTag(id);
return success(true);
}
@GetMapping("/list")
@Operation(summary = "获得样本标签库列表")
// @PreAuthorize("@ss.hasPermission('ai:sampleTag:query')")
@PermitAll
public CommonResult<List<AiSampleTagListRespVO>> getAiSampleTagList() {
return success(BeanUtils.toBean(aiSampleTagService.getAiSampleTagList(), AiSampleTagListRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得样本标签库分页")
// @PreAuthorize("@ss.hasPermission('ai:sampleTag:query')")
@PermitAll
public CommonResult<PageResult<AiSampleTagDO>> getAiSampleTagPage(@Valid AiSampleTagPageReqVO pageReqVO) {
PageResult<AiSampleTagDO> pageResult = aiSampleTagService.getAiSampleTagPage(pageReqVO);
return success(pageResult);
}
}

View File

@@ -1,68 +0,0 @@
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.annotation.security.PermitAll;
import jakarta.validation.Valid;
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')")
@PermitAll
public CommonResult<Long> createAiSampleTagGroup(@Valid @RequestBody AiSampleTagGroupSaveReqVO createReqVO) {
return success(aiSampleTagGroupService.createAiSampleTagGroup(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新样本标签分组库")
// @PreAuthorize("@ss.hasPermission('ai:sampleTagGroup:update')")
@PermitAll
public CommonResult<Boolean> 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')")
@PermitAll
public CommonResult<Boolean> deleteAiSampleTagGroup(@RequestParam("id") Long id) {
aiSampleTagGroupService.deleteAiSampleTagGroup(id);
return success(true);
}
@GetMapping("/list")
@Operation(summary = "获得样本标签分组库列表")
// @PreAuthorize("@ss.hasPermission('ai:sampleTagGroup:query')")
@PermitAll
public CommonResult<List<AiSampleTagGroupRespVO>> getAiSampleTagGroupPage() {
List<AiSampleTagGroupDO> aiSampleTagGroupList = aiSampleTagGroupService.getAiSampleTagGroupList();
return success(BeanUtils.toBean(aiSampleTagGroupList, AiSampleTagGroupRespVO.class));
}
}

View File

@@ -1,17 +0,0 @@
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 AiSampleFileRespVO {
@Schema(description = "文件地址")
private String fileUrl;
@Schema(description = "文件名称")
private String fileName;
}

View File

@@ -1,28 +0,0 @@
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 AiSamplePageReqVO extends PageParam {
@Schema(description = "标签ids", example = "25839")
private String tagIds;
@Schema(description = "样本名称", example = "张三")
private String sampleName;
@Schema(description = "样本格式", example = "1")
private String sampleMineType;
@Schema(description = "排序", example = "正序asc倒序desc")
private String sort;
}

View File

@@ -1,17 +0,0 @@
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 List<Long> tagId;
@Schema(description = "样本ids")
private List<Long> sampleIds;
}

View File

@@ -1,48 +0,0 @@
package com.tashow.cloud.ai.controller.admin.aisample.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.fasterxml.jackson.annotation.JsonFormat;
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<AiSampleTagDO> 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 = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
@Schema(description = "修改时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime;
}

View File

@@ -1,19 +0,0 @@
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;
}

View File

@@ -1,27 +0,0 @@
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;
}

View File

@@ -1,26 +0,0 @@
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;
}

View File

@@ -1,16 +0,0 @@
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;
}

View File

@@ -1,16 +0,0 @@
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;
}

View File

@@ -1,20 +0,0 @@
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")
private Long groupId;
@Schema(description = "标签名称", example = "张三")
private String tagName;
}

View File

@@ -1,20 +0,0 @@
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<AiSampleTagDO> tags;
@Schema(description = "分组列表")
List<AiSampleTagGroupDO> tagGroupDOS;
}

View File

@@ -1,21 +0,0 @@
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<Long> groupIds;
@Schema(description = "标签名称", example = "张三")
private String tagName;
}

View File

@@ -1,15 +0,0 @@
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;
}

View File

@@ -1 +0,0 @@
package com.tashow.cloud.ai.controller.admin;

View File

@@ -1,57 +0,0 @@
package com.tashow.cloud.ai.controller.app.dialog;
import com.tashow.cloud.ai.controller.app.dialog.vo.DialogResp;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateReqVo;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateRespVo;
import com.tashow.cloud.ai.service.dialog.AiDialogService;
import com.tashow.cloud.common.pojo.CommonResult;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.tashow.cloud.common.pojo.CommonResult.success;
/**
* 翻译
*/
@RestController
@RequestMapping("/ai/dialog")
@Validated
@Slf4j
public class DialogController {
List<String> message = List.of("渴了", "饿了", "想睡觉", "想出去玩", "想溜达", "情绪低落", "很开心", "很伤心", "想哭");
@Resource
private AiDialogService aiDialogService;
/**
* 获取对话消息列表
*/
@GetMapping("/getDialog")
@PermitAll
public CommonResult<DialogResp> msList() {
//获取当前登录用户
Long userId = 1L;
return success(aiDialogService.getDialog(userId));
}
/**
* 翻译
*/
@PostMapping("/translate")
@PermitAll
public CommonResult<TranslateRespVo> translate(@Validated TranslateReqVo fileReqVo) {
return success(aiDialogService.translate(fileReqVo));
}
}

View File

@@ -1,76 +0,0 @@
package com.tashow.cloud.ai.controller.app.dialog.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* ai-对话消息 DO
*
* @author 芋道源码
*/
@Schema(description = "api - 对话 Response VO")
@Data
public class AiDialogMessageRespVo {
private Long id;
/**
* 对话id
*/
private Long dialogId;
/**
* 内容
*/
private String contentText;
/**
* 文件时长
*/
private Long contentDuration;
/**
* 文本类型1text2image3file4audio
*/
private Integer contentType;
/**
* 对话中的顺序
*/
private Integer messageOrder;
/**
* 消息状态 1正常 0删除
*/
private Integer messageStatus;
/**
* 宠物id
*/
private Long petId;
/**
* 宠物名称
*/
private String petName;
/**
* 宠物头像
*/
private String petAvatar;
/**
* 宠物类型
*/
private String petType;
/**
* 翻译结果
*/
private String transResult;
/**
* 翻译结果
*/
private Integer transStatus;
/**
* 创建时间
*/
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date createTime;
}

View File

@@ -1,21 +0,0 @@
package com.tashow.cloud.ai.controller.app.dialog.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "api - 对话 Response VO")
@Data
public class DialogResp {
@Schema(description = "对话id")
private Long dialogId;
@Schema(description = "对话标题")
private String title;
@Schema(description = "对话状态")
private String dialogStatus;
@Schema(description = "对话消息内容")
private List<AiDialogMessageRespVo> messages;
}

View File

@@ -1,24 +0,0 @@
package com.tashow.cloud.ai.controller.app.dialog.vo;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/**
* 翻译接口请求vo
*/
@Data
public class TranslateReqVo {
//对话id
private Long dialogId;
//消息id
private Long msgId;
/** 文件附件 */
private MultipartFile file;
//文件时长
private String contentDuration;
}

View File

@@ -1,47 +0,0 @@
package com.tashow.cloud.ai.controller.app.dialog.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 翻译接口结果vo
*/
@Data
public class TranslateRespVo {
//消息id
private Long id;
//对话id
private Long dialogId;
//消息顺序
private Integer messageOrder;
//消息类型1text2file
private Integer contentType;
//消息内容 若消息类型是file 则为文件地址
private String contentText;
//消息内容 若消息类型是file 则为文件地址
private String fileType;
//宠物类型
private String petType;
//宠物名称
private String petName;
//宠物头像
private String petAvatar;
//宠物id
private Long petId;
//翻译状态
private Integer transStatus;
//翻译结果
private String transResult;
//文件时长
private Long contentDuration;
//发送时间
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private String createTime;
}

View File

@@ -1 +0,0 @@
package com.tashow.cloud.ai.controller.app;

View File

@@ -1 +0,0 @@
package com.tashow.cloud.ai.controller;

View File

@@ -1,61 +0,0 @@
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<AiSampleTagDO> tags;
/**
* 样本文件id
*/
private String sampleFilePath;
/**
* 样本名称
*/
private String sampleName;
/**
* 样本时长
*/
private String sampleTime;
/**
* 样本格式
*/
private String sampleMineType;
/**
* 样本大小
*/
private Long sampleSize;
/**
* 样本注释
*/
private String remark;
}

View File

@@ -1,34 +0,0 @@
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;
}

View File

@@ -1,34 +0,0 @@
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;
}

View File

@@ -1,36 +0,0 @@
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 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
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiSampleTagGroupRelateDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 样本标签id
*/
private Long sampleTagId;
/**
* 样本标签分组id
*/
private Long sampleTagGroupId;
}

View File

@@ -1,36 +0,0 @@
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 lombok.*;
/**
* 样本-标签关联 DO
*
* @author 芋道源码
*/
@TableName("tz_ai_sample_tag_relate")
@KeySequence("tz_ai_sample_tag_relate_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiSampleTagRelateDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 样本id
*/
private Long sampleId;
/**
* 样本标签id
*/
private Long sampleTagId;
}

View File

@@ -1,40 +0,0 @@
package com.tashow.cloud.ai.dal.dataobject.dialog;
import lombok.*;
import com.baomidou.mybatisplus.annotation.*;
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
/**
* ai-对话 DO
*
* @author 芋道源码
*/
@TableName("tz_ai_dialog")
@KeySequence("tz_ai_dialog_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiDialogDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 对话标题
*/
private String title;
/**
* 用户id
*/
private Long userId;
/**
* 对话状态1active, 2archived, 3deleted
*/
private Integer dialogStatus;
}

View File

@@ -1,94 +0,0 @@
package com.tashow.cloud.ai.dal.dataobject.dialog;
import com.baomidou.mybatisplus.annotation.*;
import lombok.*;
import java.time.LocalDateTime;
/**
* ai-对话消息 DO
*
* @author 芋道源码
*/
@TableName("tz_ai_dialog_message")
@KeySequence("tz_ai_dialog_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiDialogMessageDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 对话id
*/
private Long dialogId;
/**
* 内容
*/
private String contentText;
/**
* 文件名
*/
private String fileName;
/**
* 文件类型
*/
private String fileType;
/**
* 文件时长
*/
private Long contentDuration;
/**
* 文本类型1text2image3file4audio
*/
private Integer contentType;
/**
* 对话中的顺序
*/
private Integer messageOrder;
/**
* 消息状态 1正常 0删除
*/
private Integer messageStatus;
/**
* 宠物id
*/
private Long petId;
/**
* 宠物名称
*/
private String petName;
/**
* 宠物头像
*/
private String petAvatar;
/**
* 宠物类型
*/
private String petType;
/**
* 原始结果
*/
private String sourceResult;
/**
* 翻译结果
*/
private String transResult;
/**
* 翻译结果
*/
private Integer transStatus;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
}

View File

@@ -1,2 +0,0 @@
package com.tashow.cloud.ai.dal.dataobject;
// 数据库对象

View File

@@ -1,43 +0,0 @@
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.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;
import org.apache.ibatis.annotations.Select;
/**
* 样本库 Mapper
*/
@Mapper
public interface AiSampleMapper extends BaseMapperX<AiSampleDO> {
default PageResult<AiSampleDO> selectPage(AiSamplePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<AiSampleDO>()
.likeIfPresent(AiSampleDO::getSampleName, reqVO.getSampleName())
.orderByDesc(AiSampleDO::getId));
}
@Select("<script>" +
"SELECT t.id, t.sample_file_path sampleFilePath,t.sample_name sampleName,t.sample_time sampleTime" +
",sample_mine_type sampleMineType ,sample_size sampleSize, remark,creator,updater,create_time createTime" +
",update_time updateTime,deleted,tenant_id tenantId " +
"FROM tz_ai_sample t " +
"INNER JOIN tz_ai_sample_tag_relate r ON t.id = r.sample_id " +
"<where>" +
" <if test=\"pageReqVO.tagIds != null and pageReqVO.tagIds != ''\">" +
" AND FIND_IN_SET(r.sample_tag_id,${pageReqVO.tagIds}) " +
" </if>" +
" <if test=\"pageReqVO.sampleName != null and pageReqVO.sampleName != ''\">" +
" AND t.sample_name LIKE CONCAT('%',#{pageReqVO.sampleName},'%')" +
" </if>" +
"</where>" +
"ORDER BY t.id DESC" +
"</script>")
IPage<AiSampleDO> getAiSamplePage(Page<AiSampleDO> objectPage, AiSamplePageReqVO pageReqVO);
}

View File

@@ -1,25 +0,0 @@
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<AiSampleTagGroupDO> {
default PageResult<AiSampleTagGroupDO> selectPage(AiSampleTagGroupPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<AiSampleTagGroupDO>()
.likeIfPresent(AiSampleTagGroupDO::getGroupName, reqVO.getGroupName())
.betweenIfPresent(AiSampleTagGroupDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(AiSampleTagGroupDO::getId));
}
}

View File

@@ -1,15 +0,0 @@
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<AiSampleTagGroupRelateDO> {
}

View File

@@ -1,42 +0,0 @@
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<AiSampleTagDO> {
default PageResult<AiSampleTagDO> selectPage(AiSampleTagPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<AiSampleTagDO>()
.orderByDesc(AiSampleTagDO::getId));
}
@Select("<script>" +
"SELECT t.id, t.tag_name tagName " +
"FROM tz_ai_sample_tag t " +
"INNER JOIN tz_ai_sample_tag_group_relate r ON t.id = r.sample_tag_id " +
"<where>" +
" <if test=\"pageReqVO.groupId != null\">" +
" AND r.sample_tag_group_id = #{pageReqVO.groupId}" +
" </if>" +
" <if test=\"pageReqVO.tagName != null and pageReqVO.tagName != ''\">" +
" AND t.tag_name LIKE CONCAT('%',#{pageReqVO.tagName},'%')" +
" </if>" +
"</where>" +
"ORDER BY t.id DESC" +
"</script>")
IPage<AiSampleTagDO> getAiSampleTagPage(Page<AiSampleTagDO> page, AiSampleTagPageReqVO pageReqVO);
}

View File

@@ -1,16 +0,0 @@
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<AiSampleTagRelateDO> {
}

View File

@@ -1,15 +0,0 @@
package com.tashow.cloud.ai.dal.mysql.dialog;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogDO;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* ai-对话 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface AiDialogMapper extends BaseMapperX<AiDialogDO> {
}

View File

@@ -1,16 +0,0 @@
package com.tashow.cloud.ai.dal.mysql.dialog;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogMessageDO;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* ai-对话消息 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface AiDialogMessageMapper extends BaseMapperX<AiDialogMessageDO> {
}

View File

@@ -1 +0,0 @@
package com.tashow.cloud.ai.dal.mysql;

View File

@@ -1 +0,0 @@
package com.tashow.cloud.ai.dal;

View File

@@ -1 +0,0 @@
package com.tashow.cloud.ai.framework;

View File

@@ -1,10 +0,0 @@
package com.tashow.cloud.ai.framework.rpc.config;
import com.tashow.cloud.infraapi.api.file.FileApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {FileApi.class})
public class RpcConfiguration {
}

View File

@@ -1 +0,0 @@
package com.tashow.cloud.ai.framework.rpc;

View File

@@ -1,44 +0,0 @@
package com.tashow.cloud.ai.framework.security.config;
import com.tashow.cloud.infraapi.enums.ApiConstants;
import com.tashow.cloud.security.security.config.AuthorizeRequestsCustomizer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
/**
* Infra 模块的 Security 配置
*/
@Configuration(proxyBeanMethods = false, value = "infraSecurityConfiguration")
public class SecurityConfiguration {
@Value("${spring.boot.admin.context-path:''}")
private String adminSeverContextPath;
@Bean("infraAuthorizeRequestsCustomizer")
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
return new AuthorizeRequestsCustomizer() {
@Override
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
// Spring Boot Actuator 的安全配置
registry.requestMatchers("/actuator").permitAll()
.requestMatchers("/actuator/**").permitAll();
// Druid 监控
registry.requestMatchers("/druid/**").permitAll();
// Spring Boot Admin Server 的安全配置
registry.requestMatchers(adminSeverContextPath).permitAll()
.requestMatchers(adminSeverContextPath + "/**").permitAll();
// 文件读取
registry.requestMatchers(buildAdminApi("/infra/file/*/get/**")).permitAll();
// TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案
// RPC 服务的安全配置
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
}
};
}
}

View File

@@ -1,4 +0,0 @@
/**
* 占位
*/
package com.tashow.cloud.ai.framework.security.core;

View File

@@ -1,73 +0,0 @@
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<AiSampleFileRespVO> createAiSample(@Valid FileUploadReqVO createReqVO);
/**
* 更新样本库
*
* @param updateReqVO 更新信息
*/
void updateAiSample(@Valid AiSampleSaveReqVO updateReqVO);
/**
* 批量更新
* @param updateReqVO
*/
void updateAiSamples(@Valid List<AiSampleSaveReqVO> updateReqVO);
/**
* 关联标签
* @param relateTagVO
*/
void relate(@Valid AiSampleRelateTagVO relateTagVO);
/**
* 删除样本库
*
* @param id 编号
*/
void deleteAiSample(String id);
/**
* 删除样本库
* @param relateTagVO 编号
*/
void deleteRelate(AiSampleRelateTagVO relateTagVO);
/**
* 获得样本库
*
* @param id 编号
* @return 样本库
*/
AiSampleDO getAiSample(Long id);
/**
* 获得样本库分页
*
* @param pageReqVO 分页查询
* @return 样本库分页
*/
PageResult<AiSampleDO> getAiSamplePage(AiSamplePageReqVO pageReqVO);
}

View File

@@ -1,180 +0,0 @@
package com.tashow.cloud.ai.service.aisample;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.tashow.cloud.ai.controller.admin.aisample.vo.*;
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 com.tashow.cloud.mybatis.mybatis.core.util.MyBatisUtils;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.Arrays;
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;
@Value("file-server")
private String fileServer;
@Override
@SneakyThrows
public List<AiSampleFileRespVO> createAiSample(FileUploadReqVO uploadReqVO) {
//返回图片路径
List<AiSampleFileRespVO> urls = new ArrayList<>();
/* 调用文件上传服务*/
for (MultipartFile file : uploadReqVO.getFiles()) {
//返回上传结果
String file1 = fileServer + 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(new AiSampleFileRespVO().setFileUrl(file1).setFileName(file.getOriginalFilename()));
}
// 返回
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<AiSampleSaveReqVO> updateReqVO) {
aiSampleMapper.updateBatch(BeanUtils.toBean(updateReqVO, AiSampleDO.class));
}
@Override
public void relate(AiSampleRelateTagVO relateTagVO) {
List<AiSampleTagRelateDO> tagRelateDOS = new ArrayList<>();
for (Long sampleId : relateTagVO.getSampleIds()) {
for (Long tagId : relateTagVO.getTagId()) {
AiSampleTagRelateDO relateDO = aiSampleTagRelateMapper.selectOne(
new LambdaQueryWrapper<AiSampleTagRelateDO>()
.eq(AiSampleTagRelateDO::getSampleId, sampleId)
.eq(AiSampleTagRelateDO::getSampleTagId, tagId) );
if (relateDO== null){
relateDO = new AiSampleTagRelateDO();
relateDO.setSampleId(sampleId);
relateDO.setSampleTagId(tagId);
tagRelateDOS.add(relateDO);
}
}
}
if (!tagRelateDOS.isEmpty()){
aiSampleTagRelateMapper.insertBatch(tagRelateDOS);
}
}
@Override
public void deleteRelate(AiSampleRelateTagVO relateTagVO) {
List<Long> tagRelateIds = new ArrayList<>();
for (Long sampleId : relateTagVO.getSampleIds()) {
for (Long tagId : relateTagVO.getTagId()) {
AiSampleTagRelateDO relateDO = aiSampleTagRelateMapper.selectOne(
new LambdaQueryWrapper<AiSampleTagRelateDO>()
.eq(AiSampleTagRelateDO::getSampleId, sampleId)
.eq(AiSampleTagRelateDO::getSampleTagId, tagId)
);
if (relateDO != null) {
tagRelateIds.add(relateDO.getId());
}
}
}
if (!tagRelateIds.isEmpty()) {
aiSampleTagRelateMapper.deleteBatchIds(tagRelateIds);
}
}
@Override
public void deleteAiSample(String ids) {
// 删除
aiSampleMapper.deleteByIds(Arrays.asList(ids.split(StrUtil.COMMA)));
}
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(aiSampleDO.getSampleFilePath());
//先获取关联的标签id
List<AiSampleTagRelateDO> tagRelateDOS = aiSampleTagRelateMapper.selectList(new LambdaQueryWrapper<AiSampleTagRelateDO>().eq(AiSampleTagRelateDO::getSampleId, id));
List<Long> tagIds = tagRelateDOS.stream().map(AiSampleTagRelateDO::getSampleTagId).toList();
aiSampleDO.setTags(aiSampleTagMapper.selectList(new LambdaQueryWrapper<AiSampleTagDO>().in(AiSampleTagDO::getId, tagIds)));
return aiSampleDO;
}
@Override
public PageResult<AiSampleDO> getAiSamplePage(AiSamplePageReqVO pageReqVO) {
// PageResult<AiSampleDO> aiSampleDOPageResult = aiSampleMapper.selectPage(pageReqVO);
IPage<AiSampleDO> aiSampleDOPageResult = aiSampleMapper.getAiSamplePage(MyBatisUtils.buildPage(pageReqVO),pageReqVO);
//根据样本id获取关联的标签id
List<Long> sampleIds = aiSampleDOPageResult.getRecords().stream().map(AiSampleDO::getId).toList();
List<AiSampleTagRelateDO> tagRelateDOS = aiSampleTagRelateMapper.selectList(
new LambdaQueryWrapper<AiSampleTagRelateDO>()
.in(!sampleIds.isEmpty(), AiSampleTagRelateDO::getSampleId, sampleIds));
List<Long> tagIds = tagRelateDOS.stream().map(AiSampleTagRelateDO::getSampleTagId).toList();
//获取标签信息
List<AiSampleTagDO> aiSampleTagDOS = aiSampleTagMapper.selectList(
new LambdaQueryWrapper<AiSampleTagDO>()
.in(!tagIds.isEmpty(), AiSampleTagDO::getId, tagIds));
//封装标签信息
for (AiSampleDO aiSampleDO : aiSampleDOPageResult.getRecords()) {
List<AiSampleTagRelateDO> list = tagRelateDOS.stream()
.filter(a -> ObjectUtil.equals(aiSampleDO.getId(), a.getSampleId()))
.toList();
Object[] tagsId = list.stream().map(AiSampleTagRelateDO::getSampleTagId).toArray();
List<AiSampleTagDO> list1 = aiSampleTagDOS.stream().filter(a -> ArrayUtil.containsAny(tagsId, a.getId())).toList();
aiSampleDO.setTags(list1);
aiSampleDO.setSampleFilePath(aiSampleDO.getSampleFilePath());
}
return new PageResult<>(aiSampleDOPageResult.getRecords(), aiSampleDOPageResult.getTotal());
}
}

View File

@@ -1,11 +0,0 @@
package com.tashow.cloud.ai.service.aisample;
/**
* 样本标签-分组关联 Service 接口
*
* @author 芋道源码
*/
public interface AiSampleTagGroupRelateService {
}

View File

@@ -1,16 +0,0 @@
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 {
}

View File

@@ -1,62 +0,0 @@
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<AiSampleTagGroupDO> getAiSampleTagGroupPage(AiSampleTagGroupPageReqVO pageReqVO);
/**
* 获取标签分组列表
* @return
*/
List<AiSampleTagGroupDO> getAiSampleTagGroupList();
}

View File

@@ -1,74 +0,0 @@
package com.tashow.cloud.ai.service.aisample;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupPageReqVO;
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<AiSampleTagGroupDO> getAiSampleTagGroupPage(AiSampleTagGroupPageReqVO pageReqVO) {
return aiSampleTagGroupMapper.selectPage(pageReqVO);
}
@Override
public List<AiSampleTagGroupDO> getAiSampleTagGroupList() {
return aiSampleTagGroupMapper.selectList(new LambdaQueryWrapper<AiSampleTagGroupDO>().orderByDesc(AiSampleTagGroupDO::getId));
}
}

View File

@@ -1,11 +0,0 @@
package com.tashow.cloud.ai.service.aisample;
/**
* 样本-标签关联 Service 接口
*
* @author 芋道源码
*/
public interface AiSampleTagRelateService {
}

View File

@@ -1,16 +0,0 @@
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 {
}

View File

@@ -1,61 +0,0 @@
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<AiSampleTagDO> getAiSampleTagPage(AiSampleTagPageReqVO pageReqVO);
/**
* 获取样本标签列表
* @return
*/
List<AiSampleTagDO> getAiSampleTagList();
}

View File

@@ -1,103 +0,0 @@
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<AiSampleTagGroupRelateDO> tagGroupRelateDOS = new ArrayList<>();
for (Long groupId : createReqVO.getGroupIds()) {
AiSampleTagGroupRelateDO tagGroupRelateDO = new AiSampleTagGroupRelateDO();
tagGroupRelateDO.setSampleTagId(createReqVO.getId());
tagGroupRelateDO.setSampleTagGroupId(groupId);
tagGroupRelateDO.setSampleTagId(aiSampleTag.getId());
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<AiSampleTagGroupRelateDO>().eq(AiSampleTagGroupRelateDO::getSampleTagId, updateReqVO.getId()));
//插入关联
List<AiSampleTagGroupRelateDO> 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<AiSampleTagDO> getAiSampleTagPage(AiSampleTagPageReqVO pageReqVO) {
IPage<AiSampleTagDO> prodPageList = aiSampleTagMapper.getAiSampleTagPage(MyBatisUtils.buildPage(pageReqVO),pageReqVO);
return new PageResult<>(prodPageList.getRecords(), prodPageList.getTotal());
}
@Override
public List<AiSampleTagDO> getAiSampleTagList() {
return aiSampleTagMapper.selectList(new LambdaQueryWrapper<AiSampleTagDO>().orderByDesc(AiSampleTagDO::getId));
}
}

View File

@@ -1,14 +0,0 @@
package com.tashow.cloud.ai.service.dialog;
import com.baomidou.mybatisplus.extension.service.IService;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogMessageDO;
/**
* ai-对话消息 Service 接口
*
* @author lwq
*/
public interface AiDialogMessageService extends IService<AiDialogMessageDO> {
}

View File

@@ -1,20 +0,0 @@
package com.tashow.cloud.ai.service.dialog;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogMessageDO;
import com.tashow.cloud.ai.dal.mysql.dialog.AiDialogMessageMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
/**
* ai-对话消息 Service 实现类
*
* @author lwq
*/
@Service
@Validated
public class AiDialogMessageServiceImpl extends ServiceImpl<AiDialogMessageMapper, AiDialogMessageDO> implements AiDialogMessageService {
}

View File

@@ -1,20 +0,0 @@
package com.tashow.cloud.ai.service.dialog;
import com.baomidou.mybatisplus.extension.service.IService;
import com.tashow.cloud.ai.controller.app.dialog.vo.DialogResp;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateReqVo;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateRespVo;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogDO;
/**
* ai-对话 Service 接口
*
* @author lwq
*/
public interface AiDialogService extends IService<AiDialogDO> {
DialogResp getDialog(Long userId);
TranslateRespVo translate(TranslateReqVo fileReqVo);
}

View File

@@ -1,195 +0,0 @@
package com.tashow.cloud.ai.service.dialog;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tashow.cloud.ai.controller.app.dialog.vo.AiDialogMessageRespVo;
import com.tashow.cloud.ai.controller.app.dialog.vo.DialogResp;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateReqVo;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateRespVo;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogDO;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogMessageDO;
import com.tashow.cloud.ai.dal.mysql.dialog.AiDialogMapper;
import com.tashow.cloud.ai.dal.mysql.dialog.AiDialogMessageMapper;
import com.tashow.cloud.common.util.object.BeanUtils;
import com.tashow.cloud.infraapi.api.file.FileApi;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
/**
* ai-对话 Service 实现类
*
* @author lwq
*/
@Service
@Validated
@Slf4j
public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO> implements AiDialogService {
@Resource
private AiDialogMessageMapper aiDialogMessageMapper;
@Value("${translate-server}")
private String translateServer;
@Value("${file-server}")
private String fileServer;
@Resource
private FileApi fileApi;
@Override
public DialogResp getDialog(Long userId) {
AiDialogDO aiDialogDO = this.getOne(new LambdaQueryWrapper<AiDialogDO>().eq(AiDialogDO::getUserId, userId));
if (aiDialogDO == null) {
aiDialogDO = new AiDialogDO();
aiDialogDO.setDialogStatus(1);
aiDialogDO.setTitle("xxx");
aiDialogDO.setUserId(userId);
this.save(aiDialogDO);
}
List<AiDialogMessageDO> messageDOS = aiDialogMessageMapper.selectList(new LambdaQueryWrapper<AiDialogMessageDO>().eq(AiDialogMessageDO::getDialogId, aiDialogDO.getId()));
DialogResp resp = new DialogResp();
resp.setDialogId(aiDialogDO.getId());
resp.setTitle(aiDialogDO.getTitle());
resp.setMessages(BeanUtils.toBean(messageDOS, AiDialogMessageRespVo.class));
return resp;
}
@Override
@SneakyThrows
public TranslateRespVo translate(TranslateReqVo fileReqVo) {
AiDialogMessageDO messageDO = aiDialogMessageMapper.selectById(fileReqVo.getMsgId());
if (messageDO == null) {
messageDO = new AiDialogMessageDO();
}
String fileName = StrUtil.isBlank(messageDO.getFileName()) ? fileReqVo.getFile().getOriginalFilename() : messageDO.getFileName();
//上传文件获取文件地址
String fileUrl = StrUtil.isBlank(messageDO.getContentText()) ? fileServer + fileApi.createFile(fileName, "", fileReqVo.getFile().getBytes()) : messageDO.getContentText();
//翻译结果
translate(messageDO, fileUrl, fileName);
//创建消息 持久化消息
if (messageDO.getId() == null) {
messageDO.setFileName(fileName);
messageDO.setFileType(fileReqVo.getFile().getContentType());
messageDO.setDialogId(fileReqVo.getDialogId());
//前端无法转换时 后端进行转
messageDO.setContentDuration(
StrUtil.isBlank(fileReqVo.getContentDuration()) || "Infinity".equals(fileReqVo.getContentDuration())
? 0 : Long.parseLong(fileReqVo.getContentDuration())
);
messageDO.setContentText(fileUrl);
messageDO.setContentType(2);
//宠物档案 todo
messageDO.setPetId(1l);
messageDO.setPetName("猫猫翻译");
messageDO.setPetAvatar("https://img1.baidu.com/it/u=1224902049,3440357835&fm=253&app=138&f=JPEG?w=801&h=800");
//获取当前最后的排序
AiDialogMessageDO aiDialogMessageDO = aiDialogMessageMapper.selectOne(new LambdaQueryWrapper<AiDialogMessageDO>()
.eq(AiDialogMessageDO::getDialogId, fileReqVo.getDialogId())
.orderByDesc(AiDialogMessageDO::getMessageOrder)
.last("limit 1")
);
messageDO.setMessageOrder(aiDialogMessageDO == null ? 1 : aiDialogMessageDO.getMessageOrder() + 1);
messageDO.setCreateTime(LocalDateTime.now());
}
int i = messageDO.getId() == null ? aiDialogMessageMapper.insert(messageDO) : aiDialogMessageMapper.updateById(messageDO);
TranslateRespVo bean = BeanUtils.toBean(messageDO, TranslateRespVo.class);
bean.setId(messageDO.getId());
return bean;
}
@SneakyThrows
private Long getFileDuration(MultipartFile file) {
AudioInputStream audioStream = AudioSystem.getAudioInputStream(file.getInputStream());
return (long) (audioStream.getFrameLength() / audioStream.getFormat().getFrameRate());
}
private AiDialogMessageDO translate(AiDialogMessageDO messageDO, String file, String fileName) {
//调用大模型接口
String result = "";
try {
result = HttpRequest.post(translateServer).form("audio_data", HttpUtil.downloadBytes(file), fileName).timeout(20000) //20秒超时时间
.execute().body();
} catch (Exception e) {
log.error("调用大模型翻译出错", e);
}
messageDO.setSourceResult(result);
//数据解析
JSONObject translateResult = JSON.parseObject(result);
if (translateResult.isEmpty() || !translateResult.containsKey("intent_result") || !translateResult.getBoolean("is_species_sound")) {
messageDO.setTransStatus(0);
messageDO.setTransResult("");
return messageDO;
}
//标签 如 cat dog
String speciesLabels = translateResult.getString("species_labels");
//解析翻译结果
JSONObject probabilities = translateResult.getJSONObject("intent_result")
.getJSONObject("probabilities");
String resultKey = probabilities.entrySet().stream()
.filter(entry -> entry.getValue() instanceof Number)
.max(Comparator.comparingDouble(entry -> ((Number) entry.getValue()).doubleValue()))
.map(Map.Entry::getKey)
.orElse(null);
//返回结果
messageDO.setPetType(speciesLabels);
messageDO.setTransStatus(1);
messageDO.setTransResult(StrUtil.isBlank(resultKey) ? "" : resultKey.split(StrUtil.UNDERLINE)[1]);
return messageDO;
}
public static void main(String[] args) throws Exception {
InputStream inputStream = getInputStreamFromUrl("https://petshy.tashowz.com/admin-api/infra/file/29/get/857def513547ec33a105f71108c8ece329cb64dacc3a4779c94b0fcc3398cc32.webm");
AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
System.out.println((long) (audioStream.getFrameLength() / audioStream.getFormat().getFrameRate()));
// byte[] bytes = HttpUtil.downloadBytes("http://192.168.1.231:48080/admin-api/infra/file/29/get/c7351abf780f18600c104ec5662d843783ed8c309c01fb427046565217f51102.wav");
//
//// File file = new File("http://192.168.1.231:48080/admin-api/infra/file/29/get/c7351abf780f18600c104ec5662d843783ed8c309c01fb427046565217f51102.wav");
// String result = HttpRequest.post("http://43.139.42.137:8000/analyze/audio").form("audio_data", bytes, "aaa").timeout(20000) //20秒超时时间
// .execute().body();
// System.out.println(result);
}
public static InputStream getInputStreamFromUrl(String urlString) throws Exception {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET"); // 可以是GET, POST等
connection.setConnectTimeout(5000); // 设置连接超时时间
connection.setReadTimeout(5000); // 设置读取超时时间
int responseCode = connection.getResponseCode(); // 获取响应码
if (responseCode == HttpURLConnection.HTTP_OK) { // 状态码200表示成功
return connection.getInputStream();
} else {
// 处理错误情况例如抛出异常或返回null等
throw new RuntimeException("Failed : HTTP error code : " + responseCode);
}
}
}

View File

@@ -1 +0,0 @@
package com.tashow.cloud.ai.service;

View File

@@ -1,18 +0,0 @@
--- #################### 注册中心 + 配置中心相关配置 ####################
spring:
cloud:
nacos:
server-addr: 43.139.42.137:8848 # Nacos 服务器地址
username: nacos # Nacos 账号
password: nacos # Nacos 密码
discovery: # 【配置中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
config: # 【注册中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
# 添加字符编码配置解决YAML解析时的字符编码问题
encode: UTF-8

View File

@@ -1,12 +0,0 @@
server:
port: 48086
spring:
application:
name: ai-server
profiles:
active: local
config:
import:
- optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置
- optional:nacos:application.yaml # 加载【Nacos】的配置
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置

View File

@@ -1,75 +0,0 @@
<configuration>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 变量 yudao.info.base-package基础业务包 -->
<springProperty scope="context" name="tashow.info.base-package" source="tashow.info.base-package"/>
<!-- 格式化输出:%d 表示日期,%X{tid} SkWalking 链路追踪编号,%thread 表示线程名,%-5level级别从左显示 5 个字符宽度,%msg日志消息%n是换行符 -->
<property name="PATTERN_DEFAULT" value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} | %highlight(${LOG_LEVEL_PATTERN:-%5p} ${PID:- }) | %boldYellow(%thread [%tid]) %boldGreen(%-40.40logger{39}) | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<!-- 控制台 Appender -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">     
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
</appender>
<!-- 文件 Appender -->
<!-- 参考 Spring Boot 的 file-appender.xml 编写 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
<!-- 日志文件名 -->
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 滚动后的日志文件名 -->
<fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
<!-- 启动服务时,是否清理历史日志,一般不建议清理 -->
<cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
<!-- 日志文件,到达多少容量,进行滚动 -->
<maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
<!-- 日志文件的总大小0 表示不限制 -->
<totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
<!-- 日志文件的保留天数 -->
<maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
</rollingPolicy>
</appender>
<!-- 异步写入日志,提升性能 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
<queueSize>256</queueSize>
<appender-ref ref="FILE"/>
</appender>
<!-- SkyWalking GRPC 日志收集实现日志中心。注意SkyWalking 8.4.0 版本开始支持 -->
<appender name="GRPC" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
</appender>
<!-- 本地环境 -->
<springProfile name="local">
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="GRPC"/> <!-- 本地环境下,如果不想接入 SkyWalking 日志服务,可以注释掉本行 -->
<appender-ref ref="ASYNC"/> <!-- 本地环境下,如果不想打印日志,可以注释掉本行 -->
</root>
</springProfile>
<!-- 其它环境 -->
<springProfile name="dev,test,stage,prod,default">
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="GRPC"/>
</root>
</springProfile>
</configuration>

View File

@@ -1,5 +1,8 @@
package com.tashow.cloud.infra.controller.admin.file; package com.tashow.cloud.infra.controller.admin.file;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import static com.tashow.cloud.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
@@ -21,15 +24,11 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import static com.tashow.cloud.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
/** 管理后台 - 文件存储 */ /** 管理后台 - 文件存储 */
@RestController @RestController
@RequestMapping("/infra/file") @RequestMapping("/infra/file")
@Validated @Validated
@Slf4j @Slf4j
@PermitAll
public class FileController { public class FileController {
@Resource private FileService fileService; @Resource private FileService fileService;
@@ -88,7 +87,6 @@ public class FileController {
response.setStatus(HttpStatus.NOT_FOUND.value()); response.setStatus(HttpStatus.NOT_FOUND.value());
return; return;
} }
response.setContentLength(content.length);
writeAttachment(response, path, content); writeAttachment(response, path, content);
} }

View File

@@ -30,7 +30,7 @@ public class LocalFileClient extends AbstractFileClient<LocalFileClientConfig> {
String filePath = getFilePath(path); String filePath = getFilePath(path);
FileUtil.writeBytes(content, filePath); FileUtil.writeBytes(content, filePath);
// 拼接返回路径 // 拼接返回路径
return super.formatFileUrl("", path); return super.formatFileUrl(config.getDomain(), path);
} }
@Override @Override

View File

@@ -7,11 +7,11 @@ spring:
username: nacos # Nacos 账号 username: nacos # Nacos 账号
password: nacos # Nacos 密码 password: nacos # Nacos 密码
discovery: # 【配置中心】配置项 discovery: # 【配置中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境 namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
metadata: metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布 version: 1.0.0 # 服务实例的版本号,可用于灰度发布
config: # 【注册中心】配置项 config: # 【注册中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境 namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP

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