mirror of
https://github.com/CodePhiliaX/Chat2DB.git
synced 2025-09-23 13:37:10 +08:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,3 +1,16 @@
|
||||
## 3.0.14
|
||||
|
||||
`2023-11-20`
|
||||
|
||||
**Changelog**
|
||||
|
||||
- 🐞【Fixed】Team paging problem
|
||||
- 🐞【Fixed】Oracle service name bug
|
||||
- 🐞【Fixed】Oracle datatype error
|
||||
- 🐞【Fixed】Fixed an issue where MySQL changed table structure without displaying comments.
|
||||
- ⚡️【Optimize】Support database or schema
|
||||
- 【Developer】Friends don't worry, the company has some things recently, and is preparing 3.1.0, be patient
|
||||
|
||||
## 3.0.13
|
||||
|
||||
`2023-11-15`
|
||||
|
@ -1,3 +1,15 @@
|
||||
## 3.0.14
|
||||
|
||||
`2023-11-20`
|
||||
|
||||
**更新日志**
|
||||
|
||||
- 🐞【修复】团队分页问题
|
||||
- 🐞【修复】Oracle服务名称错误
|
||||
- 🐞【修复】Oracle数据类型错误
|
||||
- 🐞【修复】修复MySQL修改表结构,不回显注释的问题。
|
||||
- ⚡️【优化】支持数据库或模式
|
||||
- 【开发者】友友们不要着急呀,最近公司有些事情,并且在准备3.1.0,耐心等待哦
|
||||
## 3.0.13
|
||||
|
||||
`2023-11-15`
|
||||
|
15
README.md
15
README.md
@ -104,8 +104,7 @@ Redis and MongoDB are partially supported , Hbase、Elasticsearch、openGauss、
|
||||
|
||||
### CONFIGURE CUSTOM AI
|
||||
|
||||
* [Refer here to deploy your ChatGLM-6B model](https://github.com/chat2db/chat2db-chatglm-6b-deploy)
|
||||
* [Refer here to deploy your sqlcoder model](https://github.com/chat2db/chat2db-sqlcoder-deploy)
|
||||
* The rest api format for Custom AI is same as ChatGPT.
|
||||
|
||||
## 📦 Docker installation
|
||||
|
||||
@ -179,12 +178,18 @@ $ npm run build:web:prod / cp -r dist ../chat2db-server/chat2db-server-start/src
|
||||
|
||||
## ☎️ Contact Us
|
||||
|
||||
Please star and fork on GitHub before joining the group.
|
||||
Follow our WeChat public account.
|
||||
### WeChat
|
||||
|
||||
<a><img src="https://github.com/chat2db/Chat2DB/assets/22975773/e4239d29-1426-4361-bf57-f1b0b67d1281" width="40%"/></a>
|
||||
|
||||
Click and join <a href="https://discord.gg/dqae8nsC">discord server</a>
|
||||
### Discord
|
||||
<!-- [](您的Discord邀请链接) -->
|
||||
[](https://discord.gg/N6JscF7q)
|
||||
|
||||
## LICENSE
|
||||
|
||||
The primary license used by this software is the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0), supplemented by the [Chat2DB License](./Chat2DB_LICENSE).
|
||||
|
||||
|
||||
## ❤️ Acknowledgements
|
||||
|
||||
|
@ -100,8 +100,7 @@ Redis和MongoDB得到部分支持,Hbase、Elasticsearch、openGauss、TiDB、I
|
||||
### 使用Chat2DB AI 上手即用
|
||||
|
||||
### 使用自定义大模型
|
||||
- [参考这里部署本地ChatGLM-6B模型](https://github.com/chat2db/chat2db-chatglm-6b-deploy/blob/main/README_CN.md)
|
||||
- [参考这里部署本地sqlcoder模型](https://github.com/chat2db/chat2db-sqlcoder-deploy/blob/main/README_CN.md)
|
||||
- 使用自定义大模型,接口格式需要和open ai的接口格式保持一致
|
||||
|
||||
## 📦 Docker 部署
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Button, Input, Table, Popconfirm, message, Drawer } from 'antd';
|
||||
import { SearchOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import ConnectionServer from '@/service/connection'
|
||||
import ConnectionServer from '@/service/connection';
|
||||
import { createDataSource, deleteDataSource, getDataSourceList, updateDataSource } from '@/service/team';
|
||||
import { IConnectionDetails } from '@/typings';
|
||||
import { AffiliationType, IDataSourceVO } from '@/typings/team';
|
||||
@ -23,13 +23,13 @@ function DataSourceManagement() {
|
||||
showQuickJumper: true,
|
||||
// pageSizeOptions: ['10', '20', '30', '40'],
|
||||
});
|
||||
const [showCreateConnection, setShowCreateConnection] = useState(false)
|
||||
const [showCreateConnection, setShowCreateConnection] = useState(false);
|
||||
const connectionInfo = useRef<IConnectionDetails>();
|
||||
|
||||
const [drawerInfo, setDrawerInfo] = useState<{ open: boolean; type: AffiliationType; id?: number }>({
|
||||
open: false,
|
||||
type: AffiliationType['DATASOURCE_USER/TEAM']
|
||||
})
|
||||
type: AffiliationType['DATASOURCE_USER/TEAM'],
|
||||
});
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
@ -49,18 +49,24 @@ function DataSourceManagement() {
|
||||
width: 300,
|
||||
render: (_: any, record: IDataSourceVO) => (
|
||||
<>
|
||||
<Button type='link' onClick={() => {
|
||||
handleEdit(record)
|
||||
}}>
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => {
|
||||
handleEdit(record);
|
||||
}}
|
||||
>
|
||||
{i18n('common.button.edit')}
|
||||
</Button>
|
||||
<Button type='link' onClick={() => {
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => {
|
||||
setDrawerInfo({
|
||||
...drawerInfo,
|
||||
open: true,
|
||||
id: record.id,
|
||||
})
|
||||
}}>
|
||||
});
|
||||
}}
|
||||
>
|
||||
{i18n('team.action.rightManagement')}
|
||||
</Button>
|
||||
<Popconfirm
|
||||
@ -89,6 +95,10 @@ function DataSourceManagement() {
|
||||
let res = await getDataSourceList({ searchKey, pageNo, pageSize });
|
||||
if (res) {
|
||||
setDataSource(res?.data ?? []);
|
||||
setPagination({
|
||||
...pagination,
|
||||
total: res?.total ?? 0,
|
||||
} as any);
|
||||
}
|
||||
};
|
||||
|
||||
@ -100,7 +110,6 @@ function DataSourceManagement() {
|
||||
};
|
||||
|
||||
const handleTableChange = (p: any) => {
|
||||
|
||||
setPagination({
|
||||
...pagination,
|
||||
...p,
|
||||
@ -110,7 +119,7 @@ function DataSourceManagement() {
|
||||
const handleAddDataSource = () => {
|
||||
connectionInfo.current = undefined;
|
||||
setShowCreateConnection(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEdit = async (record: IDataSourceVO) => {
|
||||
const { id } = record;
|
||||
@ -118,14 +127,14 @@ function DataSourceManagement() {
|
||||
return;
|
||||
}
|
||||
|
||||
let detail = await ConnectionServer.getDetails({ id })
|
||||
let detail = await ConnectionServer.getDetails({ id });
|
||||
connectionInfo.current = detail;
|
||||
setShowCreateConnection(true)
|
||||
}
|
||||
setShowCreateConnection(true);
|
||||
};
|
||||
|
||||
const handleDelete = async (id?: number) => {
|
||||
if (isNumber(id)) {
|
||||
await deleteDataSource({ id })
|
||||
await deleteDataSource({ id });
|
||||
message.success(i18n('common.text.successfullyDelete'));
|
||||
queryDataSourceList();
|
||||
}
|
||||
@ -140,14 +149,12 @@ function DataSourceManagement() {
|
||||
const isUpdate = isValid(connectionInfo?.current?.id);
|
||||
const requestApi = isUpdate ? updateDataSource : createDataSource;
|
||||
try {
|
||||
await requestApi({ ...connectionInfo.current })
|
||||
message.success(isUpdate ? i18n('common.tips.updateSuccess') : i18n('common.tips.createSuccess'))
|
||||
setShowCreateConnection(false)
|
||||
queryDataSourceList()
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
await requestApi({ ...connectionInfo.current });
|
||||
message.success(isUpdate ? i18n('common.tips.updateSuccess') : i18n('common.tips.createSuccess'));
|
||||
setShowCreateConnection(false);
|
||||
queryDataSourceList();
|
||||
} catch {}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -163,6 +170,11 @@ function DataSourceManagement() {
|
||||
</Button>
|
||||
</div>
|
||||
<Table
|
||||
style={{
|
||||
maxHeight: '82vh',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
sticky
|
||||
rowKey={'id'}
|
||||
dataSource={dataSource}
|
||||
columns={columns}
|
||||
@ -185,8 +197,8 @@ function DataSourceManagement() {
|
||||
onClose={() => {
|
||||
setDrawerInfo({
|
||||
...drawerInfo,
|
||||
open: false
|
||||
})
|
||||
open: false,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,4 +1,9 @@
|
||||
.teamWrapper {
|
||||
height: 100vh;
|
||||
padding: 24px 36px;
|
||||
padding: 14px 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.teamTabsBox{
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ const Team = () => {
|
||||
return (
|
||||
<div className={styles.teamWrapper}>
|
||||
<Tabs
|
||||
className={styles.teamTabsBox}
|
||||
activeKey={activeKey}
|
||||
onChange={(activeKey) => setActiveKey(activeKey)}
|
||||
items={tabList.map((tab, index) => {
|
||||
|
@ -17,7 +17,7 @@ const requireRule = { required: true, message: i18n('common.form.error.required'
|
||||
|
||||
function TeamManagement() {
|
||||
const [form] = Form.useForm();
|
||||
const [loadding, setLoading] = useState(false)
|
||||
const [loadding, setLoading] = useState(false);
|
||||
const [dataSource, setDataSource] = useState<ITeamVO[]>([]);
|
||||
const [pagination, setPagination] = useState({
|
||||
searchKey: '',
|
||||
@ -66,7 +66,7 @@ function TeamManagement() {
|
||||
...drawerInfo,
|
||||
open: true,
|
||||
teamId: record.id,
|
||||
type: AffiliationType.TEAM_USER
|
||||
type: AffiliationType.TEAM_USER,
|
||||
});
|
||||
}}
|
||||
>
|
||||
@ -79,9 +79,10 @@ function TeamManagement() {
|
||||
...drawerInfo,
|
||||
open: true,
|
||||
teamId: record.id,
|
||||
type: AffiliationType.TEAM_DATASOURCE
|
||||
type: AffiliationType.TEAM_DATASOURCE,
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{i18n('team.action.affiliation.datasource')}
|
||||
</Button>
|
||||
<Popconfirm
|
||||
@ -112,11 +113,14 @@ function TeamManagement() {
|
||||
let res = await getTeamManagementList({ searchKey, pageNo, pageSize });
|
||||
if (res) {
|
||||
setDataSource(res?.data ?? []);
|
||||
setPagination({
|
||||
...pagination,
|
||||
total: res?.total ?? 0,
|
||||
} as any);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setLoading(false)
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -169,6 +173,11 @@ function TeamManagement() {
|
||||
</Button>
|
||||
</div>
|
||||
<Table
|
||||
style={{
|
||||
maxHeight: '82vh',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
sticky
|
||||
rowKey={'id'}
|
||||
loading={loadding}
|
||||
dataSource={dataSource}
|
||||
@ -192,7 +201,7 @@ function TeamManagement() {
|
||||
.catch((errorInfo) => {
|
||||
form.scrollToField(errorInfo.errorFields[0].name);
|
||||
form.setFields(errorInfo.errorFields);
|
||||
})
|
||||
});
|
||||
}}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
|
@ -116,6 +116,10 @@ function UserManagement() {
|
||||
let res = await getUserManagementList({ searchKey, pageNo, pageSize });
|
||||
if (res) {
|
||||
setDataSource(res?.data ?? []);
|
||||
setPagination({
|
||||
...pagination,
|
||||
total: res?.total ?? 0,
|
||||
} as any);
|
||||
}
|
||||
};
|
||||
|
||||
@ -178,6 +182,11 @@ function UserManagement() {
|
||||
</Button>
|
||||
</div>
|
||||
<Table
|
||||
style={{
|
||||
maxHeight: '82vh',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
sticky
|
||||
rowKey={'id'}
|
||||
dataSource={dataSource}
|
||||
columns={columns}
|
||||
|
@ -626,7 +626,8 @@ public class ChatController {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return schemaProperty;
|
||||
String cleanedInput = schemaProperty.replaceAll("[\r\t]", "");
|
||||
return cleanedInput;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,15 +1,5 @@
|
||||
package ai.chat2db.spi.sql;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
|
||||
import ai.chat2db.server.tools.common.util.I18nUtils;
|
||||
import ai.chat2db.spi.ValueHandler;
|
||||
@ -24,6 +14,12 @@ import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Dbhub 统一数据库连接管理
|
||||
*
|
||||
@ -342,8 +338,33 @@ public class SQLExecutor {
|
||||
*/
|
||||
public List<Table> tables(Connection connection, String databaseName, String schemaName, String tableName,
|
||||
String types[]) {
|
||||
try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, tableName,
|
||||
types)) {
|
||||
|
||||
try {
|
||||
DatabaseMetaData metadata = connection.getMetaData();
|
||||
ResultSet resultSet = metadata.getTables(databaseName, schemaName, tableName,
|
||||
types);
|
||||
// 如果connection为mysql
|
||||
if ("MySQL".equalsIgnoreCase(metadata.getDatabaseProductName())) {
|
||||
// 获取mysql表的comment
|
||||
List<Table> tables = ResultSetUtils.toObjectList(resultSet, Table.class);
|
||||
if (CollectionUtils.isNotEmpty(tables)) {
|
||||
for (Table table : tables) {
|
||||
String sql = "show table status where name = '" + table.getName() + "'";
|
||||
try (Statement stmt = connection.createStatement()) {
|
||||
boolean query = stmt.execute(sql);
|
||||
if (query) {
|
||||
try (ResultSet rs = stmt.getResultSet();) {
|
||||
while (rs.next()) {
|
||||
table.setComment(rs.getString("Comment"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
}
|
||||
return ResultSetUtils.toObjectList(resultSet, Table.class);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
Reference in New Issue
Block a user