update admin
This commit is contained in:
@@ -1,42 +1,365 @@
|
||||
package com.yaoyuan.jiscuss.controller;
|
||||
|
||||
import com.yaoyuan.jiscuss.entity.Discussion;
|
||||
import com.yaoyuan.jiscuss.entity.Post;
|
||||
import com.yaoyuan.jiscuss.entity.Role;
|
||||
import com.yaoyuan.jiscuss.entity.UpgradeLog;
|
||||
import com.yaoyuan.jiscuss.entity.User;
|
||||
import com.yaoyuan.jiscuss.entity.UserInfo;
|
||||
import com.yaoyuan.jiscuss.entity.UserRole;
|
||||
import com.yaoyuan.jiscuss.entity.AuditLog;
|
||||
import com.yaoyuan.jiscuss.repository.DiscussionsRepository;
|
||||
import com.yaoyuan.jiscuss.repository.DiscussionsTagsRepository;
|
||||
import com.yaoyuan.jiscuss.repository.PostsRepository;
|
||||
import com.yaoyuan.jiscuss.repository.RoleRepository;
|
||||
import com.yaoyuan.jiscuss.repository.UpgradeLogRepository;
|
||||
import com.yaoyuan.jiscuss.repository.UserRoleRepository;
|
||||
import com.yaoyuan.jiscuss.repository.UsersRepository;
|
||||
import com.yaoyuan.jiscuss.repository.AuditLogRepository;
|
||||
import com.yaoyuan.jiscuss.service.AuditLogService;
|
||||
import org.springframework.boot.SpringBootVersion;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
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.RequestParam;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author yaoyuan2.chu
|
||||
* 后台系统控制器
|
||||
*/
|
||||
@Controller
|
||||
public class AdminSystemController {
|
||||
public class AdminSystemController extends BaseController {
|
||||
|
||||
/**
|
||||
* 后台登录
|
||||
*/
|
||||
private final UsersRepository usersRepository;
|
||||
private final DiscussionsRepository discussionsRepository;
|
||||
private final PostsRepository postsRepository;
|
||||
private final DiscussionsTagsRepository discussionsTagsRepository;
|
||||
private final RoleRepository roleRepository;
|
||||
private final UserRoleRepository userRoleRepository;
|
||||
private final UpgradeLogRepository upgradeLogRepository;
|
||||
private final AuditLogRepository auditLogRepository;
|
||||
private final AuditLogService auditLogService;
|
||||
|
||||
/**
|
||||
* 后台退出
|
||||
*/
|
||||
@RequestMapping("/admin/logout")
|
||||
public String logout(@RequestParam(defaultValue = "discussion") String type, HttpServletRequest request, ModelMap map) {
|
||||
return "admin/logout";
|
||||
public AdminSystemController(
|
||||
UsersRepository usersRepository,
|
||||
DiscussionsRepository discussionsRepository,
|
||||
PostsRepository postsRepository,
|
||||
DiscussionsTagsRepository discussionsTagsRepository,
|
||||
RoleRepository roleRepository,
|
||||
UserRoleRepository userRoleRepository,
|
||||
UpgradeLogRepository upgradeLogRepository,
|
||||
AuditLogRepository auditLogRepository,
|
||||
AuditLogService auditLogService) {
|
||||
this.usersRepository = usersRepository;
|
||||
this.discussionsRepository = discussionsRepository;
|
||||
this.postsRepository = postsRepository;
|
||||
this.discussionsTagsRepository = discussionsTagsRepository;
|
||||
this.roleRepository = roleRepository;
|
||||
this.userRoleRepository = userRoleRepository;
|
||||
this.upgradeLogRepository = upgradeLogRepository;
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
this.auditLogService = auditLogService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台设置管理
|
||||
*/
|
||||
@GetMapping("/admin/home")
|
||||
public String home(HttpServletRequest request, ModelMap map) {
|
||||
fillCommon(request, map, "home");
|
||||
|
||||
map.put("userCount", usersRepository.count());
|
||||
map.put("discussionCount", discussionsRepository.count());
|
||||
map.put("postCount", postsRepository.count());
|
||||
map.put("roleCount", roleRepository.count());
|
||||
|
||||
map.put("sys", buildSystemMetrics());
|
||||
map.put("deps", buildDependencyVersions());
|
||||
|
||||
/**
|
||||
* 后台页面
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/admin/home")
|
||||
public String home(@RequestParam(defaultValue = "discussion") String type, HttpServletRequest request, ModelMap map) {
|
||||
return "admin/home";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/users")
|
||||
public String users(HttpServletRequest request, ModelMap map) {
|
||||
fillCommon(request, map, "users");
|
||||
|
||||
List<User> users = usersRepository.findAll(Sort.by(Sort.Direction.ASC, "id"));
|
||||
List<Role> roles = roleRepository.findAllByOrderByIdAsc();
|
||||
Map<Integer, Set<Integer>> userRoleIds = new HashMap<>();
|
||||
for (UserRole ur : userRoleRepository.findAll()) {
|
||||
userRoleIds.computeIfAbsent(ur.getUserId(), key -> new HashSet<>()).add(ur.getRoleId());
|
||||
}
|
||||
|
||||
map.put("users", users);
|
||||
map.put("roles", roles);
|
||||
map.put("userRoleIds", userRoleIds);
|
||||
return "admin/users";
|
||||
}
|
||||
|
||||
@PostMapping("/admin/users/{id}/roles")
|
||||
@Transactional
|
||||
public String updateUserRoles(HttpServletRequest request,
|
||||
@PathVariable("id") Integer id,
|
||||
@RequestParam(value = "roleIds", required = false) List<Integer> roleIds) {
|
||||
UserInfo currentAdmin = getUserInfo(request);
|
||||
userRoleRepository.deleteByUserId(id);
|
||||
if (roleIds != null) {
|
||||
Date now = new Date();
|
||||
Set<Integer> uniqueRoleIds = new HashSet<>(roleIds);
|
||||
Set<Integer> validEnabledRoleIds = roleRepository.findAllById(uniqueRoleIds).stream()
|
||||
.filter(role -> role.getEnabled() != null && role.getEnabled() == 1)
|
||||
.map(Role::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (Integer roleId : validEnabledRoleIds) {
|
||||
UserRole ur = new UserRole();
|
||||
ur.setUserId(id);
|
||||
ur.setRoleId(roleId);
|
||||
ur.setCreateTime(now);
|
||||
userRoleRepository.save(ur);
|
||||
}
|
||||
}
|
||||
auditLogService.log(currentAdmin, "update_user_roles", "user", id,
|
||||
"Updated user role assignments. Role count: " + (roleIds == null ? 0 : roleIds.size()));
|
||||
return "redirect:/admin/users";
|
||||
}
|
||||
|
||||
@PostMapping("/admin/roles")
|
||||
public String createRole(HttpServletRequest request,
|
||||
@RequestParam("code") String code,
|
||||
@RequestParam("name") String name,
|
||||
@RequestParam(value = "description", required = false) String description) {
|
||||
UserInfo currentAdmin = getUserInfo(request);
|
||||
String normalizedCode = code == null ? "" : code.trim().toUpperCase();
|
||||
if (normalizedCode.isEmpty()) {
|
||||
return "redirect:/admin/users";
|
||||
}
|
||||
|
||||
Role exists = roleRepository.findByCodeIgnoreCase(normalizedCode);
|
||||
if (exists == null) {
|
||||
Date now = new Date();
|
||||
Role role = new Role();
|
||||
role.setCode(normalizedCode);
|
||||
role.setName(name == null ? "" : name.trim());
|
||||
role.setDescription(description);
|
||||
role.setEnabled(1);
|
||||
role.setCreateTime(now);
|
||||
role.setUpdateTime(now);
|
||||
roleRepository.save(role);
|
||||
auditLogService.log(currentAdmin, "create_role", "role", role.getId(),
|
||||
"Created role: " + normalizedCode);
|
||||
} else {
|
||||
auditLogService.log(currentAdmin, "create_role_duplicate", "role", exists.getId(),
|
||||
"Attempted to create duplicate role: " + normalizedCode, "ignored");
|
||||
}
|
||||
return "redirect:/admin/users";
|
||||
}
|
||||
|
||||
@PostMapping("/admin/roles/{id}/toggle")
|
||||
public String toggleRole(HttpServletRequest request,
|
||||
@PathVariable("id") Integer id) {
|
||||
UserInfo currentAdmin = getUserInfo(request);
|
||||
Role role = roleRepository.findById(id).orElse(null);
|
||||
if (role != null) {
|
||||
int nextStatus = role.getEnabled() != null && role.getEnabled() == 1 ? 0 : 1;
|
||||
// Keep built-in ADMIN role enabled to avoid locking out management access.
|
||||
if ("ADMIN".equalsIgnoreCase(role.getCode()) && nextStatus == 0) {
|
||||
auditLogService.log(currentAdmin, "toggle_role_blocked", "role", id,
|
||||
"Attempted to disable built-in ADMIN role", "blocked");
|
||||
return "redirect:/admin/users";
|
||||
}
|
||||
role.setEnabled(nextStatus);
|
||||
role.setUpdateTime(new Date());
|
||||
roleRepository.save(role);
|
||||
auditLogService.log(currentAdmin, "toggle_role", "role", id,
|
||||
"Toggled role status to " + (nextStatus == 1 ? "enabled" : "disabled"));
|
||||
}
|
||||
return "redirect:/admin/users";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/discussions")
|
||||
public String discussions(HttpServletRequest request, ModelMap map) {
|
||||
fillCommon(request, map, "discussions");
|
||||
List<Discussion> discussions = discussionsRepository.findAll(Sort.by(Sort.Direction.DESC, "createTime"));
|
||||
map.put("discussions", discussions);
|
||||
return "admin/discussions";
|
||||
}
|
||||
|
||||
@PostMapping("/admin/discussions/{id}/delete")
|
||||
@Transactional
|
||||
public String deleteDiscussion(HttpServletRequest request,
|
||||
@PathVariable("id") Integer id) {
|
||||
UserInfo currentAdmin = getUserInfo(request);
|
||||
Discussion discussion = discussionsRepository.findById(id).orElse(null);
|
||||
String title = discussion == null ? "unknown" : discussion.getTitle();
|
||||
discussionsTagsRepository.deleteByDiscussionId(id);
|
||||
postsRepository.deleteByDiscussionId(id);
|
||||
discussionsRepository.deleteById(id);
|
||||
auditLogService.log(currentAdmin, "delete_discussion", "discussion", id,
|
||||
"Deleted discussion: " + title);
|
||||
return "redirect:/admin/discussions";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/posts")
|
||||
public String posts(HttpServletRequest request, ModelMap map) {
|
||||
fillCommon(request, map, "posts");
|
||||
List<Post> posts = postsRepository.findAll(Sort.by(Sort.Direction.DESC, "createTime"));
|
||||
map.put("posts", posts);
|
||||
|
||||
Set<Integer> userIds = posts.stream().map(Post::getCreateId).filter(v -> v != null).collect(Collectors.toSet());
|
||||
Set<Integer> discussionIds = posts.stream().map(Post::getDiscussionId).filter(v -> v != null).collect(Collectors.toSet());
|
||||
|
||||
Map<Integer, String> userNames = usersRepository.findAllById(userIds).stream()
|
||||
.collect(Collectors.toMap(User::getId, User::getUsername));
|
||||
Map<Integer, String> discussionNames = discussionsRepository.findAllById(discussionIds).stream()
|
||||
.collect(Collectors.toMap(Discussion::getId, Discussion::getTitle));
|
||||
|
||||
map.put("userNames", userNames);
|
||||
map.put("discussionNames", discussionNames);
|
||||
return "admin/posts";
|
||||
}
|
||||
|
||||
@PostMapping("/admin/posts/{id}/delete")
|
||||
public String deletePost(HttpServletRequest request,
|
||||
@PathVariable("id") Integer id) {
|
||||
UserInfo currentAdmin = getUserInfo(request);
|
||||
Post post = postsRepository.findById(id).orElse(null);
|
||||
String content = post == null ? "unknown" : (post.getContent() == null ? "" : post.getContent());
|
||||
if (content.length() > 50) {
|
||||
content = content.substring(0, 50) + "...";
|
||||
}
|
||||
postsRepository.deleteById(id);
|
||||
auditLogService.log(currentAdmin, "delete_post", "post", id,
|
||||
"Deleted post: " + content);
|
||||
return "redirect:/admin/posts";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/upgrade-logs")
|
||||
public String upgradeLogs(HttpServletRequest request, ModelMap map) {
|
||||
fillCommon(request, map, "upgradeLogs");
|
||||
map.put("logs", upgradeLogRepository.findAllByOrderByCreateTimeDesc());
|
||||
return "admin/upgrade-logs";
|
||||
}
|
||||
|
||||
@PostMapping("/admin/upgrade-logs")
|
||||
public String createUpgradeLog(HttpServletRequest request,
|
||||
@RequestParam("version") String version,
|
||||
@RequestParam("title") String title,
|
||||
@RequestParam(value = "type", defaultValue = "feature") String type,
|
||||
@RequestParam("content") String content) {
|
||||
UserInfo user = getUserInfo(request);
|
||||
UpgradeLog log = new UpgradeLog();
|
||||
log.setVersion(version);
|
||||
log.setTitle(title);
|
||||
log.setType(type);
|
||||
log.setContent(content);
|
||||
log.setCreatedBy(user == null ? null : user.getId());
|
||||
log.setCreateTime(new Date());
|
||||
upgradeLogRepository.save(log);
|
||||
auditLogService.log(user, "create_upgrade_log", "upgrade_log", log.getId(),
|
||||
"Created upgrade log: " + version + " - " + title);
|
||||
return "redirect:/admin/upgrade-logs";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/plugins")
|
||||
public String plugins(HttpServletRequest request, ModelMap map) {
|
||||
fillCommon(request, map, "plugins");
|
||||
return "admin/plugins";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/audit-logs")
|
||||
public String auditLogs(HttpServletRequest request, ModelMap map) {
|
||||
fillCommon(request, map, "auditLogs");
|
||||
List<AuditLog> logs = auditLogRepository.findAllByOrderByCreateTimeDesc();
|
||||
map.put("logs", logs);
|
||||
return "admin/audit-logs";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/themes")
|
||||
public String themes(HttpServletRequest request, ModelMap map) {
|
||||
fillCommon(request, map, "themes");
|
||||
return "admin/themes";
|
||||
}
|
||||
|
||||
private void fillCommon(HttpServletRequest request, ModelMap map, String active) {
|
||||
UserInfo user = getUserInfo(request);
|
||||
map.put("adminName", user == null ? "admin" : user.getUsername());
|
||||
map.put("active", active);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildSystemMetrics() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
|
||||
|
||||
long totalMb = bytesToMb(rt.totalMemory());
|
||||
long freeMb = bytesToMb(rt.freeMemory());
|
||||
long usedMb = totalMb - freeMb;
|
||||
long maxMb = bytesToMb(rt.maxMemory());
|
||||
long heapUsedMb = bytesToMb(memoryMXBean.getHeapMemoryUsage().getUsed());
|
||||
|
||||
result.put("totalMb", totalMb);
|
||||
result.put("freeMb", freeMb);
|
||||
result.put("usedMb", usedMb);
|
||||
result.put("maxMb", maxMb);
|
||||
result.put("heapUsedMb", heapUsedMb);
|
||||
result.put("uptimeMs", ManagementFactory.getRuntimeMXBean().getUptime());
|
||||
result.put("cpuLoad", readCpuLoadPercent());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, String> buildDependencyVersions() {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("springBoot", SpringBootVersion.getVersion());
|
||||
result.put("java", System.getProperty("java.version"));
|
||||
result.put("spring", implVersion("org.springframework.core.SpringVersion"));
|
||||
result.put("hibernate", implVersion("org.hibernate.Version"));
|
||||
result.put("flyway", implVersion("org.flywaydb.core.Flyway"));
|
||||
result.put("druid", implVersion("com.alibaba.druid.pool.DruidDataSource"));
|
||||
result.put("ehcache", implVersion("org.ehcache.CacheManager"));
|
||||
result.put("springdoc", implVersion("org.springdoc.core.configuration.SpringDocConfiguration"));
|
||||
return result;
|
||||
}
|
||||
|
||||
private String implVersion(String className) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
Package pkg = clazz.getPackage();
|
||||
String version = pkg == null ? null : pkg.getImplementationVersion();
|
||||
return version == null ? "unknown" : version;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private String readCpuLoadPercent() {
|
||||
try {
|
||||
java.lang.management.OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
|
||||
if (osBean instanceof com.sun.management.OperatingSystemMXBean sunOsBean) {
|
||||
double value = sunOsBean.getCpuLoad();
|
||||
if (value >= 0) {
|
||||
return String.format("%.2f%%", value * 100);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
private long bytesToMb(long bytes) {
|
||||
return bytes / 1024 / 1024;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.yaoyuan.jiscuss.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "audit_log")
|
||||
public class AuditLog implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "admin_id")
|
||||
private Integer adminId;
|
||||
|
||||
@Column(name = "admin_name")
|
||||
private String adminName;
|
||||
|
||||
@Column(name = "action_type")
|
||||
private String actionType;
|
||||
|
||||
@Column(name = "target_type")
|
||||
private String targetType;
|
||||
|
||||
@Column(name = "target_id")
|
||||
private Integer targetId;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "status")
|
||||
private String status;
|
||||
|
||||
@Column(name = "create_time")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.yaoyuan.jiscuss.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "rbac_role")
|
||||
public class Role implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "code")
|
||||
private String code;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "enabled")
|
||||
private Integer enabled;
|
||||
|
||||
@Column(name = "create_time")
|
||||
private Date createTime;
|
||||
|
||||
@Column(name = "update_time")
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.yaoyuan.jiscuss.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "upgrade_log")
|
||||
public class UpgradeLog implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "version")
|
||||
private String version;
|
||||
|
||||
@Column(name = "title")
|
||||
private String title;
|
||||
|
||||
@Column(name = "content")
|
||||
private String content;
|
||||
|
||||
@Column(name = "type")
|
||||
private String type;
|
||||
|
||||
@Column(name = "created_by")
|
||||
private Integer createdBy;
|
||||
|
||||
@Column(name = "create_time")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.yaoyuan.jiscuss.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "rbac_user_role")
|
||||
public class UserRole implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "user_id")
|
||||
private Integer userId;
|
||||
|
||||
@Column(name = "role_id")
|
||||
private Integer roleId;
|
||||
|
||||
@Column(name = "create_time")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.yaoyuan.jiscuss.repository;
|
||||
|
||||
import com.yaoyuan.jiscuss.entity.AuditLog;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface AuditLogRepository extends JpaRepository<AuditLog, Integer> {
|
||||
List<AuditLog> findAllByOrderByCreateTimeDesc();
|
||||
|
||||
Page<AuditLog> findAllByOrderByCreateTimeDesc(Pageable pageable);
|
||||
|
||||
List<AuditLog> findByTargetTypeAndTargetIdOrderByCreateTimeDesc(String targetType, Integer targetId);
|
||||
|
||||
List<AuditLog> findByAdminIdOrderByCreateTimeDesc(Integer adminId);
|
||||
}
|
||||
@@ -7,4 +7,5 @@ import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface DiscussionsTagsRepository extends JpaRepository<DiscussionTag, Integer> {
|
||||
void deleteByDiscussionId(Integer discussionId);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import java.util.Map;
|
||||
@Repository
|
||||
public interface PostsRepository extends JpaRepository<Post, Integer> {
|
||||
|
||||
void deleteByDiscussionId(Integer discussionId);
|
||||
|
||||
/**
|
||||
* @param dId
|
||||
* @return
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.yaoyuan.jiscuss.repository;
|
||||
|
||||
import com.yaoyuan.jiscuss.entity.Role;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface RoleRepository extends JpaRepository<Role, Integer> {
|
||||
Role findByCode(String code);
|
||||
|
||||
Role findByCodeIgnoreCase(String code);
|
||||
|
||||
List<Role> findAllByOrderByIdAsc();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.yaoyuan.jiscuss.repository;
|
||||
|
||||
import com.yaoyuan.jiscuss.entity.UpgradeLog;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface UpgradeLogRepository extends JpaRepository<UpgradeLog, Integer> {
|
||||
List<UpgradeLog> findAllByOrderByCreateTimeDesc();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.yaoyuan.jiscuss.repository;
|
||||
|
||||
import com.yaoyuan.jiscuss.entity.UserRole;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface UserRoleRepository extends JpaRepository<UserRole, Integer> {
|
||||
List<UserRole> findByUserId(Integer userId);
|
||||
|
||||
void deleteByUserId(Integer userId);
|
||||
|
||||
@Query(value = "select r.code from rbac_user_role ur join rbac_role r on ur.role_id = r.id where ur.user_id = :userId and r.enabled = 1",
|
||||
nativeQuery = true)
|
||||
List<String> findRoleCodesByUserId(@Param("userId") Integer userId);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.yaoyuan.jiscuss.service;
|
||||
|
||||
import com.yaoyuan.jiscuss.entity.AuditLog;
|
||||
import com.yaoyuan.jiscuss.entity.UserInfo;
|
||||
import com.yaoyuan.jiscuss.repository.AuditLogRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Service for recording admin operations in audit log.
|
||||
*/
|
||||
@Service
|
||||
public class AuditLogService {
|
||||
|
||||
private final AuditLogRepository auditLogRepository;
|
||||
|
||||
public AuditLogService(AuditLogRepository auditLogRepository) {
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void log(Integer adminId, String adminName, String actionType, String targetType,
|
||||
Integer targetId, String description, String status) {
|
||||
AuditLog log = new AuditLog();
|
||||
log.setAdminId(adminId);
|
||||
log.setAdminName(adminName);
|
||||
log.setActionType(actionType);
|
||||
log.setTargetType(targetType);
|
||||
log.setTargetId(targetId);
|
||||
log.setDescription(description);
|
||||
log.setStatus(status == null ? "success" : status);
|
||||
log.setCreateTime(new Date());
|
||||
auditLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void log(Integer adminId, String adminName, String actionType, String targetType,
|
||||
Integer targetId, String description) {
|
||||
log(adminId, adminName, actionType, targetType, targetId, description, "success");
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void log(UserInfo user, String actionType, String targetType,
|
||||
Integer targetId, String description) {
|
||||
Integer userId = user == null ? null : user.getId();
|
||||
String userName = user == null ? "unknown" : user.getUsername();
|
||||
log(userId, userName, actionType, targetType, targetId, description, "success");
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void log(UserInfo user, String actionType, String targetType,
|
||||
Integer targetId, String description, String status) {
|
||||
Integer userId = user == null ? null : user.getId();
|
||||
String userName = user == null ? "unknown" : user.getUsername();
|
||||
log(userId, userName, actionType, targetType, targetId, description, status);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.yaoyuan.jiscuss.service.impl;
|
||||
|
||||
import com.yaoyuan.jiscuss.entity.User;
|
||||
import com.yaoyuan.jiscuss.entity.UserInfo;
|
||||
import com.yaoyuan.jiscuss.repository.UserRoleRepository;
|
||||
import com.yaoyuan.jiscuss.service.IUsersService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
@@ -29,6 +30,9 @@ public class UserDetailServiceImpl implements UserDetailsService {
|
||||
@Autowired
|
||||
private IUsersService userInfoService;
|
||||
|
||||
@Autowired
|
||||
private UserRoleRepository userRoleRepository;
|
||||
|
||||
@Override
|
||||
public UserInfo loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
User userInfo = userInfoService.getByUsername(username);
|
||||
@@ -36,11 +40,16 @@ public class UserDetailServiceImpl implements UserDetailsService {
|
||||
throw new UsernameNotFoundException("用户不存在: " + username);
|
||||
}
|
||||
|
||||
// Assign role based on user level: level >= 1 → ADMIN, else → USER
|
||||
String role = (userInfo.getLevel() != null && userInfo.getLevel() >= 1) ? "ADMIN" : "USER";
|
||||
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
|
||||
List<String> roleCodes = userRoleRepository.findRoleCodesByUserId(userInfo.getId());
|
||||
if (roleCodes == null || roleCodes.isEmpty()) {
|
||||
// Compatibility fallback for legacy data before RBAC migration.
|
||||
String role = (userInfo.getLevel() != null && userInfo.getLevel() >= 1) ? "ADMIN" : "USER";
|
||||
roleCodes = List.of(role);
|
||||
}
|
||||
for (String roleCode : roleCodes) {
|
||||
authorities.add(new SimpleGrantedAuthority("ROLE_" + roleCode));
|
||||
}
|
||||
|
||||
return new UserInfo(
|
||||
authorities,
|
||||
|
||||
Reference in New Issue
Block a user