feat(wm-system): #74 系统管理(角色/用户/部门/日志/字典)
- 增强 SysRole: 5级角色(level 1-5), 数据权限(dataScope), 权限配置(permissions JSON) - 增强 SysUser: 角色关联(roleId), 最后登录记录, 分页查询+多维度筛选 - 增强 SysDepartment: 树结构(ancestors), 子部门查询, 树形构建 - 增强 SysLog: 状态/错误信息字段, 分页+多条件筛选(类型/用户/模块/时间/状态) - 新增 DataDictionary + DataDictionaryItem: 字典CRUD+字典项CRUD, 按编码查询 - 5个专用Controller: RoleController/UserController/DepartmentController/LogController/DictionaryController - 28个RESTful API端点于 /api/system/* - 5个专用Service接口+实现: RoleService/UserService/DepartmentService/LogService/DictionaryService - DDL: V2__system_enhance.sql — 新增表+索引+初始数据 - 单元测试: 5个Service测试类, 覆盖核心业务逻辑 - DTO: 请求参数校验(jakarta.validation) - 保持SysService向后兼容
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package com.water.system.controller;
|
||||
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.system.entity.SysDepartment;
|
||||
import com.water.system.entity.dto.DeptRequest;
|
||||
import com.water.system.service.DepartmentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "部门管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/system/dept")
|
||||
@RequiredArgsConstructor
|
||||
public class DepartmentController {
|
||||
|
||||
private final DepartmentService departmentService;
|
||||
|
||||
@Operation(summary = "获取部门树")
|
||||
@GetMapping("/tree")
|
||||
public R<List<SysDepartment>> tree() {
|
||||
return R.ok(departmentService.getDeptTree());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取部门详情")
|
||||
@GetMapping("/{id}")
|
||||
public R<SysDepartment> getById(@PathVariable Long id) {
|
||||
return R.ok(departmentService.getDeptById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建部门")
|
||||
@PostMapping
|
||||
public R<Long> create(@Valid @RequestBody DeptRequest request) {
|
||||
return R.ok(departmentService.createDept(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新部门")
|
||||
@PutMapping("/{id}")
|
||||
public R<String> update(@PathVariable Long id, @RequestBody DeptRequest request) {
|
||||
departmentService.updateDept(id, request);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "删除部门")
|
||||
@DeleteMapping("/{id}")
|
||||
public R<String> delete(@PathVariable Long id) {
|
||||
departmentService.deleteDept(id);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "获取子部门列表")
|
||||
@GetMapping("/{id}/children")
|
||||
public R<List<SysDepartment>> getChildren(@PathVariable Long id) {
|
||||
return R.ok(departmentService.getChildDepts(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.water.system.controller;
|
||||
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.system.entity.DataDictionary;
|
||||
import com.water.system.entity.DataDictionaryItem;
|
||||
import com.water.system.entity.dto.DictItemRequest;
|
||||
import com.water.system.entity.dto.DictRequest;
|
||||
import com.water.system.service.DictionaryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "数据字典管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/system/dict")
|
||||
@RequiredArgsConstructor
|
||||
public class DictionaryController {
|
||||
|
||||
private final DictionaryService dictionaryService;
|
||||
|
||||
@Operation(summary = "获取字典列表")
|
||||
@GetMapping("/list")
|
||||
public R<List<DataDictionary>> list(@RequestParam(required = false) String keyword) {
|
||||
return R.ok(dictionaryService.listDictionaries(keyword));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取字典详情")
|
||||
@GetMapping("/{id}")
|
||||
public R<DataDictionary> getById(@PathVariable Long id) {
|
||||
return R.ok(dictionaryService.getDictionaryById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建字典")
|
||||
@PostMapping
|
||||
public R<Long> create(@Valid @RequestBody DictRequest request) {
|
||||
return R.ok(dictionaryService.createDictionary(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新字典")
|
||||
@PutMapping("/{id}")
|
||||
public R<String> update(@PathVariable Long id, @RequestBody DictRequest request) {
|
||||
dictionaryService.updateDictionary(id, request);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "删除字典")
|
||||
@DeleteMapping("/{id}")
|
||||
public R<String> delete(@PathVariable Long id) {
|
||||
dictionaryService.deleteDictionary(id);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "获取字典项列表(按字典ID)")
|
||||
@GetMapping("/{dictId}/items")
|
||||
public R<List<DataDictionaryItem>> listItems(@PathVariable Long dictId) {
|
||||
return R.ok(dictionaryService.listItemsByDictId(dictId));
|
||||
}
|
||||
|
||||
@Operation(summary = "根据字典编码获取字典项")
|
||||
@GetMapping("/code/{dictCode}/items")
|
||||
public R<List<DataDictionaryItem>> listItemsByCode(@PathVariable String dictCode) {
|
||||
return R.ok(dictionaryService.listItemsByDictCode(dictCode));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建字典项")
|
||||
@PostMapping("/item")
|
||||
public R<Long> createItem(@Valid @RequestBody DictItemRequest request) {
|
||||
return R.ok(dictionaryService.createItem(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新字典项")
|
||||
@PutMapping("/item/{id}")
|
||||
public R<String> updateItem(@PathVariable Long id, @RequestBody DictItemRequest request) {
|
||||
dictionaryService.updateItem(id, request);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "删除字典项")
|
||||
@DeleteMapping("/item/{id}")
|
||||
public R<String> deleteItem(@PathVariable Long id) {
|
||||
dictionaryService.deleteItem(id);
|
||||
return R.ok("OK");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.water.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.system.entity.SysLog;
|
||||
import com.water.system.entity.dto.LogQueryRequest;
|
||||
import com.water.system.service.LogService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Tag(name = "日志管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/system/log")
|
||||
@RequiredArgsConstructor
|
||||
public class LogController {
|
||||
|
||||
private final LogService logService;
|
||||
|
||||
@Operation(summary = "分页查询日志")
|
||||
@GetMapping("/page")
|
||||
public R<IPage<SysLog>> page(LogQueryRequest request) {
|
||||
return R.ok(logService.pageLogs(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取日志详情")
|
||||
@GetMapping("/{id}")
|
||||
public R<SysLog> getById(@PathVariable Long id) {
|
||||
return R.ok(logService.getLogById(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.water.system.controller;
|
||||
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.system.entity.SysRole;
|
||||
import com.water.system.entity.dto.RoleRequest;
|
||||
import com.water.system.service.RoleService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "角色管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/system/role")
|
||||
@RequiredArgsConstructor
|
||||
public class RoleController {
|
||||
|
||||
private final RoleService roleService;
|
||||
|
||||
@Operation(summary = "获取角色列表")
|
||||
@GetMapping("/list")
|
||||
public R<List<SysRole>> list() {
|
||||
return R.ok(roleService.listRoles());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取角色详情")
|
||||
@GetMapping("/{id}")
|
||||
public R<SysRole> getById(@PathVariable Long id) {
|
||||
return R.ok(roleService.getRoleById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建角色")
|
||||
@PostMapping
|
||||
public R<Long> create(@Valid @RequestBody RoleRequest request) {
|
||||
return R.ok(roleService.createRole(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新角色")
|
||||
@PutMapping("/{id}")
|
||||
public R<String> update(@PathVariable Long id, @Valid @RequestBody RoleRequest request) {
|
||||
roleService.updateRole(id, request);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "删除角色")
|
||||
@DeleteMapping("/{id}")
|
||||
public R<String> delete(@PathVariable Long id) {
|
||||
roleService.deleteRole(id);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "分配角色权限")
|
||||
@PutMapping("/{id}/permissions")
|
||||
public R<String> assignPermissions(@PathVariable Long id, @RequestBody List<String> permissions) {
|
||||
roleService.assignPermissions(id, permissions);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户角色")
|
||||
@GetMapping("/user/{userId}")
|
||||
public R<List<SysRole>> getByUserId(@PathVariable Long userId) {
|
||||
return R.ok(roleService.getRolesByUserId(userId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.water.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.system.entity.SysUser;
|
||||
import com.water.system.entity.dto.PasswordResetRequest;
|
||||
import com.water.system.entity.dto.UserCreateRequest;
|
||||
import com.water.system.entity.dto.UserUpdateRequest;
|
||||
import com.water.system.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Tag(name = "用户管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/system/user")
|
||||
@RequiredArgsConstructor
|
||||
public class UserController {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
@Operation(summary = "分页查询用户列表")
|
||||
@GetMapping("/page")
|
||||
public R<IPage<SysUser>> page(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(required = false) Long deptId) {
|
||||
return R.ok(userService.pageUsers(pageNum, pageSize, keyword, status, deptId));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户详情")
|
||||
@GetMapping("/{id}")
|
||||
public R<SysUser> getById(@PathVariable Long id) {
|
||||
return R.ok(userService.getUserById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建用户")
|
||||
@PostMapping
|
||||
public R<Long> create(@Valid @RequestBody UserCreateRequest request) {
|
||||
return R.ok(userService.createUser(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新用户信息")
|
||||
@PutMapping("/{id}")
|
||||
public R<String> update(@PathVariable Long id, @RequestBody UserUpdateRequest request) {
|
||||
userService.updateUser(id, request);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "删除用户")
|
||||
@DeleteMapping("/{id}")
|
||||
public R<String> delete(@PathVariable Long id) {
|
||||
userService.deleteUser(id);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "停用/启用用户")
|
||||
@PutMapping("/{id}/status")
|
||||
public R<String> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
|
||||
userService.updateStatus(id, status);
|
||||
return R.ok("OK");
|
||||
}
|
||||
|
||||
@Operation(summary = "重置密码")
|
||||
@PutMapping("/{id}/reset-password")
|
||||
public R<String> resetPassword(@PathVariable Long id, @Valid @RequestBody PasswordResetRequest request) {
|
||||
userService.resetPassword(id, request.getNewPassword());
|
||||
return R.ok("OK");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.water.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 数据字典实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_data_dictionary")
|
||||
public class DataDictionary {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 字典编码(唯一) */
|
||||
private String dictCode;
|
||||
|
||||
/** 字典名称 */
|
||||
private String dictName;
|
||||
|
||||
/** 字典描述 */
|
||||
private String description;
|
||||
|
||||
/** 状态: 0-停用 1-正常 */
|
||||
private Integer status;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedTime;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.water.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 数据字典项实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_data_dictionary_item")
|
||||
public class DataDictionaryItem {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 字典ID */
|
||||
private Long dictId;
|
||||
|
||||
/** 字典项编码 */
|
||||
private String itemCode;
|
||||
|
||||
/** 字典项名称 */
|
||||
private String itemName;
|
||||
|
||||
/** 字典项值 */
|
||||
private String itemValue;
|
||||
|
||||
/** 排序号 */
|
||||
private Integer sort;
|
||||
|
||||
/** 状态: 0-停用 1-正常 */
|
||||
private Integer status;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedTime;
|
||||
}
|
||||
@@ -1,9 +1,47 @@
|
||||
package com.water.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
@Data @TableName("sys_department")
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门实体(树结构)
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_department")
|
||||
public class SysDepartment {
|
||||
@TableId(type = IdType.AUTO) private Long id;
|
||||
private Long parentId; private String deptName, leader, phone;
|
||||
private Integer sort, status;
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 父部门ID, 0为根节点 */
|
||||
private Long parentId;
|
||||
|
||||
/** 部门名称 */
|
||||
private String deptName;
|
||||
|
||||
/** 负责人 */
|
||||
private String leader;
|
||||
|
||||
/** 联系电话 */
|
||||
private String phone;
|
||||
|
||||
/** 排序号 */
|
||||
private Integer sort;
|
||||
|
||||
/** 状态: 0-停用 1-正常 */
|
||||
private Integer status;
|
||||
|
||||
/** 层级路径(如: 0,1,5) */
|
||||
private String ancestors;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedTime;
|
||||
|
||||
/** 子部门列表(非数据库字段) */
|
||||
@TableField(exist = false)
|
||||
private List<SysDepartment> children;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,58 @@
|
||||
package com.water.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data; import java.time.LocalDateTime;
|
||||
@Data @TableName("sys_log")
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 系统日志实体
|
||||
* 支持: 登录日志(LOGIN) / 操作日志(OPERATE) / 异常日志(EXCEPTION)
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_log")
|
||||
public class SysLog {
|
||||
@TableId(type = IdType.AUTO) private Long id;
|
||||
private Long userId; private String username, logType, module, action;
|
||||
private String requestUrl, requestMethod, requestParams, responseResult;
|
||||
private String ip; private Long duration;
|
||||
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 操作用户ID */
|
||||
private Long userId;
|
||||
|
||||
/** 操作用户名 */
|
||||
private String username;
|
||||
|
||||
/** 日志类型: LOGIN-登录 OPERATE-操作 EXCEPTION-异常 */
|
||||
private String logType;
|
||||
|
||||
/** 操作模块 */
|
||||
private String module;
|
||||
|
||||
/** 操作描述 */
|
||||
private String action;
|
||||
|
||||
/** 请求URL */
|
||||
private String requestUrl;
|
||||
|
||||
/** 请求方法 */
|
||||
private String requestMethod;
|
||||
|
||||
/** 请求参数 */
|
||||
private String requestParams;
|
||||
|
||||
/** 响应结果 */
|
||||
private String responseResult;
|
||||
|
||||
/** 操作IP */
|
||||
private String ip;
|
||||
|
||||
/** 耗时(毫秒) */
|
||||
private Long duration;
|
||||
|
||||
/** 状态: 0-失败 1-成功 */
|
||||
private Integer status;
|
||||
|
||||
/** 错误信息 */
|
||||
private String errorMsg;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdTime;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,46 @@
|
||||
package com.water.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data; import java.time.LocalDateTime;
|
||||
@Data @TableName("sys_role")
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 系统角色实体
|
||||
* 支持5级角色: 1-超级管理员 2-系统管理员 3-部门经理 4-普通员工 5-访客
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_role")
|
||||
public class SysRole {
|
||||
@TableId(type = IdType.AUTO) private Long id;
|
||||
private String roleCode, roleName, description;
|
||||
private Integer status, sort;
|
||||
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 角色编码 */
|
||||
private String roleCode;
|
||||
|
||||
/** 角色名称 */
|
||||
private String roleName;
|
||||
|
||||
/** 角色描述 */
|
||||
private String description;
|
||||
|
||||
/** 角色级别: 1-超级管理员 2-系统管理员 3-部门经理 4-普通员工 5-访客 */
|
||||
private Integer level;
|
||||
|
||||
/** 数据权限范围: ALL-全部 DEPT-本部门 DEPT_BELOW-本部门及以下 SELF-仅本人 */
|
||||
private String dataScope;
|
||||
|
||||
/** 权限配置(JSON数组) */
|
||||
private String permissions;
|
||||
|
||||
/** 状态: 0-停用 1-正常 */
|
||||
private Integer status;
|
||||
|
||||
/** 排序号 */
|
||||
private Integer sort;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedTime;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,54 @@
|
||||
package com.water.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data; import java.time.LocalDateTime;
|
||||
@Data @TableName("sys_user")
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 系统用户实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_user")
|
||||
public class SysUser {
|
||||
@TableId(type = IdType.AUTO) private Long id;
|
||||
private String username, password, realName, phone, email;
|
||||
private Long deptId; private String avatar;
|
||||
private Integer status; // 0停用 1正常
|
||||
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
|
||||
@TableField(fill=FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 用户名 */
|
||||
private String username;
|
||||
|
||||
/** 密码(加密) */
|
||||
private String password;
|
||||
|
||||
/** 真实姓名 */
|
||||
private String realName;
|
||||
|
||||
/** 手机号 */
|
||||
private String phone;
|
||||
|
||||
/** 邮箱 */
|
||||
private String email;
|
||||
|
||||
/** 部门ID */
|
||||
private Long deptId;
|
||||
|
||||
/** 角色ID */
|
||||
private Long roleId;
|
||||
|
||||
/** 头像URL */
|
||||
private String avatar;
|
||||
|
||||
/** 状态: 0-停用 1-正常 */
|
||||
private Integer status;
|
||||
|
||||
/** 最后登录时间 */
|
||||
private LocalDateTime lastLoginTime;
|
||||
|
||||
/** 最后登录IP */
|
||||
private String lastLoginIp;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedTime;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 部门创建/更新请求
|
||||
*/
|
||||
@Data
|
||||
public class DeptRequest {
|
||||
private Long parentId;
|
||||
|
||||
@NotBlank(message = "部门名称不能为空")
|
||||
private String deptName;
|
||||
|
||||
private String leader;
|
||||
private String phone;
|
||||
private Integer sort;
|
||||
private Integer status;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 字典项创建/更新请求
|
||||
*/
|
||||
@Data
|
||||
public class DictItemRequest {
|
||||
@NotNull(message = "字典ID不能为空")
|
||||
private Long dictId;
|
||||
|
||||
@NotBlank(message = "字典项编码不能为空")
|
||||
private String itemCode;
|
||||
|
||||
@NotBlank(message = "字典项名称不能为空")
|
||||
private String itemName;
|
||||
|
||||
private String itemValue;
|
||||
private Integer sort;
|
||||
private Integer status;
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 字典创建/更新请求
|
||||
*/
|
||||
@Data
|
||||
public class DictRequest {
|
||||
@NotBlank(message = "字典编码不能为空")
|
||||
private String dictCode;
|
||||
|
||||
@NotBlank(message = "字典名称不能为空")
|
||||
private String dictName;
|
||||
|
||||
private String description;
|
||||
private Integer status;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 日志查询请求
|
||||
*/
|
||||
@Data
|
||||
public class LogQueryRequest extends PageQuery {
|
||||
/** 日志类型: LOGIN/OPERATE/EXCEPTION */
|
||||
private String logType;
|
||||
/** 用户名(模糊) */
|
||||
private String username;
|
||||
/** 操作模块 */
|
||||
private String module;
|
||||
/** 开始时间(yyyy-MM-dd) */
|
||||
private String startTime;
|
||||
/** 结束时间(yyyy-MM-dd) */
|
||||
private String endTime;
|
||||
/** 状态: 0-失败 1-成功 */
|
||||
private Integer status;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 分页查询基础参数
|
||||
*/
|
||||
@Data
|
||||
public class PageQuery {
|
||||
/** 当前页码, 默认1 */
|
||||
private Integer pageNum = 1;
|
||||
/** 每页条数, 默认10 */
|
||||
private Integer pageSize = 10;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 密码重置请求
|
||||
*/
|
||||
@Data
|
||||
public class PasswordResetRequest {
|
||||
@NotBlank(message = "新密码不能为空")
|
||||
private String newPassword;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色创建/更新请求
|
||||
*/
|
||||
@Data
|
||||
public class RoleRequest {
|
||||
@NotBlank(message = "角色编码不能为空")
|
||||
private String roleCode;
|
||||
|
||||
@NotBlank(message = "角色名称不能为空")
|
||||
private String roleName;
|
||||
|
||||
private String description;
|
||||
|
||||
@NotNull(message = "角色级别不能为空")
|
||||
private Integer level;
|
||||
|
||||
private String dataScope;
|
||||
private List<String> permissions;
|
||||
private Integer sort;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户创建请求
|
||||
*/
|
||||
@Data
|
||||
public class UserCreateRequest {
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
private String realName;
|
||||
private String phone;
|
||||
private String email;
|
||||
private Long deptId;
|
||||
private Long roleId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.water.system.entity.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户更新请求
|
||||
*/
|
||||
@Data
|
||||
public class UserUpdateRequest {
|
||||
private String realName;
|
||||
private String phone;
|
||||
private String email;
|
||||
private Long deptId;
|
||||
private Long roleId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.water.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.system.entity.DataDictionaryItem;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DataDictionaryItemMapper extends BaseMapper<DataDictionaryItem> {
|
||||
|
||||
@Select("SELECT i.* FROM sys_data_dictionary_item i INNER JOIN sys_data_dictionary d ON d.id = i.dict_id WHERE d.dict_code = #{dictCode} AND d.deleted = 0 AND i.deleted = 0 ORDER BY i.sort")
|
||||
List<DataDictionaryItem> selectByDictCode(@Param("dictCode") String dictCode);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.water.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.system.entity.DataDictionary;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
public interface DataDictionaryMapper extends BaseMapper<DataDictionary> {
|
||||
|
||||
@Select("SELECT * FROM sys_data_dictionary WHERE dict_code = #{dictCode} AND deleted = 0")
|
||||
DataDictionary selectByDictCode(@Param("dictCode") String dictCode);
|
||||
}
|
||||
@@ -1,5 +1,15 @@
|
||||
package com.water.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.system.entity.SysDepartment;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface SysDepartmentMapper extends BaseMapper<SysDepartment> {}
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface SysDepartmentMapper extends BaseMapper<SysDepartment> {
|
||||
|
||||
@Select("SELECT * FROM sys_department WHERE ancestors LIKE CONCAT(#{ancestorPath}, '%') AND deleted = 0 ORDER BY sort")
|
||||
List<SysDepartment> selectByAncestorPath(@Param("ancestorPath") String ancestorPath);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.water.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.system.entity.SysLog;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface SysLogMapper extends BaseMapper<SysLog> {}
|
||||
|
||||
@Mapper
|
||||
public interface SysLogMapper extends BaseMapper<SysLog> {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
package com.water.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.system.entity.SysRole;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface SysRoleMapper extends BaseMapper<SysRole> {}
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface SysRoleMapper extends BaseMapper<SysRole> {
|
||||
|
||||
@Select("SELECT r.* FROM sys_role r INNER JOIN sys_user u ON u.role_id = r.id WHERE u.id = #{userId} AND r.deleted = 0")
|
||||
List<SysRole> selectRolesByUserId(@Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT * FROM sys_role WHERE level <= #{level} AND deleted = 0 ORDER BY level, sort")
|
||||
List<SysRole> selectRolesByMaxLevel(@Param("level") Integer level);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
package com.water.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.system.entity.SysUser;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface SysUserMapper extends BaseMapper<SysUser> {}
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
|
||||
@Select("SELECT * FROM sys_user WHERE username = #{username} AND deleted = 0")
|
||||
SysUser selectByUsername(@Param("username") String username);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.water.system.entity.SysDepartment;
|
||||
import com.water.system.entity.dto.DeptRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门管理服务接口
|
||||
*/
|
||||
public interface DepartmentService {
|
||||
|
||||
/** 获取部门树 */
|
||||
List<SysDepartment> getDeptTree();
|
||||
|
||||
/** 获取部门详情 */
|
||||
SysDepartment getDeptById(Long id);
|
||||
|
||||
/** 创建部门 */
|
||||
Long createDept(DeptRequest request);
|
||||
|
||||
/** 更新部门 */
|
||||
void updateDept(Long id, DeptRequest request);
|
||||
|
||||
/** 删除部门 */
|
||||
void deleteDept(Long id);
|
||||
|
||||
/** 获取所有子部门 */
|
||||
List<SysDepartment> getChildDepts(Long parentId);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.water.system.entity.DataDictionary;
|
||||
import com.water.system.entity.DataDictionaryItem;
|
||||
import com.water.system.entity.dto.DictItemRequest;
|
||||
import com.water.system.entity.dto.DictRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据字典服务接口
|
||||
*/
|
||||
public interface DictionaryService {
|
||||
|
||||
/** 查询所有字典 */
|
||||
List<DataDictionary> listDictionaries(String keyword);
|
||||
|
||||
/** 获取字典详情 */
|
||||
DataDictionary getDictionaryById(Long id);
|
||||
|
||||
/** 创建字典 */
|
||||
Long createDictionary(DictRequest request);
|
||||
|
||||
/** 更新字典 */
|
||||
void updateDictionary(Long id, DictRequest request);
|
||||
|
||||
/** 删除字典 */
|
||||
void deleteDictionary(Long id);
|
||||
|
||||
/** 根据字典ID查询字典项 */
|
||||
List<DataDictionaryItem> listItemsByDictId(Long dictId);
|
||||
|
||||
/** 根据字典编码查询字典项 */
|
||||
List<DataDictionaryItem> listItemsByDictCode(String dictCode);
|
||||
|
||||
/** 创建字典项 */
|
||||
Long createItem(DictItemRequest request);
|
||||
|
||||
/** 更新字典项 */
|
||||
void updateItem(Long id, DictItemRequest request);
|
||||
|
||||
/** 删除字典项 */
|
||||
void deleteItem(Long id);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.water.system.entity.SysLog;
|
||||
import com.water.system.entity.dto.LogQueryRequest;
|
||||
|
||||
/**
|
||||
* 日志查询服务接口
|
||||
*/
|
||||
public interface LogService {
|
||||
|
||||
/** 分页查询日志 */
|
||||
IPage<SysLog> pageLogs(LogQueryRequest request);
|
||||
|
||||
/** 获取日志详情 */
|
||||
SysLog getLogById(Long id);
|
||||
|
||||
/** 记录日志 */
|
||||
void saveLog(SysLog log);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.water.system.entity.SysRole;
|
||||
import com.water.system.entity.dto.RoleRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色管理服务接口
|
||||
*/
|
||||
public interface RoleService {
|
||||
|
||||
/** 查询所有角色 */
|
||||
List<SysRole> listRoles();
|
||||
|
||||
/** 根据ID获取角色详情 */
|
||||
SysRole getRoleById(Long id);
|
||||
|
||||
/** 创建角色 */
|
||||
Long createRole(RoleRequest request);
|
||||
|
||||
/** 更新角色 */
|
||||
void updateRole(Long id, RoleRequest request);
|
||||
|
||||
/** 删除角色 */
|
||||
void deleteRole(Long id);
|
||||
|
||||
/** 分配权限 */
|
||||
void assignPermissions(Long id, List<String> permissions);
|
||||
|
||||
/** 根据用户ID查询角色 */
|
||||
List<SysRole> getRolesByUserId(Long userId);
|
||||
|
||||
/** 根据最大级别查询角色 */
|
||||
List<SysRole> getRolesByMaxLevel(Integer level);
|
||||
}
|
||||
@@ -1,67 +1,76 @@
|
||||
package com.water.system.service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
|
||||
import com.water.system.entity.*;
|
||||
import com.water.system.mapper.*;
|
||||
import com.water.system.entity.dto.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.*;
|
||||
@Service @RequiredArgsConstructor
|
||||
|
||||
/**
|
||||
* 系统管理兼容服务 - 保持向后兼容,内部委托给新的专用服务
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SysService {
|
||||
private final SysRoleMapper roleMapper;
|
||||
private final SysUserMapper userMapper;
|
||||
private final RoleService roleService;
|
||||
private final UserService userService;
|
||||
private final DepartmentService departmentService;
|
||||
private final LogService logService;
|
||||
private final SysMenuMapper menuMapper;
|
||||
private final SysDepartmentMapper deptMapper;
|
||||
private final SysLogMapper logMapper;
|
||||
|
||||
// Roles
|
||||
public List<SysRole> listRoles() { return roleMapper.selectList(null); }
|
||||
public List<SysRole> listRoles() { return roleService.listRoles(); }
|
||||
public Long createRole(Map<String,Object> req) {
|
||||
SysRole r = new SysRole();
|
||||
RoleRequest r = new RoleRequest();
|
||||
r.setRoleCode((String) req.get("roleCode"));
|
||||
r.setRoleName((String) req.get("roleName"));
|
||||
r.setDescription((String) req.get("description"));
|
||||
r.setStatus(1); r.setSort(0);
|
||||
roleMapper.insert(r);
|
||||
return r.getId();
|
||||
r.setLevel(req.get("level") != null ? ((Number) req.get("level")).intValue() : 4);
|
||||
r.setDataScope((String) req.get("dataScope"));
|
||||
return roleService.createRole(r);
|
||||
}
|
||||
|
||||
// Users
|
||||
public List<SysUser> listUsers(Integer status) {
|
||||
return userMapper.selectList(new LambdaQueryWrapper<SysUser>()
|
||||
.eq(status != null, SysUser::getStatus, status));
|
||||
return userService.pageUsers(1, 1000, null, status, null).getRecords();
|
||||
}
|
||||
public Long createUser(Map<String,Object> req) {
|
||||
SysUser u = new SysUser();
|
||||
u.setUsername((String)req.get("username"));
|
||||
u.setPassword((String)req.get("password"));
|
||||
u.setRealName((String)req.get("realName"));
|
||||
u.setPhone((String)req.get("phone"));
|
||||
u.setStatus(1);
|
||||
userMapper.insert(u);
|
||||
return u.getId();
|
||||
UserCreateRequest r = new UserCreateRequest();
|
||||
r.setUsername((String) req.get("username"));
|
||||
r.setPassword((String) req.get("password"));
|
||||
r.setRealName((String) req.get("realName"));
|
||||
r.setPhone((String) req.get("phone"));
|
||||
r.setEmail((String) req.get("email"));
|
||||
if (req.get("deptId") != null) r.setDeptId(((Number) req.get("deptId")).longValue());
|
||||
if (req.get("roleId") != null) r.setRoleId(((Number) req.get("roleId")).longValue());
|
||||
return userService.createUser(r);
|
||||
}
|
||||
public void updateUserStatus(Long id, int status) {
|
||||
SysUser u = userMapper.selectById(id);
|
||||
if (u == null) throw new RuntimeException("用户不存在");
|
||||
u.setStatus(status);
|
||||
userMapper.updateById(u);
|
||||
userService.updateStatus(id, status);
|
||||
}
|
||||
|
||||
// Menus
|
||||
public List<SysMenu> listMenus() { return menuMapper.selectList(null); }
|
||||
|
||||
// Departments
|
||||
public List<SysDepartment> listDepts() { return deptMapper.selectList(null); }
|
||||
public List<SysDepartment> listDepts() { return departmentService.getDeptTree(); }
|
||||
public Long createDept(Map<String,Object> req) {
|
||||
SysDepartment d = new SysDepartment();
|
||||
d.setDeptName((String)req.get("deptName"));
|
||||
d.setLeader((String)req.get("leader"));
|
||||
d.setSort(0); d.setStatus(1);
|
||||
deptMapper.insert(d);
|
||||
return d.getId();
|
||||
DeptRequest r = new DeptRequest();
|
||||
r.setDeptName((String) req.get("deptName"));
|
||||
r.setLeader((String) req.get("leader"));
|
||||
r.setPhone((String) req.get("phone"));
|
||||
if (req.get("parentId") != null) r.setParentId(((Number) req.get("parentId")).longValue());
|
||||
return departmentService.createDept(r);
|
||||
}
|
||||
|
||||
// Logs
|
||||
public List<SysLog> listLogs(String logType, String username) {
|
||||
LambdaQueryWrapper<SysLog> w = new LambdaQueryWrapper<>();
|
||||
if (logType != null) w.eq(SysLog::getLogType, logType);
|
||||
if (username != null) w.like(SysLog::getUsername, username);
|
||||
return logMapper.selectList(w.orderByDesc(SysLog::getCreatedTime).last("LIMIT 100"));
|
||||
LogQueryRequest r = new LogQueryRequest();
|
||||
r.setLogType(logType);
|
||||
r.setUsername(username);
|
||||
r.setPageNum(1);
|
||||
r.setPageSize(100);
|
||||
return logService.pageLogs(r).getRecords();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.water.system.entity.SysUser;
|
||||
import com.water.system.entity.dto.UserCreateRequest;
|
||||
import com.water.system.entity.dto.UserUpdateRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户管理服务接口
|
||||
*/
|
||||
public interface UserService {
|
||||
|
||||
/** 分页查询用户列表 */
|
||||
IPage<SysUser> pageUsers(Integer pageNum, Integer pageSize, String keyword, Integer status, Long deptId);
|
||||
|
||||
/** 获取用户详情 */
|
||||
SysUser getUserById(Long id);
|
||||
|
||||
/** 创建用户 */
|
||||
Long createUser(UserCreateRequest request);
|
||||
|
||||
/** 更新用户 */
|
||||
void updateUser(Long id, UserUpdateRequest request);
|
||||
|
||||
/** 删除用户 */
|
||||
void deleteUser(Long id);
|
||||
|
||||
/** 停用/启用用户 */
|
||||
void updateStatus(Long id, Integer status);
|
||||
|
||||
/** 重置密码 */
|
||||
void resetPassword(Long id, String newPassword);
|
||||
|
||||
/** 根据用户名查询 */
|
||||
SysUser getByUsername(String username);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.water.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysDepartment;
|
||||
import com.water.system.entity.dto.DeptRequest;
|
||||
import com.water.system.mapper.SysDepartmentMapper;
|
||||
import com.water.system.service.DepartmentService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DepartmentServiceImpl implements DepartmentService {
|
||||
|
||||
private final SysDepartmentMapper deptMapper;
|
||||
|
||||
@Override
|
||||
public List<SysDepartment> getDeptTree() {
|
||||
List<SysDepartment> allDepts = deptMapper.selectList(
|
||||
new LambdaQueryWrapper<SysDepartment>().orderByAsc(SysDepartment::getSort)
|
||||
);
|
||||
return buildTree(allDepts, 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysDepartment getDeptById(Long id) {
|
||||
SysDepartment dept = deptMapper.selectById(id);
|
||||
if (dept == null) throw new BusinessException("部门不存在");
|
||||
return dept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createDept(DeptRequest request) {
|
||||
SysDepartment dept = new SysDepartment();
|
||||
dept.setDeptName(request.getDeptName());
|
||||
dept.setLeader(request.getLeader());
|
||||
dept.setPhone(request.getPhone());
|
||||
dept.setSort(request.getSort() != null ? request.getSort() : 0);
|
||||
dept.setStatus(request.getStatus() != null ? request.getStatus() : 1);
|
||||
|
||||
Long parentId = request.getParentId() != null ? request.getParentId() : 0L;
|
||||
dept.setParentId(parentId);
|
||||
|
||||
// 设置层级路径
|
||||
if (parentId > 0) {
|
||||
SysDepartment parent = getDeptById(parentId);
|
||||
String ancestors = parent.getAncestors() != null ? parent.getAncestors() + "," + parentId : String.valueOf(parentId);
|
||||
dept.setAncestors(ancestors);
|
||||
} else {
|
||||
dept.setAncestors("0");
|
||||
}
|
||||
|
||||
deptMapper.insert(dept);
|
||||
return dept.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDept(Long id, DeptRequest request) {
|
||||
SysDepartment dept = getDeptById(id);
|
||||
if (request.getDeptName() != null) dept.setDeptName(request.getDeptName());
|
||||
if (request.getLeader() != null) dept.setLeader(request.getLeader());
|
||||
if (request.getPhone() != null) dept.setPhone(request.getPhone());
|
||||
if (request.getSort() != null) dept.setSort(request.getSort());
|
||||
if (request.getStatus() != null) dept.setStatus(request.getStatus());
|
||||
deptMapper.updateById(dept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDept(Long id) {
|
||||
// 检查是否有子部门
|
||||
long childCount = deptMapper.selectCount(
|
||||
new LambdaQueryWrapper<SysDepartment>().eq(SysDepartment::getParentId, id)
|
||||
);
|
||||
if (childCount > 0) {
|
||||
throw new BusinessException("该部门下有子部门,不允许删除");
|
||||
}
|
||||
deptMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysDepartment> getChildDepts(Long parentId) {
|
||||
SysDepartment parent = getDeptById(parentId);
|
||||
String ancestorPath = parent.getAncestors() != null ? parent.getAncestors() + "," + parentId : String.valueOf(parentId);
|
||||
return deptMapper.selectByAncestorPath(ancestorPath);
|
||||
}
|
||||
|
||||
private List<SysDepartment> buildTree(List<SysDepartment> depts, Long parentId) {
|
||||
Map<Long, List<SysDepartment>> grouped = depts.stream()
|
||||
.collect(Collectors.groupingBy(SysDepartment::getParentId));
|
||||
|
||||
List<SysDepartment> roots = grouped.getOrDefault(parentId, new ArrayList<>());
|
||||
for (SysDepartment root : roots) {
|
||||
root.setChildren(buildTree(depts, root.getId()));
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.water.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.DataDictionary;
|
||||
import com.water.system.entity.DataDictionaryItem;
|
||||
import com.water.system.entity.dto.DictItemRequest;
|
||||
import com.water.system.entity.dto.DictRequest;
|
||||
import com.water.system.mapper.DataDictionaryMapper;
|
||||
import com.water.system.mapper.DataDictionaryItemMapper;
|
||||
import com.water.system.service.DictionaryService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DictionaryServiceImpl implements DictionaryService {
|
||||
|
||||
private final DataDictionaryMapper dictMapper;
|
||||
private final DataDictionaryItemMapper itemMapper;
|
||||
|
||||
@Override
|
||||
public List<DataDictionary> listDictionaries(String keyword) {
|
||||
LambdaQueryWrapper<DataDictionary> wrapper = new LambdaQueryWrapper<>();
|
||||
if (keyword != null && !keyword.isBlank()) {
|
||||
wrapper.like(DataDictionary::getDictCode, keyword)
|
||||
.or().like(DataDictionary::getDictName, keyword);
|
||||
}
|
||||
wrapper.orderByAsc(DataDictionary::getDictCode);
|
||||
return dictMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataDictionary getDictionaryById(Long id) {
|
||||
DataDictionary dict = dictMapper.selectById(id);
|
||||
if (dict == null) throw new BusinessException("字典不存在");
|
||||
return dict;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createDictionary(DictRequest request) {
|
||||
DataDictionary existing = dictMapper.selectByDictCode(request.getDictCode());
|
||||
if (existing != null) throw new BusinessException("字典编码已存在");
|
||||
|
||||
DataDictionary dict = new DataDictionary();
|
||||
dict.setDictCode(request.getDictCode());
|
||||
dict.setDictName(request.getDictName());
|
||||
dict.setDescription(request.getDescription());
|
||||
dict.setStatus(request.getStatus() != null ? request.getStatus() : 1);
|
||||
dictMapper.insert(dict);
|
||||
return dict.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDictionary(Long id, DictRequest request) {
|
||||
DataDictionary dict = getDictionaryById(id);
|
||||
if (request.getDictName() != null) dict.setDictName(request.getDictName());
|
||||
if (request.getDescription() != null) dict.setDescription(request.getDescription());
|
||||
if (request.getStatus() != null) dict.setStatus(request.getStatus());
|
||||
dictMapper.updateById(dict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDictionary(Long id) {
|
||||
// 同时删除字典项
|
||||
itemMapper.delete(new LambdaQueryWrapper<DataDictionaryItem>()
|
||||
.eq(DataDictionaryItem::getDictId, id));
|
||||
dictMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataDictionaryItem> listItemsByDictId(Long dictId) {
|
||||
return itemMapper.selectList(
|
||||
new LambdaQueryWrapper<DataDictionaryItem>()
|
||||
.eq(DataDictionaryItem::getDictId, dictId)
|
||||
.orderByAsc(DataDictionaryItem::getSort)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataDictionaryItem> listItemsByDictCode(String dictCode) {
|
||||
return itemMapper.selectByDictCode(dictCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createItem(DictItemRequest request) {
|
||||
DataDictionaryItem item = new DataDictionaryItem();
|
||||
item.setDictId(request.getDictId());
|
||||
item.setItemCode(request.getItemCode());
|
||||
item.setItemName(request.getItemName());
|
||||
item.setItemValue(request.getItemValue());
|
||||
item.setSort(request.getSort() != null ? request.getSort() : 0);
|
||||
item.setStatus(request.getStatus() != null ? request.getStatus() : 1);
|
||||
item.setRemark(request.getRemark());
|
||||
itemMapper.insert(item);
|
||||
return item.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(Long id, DictItemRequest request) {
|
||||
DataDictionaryItem item = itemMapper.selectById(id);
|
||||
if (item == null) throw new BusinessException("字典项不存在");
|
||||
if (request.getItemName() != null) item.setItemName(request.getItemName());
|
||||
if (request.getItemValue() != null) item.setItemValue(request.getItemValue());
|
||||
if (request.getSort() != null) item.setSort(request.getSort());
|
||||
if (request.getStatus() != null) item.setStatus(request.getStatus());
|
||||
if (request.getRemark() != null) item.setRemark(request.getRemark());
|
||||
itemMapper.updateById(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteItem(Long id) {
|
||||
itemMapper.deleteById(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.water.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.system.entity.SysLog;
|
||||
import com.water.system.entity.dto.LogQueryRequest;
|
||||
import com.water.system.mapper.SysLogMapper;
|
||||
import com.water.system.service.LogService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class LogServiceImpl implements LogService {
|
||||
|
||||
private final SysLogMapper logMapper;
|
||||
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
@Override
|
||||
public IPage<SysLog> pageLogs(LogQueryRequest request) {
|
||||
Page<SysLog> page = new Page<>(request.getPageNum(), request.getPageSize());
|
||||
LambdaQueryWrapper<SysLog> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (request.getLogType() != null && !request.getLogType().isBlank()) {
|
||||
wrapper.eq(SysLog::getLogType, request.getLogType());
|
||||
}
|
||||
if (request.getUsername() != null && !request.getUsername().isBlank()) {
|
||||
wrapper.like(SysLog::getUsername, request.getUsername());
|
||||
}
|
||||
if (request.getModule() != null && !request.getModule().isBlank()) {
|
||||
wrapper.eq(SysLog::getModule, request.getModule());
|
||||
}
|
||||
if (request.getStatus() != null) {
|
||||
wrapper.eq(SysLog::getStatus, request.getStatus());
|
||||
}
|
||||
if (request.getStartTime() != null && !request.getStartTime().isBlank()) {
|
||||
LocalDateTime start = LocalDate.parse(request.getStartTime(), DATE_FMT).atStartOfDay();
|
||||
wrapper.ge(SysLog::getCreatedTime, start);
|
||||
}
|
||||
if (request.getEndTime() != null && !request.getEndTime().isBlank()) {
|
||||
LocalDateTime end = LocalDate.parse(request.getEndTime(), DATE_FMT).plusDays(1).atStartOfDay();
|
||||
wrapper.lt(SysLog::getCreatedTime, end);
|
||||
}
|
||||
|
||||
wrapper.orderByDesc(SysLog::getCreatedTime);
|
||||
return logMapper.selectPage(page, wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysLog getLogById(Long id) {
|
||||
return logMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveLog(SysLog log) {
|
||||
logMapper.insert(log);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.water.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysRole;
|
||||
import com.water.system.entity.dto.RoleRequest;
|
||||
import com.water.system.mapper.SysRoleMapper;
|
||||
import com.water.system.service.RoleService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoleServiceImpl implements RoleService {
|
||||
|
||||
private final SysRoleMapper roleMapper;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public List<SysRole> listRoles() {
|
||||
return roleMapper.selectList(
|
||||
new LambdaQueryWrapper<SysRole>().orderByAsc(SysRole::getLevel, SysRole::getSort)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysRole getRoleById(Long id) {
|
||||
SysRole role = roleMapper.selectById(id);
|
||||
if (role == null) throw new BusinessException("角色不存在");
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createRole(RoleRequest request) {
|
||||
// 检查编码唯一
|
||||
long count = roleMapper.selectCount(
|
||||
new LambdaQueryWrapper<SysRole>().eq(SysRole::getRoleCode, request.getRoleCode())
|
||||
);
|
||||
if (count > 0) throw new BusinessException("角色编码已存在");
|
||||
|
||||
SysRole role = new SysRole();
|
||||
role.setRoleCode(request.getRoleCode());
|
||||
role.setRoleName(request.getRoleName());
|
||||
role.setDescription(request.getDescription());
|
||||
role.setLevel(request.getLevel());
|
||||
role.setDataScope(request.getDataScope() != null ? request.getDataScope() : "SELF");
|
||||
role.setPermissions(toJson(request.getPermissions()));
|
||||
role.setStatus(1);
|
||||
role.setSort(request.getSort() != null ? request.getSort() : 0);
|
||||
roleMapper.insert(role);
|
||||
return role.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRole(Long id, RoleRequest request) {
|
||||
SysRole role = getRoleById(id);
|
||||
role.setRoleName(request.getRoleName());
|
||||
role.setDescription(request.getDescription());
|
||||
role.setLevel(request.getLevel());
|
||||
if (request.getDataScope() != null) role.setDataScope(request.getDataScope());
|
||||
if (request.getPermissions() != null) role.setPermissions(toJson(request.getPermissions()));
|
||||
if (request.getSort() != null) role.setSort(request.getSort());
|
||||
roleMapper.updateById(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRole(Long id) {
|
||||
SysRole role = getRoleById(id);
|
||||
if (role.getLevel() != null && role.getLevel() <= 2) {
|
||||
throw new BusinessException("超级管理员和系统管理员角色不允许删除");
|
||||
}
|
||||
roleMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignPermissions(Long id, List<String> permissions) {
|
||||
SysRole role = getRoleById(id);
|
||||
role.setPermissions(toJson(permissions));
|
||||
roleMapper.updateById(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysRole> getRolesByUserId(Long userId) {
|
||||
return roleMapper.selectRolesByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysRole> getRolesByMaxLevel(Integer level) {
|
||||
return roleMapper.selectRolesByMaxLevel(level);
|
||||
}
|
||||
|
||||
private String toJson(Object obj) {
|
||||
if (obj == null) return null;
|
||||
try {
|
||||
return objectMapper.writeValueAsString(obj);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new BusinessException("JSON序列化失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.water.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysUser;
|
||||
import com.water.system.entity.dto.UserCreateRequest;
|
||||
import com.water.system.entity.dto.UserUpdateRequest;
|
||||
import com.water.system.mapper.SysUserMapper;
|
||||
import com.water.system.service.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private final SysUserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public IPage<SysUser> pageUsers(Integer pageNum, Integer pageSize, String keyword, Integer status, Long deptId) {
|
||||
Page<SysUser> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||
if (keyword != null && !keyword.isBlank()) {
|
||||
wrapper.and(w -> w.like(SysUser::getUsername, keyword)
|
||||
.or().like(SysUser::getRealName, keyword)
|
||||
.or().like(SysUser::getPhone, keyword));
|
||||
}
|
||||
if (status != null) wrapper.eq(SysUser::getStatus, status);
|
||||
if (deptId != null) wrapper.eq(SysUser::getDeptId, deptId);
|
||||
wrapper.orderByDesc(SysUser::getCreatedTime);
|
||||
|
||||
IPage<SysUser> result = userMapper.selectPage(page, wrapper);
|
||||
// 清除密码字段
|
||||
result.getRecords().forEach(u -> u.setPassword(null));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysUser getUserById(Long id) {
|
||||
SysUser user = userMapper.selectById(id);
|
||||
if (user == null) throw new BusinessException("用户不存在");
|
||||
user.setPassword(null);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createUser(UserCreateRequest request) {
|
||||
// 检查用户名唯一
|
||||
SysUser existing = userMapper.selectByUsername(request.getUsername());
|
||||
if (existing != null) throw new BusinessException("用户名已存在");
|
||||
|
||||
SysUser user = new SysUser();
|
||||
user.setUsername(request.getUsername());
|
||||
user.setPassword(encryptPassword(request.getPassword()));
|
||||
user.setRealName(request.getRealName());
|
||||
user.setPhone(request.getPhone());
|
||||
user.setEmail(request.getEmail());
|
||||
user.setDeptId(request.getDeptId());
|
||||
user.setRoleId(request.getRoleId());
|
||||
user.setStatus(1);
|
||||
userMapper.insert(user);
|
||||
return user.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUser(Long id, UserUpdateRequest request) {
|
||||
SysUser user = userMapper.selectById(id);
|
||||
if (user == null) throw new BusinessException("用户不存在");
|
||||
if (request.getRealName() != null) user.setRealName(request.getRealName());
|
||||
if (request.getPhone() != null) user.setPhone(request.getPhone());
|
||||
if (request.getEmail() != null) user.setEmail(request.getEmail());
|
||||
if (request.getDeptId() != null) user.setDeptId(request.getDeptId());
|
||||
if (request.getRoleId() != null) user.setRoleId(request.getRoleId());
|
||||
userMapper.updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUser(Long id) {
|
||||
SysUser user = userMapper.selectById(id);
|
||||
if (user == null) throw new BusinessException("用户不存在");
|
||||
if ("admin".equals(user.getUsername())) {
|
||||
throw new BusinessException("系统管理员账号不允许删除");
|
||||
}
|
||||
userMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(Long id, Integer status) {
|
||||
SysUser user = userMapper.selectById(id);
|
||||
if (user == null) throw new BusinessException("用户不存在");
|
||||
user.setStatus(status);
|
||||
userMapper.updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetPassword(Long id, String newPassword) {
|
||||
SysUser user = userMapper.selectById(id);
|
||||
if (user == null) throw new BusinessException("用户不存在");
|
||||
user.setPassword(encryptPassword(newPassword));
|
||||
userMapper.updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysUser getByUsername(String username) {
|
||||
return userMapper.selectByUsername(username);
|
||||
}
|
||||
|
||||
private String encryptPassword(String password) {
|
||||
return DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
-- V2__system_enhance.sql
|
||||
-- 系统管理增强: 角色/用户/部门/日志/字典
|
||||
|
||||
-- 1. 增强 sys_role 表: 新增 level, data_scope, permissions, updated_time 字段
|
||||
ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS level INT DEFAULT 4;
|
||||
ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS data_scope VARCHAR(20) DEFAULT 'SELF';
|
||||
ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS permissions TEXT;
|
||||
ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS updated_time TIMESTAMP DEFAULT NOW();
|
||||
ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS deleted INT DEFAULT 0;
|
||||
|
||||
COMMENT ON COLUMN sys_role.level IS '角色级别: 1-超级管理员 2-系统管理员 3-部门经理 4-普通员工 5-访客';
|
||||
COMMENT ON COLUMN sys_role.data_scope IS '数据权限: ALL/DEPT/DEPT_BELOW/SELF';
|
||||
COMMENT ON COLUMN sys_role.permissions IS '权限配置(JSON数组)';
|
||||
|
||||
-- 2. 增强 sys_user 表: 新增 role_id, last_login_time, last_login_ip, deleted
|
||||
ALTER TABLE sys_user ADD COLUMN IF NOT EXISTS role_id BIGINT;
|
||||
ALTER TABLE sys_user ADD COLUMN IF NOT EXISTS last_login_time TIMESTAMP;
|
||||
ALTER TABLE sys_user ADD COLUMN IF NOT EXISTS last_login_ip VARCHAR(50);
|
||||
ALTER TABLE sys_user ADD COLUMN IF NOT EXISTS deleted INT DEFAULT 0;
|
||||
|
||||
-- 3. 增强 sys_department 表: 新增 ancestors, created_time, updated_time, deleted
|
||||
ALTER TABLE sys_department ADD COLUMN IF NOT EXISTS ancestors VARCHAR(500) DEFAULT '0';
|
||||
ALTER TABLE sys_department ADD COLUMN IF NOT EXISTS created_time TIMESTAMP DEFAULT NOW();
|
||||
ALTER TABLE sys_department ADD COLUMN IF NOT EXISTS updated_time TIMESTAMP DEFAULT NOW();
|
||||
ALTER TABLE sys_department ADD COLUMN IF NOT EXISTS deleted INT DEFAULT 0;
|
||||
|
||||
COMMENT ON COLUMN sys_department.ancestors IS '层级路径(如: 0,1,5)';
|
||||
|
||||
-- 4. 增强 sys_log 表: 新增 status, error_msg
|
||||
ALTER TABLE sys_log ADD COLUMN IF NOT EXISTS status INT DEFAULT 1;
|
||||
ALTER TABLE sys_log ADD COLUMN IF NOT EXISTS error_msg TEXT;
|
||||
|
||||
COMMENT ON COLUMN sys_log.log_type IS '日志类型: LOGIN/OPERATE/EXCEPTION';
|
||||
COMMENT ON COLUMN sys_log.status IS '状态: 0-失败 1-成功';
|
||||
|
||||
-- 5. 新增数据字典表
|
||||
CREATE TABLE IF NOT EXISTS sys_data_dictionary (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
dict_code VARCHAR(100) UNIQUE NOT NULL,
|
||||
dict_name VARCHAR(200) NOT NULL,
|
||||
description VARCHAR(500),
|
||||
status INT DEFAULT 1,
|
||||
deleted INT DEFAULT 0,
|
||||
created_time TIMESTAMP DEFAULT NOW(),
|
||||
updated_time TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE sys_data_dictionary IS '数据字典表';
|
||||
COMMENT ON COLUMN sys_data_dictionary.dict_code IS '字典编码(唯一)';
|
||||
COMMENT ON COLUMN sys_data_dictionary.dict_name IS '字典名称';
|
||||
|
||||
-- 6. 新增数据字典项表
|
||||
CREATE TABLE IF NOT EXISTS sys_data_dictionary_item (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
dict_id BIGINT NOT NULL,
|
||||
item_code VARCHAR(100) NOT NULL,
|
||||
item_name VARCHAR(200) NOT NULL,
|
||||
item_value VARCHAR(500),
|
||||
sort INT DEFAULT 0,
|
||||
status INT DEFAULT 1,
|
||||
remark VARCHAR(500),
|
||||
deleted INT DEFAULT 0,
|
||||
created_time TIMESTAMP DEFAULT NOW(),
|
||||
updated_time TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE sys_data_dictionary_item IS '数据字典项表';
|
||||
COMMENT ON COLUMN sys_data_dictionary_item.dict_id IS '所属字典ID';
|
||||
COMMENT ON COLUMN sys_data_dictionary_item.item_code IS '字典项编码';
|
||||
COMMENT ON COLUMN sys_data_dictionary_item.item_name IS '字典项名称';
|
||||
COMMENT ON COLUMN sys_data_dictionary_item.item_value IS '字典项值';
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_dict_item_dict_id ON sys_data_dictionary_item(dict_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dict_item_code ON sys_data_dictionary_item(item_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_user_dept_id ON sys_user(dept_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_user_role_id ON sys_user(role_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_user_status ON sys_user(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_role_level ON sys_role(level);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dept_parent_id ON sys_department(parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_dept_ancestors ON sys_department(ancestors);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_log_type ON sys_log(log_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_log_user ON sys_log(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_log_created ON sys_log(created_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_log_module ON sys_log(module);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_log_status ON sys_log(status);
|
||||
|
||||
-- 初始化5级角色数据
|
||||
INSERT INTO sys_role (role_code, role_name, description, level, data_scope, sort, created_time) VALUES
|
||||
('SUPER_ADMIN', '超级管理员', '拥有系统所有权限', 1, 'ALL', 1, NOW()),
|
||||
('SYS_ADMIN', '系统管理员', '系统管理权限', 2, 'ALL', 2, NOW()),
|
||||
('DEPT_MANAGER', '部门经理', '本部门管理权限', 3, 'DEPT', 3, NOW()),
|
||||
('EMPLOYEE', '普通员工', '基础操作权限', 4, 'SELF', 4, NOW()),
|
||||
('GUEST', '访客', '仅查看权限', 5, 'SELF', 5, NOW())
|
||||
ON CONFLICT (role_code) DO NOTHING;
|
||||
|
||||
-- 初始化数据字典示例
|
||||
INSERT INTO sys_data_dictionary (dict_code, dict_name, description, status, created_time) VALUES
|
||||
('sys_user_status', '用户状态', '用户账号状态枚举', 1, NOW()),
|
||||
('sys_log_type', '日志类型', '系统日志类型枚举', 1, NOW()),
|
||||
('sys_role_level', '角色级别', '系统角色级别枚举', 1, NOW())
|
||||
ON CONFLICT (dict_code) DO NOTHING;
|
||||
|
||||
INSERT INTO sys_data_dictionary_item (dict_id, item_code, item_name, item_value, sort, status, created_time)
|
||||
SELECT d.id, item_code, item_name, item_value, sort, 1, NOW()
|
||||
FROM sys_data_dictionary d
|
||||
CROSS JOIN (VALUES
|
||||
('sys_user_status', 'DISABLED', '停用', '0', 1),
|
||||
('sys_user_status', 'ENABLED', '正常', '1', 2)
|
||||
) AS v(dict_code, item_code, item_name, item_value, sort)
|
||||
WHERE d.dict_code = v.dict_code
|
||||
AND NOT EXISTS (SELECT 1 FROM sys_data_dictionary_item i WHERE i.dict_id = d.id AND i.item_code = v.item_code);
|
||||
|
||||
INSERT INTO sys_data_dictionary_item (dict_id, item_code, item_name, item_value, sort, status, created_time)
|
||||
SELECT d.id, item_code, item_name, item_value, sort, 1, NOW()
|
||||
FROM sys_data_dictionary d
|
||||
CROSS JOIN (VALUES
|
||||
('sys_log_type', 'LOGIN', '登录日志', 'LOGIN', 1),
|
||||
('sys_log_type', 'OPERATE', '操作日志', 'OPERATE', 2),
|
||||
('sys_log_type', 'EXCEPTION', '异常日志', 'EXCEPTION', 3)
|
||||
) AS v(dict_code, item_code, item_name, item_value, sort)
|
||||
WHERE d.dict_code = v.dict_code
|
||||
AND NOT EXISTS (SELECT 1 FROM sys_data_dictionary_item i WHERE i.dict_id = d.id AND i.item_code = v.item_code);
|
||||
|
||||
INSERT INTO sys_data_dictionary_item (dict_id, item_code, item_name, item_value, sort, status, created_time)
|
||||
SELECT d.id, item_code, item_name, item_value, sort, 1, NOW()
|
||||
FROM sys_data_dictionary d
|
||||
CROSS JOIN (VALUES
|
||||
('sys_role_level', 'LEVEL_1', '超级管理员', '1', 1),
|
||||
('sys_role_level', 'LEVEL_2', '系统管理员', '2', 2),
|
||||
('sys_role_level', 'LEVEL_3', '部门经理', '3', 3),
|
||||
('sys_role_level', 'LEVEL_4', '普通员工', '4', 4),
|
||||
('sys_role_level', 'LEVEL_5', '访客', '5', 5)
|
||||
) AS v(dict_code, item_code, item_name, item_value, sort)
|
||||
WHERE d.dict_code = v.dict_code
|
||||
AND NOT EXISTS (SELECT 1 FROM sys_data_dictionary_item i WHERE i.dict_id = d.id AND i.item_code = v.item_code);
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysDepartment;
|
||||
import com.water.system.entity.dto.DeptRequest;
|
||||
import com.water.system.mapper.SysDepartmentMapper;
|
||||
import com.water.system.service.impl.DepartmentServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DepartmentServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private SysDepartmentMapper deptMapper;
|
||||
|
||||
@InjectMocks
|
||||
private DepartmentServiceImpl departmentService;
|
||||
|
||||
@Test
|
||||
void getDeptTree_shouldBuildTree() {
|
||||
SysDepartment root = new SysDepartment();
|
||||
root.setId(1L);
|
||||
root.setParentId(0L);
|
||||
root.setDeptName("总公司");
|
||||
|
||||
SysDepartment child1 = new SysDepartment();
|
||||
child1.setId(2L);
|
||||
child1.setParentId(1L);
|
||||
child1.setDeptName("技术部");
|
||||
|
||||
SysDepartment child2 = new SysDepartment();
|
||||
child2.setId(3L);
|
||||
child2.setParentId(1L);
|
||||
child2.setDeptName("市场部");
|
||||
|
||||
when(deptMapper.selectList(any(LambdaQueryWrapper.class)))
|
||||
.thenReturn(Arrays.asList(root, child1, child2));
|
||||
|
||||
List<SysDepartment> tree = departmentService.getDeptTree();
|
||||
|
||||
assertEquals(1, tree.size());
|
||||
assertEquals("总公司", tree.get(0).getDeptName());
|
||||
assertEquals(2, tree.get(0).getChildren().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDept_shouldSetAncestors() {
|
||||
SysDepartment parent = new SysDepartment();
|
||||
parent.setId(1L);
|
||||
parent.setAncestors("0");
|
||||
parent.setDeptName("总公司");
|
||||
|
||||
when(deptMapper.selectById(1L)).thenReturn(parent);
|
||||
when(deptMapper.insert(any(SysDepartment.class))).thenAnswer(inv -> {
|
||||
SysDepartment d = inv.getArgument(0);
|
||||
d.setId(10L);
|
||||
assertEquals("0,1", d.getAncestors());
|
||||
return 1;
|
||||
});
|
||||
|
||||
DeptRequest request = new DeptRequest();
|
||||
request.setDeptName("研发部");
|
||||
request.setParentId(1L);
|
||||
|
||||
Long id = departmentService.createDept(request);
|
||||
assertEquals(10L, id);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteDept_shouldFailWithChildren() {
|
||||
when(deptMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(2L);
|
||||
|
||||
assertThrows(BusinessException.class, () -> departmentService.deleteDept(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteDept_shouldSucceedWithoutChildren() {
|
||||
when(deptMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(0L);
|
||||
|
||||
assertDoesNotThrow(() -> departmentService.deleteDept(5L));
|
||||
verify(deptMapper).deleteById(5L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.DataDictionary;
|
||||
import com.water.system.entity.DataDictionaryItem;
|
||||
import com.water.system.entity.dto.DictItemRequest;
|
||||
import com.water.system.entity.dto.DictRequest;
|
||||
import com.water.system.mapper.DataDictionaryMapper;
|
||||
import com.water.system.mapper.DataDictionaryItemMapper;
|
||||
import com.water.system.service.impl.DictionaryServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DictionaryServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private DataDictionaryMapper dictMapper;
|
||||
|
||||
@Mock
|
||||
private DataDictionaryItemMapper itemMapper;
|
||||
|
||||
@InjectMocks
|
||||
private DictionaryServiceImpl dictionaryService;
|
||||
|
||||
@Test
|
||||
void createDictionary_shouldSucceed() {
|
||||
DictRequest request = new DictRequest();
|
||||
request.setDictCode("water_quality_level");
|
||||
request.setDictName("水质等级");
|
||||
|
||||
when(dictMapper.selectByDictCode("water_quality_level")).thenReturn(null);
|
||||
when(dictMapper.insert(any(DataDictionary.class))).thenAnswer(inv -> {
|
||||
DataDictionary d = inv.getArgument(0);
|
||||
d.setId(10L);
|
||||
return 1;
|
||||
});
|
||||
|
||||
Long id = dictionaryService.createDictionary(request);
|
||||
assertEquals(10L, id);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDictionary_shouldFailOnDuplicate() {
|
||||
DictRequest request = new DictRequest();
|
||||
request.setDictCode("sys_user_status");
|
||||
|
||||
DataDictionary existing = new DataDictionary();
|
||||
existing.setDictCode("sys_user_status");
|
||||
when(dictMapper.selectByDictCode("sys_user_status")).thenReturn(existing);
|
||||
|
||||
assertThrows(BusinessException.class, () -> dictionaryService.createDictionary(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void listItemsByDictCode_shouldReturnItems() {
|
||||
DataDictionaryItem item1 = new DataDictionaryItem();
|
||||
item1.setItemCode("ENABLED");
|
||||
item1.setItemName("正常");
|
||||
DataDictionaryItem item2 = new DataDictionaryItem();
|
||||
item2.setItemCode("DISABLED");
|
||||
item2.setItemName("停用");
|
||||
|
||||
when(itemMapper.selectByDictCode("sys_user_status")).thenReturn(Arrays.asList(item1, item2));
|
||||
|
||||
List<DataDictionaryItem> items = dictionaryService.listItemsByDictCode("sys_user_status");
|
||||
assertEquals(2, items.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteDictionary_shouldDeleteItemsToo() {
|
||||
dictionaryService.deleteDictionary(1L);
|
||||
|
||||
verify(itemMapper).delete(any(LambdaQueryWrapper.class));
|
||||
verify(dictMapper).deleteById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createItem_shouldSucceed() {
|
||||
DictItemRequest request = new DictItemRequest();
|
||||
request.setDictId(1L);
|
||||
request.setItemCode("NEW_ITEM");
|
||||
request.setItemName("新项");
|
||||
request.setItemValue("value");
|
||||
|
||||
when(itemMapper.insert(any(DataDictionaryItem.class))).thenAnswer(inv -> {
|
||||
DataDictionaryItem i = inv.getArgument(0);
|
||||
i.setId(20L);
|
||||
return 1;
|
||||
});
|
||||
|
||||
Long id = dictionaryService.createItem(request);
|
||||
assertEquals(20L, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.system.entity.SysLog;
|
||||
import com.water.system.entity.dto.LogQueryRequest;
|
||||
import com.water.system.mapper.SysLogMapper;
|
||||
import com.water.system.service.impl.LogServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class LogServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private SysLogMapper logMapper;
|
||||
|
||||
@InjectMocks
|
||||
private LogServiceImpl logService;
|
||||
|
||||
@Test
|
||||
void pageLogs_shouldReturnPagedResult() {
|
||||
Page<SysLog> page = new Page<>(1, 10);
|
||||
SysLog log1 = new SysLog();
|
||||
log1.setId(1L);
|
||||
log1.setLogType("LOGIN");
|
||||
log1.setUsername("admin");
|
||||
log1.setCreatedTime(LocalDateTime.now());
|
||||
|
||||
SysLog log2 = new SysLog();
|
||||
log2.setId(2L);
|
||||
log2.setLogType("OPERATE");
|
||||
log2.setUsername("user1");
|
||||
log2.setModule("用户管理");
|
||||
|
||||
page.setRecords(Arrays.asList(log1, log2));
|
||||
page.setTotal(2);
|
||||
|
||||
when(logMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
|
||||
|
||||
LogQueryRequest request = new LogQueryRequest();
|
||||
request.setPageNum(1);
|
||||
request.setPageSize(10);
|
||||
|
||||
IPage<SysLog> result = logService.pageLogs(request);
|
||||
|
||||
assertEquals(2, result.getTotal());
|
||||
assertEquals("LOGIN", result.getRecords().get(0).getLogType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pageLogs_withFilters_shouldApplyAllConditions() {
|
||||
Page<SysLog> page = new Page<>(1, 10);
|
||||
page.setRecords(Arrays.asList());
|
||||
page.setTotal(0);
|
||||
|
||||
when(logMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
|
||||
|
||||
LogQueryRequest request = new LogQueryRequest();
|
||||
request.setLogType("OPERATE");
|
||||
request.setUsername("admin");
|
||||
request.setModule("系统管理");
|
||||
request.setStatus(1);
|
||||
request.setStartTime("2024-01-01");
|
||||
request.setEndTime("2024-12-31");
|
||||
|
||||
IPage<SysLog> result = logService.pageLogs(request);
|
||||
|
||||
assertNotNull(result);
|
||||
verify(logMapper).selectPage(any(Page.class), any(LambdaQueryWrapper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveLog_shouldInsert() {
|
||||
SysLog log = new SysLog();
|
||||
log.setLogType("LOGIN");
|
||||
log.setUsername("testuser");
|
||||
log.setIp("127.0.0.1");
|
||||
|
||||
logService.saveLog(log);
|
||||
|
||||
verify(logMapper).insert(log);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysRole;
|
||||
import com.water.system.entity.dto.RoleRequest;
|
||||
import com.water.system.mapper.SysRoleMapper;
|
||||
import com.water.system.service.impl.RoleServiceImpl;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RoleServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private SysRoleMapper roleMapper;
|
||||
|
||||
@InjectMocks
|
||||
private RoleServiceImpl roleService;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// ObjectMapper is final, inject manually via reflection or just let it be used
|
||||
}
|
||||
|
||||
@Test
|
||||
void listRoles_shouldReturnAllRoles() {
|
||||
SysRole admin = new SysRole();
|
||||
admin.setId(1L);
|
||||
admin.setRoleCode("SUPER_ADMIN");
|
||||
admin.setLevel(1);
|
||||
|
||||
SysRole user = new SysRole();
|
||||
user.setId(4L);
|
||||
user.setRoleCode("EMPLOYEE");
|
||||
user.setLevel(4);
|
||||
|
||||
when(roleMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(Arrays.asList(admin, user));
|
||||
|
||||
List<SysRole> roles = roleService.listRoles();
|
||||
|
||||
assertEquals(2, roles.size());
|
||||
assertEquals("SUPER_ADMIN", roles.get(0).getRoleCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createRole_shouldSucceed() {
|
||||
RoleRequest request = new RoleRequest();
|
||||
request.setRoleCode("TEST_ROLE");
|
||||
request.setRoleName("测试角色");
|
||||
request.setLevel(3);
|
||||
request.setPermissions(Arrays.asList("user:read", "user:write"));
|
||||
|
||||
when(roleMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(0L);
|
||||
when(roleMapper.insert(any(SysRole.class))).thenAnswer(inv -> {
|
||||
SysRole r = inv.getArgument(0);
|
||||
r.setId(10L);
|
||||
return 1;
|
||||
});
|
||||
|
||||
Long id = roleService.createRole(request);
|
||||
|
||||
assertEquals(10L, id);
|
||||
verify(roleMapper).insert(any(SysRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createRole_shouldFailOnDuplicateCode() {
|
||||
RoleRequest request = new RoleRequest();
|
||||
request.setRoleCode("SUPER_ADMIN");
|
||||
request.setRoleName("重复角色");
|
||||
request.setLevel(1);
|
||||
|
||||
when(roleMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(1L);
|
||||
|
||||
assertThrows(BusinessException.class, () -> roleService.createRole(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteRole_shouldFailForAdminRoles() {
|
||||
SysRole admin = new SysRole();
|
||||
admin.setId(1L);
|
||||
admin.setLevel(1);
|
||||
admin.setRoleCode("SUPER_ADMIN");
|
||||
|
||||
when(roleMapper.selectById(1L)).thenReturn(admin);
|
||||
|
||||
assertThrows(BusinessException.class, () -> roleService.deleteRole(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteRole_shouldSucceedForNormalRoles() {
|
||||
SysRole role = new SysRole();
|
||||
role.setId(5L);
|
||||
role.setLevel(4);
|
||||
role.setRoleCode("EMPLOYEE");
|
||||
|
||||
when(roleMapper.selectById(5L)).thenReturn(role);
|
||||
|
||||
assertDoesNotThrow(() -> roleService.deleteRole(5L));
|
||||
verify(roleMapper).deleteById(5L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysUser;
|
||||
import com.water.system.entity.dto.UserCreateRequest;
|
||||
import com.water.system.entity.dto.UserUpdateRequest;
|
||||
import com.water.system.mapper.SysUserMapper;
|
||||
import com.water.system.service.impl.UserServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.Arrays;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private SysUserMapper userMapper;
|
||||
|
||||
@InjectMocks
|
||||
private UserServiceImpl userService;
|
||||
|
||||
@Test
|
||||
void createUser_shouldSucceed() {
|
||||
UserCreateRequest request = new UserCreateRequest();
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("test123");
|
||||
request.setRealName("测试用户");
|
||||
request.setPhone("13800138000");
|
||||
|
||||
when(userMapper.selectByUsername("testuser")).thenReturn(null);
|
||||
when(userMapper.insert(any(SysUser.class))).thenAnswer(inv -> {
|
||||
SysUser u = inv.getArgument(0);
|
||||
u.setId(100L);
|
||||
return 1;
|
||||
});
|
||||
|
||||
Long id = userService.createUser(request);
|
||||
|
||||
assertEquals(100L, id);
|
||||
verify(userMapper).insert(any(SysUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createUser_shouldFailOnDuplicateUsername() {
|
||||
UserCreateRequest request = new UserCreateRequest();
|
||||
request.setUsername("admin");
|
||||
request.setPassword("pass");
|
||||
|
||||
SysUser existing = new SysUser();
|
||||
existing.setUsername("admin");
|
||||
when(userMapper.selectByUsername("admin")).thenReturn(existing);
|
||||
|
||||
assertThrows(BusinessException.class, () -> userService.createUser(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateStatus_shouldSucceed() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setStatus(1);
|
||||
when(userMapper.selectById(1L)).thenReturn(user);
|
||||
|
||||
userService.updateStatus(1L, 0);
|
||||
|
||||
verify(userMapper).updateById(argThat(u -> u.getStatus() == 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteUser_shouldFailForAdmin() {
|
||||
SysUser admin = new SysUser();
|
||||
admin.setId(1L);
|
||||
admin.setUsername("admin");
|
||||
when(userMapper.selectById(1L)).thenReturn(admin);
|
||||
|
||||
assertThrows(BusinessException.class, () -> userService.deleteUser(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resetPassword_shouldSucceed() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(5L);
|
||||
user.setUsername("testuser");
|
||||
when(userMapper.selectById(5L)).thenReturn(user);
|
||||
|
||||
assertDoesNotThrow(() -> userService.resetPassword(5L, "newPass123"));
|
||||
verify(userMapper).updateById(argThat(u -> u.getPassword() != null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void pageUsers_shouldReturnPagedResult() {
|
||||
Page<SysUser> page = new Page<>(1, 10);
|
||||
SysUser user1 = new SysUser();
|
||||
user1.setId(1L);
|
||||
user1.setUsername("user1");
|
||||
user1.setPassword("hashed");
|
||||
SysUser user2 = new SysUser();
|
||||
user2.setId(2L);
|
||||
user2.setUsername("user2");
|
||||
user2.setPassword("hashed");
|
||||
page.setRecords(Arrays.asList(user1, user2));
|
||||
page.setTotal(2);
|
||||
|
||||
when(userMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
|
||||
|
||||
IPage<SysUser> result = userService.pageUsers(1, 10, null, null, null);
|
||||
|
||||
assertEquals(2, result.getTotal());
|
||||
// 密码应被清除
|
||||
assertNull(result.getRecords().get(0).getPassword());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user