1. 新增 api 调用日志记录,历史版本回滚
2. 新增用户角色权限监督功能
This commit is contained in:
7
pom.xml
7
pom.xml
@@ -205,8 +205,13 @@
|
|||||||
<name>中铜 ZStack 私服</name>
|
<name>中铜 ZStack 私服</name>
|
||||||
<url>http://172.16.46.63:30708/repository/test/</url>
|
<url>http://172.16.46.63:30708/repository/test/</url>
|
||||||
<releases>
|
<releases>
|
||||||
<enabled>true</enabled>
|
<updatePolicy>always</updatePolicy>
|
||||||
|
<checksumPolicy>warn</checksumPolicy>
|
||||||
</releases>
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
<updatePolicy>always</updatePolicy>
|
||||||
|
</snapshots>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|||||||
33
sql/dm/数据总线API版本历史菜单权限_备忘录模式_20251030.sql
Normal file
33
sql/dm/数据总线API版本历史菜单权限_备忘录模式_20251030.sql
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
-- =============================================
|
||||||
|
-- 数据总线 API 版本历史菜单权限(备忘录模式)
|
||||||
|
-- 功能说明:
|
||||||
|
-- 1. 查看版本历史
|
||||||
|
-- 2. 查看版本详情
|
||||||
|
-- 3. 版本回滚
|
||||||
|
-- 4. 版本对比
|
||||||
|
-- =============================================
|
||||||
|
|
||||||
|
-- 删除旧的版本管理菜单权限
|
||||||
|
DELETE FROM system_menu WHERE id IN (650107, 650108, 650109, 650110);
|
||||||
|
|
||||||
|
-- 插入新的版本历史管理权限
|
||||||
|
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
|
||||||
|
status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
|
||||||
|
VALUES
|
||||||
|
-- 查询版本历史列表
|
||||||
|
(650107, 'API版本历史', 'databus:gateway:version:query', 3, 7, 6501, '', '', '', '',
|
||||||
|
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
|
||||||
|
-- 查看版本详情
|
||||||
|
(650108, 'API版本详情', 'databus:gateway:version:detail', 3, 8, 6501, '', '', '', '',
|
||||||
|
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
|
||||||
|
-- 版本回滚
|
||||||
|
(650109, 'API版本回滚', 'databus:gateway:version:rollback', 3, 9, 6501, '', '', '', '',
|
||||||
|
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
|
||||||
|
-- 版本对比
|
||||||
|
(650110, 'API版本对比', 'databus:gateway:version:compare', 3, 10, 6501, '', '', '', '',
|
||||||
|
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0');
|
||||||
|
|
||||||
|
-- 说明
|
||||||
|
-- 1. 不再需要"创建版本"权限,因为系统自动创建
|
||||||
|
-- 2. 不再需要"删除版本"权限,版本历史不可删除
|
||||||
|
-- 3. 保留查询、详情、回滚、对比四个核心功能
|
||||||
52
sql/dm/数据总线API版本历史表结构_备忘录模式_20251030.sql
Normal file
52
sql/dm/数据总线API版本历史表结构_备忘录模式_20251030.sql
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
-- =============================================
|
||||||
|
-- 数据总线 API 版本历史表(备忘录模式)
|
||||||
|
-- 功能说明:
|
||||||
|
-- 1. 每次保存 API 配置时自动创建版本记录
|
||||||
|
-- 2. 版本号自动递增(v1, v2, v3...)
|
||||||
|
-- 3. 保留完整历史链,不可删除
|
||||||
|
-- 4. 支持一键回滚到任意历史版本
|
||||||
|
-- 5. 支持版本对比功能
|
||||||
|
-- =============================================
|
||||||
|
|
||||||
|
-- 如果表已存在则删除
|
||||||
|
DROP TABLE IF EXISTS databus_api_version;
|
||||||
|
|
||||||
|
-- 创建版本历史表(DM8 语法)
|
||||||
|
CREATE TABLE databus_api_version (
|
||||||
|
id BIGINT NOT NULL,
|
||||||
|
api_id BIGINT NOT NULL,
|
||||||
|
version_number INTEGER NOT NULL,
|
||||||
|
snapshot_data CLOB NOT NULL,
|
||||||
|
description VARCHAR(500),
|
||||||
|
is_current NUMBER(1) DEFAULT 0 NOT NULL,
|
||||||
|
operator VARCHAR(64),
|
||||||
|
creator VARCHAR(64) DEFAULT '' NOT NULL,
|
||||||
|
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updater VARCHAR(64) DEFAULT '' NOT NULL,
|
||||||
|
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
deleted NUMBER(1) DEFAULT 0 NOT NULL,
|
||||||
|
tenant_id BIGINT DEFAULT 0 NOT NULL,
|
||||||
|
CONSTRAINT pk_databus_api_version PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 创建索引
|
||||||
|
CREATE INDEX idx_databus_api_version_api_id ON databus_api_version (api_id);
|
||||||
|
CREATE INDEX idx_databus_api_version_version_number ON databus_api_version (api_id, version_number);
|
||||||
|
CREATE INDEX idx_databus_api_version_is_current ON databus_api_version (api_id, is_current);
|
||||||
|
CREATE INDEX idx_databus_api_version_create_time ON databus_api_version (create_time);
|
||||||
|
CREATE INDEX idx_databus_api_version_operator ON databus_api_version (operator);
|
||||||
|
|
||||||
|
COMMENT ON TABLE databus_api_version IS '数据总线API版本历史表:采用备忘录模式,每次保存API时自动创建版本快照,支持完整的版本历史追溯和回滚';
|
||||||
|
COMMENT ON COLUMN databus_api_version.id IS '主键ID';
|
||||||
|
COMMENT ON COLUMN databus_api_version.api_id IS 'API定义ID,关联databus_api_definition表';
|
||||||
|
COMMENT ON COLUMN databus_api_version.version_number IS '版本号,同一API下自动递增(1,2,3...)';
|
||||||
|
COMMENT ON COLUMN databus_api_version.snapshot_data IS 'API完整配置快照(JSON格式),包含definition、steps、transforms等所有信息';
|
||||||
|
COMMENT ON COLUMN databus_api_version.description IS '变更说明,记录本次修改的内容';
|
||||||
|
COMMENT ON COLUMN databus_api_version.is_current IS '是否为当前版本(1=是,0=否),同一API只有一个当前版本';
|
||||||
|
COMMENT ON COLUMN databus_api_version.operator IS '操作人,记录谁创建了这个版本';
|
||||||
|
COMMENT ON COLUMN databus_api_version.creator IS '创建者';
|
||||||
|
COMMENT ON COLUMN databus_api_version.create_time IS '创建时间(版本创建时间)';
|
||||||
|
COMMENT ON COLUMN databus_api_version.updater IS '更新者';
|
||||||
|
COMMENT ON COLUMN databus_api_version.update_time IS '更新时间';
|
||||||
|
COMMENT ON COLUMN databus_api_version.deleted IS '是否删除(逻辑删除,实际不删除版本历史)';
|
||||||
|
COMMENT ON COLUMN databus_api_version.tenant_id IS '租户ID';
|
||||||
13
sql/dm/数据总线API访问日志菜单权限_20251028.sql
Normal file
13
sql/dm/数据总线API访问日志菜单权限_20251028.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- 数据总线 API 访问日志菜单权限初始化(DM8)
|
||||||
|
-- 创建访问日志页面及查询按钮权限。如已存在将先行移除再新增。
|
||||||
|
DELETE FROM system_menu WHERE id IN (6504, 650401);
|
||||||
|
|
||||||
|
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
|
||||||
|
status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
|
||||||
|
VALUES (6504, '访问日志', 'databus:gateway:access-log:query', 2, 40, 6500, 'access-log', 'ep:document', 'databus/accesslog/index', 'DatabusAccessLog',
|
||||||
|
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0');
|
||||||
|
|
||||||
|
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
|
||||||
|
status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
|
||||||
|
VALUES (650401, '访问日志查询', 'databus:gateway:access-log:query', 3, 1, 6504, '', '', '', '',
|
||||||
|
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0');
|
||||||
0
sql/dm/数据总线API访问日志表结构_20251028.sql
Normal file
0
sql/dm/数据总线API访问日志表结构_20251028.sql
Normal file
52
sql/dm/权限监督功能.sql
Normal file
52
sql/dm/权限监督功能.sql
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
-- 权限监督按钮及接口权限(DM8 专用)
|
||||||
|
-- 执行前请确认未占用 1068 主键
|
||||||
|
|
||||||
|
DELETE FROM system_menu WHERE id = 1068;
|
||||||
|
|
||||||
|
INSERT INTO system_menu (
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
permission,
|
||||||
|
type,
|
||||||
|
sort,
|
||||||
|
parent_id,
|
||||||
|
path,
|
||||||
|
icon,
|
||||||
|
component,
|
||||||
|
component_name,
|
||||||
|
status,
|
||||||
|
visible,
|
||||||
|
keep_alive,
|
||||||
|
always_show,
|
||||||
|
creator,
|
||||||
|
create_time,
|
||||||
|
updater,
|
||||||
|
update_time,
|
||||||
|
deleted
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
1068,
|
||||||
|
'权限监督',
|
||||||
|
'system:permission:user-permission-supervision',
|
||||||
|
3,
|
||||||
|
9,
|
||||||
|
101,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
'1',
|
||||||
|
'1',
|
||||||
|
'1',
|
||||||
|
'admin',
|
||||||
|
'2025-10-29 00:00:00',
|
||||||
|
'',
|
||||||
|
'2025-10-29 00:00:00',
|
||||||
|
'0'
|
||||||
|
FROM dual
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM system_menu
|
||||||
|
WHERE id = 1068
|
||||||
|
);
|
||||||
@@ -31,6 +31,11 @@
|
|||||||
<artifactId>zt-spring-boot-starter-biz-data-permission</artifactId>
|
<artifactId>zt-spring-boot-starter-biz-data-permission</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zt.plat</groupId>
|
||||||
|
<artifactId>zt-spring-boot-starter-biz-tenant</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
<!-- Test 测试相关 -->
|
<!-- Test 测试相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.zt.plat</groupId>
|
<groupId>com.zt.plat</groupId>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -20,30 +20,68 @@ public class CompanyVisitContextInterceptor implements HandlerInterceptor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||||
// 解析 header 并设置 visitCompanyId
|
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||||
|
|
||||||
Long companyId = WebFrameworkUtils.getCompanyId(request);
|
Long companyId = WebFrameworkUtils.getCompanyId(request);
|
||||||
|
// 优先使用请求头上的公司信息,若缺失则回退到请求属性或当前登录用户已缓存的访问公司
|
||||||
|
if (companyId == null || companyId <= 0L) {
|
||||||
|
Long attrCompanyId = resolveLong(request.getAttribute(WebFrameworkUtils.HEADER_VISIT_COMPANY_ID));
|
||||||
|
if (attrCompanyId != null && attrCompanyId > 0L) {
|
||||||
|
companyId = attrCompanyId;
|
||||||
|
} else if (loginUser != null && loginUser.getVisitCompanyId() != null && loginUser.getVisitCompanyId() > 0L) {
|
||||||
|
companyId = loginUser.getVisitCompanyId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String companyName = WebFrameworkUtils.getCompanyName(request);
|
String companyName = WebFrameworkUtils.getCompanyName(request);
|
||||||
if (companyId <= 0L) {
|
if (companyName == null || companyName.isEmpty()) {
|
||||||
// 如果没有设置 companyId,则忽略
|
Object attrCompanyName = request.getAttribute(WebFrameworkUtils.HEADER_VISIT_COMPANY_NAME);
|
||||||
|
if (attrCompanyName instanceof String) {
|
||||||
|
companyName = (String) attrCompanyName;
|
||||||
|
} else if (loginUser != null) {
|
||||||
|
companyName = loginUser.getVisitCompanyName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Long deptId = WebFrameworkUtils.getDeptId(request);
|
||||||
|
// 部门信息同样遵循“请求头 -> 请求属性 -> 登录缓存”的回退顺序
|
||||||
|
if (deptId == null || deptId <= 0L) {
|
||||||
|
Long attrDeptId = resolveLong(request.getAttribute(WebFrameworkUtils.HEADER_VISIT_DEPT_ID));
|
||||||
|
if (attrDeptId != null && attrDeptId > 0L) {
|
||||||
|
deptId = attrDeptId;
|
||||||
|
} else if (loginUser != null && loginUser.getVisitDeptId() != null && loginUser.getVisitDeptId() > 0L) {
|
||||||
|
deptId = loginUser.getVisitDeptId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String deptName = WebFrameworkUtils.getDeptName(request);
|
||||||
|
if (deptName == null || deptName.isEmpty()) {
|
||||||
|
Object attrDeptName = request.getAttribute(WebFrameworkUtils.HEADER_VISIT_DEPT_NAME);
|
||||||
|
if (attrDeptName instanceof String) {
|
||||||
|
deptName = (String) attrDeptName;
|
||||||
|
} else if (loginUser != null) {
|
||||||
|
deptName = loginUser.getVisitDeptName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (companyId == null || companyId <= 0L) {
|
||||||
CompanyContextHolder.setIgnore(true);
|
CompanyContextHolder.setIgnore(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Long deptId = WebFrameworkUtils.getDeptId(request);
|
|
||||||
String deptName = WebFrameworkUtils.getDeptName(request);
|
CompanyContextHolder.setIgnore(false);
|
||||||
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
CompanyContextHolder.setCompanyId(companyId);
|
||||||
if (loginUser == null) {
|
if (loginUser == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (deptId > 0L) {
|
|
||||||
|
// 同步最新的访问公司/部门到登录用户对象,供后续数据权限及上下文读取
|
||||||
|
loginUser.setVisitCompanyId(companyId);
|
||||||
|
loginUser.setVisitCompanyName(companyName);
|
||||||
|
if (deptId != null && deptId > 0L) {
|
||||||
loginUser.setVisitDeptId(deptId);
|
loginUser.setVisitDeptId(deptId);
|
||||||
loginUser.setVisitDeptName(deptName);
|
loginUser.setVisitDeptName(deptName);
|
||||||
}
|
}
|
||||||
// if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) {
|
|
||||||
// throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换部门");
|
|
||||||
// }
|
|
||||||
loginUser.setVisitCompanyId(companyId);
|
|
||||||
loginUser.setVisitCompanyName(companyName);
|
|
||||||
CompanyContextHolder.setCompanyId(companyId);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,4 +93,18 @@ public class CompanyVisitContextInterceptor implements HandlerInterceptor {
|
|||||||
loginUser.setVisitCompanyId(0L);
|
loginUser.setVisitCompanyId(0L);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Long resolveLong(Object value) {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).longValue();
|
||||||
|
}
|
||||||
|
if (value instanceof String) {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(((String) value).trim());
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.zt.plat.module.databus.controller.admin.gateway;
|
||||||
|
|
||||||
|
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||||
|
import com.zt.plat.framework.common.pojo.PageResult;
|
||||||
|
import com.zt.plat.module.databus.controller.admin.gateway.convert.ApiAccessLogConvert;
|
||||||
|
import com.zt.plat.module.databus.controller.admin.gateway.vo.accesslog.ApiAccessLogPageReqVO;
|
||||||
|
import com.zt.plat.module.databus.controller.admin.gateway.vo.accesslog.ApiAccessLogRespVO;
|
||||||
|
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiAccessLogDO;
|
||||||
|
import com.zt.plat.module.databus.service.gateway.ApiAccessLogService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Databus API 访问日志控制器。
|
||||||
|
*/
|
||||||
|
@Tag(name = "管理后台 - Databus API 访问日志")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/databus/gateway/access-log")
|
||||||
|
@Validated
|
||||||
|
public class ApiAccessLogController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ApiAccessLogService apiAccessLogService;
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获取访问日志详情")
|
||||||
|
@Parameter(name = "id", description = "日志编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('databus:gateway:access-log:query')")
|
||||||
|
public CommonResult<ApiAccessLogRespVO> get(@RequestParam("id") Long id) {
|
||||||
|
ApiAccessLogDO logDO = apiAccessLogService.get(id);
|
||||||
|
return success(ApiAccessLogConvert.INSTANCE.convert(logDO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "分页查询访问日志")
|
||||||
|
@PreAuthorize("@ss.hasPermission('databus:gateway:access-log:query')")
|
||||||
|
public CommonResult<PageResult<ApiAccessLogRespVO>> page(@Valid ApiAccessLogPageReqVO pageReqVO) {
|
||||||
|
PageResult<ApiAccessLogDO> pageResult = apiAccessLogService.getPage(pageReqVO);
|
||||||
|
return success(ApiAccessLogConvert.INSTANCE.convertPage(pageResult));
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user