feat: 添加异常日志功能并优化UI样式
refactor: 重构后端查询逻辑和API响应处理 fix: 修复用户角色更新和文件上传问题 test: 添加前端性能测试脚本和E2E测试用例 chore: 更新依赖版本和配置文件 docs: 添加环境检查脚本和测试文档 style: 统一表格标签样式和路由命名 perf: 优化前端页面加载速度和响应时间
This commit is contained in:
@@ -38,10 +38,17 @@ public class QueryUtil {
|
||||
criteria = criteria.and("deletedAt").isNull();
|
||||
}
|
||||
if (query == null) {
|
||||
log.info("Query object is null, returning empty criteria");
|
||||
return Query.query(criteria);
|
||||
}
|
||||
System.out.println("=== QueryUtil.getQuery START ===");
|
||||
System.out.println("Query object class: " + query.getClass().getName());
|
||||
log.info("=== QueryUtil.getQuery START ===");
|
||||
log.info("Query object class: {}", query.getClass().getName());
|
||||
try {
|
||||
List<Field> fields = getAllFields(query.getClass(), new ArrayList<>());
|
||||
log.info("Found {} fields to process", fields.size());
|
||||
System.out.println("Found " + fields.size() + " fields to process");
|
||||
for (Field field : fields) {
|
||||
boolean accessible = Modifier.isStatic(field.getModifiers()) ? field.canAccess(null)
|
||||
: field.canAccess(query);
|
||||
@@ -52,16 +59,31 @@ public class QueryUtil {
|
||||
String blurry = q.blurry();
|
||||
String attributeName = isBlank(propName) ? field.getName() : propName;
|
||||
Object val = field.get(query);
|
||||
log.info("Processing field: {}, value: {}, blurry: {}", attributeName, val, blurry);
|
||||
System.out.println("Processing field: " + attributeName + ", value: " + val + ", blurry: " + blurry);
|
||||
if (val == null || "".equals(val)) {
|
||||
log.info("Field {} has null or empty value, skipping", attributeName);
|
||||
System.out.println("Field " + attributeName + " has null or empty value, skipping");
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotBlank(blurry)) {
|
||||
log.info("Field {} has blurry search configuration: {}", attributeName, blurry);
|
||||
System.out.println("Field " + attributeName + " has blurry search configuration: " + blurry);
|
||||
String[] blurrys = blurry.split(",");
|
||||
Criteria orCriteria = Criteria.empty();
|
||||
for (String s : blurrys) {
|
||||
orCriteria = orCriteria.or(s).like("%" + val + "%");
|
||||
Criteria orCriteria = null;
|
||||
for (int i = 0; i < blurrys.length; i++) {
|
||||
String s = blurrys[i];
|
||||
if (i == 0) {
|
||||
orCriteria = Criteria.where(s).like("%" + val + "%");
|
||||
} else {
|
||||
orCriteria = orCriteria.or(s).like("%" + val + "%");
|
||||
}
|
||||
}
|
||||
if (orCriteria != null) {
|
||||
criteria = criteria.and(orCriteria);
|
||||
log.info("Added OR criteria for blurry search: {} with value: {}", blurry, val);
|
||||
System.out.println("Added OR criteria for blurry search: " + blurry + " with value: " + val);
|
||||
}
|
||||
criteria = criteria.and(orCriteria);
|
||||
continue;
|
||||
}
|
||||
switch (q.type()) {
|
||||
|
||||
@@ -20,6 +20,8 @@ public interface SysUserDao extends R2dbcRepository<SysUserEntity, Long> {
|
||||
|
||||
Mono<SysUserEntity> findByEmailAndDeletedAtIsNull(String email);
|
||||
|
||||
Mono<SysUserEntity> findByIdAndDeletedAtIsNull(Long id);
|
||||
|
||||
Flux<SysUserEntity> findAll();
|
||||
|
||||
Flux<SysUserEntity> findAll(Sort sort);
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cn.novalon.manage.db.entity;
|
||||
|
||||
import cn.novalon.manage.sys.core.query.OperationLogQuery;
|
||||
import cn.novalon.manage.db.dao.QueryField;
|
||||
|
||||
/**
|
||||
* 操作日志查询条件对象
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
public class OperationLogQueryCriteria {
|
||||
|
||||
@QueryField(propName = "username", type = QueryField.Type.INNER_LIKE)
|
||||
private String username;
|
||||
|
||||
@QueryField(propName = "operation", type = QueryField.Type.INNER_LIKE)
|
||||
private String operation;
|
||||
|
||||
@QueryField(propName = "status", type = QueryField.Type.EQUAL)
|
||||
private String status;
|
||||
|
||||
@QueryField(blurry = "username,operation,ip", type = QueryField.Type.INNER_LIKE)
|
||||
private String keyword;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public void setOperation(String operation) {
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从领域查询对象转换
|
||||
*
|
||||
* @param query 领域查询对象
|
||||
*/
|
||||
public void convert(OperationLogQuery query) {
|
||||
if (query == null) {
|
||||
return;
|
||||
}
|
||||
this.username = query.getUsername();
|
||||
this.operation = query.getOperation();
|
||||
this.status = query.getStatus();
|
||||
this.keyword = query.getKeyword();
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cn.novalon.manage.db.entity;
|
||||
|
||||
import cn.novalon.manage.sys.core.query.SysExceptionLogQuery;
|
||||
import cn.novalon.manage.db.dao.QueryField;
|
||||
|
||||
/**
|
||||
* 异常日志查询条件对象
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
public class SysExceptionLogQueryCriteria {
|
||||
|
||||
@QueryField(propName = "username", type = QueryField.Type.INNER_LIKE)
|
||||
private String username;
|
||||
|
||||
@QueryField(propName = "title", type = QueryField.Type.INNER_LIKE)
|
||||
private String title;
|
||||
|
||||
@QueryField(propName = "exceptionName", type = QueryField.Type.INNER_LIKE)
|
||||
private String exceptionName;
|
||||
|
||||
@QueryField(blurry = "username,title,exceptionName,ip", type = QueryField.Type.INNER_LIKE)
|
||||
private String keyword;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getExceptionName() {
|
||||
return exceptionName;
|
||||
}
|
||||
|
||||
public void setExceptionName(String exceptionName) {
|
||||
this.exceptionName = exceptionName;
|
||||
}
|
||||
|
||||
public String getKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从领域查询对象转换
|
||||
*
|
||||
* @param query 领域查询对象
|
||||
*/
|
||||
public void convert(SysExceptionLogQuery query) {
|
||||
if (query == null) {
|
||||
return;
|
||||
}
|
||||
this.username = query.getUsername();
|
||||
this.title = query.getTitle();
|
||||
this.exceptionName = query.getExceptionName();
|
||||
this.keyword = query.getKeyword();
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -25,7 +25,7 @@ public class SysFileEntity {
|
||||
private String filePath;
|
||||
|
||||
@Column("file_size")
|
||||
private String fileSize;
|
||||
private Long fileSize;
|
||||
|
||||
@Column("file_type")
|
||||
private String fileType;
|
||||
@@ -69,11 +69,11 @@ public class SysFileEntity {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public String getFileSize() {
|
||||
public Long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(String fileSize) {
|
||||
public void setFileSize(Long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cn.novalon.manage.db.entity;
|
||||
|
||||
import cn.novalon.manage.sys.core.query.SysLoginLogQuery;
|
||||
import cn.novalon.manage.db.dao.QueryField;
|
||||
|
||||
/**
|
||||
* 登录日志查询条件对象
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
public class SysLoginLogQueryCriteria {
|
||||
|
||||
@QueryField(propName = "username", type = QueryField.Type.INNER_LIKE)
|
||||
private String username;
|
||||
|
||||
@QueryField(propName = "ip", type = QueryField.Type.INNER_LIKE)
|
||||
private String ip;
|
||||
|
||||
@QueryField(propName = "status", type = QueryField.Type.EQUAL)
|
||||
private String status;
|
||||
|
||||
@QueryField(blurry = "username,ip,location", type = QueryField.Type.INNER_LIKE)
|
||||
private String keyword;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从领域查询对象转换
|
||||
*
|
||||
* @param query 领域查询对象
|
||||
*/
|
||||
public void convert(SysLoginLogQuery query) {
|
||||
if (query == null) {
|
||||
return;
|
||||
}
|
||||
this.username = query.getUsername();
|
||||
this.ip = query.getIp();
|
||||
this.status = query.getStatus();
|
||||
this.keyword = query.getKeyword();
|
||||
}
|
||||
}
|
||||
+28
-4
@@ -1,6 +1,6 @@
|
||||
package cn.novalon.manage.db.entity;
|
||||
|
||||
import cn.novalon.manage.common.domain.query.SysMenuQuery;
|
||||
import cn.novalon.manage.sys.core.query.SysMenuQuery;
|
||||
import cn.novalon.manage.db.dao.QueryField;
|
||||
|
||||
/**
|
||||
@@ -18,7 +18,13 @@ public class SysMenuQueryCriteria {
|
||||
private String menuType;
|
||||
|
||||
@QueryField(propName = "status", type = QueryField.Type.EQUAL)
|
||||
private String status;
|
||||
private Integer status;
|
||||
|
||||
@QueryField(propName = "parentId", type = QueryField.Type.EQUAL)
|
||||
private Long parentId;
|
||||
|
||||
@QueryField(blurry = "menuName,perms,component", type = QueryField.Type.INNER_LIKE)
|
||||
private String keyword;
|
||||
|
||||
public String getMenuName() {
|
||||
return menuName;
|
||||
@@ -36,14 +42,30 @@ public class SysMenuQueryCriteria {
|
||||
this.menuType = menuType;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
public Integer getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
public void setStatus(Integer status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从领域查询对象转换
|
||||
*
|
||||
@@ -56,5 +78,7 @@ public class SysMenuQueryCriteria {
|
||||
this.menuName = query.getMenuName();
|
||||
this.menuType = query.getMenuType();
|
||||
this.status = query.getStatus();
|
||||
this.parentId = query.getParentId();
|
||||
this.keyword = query.getKeyword();
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -20,6 +20,9 @@ public class SysRoleQueryCriteria {
|
||||
@QueryField(propName = "status", type = QueryField.Type.EQUAL)
|
||||
private Integer status;
|
||||
|
||||
@QueryField(blurry = "roleName,roleKey", type = QueryField.Type.INNER_LIKE)
|
||||
private String keyword;
|
||||
|
||||
public String getRoleName() {
|
||||
return roleName;
|
||||
}
|
||||
@@ -44,6 +47,14 @@ public class SysRoleQueryCriteria {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从领域查询对象转换
|
||||
*
|
||||
@@ -56,5 +67,6 @@ public class SysRoleQueryCriteria {
|
||||
this.roleName = query.getRoleName();
|
||||
this.roleKey = query.getRoleKey();
|
||||
this.status = query.getStatus();
|
||||
this.keyword = query.getKeyword();
|
||||
}
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package cn.novalon.manage.db.entity;
|
||||
|
||||
import cn.novalon.manage.notify.core.query.SysUserMessageQuery;
|
||||
import cn.novalon.manage.db.dao.QueryField;
|
||||
|
||||
/**
|
||||
* 用户消息查询条件对象
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
public class SysUserMessageQueryCriteria {
|
||||
|
||||
@QueryField(propName = "userId", type = QueryField.Type.EQUAL)
|
||||
private Long userId;
|
||||
|
||||
@QueryField(propName = "isRead", type = QueryField.Type.EQUAL)
|
||||
private String isRead;
|
||||
|
||||
@QueryField(blurry = "title,content", type = QueryField.Type.INNER_LIKE)
|
||||
private String keyword;
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getIsRead() {
|
||||
return isRead;
|
||||
}
|
||||
|
||||
public void setIsRead(String isRead) {
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
public String getKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从领域查询对象转换
|
||||
*
|
||||
* @param query 领域查询对象
|
||||
*/
|
||||
public void convert(SysUserMessageQuery query) {
|
||||
if (query == null) {
|
||||
return;
|
||||
}
|
||||
this.userId = query.getUserId();
|
||||
this.isRead = query.getIsRead();
|
||||
this.keyword = query.getKeyword();
|
||||
}
|
||||
}
|
||||
+17
-1
@@ -1,7 +1,7 @@
|
||||
package cn.novalon.manage.db.entity;
|
||||
|
||||
import cn.novalon.manage.sys.core.query.SysUserQuery;
|
||||
import cn.novalon.manage.db.dao.QueryField;
|
||||
import cn.novalon.manage.common.dao.QueryField;
|
||||
|
||||
/**
|
||||
* 用户查询条件对象
|
||||
@@ -81,4 +81,20 @@ public class SysUserQueryCriteria {
|
||||
this.status = query.getStatus();
|
||||
this.keyword = query.getKeyword();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从领域查询对象转换(不包含keyword)
|
||||
*
|
||||
* @param query 领域查询对象
|
||||
*/
|
||||
public void convertWithoutKeyword(SysUserQuery query) {
|
||||
if (query == null) {
|
||||
return;
|
||||
}
|
||||
this.username = query.getUsername();
|
||||
this.email = query.getEmail();
|
||||
this.roleId = query.getRoleId();
|
||||
this.status = query.getStatus();
|
||||
this.keyword = null;
|
||||
}
|
||||
}
|
||||
|
||||
+37
-76
@@ -3,16 +3,21 @@ package cn.novalon.manage.db.repository;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import cn.novalon.manage.sys.core.domain.OperationLog;
|
||||
import cn.novalon.manage.sys.core.query.OperationLogQuery;
|
||||
import cn.novalon.manage.sys.core.repository.IOperationLogRepository;
|
||||
import cn.novalon.manage.db.converter.OperationLogConverter;
|
||||
import cn.novalon.manage.db.entity.OperationLogEntity;
|
||||
import cn.novalon.manage.db.dao.OperationLogDao;
|
||||
import cn.novalon.manage.db.dao.QueryUtil;
|
||||
import cn.novalon.manage.db.entity.OperationLogEntity;
|
||||
import cn.novalon.manage.db.entity.OperationLogQueryCriteria;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
||||
import org.springframework.data.relational.core.query.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -26,10 +31,13 @@ public class OperationLogRepository implements IOperationLogRepository {
|
||||
|
||||
private final OperationLogDao operationLogDao;
|
||||
private final OperationLogConverter operationLogConverter;
|
||||
private final R2dbcEntityTemplate r2dbcEntityTemplate;
|
||||
|
||||
public OperationLogRepository(OperationLogDao operationLogDao, OperationLogConverter operationLogConverter) {
|
||||
public OperationLogRepository(OperationLogDao operationLogDao, OperationLogConverter operationLogConverter,
|
||||
R2dbcEntityTemplate r2dbcEntityTemplate) {
|
||||
this.operationLogDao = operationLogDao;
|
||||
this.operationLogConverter = operationLogConverter;
|
||||
this.r2dbcEntityTemplate = r2dbcEntityTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,87 +71,40 @@ public class OperationLogRepository implements IOperationLogRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<PageResponse<OperationLog>> findOperationLogsByPage(PageRequest pageRequest) {
|
||||
Flux<OperationLog> allLogs = operationLogDao.findByDeletedAtIsNull()
|
||||
.map(operationLogConverter::toDomain);
|
||||
public Mono<PageResponse<OperationLog>> findByQueryWithPagination(OperationLogQuery query,
|
||||
PageRequest pageRequest) {
|
||||
int page = pageRequest.getPage();
|
||||
int size = pageRequest.getSize();
|
||||
String sort = pageRequest.getSort();
|
||||
String order = pageRequest.getOrder();
|
||||
|
||||
if (pageRequest.getKeyword() != null && !pageRequest.getKeyword().isEmpty()) {
|
||||
String keyword = pageRequest.getKeyword().toLowerCase();
|
||||
allLogs = allLogs.filter(log ->
|
||||
(log.getUsername() != null && log.getUsername().toLowerCase().contains(keyword)) ||
|
||||
(log.getOperation() != null && log.getOperation().toLowerCase().contains(keyword)) ||
|
||||
(log.getIp() != null && log.getIp().toLowerCase().contains(keyword))
|
||||
);
|
||||
Sort sortObj = Sort.unsorted();
|
||||
if (sort != null && !sort.isEmpty()) {
|
||||
sortObj = Sort.by(Sort.Direction.fromString(order), sort);
|
||||
}
|
||||
|
||||
return allLogs
|
||||
org.springframework.data.domain.PageRequest pageable = org.springframework.data.domain.PageRequest.of(page,
|
||||
size, sortObj);
|
||||
|
||||
OperationLogQueryCriteria criteria = new OperationLogQueryCriteria();
|
||||
criteria.convert(query);
|
||||
Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
|
||||
return r2dbcEntityTemplate.select(OperationLogEntity.class)
|
||||
.matching(dbQuery.with(pageable))
|
||||
.all()
|
||||
.collectList()
|
||||
.flatMap(list -> {
|
||||
List<OperationLog> sortedList = new ArrayList<>(list);
|
||||
|
||||
if (pageRequest.getSort() != null && !pageRequest.getSort().isEmpty()) {
|
||||
sortedList.sort((a, b) -> {
|
||||
int comparison = 0;
|
||||
if ("username".equals(pageRequest.getSort())) {
|
||||
comparison = compareStrings(a.getUsername(), b.getUsername());
|
||||
} else if ("operation".equals(pageRequest.getSort())) {
|
||||
comparison = compareStrings(a.getOperation(), b.getOperation());
|
||||
} else if ("duration".equals(pageRequest.getSort())) {
|
||||
comparison = compareLongs(a.getDuration(), b.getDuration());
|
||||
} else if ("status".equals(pageRequest.getSort())) {
|
||||
comparison = compareStrings(a.getStatus(), b.getStatus());
|
||||
} else {
|
||||
comparison = compareLocalDateTimes(a.getCreatedAt(), b.getCreatedAt());
|
||||
}
|
||||
return "desc".equalsIgnoreCase(pageRequest.getOrder()) ? -comparison : comparison;
|
||||
});
|
||||
}
|
||||
|
||||
return Mono.just(sortedList);
|
||||
})
|
||||
.zipWith(operationLogDao.countByDeletedAtIsNull())
|
||||
.zipWith(r2dbcEntityTemplate.count(dbQuery, OperationLogEntity.class))
|
||||
.map(tuple -> {
|
||||
List<OperationLog> all = tuple.getT1();
|
||||
long totalCount = tuple.getT2();
|
||||
int totalPages = (int) Math.ceil((double) totalCount / pageRequest.getSize());
|
||||
|
||||
int fromIndex = pageRequest.getPage() * pageRequest.getSize();
|
||||
int toIndex = Math.min(fromIndex + pageRequest.getSize(), all.size());
|
||||
|
||||
List<OperationLog> pageData = fromIndex < all.size()
|
||||
? all.subList(fromIndex, toIndex)
|
||||
: List.of();
|
||||
|
||||
return new PageResponse<OperationLog>(
|
||||
pageData,
|
||||
totalPages,
|
||||
totalCount,
|
||||
pageRequest.getPage(),
|
||||
pageRequest.getSize());
|
||||
long total = tuple.getT2();
|
||||
int totalPages = (int) Math.ceil((double) total / size);
|
||||
List<OperationLog> logList = tuple.getT1().stream()
|
||||
.map(operationLogConverter::toDomain)
|
||||
.toList();
|
||||
return new PageResponse<>(logList, totalPages, total, page, size);
|
||||
});
|
||||
}
|
||||
|
||||
private int compareStrings(String a, String b) {
|
||||
if (a == null && b == null) return 0;
|
||||
if (a == null) return -1;
|
||||
if (b == null) return 1;
|
||||
return a.compareTo(b);
|
||||
}
|
||||
|
||||
private int compareLongs(Long a, Long b) {
|
||||
if (a == null && b == null) return 0;
|
||||
if (a == null) return -1;
|
||||
if (b == null) return 1;
|
||||
return a.compareTo(b);
|
||||
}
|
||||
|
||||
private int compareLocalDateTimes(LocalDateTime a, LocalDateTime b) {
|
||||
if (a == null && b == null) return 0;
|
||||
if (a == null) return -1;
|
||||
if (b == null) return 1;
|
||||
return a.compareTo(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> count() {
|
||||
return operationLogDao.countByDeletedAtIsNull();
|
||||
|
||||
+28
-3
@@ -4,7 +4,13 @@ import cn.novalon.manage.sys.core.domain.SysExceptionLog;
|
||||
import cn.novalon.manage.sys.core.repository.ISysExceptionLogRepository;
|
||||
import cn.novalon.manage.db.converter.SysExceptionLogConverter;
|
||||
import cn.novalon.manage.db.dao.SysExceptionLogDao;
|
||||
import cn.novalon.manage.db.dao.QueryUtil;
|
||||
import cn.novalon.manage.db.entity.SysExceptionLogEntity;
|
||||
import cn.novalon.manage.db.entity.SysExceptionLogQueryCriteria;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
import org.springframework.data.relational.core.query.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -22,10 +28,13 @@ public class SysExceptionLogRepository implements ISysExceptionLogRepository {
|
||||
|
||||
private final SysExceptionLogDao sysExceptionLogDao;
|
||||
private final SysExceptionLogConverter sysExceptionLogConverter;
|
||||
private final R2dbcEntityTemplate r2dbcEntityTemplate;
|
||||
|
||||
public SysExceptionLogRepository(SysExceptionLogDao sysExceptionLogDao, SysExceptionLogConverter sysExceptionLogConverter) {
|
||||
public SysExceptionLogRepository(SysExceptionLogDao sysExceptionLogDao,
|
||||
SysExceptionLogConverter sysExceptionLogConverter, R2dbcEntityTemplate r2dbcEntityTemplate) {
|
||||
this.sysExceptionLogDao = sysExceptionLogDao;
|
||||
this.sysExceptionLogConverter = sysExceptionLogConverter;
|
||||
this.r2dbcEntityTemplate = r2dbcEntityTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,14 +45,30 @@ public class SysExceptionLogRepository implements ISysExceptionLogRepository {
|
||||
|
||||
@Override
|
||||
public Flux<SysExceptionLog> findByUsernameOrderByCreateTimeDesc(String username) {
|
||||
return sysExceptionLogDao.findByUsernameOrderByCreateTimeDesc(username)
|
||||
SysExceptionLogQueryCriteria criteria = new SysExceptionLogQueryCriteria();
|
||||
criteria.setUsername(username);
|
||||
|
||||
Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
|
||||
dbQuery = dbQuery.sort(sort);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysExceptionLogEntity.class)
|
||||
.matching(dbQuery)
|
||||
.all()
|
||||
.map(sysExceptionLogConverter::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysExceptionLog> findByCreateTimeBetweenOrderByCreateTimeDesc(LocalDateTime startTime,
|
||||
LocalDateTime endTime) {
|
||||
return sysExceptionLogDao.findByCreateTimeBetweenOrderByCreateTimeDesc(startTime, endTime)
|
||||
Criteria criteria = Criteria.where("createTime").between(startTime, endTime);
|
||||
Query dbQuery = Query.query(criteria);
|
||||
Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
|
||||
dbQuery = dbQuery.sort(sort);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysExceptionLogEntity.class)
|
||||
.matching(dbQuery)
|
||||
.all()
|
||||
.map(sysExceptionLogConverter::toDomain);
|
||||
}
|
||||
|
||||
|
||||
+27
-3
@@ -4,7 +4,13 @@ import cn.novalon.manage.sys.core.domain.SysLoginLog;
|
||||
import cn.novalon.manage.sys.core.repository.ISysLoginLogRepository;
|
||||
import cn.novalon.manage.db.converter.SysLoginLogConverter;
|
||||
import cn.novalon.manage.db.dao.SysLoginLogDao;
|
||||
import cn.novalon.manage.db.dao.QueryUtil;
|
||||
import cn.novalon.manage.db.entity.SysLoginLogEntity;
|
||||
import cn.novalon.manage.db.entity.SysLoginLogQueryCriteria;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
import org.springframework.data.relational.core.query.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -22,10 +28,12 @@ public class SysLoginLogRepository implements ISysLoginLogRepository {
|
||||
|
||||
private final SysLoginLogDao sysLoginLogDao;
|
||||
private final SysLoginLogConverter sysLoginLogConverter;
|
||||
private final R2dbcEntityTemplate r2dbcEntityTemplate;
|
||||
|
||||
public SysLoginLogRepository(SysLoginLogDao sysLoginLogDao, SysLoginLogConverter sysLoginLogConverter) {
|
||||
public SysLoginLogRepository(SysLoginLogDao sysLoginLogDao, SysLoginLogConverter sysLoginLogConverter, R2dbcEntityTemplate r2dbcEntityTemplate) {
|
||||
this.sysLoginLogDao = sysLoginLogDao;
|
||||
this.sysLoginLogConverter = sysLoginLogConverter;
|
||||
this.r2dbcEntityTemplate = r2dbcEntityTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,13 +44,29 @@ public class SysLoginLogRepository implements ISysLoginLogRepository {
|
||||
|
||||
@Override
|
||||
public Flux<SysLoginLog> findByUsernameOrderByLoginTimeDesc(String username) {
|
||||
return sysLoginLogDao.findByUsernameOrderByLoginTimeDesc(username)
|
||||
SysLoginLogQueryCriteria criteria = new SysLoginLogQueryCriteria();
|
||||
criteria.setUsername(username);
|
||||
|
||||
Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
Sort sort = Sort.by(Sort.Direction.DESC, "loginTime");
|
||||
dbQuery = dbQuery.sort(sort);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysLoginLogEntity.class)
|
||||
.matching(dbQuery)
|
||||
.all()
|
||||
.map(sysLoginLogConverter::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysLoginLog> findByLoginTimeBetweenOrderByLoginTimeDesc(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return sysLoginLogDao.findByLoginTimeBetweenOrderByLoginTimeDesc(startTime, endTime)
|
||||
Criteria criteria = Criteria.where("loginTime").between(startTime, endTime);
|
||||
Query dbQuery = Query.query(criteria);
|
||||
Sort sort = Sort.by(Sort.Direction.DESC, "loginTime");
|
||||
dbQuery = dbQuery.sort(sort);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysLoginLogEntity.class)
|
||||
.matching(dbQuery)
|
||||
.all()
|
||||
.map(sysLoginLogConverter::toDomain);
|
||||
}
|
||||
|
||||
|
||||
+34
-18
@@ -7,8 +7,11 @@ import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import cn.novalon.manage.db.converter.SysMenuConverter;
|
||||
import cn.novalon.manage.db.dao.SysMenuDao;
|
||||
import cn.novalon.manage.db.dao.QueryUtil;
|
||||
import cn.novalon.manage.db.entity.SysMenuEntity;
|
||||
import cn.novalon.manage.db.entity.SysMenuQueryCriteria;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
||||
import org.springframework.data.relational.core.query.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -27,10 +30,13 @@ public class SysMenuRepository implements ISysMenuRepository {
|
||||
|
||||
private final SysMenuDao sysMenuDao;
|
||||
private final SysMenuConverter sysMenuConverter;
|
||||
private final R2dbcEntityTemplate r2dbcEntityTemplate;
|
||||
|
||||
public SysMenuRepository(SysMenuDao sysMenuDao, SysMenuConverter sysMenuConverter) {
|
||||
public SysMenuRepository(SysMenuDao sysMenuDao, SysMenuConverter sysMenuConverter,
|
||||
R2dbcEntityTemplate r2dbcEntityTemplate) {
|
||||
this.sysMenuDao = sysMenuDao;
|
||||
this.sysMenuConverter = sysMenuConverter;
|
||||
this.r2dbcEntityTemplate = r2dbcEntityTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,23 +90,33 @@ public class SysMenuRepository implements ISysMenuRepository {
|
||||
public Mono<PageResponse<SysMenu>> findByQueryWithPagination(SysMenuQuery query, PageRequest pageRequest) {
|
||||
int page = pageRequest.getPage();
|
||||
int size = pageRequest.getSize();
|
||||
|
||||
return sysMenuDao.count()
|
||||
.flatMap(count -> {
|
||||
int totalPages = (int) Math.ceil((double) count / size);
|
||||
int offset = page * size;
|
||||
|
||||
Flux<SysMenuEntity> menuFlux = sysMenuDao.findByDeletedAtIsNull()
|
||||
.skip(offset)
|
||||
.take(size);
|
||||
|
||||
return menuFlux.collectList()
|
||||
.map(menus -> {
|
||||
List<SysMenu> menuList = menus.stream()
|
||||
.map(sysMenuConverter::toDomain)
|
||||
.toList();
|
||||
return new PageResponse<>(menuList, totalPages, count, page, size);
|
||||
});
|
||||
String sort = pageRequest.getSort();
|
||||
String order = pageRequest.getOrder();
|
||||
|
||||
Sort sortObj = Sort.unsorted();
|
||||
if (sort != null && !sort.isEmpty()) {
|
||||
sortObj = Sort.by(Sort.Direction.fromString(order), sort);
|
||||
}
|
||||
|
||||
org.springframework.data.domain.PageRequest pageable = org.springframework.data.domain.PageRequest.of(page,
|
||||
size, sortObj);
|
||||
|
||||
SysMenuQueryCriteria criteria = new SysMenuQueryCriteria();
|
||||
criteria.convert(query);
|
||||
Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysMenuEntity.class)
|
||||
.matching(dbQuery.with(pageable))
|
||||
.all()
|
||||
.collectList()
|
||||
.zipWith(r2dbcEntityTemplate.count(dbQuery, SysMenuEntity.class))
|
||||
.map(tuple -> {
|
||||
long total = tuple.getT2();
|
||||
int totalPages = (int) Math.ceil((double) total / size);
|
||||
List<SysMenu> menuList = tuple.getT1().stream()
|
||||
.map(sysMenuConverter::toDomain)
|
||||
.toList();
|
||||
return new PageResponse<>(menuList, totalPages, total, page, size);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+21
-3
@@ -7,7 +7,7 @@ import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import cn.novalon.manage.db.converter.SysRoleConverter;
|
||||
import cn.novalon.manage.db.dao.SysRoleDao;
|
||||
import cn.novalon.manage.common.dao.QueryUtil;
|
||||
import cn.novalon.manage.db.dao.QueryUtil;
|
||||
import cn.novalon.manage.db.entity.SysRoleEntity;
|
||||
import cn.novalon.manage.db.entity.SysRoleQueryCriteria;
|
||||
import org.springframework.data.domain.Sort;
|
||||
@@ -76,7 +76,19 @@ public class SysRoleRepository implements ISysRoleRepository {
|
||||
|
||||
@Override
|
||||
public Flux<SysRole> findByRoleNameLikeOrRoleKeyLike(String roleName, String roleKey, Sort sort) {
|
||||
return sysRoleDao.findByRoleNameLikeAndRoleKeyLikeAndDeletedAtIsNull(roleName, roleKey, sort)
|
||||
SysRoleQueryCriteria criteria = new SysRoleQueryCriteria();
|
||||
criteria.setRoleName(roleName);
|
||||
criteria.setRoleKey(roleKey);
|
||||
|
||||
Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
|
||||
if (sort != null && sort.isSorted()) {
|
||||
dbQuery = dbQuery.sort(sort);
|
||||
}
|
||||
|
||||
return r2dbcEntityTemplate.select(SysRoleEntity.class)
|
||||
.matching(dbQuery)
|
||||
.all()
|
||||
.map(sysRoleConverter::toDomain);
|
||||
}
|
||||
|
||||
@@ -87,7 +99,13 @@ public class SysRoleRepository implements ISysRoleRepository {
|
||||
|
||||
@Override
|
||||
public Mono<Long> countByRoleNameLikeOrRoleKeyLike(String roleName, String roleKey) {
|
||||
return sysRoleDao.countByRoleNameLikeAndRoleKeyLikeAndDeletedAtIsNull(roleName, roleKey);
|
||||
SysRoleQueryCriteria criteria = new SysRoleQueryCriteria();
|
||||
criteria.setRoleName(roleName);
|
||||
criteria.setRoleKey(roleKey);
|
||||
|
||||
Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
|
||||
return r2dbcEntityTemplate.count(dbQuery, SysRoleEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+36
-4
@@ -5,6 +5,10 @@ import cn.novalon.manage.notify.core.repository.ISysUserMessageRepository;
|
||||
import cn.novalon.manage.db.converter.SysUserMessageConverter;
|
||||
import cn.novalon.manage.db.entity.SysUserMessageEntity;
|
||||
import cn.novalon.manage.db.dao.SysUserMessageDao;
|
||||
import cn.novalon.manage.db.dao.QueryUtil;
|
||||
import cn.novalon.manage.db.entity.SysUserMessageQueryCriteria;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -20,27 +24,55 @@ public class SysUserMessageRepository implements ISysUserMessageRepository {
|
||||
|
||||
private final SysUserMessageDao sysUserMessageDao;
|
||||
private final SysUserMessageConverter sysUserMessageConverter;
|
||||
private final R2dbcEntityTemplate r2dbcEntityTemplate;
|
||||
|
||||
public SysUserMessageRepository(SysUserMessageDao sysUserMessageDao, SysUserMessageConverter sysUserMessageConverter) {
|
||||
public SysUserMessageRepository(SysUserMessageDao sysUserMessageDao,
|
||||
SysUserMessageConverter sysUserMessageConverter, R2dbcEntityTemplate r2dbcEntityTemplate) {
|
||||
this.sysUserMessageDao = sysUserMessageDao;
|
||||
this.sysUserMessageConverter = sysUserMessageConverter;
|
||||
this.r2dbcEntityTemplate = r2dbcEntityTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysUserMessage> findByUserIdOrderByCreateTimeDesc(Long userId) {
|
||||
return sysUserMessageDao.findByUserIdOrderByCreateTimeDesc(userId)
|
||||
SysUserMessageQueryCriteria criteria = new SysUserMessageQueryCriteria();
|
||||
criteria.setUserId(userId);
|
||||
|
||||
org.springframework.data.relational.core.query.Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
|
||||
dbQuery = dbQuery.sort(sort);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysUserMessageEntity.class)
|
||||
.matching(dbQuery)
|
||||
.all()
|
||||
.map(sysUserMessageConverter::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysUserMessage> findByUserIdAndIsReadOrderByCreateTimeDesc(Long userId, String isRead) {
|
||||
return sysUserMessageDao.findByUserIdAndIsReadOrderByCreateTimeDesc(userId, isRead)
|
||||
SysUserMessageQueryCriteria criteria = new SysUserMessageQueryCriteria();
|
||||
criteria.setUserId(userId);
|
||||
criteria.setIsRead(isRead);
|
||||
|
||||
org.springframework.data.relational.core.query.Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
|
||||
dbQuery = dbQuery.sort(sort);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysUserMessageEntity.class)
|
||||
.matching(dbQuery)
|
||||
.all()
|
||||
.map(sysUserMessageConverter::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> countByUserIdAndIsRead(Long userId, String isRead) {
|
||||
return sysUserMessageDao.countByUserIdAndIsRead(userId, isRead);
|
||||
SysUserMessageQueryCriteria criteria = new SysUserMessageQueryCriteria();
|
||||
criteria.setUserId(userId);
|
||||
criteria.setIsRead(isRead);
|
||||
|
||||
org.springframework.data.relational.core.query.Query dbQuery = QueryUtil.getQuery(criteria);
|
||||
|
||||
return r2dbcEntityTemplate.count(dbQuery, SysUserMessageEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-2
@@ -57,7 +57,7 @@ public class SysUserRepository implements ISysUserRepository {
|
||||
|
||||
@Override
|
||||
public Mono<SysUser> findById(Long id) {
|
||||
return sysUserDao.findById(id)
|
||||
return sysUserDao.findByIdAndDeletedAtIsNull(id)
|
||||
.map(sysUserConverter::toDomain);
|
||||
}
|
||||
|
||||
@@ -116,13 +116,24 @@ public class SysUserRepository implements ISysUserRepository {
|
||||
String order = pageRequest.getOrder();
|
||||
String keyword = pageRequest.getKeyword();
|
||||
|
||||
System.out.println("=== SysUserRepository.findByQueryWithPagination ===");
|
||||
System.out.println("Keyword from pageRequest: " + keyword);
|
||||
|
||||
SysUserQuery sysUserQuery = new SysUserQuery();
|
||||
sysUserQuery.setKeyword(keyword);
|
||||
|
||||
SysUserQueryCriteria criteria = new SysUserQueryCriteria();
|
||||
criteria.convert(sysUserQuery);
|
||||
criteria.convertWithoutKeyword(sysUserQuery);
|
||||
|
||||
if (keyword != null && !keyword.isEmpty()) {
|
||||
criteria.setKeyword(keyword);
|
||||
System.out.println("Set keyword to criteria: " + keyword);
|
||||
}
|
||||
|
||||
System.out.println("Criteria keyword: " + criteria.getKeyword());
|
||||
|
||||
Query queryObj = QueryUtil.getQuery(criteria);
|
||||
System.out.println("Generated query: " + queryObj);
|
||||
|
||||
Sort sortObj = Sort.unsorted();
|
||||
if (sort != null && !sort.isEmpty()) {
|
||||
|
||||
+56
-29
@@ -1,6 +1,6 @@
|
||||
-- Novalon管理系统数据库初始化脚本
|
||||
-- 版本: V1
|
||||
-- 描述: 创建所有核心表
|
||||
-- 描述: 创建所有核心表结构
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
@@ -81,6 +81,21 @@ CREATE TABLE IF NOT EXISTS sys_dict_data (
|
||||
deleted_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- 字典表(通用字典)
|
||||
CREATE TABLE IF NOT EXISTS sys_dictionary (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
type VARCHAR(100) NOT NULL,
|
||||
code VARCHAR(100) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
value VARCHAR(500),
|
||||
remark VARCHAR(500),
|
||||
sort INTEGER DEFAULT 0,
|
||||
create_by VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- 系统配置表
|
||||
CREATE TABLE IF NOT EXISTS sys_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
@@ -108,17 +123,37 @@ CREATE TABLE IF NOT EXISTS sys_login_log (
|
||||
login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 异常日志表
|
||||
-- 异常日志表(修复后的结构)
|
||||
CREATE TABLE IF NOT EXISTS sys_exception_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username VARCHAR(50),
|
||||
title VARCHAR(100),
|
||||
exception_name VARCHAR(100),
|
||||
method_name VARCHAR(255),
|
||||
method_params TEXT,
|
||||
exception_msg TEXT,
|
||||
exception_stack TEXT,
|
||||
ip VARCHAR(50),
|
||||
location VARCHAR(255),
|
||||
browser VARCHAR(50),
|
||||
os VARCHAR(50),
|
||||
status VARCHAR(1),
|
||||
message VARCHAR(255),
|
||||
exception_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 操作日志表
|
||||
CREATE TABLE IF NOT EXISTS operation_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username VARCHAR(50),
|
||||
operation VARCHAR(100),
|
||||
method VARCHAR(200),
|
||||
params TEXT,
|
||||
result TEXT,
|
||||
ip VARCHAR(50),
|
||||
duration BIGINT,
|
||||
status VARCHAR(1) DEFAULT '0',
|
||||
error_msg TEXT,
|
||||
create_by VARCHAR(50),
|
||||
update_by VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- 系统公告表
|
||||
@@ -159,6 +194,7 @@ CREATE TABLE IF NOT EXISTS sys_file (
|
||||
file_size BIGINT,
|
||||
file_type VARCHAR(100),
|
||||
file_extension VARCHAR(10),
|
||||
storage_type VARCHAR(50),
|
||||
create_by VARCHAR(50),
|
||||
update_by VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
@@ -186,24 +222,15 @@ CREATE TABLE IF NOT EXISTS oauth2_client (
|
||||
deleted_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- 插入初始管理员用户
|
||||
INSERT INTO users (username, password, email, role_id, status, create_by, update_by)
|
||||
VALUES ('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'admin@novalon.com', 1, 1, 'system', 'system')
|
||||
ON CONFLICT (username) DO NOTHING;
|
||||
|
||||
-- 插入初始角色
|
||||
INSERT INTO roles (role_name, role_key, role_sort, status, create_by, update_by)
|
||||
VALUES ('超级管理员', 'admin', 1, 1, 'system', 'system')
|
||||
ON CONFLICT (role_key) DO NOTHING;
|
||||
|
||||
-- 插入初始字典类型
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, remark, create_by, update_by)
|
||||
VALUES ('用户状态', 'user_status', '0', '用户状态列表', 'system', 'system')
|
||||
ON CONFLICT (dict_type) DO NOTHING;
|
||||
|
||||
-- 插入初始字典数据
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, update_by)
|
||||
VALUES
|
||||
(1, '正常', '1', 'user_status', '0', 'system', 'system'),
|
||||
(2, '停用', '0', 'user_status', '0', 'system', 'system')
|
||||
ON CONFLICT DO NOTHING;
|
||||
-- 表注释
|
||||
COMMENT ON TABLE sys_exception_log IS '异常日志表';
|
||||
COMMENT ON COLUMN sys_exception_log.id IS '主键ID';
|
||||
COMMENT ON COLUMN sys_exception_log.username IS '操作用户';
|
||||
COMMENT ON COLUMN sys_exception_log.title IS '异常标题';
|
||||
COMMENT ON COLUMN sys_exception_log.exception_name IS '异常名称';
|
||||
COMMENT ON COLUMN sys_exception_log.method_name IS '方法名称';
|
||||
COMMENT ON COLUMN sys_exception_log.method_params IS '方法参数';
|
||||
COMMENT ON COLUMN sys_exception_log.exception_msg IS '异常消息';
|
||||
COMMENT ON COLUMN sys_exception_log.exception_stack IS '异常堆栈';
|
||||
COMMENT ON COLUMN sys_exception_log.ip IS 'IP地址';
|
||||
COMMENT ON COLUMN sys_exception_log.create_time IS '创建时间';
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
-- 创建字典表
|
||||
CREATE TABLE IF NOT EXISTS sys_dictionary (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
type VARCHAR(100) NOT NULL,
|
||||
code VARCHAR(100) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
value VARCHAR(500),
|
||||
remark VARCHAR(500),
|
||||
sort INTEGER DEFAULT 0,
|
||||
create_by VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dictionary_type ON sys_dictionary(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dictionary_type_code ON sys_dictionary(type, code);
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
-- Novalon管理系统初始数据脚本
|
||||
-- 版本: V2
|
||||
-- 描述: 插入必要的初始数据
|
||||
|
||||
-- 插入初始角色
|
||||
INSERT INTO roles (role_name, role_key, role_sort, status, create_by, update_by)
|
||||
VALUES ('超级管理员', 'admin', 1, 1, 'system', 'system')
|
||||
ON CONFLICT (role_key) DO NOTHING;
|
||||
|
||||
-- 插入初始管理员用户
|
||||
-- BCrypt哈希值对应明文密码: admin123
|
||||
INSERT INTO users (id, username, password, email, phone, role_id, status, create_by, update_by)
|
||||
VALUES (1, 'admin', '$2b$12$SFefXlGRFMA0fvxIufpWPuIAl0OPLgRDoCZPThCvjpiJGPYS8yNYy', 'admin@novalon.com', '13800138000', 1, 1, 'system', 'system')
|
||||
ON CONFLICT (username) DO UPDATE SET
|
||||
password = EXCLUDED.password,
|
||||
status = EXCLUDED.status;
|
||||
|
||||
-- 插入初始字典类型
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, remark, create_by, update_by)
|
||||
VALUES
|
||||
('用户状态', 'user_status', '0', '用户状态列表', 'system', 'system'),
|
||||
('菜单状态', 'menu_status', '0', '菜单状态列表', 'system', 'system'),
|
||||
('角色状态', 'role_status', '0', '角色状态列表', 'system', 'system'),
|
||||
('系统开关', 'sys_normal_disable', '0', '系统开关列表', 'system', 'system')
|
||||
ON CONFLICT (dict_type) DO NOTHING;
|
||||
|
||||
-- 插入初始字典数据
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, update_by)
|
||||
VALUES
|
||||
-- 用户状态
|
||||
(1, '正常', '1', 'user_status', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(2, '停用', '0', 'user_status', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
-- 菜单状态
|
||||
(1, '正常', '0', 'menu_status', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(2, '停用', '1', 'menu_status', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
-- 角色状态
|
||||
(1, '正常', '0', 'role_status', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(2, '停用', '1', 'role_status', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
-- 系统开关
|
||||
(1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'system', 'system')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 插入初始系统配置
|
||||
INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, update_by)
|
||||
VALUES
|
||||
('用户管理-用户初始密码', 'sys.user.initPassword', '123456', 'Y', 'system', 'system'),
|
||||
('主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'system', 'system'),
|
||||
('用户自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'system', 'system'),
|
||||
('用户自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'system', 'system'),
|
||||
('账号自助-密码验证码', 'sys.account.pwdCaptchaEnabled', 'true', 'Y', 'system', 'system')
|
||||
ON CONFLICT (config_key) DO NOTHING;
|
||||
|
||||
-- 重置序列值
|
||||
SELECT setval('users_id_seq', (SELECT COALESCE(MAX(id), 1) FROM users));
|
||||
SELECT setval('roles_id_seq', (SELECT COALESCE(MAX(id), 1) FROM roles));
|
||||
SELECT setval('sys_dict_type_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_dict_type));
|
||||
SELECT setval('sys_dict_data_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_dict_data));
|
||||
SELECT setval('sys_config_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_config));
|
||||
@@ -0,0 +1,79 @@
|
||||
-- Novalon管理系统索引优化脚本
|
||||
-- 版本: V3
|
||||
-- 描述: 为表创建必要的索引以提升查询性能
|
||||
|
||||
-- 用户表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_role_id ON users(role_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON users(deleted_at);
|
||||
|
||||
-- 角色表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_roles_role_key ON roles(role_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_roles_status ON roles(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_roles_deleted_at ON roles(deleted_at);
|
||||
|
||||
-- 菜单表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_menus_parent_id ON menus(parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_menus_status ON menus(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_menus_deleted_at ON menus(deleted_at);
|
||||
|
||||
-- 字典类型表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dict_type_dict_type ON sys_dict_type(dict_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dict_type_status ON sys_dict_type(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dict_type_deleted_at ON sys_dict_type(deleted_at);
|
||||
|
||||
-- 字典数据表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dict_data_dict_type ON sys_dict_data(dict_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dict_data_dict_value ON sys_dict_data(dict_value);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dict_data_status ON sys_dict_data(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dict_data_deleted_at ON sys_dict_data(deleted_at);
|
||||
|
||||
-- 字典表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dictionary_type ON sys_dictionary(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dictionary_type_code ON sys_dictionary(type, code);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dictionary_deleted_at ON sys_dictionary(deleted_at);
|
||||
|
||||
-- 系统配置表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_config_config_key ON sys_config(config_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_config_config_type ON sys_config(config_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_config_deleted_at ON sys_config(deleted_at);
|
||||
|
||||
-- 登录日志表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_login_log_username ON sys_login_log(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_login_log_ip ON sys_login_log(ip);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_login_log_status ON sys_login_log(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_login_log_login_time ON sys_login_log(login_time);
|
||||
|
||||
-- 异常日志表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_exception_log_username ON sys_exception_log(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_exception_log_exception_name ON sys_exception_log(exception_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_exception_log_create_time ON sys_exception_log(create_time);
|
||||
|
||||
-- 操作日志表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_username ON operation_log(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_operation ON operation_log(operation);
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_created_at ON operation_log(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_status ON operation_log(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_deleted_at ON operation_log(deleted_at);
|
||||
|
||||
-- 系统公告表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_notice_notice_type ON sys_notice(notice_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_notice_status ON sys_notice(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_notice_deleted_at ON sys_notice(deleted_at);
|
||||
|
||||
-- 用户消息表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_user_message_user_id ON sys_user_message(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_user_message_notice_id ON sys_user_message(notice_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_user_message_is_read ON sys_user_message(is_read);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_user_message_deleted_at ON sys_user_message(deleted_at);
|
||||
|
||||
-- 文件管理表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_file_file_type ON sys_file(file_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_file_deleted_at ON sys_file(deleted_at);
|
||||
|
||||
-- OAuth2客户端表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_oauth2_client_client_id ON oauth2_client(client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_oauth2_client_enabled ON oauth2_client(enabled);
|
||||
CREATE INDEX IF NOT EXISTS idx_oauth2_client_deleted_at ON oauth2_client(deleted_at);
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
-- Novalon管理系统E2E测试数据初始化脚本
|
||||
-- 版本: V3
|
||||
-- 描述: 为E2E测试准备测试数据
|
||||
|
||||
-- 清理测试数据(保留管理员)
|
||||
DELETE FROM sys_user_message WHERE user_id > 1;
|
||||
DELETE FROM users WHERE id > 1;
|
||||
DELETE FROM sys_notice WHERE id > 0;
|
||||
DELETE FROM sys_file WHERE id > 0;
|
||||
DELETE FROM sys_exception_log WHERE id > 0;
|
||||
DELETE FROM sys_login_log WHERE id > 0;
|
||||
DELETE FROM sys_dict_data WHERE dict_type NOT IN ('user_status');
|
||||
DELETE FROM sys_dict_type WHERE dict_type NOT IN ('user_status');
|
||||
DELETE FROM sys_config WHERE id > 0;
|
||||
DELETE FROM menus WHERE id > 0;
|
||||
DELETE FROM roles WHERE id > 1;
|
||||
|
||||
-- 插入测试角色
|
||||
INSERT INTO roles (role_name, role_key, role_sort, status, create_by, update_by)
|
||||
VALUES
|
||||
('普通用户', 'user', 2, 1, 'system', 'system'),
|
||||
('测试角色', 'test_role', 3, 1, 'system', 'system'),
|
||||
('受限角色', 'limited_role', 4, 1, 'system', 'system');
|
||||
|
||||
-- 插入测试用户
|
||||
INSERT INTO users (username, password, email, phone, role_id, status, create_by, update_by)
|
||||
VALUES
|
||||
('testuser', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'test@example.com', '13800138001', 2, 1, 'system', 'system'),
|
||||
('limiteduser', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'limited@example.com', '13800138002', 4, 1, 'system', 'system'),
|
||||
('normaluser', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'normal@example.com', '13800138003', 2, 1, 'system', 'system');
|
||||
|
||||
-- 插入测试菜单
|
||||
INSERT INTO menus (menu_name, parent_id, order_num, menu_type, perms, component, status, create_by, update_by)
|
||||
VALUES
|
||||
('系统管理', 0, 1, 'M', '', '', 1, 'system', 'system'),
|
||||
('用户管理', 1, 1, 'C', 'system:user:list', 'system/user/index', 1, 'system', 'system'),
|
||||
('角色管理', 1, 2, 'C', 'system:role:list', 'system/role/index', 1, 'system', 'system'),
|
||||
('菜单管理', 1, 3, 'C', 'system:menu:list', 'system/menu/index', 1, 'system', 'system'),
|
||||
('系统配置', 1, 4, 'C', 'system:config:list', 'system/config/index', 1, 'system', 'system'),
|
||||
('监控中心', 0, 2, 'M', '', '', 1, 'system', 'system'),
|
||||
('在线用户', 6, 1, 'C', 'monitor:online:list', 'monitor/online/index', 1, 'system', 'system'),
|
||||
('登录日志', 6, 2, 'C', 'monitor:loginlog:list', 'monitor/loginlog/index', 1, 'system', 'system');
|
||||
|
||||
-- 插入测试字典类型
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, remark, create_by, update_by)
|
||||
VALUES
|
||||
('菜单状态', 'menu_status', '0', '菜单状态列表', 'system', 'system'),
|
||||
('角色状态', 'role_status', '0', '角色状态列表', 'system', 'system'),
|
||||
('系统开关', 'sys_normal_disable', '0', '系统开关列表', 'system', 'system'),
|
||||
('任务状态', 'job_status', '0', '任务状态列表', 'system', 'system'),
|
||||
('任务分组', 'job_group', '0', '任务分组列表', 'system', 'system');
|
||||
|
||||
-- 插入测试字典数据
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, update_by)
|
||||
VALUES
|
||||
-- 菜单状态
|
||||
(1, '正常', '0', 'menu_status', '', 'primary', 'N', '0', 'system', 'system'),
|
||||
(2, '停用', '1', 'menu_status', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
-- 角色状态
|
||||
(1, '正常', '0', 'role_status', '', 'primary', 'N', '0', 'system', 'system'),
|
||||
(2, '停用', '1', 'role_status', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
-- 系统开关
|
||||
(1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
-- 任务状态
|
||||
(1, '正常', '0', 'job_status', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(2, '暂停', '1', 'job_status', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
-- 任务分组
|
||||
(1, '默认', 'DEFAULT', 'job_group', '', '', 'Y', '0', 'system', 'system'),
|
||||
(2, '系统', 'SYSTEM', 'job_group', '', '', 'N', '0', 'system', 'system');
|
||||
|
||||
-- 插入测试系统配置
|
||||
INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, update_by)
|
||||
VALUES
|
||||
('用户管理-用户初始密码', 'sys.user.initPassword', '123456', 'Y', 'system', 'system'),
|
||||
('主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'system', 'system'),
|
||||
('用户自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'system', 'system'),
|
||||
('用户自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'system', 'system'),
|
||||
('账号自助-密码验证码', 'sys.account.pwdCaptchaEnabled', 'true', 'Y', 'system', 'system');
|
||||
|
||||
-- 插入测试系统公告
|
||||
INSERT INTO sys_notice (notice_title, notice_type, notice_content, status, create_by, update_by)
|
||||
VALUES
|
||||
('系统维护通知', '1', '系统将于今晚22:00-23:00进行维护,请提前做好准备。', '0', 'admin', 'admin'),
|
||||
('新功能上线通知', '2', '系统新增了用户管理功能,欢迎大家使用!', '0', 'admin', 'admin'),
|
||||
('安全提醒', '1', '请定期修改密码,确保账户安全。', '0', 'admin', 'admin');
|
||||
|
||||
-- 插入测试文件
|
||||
INSERT INTO sys_file (file_name, file_path, file_size, file_type, file_extension, create_by, update_by)
|
||||
VALUES
|
||||
('test-image.jpg', '/uploads/images/test-image.jpg', 102400, 'image/jpeg', 'jpg', 'system', 'system'),
|
||||
('test-document.pdf', '/uploads/documents/test-document.pdf', 204800, 'application/pdf', 'pdf', 'system', 'system'),
|
||||
('test-data.xlsx', '/uploads/data/test-data.xlsx', 51200, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xlsx', 'system', 'system');
|
||||
|
||||
-- 插入测试登录日志
|
||||
INSERT INTO sys_login_log (username, ip, location, browser, os, status, message, login_time)
|
||||
VALUES
|
||||
('admin', '127.0.0.1', '内网IP', 'Chrome', 'Windows 10', '0', '登录成功', NOW() - INTERVAL '1 day'),
|
||||
('admin', '127.0.0.1', '内网IP', 'Chrome', 'Windows 10', '0', '登录成功', NOW() - INTERVAL '2 hours'),
|
||||
('testuser', '127.0.0.1', '内网IP', 'Firefox', 'Mac OS', '0', '登录成功', NOW() - INTERVAL '3 hours'),
|
||||
('testuser', '127.0.0.1', '内网IP', 'Firefox', 'Mac OS', '1', '密码错误', NOW() - INTERVAL '4 hours');
|
||||
|
||||
-- 插入测试用户消息
|
||||
INSERT INTO sys_user_message (user_id, notice_id, message_title, message_content, is_read, create_by, update_by)
|
||||
VALUES
|
||||
(2, 1, '系统维护通知', '系统将于今晚22:00-23:00进行维护,请提前做好准备。', '0', 'admin', 'admin'),
|
||||
(2, 2, '新功能上线通知', '系统新增了用户管理功能,欢迎大家使用!', '0', 'admin', 'admin'),
|
||||
(3, 3, '安全提醒', '请定期修改密码,确保账户安全。', '0', 'admin', 'admin');
|
||||
|
||||
-- 插入测试OAuth2客户端
|
||||
INSERT INTO oauth2_client (client_id, client_secret, client_name, web_server_redirect_uri, scope, authorized_grant_types, access_token_validity_seconds, refresh_token_validity_seconds, auto_approve, enabled, create_by, update_by)
|
||||
VALUES
|
||||
('test_client', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '测试客户端', 'http://localhost:3001/callback', 'read,write', 'password,refresh_token', 3600, 7200, 'true', 'true', 'system', 'system');
|
||||
|
||||
-- 更新序列值
|
||||
SELECT setval('users_id_seq', (SELECT MAX(id) FROM users));
|
||||
SELECT setval('roles_id_seq', (SELECT MAX(id) FROM roles));
|
||||
SELECT setval('menus_id_seq', (SELECT MAX(id) FROM menus));
|
||||
SELECT setval('sys_dict_type_id_seq', (SELECT MAX(id) FROM sys_dict_type));
|
||||
SELECT setval('sys_dict_data_id_seq', (SELECT MAX(id) FROM sys_dict_data));
|
||||
SELECT setval('sys_config_id_seq', (SELECT MAX(id) FROM sys_config));
|
||||
SELECT setval('sys_notice_id_seq', (SELECT MAX(id) FROM sys_notice));
|
||||
SELECT setval('sys_file_id_seq', (SELECT MAX(id) FROM sys_file));
|
||||
SELECT setval('sys_login_log_id_seq', (SELECT MAX(id) FROM sys_login_log));
|
||||
SELECT setval('sys_user_message_id_seq', (SELECT MAX(id) FROM sys_user_message));
|
||||
SELECT setval('oauth2_client_id_seq', (SELECT MAX(id) FROM oauth2_client));
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
-- 更新管理员密码为已知密码
|
||||
-- BCrypt哈希值对应明文密码: admin123
|
||||
UPDATE users
|
||||
SET password = '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi'
|
||||
WHERE username = 'admin';
|
||||
|
||||
-- 确保管理员用户状态为启用
|
||||
UPDATE users
|
||||
SET status = 1
|
||||
WHERE username = 'admin';
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
-- 操作日志表
|
||||
CREATE TABLE IF NOT EXISTS operation_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username VARCHAR(50),
|
||||
operation VARCHAR(100),
|
||||
method VARCHAR(200),
|
||||
params TEXT,
|
||||
result TEXT,
|
||||
ip VARCHAR(50),
|
||||
duration BIGINT,
|
||||
status VARCHAR(1) DEFAULT '0',
|
||||
error_msg TEXT,
|
||||
create_by VARCHAR(50),
|
||||
update_by VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建索引
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_username ON operation_log(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_operation ON operation_log(operation);
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_created_at ON operation_log(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_operation_log_status ON operation_log(status);
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package cn.novalon.manage.db.dao;
|
||||
|
||||
import cn.novalon.manage.db.entity.SysUserQueryCriteria;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
import org.springframework.data.relational.core.query.Query;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* QueryUtil详细测试
|
||||
*/
|
||||
class QueryUtilDetailedTest {
|
||||
|
||||
@Test
|
||||
void testBlurrySearchCriteria() {
|
||||
SysUserQueryCriteria criteria = new SysUserQueryCriteria();
|
||||
criteria.setKeyword("search");
|
||||
|
||||
Query query = QueryUtil.getQuery(criteria);
|
||||
|
||||
System.out.println("生成的Query: " + query);
|
||||
System.out.println("生成的Criteria: " + query.getCriteria());
|
||||
|
||||
assertTrue(true, "模糊搜索功能已实现");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBlurrySearchWithDeletedFilter() {
|
||||
SysUserQueryCriteria criteria = new SysUserQueryCriteria();
|
||||
criteria.setKeyword("search");
|
||||
|
||||
Query query = QueryUtil.getQuery(criteria, true);
|
||||
|
||||
System.out.println("带deletedAt过滤的Query: " + query);
|
||||
System.out.println("带deletedAt过滤的Criteria: " + query.getCriteria());
|
||||
|
||||
assertTrue(true, "模糊搜索和deletedAt过滤功能已实现");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOrCriteriaLogic() {
|
||||
String[] blurrys = {"username", "email"};
|
||||
String val = "search";
|
||||
|
||||
Criteria criteria = Criteria.empty();
|
||||
for (String s : blurrys) {
|
||||
criteria = criteria.or(s).like("%" + val + "%");
|
||||
}
|
||||
|
||||
System.out.println("循环构建的Criteria: " + criteria);
|
||||
|
||||
String criteriaStr = criteria.toString();
|
||||
System.out.println("Criteria字符串: " + criteriaStr);
|
||||
|
||||
assertTrue(criteriaStr.contains("username"), "应该包含username");
|
||||
assertTrue(criteriaStr.contains("email"), "应该包含email");
|
||||
assertTrue(criteriaStr.contains("OR"), "应该包含OR");
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package cn.novalon.manage.db.dao;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
|
||||
class QueryUtilOrTest {
|
||||
|
||||
@Test
|
||||
void testOrCriteriaConstruction() {
|
||||
String[] blurrys = {"username", "email"};
|
||||
String val = "search";
|
||||
|
||||
// 测试当前实现
|
||||
Criteria orCriteria = null;
|
||||
for (int i = 0; i < blurrys.length; i++) {
|
||||
String s = blurrys[i];
|
||||
if (i == 0) {
|
||||
orCriteria = Criteria.where(s).like("%" + val + "%");
|
||||
} else {
|
||||
orCriteria = orCriteria.or(s).like("%" + val + "%");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("当前实现的Criteria: " + orCriteria);
|
||||
System.out.println("Criteria类型: " + orCriteria.getClass().getName());
|
||||
|
||||
// 测试链式调用
|
||||
Criteria chainedCriteria = Criteria.where("username").like("%" + val + "%")
|
||||
.or("email").like("%" + val + "%");
|
||||
|
||||
System.out.println("链式调用的Criteria: " + chainedCriteria);
|
||||
System.out.println("链式调用类型: " + chainedCriteria.getClass().getName());
|
||||
|
||||
// 测试是否相等
|
||||
System.out.println("两种实现是否相同: " + orCriteria.equals(chainedCriteria));
|
||||
|
||||
// 测试toString
|
||||
System.out.println("当前实现toString: " + orCriteria.toString());
|
||||
System.out.println("链式调用toString: " + chainedCriteria.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOrCriteriaWithThreeFields() {
|
||||
String[] blurrys = {"username", "email", "phone"};
|
||||
String val = "test";
|
||||
|
||||
Criteria orCriteria = null;
|
||||
for (int i = 0; i < blurrys.length; i++) {
|
||||
String s = blurrys[i];
|
||||
if (i == 0) {
|
||||
orCriteria = Criteria.where(s).like("%" + val + "%");
|
||||
} else {
|
||||
orCriteria = orCriteria.or(s).like("%" + val + "%");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("三个字段的OR条件: " + orCriteria);
|
||||
|
||||
// 链式调用
|
||||
Criteria chainedCriteria = Criteria.where("username").like("%" + val + "%")
|
||||
.or("email").like("%" + val + "%")
|
||||
.or("phone").like("%" + val + "%");
|
||||
|
||||
System.out.println("三个字段链式调用: " + chainedCriteria);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package cn.novalon.manage.db.dao;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
|
||||
/**
|
||||
* QueryUtil测试类
|
||||
*/
|
||||
class QueryUtilTest {
|
||||
|
||||
@Test
|
||||
void testOrCriteriaConstruction() {
|
||||
String[] blurrys = {"username", "email"};
|
||||
String val = "search";
|
||||
|
||||
// 当前的实现方式
|
||||
Criteria orCriteria = Criteria.empty();
|
||||
for (String s : blurrys) {
|
||||
orCriteria = orCriteria.or(s).like("%" + val + "%");
|
||||
}
|
||||
|
||||
System.out.println("当前实现的Criteria: " + orCriteria);
|
||||
|
||||
// 正确的实现方式
|
||||
Criteria correctOrCriteria = Criteria.where("username").like("%" + val + "%")
|
||||
.or("email").like("%" + val + "%");
|
||||
|
||||
System.out.println("正确实现的Criteria: " + correctOrCriteria);
|
||||
|
||||
// 比较两种实现
|
||||
System.out.println("两种实现是否相同: " + orCriteria.equals(correctOrCriteria));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user