refactor: 后端标识统一为 FruitShorts,并落地 05 离线下载
为什么: 1) 产品已定名 FruitShorts,但后端仍是 reelshort 系列标识 —— Java 包 com.reelshort、 模块名与 jar 名 reelshort-*、spring.application.name、配置前缀 reelshort.*, 这些会出现在启动日志、命令行和 IDE 里,与产品名不一致。本次整体替换为 fruitshorts, 数据库连接名上一轮已改名,保持不变。 2) 第一阶段下一批第 1 步(05 离线下载)要求把下载授权(设备绑定/有效期/吊销)、 下载任务与断点续传、配额与过期清理、仅 Wi-Fi 规则做成完整接口,本提交一并落地。 3) 顺带修复:schema-phase1.sql 里 playback_ticket / order_event 仍按普通表定义唯一索引, 表分区化之后重跑脚本必定报错(分区表的唯一索引必须包含分区键)。改为在本脚本里就 按分区表定义,并验证脚本可重复执行。
This commit is contained in:
1 parent
a4aa749cc6
commit
b3179fc836
251 files changed
+7261
-767
No files matched your search
+36
@@ -0,0 +1,36 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("sys_admin_user")
|
||||
public class AdminUser {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String username;
|
||||
private String password;
|
||||
private String realName;
|
||||
private String mobile;
|
||||
private String email;
|
||||
private String avatar;
|
||||
private Integer status;
|
||||
private String ipWhitelist;
|
||||
private Integer twoFactor;
|
||||
private LocalDateTime lastLoginAt;
|
||||
private String lastLoginIp;
|
||||
private LocalDateTime createdAt;
|
||||
/** updated_at 交给数据库的 ON UPDATE CURRENT_TIMESTAMP 维护 */
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private LocalDateTime updatedAt;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("sys_admin_user_role")
|
||||
public class AdminUserRole {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private Long roleId;
|
||||
|
||||
public AdminUserRole(Long userId, Long roleId) {
|
||||
this.userId = userId;
|
||||
this.roleId = roleId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("sys_menu")
|
||||
public class Menu {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long parentId;
|
||||
private String title;
|
||||
private String routePath;
|
||||
private String routeName;
|
||||
private String component;
|
||||
private String redirect;
|
||||
private String icon;
|
||||
private String permissionCode;
|
||||
private String type;
|
||||
private Integer alwaysShow;
|
||||
private Integer affix;
|
||||
private Integer sort;
|
||||
private Integer visible;
|
||||
private Integer status;
|
||||
private LocalDateTime createdAt;
|
||||
/** updated_at 交给数据库的 ON UPDATE CURRENT_TIMESTAMP 维护 */
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("sys_operation_log")
|
||||
public class OperationLog {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long adminUserId;
|
||||
private String username;
|
||||
private String module;
|
||||
private String action;
|
||||
private String requestMethod;
|
||||
private String requestUrl;
|
||||
private String requestParams;
|
||||
private String requestBody;
|
||||
private String responseBody;
|
||||
/** SUCCESS / FAIL */
|
||||
private String status;
|
||||
private String errorMsg;
|
||||
private Integer durationMs;
|
||||
private String ip;
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("sys_permission")
|
||||
public class Permission {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String code;
|
||||
private String name;
|
||||
private String groupName;
|
||||
private String type;
|
||||
private Integer sort;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("sys_role")
|
||||
public class Role {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private Long categoryId;
|
||||
private Integer level;
|
||||
private String description;
|
||||
private Integer status;
|
||||
private Integer isBuiltin;
|
||||
private LocalDateTime createdAt;
|
||||
/** updated_at 交给数据库的 ON UPDATE CURRENT_TIMESTAMP 维护 */
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private LocalDateTime updatedAt;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("sys_role_category")
|
||||
public class RoleCategory {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String name;
|
||||
private String description;
|
||||
private Integer sort;
|
||||
private LocalDateTime createdAt;
|
||||
/** updated_at 交给数据库的 ON UPDATE CURRENT_TIMESTAMP 维护 */
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private LocalDateTime updatedAt;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("sys_role_menu")
|
||||
public class RoleMenu {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long roleId;
|
||||
private Long menuId;
|
||||
|
||||
public RoleMenu(Long roleId, Long menuId) {
|
||||
this.roleId = roleId;
|
||||
this.menuId = menuId;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("sys_role_permission")
|
||||
public class RolePermission {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long roleId;
|
||||
private Long permissionId;
|
||||
|
||||
public RolePermission(Long roleId, Long permissionId) {
|
||||
this.roleId = roleId;
|
||||
this.permissionId = permissionId;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.fruitshorts.system.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("sys_config")
|
||||
public class SysConfig {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String configKey;
|
||||
private String configValue;
|
||||
private String remark;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.AdminUser;
|
||||
|
||||
public interface AdminUserMapper extends BaseMapper<AdminUser> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.AdminUserRole;
|
||||
|
||||
public interface AdminUserRoleMapper extends BaseMapper<AdminUserRole> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.Menu;
|
||||
|
||||
public interface MenuMapper extends BaseMapper<Menu> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.OperationLog;
|
||||
|
||||
public interface OperationLogMapper extends BaseMapper<OperationLog> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.Permission;
|
||||
|
||||
public interface PermissionMapper extends BaseMapper<Permission> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.RoleCategory;
|
||||
|
||||
public interface RoleCategoryMapper extends BaseMapper<RoleCategory> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.Role;
|
||||
|
||||
public interface RoleMapper extends BaseMapper<Role> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.RoleMenu;
|
||||
|
||||
public interface RoleMenuMapper extends BaseMapper<RoleMenu> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.RolePermission;
|
||||
|
||||
public interface RolePermissionMapper extends BaseMapper<RolePermission> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.fruitshorts.system.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fruitshorts.system.dal.entity.SysConfig;
|
||||
|
||||
public interface SysConfigMapper extends BaseMapper<SysConfig> {
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
package com.fruitshorts.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fruitshorts.common.exception.ErrorCode;
|
||||
import com.fruitshorts.common.exception.ServiceException;
|
||||
import com.fruitshorts.common.pojo.PageResult;
|
||||
import com.fruitshorts.system.dal.entity.AdminUser;
|
||||
import com.fruitshorts.system.dal.entity.AdminUserRole;
|
||||
import com.fruitshorts.system.dal.mapper.AdminUserMapper;
|
||||
import com.fruitshorts.system.dal.mapper.AdminUserRoleMapper;
|
||||
import com.fruitshorts.system.vo.AdminUserPageReqVO;
|
||||
import com.fruitshorts.system.vo.AdminUserRespVO;
|
||||
import com.fruitshorts.system.vo.AdminUserSaveReqVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdminUserService {
|
||||
|
||||
/** 内置超级管理员账号,不允许删除或停用。 */
|
||||
public static final String BUILTIN_ADMIN = "admin";
|
||||
|
||||
private final AdminUserMapper adminUserMapper;
|
||||
private final AdminUserRoleMapper adminUserRoleMapper;
|
||||
private final RoleService roleService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public PageResult<AdminUserRespVO> page(AdminUserPageReqVO req) {
|
||||
List<Long> userIdsOfRole = req.getRoleId() == null ? null
|
||||
: adminUserRoleMapper.selectList(Wrappers.<AdminUserRole>lambdaQuery()
|
||||
.eq(AdminUserRole::getRoleId, req.getRoleId()))
|
||||
.stream().map(AdminUserRole::getUserId).toList();
|
||||
if (userIdsOfRole != null && userIdsOfRole.isEmpty()) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
|
||||
Page<AdminUser> page = new Page<>(req.getSafePageIndex(), req.getSafePageSize());
|
||||
IPage<AdminUser> result = adminUserMapper.selectPage(page, Wrappers.<AdminUser>lambdaQuery()
|
||||
.like(StringUtils.hasText(req.getUsername()), AdminUser::getUsername, req.getUsername())
|
||||
.eq(req.getStatus() != null, AdminUser::getStatus, req.getStatus())
|
||||
.in(userIdsOfRole != null, AdminUser::getId, userIdsOfRole)
|
||||
.orderByAsc(AdminUser::getId));
|
||||
return new PageResult<>(result.getTotal(), toRespList(result.getRecords()));
|
||||
}
|
||||
|
||||
public AdminUserRespVO get(Long id) {
|
||||
AdminUser user = getExisting(id);
|
||||
return toRespList(List.of(user)).get(0);
|
||||
}
|
||||
|
||||
public AdminUser getByUsername(String username) {
|
||||
return adminUserMapper.selectOne(
|
||||
Wrappers.<AdminUser>lambdaQuery().eq(AdminUser::getUsername, username));
|
||||
}
|
||||
|
||||
public AdminUser getExisting(Long id) {
|
||||
AdminUser user = id == null ? null : adminUserMapper.selectById(id);
|
||||
if (user == null) {
|
||||
throw new ServiceException(ErrorCode.DATA_NOT_FOUND, "管理员不存在");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long create(AdminUserSaveReqVO req) {
|
||||
if (!StringUtils.hasText(req.getUsername())) {
|
||||
throw new ServiceException(ErrorCode.BAD_REQUEST, "请输入登录账号");
|
||||
}
|
||||
if (!StringUtils.hasText(req.getPassword())) {
|
||||
throw new ServiceException(ErrorCode.BAD_REQUEST, "请输入初始密码");
|
||||
}
|
||||
if (getByUsername(req.getUsername()) != null) {
|
||||
throw new ServiceException(ErrorCode.DATA_EXISTS, "登录账号已存在");
|
||||
}
|
||||
roleService.getExisting(req.getRoleId());
|
||||
|
||||
AdminUser user = new AdminUser();
|
||||
user.setUsername(req.getUsername());
|
||||
user.setPassword(passwordEncoder.encode(req.getPassword()));
|
||||
user.setRealName(req.getName());
|
||||
user.setStatus(req.getStatus() == null ? 1 : req.getStatus());
|
||||
user.setIpWhitelist(req.getIpWhitelist() == null ? "" : req.getIpWhitelist());
|
||||
user.setTwoFactor(req.getTwoFactor() == null ? 0 : req.getTwoFactor());
|
||||
user.setMobile("");
|
||||
user.setEmail("");
|
||||
user.setAvatar("");
|
||||
user.setLastLoginIp("");
|
||||
adminUserMapper.insert(user);
|
||||
adminUserRoleMapper.insert(new AdminUserRole(user.getId(), req.getRoleId()));
|
||||
return user.getId();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(Long id, AdminUserSaveReqVO req) {
|
||||
AdminUser user = getExisting(id);
|
||||
roleService.getExisting(req.getRoleId());
|
||||
user.setRealName(req.getName());
|
||||
if (req.getIpWhitelist() != null) {
|
||||
user.setIpWhitelist(req.getIpWhitelist());
|
||||
}
|
||||
if (req.getTwoFactor() != null) {
|
||||
user.setTwoFactor(req.getTwoFactor());
|
||||
}
|
||||
if (req.getStatus() != null) {
|
||||
if (BUILTIN_ADMIN.equals(user.getUsername()) && req.getStatus() != 1) {
|
||||
throw new ServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "内置管理员账号不能停用");
|
||||
}
|
||||
user.setStatus(req.getStatus());
|
||||
}
|
||||
adminUserMapper.updateById(user);
|
||||
|
||||
adminUserRoleMapper.delete(Wrappers.<AdminUserRole>lambdaQuery().eq(AdminUserRole::getUserId, id));
|
||||
adminUserRoleMapper.insert(new AdminUserRole(id, req.getRoleId()));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delete(Long id) {
|
||||
AdminUser user = getExisting(id);
|
||||
if (BUILTIN_ADMIN.equals(user.getUsername())) {
|
||||
throw new ServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "内置管理员账号不能删除");
|
||||
}
|
||||
adminUserMapper.deleteById(id);
|
||||
adminUserRoleMapper.delete(Wrappers.<AdminUserRole>lambdaQuery().eq(AdminUserRole::getUserId, id));
|
||||
}
|
||||
|
||||
public void updateStatus(Long id, Integer status) {
|
||||
AdminUser user = getExisting(id);
|
||||
if (BUILTIN_ADMIN.equals(user.getUsername()) && status != null && status != 1) {
|
||||
throw new ServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "内置管理员账号不能停用");
|
||||
}
|
||||
user.setStatus(status == null ? 1 : status);
|
||||
adminUserMapper.updateById(user);
|
||||
}
|
||||
|
||||
public void resetPassword(Long id, String newPassword) {
|
||||
AdminUser user = getExisting(id);
|
||||
if (!StringUtils.hasText(newPassword) || newPassword.length() < 6) {
|
||||
throw new ServiceException(ErrorCode.BAD_REQUEST, "新密码长度不能少于 6 位");
|
||||
}
|
||||
user.setPassword(passwordEncoder.encode(newPassword));
|
||||
adminUserMapper.updateById(user);
|
||||
}
|
||||
|
||||
public void changePassword(Long id, String oldPassword, String newPassword) {
|
||||
AdminUser user = getExisting(id);
|
||||
if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
|
||||
throw new ServiceException(ErrorCode.BAD_REQUEST, "原密码不正确");
|
||||
}
|
||||
user.setPassword(passwordEncoder.encode(newPassword));
|
||||
adminUserMapper.updateById(user);
|
||||
}
|
||||
|
||||
public void recordLogin(Long id, String ip) {
|
||||
AdminUser user = adminUserMapper.selectById(id);
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
user.setLastLoginAt(LocalDateTime.now());
|
||||
user.setLastLoginIp(ip == null ? "" : ip);
|
||||
adminUserMapper.updateById(user);
|
||||
}
|
||||
|
||||
private List<AdminUserRespVO> toRespList(List<AdminUser> users) {
|
||||
if (users.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
Map<Long, String> roleNames = roleService.roleNameMap();
|
||||
return users.stream().map(user -> {
|
||||
List<Long> roleIds = roleService.listRoleIdsByUserId(user.getId());
|
||||
Long roleId = roleIds.isEmpty() ? null : roleIds.get(0);
|
||||
AdminUserRespVO vo = new AdminUserRespVO();
|
||||
vo.setId(user.getId());
|
||||
vo.setUsername(user.getUsername());
|
||||
vo.setName(user.getRealName());
|
||||
vo.setRoleId(roleId);
|
||||
vo.setRole(roleId == null ? "" : roleNames.getOrDefault(roleId, ""));
|
||||
vo.setIpWhitelist(StringUtils.hasText(user.getIpWhitelist()) ? user.getIpWhitelist() : "不限");
|
||||
vo.setStatus(user.getStatus());
|
||||
vo.setStatusText(user.getStatus() != null && user.getStatus() == 1 ? "启用" : "停用");
|
||||
vo.setTwoFactor(user.getTwoFactor());
|
||||
vo.setTwoFactorText(user.getTwoFactor() != null && user.getTwoFactor() == 1 ? "已开启" : "未开启");
|
||||
vo.setCreatedAt(user.getCreatedAt());
|
||||
vo.setLastLogin(user.getLastLoginAt());
|
||||
return vo;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package com.fruitshorts.system.service;
|
||||
|
||||
import com.fruitshorts.common.exception.ErrorCode;
|
||||
import com.fruitshorts.common.exception.ServiceException;
|
||||
import com.fruitshorts.framework.security.JwtTokenProvider;
|
||||
import com.fruitshorts.framework.security.LoginUser;
|
||||
import com.fruitshorts.system.dal.entity.AdminUser;
|
||||
import com.fruitshorts.system.dal.entity.Role;
|
||||
import com.fruitshorts.system.vo.LoginReqVO;
|
||||
import com.fruitshorts.system.vo.LoginRespVO;
|
||||
import com.fruitshorts.system.vo.MenuVO;
|
||||
import com.fruitshorts.system.vo.ProfileRespVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AuthService {
|
||||
|
||||
private final AdminUserService adminUserService;
|
||||
private final RoleService roleService;
|
||||
private final MenuService menuService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtTokenProvider tokenProvider;
|
||||
|
||||
public LoginRespVO login(LoginReqVO req, String ip) {
|
||||
AdminUser user = adminUserService.getByUsername(req.getUsername());
|
||||
if (user == null || !passwordEncoder.matches(req.getPassword(), user.getPassword())) {
|
||||
throw new ServiceException(ErrorCode.LOGIN_FAILED);
|
||||
}
|
||||
if (user.getStatus() == null || user.getStatus() != 1) {
|
||||
throw new ServiceException(ErrorCode.ACCOUNT_DISABLED);
|
||||
}
|
||||
Role role = roleService.getRoleOfUser(user.getId());
|
||||
List<String> permissions = role == null ? List.of() : roleService.listCodesByRoleId(role.getId());
|
||||
LoginUser loginUser = new LoginUser(
|
||||
user.getId(), user.getUsername(), role == null ? null : role.getCode(), permissions);
|
||||
adminUserService.recordLogin(user.getId(), ip);
|
||||
|
||||
return LoginRespVO.builder()
|
||||
.token(tokenProvider.createAdminToken(loginUser))
|
||||
.userId(user.getId())
|
||||
.username(user.getUsername())
|
||||
.name(user.getRealName())
|
||||
.avatar(user.getAvatar())
|
||||
.role(role == null ? null : role.getCode())
|
||||
.roleName(role == null ? null : role.getName())
|
||||
.roleId(role == null ? null : role.getId())
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
}
|
||||
|
||||
public List<MenuVO> listMenus(Long userId) {
|
||||
return menuService.listForUser(userId);
|
||||
}
|
||||
|
||||
/** 当前登录人信息(含实时权限),前端每次进入应用会重新拉取。 */
|
||||
public ProfileRespVO profile(Long userId) {
|
||||
AdminUser user = adminUserService.getExisting(userId);
|
||||
Role role = roleService.getRoleOfUser(userId);
|
||||
List<String> permissions = role == null ? List.of() : roleService.listCodesByRoleId(role.getId());
|
||||
return ProfileRespVO.builder()
|
||||
.userId(user.getId())
|
||||
.username(user.getUsername())
|
||||
.name(user.getRealName())
|
||||
.avatar(user.getAvatar())
|
||||
.role(role == null ? null : role.getCode())
|
||||
.roleName(role == null ? null : role.getName())
|
||||
.roleId(role == null ? null : role.getId())
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void changePassword(Long userId, String oldPassword, String newPassword) {
|
||||
adminUserService.changePassword(userId, oldPassword, newPassword);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.fruitshorts.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.fruitshorts.system.dal.entity.SysConfig;
|
||||
import com.fruitshorts.system.dal.mapper.SysConfigMapper;
|
||||
import com.fruitshorts.system.vo.ConfigItemVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ConfigService {
|
||||
|
||||
private final SysConfigMapper sysConfigMapper;
|
||||
|
||||
public List<ConfigItemVO> list() {
|
||||
return sysConfigMapper.selectList(null).stream()
|
||||
.map(config -> new ConfigItemVO(config.getConfigKey(), config.getConfigValue(), config.getRemark()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public String get(String key, String defaultValue) {
|
||||
SysConfig config = sysConfigMapper.selectOne(
|
||||
Wrappers.<SysConfig>lambdaQuery().eq(SysConfig::getConfigKey, key));
|
||||
return config == null || config.getConfigValue() == null ? defaultValue : config.getConfigValue();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(Map<String, String> values) {
|
||||
values.forEach((key, value) -> {
|
||||
SysConfig config = sysConfigMapper.selectOne(
|
||||
Wrappers.<SysConfig>lambdaQuery().eq(SysConfig::getConfigKey, key));
|
||||
if (config == null) {
|
||||
config = new SysConfig();
|
||||
config.setConfigKey(key);
|
||||
config.setConfigValue(value);
|
||||
config.setRemark("");
|
||||
sysConfigMapper.insert(config);
|
||||
} else {
|
||||
config.setConfigValue(value);
|
||||
sysConfigMapper.updateById(config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package com.fruitshorts.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.fruitshorts.common.exception.ErrorCode;
|
||||
import com.fruitshorts.common.exception.ServiceException;
|
||||
import com.fruitshorts.system.dal.entity.Menu;
|
||||
import com.fruitshorts.system.dal.entity.Role;
|
||||
import com.fruitshorts.system.dal.mapper.MenuMapper;
|
||||
import com.fruitshorts.system.vo.MenuSaveReqVO;
|
||||
import com.fruitshorts.system.vo.MenuVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MenuService {
|
||||
|
||||
private final MenuMapper menuMapper;
|
||||
private final RoleService roleService;
|
||||
|
||||
/** 当前用户可见的菜单路由树。 */
|
||||
public List<MenuVO> listForUser(Long userId) {
|
||||
Role role = roleService.getRoleOfUser(userId);
|
||||
if (role == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<Menu> all = listEnabled();
|
||||
if (RoleService.SUPER_ADMIN_CODE.equals(role.getCode())) {
|
||||
return buildTree(all);
|
||||
}
|
||||
List<String> codes = roleService.listCodesByRoleId(role.getId());
|
||||
return buildTree(all.stream()
|
||||
.filter(menu -> codes.contains(menu.getPermissionCode()))
|
||||
.toList());
|
||||
}
|
||||
|
||||
/** 菜单管理页使用的完整菜单树。 */
|
||||
public List<MenuVO> listAll() {
|
||||
return buildTree(menuMapper.selectList(
|
||||
Wrappers.<Menu>lambdaQuery().orderByAsc(Menu::getParentId).orderByAsc(Menu::getSort)));
|
||||
}
|
||||
|
||||
public Long create(MenuSaveReqVO req) {
|
||||
Menu menu = new Menu();
|
||||
apply(menu, req);
|
||||
menuMapper.insert(menu);
|
||||
return menu.getId();
|
||||
}
|
||||
|
||||
public void update(Long id, MenuSaveReqVO req) {
|
||||
Menu menu = menuMapper.selectById(id);
|
||||
if (menu == null) {
|
||||
throw new ServiceException(ErrorCode.DATA_NOT_FOUND, "菜单不存在");
|
||||
}
|
||||
if (id.equals(req.getParentId())) {
|
||||
throw new ServiceException(ErrorCode.BAD_REQUEST, "上级菜单不能是自己");
|
||||
}
|
||||
apply(menu, req);
|
||||
menuMapper.updateById(menu);
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
Long children = menuMapper.selectCount(
|
||||
Wrappers.<Menu>lambdaQuery().eq(Menu::getParentId, id));
|
||||
if (children != null && children > 0) {
|
||||
throw new ServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "请先删除子菜单");
|
||||
}
|
||||
menuMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private List<Menu> listEnabled() {
|
||||
return menuMapper.selectList(Wrappers.<Menu>lambdaQuery()
|
||||
.eq(Menu::getStatus, 1)
|
||||
.eq(Menu::getVisible, 1)
|
||||
.orderByAsc(Menu::getSort));
|
||||
}
|
||||
|
||||
private void apply(Menu menu, MenuSaveReqVO req) {
|
||||
menu.setParentId(req.getParentId() == null ? 0L : req.getParentId());
|
||||
menu.setTitle(req.getTitle());
|
||||
menu.setRoutePath(nullToEmpty(req.getRoutePath()));
|
||||
menu.setRouteName(nullToEmpty(req.getRouteName()));
|
||||
menu.setComponent(nullToEmpty(req.getComponent()));
|
||||
menu.setRedirect(nullToEmpty(req.getRedirect()));
|
||||
menu.setIcon(nullToEmpty(req.getIcon()));
|
||||
menu.setPermissionCode(nullToEmpty(req.getPermissionCode()));
|
||||
menu.setType(req.getType() == null ? "MENU" : req.getType());
|
||||
menu.setSort(req.getSort() == null ? 1 : req.getSort());
|
||||
menu.setVisible(req.getVisible() == null ? 1 : req.getVisible());
|
||||
menu.setStatus(req.getStatus() == null ? 1 : req.getStatus());
|
||||
boolean catalog = "CATALOG".equals(menu.getType());
|
||||
menu.setAlwaysShow(catalog ? 1 : 0);
|
||||
menu.setAffix(menu.getAffix() == null ? 0 : menu.getAffix());
|
||||
}
|
||||
|
||||
private String nullToEmpty(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
private List<MenuVO> buildTree(List<Menu> menus) {
|
||||
Map<Long, MenuVO> nodes = new LinkedHashMap<>();
|
||||
for (Menu menu : menus) {
|
||||
MenuVO vo = new MenuVO();
|
||||
vo.setId(menu.getId());
|
||||
vo.setParentId(menu.getParentId());
|
||||
vo.setPath(menu.getRoutePath());
|
||||
vo.setName(menu.getRouteName());
|
||||
vo.setComponent(menu.getComponent());
|
||||
vo.setRedirect(menu.getRedirect());
|
||||
vo.setIcon(menu.getIcon());
|
||||
vo.setType(menu.getType());
|
||||
vo.setPermissionCode(menu.getPermissionCode());
|
||||
vo.setSort(menu.getSort());
|
||||
vo.setVisible(menu.getVisible());
|
||||
vo.setStatus(menu.getStatus());
|
||||
vo.setMeta(new MenuVO.Meta(
|
||||
menu.getTitle(),
|
||||
menu.getIcon().isEmpty() ? null : menu.getIcon(),
|
||||
menu.getAlwaysShow() != null && menu.getAlwaysShow() == 1 ? Boolean.TRUE : null,
|
||||
menu.getAffix() != null && menu.getAffix() == 1 ? Boolean.TRUE : null));
|
||||
nodes.put(menu.getId(), vo);
|
||||
}
|
||||
List<MenuVO> roots = new ArrayList<>();
|
||||
for (Menu menu : menus) {
|
||||
MenuVO node = nodes.get(menu.getId());
|
||||
MenuVO parent = menu.getParentId() == null ? null : nodes.get(menu.getParentId());
|
||||
if (parent == null) {
|
||||
roots.add(node);
|
||||
} else {
|
||||
if (parent.getChildren() == null) {
|
||||
parent.setChildren(new ArrayList<>());
|
||||
}
|
||||
parent.getChildren().add(node);
|
||||
}
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package com.fruitshorts.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fruitshorts.common.exception.ErrorCode;
|
||||
import com.fruitshorts.common.exception.ServiceException;
|
||||
import com.fruitshorts.common.pojo.PageResult;
|
||||
import com.fruitshorts.framework.log.OperationLogEntry;
|
||||
import com.fruitshorts.framework.log.OperationLogSink;
|
||||
import com.fruitshorts.system.dal.entity.OperationLog;
|
||||
import com.fruitshorts.system.dal.mapper.OperationLogMapper;
|
||||
import com.fruitshorts.system.vo.OperationLogPageReqVO;
|
||||
import com.fruitshorts.system.vo.OperationLogRespVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 操作日志:由 {@code @OperationLog} 切面自动写入,后台只读查询。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OperationLogService implements OperationLogSink {
|
||||
|
||||
private final OperationLogMapper operationLogMapper;
|
||||
|
||||
@Override
|
||||
public void save(OperationLogEntry entry) {
|
||||
OperationLog entity = new OperationLog();
|
||||
entity.setAdminUserId(entry.getUserId() == null ? 0L : entry.getUserId());
|
||||
entity.setUsername(entry.getUsername() == null ? "" : entry.getUsername());
|
||||
entity.setModule(entry.getModule() == null ? "" : entry.getModule());
|
||||
entity.setAction(entry.getAction() == null ? "" : entry.getAction());
|
||||
entity.setRequestMethod(entry.getRequestMethod() == null ? "" : entry.getRequestMethod());
|
||||
entity.setRequestUrl(entry.getRequestUrl() == null ? "" : entry.getRequestUrl());
|
||||
entity.setRequestParams(entry.getRequestParams());
|
||||
entity.setRequestBody(entry.getRequestBody());
|
||||
entity.setResponseBody(entry.getResponseBody());
|
||||
entity.setStatus(entry.getStatus() == null ? "SUCCESS" : entry.getStatus());
|
||||
entity.setErrorMsg(entry.getErrorMsg() == null ? "" : entry.getErrorMsg());
|
||||
entity.setDurationMs(entry.getDurationMs() == null ? 0 : entry.getDurationMs());
|
||||
entity.setIp(entry.getIp() == null ? "" : entry.getIp());
|
||||
operationLogMapper.insert(entity);
|
||||
}
|
||||
|
||||
public PageResult<OperationLogRespVO> page(OperationLogPageReqVO req) {
|
||||
Page<OperationLog> page = new Page<>(req.getSafePageIndex(), req.getSafePageSize());
|
||||
IPage<OperationLog> result = operationLogMapper.selectPage(page,
|
||||
Wrappers.<OperationLog>lambdaQuery()
|
||||
.like(StringUtils.hasText(req.getUsername()), OperationLog::getUsername, req.getUsername())
|
||||
.eq(StringUtils.hasText(req.getModule()), OperationLog::getModule, req.getModule())
|
||||
.like(StringUtils.hasText(req.getAction()), OperationLog::getAction, req.getAction())
|
||||
.eq(StringUtils.hasText(req.getStatus()), OperationLog::getStatus, req.getStatus())
|
||||
.ge(req.getStartTime() != null, OperationLog::getCreatedAt, req.getStartTime())
|
||||
.le(req.getEndTime() != null, OperationLog::getCreatedAt, req.getEndTime())
|
||||
.orderByDesc(OperationLog::getId));
|
||||
return new PageResult<>(result.getTotal(), toRespList(result.getRecords()));
|
||||
}
|
||||
|
||||
public OperationLogRespVO get(Long id) {
|
||||
OperationLog entity = id == null ? null : operationLogMapper.selectById(id);
|
||||
if (entity == null) {
|
||||
throw new ServiceException(ErrorCode.DATA_NOT_FOUND, "操作日志不存在");
|
||||
}
|
||||
return toRespList(List.of(entity)).get(0);
|
||||
}
|
||||
|
||||
private List<OperationLogRespVO> toRespList(List<OperationLog> logs) {
|
||||
return logs.stream().map(entity -> {
|
||||
OperationLogRespVO vo = new OperationLogRespVO();
|
||||
vo.setId(entity.getId());
|
||||
vo.setUserId(entity.getAdminUserId());
|
||||
vo.setUsername(entity.getUsername());
|
||||
vo.setModule(entity.getModule());
|
||||
vo.setAction(entity.getAction());
|
||||
vo.setRequestMethod(entity.getRequestMethod());
|
||||
vo.setRequestUrl(entity.getRequestUrl());
|
||||
vo.setRequestParams(entity.getRequestParams());
|
||||
vo.setRequestBody(entity.getRequestBody());
|
||||
vo.setResponseBody(entity.getResponseBody());
|
||||
vo.setStatus(entity.getStatus());
|
||||
vo.setStatusText("SUCCESS".equals(entity.getStatus()) ? "成功" : "失败");
|
||||
vo.setErrorMsg(entity.getErrorMsg());
|
||||
vo.setDurationMs(entity.getDurationMs());
|
||||
vo.setIp(entity.getIp());
|
||||
vo.setCreatedAt(entity.getCreatedAt());
|
||||
return vo;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.fruitshorts.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.fruitshorts.common.exception.ErrorCode;
|
||||
import com.fruitshorts.common.exception.ServiceException;
|
||||
import com.fruitshorts.system.dal.entity.Permission;
|
||||
import com.fruitshorts.system.dal.mapper.PermissionMapper;
|
||||
import com.fruitshorts.system.vo.PermissionGroupVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 权限点主数据。
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionService {
|
||||
|
||||
private final PermissionMapper permissionMapper;
|
||||
|
||||
public List<Permission> listAll() {
|
||||
return permissionMapper.selectList(
|
||||
Wrappers.<Permission>lambdaQuery().orderByAsc(Permission::getSort));
|
||||
}
|
||||
|
||||
public List<PermissionGroupVO> listGroups() {
|
||||
Map<String, List<PermissionGroupVO.Item>> grouped = new LinkedHashMap<>();
|
||||
for (Permission permission : listAll()) {
|
||||
grouped.computeIfAbsent(permission.getGroupName(), key -> new ArrayList<>())
|
||||
.add(new PermissionGroupVO.Item(permission.getCode(), permission.getName()));
|
||||
}
|
||||
return grouped.entrySet().stream()
|
||||
.map(entry -> new PermissionGroupVO(entry.getKey(), entry.getValue()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<Long> listIdsByCodes(List<String> codes) {
|
||||
if (codes == null || codes.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<Permission> permissions = permissionMapper.selectList(
|
||||
Wrappers.<Permission>lambdaQuery().in(Permission::getCode, codes));
|
||||
if (permissions.size() != codes.stream().distinct().count()) {
|
||||
throw new ServiceException(ErrorCode.BAD_REQUEST, "存在无效的权限码");
|
||||
}
|
||||
return permissions.stream().map(Permission::getId).toList();
|
||||
}
|
||||
|
||||
public List<String> listCodesByIds(List<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return permissionMapper.selectList(
|
||||
Wrappers.<Permission>lambdaQuery().in(Permission::getId, ids))
|
||||
.stream().map(Permission::getCode).toList();
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package com.fruitshorts.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.fruitshorts.common.exception.ErrorCode;
|
||||
import com.fruitshorts.common.exception.ServiceException;
|
||||
import com.fruitshorts.system.dal.entity.Role;
|
||||
import com.fruitshorts.system.dal.entity.RoleCategory;
|
||||
import com.fruitshorts.system.dal.mapper.RoleCategoryMapper;
|
||||
import com.fruitshorts.system.dal.mapper.RoleMapper;
|
||||
import com.fruitshorts.system.vo.RoleCategoryRespVO;
|
||||
import com.fruitshorts.system.vo.RoleCategorySaveReqVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoleCategoryService {
|
||||
|
||||
private final RoleCategoryMapper roleCategoryMapper;
|
||||
private final RoleMapper roleMapper;
|
||||
|
||||
public List<RoleCategoryRespVO> list() {
|
||||
Map<Long, Integer> counts = new HashMap<>();
|
||||
roleMapper.selectList(null).forEach(role ->
|
||||
counts.merge(role.getCategoryId(), 1, Integer::sum));
|
||||
return roleCategoryMapper.selectList(
|
||||
Wrappers.<RoleCategory>lambdaQuery().orderByAsc(RoleCategory::getSort))
|
||||
.stream().map(category -> {
|
||||
RoleCategoryRespVO vo = new RoleCategoryRespVO();
|
||||
vo.setId(category.getId());
|
||||
vo.setName(category.getName());
|
||||
vo.setDescription(category.getDescription());
|
||||
vo.setRoleCount(counts.getOrDefault(category.getId(), 0));
|
||||
return vo;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
public Long create(RoleCategorySaveReqVO req) {
|
||||
ensureNameAvailable(req.getName(), null);
|
||||
RoleCategory category = new RoleCategory();
|
||||
category.setName(req.getName());
|
||||
category.setDescription(req.getDescription() == null ? "" : req.getDescription());
|
||||
Long count = roleCategoryMapper.selectCount(null);
|
||||
category.setSort((count == null ? 0 : count.intValue()) + 1);
|
||||
roleCategoryMapper.insert(category);
|
||||
return category.getId();
|
||||
}
|
||||
|
||||
public void update(Long id, RoleCategorySaveReqVO req) {
|
||||
RoleCategory category = getExisting(id);
|
||||
ensureNameAvailable(req.getName(), id);
|
||||
category.setName(req.getName());
|
||||
if (req.getDescription() != null) {
|
||||
category.setDescription(req.getDescription());
|
||||
}
|
||||
roleCategoryMapper.updateById(category);
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
RoleCategory category = getExisting(id);
|
||||
Long used = roleMapper.selectCount(
|
||||
Wrappers.<Role>lambdaQuery().eq(Role::getCategoryId, id));
|
||||
if (used != null && used > 0) {
|
||||
throw new ServiceException(ErrorCode.OPERATION_NOT_ALLOWED,
|
||||
"分类「" + category.getName() + "」下仍有角色,无法删除");
|
||||
}
|
||||
roleCategoryMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private RoleCategory getExisting(Long id) {
|
||||
RoleCategory category = id == null ? null : roleCategoryMapper.selectById(id);
|
||||
if (category == null) {
|
||||
throw new ServiceException(ErrorCode.DATA_NOT_FOUND, "角色分类不存在");
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
private void ensureNameAvailable(String name, Long excludeId) {
|
||||
Long count = roleCategoryMapper.selectCount(Wrappers.<RoleCategory>lambdaQuery()
|
||||
.eq(RoleCategory::getName, name)
|
||||
.ne(excludeId != null, RoleCategory::getId, excludeId));
|
||||
if (count != null && count > 0) {
|
||||
throw new ServiceException(ErrorCode.DATA_EXISTS, "分类名称已存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
package com.fruitshorts.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fruitshorts.common.exception.ErrorCode;
|
||||
import com.fruitshorts.common.exception.ServiceException;
|
||||
import com.fruitshorts.common.pojo.PageResult;
|
||||
import com.fruitshorts.system.dal.entity.AdminUserRole;
|
||||
import com.fruitshorts.system.dal.entity.Menu;
|
||||
import com.fruitshorts.system.dal.entity.Role;
|
||||
import com.fruitshorts.system.dal.entity.RoleCategory;
|
||||
import com.fruitshorts.system.dal.entity.RoleMenu;
|
||||
import com.fruitshorts.system.dal.entity.RolePermission;
|
||||
import com.fruitshorts.system.dal.mapper.AdminUserRoleMapper;
|
||||
import com.fruitshorts.system.dal.mapper.MenuMapper;
|
||||
import com.fruitshorts.system.dal.mapper.RoleCategoryMapper;
|
||||
import com.fruitshorts.system.dal.mapper.RoleMapper;
|
||||
import com.fruitshorts.system.dal.mapper.RoleMenuMapper;
|
||||
import com.fruitshorts.system.dal.mapper.RolePermissionMapper;
|
||||
import com.fruitshorts.system.vo.RoleRespVO;
|
||||
import com.fruitshorts.system.vo.RoleSaveReqVO;
|
||||
import com.fruitshorts.framework.security.PermissionProvider;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoleService implements PermissionProvider {
|
||||
|
||||
public static final String SUPER_ADMIN_CODE = "super_admin";
|
||||
|
||||
private final RoleMapper roleMapper;
|
||||
private final RoleCategoryMapper roleCategoryMapper;
|
||||
private final RolePermissionMapper rolePermissionMapper;
|
||||
private final RoleMenuMapper roleMenuMapper;
|
||||
private final AdminUserRoleMapper adminUserRoleMapper;
|
||||
private final MenuMapper menuMapper;
|
||||
private final PermissionService permissionService;
|
||||
|
||||
public PageResult<RoleRespVO> page(String keyword, Integer pageIndex, Integer pageSize) {
|
||||
Page<Role> page = new Page<>(pageIndex == null || pageIndex < 1 ? 1 : pageIndex,
|
||||
pageSize == null || pageSize < 1 ? 10 : pageSize);
|
||||
IPage<Role> result = roleMapper.selectPage(page,
|
||||
Wrappers.<Role>lambdaQuery()
|
||||
.like(StringUtils.hasText(keyword), Role::getName, keyword)
|
||||
.orderByAsc(Role::getLevel));
|
||||
return new PageResult<>(result.getTotal(), toRespList(result.getRecords()));
|
||||
}
|
||||
|
||||
public List<RoleRespVO> listAll() {
|
||||
List<Role> roles = roleMapper.selectList(
|
||||
Wrappers.<Role>lambdaQuery().orderByAsc(Role::getLevel));
|
||||
return toRespList(roles);
|
||||
}
|
||||
|
||||
public RoleRespVO get(Long id) {
|
||||
Role role = getExisting(id);
|
||||
return toRespList(List.of(role)).get(0);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long create(RoleSaveReqVO req) {
|
||||
ensureNameAvailable(req.getName(), null);
|
||||
Role role = new Role();
|
||||
role.setName(req.getName());
|
||||
role.setCode(StringUtils.hasText(req.getCode()) ? req.getCode() : req.getName());
|
||||
role.setCategoryId(req.getCategoryId() == null ? 0L : req.getCategoryId());
|
||||
role.setLevel(req.getLevel() == null ? 9 : req.getLevel());
|
||||
role.setDescription(req.getDescription() == null ? "" : req.getDescription());
|
||||
role.setStatus(req.getStatus() == null ? 1 : req.getStatus());
|
||||
role.setIsBuiltin(0);
|
||||
roleMapper.insert(role);
|
||||
if (req.getPermissions() != null) {
|
||||
replacePermissions(role.getId(), req.getPermissions());
|
||||
}
|
||||
return role.getId();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(Long id, RoleSaveReqVO req) {
|
||||
Role role = getExisting(id);
|
||||
ensureNameAvailable(req.getName(), id);
|
||||
role.setName(req.getName());
|
||||
if (StringUtils.hasText(req.getCode()) && !SUPER_ADMIN_CODE.equals(role.getCode())) {
|
||||
role.setCode(req.getCode());
|
||||
}
|
||||
if (req.getCategoryId() != null) {
|
||||
role.setCategoryId(req.getCategoryId());
|
||||
}
|
||||
if (req.getLevel() != null) {
|
||||
role.setLevel(req.getLevel());
|
||||
}
|
||||
if (req.getDescription() != null) {
|
||||
role.setDescription(req.getDescription());
|
||||
}
|
||||
if (req.getStatus() != null && role.getIsBuiltin() != 1) {
|
||||
role.setStatus(req.getStatus());
|
||||
}
|
||||
roleMapper.updateById(role);
|
||||
if (req.getPermissions() != null) {
|
||||
replacePermissions(id, req.getPermissions());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delete(Long id) {
|
||||
Role role = getExisting(id);
|
||||
if (role.getIsBuiltin() == 1) {
|
||||
throw new ServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "内置角色不可删除");
|
||||
}
|
||||
roleMapper.deleteById(id);
|
||||
rolePermissionMapper.delete(Wrappers.<RolePermission>lambdaQuery().eq(RolePermission::getRoleId, id));
|
||||
roleMenuMapper.delete(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId, id));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updatePermissions(Long roleId, List<String> codes) {
|
||||
getExisting(roleId);
|
||||
replacePermissions(roleId, codes);
|
||||
}
|
||||
|
||||
public List<String> listCodesByRoleId(Long roleId) {
|
||||
List<RolePermission> links = rolePermissionMapper.selectList(
|
||||
Wrappers.<RolePermission>lambdaQuery().eq(RolePermission::getRoleId, roleId));
|
||||
return permissionService.listCodesByIds(links.stream().map(RolePermission::getPermissionId).toList());
|
||||
}
|
||||
|
||||
public List<String> listCodesByUserId(Long userId) {
|
||||
List<Long> roleIds = listRoleIdsByUserId(userId);
|
||||
if (roleIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<RolePermission> links = rolePermissionMapper.selectList(
|
||||
Wrappers.<RolePermission>lambdaQuery().in(RolePermission::getRoleId, roleIds));
|
||||
return permissionService.listCodesByIds(
|
||||
links.stream().map(RolePermission::getPermissionId).distinct().toList());
|
||||
}
|
||||
|
||||
/** 供框架层实时判断权限使用:角色的权限改了立即生效,不需要重新登录。 */
|
||||
@Override
|
||||
public List<String> listPermissionsByUserId(Long userId) {
|
||||
return listCodesByUserId(userId);
|
||||
}
|
||||
|
||||
public List<Long> listRoleIdsByUserId(Long userId) {
|
||||
return adminUserRoleMapper.selectList(
|
||||
Wrappers.<AdminUserRole>lambdaQuery().eq(AdminUserRole::getUserId, userId))
|
||||
.stream().map(AdminUserRole::getRoleId).toList();
|
||||
}
|
||||
|
||||
public Role getRoleOfUser(Long userId) {
|
||||
List<Long> roleIds = listRoleIdsByUserId(userId);
|
||||
if (roleIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return roleMapper.selectById(roleIds.get(0));
|
||||
}
|
||||
|
||||
public Role getExisting(Long id) {
|
||||
Role role = id == null ? null : roleMapper.selectById(id);
|
||||
if (role == null) {
|
||||
throw new ServiceException(ErrorCode.DATA_NOT_FOUND, "角色不存在");
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
/** 权限变化时同步角色可访问的菜单,菜单树由权限码派生。 */
|
||||
private void replacePermissions(Long roleId, List<String> codes) {
|
||||
List<Long> permissionIds = permissionService.listIdsByCodes(codes);
|
||||
rolePermissionMapper.delete(Wrappers.<RolePermission>lambdaQuery().eq(RolePermission::getRoleId, roleId));
|
||||
permissionIds.forEach(permissionId -> rolePermissionMapper.insert(new RolePermission(roleId, permissionId)));
|
||||
|
||||
List<Long> menuIds = codes == null || codes.isEmpty() ? List.of()
|
||||
: menuMapper.selectList(Wrappers.<Menu>lambdaQuery().in(Menu::getPermissionCode, codes))
|
||||
.stream().map(Menu::getId).toList();
|
||||
roleMenuMapper.delete(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId, roleId));
|
||||
menuIds.forEach(menuId -> roleMenuMapper.insert(new RoleMenu(roleId, menuId)));
|
||||
}
|
||||
|
||||
private void ensureNameAvailable(String name, Long excludeId) {
|
||||
Long count = roleMapper.selectCount(Wrappers.<Role>lambdaQuery()
|
||||
.eq(Role::getName, name)
|
||||
.ne(excludeId != null, Role::getId, excludeId));
|
||||
if (count != null && count > 0) {
|
||||
throw new ServiceException(ErrorCode.DATA_EXISTS, "角色名称已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private List<RoleRespVO> toRespList(List<Role> roles) {
|
||||
if (roles.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
Map<Long, String> categoryNames = roleCategoryMapper.selectList(null).stream()
|
||||
.collect(Collectors.toMap(RoleCategory::getId, RoleCategory::getName, (a, b) -> a));
|
||||
Map<Long, Integer> memberCounts = new HashMap<>();
|
||||
adminUserRoleMapper.selectList(null)
|
||||
.forEach(link -> memberCounts.merge(link.getRoleId(), 1, Integer::sum));
|
||||
|
||||
Map<Long, List<String>> permissionCache = new HashMap<>();
|
||||
return roles.stream().map(role -> {
|
||||
RoleRespVO vo = new RoleRespVO();
|
||||
vo.setId(role.getId());
|
||||
vo.setName(role.getName());
|
||||
vo.setCode(role.getCode());
|
||||
vo.setDescription(role.getDescription());
|
||||
vo.setCategoryId(role.getCategoryId());
|
||||
vo.setCategory(categoryNames.getOrDefault(role.getCategoryId(), ""));
|
||||
vo.setLevel(role.getLevel());
|
||||
vo.setStatus(role.getStatus());
|
||||
vo.setStatusText(role.getStatus() != null && role.getStatus() == 1 ? "启用" : "停用");
|
||||
vo.setBuiltin(role.getIsBuiltin() != null && role.getIsBuiltin() == 1);
|
||||
vo.setMemberCount(memberCounts.getOrDefault(role.getId(), 0));
|
||||
vo.setPermissions(permissionCache.computeIfAbsent(role.getId(), this::listCodesByRoleId));
|
||||
return vo;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
public Map<Long, String> roleNameMap() {
|
||||
return roleMapper.selectList(null).stream()
|
||||
.collect(Collectors.toMap(Role::getId, Role::getName, (a, b) -> a));
|
||||
}
|
||||
|
||||
public Function<Long, String> roleNameResolver() {
|
||||
Map<Long, String> names = roleNameMap();
|
||||
return id -> names.getOrDefault(id, "");
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import com.fruitshorts.common.pojo.PageParam;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AdminUserPageReqVO extends PageParam {
|
||||
|
||||
private String username;
|
||||
private Long roleId;
|
||||
private Integer status;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class AdminUserRespVO {
|
||||
|
||||
private Long id;
|
||||
private String username;
|
||||
private String name;
|
||||
private Long roleId;
|
||||
private String role;
|
||||
private String ipWhitelist;
|
||||
private Integer status;
|
||||
private String statusText;
|
||||
private Integer twoFactor;
|
||||
private String twoFactorText;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime lastLogin;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AdminUserSaveReqVO {
|
||||
|
||||
/** 新增时必填,编辑时忽略 */
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
@NotBlank(message = "请输入姓名")
|
||||
private String name;
|
||||
|
||||
@NotNull(message = "请选择角色")
|
||||
private Long roleId;
|
||||
|
||||
private String ipWhitelist;
|
||||
private Integer status;
|
||||
private Integer twoFactor;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ConfigItemVO {
|
||||
|
||||
private String key;
|
||||
private String value;
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LoginReqVO {
|
||||
|
||||
@NotBlank(message = "请输入账号")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "请输入密码")
|
||||
private String password;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class LoginRespVO {
|
||||
|
||||
private String token;
|
||||
private Long userId;
|
||||
private String username;
|
||||
private String name;
|
||||
private String avatar;
|
||||
/** 角色编码,如 super_admin */
|
||||
private String role;
|
||||
private String roleName;
|
||||
private Long roleId;
|
||||
private List<String> permissions;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MenuSaveReqVO {
|
||||
|
||||
private Long parentId;
|
||||
|
||||
@NotBlank(message = "请输入菜单名称")
|
||||
private String title;
|
||||
|
||||
private String routePath;
|
||||
private String routeName;
|
||||
private String component;
|
||||
private String redirect;
|
||||
private String icon;
|
||||
private String permissionCode;
|
||||
private String type;
|
||||
private Integer sort;
|
||||
private Integer visible;
|
||||
private Integer status;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单路由树,结构必须与前端 element-plus-admin 的动态路由一致。
|
||||
*/
|
||||
@Data
|
||||
public class MenuVO {
|
||||
|
||||
private Long id;
|
||||
private Long parentId;
|
||||
private String path;
|
||||
private String name;
|
||||
private String component;
|
||||
private String redirect;
|
||||
private String icon;
|
||||
/** CATALOG / MENU / BUTTON */
|
||||
private String type;
|
||||
private String permissionCode;
|
||||
private Integer sort;
|
||||
private Integer visible;
|
||||
private Integer status;
|
||||
private Meta meta;
|
||||
private List<MenuVO> children;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public static class Meta {
|
||||
private String title;
|
||||
private String icon;
|
||||
private Boolean alwaysShow;
|
||||
private Boolean affix;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import com.fruitshorts.common.pojo.PageParam;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OperationLogPageReqVO extends PageParam {
|
||||
|
||||
/** 操作人账号,模糊匹配 */
|
||||
private String username;
|
||||
/** 模块,精确匹配 */
|
||||
private String module;
|
||||
/** 动作,模糊匹配 */
|
||||
private String action;
|
||||
/** SUCCESS / FAIL */
|
||||
private String status;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime endTime;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class OperationLogRespVO {
|
||||
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private String username;
|
||||
private String module;
|
||||
private String action;
|
||||
private String requestMethod;
|
||||
private String requestUrl;
|
||||
private String requestParams;
|
||||
private String requestBody;
|
||||
private String responseBody;
|
||||
private String status;
|
||||
private String statusText;
|
||||
private String errorMsg;
|
||||
private Integer durationMs;
|
||||
private String ip;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PasswordChangeReqVO {
|
||||
|
||||
@NotBlank(message = "请输入原密码")
|
||||
private String oldPassword;
|
||||
|
||||
@NotBlank(message = "请输入新密码")
|
||||
@Size(min = 6, max = 32, message = "新密码长度需在 6-32 位之间")
|
||||
private String newPassword;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class PermissionGroupVO {
|
||||
|
||||
private String name;
|
||||
private List<Item> items;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class Item {
|
||||
private String code;
|
||||
private String name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 当前登录人信息,字段与登录返回保持一致,便于前端直接覆盖本地缓存的用户信息。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class ProfileRespVO {
|
||||
|
||||
private Long userId;
|
||||
private String username;
|
||||
private String name;
|
||||
private String avatar;
|
||||
/** 角色编码,如 super_admin */
|
||||
private String role;
|
||||
private String roleName;
|
||||
private Long roleId;
|
||||
private List<String> permissions;
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RoleCategoryRespVO {
|
||||
|
||||
private Long id;
|
||||
private String name;
|
||||
private String description;
|
||||
private Integer roleCount;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RoleCategorySaveReqVO {
|
||||
|
||||
@NotBlank(message = "请输入分类名称")
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RolePermissionSaveReqVO {
|
||||
|
||||
@NotNull(message = "权限不能为空")
|
||||
private List<String> permissions;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RoleRespVO {
|
||||
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private String description;
|
||||
private Long categoryId;
|
||||
private String category;
|
||||
private Integer level;
|
||||
private Integer status;
|
||||
private String statusText;
|
||||
private Boolean builtin;
|
||||
private Integer memberCount;
|
||||
private List<String> permissions;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.fruitshorts.system.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RoleSaveReqVO {
|
||||
|
||||
@NotBlank(message = "请输入角色名称")
|
||||
private String name;
|
||||
|
||||
/** 不传时按名称生成 */
|
||||
private String code;
|
||||
|
||||
private String description;
|
||||
private Long categoryId;
|
||||
private Integer level;
|
||||
private Integer status;
|
||||
|
||||
/** 角色权限码,传则整体覆盖 */
|
||||
private List<String> permissions;
|
||||
}
|
||||
Reference in new issue
Block a user