Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2026 github.com/MaloneTalk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* limitations under the License.
*/
package io.github.malonetalk.config;

import io.github.malonetalk.entity.SysRole;
import io.github.malonetalk.mapper.SysRoleMapper;
import java.time.LocalDateTime;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

/**
* 启动引导:sys_role 为空时创建初始角色。
*
* <p>创建管理员(id=1)和普通用户(id=2)两个角色,
* 与 {@code @AdminOnly} 注解配合实现管理员权限判定。
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class RoleBootstrapRunner implements CommandLineRunner {

private final SysRoleMapper sysRoleMapper;

@Override
public void run(String... args) {
if (sysRoleMapper.countAll() > 0) {
return;
}
LocalDateTime now = LocalDateTime.now();

SysRole admin = new SysRole();
admin.setId(1);
admin.setName("管理员");
admin.setDescription("系统管理员,拥有所有权限");
admin.setCreateTime(now);
admin.setUpdateTime(now);
sysRoleMapper.insert(admin);

SysRole user = new SysRole();
user.setId(2);
user.setName("普通用户");
user.setDescription("普通用户,受限权限");
user.setCreateTime(now);
user.setUpdateTime(now);
sysRoleMapper.insert(user);

log.info("Bootstrapped initial roles: 管理员 (id=1), 普通用户 (id=2).");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@
import io.github.malonetalk.convertor.DatasourceConverter;
import io.github.malonetalk.dto.DatasourceRequest;
import io.github.malonetalk.dto.DatasourceResponse;
import io.github.malonetalk.entity.ColumnInfo;
import io.github.malonetalk.entity.Datasource;
import io.github.malonetalk.entity.TableInfo;
import io.github.malonetalk.enums.Status;
import io.github.malonetalk.exception.BusinessException;
import io.github.malonetalk.mapper.ColumnSemanticInfoMapper;
import io.github.malonetalk.mapper.TableInfoMapper;
import io.github.malonetalk.service.DatasourceService;
import jakarta.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -48,6 +54,18 @@ public class DatasourceController {
private final DatasourceService dataSourceService;
private final DatasourceConverter datasourceConverter;

/**
* TableInfoMapper 和 ColumnSemanticInfoMapper 直接注入 Controller 而非通过 Service:
*
* <p>{@code GET /{id}/tables} 和 {@code GET /{id}/columns} 仅做"查 mapper → 返回"的纯透传,
* 零业务逻辑(无 join、无事务、无缓存、无校验)。引入中间 Service 只会产生一层无意义的委托代码。
*
* <p>如果后续需要按角色过滤返回结果(权限控制),届时再将两个查询收拢到 TableMetadataService。
*/
private final TableInfoMapper tableInfoMapper;

private final ColumnSemanticInfoMapper columnSemanticInfoMapper;

