feat : portal模块添加注释信息

This commit is contained in:
clay
2024-04-19 21:50:24 +08:00
parent 286e7cf2b8
commit fe89df9aa1
8 changed files with 160 additions and 21 deletions

View File

@@ -57,6 +57,7 @@ public class PortalController {
} }
@ApiOperation("新增接口") @ApiOperation("新增接口")
@Encrypt
@PostMapping @PostMapping
@PreAuthorize("@ss.hasPermission('query:portal:add')") @PreAuthorize("@ss.hasPermission('query:portal:add')")
public Result<PortalIdWrapper> add(@RequestBody @Validated PortalDto portalDto) { public Result<PortalIdWrapper> add(@RequestBody @Validated PortalDto portalDto) {
@@ -65,6 +66,7 @@ public class PortalController {
} }
@ApiOperation("修改接口") @ApiOperation("修改接口")
@Encrypt
@PutMapping @PutMapping
@PreAuthorize("@ss.hasPermission('query:portal:edit')") @PreAuthorize("@ss.hasPermission('query:portal:edit')")
public Result<PortalIdWrapper> edit(@RequestBody @Validated PortalDto portalDto) { public Result<PortalIdWrapper> edit(@RequestBody @Validated PortalDto portalDto) {

View File

@@ -53,10 +53,13 @@ public class PortalDispatchServlet {
} }
private void doDispatch(HttpServletRequest request, HttpServletResponse response) { private void doDispatch(HttpServletRequest request, HttpServletResponse response) {
//获取到请求路径
String path = request.getServletPath(); String path = request.getServletPath();
//获取请求方法
String method = request.getMethod(); String method = request.getMethod();
//从Redis中获取到当前的方法信息
PortalBo portalBo = redisTemplate.opsForValue().get(QueryConstant.PORTAL_KEY + path + ":" + method); PortalBo portalBo = redisTemplate.opsForValue().get(QueryConstant.PORTAL_KEY + path + ":" + method);
//判断是否为空,如果为空则从数据库中查找
if (portalBo == null) { if (portalBo == null) {
Portal portal = portalMapper.selectByPath(path, method); Portal portal = portalMapper.selectByPath(path, method);
if (portal == null) { if (portal == null) {
@@ -67,10 +70,10 @@ public class PortalDispatchServlet {
portalBo = PortalBo.toPortalBo(portal, portalMappings); portalBo = PortalBo.toPortalBo(portal, portalMappings);
redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo); redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo);
} }
//自定义查询编辑查询对象
SearchInfo searchInfo = new SearchInfo(); SearchInfo searchInfo = new SearchInfo();
List<UniConDto> uniConList = new ArrayList<>(); List<UniConDto> uniConList = new ArrayList<>();
//根据映射关系从request中获取请求参数
for (PortalMapping portalMapping : portalBo.getMappings()) { for (PortalMapping portalMapping : portalBo.getMappings()) {
UniConDto uniCon = new UniConDto(); UniConDto uniCon = new UniConDto();
String mappingValue = portalMapping.getMappingValue(); String mappingValue = portalMapping.getMappingValue();
@@ -86,14 +89,16 @@ public class PortalDispatchServlet {
uniConList.add(uniCon); uniConList.add(uniCon);
} }
searchInfo.setList(uniConList); searchInfo.setList(uniConList);
//获取当当前接口对应的数据适配器
DataAdapter dataAdapter = dataAdapterMapper.selectById(portalBo.getAdapterId()); DataAdapter dataAdapter = dataAdapterMapper.selectById(portalBo.getAdapterId());
//进行数据适配器的执行逻辑
Object result = null; Object result = null;
if (portalBo.getState() == 1 || portalBo.getState() == 2) { if (portalBo.getState() == 1 || portalBo.getState() == 2) {
result = dataAdapterHandler.execute(dataAdapter, portalBo, searchInfo); result = dataAdapterHandler.execute(dataAdapter, portalBo, searchInfo);
} else { } else {
result = dataAdapterHandler.mockExecute(dataAdapter, portalBo, searchInfo); result = dataAdapterHandler.mockExecute(dataAdapter, portalBo, searchInfo);
} }
//将返回结果放入response
ResponseRender.renderString(response, Result.ok(result)); ResponseRender.renderString(response, Result.ok(result));
} }

View File

@@ -51,19 +51,25 @@ public class DispatchSyncService {
public DispatchSyncService(NacosDiscoveryProperties nacosDiscoveryProperties, Environment environment, NacosRegistration nacosRegistration, ThreadPoolTaskExecutor taskExecuteExecutor, DubboServiceBean serviceBean) { public DispatchSyncService(NacosDiscoveryProperties nacosDiscoveryProperties, Environment environment, NacosRegistration nacosRegistration, ThreadPoolTaskExecutor taskExecuteExecutor, DubboServiceBean serviceBean) {
//获取当前主机地址
this.host = nacosRegistration.getHost(); this.host = nacosRegistration.getHost();
//获取到端口信息的bean
this.serviceBean = serviceBean; this.serviceBean = serviceBean;
NacosServiceManager nacosServiceManager = new NacosServiceManager(); NacosServiceManager nacosServiceManager = new NacosServiceManager();
Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties(); Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties();
//获取dubbo的命名空间
String namespace = environment.getProperty("dubbo.registry.parameters.namespace"); String namespace = environment.getProperty("dubbo.registry.parameters.namespace");
//获取到当前服务名称
this.serviceName = environment.getProperty("dubbo.application.name"); this.serviceName = environment.getProperty("dubbo.application.name");
nacosProperties.setProperty(PropertyKeyConst.NAMESPACE, namespace); nacosProperties.setProperty(PropertyKeyConst.NAMESPACE, namespace);
//获取到nacos的管理服务
this.namingService = nacosServiceManager.getNamingService(nacosProperties); this.namingService = nacosServiceManager.getNamingService(nacosProperties);
//启动单线程,监听阻塞队列
new Thread(() -> { new Thread(() -> {
while (true) { while (true) {
try { try {
Runnable take = queue.take(); Runnable take = queue.take();
//执行同步任务
taskExecuteExecutor.execute(take); taskExecuteExecutor.execute(take);
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.info("", e); log.info("", e);
@@ -80,24 +86,33 @@ public class DispatchSyncService {
private void syncDispatch(String path, String requestMethod, Boolean publish) { private void syncDispatch(String path, String requestMethod, Boolean publish) {
try { try {
//获取nacos中的服务信息
List<Instance> allInstances = namingService.getAllInstances(serviceName); List<Instance> allInstances = namingService.getAllInstances(serviceName);
for (Instance instance : allInstances) { for (Instance instance : allInstances) {
//判断当前服务是否为本服务
if (!(instance.getIp().equals(host) && instance.getPort() == serviceBean.getPort())) { if (!(instance.getIp().equals(host) && instance.getPort() == serviceBean.getPort())) {
//不是本服务则需要发起同步任务
Task task = new Task(instance.getIp(), instance.getPort(), path, publish, requestMethod) { Task task = new Task(instance.getIp(), instance.getPort(), path, publish, requestMethod) {
@Override @Override
public void run() { public void run() {
try { try {
//获取云端服务信息
List<Instance> allInstances = namingService.getAllInstances(serviceName); List<Instance> allInstances = namingService.getAllInstances(serviceName);
//如果没有则不进行同步
if (ObjectUtils.isEmpty(allInstances)) { if (ObjectUtils.isEmpty(allInstances)) {
return; return;
} }
//检查云端实例中是否还存在当前服务,不存在当前任务结束
Optional<Instance> optional = allInstances.stream().filter(instance -> this.ip.equals(instance.getIp()) && this.port == instance.getPort()).findAny(); Optional<Instance> optional = allInstances.stream().filter(instance -> this.ip.equals(instance.getIp()) && this.port == instance.getPort()).findAny();
if (optional.isEmpty()) { if (optional.isEmpty()) {
return; return;
} }
Instance instance = optional.get(); Instance instance = optional.get();
//指定需要同步服务的ip + port
UserSpecifiedAddressUtil.setAddress(new Address(instance.getIp(), instance.getPort(), true)); UserSpecifiedAddressUtil.setAddress(new Address(instance.getIp(), instance.getPort(), true));
//记录同步状态
Boolean state = null; Boolean state = null;
//是否为发布任务
if (publish) { if (publish) {
log.info("dubboDispatchServletPublish.publish({}, {})", path, requestMethod); log.info("dubboDispatchServletPublish.publish({}, {})", path, requestMethod);
state = dubboDispatchServletPublish.publish(path, requestMethod); state = dubboDispatchServletPublish.publish(path, requestMethod);
@@ -105,17 +120,22 @@ public class DispatchSyncService {
log.info("dubboDispatchServletPublish.unpublish({}, {})", path, requestMethod); log.info("dubboDispatchServletPublish.unpublish({}, {})", path, requestMethod);
state = dubboDispatchServletPublish.unpublish(path, requestMethod); state = dubboDispatchServletPublish.unpublish(path, requestMethod);
} }
//如果为false,则将当前任务重新放入到队列中,等待下一次执行
if (state == null || !state) { if (state == null || !state) {
queue.add(this); queue.add(this);
} }
} catch (NacosException e) { } catch (NacosException e) {
log.error("NacosException: {}", e.getMessage()); log.error("NacosException: {}", e.getMessage());
//如果发生异常也是
//todo 后续可以添加当前任务报错次数的处理
queue.add(this); queue.add(this);
} finally { } finally {
//将指定的地址恢复
UserSpecifiedAddressUtil.setAddress(null); UserSpecifiedAddressUtil.setAddress(null);
} }
} }
}; };
//像阻塞队列中添加任务
queue.add(task); queue.add(task);
} }
} }
@@ -126,15 +146,25 @@ public class DispatchSyncService {
private static abstract class Task implements Runnable { private static abstract class Task implements Runnable {
/**
* 服务ip
*/
protected final String ip; protected final String ip;
/**
* 服务端口
*/
protected final int port; protected final int port;
/**
* 路径
*/
protected final String path; protected final String path;
/**
* 是否为发布
*/
protected final Boolean publish; protected final Boolean publish;
/**
* 方法类型
*/
protected final String requestMethod; protected final String requestMethod;

View File

@@ -55,6 +55,7 @@ public class HandlerMethodService implements ApplicationContextAware {
public HandlerMethodService() { public HandlerMethodService() {
//固定PortalDispatchServlet为入口类,其中doDispatch为入口方法
Class<PortalDispatchServlet> portalDispatchServletClass = PortalDispatchServlet.class; Class<PortalDispatchServlet> portalDispatchServletClass = PortalDispatchServlet.class;
Method[] declaredMethods = portalDispatchServletClass.getDeclaredMethods(); Method[] declaredMethods = portalDispatchServletClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) { for (Method declaredMethod : declaredMethods) {
@@ -65,11 +66,13 @@ public class HandlerMethodService implements ApplicationContextAware {
if (mappingMethod == null) { if (mappingMethod == null) {
log.error("mappingMethod is null"); log.error("mappingMethod is null");
} }
//设置权限
mappingMethod.setAccessible(Boolean.TRUE); mappingMethod.setAccessible(Boolean.TRUE);
} }
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//获取到web mvc的接口存储mapping
mapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping"); mapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
} }
@@ -95,7 +98,9 @@ public class HandlerMethodService implements ApplicationContextAware {
throw new CustomException("path is exist"); throw new CustomException("path is exist");
} }
mapping.registerMapping(requestMappingInfo, "portalDispatchServlet", mappingMethod); mapping.registerMapping(requestMappingInfo, "portalDispatchServlet", mappingMethod);
// 判断是否需要发起同步
if (sync) { if (sync) {
//使用事件监听机制,避免循环注入
SpringContextHolder.publishEvent(new DispatchSyncEvent(path, requestMethod.name(), Boolean.TRUE)); SpringContextHolder.publishEvent(new DispatchSyncEvent(path, requestMethod.name(), Boolean.TRUE));
} }
} }

View File

@@ -15,16 +15,33 @@ public interface PortalService {
/** /**
* 根据id查询 * 根据id查询
*
* @param portalId id * @param portalId id
* @return 结果 * @return 结果
*/ */
PortalVo searchById(Long portalId); PortalVo searchById(Long portalId);
/**
* 查询接口列表
*
* @param query 查询条件
* @return 查询表格结果
*/
TableDataInfo<SimplePortalVo> searchList(PortalQuery query); TableDataInfo<SimplePortalVo> searchList(PortalQuery query);
/**
* 保存接口信息
*
* @param portalDto 接口信息
* @return 保存完成后接口id和数据适配器id
*/
PortalIdWrapper save(PortalDto portalDto); PortalIdWrapper save(PortalDto portalDto);
/**
* 修改接口信息
*
* @param portalDto 接口信息
* @return 修改完成后接口id和数据适配器id
*/
PortalIdWrapper edit(PortalDto portalDto); PortalIdWrapper edit(PortalDto portalDto);
} }

View File

@@ -133,6 +133,7 @@ public class PortalServiceImpl implements PortalService {
adapterIds.add(portal.getAdapterId()); adapterIds.add(portal.getAdapterId());
} }
}); });
//映射自定义擦洗名称
Map<Long, UniQuery> queryMap = new HashMap<>(); Map<Long, UniQuery> queryMap = new HashMap<>();
if (!ObjectUtils.isEmpty(queryIds)) { if (!ObjectUtils.isEmpty(queryIds)) {
List<UniQuery> queryList = queryMapper.selectListByIds(queryIds); List<UniQuery> queryList = queryMapper.selectListByIds(queryIds);
@@ -140,6 +141,7 @@ public class PortalServiceImpl implements PortalService {
queryMap.put(uniQuery.getId(), uniQuery); queryMap.put(uniQuery.getId(), uniQuery);
} }
} }
//映射数据适配器名称
Map<Long, String> adapterMap = new HashMap<>(); Map<Long, String> adapterMap = new HashMap<>();
if (!ObjectUtils.isEmpty(adapterIds)) { if (!ObjectUtils.isEmpty(adapterIds)) {
List<DataAdapter> adapterList = adapterMapper.selectListByIds(adapterIds); List<DataAdapter> adapterList = adapterMapper.selectListByIds(adapterIds);
@@ -147,6 +149,7 @@ public class PortalServiceImpl implements PortalService {
adapterMap.put(adapter.getAdapterId(), adapter.getAdapterName()); adapterMap.put(adapter.getAdapterId(), adapter.getAdapterName());
} }
} }
//映射重组
return PageUtils.convertDataTable(list, portal -> { return PageUtils.convertDataTable(list, portal -> {
SimplePortalVo simplePortalVo = SimplePortalVo.toPortalVo(portal); SimplePortalVo simplePortalVo = SimplePortalVo.toPortalVo(portal);
UniQuery uniQuery = queryMap.getOrDefault(portal.getQueryId(), null); UniQuery uniQuery = queryMap.getOrDefault(portal.getQueryId(), null);
@@ -191,6 +194,7 @@ public class PortalServiceImpl implements PortalService {
} }
portalMappingMapper.insertBatch(mappings); portalMappingMapper.insertBatch(mappings);
} }
//发布接口
publishPortal(portal, mappings, Boolean.TRUE); publishPortal(portal, mappings, Boolean.TRUE);
return PortalIdWrapper.builder() return PortalIdWrapper.builder()
.portalId(String.valueOf(portal.getPortalId())) .portalId(String.valueOf(portal.getPortalId()))
@@ -215,12 +219,17 @@ public class PortalServiceImpl implements PortalService {
} }
} }
PortalBo portalBo = PortalBo.toPortalBo(portal, portalDto.getMappings()); PortalBo portalBo = PortalBo.toPortalBo(portal, portalDto.getMappings());
//存在接口路径或者请求类型不同则需要重新发布
if (!old.getPath().equals(portal.getPath()) if (!old.getPath().equals(portal.getPath())
|| !old.getRequestMethod().equals(portal.getRequestMethod())) { || !old.getRequestMethod().equals(portal.getRequestMethod())) {
//先卸载接口,需要进行数据同步
unpublishPortal(old, true); unpublishPortal(old, true);
//注册新的映射
methodService.registerMapping(portalBo.getPath(), RequestMethod.valueOf(portalBo.getRequestMethod()), true); methodService.registerMapping(portalBo.getPath(), RequestMethod.valueOf(portalBo.getRequestMethod()), true);
} }
//修改Redis中的数据信息
redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo); redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo);
//返回接口id和适配器id
return PortalIdWrapper.builder() return PortalIdWrapper.builder()
.portalId(String.valueOf(portal.getPortalId())) .portalId(String.valueOf(portal.getPortalId()))
.adapterId(String.valueOf(portal.getAdapterId())) .adapterId(String.valueOf(portal.getAdapterId()))
@@ -254,19 +263,36 @@ public class PortalServiceImpl implements PortalService {
return false; return false;
} }
/**
* 发布接口
*
* @param portal 接口信息
* @param mappings 映射信息
* @param sync 是否同步
*/
private void publishPortal(Portal portal, List<PortalMapping> mappings, Boolean sync) { private void publishPortal(Portal portal, List<PortalMapping> mappings, Boolean sync) {
PortalBo portalBo = PortalBo.toPortalBo(portal, mappings); PortalBo portalBo = PortalBo.toPortalBo(portal, mappings);
redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo); redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo);
methodService.registerMapping(portalBo.getPath(), RequestMethod.valueOf(portalBo.getRequestMethod()), sync); methodService.registerMapping(portalBo.getPath(), RequestMethod.valueOf(portalBo.getRequestMethod()), sync);
} }
/**
* 取消接口映射
*
* @param portal 接口信息
* @param sync 是否同步
*/
private void unpublishPortal(Portal portal, Boolean sync) { private void unpublishPortal(Portal portal, Boolean sync) {
methodService.unregisterMapping(portal.getPath(), RequestMethod.valueOf(portal.getRequestMethod()), sync); methodService.unregisterMapping(portal.getPath(), RequestMethod.valueOf(portal.getRequestMethod()), sync);
redisTemplate.delete(QueryConstant.PORTAL_KEY + portal.getPath() + ":" + portal.getRequestMethod()); redisTemplate.delete(QueryConstant.PORTAL_KEY + portal.getPath() + ":" + portal.getRequestMethod());
} }
/**
* 创建数据适配器
*
* @param portalDto 接口dto对象
* @param portal 接口信息
*/
private void createDataAdapter(PortalDto portalDto, Portal portal) { private void createDataAdapter(PortalDto portalDto, Portal portal) {
DataAdapterDto dataAdapterDto = portalDto.getDataAdapter(); DataAdapterDto dataAdapterDto = portalDto.getDataAdapter();
if (ObjectUtils.isEmpty(dataAdapterDto)) { if (ObjectUtils.isEmpty(dataAdapterDto)) {
@@ -278,12 +304,19 @@ public class PortalServiceImpl implements PortalService {
} }
dataAdapter.setAdapterName(portal.getPortalName() + "专用数据适配器!"); dataAdapter.setAdapterName(portal.getPortalName() + "专用数据适配器!");
dataAdapter.setCommon(Boolean.FALSE); dataAdapter.setCommon(Boolean.FALSE);
//初始化对应的数据适配器
dataAdapter.init(); dataAdapter.init();
//插入数据适配器
adapterMapper.insert(dataAdapter); adapterMapper.insert(dataAdapter);
//会写数据适配器id
portal.setAdapterId(dataAdapter.getAdapterId()); portal.setAdapterId(dataAdapter.getAdapterId());
} }
/**
* 检查接口类型是否正确
*
* @param portal 接口信息
*/
private void checkPortalType(Portal portal) { private void checkPortalType(Portal portal) {
if (PortalEnum.LOCAL.equals(portal.getType())) { if (PortalEnum.LOCAL.equals(portal.getType())) {
if (ObjectUtils.isEmpty(portal.getQueryId())) { if (ObjectUtils.isEmpty(portal.getQueryId())) {

View File

@@ -13,9 +13,7 @@
<if test="uniCon.ucCon == 'GTE'">and ${uniCon.ucKey} >= #{uniCon.ucMock}</if> <if test="uniCon.ucCon == 'GTE'">and ${uniCon.ucKey} >= #{uniCon.ucMock}</if>
<if test="uniCon.ucCon == 'LT'">and ${uniCon.ucKey} &lt; #{uniCon.ucMock}</if> <if test="uniCon.ucCon == 'LT'">and ${uniCon.ucKey} &lt; #{uniCon.ucMock}</if>
<if test="uniCon.ucCon == 'LTE'">and ${uniCon.ucKey} &lt;= #{uniCon.ucMock}</if> <if test="uniCon.ucCon == 'LTE'">and ${uniCon.ucKey} &lt;= #{uniCon.ucMock}</if>
<if test="uniCon.ucCon == 'LIKE'">and ${uniCon.ucKey} like concat(concat('%', #{uniCon.ucMock}), <if test="uniCon.ucCon == 'LIKE'">and ${uniCon.ucKey} like concat(concat('%', #{uniCon.ucMock}),'%') </if>
'%')
</if>
</if> </if>
<if test="uniCon.ucMock != null and uniCon.ucType != 'input'"> <if test="uniCon.ucMock != null and uniCon.ucType != 'input'">
<if test="uniCon.ucCon == 'EQ'">and ${uniCon.ucKey} = #{uniCon.ucMock}</if> <if test="uniCon.ucCon == 'EQ'">and ${uniCon.ucKey} = #{uniCon.ucMock}</if>
@@ -24,9 +22,7 @@
<if test="uniCon.ucCon == 'GTE'">and ${uniCon.ucKey} >= #{uniCon.ucMock}</if> <if test="uniCon.ucCon == 'GTE'">and ${uniCon.ucKey} >= #{uniCon.ucMock}</if>
<if test="uniCon.ucCon == 'LT'">and ${uniCon.ucKey} &lt; #{uniCon.ucMock}</if> <if test="uniCon.ucCon == 'LT'">and ${uniCon.ucKey} &lt; #{uniCon.ucMock}</if>
<if test="uniCon.ucCon == 'LTE'">and ${uniCon.ucKey} &lt;= #{uniCon.ucMock}</if> <if test="uniCon.ucCon == 'LTE'">and ${uniCon.ucKey} &lt;= #{uniCon.ucMock}</if>
<if test="uniCon.ucCon == 'LIKE'">and ${uniCon.ucKey} like concat(concat('%', #{uniCon.ucMock}), <if test="uniCon.ucCon == 'LIKE'">and ${uniCon.ucKey} like concat(concat('%', #{uniCon.ucMock}), '%')</if>
'%')
</if>
<if test="uniCon.ucCon == 'BETWEEN'">and ${uniCon.ucKey} between #{uniCon.begin} and #{uniCon.end} <if test="uniCon.ucCon == 'BETWEEN'">and ${uniCon.ucKey} between #{uniCon.begin} and #{uniCon.end}
</if> </if>
</if> </if>

View File

@@ -0,0 +1,51 @@
select ut.table_name,utc.comments as table_comment,ut.last_analyzed as update_time
from user_tables ut
left join user_tab_comments utc on ut.table_name = utc.table_name
SELECT * FROM dba_tables dt
LEFT JOIN user_tab_comments utc on dt.table_name = utc.table_name
WHERE owner = 'FATEVERSE_LOG'
-- 使用CURRENT_SCHEMA函数获取当前模式
SELECT CURRENT_SCHEMA() AS current_schema;
-- 使用CURRENT_USER函数获取当前用户及其默认模式
SELECT CURRENT_USER AS current_user;
SELECT dt.table_name,utc.comments as table_comment FROM dba_tables dt
LEFT JOIN user_tab_comments utc on dt.table_name = utc.table_name
WHERE owner = 'FATEVERSE_LOG's
SELECT * FROM user_tables
select distinct utc.column_name,
utc.table_name as remark,
CONCAT(CONCAT(CONCAT(lower(utc.data_type),'('), utc.data_length),')') as column_type,
utc.data_scale as column_scale,
utc.column_id as sort,
(case when uc.constraint_type = 'P' then '1' else '0' end) as pk ,
ucc.comments as column_comment
from user_tab_columns utc
inner join user_col_comments ucc on ucc.column_name = utc.column_name and ucc.table_name = utc.table_name
left join user_cons_columns uccs on uccs.column_name = utc.column_name
left join user_constraints uc on uc.constraint_name = uccs.constraint_name
select ut.table_name,utc.comments as table_comment,ut.last_analyzed as update_time
from user_tables ut
left join user_tab_comments utc on ut.table_name = utc.table_name
select 1 from dual