Merge remote-tracking branch 'origin/dev' into feature/springdoc

# Conflicts:
#	yudao-dependencies/pom.xml
#	yudao-framework/yudao-spring-boot-starter-web/pom.xml
#	yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
#	yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/SpringFoxHandlerProviderBeanPostProcessor.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java
This commit is contained in:
xingyu
2023-01-16 14:25:03 +08:00
117 changed files with 2765 additions and 2561 deletions

View File

@ -111,6 +111,12 @@
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-file</artifactId>
</dependency>
<!-- WebSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -75,7 +75,7 @@ public class ConfigController {
if (config == null) {
return null;
}
if (config.getVisible()) {
if (!config.getVisible()) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);
}
return success(config.getValue());

View File

@ -11,9 +11,11 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import org.apache.ibatis.type.JdbcType;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
@ -37,7 +39,7 @@ public interface CodegenConvert {
@Mappings({
@Mapping(source = "name", target = "columnName"),
@Mapping(source = "type", target = "dataType"),
@Mapping(source = "metaInfo.jdbcType", target = "dataType", qualifiedByName = "getDataType"),
@Mapping(source = "comment", target = "columnComment"),
@Mapping(source = "metaInfo.nullable", target = "nullable"),
@Mapping(source = "keyFlag", target = "primaryKey"),
@ -47,6 +49,11 @@ public interface CodegenConvert {
})
CodegenColumnDO convert(TableField bean);
@Named("getDataType")
default String getDataType(JdbcType jdbcType) {
return jdbcType.name();
}
// ========== CodegenTableDO 相关 ==========
// List<CodegenTableRespVO> convertList02(List<CodegenTableDO> list);

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnu
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -29,7 +30,7 @@ public class CodegenColumnDO extends BaseDO {
private Long id;
/**
* 表编号
*
* <p>
* 关联 {@link CodegenTableDO#getId()}
*/
private Long tableId;
@ -41,7 +42,8 @@ public class CodegenColumnDO extends BaseDO {
*/
private String columnName;
/**
* 字段类型
* 数据库字段类型
* 关联 {@link TableField.MetaInfo#getJdbcType()}
*/
private String dataType;
/**
@ -69,7 +71,7 @@ public class CodegenColumnDO extends BaseDO {
/**
* Java 属性类型
*
* <p>
* 例如说 String、Boolean 等等
*/
private String javaType;
@ -79,7 +81,7 @@ public class CodegenColumnDO extends BaseDO {
private String javaField;
/**
* 字典类型
*
* <p>
* 关联 DictTypeDO 的 type 属性
*/
private String dictType;
@ -104,7 +106,7 @@ public class CodegenColumnDO extends BaseDO {
private Boolean listOperation;
/**
* List 查询操作的条件类型
*
* <p>
* 枚举 {@link CodegenColumnListConditionEnum}
*/
private String listOperationCondition;
@ -117,7 +119,7 @@ public class CodegenColumnDO extends BaseDO {
/**
* 显示类型
*
* <p>
* 枚举 {@link CodegenColumnHtmlTypeEnum}
*/
private String htmlType;

View File

@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.infra.websocket;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Semaphore;
/**
* 信号量相关处理
*
*/
@Slf4j
public class SemaphoreUtils {
/**
* 获取信号量
*
* @param semaphore
* @return
*/
public static boolean tryAcquire(Semaphore semaphore) {
boolean flag = false;
try {
flag = semaphore.tryAcquire();
} catch (Exception e) {
log.error("获取信号量异常", e);
}
return flag;
}
/**
* 释放信号量
*
* @param semaphore
*/
public static void release(Semaphore semaphore) {
try {
semaphore.release();
} catch (Exception e) {
log.error("释放信号量异常", e);
}
}
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.infra.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* websocket 配置
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.infra.websocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.Semaphore;
/**
* websocket 消息处理
*/
@Component
@ServerEndpoint("/websocket/message")
@Slf4j
public class WebSocketServer {
/**
* 默认最多允许同时在线用户数100
*/
public static int socketMaxOnlineCount = 100;
private static final Semaphore SOCKET_SEMAPHORE = new Semaphore(socketMaxOnlineCount);
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session) throws Exception {
// 尝试获取信号量
boolean semaphoreFlag = SemaphoreUtils.tryAcquire(SOCKET_SEMAPHORE);
if (!semaphoreFlag) {
// 未获取到信号量
log.error("当前在线人数超过限制数:{}", socketMaxOnlineCount);
WebSocketUsers.sendMessage(session, "当前在线人数超过限制数:" + socketMaxOnlineCount);
session.close();
} else {
String userId = WebSocketUsers.getParam("userId", session);
if (userId != null) {
// 添加用户
WebSocketUsers.addSession(userId, session);
log.info("用户【userId={}】建立连接,当前连接用户总数:{}", userId, WebSocketUsers.getUsers().size());
WebSocketUsers.sendMessage(session, "接收内容:连接成功");
} else {
WebSocketUsers.sendMessage(session, "接收内容:连接失败");
}
}
}
/**
* 连接关闭时处理
*/
@OnClose
public void onClose(Session session) {
log.info("用户【sessionId={}】关闭连接!", session.getId());
// 移除用户
WebSocketUsers.removeSession(session);
// 获取到信号量则需释放
SemaphoreUtils.release(SOCKET_SEMAPHORE);
}
/**
* 抛出异常时处理
*/
@OnError
public void onError(Session session, Throwable exception) throws Exception {
if (session.isOpen()) {
// 关闭连接
session.close();
}
String sessionId = session.getId();
log.info("用户【sessionId={}】连接异常!异常信息:{}", sessionId, exception);
// 移出用户
WebSocketUsers.removeSession(session);
// 获取到信号量则需释放
SemaphoreUtils.release(SOCKET_SEMAPHORE);
}
/**
* 收到客户端消息时调用的方法
*/
@OnMessage
public void onMessage(Session session, String message) {
WebSocketUsers.sendMessage(session, "接收内容:" + message);
}
}

View File

@ -0,0 +1,178 @@
package cn.iocoder.yudao.module.infra.websocket;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.Strings;
import javax.validation.constraints.NotNull;
import javax.websocket.Session;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* websocket 客户端用户
*/
@Slf4j
public class WebSocketUsers {
/**
* 用户集
* TODO 需要登录用户的session
*/
private static final Map<String, Session> SESSION_MAP = new ConcurrentHashMap<>();
/**
* 存储用户
*
* @param userId 唯一键
* @param session 用户信息
*/
public static void addSession(String userId, Session session) {
SESSION_MAP.put(userId, session);
}
/**
* 移除用户
*
* @param session 用户信息
* @return 移除结果
*/
public static boolean removeSession(Session session) {
String key = null;
boolean flag = SESSION_MAP.containsValue(session);
if (flag) {
Set<Map.Entry<String, Session>> entries = SESSION_MAP.entrySet();
for (Map.Entry<String, Session> entry : entries) {
Session value = entry.getValue();
if (value.equals(session)) {
key = entry.getKey();
break;
}
}
} else {
return true;
}
return removeSession(key);
}
/**
* 移出用户
*
* @param userId 用户id
*/
public static boolean removeSession(String userId) {
log.info("用户【userId={}】退出", userId);
Session remove = SESSION_MAP.remove(userId);
if (remove != null) {
boolean containsValue = SESSION_MAP.containsValue(remove);
log.info("用户【userId={}】退出{},当前连接用户总数:{}", userId, containsValue ? "失败" : "成功", SESSION_MAP.size());
return containsValue;
} else {
return true;
}
}
/**
* 获取在线用户列表
*
* @return 返回用户集合
*/
public static Map<String, Session> getUsers() {
return SESSION_MAP;
}
/**
* 向所有在线人发送消息
*
* @param message 消息内容
*/
public static void sendMessageToAll(String message) {
SESSION_MAP.forEach((userId, session) -> {
if (session.isOpen()) {
sendMessage(session, message);
}
});
}
/**
* 异步发送文本消息
*
* @param session 用户session
* @param message 消息内容
*/
public static void sendMessageAsync(Session session, String message) {
if (session.isOpen()) {
// TODO 需要加synchronized锁synchronized(session)单个session创建线程
session.getAsyncRemote().sendText(message);
} else {
log.warn("用户【session={}】不在线", session.getId());
}
}
/**
* 同步发送文本消息
*
* @param session 用户session
* @param message 消息内容
*/
public static void sendMessage(Session session, String message) {
try {
if (session.isOpen()) {
// TODO 需要加synchronized锁synchronized(session)单个session创建线程
session.getBasicRemote().sendText(message);
} else {
log.warn("用户【session={}】不在线", session.getId());
}
} catch (IOException e) {
log.error("发送消息异常", e);
}
}
/**
* 根据用户id发送消息
*
* @param userId 用户id
* @param message 消息内容
*/
public static void sendMessage(String userId, String message) {
Session session = SESSION_MAP.get(userId);
//判断是否存在该用户的session并且是否在线
if (session == null || !session.isOpen()) {
return;
}
sendMessage(session, message);
}
/**
* 获取session中的指定参数值
*
* @param key 参数key
* @param session 用户session
*/
public static String getParam(@NotNull String key, Session session) {
//TODO 目前只针对获取一个key的值后期根据情况拓展多个 或者直接在onClose onOpen上获取参数
String value = null;
Map<String, List<String>> parameters = session.getRequestParameterMap();
if (MapUtil.isNotEmpty(parameters)) {
value = parameters.get(key).get(0);
} else {
String queryString = session.getQueryString();
if (!StrUtil.isEmpty(queryString)) {
String[] params = Strings.split(queryString, '&');
for (String paramPair : params) {
String[] nameValues = Strings.split(paramPair, '=');
if (key.equals(nameValues[0])) {
value = nameValues[1];
}
}
}
}
return value;
}
}

View File

@ -17,7 +17,7 @@
preIcon="ep:download"
:title="t('action.export')"
v-hasPermi="['${permissionPrefix}:export']"
@click="handleExport()"
@click="exportList('${table.classComment}.xls')"
/>
</template>
<template #actionbtns_default="{ row }">
@ -40,7 +40,7 @@
preIcon="ep:delete"
:title="t('action.del')"
v-hasPermi="['${permissionPrefix}:delete']"
@click="handleDelete(row.id)"
@click="deleteData(row.id)"
/>
</template>
</XTable>
@ -119,11 +119,6 @@ const handleCreate = () => {
modelLoading.value = false
}
// 导出操作
const handleExport = async () => {
await exportList('${table.classComment}.xls')
}
// 修改操作
const handleUpdate = async (rowId: number) => {
setDialogTile('update')
@ -141,11 +136,6 @@ const handleDetail = async (rowId: number) => {
modelLoading.value = false
}
// 删除操作
const handleDelete = async (rowId: number) => {
await deleteData(rowId)
}
// 提交按钮
const submitForm = async () => {
const elForm = unref(formRef)?.getElFormRef()

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.infra.service;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.generator.IDatabaseQuery.DefaultDatabaseQuery;
import com.baomidou.mybatisplus.generator.query.DefaultQuery;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
@ -19,7 +19,7 @@ public class DefaultDatabaseQueryTest {
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null);
DefaultDatabaseQuery query = new DefaultDatabaseQuery(builder);
DefaultQuery query = new DefaultQuery(builder);
long time = System.currentTimeMillis();
List<TableInfo> tableInfos = query.queryTables();