@GetMapping
public Result<List<DatasourceResponse>> findAll() {
List<DatasourceResponse> list =
Expand All @@ -60,6 +78,33 @@ public Result<DatasourceResponse> findById(@PathVariable Integer id) {
return Result.success(datasourceConverter.toResponse(requireDatasource(id)));
}

/** 返回某数据源下的所有物理表名,供权限配置页使用。 */
@AdminOnly
@GetMapping("/{id}/tables")
public Result<List<String>> listTableNames(@PathVariable Integer id) {
requireDatasource(id);
List<String> tables =
tableInfoMapper.selectByDatasourceId(id).stream()
.map(TableInfo::getTableName)
.toList();
return Result.success(tables);
}

/** 返回某数据源下所有表的列名,一次查询,供列级权限配置使用。 */
@AdminOnly
@GetMapping("/{id}/columns")
public Result<Map<String, List<String>>> listAllColumns(@PathVariable Integer id) {
requireDatasource(id);
Map<String, List<String>> map =
columnSemanticInfoMapper.selectByDatasourceId(id).stream()
.collect(
Collectors.groupingBy(
ColumnInfo::getTableName,
Collectors.mapping(
ColumnInfo::getColumnName, Collectors.toList())));
return Result.success(map);
}

@AdminOnly
@PostMapping
public Result<Boolean> save(@Valid @RequestBody DatasourceRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@AdminOnly
@Slf4j
@RestController
@AllArgsConstructor
Expand All @@ -50,12 +49,14 @@ public class MetricController {
private final MetricService metricService;
private final MetricConverter metricConverter;

@AdminOnly
@PostMapping
public Result<MetricResponse> create(@Valid @RequestBody MetricRequest request) {
MetricInfo entity = metricConverter.toEntity(request);
return Result.success(metricConverter.toResponse(metricService.create(entity)));
}

@AdminOnly
@PutMapping("/{id}")
public Result<MetricResponse> update(
@PathVariable @Positive(message = "id 必须为正数") Integer id,
Expand All @@ -64,6 +65,7 @@ public Result<MetricResponse> update(
return Result.success(metricConverter.toResponse(metricService.update(id, entity)));
}

@AdminOnly
@DeleteMapping("/{id}")
public Result<Boolean> delete(@PathVariable @Positive(message = "id 必须为正数") Integer id) {
metricService.delete(id);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2026 github.com/MaloneTalk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* limitations under the License.
*/
package io.github.malonetalk.controller;

import io.github.malonetalk.annotation.AdminOnly;
import io.github.malonetalk.common.Result;
import io.github.malonetalk.dto.ColumnPermissionResponse;
import io.github.malonetalk.dto.RoleRequest;
import io.github.malonetalk.dto.RoleResponse;
import io.github.malonetalk.dto.SaveColumnPermissionRequest;
import io.github.malonetalk.dto.SaveTablePermissionRequest;
import io.github.malonetalk.dto.TablePermissionResponse;
import io.github.malonetalk.service.SysRoleService;
import jakarta.validation.Valid;
import java.util.List;
import lombok.AllArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@AdminOnly
@RestController
@AllArgsConstructor
@RequestMapping("/api/sys/role")
@Validated
public class SysRoleController {

private final SysRoleService sysRoleService;

@GetMapping
public Result<List<RoleResponse>> listAll() {
return Result.success(sysRoleService.listAll());
}

@PostMapping
public Result<RoleResponse> create(@Valid @RequestBody RoleRequest request) {
return Result.success(sysRoleService.create(request));
}

@PutMapping("/{id}")
public Result<RoleResponse> update(
@PathVariable Integer id, @Valid @RequestBody RoleRequest request) {
return Result.success(sysRoleService.update(id, request));
}

@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable Integer id) {
sysRoleService.delete(id);
return Result.success();
}

@GetMapping("/{roleId}/permissions")
public Result<List<TablePermissionResponse>> getPermissions(@PathVariable Integer roleId) {
return Result.success(sysRoleService.getPermissions(roleId));
}

@PutMapping("/{roleId}/permissions")
public Result<Void> savePermissions(
@PathVariable Integer roleId, @Valid @RequestBody SaveTablePermissionRequest request) {
sysRoleService.savePermissions(roleId, request);
return Result.success();
}

@GetMapping("/{roleId}/columns")
public Result<List<ColumnPermissionResponse>> getColumnPermissions(
@PathVariable Integer roleId, @RequestParam Integer datasourceId) {
return Result.success(sysRoleService.getColumnPermissions(roleId, datasourceId));
}

@PutMapping("/{roleId}/columns")
public Result<Void> saveColumnPermissions(
@PathVariable Integer roleId, @Valid @RequestBody SaveColumnPermissionRequest request) {
sysRoleService.saveColumnPermissions(roleId, request);
return Result.success();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2026 github.com/MaloneTalk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* limitations under the License.
*/
package io.github.malonetalk.convertor;

import io.github.malonetalk.dto.RoleResponse;
import io.github.malonetalk.entity.SysRole;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface RoleConverter {

RoleResponse toResponse(SysRole role);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2026 github.com/MaloneTalk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* limitations under the License.
*/
package io.github.malonetalk.dto;

import java.util.List;

public record ColumnPermissionResponse(String tableName, List<String> columnNames) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2026 github.com/MaloneTalk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* limitations under the License.
*/
package io.github.malonetalk.dto;

import jakarta.validation.constraints.NotBlank;

public record RoleRequest(@NotBlank(message = "name 不能为空") String name, String description) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2026 github.com/MaloneTalk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* limitations under the License.
*/
package io.github.malonetalk.dto;

import java.time.LocalDateTime;

public record RoleResponse(Integer id, String name, String description, LocalDateTime createTime) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2026 github.com/MaloneTalk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* limitations under the License.
*/
package io.github.malonetalk.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.util.List;

public record SaveColumnPermissionRequest(
@NotNull(message = "datasourceId 不能为空") Integer datasourceId,
@NotBlank(message = "tableName 不能为空") String tableName,
List<String> columnNames) {}
Loading
Loading