Pārlūkot izejas kodu

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向后兼容
bot_dev2 5 dienas atpakaļ
vecāks
revīzija
51c292f25b
43 mainītis faili ar 2199 papildinājumiem un 68 dzēšanām
  1. 59
    0
      wm-system/src/main/java/com/water/system/controller/DepartmentController.java
  2. 87
    0
      wm-system/src/main/java/com/water/system/controller/DictionaryController.java
  3. 32
    0
      wm-system/src/main/java/com/water/system/controller/LogController.java
  4. 66
    0
      wm-system/src/main/java/com/water/system/controller/RoleController.java
  5. 74
    0
      wm-system/src/main/java/com/water/system/controller/UserController.java
  6. 33
    0
      wm-system/src/main/java/com/water/system/entity/DataDictionary.java
  7. 42
    0
      wm-system/src/main/java/com/water/system/entity/DataDictionaryItem.java
  8. 42
    4
      wm-system/src/main/java/com/water/system/entity/SysDepartment.java
  9. 54
    7
      wm-system/src/main/java/com/water/system/entity/SysLog.java
  10. 42
    6
      wm-system/src/main/java/com/water/system/entity/SysRole.java
  11. 50
    8
      wm-system/src/main/java/com/water/system/entity/SysUser.java
  12. 20
    0
      wm-system/src/main/java/com/water/system/entity/dto/DeptRequest.java
  13. 25
    0
      wm-system/src/main/java/com/water/system/entity/dto/DictItemRequest.java
  14. 19
    0
      wm-system/src/main/java/com/water/system/entity/dto/DictRequest.java
  15. 22
    0
      wm-system/src/main/java/com/water/system/entity/dto/LogQueryRequest.java
  16. 14
    0
      wm-system/src/main/java/com/water/system/entity/dto/PageQuery.java
  17. 13
    0
      wm-system/src/main/java/com/water/system/entity/dto/PasswordResetRequest.java
  18. 27
    0
      wm-system/src/main/java/com/water/system/entity/dto/RoleRequest.java
  19. 22
    0
      wm-system/src/main/java/com/water/system/entity/dto/UserCreateRequest.java
  20. 15
    0
      wm-system/src/main/java/com/water/system/entity/dto/UserUpdateRequest.java
  21. 15
    0
      wm-system/src/main/java/com/water/system/mapper/DataDictionaryItemMapper.java
  22. 14
    0
      wm-system/src/main/java/com/water/system/mapper/DataDictionaryMapper.java
  23. 11
    1
      wm-system/src/main/java/com/water/system/mapper/SysDepartmentMapper.java
  24. 5
    1
      wm-system/src/main/java/com/water/system/mapper/SysLogMapper.java
  25. 15
    1
      wm-system/src/main/java/com/water/system/mapper/SysRoleMapper.java
  26. 10
    1
      wm-system/src/main/java/com/water/system/mapper/SysUserMapper.java
  27. 29
    0
      wm-system/src/main/java/com/water/system/service/DepartmentService.java
  28. 43
    0
      wm-system/src/main/java/com/water/system/service/DictionaryService.java
  29. 20
    0
      wm-system/src/main/java/com/water/system/service/LogService.java
  30. 35
    0
      wm-system/src/main/java/com/water/system/service/RoleService.java
  31. 48
    39
      wm-system/src/main/java/com/water/system/service/SysService.java
  32. 37
    0
      wm-system/src/main/java/com/water/system/service/UserService.java
  33. 102
    0
      wm-system/src/main/java/com/water/system/service/impl/DepartmentServiceImpl.java
  34. 116
    0
      wm-system/src/main/java/com/water/system/service/impl/DictionaryServiceImpl.java
  35. 62
    0
      wm-system/src/main/java/com/water/system/service/impl/LogServiceImpl.java
  36. 103
    0
      wm-system/src/main/java/com/water/system/service/impl/RoleServiceImpl.java
  37. 115
    0
      wm-system/src/main/java/com/water/system/service/impl/UserServiceImpl.java
  38. 136
    0
      wm-system/src/main/resources/db/V2__system_enhance.sql
  39. 93
    0
      wm-system/src/test/java/com/water/system/service/DepartmentServiceImplTest.java
  40. 104
    0
      wm-system/src/test/java/com/water/system/service/DictionaryServiceImplTest.java
  41. 93
    0
      wm-system/src/test/java/com/water/system/service/LogServiceImplTest.java
  42. 115
    0
      wm-system/src/test/java/com/water/system/service/RoleServiceImplTest.java
  43. 120
    0
      wm-system/src/test/java/com/water/system/service/UserServiceImplTest.java

+ 59
- 0
wm-system/src/main/java/com/water/system/controller/DepartmentController.java Parādīt failu

@@ -0,0 +1,59 @@
1
+package com.water.system.controller;
2
+
3
+import com.water.common.core.result.R;
4
+import com.water.system.entity.SysDepartment;
5
+import com.water.system.entity.dto.DeptRequest;
6
+import com.water.system.service.DepartmentService;
7
+import io.swagger.v3.oas.annotations.Operation;
8
+import io.swagger.v3.oas.annotations.tags.Tag;
9
+import jakarta.validation.Valid;
10
+import lombok.RequiredArgsConstructor;
11
+import org.springframework.web.bind.annotation.*;
12
+import java.util.List;
13
+
14
+@Tag(name = "部门管理")
15
+@RestController
16
+@RequestMapping("/api/system/dept")
17
+@RequiredArgsConstructor
18
+public class DepartmentController {
19
+
20
+    private final DepartmentService departmentService;
21
+
22
+    @Operation(summary = "获取部门树")
23
+    @GetMapping("/tree")
24
+    public R<List<SysDepartment>> tree() {
25
+        return R.ok(departmentService.getDeptTree());
26
+    }
27
+
28
+    @Operation(summary = "获取部门详情")
29
+    @GetMapping("/{id}")
30
+    public R<SysDepartment> getById(@PathVariable Long id) {
31
+        return R.ok(departmentService.getDeptById(id));
32
+    }
33
+
34
+    @Operation(summary = "创建部门")
35
+    @PostMapping
36
+    public R<Long> create(@Valid @RequestBody DeptRequest request) {
37
+        return R.ok(departmentService.createDept(request));
38
+    }
39
+
40
+    @Operation(summary = "更新部门")
41
+    @PutMapping("/{id}")
42
+    public R<String> update(@PathVariable Long id, @RequestBody DeptRequest request) {
43
+        departmentService.updateDept(id, request);
44
+        return R.ok("OK");
45
+    }
46
+
47
+    @Operation(summary = "删除部门")
48
+    @DeleteMapping("/{id}")
49
+    public R<String> delete(@PathVariable Long id) {
50
+        departmentService.deleteDept(id);
51
+        return R.ok("OK");
52
+    }
53
+
54
+    @Operation(summary = "获取子部门列表")
55
+    @GetMapping("/{id}/children")
56
+    public R<List<SysDepartment>> getChildren(@PathVariable Long id) {
57
+        return R.ok(departmentService.getChildDepts(id));
58
+    }
59
+}

+ 87
- 0
wm-system/src/main/java/com/water/system/controller/DictionaryController.java Parādīt failu

@@ -0,0 +1,87 @@
1
+package com.water.system.controller;
2
+
3
+import com.water.common.core.result.R;
4
+import com.water.system.entity.DataDictionary;
5
+import com.water.system.entity.DataDictionaryItem;
6
+import com.water.system.entity.dto.DictItemRequest;
7
+import com.water.system.entity.dto.DictRequest;
8
+import com.water.system.service.DictionaryService;
9
+import io.swagger.v3.oas.annotations.Operation;
10
+import io.swagger.v3.oas.annotations.tags.Tag;
11
+import jakarta.validation.Valid;
12
+import lombok.RequiredArgsConstructor;
13
+import org.springframework.web.bind.annotation.*;
14
+import java.util.List;
15
+
16
+@Tag(name = "数据字典管理")
17
+@RestController
18
+@RequestMapping("/api/system/dict")
19
+@RequiredArgsConstructor
20
+public class DictionaryController {
21
+
22
+    private final DictionaryService dictionaryService;
23
+
24
+    @Operation(summary = "获取字典列表")
25
+    @GetMapping("/list")
26
+    public R<List<DataDictionary>> list(@RequestParam(required = false) String keyword) {
27
+        return R.ok(dictionaryService.listDictionaries(keyword));
28
+    }
29
+
30
+    @Operation(summary = "获取字典详情")
31
+    @GetMapping("/{id}")
32
+    public R<DataDictionary> getById(@PathVariable Long id) {
33
+        return R.ok(dictionaryService.getDictionaryById(id));
34
+    }
35
+
36
+    @Operation(summary = "创建字典")
37
+    @PostMapping
38
+    public R<Long> create(@Valid @RequestBody DictRequest request) {
39
+        return R.ok(dictionaryService.createDictionary(request));
40
+    }
41
+
42
+    @Operation(summary = "更新字典")
43
+    @PutMapping("/{id}")
44
+    public R<String> update(@PathVariable Long id, @RequestBody DictRequest request) {
45
+        dictionaryService.updateDictionary(id, request);
46
+        return R.ok("OK");
47
+    }
48
+
49
+    @Operation(summary = "删除字典")
50
+    @DeleteMapping("/{id}")
51
+    public R<String> delete(@PathVariable Long id) {
52
+        dictionaryService.deleteDictionary(id);
53
+        return R.ok("OK");
54
+    }
55
+
56
+    @Operation(summary = "获取字典项列表(按字典ID)")
57
+    @GetMapping("/{dictId}/items")
58
+    public R<List<DataDictionaryItem>> listItems(@PathVariable Long dictId) {
59
+        return R.ok(dictionaryService.listItemsByDictId(dictId));
60
+    }
61
+
62
+    @Operation(summary = "根据字典编码获取字典项")
63
+    @GetMapping("/code/{dictCode}/items")
64
+    public R<List<DataDictionaryItem>> listItemsByCode(@PathVariable String dictCode) {
65
+        return R.ok(dictionaryService.listItemsByDictCode(dictCode));
66
+    }
67
+
68
+    @Operation(summary = "创建字典项")
69
+    @PostMapping("/item")
70
+    public R<Long> createItem(@Valid @RequestBody DictItemRequest request) {
71
+        return R.ok(dictionaryService.createItem(request));
72
+    }
73
+
74
+    @Operation(summary = "更新字典项")
75
+    @PutMapping("/item/{id}")
76
+    public R<String> updateItem(@PathVariable Long id, @RequestBody DictItemRequest request) {
77
+        dictionaryService.updateItem(id, request);
78
+        return R.ok("OK");
79
+    }
80
+
81
+    @Operation(summary = "删除字典项")
82
+    @DeleteMapping("/item/{id}")
83
+    public R<String> deleteItem(@PathVariable Long id) {
84
+        dictionaryService.deleteItem(id);
85
+        return R.ok("OK");
86
+    }
87
+}

+ 32
- 0
wm-system/src/main/java/com/water/system/controller/LogController.java Parādīt failu

@@ -0,0 +1,32 @@
1
+package com.water.system.controller;
2
+
3
+import com.baomidou.mybatisplus.core.metadata.IPage;
4
+import com.water.common.core.result.R;
5
+import com.water.system.entity.SysLog;
6
+import com.water.system.entity.dto.LogQueryRequest;
7
+import com.water.system.service.LogService;
8
+import io.swagger.v3.oas.annotations.Operation;
9
+import io.swagger.v3.oas.annotations.tags.Tag;
10
+import lombok.RequiredArgsConstructor;
11
+import org.springframework.web.bind.annotation.*;
12
+
13
+@Tag(name = "日志管理")
14
+@RestController
15
+@RequestMapping("/api/system/log")
16
+@RequiredArgsConstructor
17
+public class LogController {
18
+
19
+    private final LogService logService;
20
+
21
+    @Operation(summary = "分页查询日志")
22
+    @GetMapping("/page")
23
+    public R<IPage<SysLog>> page(LogQueryRequest request) {
24
+        return R.ok(logService.pageLogs(request));
25
+    }
26
+
27
+    @Operation(summary = "获取日志详情")
28
+    @GetMapping("/{id}")
29
+    public R<SysLog> getById(@PathVariable Long id) {
30
+        return R.ok(logService.getLogById(id));
31
+    }
32
+}

+ 66
- 0
wm-system/src/main/java/com/water/system/controller/RoleController.java Parādīt failu

@@ -0,0 +1,66 @@
1
+package com.water.system.controller;
2
+
3
+import com.water.common.core.result.R;
4
+import com.water.system.entity.SysRole;
5
+import com.water.system.entity.dto.RoleRequest;
6
+import com.water.system.service.RoleService;
7
+import io.swagger.v3.oas.annotations.Operation;
8
+import io.swagger.v3.oas.annotations.tags.Tag;
9
+import jakarta.validation.Valid;
10
+import lombok.RequiredArgsConstructor;
11
+import org.springframework.web.bind.annotation.*;
12
+import java.util.List;
13
+
14
+@Tag(name = "角色管理")
15
+@RestController
16
+@RequestMapping("/api/system/role")
17
+@RequiredArgsConstructor
18
+public class RoleController {
19
+
20
+    private final RoleService roleService;
21
+
22
+    @Operation(summary = "获取角色列表")
23
+    @GetMapping("/list")
24
+    public R<List<SysRole>> list() {
25
+        return R.ok(roleService.listRoles());
26
+    }
27
+
28
+    @Operation(summary = "获取角色详情")
29
+    @GetMapping("/{id}")
30
+    public R<SysRole> getById(@PathVariable Long id) {
31
+        return R.ok(roleService.getRoleById(id));
32
+    }
33
+
34
+    @Operation(summary = "创建角色")
35
+    @PostMapping
36
+    public R<Long> create(@Valid @RequestBody RoleRequest request) {
37
+        return R.ok(roleService.createRole(request));
38
+    }
39
+
40
+    @Operation(summary = "更新角色")
41
+    @PutMapping("/{id}")
42
+    public R<String> update(@PathVariable Long id, @Valid @RequestBody RoleRequest request) {
43
+        roleService.updateRole(id, request);
44
+        return R.ok("OK");
45
+    }
46
+
47
+    @Operation(summary = "删除角色")
48
+    @DeleteMapping("/{id}")
49
+    public R<String> delete(@PathVariable Long id) {
50
+        roleService.deleteRole(id);
51
+        return R.ok("OK");
52
+    }
53
+
54
+    @Operation(summary = "分配角色权限")
55
+    @PutMapping("/{id}/permissions")
56
+    public R<String> assignPermissions(@PathVariable Long id, @RequestBody List<String> permissions) {
57
+        roleService.assignPermissions(id, permissions);
58
+        return R.ok("OK");
59
+    }
60
+
61
+    @Operation(summary = "获取用户角色")
62
+    @GetMapping("/user/{userId}")
63
+    public R<List<SysRole>> getByUserId(@PathVariable Long userId) {
64
+        return R.ok(roleService.getRolesByUserId(userId));
65
+    }
66
+}

+ 74
- 0
wm-system/src/main/java/com/water/system/controller/UserController.java Parādīt failu

@@ -0,0 +1,74 @@
1
+package com.water.system.controller;
2
+
3
+import com.baomidou.mybatisplus.core.metadata.IPage;
4
+import com.water.common.core.result.R;
5
+import com.water.system.entity.SysUser;
6
+import com.water.system.entity.dto.PasswordResetRequest;
7
+import com.water.system.entity.dto.UserCreateRequest;
8
+import com.water.system.entity.dto.UserUpdateRequest;
9
+import com.water.system.service.UserService;
10
+import io.swagger.v3.oas.annotations.Operation;
11
+import io.swagger.v3.oas.annotations.tags.Tag;
12
+import jakarta.validation.Valid;
13
+import lombok.RequiredArgsConstructor;
14
+import org.springframework.web.bind.annotation.*;
15
+
16
+@Tag(name = "用户管理")
17
+@RestController
18
+@RequestMapping("/api/system/user")
19
+@RequiredArgsConstructor
20
+public class UserController {
21
+
22
+    private final UserService userService;
23
+
24
+    @Operation(summary = "分页查询用户列表")
25
+    @GetMapping("/page")
26
+    public R<IPage<SysUser>> page(
27
+            @RequestParam(defaultValue = "1") Integer pageNum,
28
+            @RequestParam(defaultValue = "10") Integer pageSize,
29
+            @RequestParam(required = false) String keyword,
30
+            @RequestParam(required = false) Integer status,
31
+            @RequestParam(required = false) Long deptId) {
32
+        return R.ok(userService.pageUsers(pageNum, pageSize, keyword, status, deptId));
33
+    }
34
+
35
+    @Operation(summary = "获取用户详情")
36
+    @GetMapping("/{id}")
37
+    public R<SysUser> getById(@PathVariable Long id) {
38
+        return R.ok(userService.getUserById(id));
39
+    }
40
+
41
+    @Operation(summary = "创建用户")
42
+    @PostMapping
43
+    public R<Long> create(@Valid @RequestBody UserCreateRequest request) {
44
+        return R.ok(userService.createUser(request));
45
+    }
46
+
47
+    @Operation(summary = "更新用户信息")
48
+    @PutMapping("/{id}")
49
+    public R<String> update(@PathVariable Long id, @RequestBody UserUpdateRequest request) {
50
+        userService.updateUser(id, request);
51
+        return R.ok("OK");
52
+    }
53
+
54
+    @Operation(summary = "删除用户")
55
+    @DeleteMapping("/{id}")
56
+    public R<String> delete(@PathVariable Long id) {
57
+        userService.deleteUser(id);
58
+        return R.ok("OK");
59
+    }
60
+
61
+    @Operation(summary = "停用/启用用户")
62
+    @PutMapping("/{id}/status")
63
+    public R<String> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
64
+        userService.updateStatus(id, status);
65
+        return R.ok("OK");
66
+    }
67
+
68
+    @Operation(summary = "重置密码")
69
+    @PutMapping("/{id}/reset-password")
70
+    public R<String> resetPassword(@PathVariable Long id, @Valid @RequestBody PasswordResetRequest request) {
71
+        userService.resetPassword(id, request.getNewPassword());
72
+        return R.ok("OK");
73
+    }
74
+}

+ 33
- 0
wm-system/src/main/java/com/water/system/entity/DataDictionary.java Parādīt failu

@@ -0,0 +1,33 @@
1
+package com.water.system.entity;
2
+
3
+import com.baomidou.mybatisplus.annotation.*;
4
+import lombok.Data;
5
+import java.time.LocalDateTime;
6
+
7
+/**
8
+ * 数据字典实体
9
+ */
10
+@Data
11
+@TableName("sys_data_dictionary")
12
+public class DataDictionary {
13
+    @TableId(type = IdType.AUTO)
14
+    private Long id;
15
+
16
+    /** 字典编码(唯一) */
17
+    private String dictCode;
18
+
19
+    /** 字典名称 */
20
+    private String dictName;
21
+
22
+    /** 字典描述 */
23
+    private String description;
24
+
25
+    /** 状态: 0-停用 1-正常 */
26
+    private Integer status;
27
+
28
+    @TableField(fill = FieldFill.INSERT)
29
+    private LocalDateTime createdTime;
30
+
31
+    @TableField(fill = FieldFill.INSERT_UPDATE)
32
+    private LocalDateTime updatedTime;
33
+}

+ 42
- 0
wm-system/src/main/java/com/water/system/entity/DataDictionaryItem.java Parādīt failu

@@ -0,0 +1,42 @@
1
+package com.water.system.entity;
2
+
3
+import com.baomidou.mybatisplus.annotation.*;
4
+import lombok.Data;
5
+import java.time.LocalDateTime;
6
+
7
+/**
8
+ * 数据字典项实体
9
+ */
10
+@Data
11
+@TableName("sys_data_dictionary_item")
12
+public class DataDictionaryItem {
13
+    @TableId(type = IdType.AUTO)
14
+    private Long id;
15
+
16
+    /** 字典ID */
17
+    private Long dictId;
18
+
19
+    /** 字典项编码 */
20
+    private String itemCode;
21
+
22
+    /** 字典项名称 */
23
+    private String itemName;
24
+
25
+    /** 字典项值 */
26
+    private String itemValue;
27
+
28
+    /** 排序号 */
29
+    private Integer sort;
30
+
31
+    /** 状态: 0-停用 1-正常 */
32
+    private Integer status;
33
+
34
+    /** 备注 */
35
+    private String remark;
36
+
37
+    @TableField(fill = FieldFill.INSERT)
38
+    private LocalDateTime createdTime;
39
+
40
+    @TableField(fill = FieldFill.INSERT_UPDATE)
41
+    private LocalDateTime updatedTime;
42
+}

+ 42
- 4
wm-system/src/main/java/com/water/system/entity/SysDepartment.java Parādīt failu

@@ -1,9 +1,47 @@
1 1
 package com.water.system.entity;
2
+
2 3
 import com.baomidou.mybatisplus.annotation.*;
3 4
 import lombok.Data;
4
-@Data @TableName("sys_department")
5
+import java.time.LocalDateTime;
6
+import java.util.List;
7
+
8
+/**
9
+ * 部门实体(树结构)
10
+ */
11
+@Data
12
+@TableName("sys_department")
5 13
 public class SysDepartment {
6
-    @TableId(type = IdType.AUTO) private Long id;
7
-    private Long parentId; private String deptName, leader, phone;
8
-    private Integer sort, status;
14
+    @TableId(type = IdType.AUTO)
15
+    private Long id;
16
+
17
+    /** 父部门ID, 0为根节点 */
18
+    private Long parentId;
19
+
20
+    /** 部门名称 */
21
+    private String deptName;
22
+
23
+    /** 负责人 */
24
+    private String leader;
25
+
26
+    /** 联系电话 */
27
+    private String phone;
28
+
29
+    /** 排序号 */
30
+    private Integer sort;
31
+
32
+    /** 状态: 0-停用 1-正常 */
33
+    private Integer status;
34
+
35
+    /** 层级路径(如: 0,1,5) */
36
+    private String ancestors;
37
+
38
+    @TableField(fill = FieldFill.INSERT)
39
+    private LocalDateTime createdTime;
40
+
41
+    @TableField(fill = FieldFill.INSERT_UPDATE)
42
+    private LocalDateTime updatedTime;
43
+
44
+    /** 子部门列表(非数据库字段) */
45
+    @TableField(exist = false)
46
+    private List<SysDepartment> children;
9 47
 }

+ 54
- 7
wm-system/src/main/java/com/water/system/entity/SysLog.java Parādīt failu

@@ -1,11 +1,58 @@
1 1
 package com.water.system.entity;
2
+
2 3
 import com.baomidou.mybatisplus.annotation.*;
3
-import lombok.Data; import java.time.LocalDateTime;
4
-@Data @TableName("sys_log")
4
+import lombok.Data;
5
+import java.time.LocalDateTime;
6
+
7
+/**
8
+ * 系统日志实体
9
+ * 支持: 登录日志(LOGIN) / 操作日志(OPERATE) / 异常日志(EXCEPTION)
10
+ */
11
+@Data
12
+@TableName("sys_log")
5 13
 public class SysLog {
6
-    @TableId(type = IdType.AUTO) private Long id;
7
-    private Long userId; private String username, logType, module, action;
8
-    private String requestUrl, requestMethod, requestParams, responseResult;
9
-    private String ip; private Long duration;
10
-    @TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
14
+    @TableId(type = IdType.AUTO)
15
+    private Long id;
16
+
17
+    /** 操作用户ID */
18
+    private Long userId;
19
+
20
+    /** 操作用户名 */
21
+    private String username;
22
+
23
+    /** 日志类型: LOGIN-登录 OPERATE-操作 EXCEPTION-异常 */
24
+    private String logType;
25
+
26
+    /** 操作模块 */
27
+    private String module;
28
+
29
+    /** 操作描述 */
30
+    private String action;
31
+
32
+    /** 请求URL */
33
+    private String requestUrl;
34
+
35
+    /** 请求方法 */
36
+    private String requestMethod;
37
+
38
+    /** 请求参数 */
39
+    private String requestParams;
40
+
41
+    /** 响应结果 */
42
+    private String responseResult;
43
+
44
+    /** 操作IP */
45
+    private String ip;
46
+
47
+    /** 耗时(毫秒) */
48
+    private Long duration;
49
+
50
+    /** 状态: 0-失败 1-成功 */
51
+    private Integer status;
52
+
53
+    /** 错误信息 */
54
+    private String errorMsg;
55
+
56
+    @TableField(fill = FieldFill.INSERT)
57
+    private LocalDateTime createdTime;
11 58
 }

+ 42
- 6
wm-system/src/main/java/com/water/system/entity/SysRole.java Parādīt failu

@@ -1,10 +1,46 @@
1 1
 package com.water.system.entity;
2
+
2 3
 import com.baomidou.mybatisplus.annotation.*;
3
-import lombok.Data; import java.time.LocalDateTime;
4
-@Data @TableName("sys_role")
4
+import lombok.Data;
5
+import java.time.LocalDateTime;
6
+
7
+/**
8
+ * 系统角色实体
9
+ * 支持5级角色: 1-超级管理员 2-系统管理员 3-部门经理 4-普通员工 5-访客
10
+ */
11
+@Data
12
+@TableName("sys_role")
5 13
 public class SysRole {
6
-    @TableId(type = IdType.AUTO) private Long id;
7
-    private String roleCode, roleName, description;
8
-    private Integer status, sort;
9
-    @TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
14
+    @TableId(type = IdType.AUTO)
15
+    private Long id;
16
+
17
+    /** 角色编码 */
18
+    private String roleCode;
19
+
20
+    /** 角色名称 */
21
+    private String roleName;
22
+
23
+    /** 角色描述 */
24
+    private String description;
25
+
26
+    /** 角色级别: 1-超级管理员 2-系统管理员 3-部门经理 4-普通员工 5-访客 */
27
+    private Integer level;
28
+
29
+    /** 数据权限范围: ALL-全部 DEPT-本部门 DEPT_BELOW-本部门及以下 SELF-仅本人 */
30
+    private String dataScope;
31
+
32
+    /** 权限配置(JSON数组) */
33
+    private String permissions;
34
+
35
+    /** 状态: 0-停用 1-正常 */
36
+    private Integer status;
37
+
38
+    /** 排序号 */
39
+    private Integer sort;
40
+
41
+    @TableField(fill = FieldFill.INSERT)
42
+    private LocalDateTime createdTime;
43
+
44
+    @TableField(fill = FieldFill.INSERT_UPDATE)
45
+    private LocalDateTime updatedTime;
10 46
 }

+ 50
- 8
wm-system/src/main/java/com/water/system/entity/SysUser.java Parādīt failu

@@ -1,12 +1,54 @@
1 1
 package com.water.system.entity;
2
+
2 3
 import com.baomidou.mybatisplus.annotation.*;
3
-import lombok.Data; import java.time.LocalDateTime;
4
-@Data @TableName("sys_user")
4
+import lombok.Data;
5
+import java.time.LocalDateTime;
6
+
7
+/**
8
+ * 系统用户实体
9
+ */
10
+@Data
11
+@TableName("sys_user")
5 12
 public class SysUser {
6
-    @TableId(type = IdType.AUTO) private Long id;
7
-    private String username, password, realName, phone, email;
8
-    private Long deptId; private String avatar;
9
-    private Integer status; // 0停用 1正常
10
-    @TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
11
-    @TableField(fill=FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
13
+    @TableId(type = IdType.AUTO)
14
+    private Long id;
15
+
16
+    /** 用户名 */
17
+    private String username;
18
+
19
+    /** 密码(加密) */
20
+    private String password;
21
+
22
+    /** 真实姓名 */
23
+    private String realName;
24
+
25
+    /** 手机号 */
26
+    private String phone;
27
+
28
+    /** 邮箱 */
29
+    private String email;
30
+
31
+    /** 部门ID */
32
+    private Long deptId;
33
+
34
+    /** 角色ID */
35
+    private Long roleId;
36
+
37
+    /** 头像URL */
38
+    private String avatar;
39
+
40
+    /** 状态: 0-停用 1-正常 */
41
+    private Integer status;
42
+
43
+    /** 最后登录时间 */
44
+    private LocalDateTime lastLoginTime;
45
+
46
+    /** 最后登录IP */
47
+    private String lastLoginIp;
48
+
49
+    @TableField(fill = FieldFill.INSERT)
50
+    private LocalDateTime createdTime;
51
+
52
+    @TableField(fill = FieldFill.INSERT_UPDATE)
53
+    private LocalDateTime updatedTime;
12 54
 }

+ 20
- 0
wm-system/src/main/java/com/water/system/entity/dto/DeptRequest.java Parādīt failu

@@ -0,0 +1,20 @@
1
+package com.water.system.entity.dto;
2
+
3
+import jakarta.validation.constraints.NotBlank;
4
+import lombok.Data;
5
+
6
+/**
7
+ * 部门创建/更新请求
8
+ */
9
+@Data
10
+public class DeptRequest {
11
+    private Long parentId;
12
+
13
+    @NotBlank(message = "部门名称不能为空")
14
+    private String deptName;
15
+
16
+    private String leader;
17
+    private String phone;
18
+    private Integer sort;
19
+    private Integer status;
20
+}

+ 25
- 0
wm-system/src/main/java/com/water/system/entity/dto/DictItemRequest.java Parādīt failu

@@ -0,0 +1,25 @@
1
+package com.water.system.entity.dto;
2
+
3
+import jakarta.validation.constraints.NotBlank;
4
+import jakarta.validation.constraints.NotNull;
5
+import lombok.Data;
6
+
7
+/**
8
+ * 字典项创建/更新请求
9
+ */
10
+@Data
11
+public class DictItemRequest {
12
+    @NotNull(message = "字典ID不能为空")
13
+    private Long dictId;
14
+
15
+    @NotBlank(message = "字典项编码不能为空")
16
+    private String itemCode;
17
+
18
+    @NotBlank(message = "字典项名称不能为空")
19
+    private String itemName;
20
+
21
+    private String itemValue;
22
+    private Integer sort;
23
+    private Integer status;
24
+    private String remark;
25
+}

+ 19
- 0
wm-system/src/main/java/com/water/system/entity/dto/DictRequest.java Parādīt failu

@@ -0,0 +1,19 @@
1
+package com.water.system.entity.dto;
2
+
3
+import jakarta.validation.constraints.NotBlank;
4
+import lombok.Data;
5
+
6
+/**
7
+ * 字典创建/更新请求
8
+ */
9
+@Data
10
+public class DictRequest {
11
+    @NotBlank(message = "字典编码不能为空")
12
+    private String dictCode;
13
+
14
+    @NotBlank(message = "字典名称不能为空")
15
+    private String dictName;
16
+
17
+    private String description;
18
+    private Integer status;
19
+}

+ 22
- 0
wm-system/src/main/java/com/water/system/entity/dto/LogQueryRequest.java Parādīt failu

@@ -0,0 +1,22 @@
1
+package com.water.system.entity.dto;
2
+
3
+import lombok.Data;
4
+
5
+/**
6
+ * 日志查询请求
7
+ */
8
+@Data
9
+public class LogQueryRequest extends PageQuery {
10
+    /** 日志类型: LOGIN/OPERATE/EXCEPTION */
11
+    private String logType;
12
+    /** 用户名(模糊) */
13
+    private String username;
14
+    /** 操作模块 */
15
+    private String module;
16
+    /** 开始时间(yyyy-MM-dd) */
17
+    private String startTime;
18
+    /** 结束时间(yyyy-MM-dd) */
19
+    private String endTime;
20
+    /** 状态: 0-失败 1-成功 */
21
+    private Integer status;
22
+}

+ 14
- 0
wm-system/src/main/java/com/water/system/entity/dto/PageQuery.java Parādīt failu

@@ -0,0 +1,14 @@
1
+package com.water.system.entity.dto;
2
+
3
+import lombok.Data;
4
+
5
+/**
6
+ * 分页查询基础参数
7
+ */
8
+@Data
9
+public class PageQuery {
10
+    /** 当前页码, 默认1 */
11
+    private Integer pageNum = 1;
12
+    /** 每页条数, 默认10 */
13
+    private Integer pageSize = 10;
14
+}

+ 13
- 0
wm-system/src/main/java/com/water/system/entity/dto/PasswordResetRequest.java Parādīt failu

@@ -0,0 +1,13 @@
1
+package com.water.system.entity.dto;
2
+
3
+import jakarta.validation.constraints.NotBlank;
4
+import lombok.Data;
5
+
6
+/**
7
+ * 密码重置请求
8
+ */
9
+@Data
10
+public class PasswordResetRequest {
11
+    @NotBlank(message = "新密码不能为空")
12
+    private String newPassword;
13
+}

+ 27
- 0
wm-system/src/main/java/com/water/system/entity/dto/RoleRequest.java Parādīt failu

@@ -0,0 +1,27 @@
1
+package com.water.system.entity.dto;
2
+
3
+import jakarta.validation.constraints.NotBlank;
4
+import jakarta.validation.constraints.NotNull;
5
+import lombok.Data;
6
+import java.util.List;
7
+
8
+/**
9
+ * 角色创建/更新请求
10
+ */
11
+@Data
12
+public class RoleRequest {
13
+    @NotBlank(message = "角色编码不能为空")
14
+    private String roleCode;
15
+
16
+    @NotBlank(message = "角色名称不能为空")
17
+    private String roleName;
18
+
19
+    private String description;
20
+
21
+    @NotNull(message = "角色级别不能为空")
22
+    private Integer level;
23
+
24
+    private String dataScope;
25
+    private List<String> permissions;
26
+    private Integer sort;
27
+}

+ 22
- 0
wm-system/src/main/java/com/water/system/entity/dto/UserCreateRequest.java Parādīt failu

@@ -0,0 +1,22 @@
1
+package com.water.system.entity.dto;
2
+
3
+import jakarta.validation.constraints.NotBlank;
4
+import lombok.Data;
5
+
6
+/**
7
+ * 用户创建请求
8
+ */
9
+@Data
10
+public class UserCreateRequest {
11
+    @NotBlank(message = "用户名不能为空")
12
+    private String username;
13
+
14
+    @NotBlank(message = "密码不能为空")
15
+    private String password;
16
+
17
+    private String realName;
18
+    private String phone;
19
+    private String email;
20
+    private Long deptId;
21
+    private Long roleId;
22
+}

+ 15
- 0
wm-system/src/main/java/com/water/system/entity/dto/UserUpdateRequest.java Parādīt failu

@@ -0,0 +1,15 @@
1
+package com.water.system.entity.dto;
2
+
3
+import lombok.Data;
4
+
5
+/**
6
+ * 用户更新请求
7
+ */
8
+@Data
9
+public class UserUpdateRequest {
10
+    private String realName;
11
+    private String phone;
12
+    private String email;
13
+    private Long deptId;
14
+    private Long roleId;
15
+}

+ 15
- 0
wm-system/src/main/java/com/water/system/mapper/DataDictionaryItemMapper.java Parādīt failu

@@ -0,0 +1,15 @@
1
+package com.water.system.mapper;
2
+
3
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4
+import com.water.system.entity.DataDictionaryItem;
5
+import org.apache.ibatis.annotations.Mapper;
6
+import org.apache.ibatis.annotations.Param;
7
+import org.apache.ibatis.annotations.Select;
8
+import java.util.List;
9
+
10
+@Mapper
11
+public interface DataDictionaryItemMapper extends BaseMapper<DataDictionaryItem> {
12
+
13
+    @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")
14
+    List<DataDictionaryItem> selectByDictCode(@Param("dictCode") String dictCode);
15
+}

+ 14
- 0
wm-system/src/main/java/com/water/system/mapper/DataDictionaryMapper.java Parādīt failu

@@ -0,0 +1,14 @@
1
+package com.water.system.mapper;
2
+
3
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4
+import com.water.system.entity.DataDictionary;
5
+import org.apache.ibatis.annotations.Mapper;
6
+import org.apache.ibatis.annotations.Param;
7
+import org.apache.ibatis.annotations.Select;
8
+
9
+@Mapper
10
+public interface DataDictionaryMapper extends BaseMapper<DataDictionary> {
11
+
12
+    @Select("SELECT * FROM sys_data_dictionary WHERE dict_code = #{dictCode} AND deleted = 0")
13
+    DataDictionary selectByDictCode(@Param("dictCode") String dictCode);
14
+}

+ 11
- 1
wm-system/src/main/java/com/water/system/mapper/SysDepartmentMapper.java Parādīt failu

@@ -1,5 +1,15 @@
1 1
 package com.water.system.mapper;
2
+
2 3
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
3 4
 import com.water.system.entity.SysDepartment;
4 5
 import org.apache.ibatis.annotations.Mapper;
5
-@Mapper public interface SysDepartmentMapper extends BaseMapper<SysDepartment> {}
6
+import org.apache.ibatis.annotations.Param;
7
+import org.apache.ibatis.annotations.Select;
8
+import java.util.List;
9
+
10
+@Mapper
11
+public interface SysDepartmentMapper extends BaseMapper<SysDepartment> {
12
+
13
+    @Select("SELECT * FROM sys_department WHERE ancestors LIKE CONCAT(#{ancestorPath}, '%') AND deleted = 0 ORDER BY sort")
14
+    List<SysDepartment> selectByAncestorPath(@Param("ancestorPath") String ancestorPath);
15
+}

+ 5
- 1
wm-system/src/main/java/com/water/system/mapper/SysLogMapper.java Parādīt failu

@@ -1,5 +1,9 @@
1 1
 package com.water.system.mapper;
2
+
2 3
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
3 4
 import com.water.system.entity.SysLog;
4 5
 import org.apache.ibatis.annotations.Mapper;
5
-@Mapper public interface SysLogMapper extends BaseMapper<SysLog> {}
6
+
7
+@Mapper
8
+public interface SysLogMapper extends BaseMapper<SysLog> {
9
+}

+ 15
- 1
wm-system/src/main/java/com/water/system/mapper/SysRoleMapper.java Parādīt failu

@@ -1,5 +1,19 @@
1 1
 package com.water.system.mapper;
2
+
2 3
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
3 4
 import com.water.system.entity.SysRole;
4 5
 import org.apache.ibatis.annotations.Mapper;
5
-@Mapper public interface SysRoleMapper extends BaseMapper<SysRole> {}
6
+import org.apache.ibatis.annotations.Param;
7
+import org.apache.ibatis.annotations.Select;
8
+import org.apache.ibatis.annotations.Update;
9
+import java.util.List;
10
+
11
+@Mapper
12
+public interface SysRoleMapper extends BaseMapper<SysRole> {
13
+
14
+    @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")
15
+    List<SysRole> selectRolesByUserId(@Param("userId") Long userId);
16
+
17
+    @Select("SELECT * FROM sys_role WHERE level <= #{level} AND deleted = 0 ORDER BY level, sort")
18
+    List<SysRole> selectRolesByMaxLevel(@Param("level") Integer level);
19
+}

+ 10
- 1
wm-system/src/main/java/com/water/system/mapper/SysUserMapper.java Parādīt failu

@@ -1,5 +1,14 @@
1 1
 package com.water.system.mapper;
2
+
2 3
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
3 4
 import com.water.system.entity.SysUser;
4 5
 import org.apache.ibatis.annotations.Mapper;
5
-@Mapper public interface SysUserMapper extends BaseMapper<SysUser> {}
6
+import org.apache.ibatis.annotations.Param;
7
+import org.apache.ibatis.annotations.Select;
8
+
9
+@Mapper
10
+public interface SysUserMapper extends BaseMapper<SysUser> {
11
+
12
+    @Select("SELECT * FROM sys_user WHERE username = #{username} AND deleted = 0")
13
+    SysUser selectByUsername(@Param("username") String username);
14
+}

+ 29
- 0
wm-system/src/main/java/com/water/system/service/DepartmentService.java Parādīt failu

@@ -0,0 +1,29 @@
1
+package com.water.system.service;
2
+
3
+import com.water.system.entity.SysDepartment;
4
+import com.water.system.entity.dto.DeptRequest;
5
+import java.util.List;
6
+
7
+/**
8
+ * 部门管理服务接口
9
+ */
10
+public interface DepartmentService {
11
+
12
+    /** 获取部门树 */
13
+    List<SysDepartment> getDeptTree();
14
+
15
+    /** 获取部门详情 */
16
+    SysDepartment getDeptById(Long id);
17
+
18
+    /** 创建部门 */
19
+    Long createDept(DeptRequest request);
20
+
21
+    /** 更新部门 */
22
+    void updateDept(Long id, DeptRequest request);
23
+
24
+    /** 删除部门 */
25
+    void deleteDept(Long id);
26
+
27
+    /** 获取所有子部门 */
28
+    List<SysDepartment> getChildDepts(Long parentId);
29
+}

+ 43
- 0
wm-system/src/main/java/com/water/system/service/DictionaryService.java Parādīt failu

@@ -0,0 +1,43 @@
1
+package com.water.system.service;
2
+
3
+import com.water.system.entity.DataDictionary;
4
+import com.water.system.entity.DataDictionaryItem;
5
+import com.water.system.entity.dto.DictItemRequest;
6
+import com.water.system.entity.dto.DictRequest;
7
+import java.util.List;
8
+
9
+/**
10
+ * 数据字典服务接口
11
+ */
12
+public interface DictionaryService {
13
+
14
+    /** 查询所有字典 */
15
+    List<DataDictionary> listDictionaries(String keyword);
16
+
17
+    /** 获取字典详情 */
18
+    DataDictionary getDictionaryById(Long id);
19
+
20
+    /** 创建字典 */
21
+    Long createDictionary(DictRequest request);
22
+
23
+    /** 更新字典 */
24
+    void updateDictionary(Long id, DictRequest request);
25
+
26
+    /** 删除字典 */
27
+    void deleteDictionary(Long id);
28
+
29
+    /** 根据字典ID查询字典项 */
30
+    List<DataDictionaryItem> listItemsByDictId(Long dictId);
31
+
32
+    /** 根据字典编码查询字典项 */
33
+    List<DataDictionaryItem> listItemsByDictCode(String dictCode);
34
+
35
+    /** 创建字典项 */
36
+    Long createItem(DictItemRequest request);
37
+
38
+    /** 更新字典项 */
39
+    void updateItem(Long id, DictItemRequest request);
40
+
41
+    /** 删除字典项 */
42
+    void deleteItem(Long id);
43
+}

+ 20
- 0
wm-system/src/main/java/com/water/system/service/LogService.java Parādīt failu

@@ -0,0 +1,20 @@
1
+package com.water.system.service;
2
+
3
+import com.baomidou.mybatisplus.core.metadata.IPage;
4
+import com.water.system.entity.SysLog;
5
+import com.water.system.entity.dto.LogQueryRequest;
6
+
7
+/**
8
+ * 日志查询服务接口
9
+ */
10
+public interface LogService {
11
+
12
+    /** 分页查询日志 */
13
+    IPage<SysLog> pageLogs(LogQueryRequest request);
14
+
15
+    /** 获取日志详情 */
16
+    SysLog getLogById(Long id);
17
+
18
+    /** 记录日志 */
19
+    void saveLog(SysLog log);
20
+}

+ 35
- 0
wm-system/src/main/java/com/water/system/service/RoleService.java Parādīt failu

@@ -0,0 +1,35 @@
1
+package com.water.system.service;
2
+
3
+import com.water.system.entity.SysRole;
4
+import com.water.system.entity.dto.RoleRequest;
5
+import java.util.List;
6
+
7
+/**
8
+ * 角色管理服务接口
9
+ */
10
+public interface RoleService {
11
+
12
+    /** 查询所有角色 */
13
+    List<SysRole> listRoles();
14
+
15
+    /** 根据ID获取角色详情 */
16
+    SysRole getRoleById(Long id);
17
+
18
+    /** 创建角色 */
19
+    Long createRole(RoleRequest request);
20
+
21
+    /** 更新角色 */
22
+    void updateRole(Long id, RoleRequest request);
23
+
24
+    /** 删除角色 */
25
+    void deleteRole(Long id);
26
+
27
+    /** 分配权限 */
28
+    void assignPermissions(Long id, List<String> permissions);
29
+
30
+    /** 根据用户ID查询角色 */
31
+    List<SysRole> getRolesByUserId(Long userId);
32
+
33
+    /** 根据最大级别查询角色 */
34
+    List<SysRole> getRolesByMaxLevel(Integer level);
35
+}

+ 48
- 39
wm-system/src/main/java/com/water/system/service/SysService.java Parādīt failu

@@ -1,67 +1,76 @@
1 1
 package com.water.system.service;
2
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
2
+
3 3
 import com.water.system.entity.*;
4 4
 import com.water.system.mapper.*;
5
+import com.water.system.entity.dto.*;
5 6
 import lombok.RequiredArgsConstructor;
6 7
 import org.springframework.stereotype.Service;
7 8
 import java.util.*;
8
-@Service @RequiredArgsConstructor
9
+
10
+/**
11
+ * 系统管理兼容服务 - 保持向后兼容,内部委托给新的专用服务
12
+ */
13
+@Service
14
+@RequiredArgsConstructor
9 15
 public class SysService {
10
-    private final SysRoleMapper roleMapper;
11
-    private final SysUserMapper userMapper;
16
+    private final RoleService roleService;
17
+    private final UserService userService;
18
+    private final DepartmentService departmentService;
19
+    private final LogService logService;
12 20
     private final SysMenuMapper menuMapper;
13
-    private final SysDepartmentMapper deptMapper;
14
-    private final SysLogMapper logMapper;
15 21
 
16 22
     // Roles
17
-    public List<SysRole> listRoles() { return roleMapper.selectList(null); }
23
+    public List<SysRole> listRoles() { return roleService.listRoles(); }
18 24
     public Long createRole(Map<String,Object> req) {
19
-        SysRole r = new SysRole();
20
-        r.setRoleCode((String)req.get("roleCode"));
21
-        r.setRoleName((String)req.get("roleName"));
22
-        r.setDescription((String)req.get("description"));
23
-        r.setStatus(1); r.setSort(0);
24
-        roleMapper.insert(r);
25
-        return r.getId();
25
+        RoleRequest r = new RoleRequest();
26
+        r.setRoleCode((String) req.get("roleCode"));
27
+        r.setRoleName((String) req.get("roleName"));
28
+        r.setDescription((String) req.get("description"));
29
+        r.setLevel(req.get("level") != null ? ((Number) req.get("level")).intValue() : 4);
30
+        r.setDataScope((String) req.get("dataScope"));
31
+        return roleService.createRole(r);
26 32
     }
33
+
27 34
     // Users
28 35
     public List<SysUser> listUsers(Integer status) {
29
-        return userMapper.selectList(new LambdaQueryWrapper<SysUser>()
30
-            .eq(status != null, SysUser::getStatus, status));
36
+        return userService.pageUsers(1, 1000, null, status, null).getRecords();
31 37
     }
32 38
     public Long createUser(Map<String,Object> req) {
33
-        SysUser u = new SysUser();
34
-        u.setUsername((String)req.get("username"));
35
-        u.setPassword((String)req.get("password"));
36
-        u.setRealName((String)req.get("realName"));
37
-        u.setPhone((String)req.get("phone"));
38
-        u.setStatus(1);
39
-        userMapper.insert(u);
40
-        return u.getId();
39
+        UserCreateRequest r = new UserCreateRequest();
40
+        r.setUsername((String) req.get("username"));
41
+        r.setPassword((String) req.get("password"));
42
+        r.setRealName((String) req.get("realName"));
43
+        r.setPhone((String) req.get("phone"));
44
+        r.setEmail((String) req.get("email"));
45
+        if (req.get("deptId") != null) r.setDeptId(((Number) req.get("deptId")).longValue());
46
+        if (req.get("roleId") != null) r.setRoleId(((Number) req.get("roleId")).longValue());
47
+        return userService.createUser(r);
41 48
     }
42 49
     public void updateUserStatus(Long id, int status) {
43
-        SysUser u = userMapper.selectById(id);
44
-        if (u == null) throw new RuntimeException("用户不存在");
45
-        u.setStatus(status);
46
-        userMapper.updateById(u);
50
+        userService.updateStatus(id, status);
47 51
     }
52
+
48 53
     // Menus
49 54
     public List<SysMenu> listMenus() { return menuMapper.selectList(null); }
55
+
50 56
     // Departments
51
-    public List<SysDepartment> listDepts() { return deptMapper.selectList(null); }
57
+    public List<SysDepartment> listDepts() { return departmentService.getDeptTree(); }
52 58
     public Long createDept(Map<String,Object> req) {
53
-        SysDepartment d = new SysDepartment();
54
-        d.setDeptName((String)req.get("deptName"));
55
-        d.setLeader((String)req.get("leader"));
56
-        d.setSort(0); d.setStatus(1);
57
-        deptMapper.insert(d);
58
-        return d.getId();
59
+        DeptRequest r = new DeptRequest();
60
+        r.setDeptName((String) req.get("deptName"));
61
+        r.setLeader((String) req.get("leader"));
62
+        r.setPhone((String) req.get("phone"));
63
+        if (req.get("parentId") != null) r.setParentId(((Number) req.get("parentId")).longValue());
64
+        return departmentService.createDept(r);
59 65
     }
66
+
60 67
     // Logs
61 68
     public List<SysLog> listLogs(String logType, String username) {
62
-        LambdaQueryWrapper<SysLog> w = new LambdaQueryWrapper<>();
63
-        if (logType != null) w.eq(SysLog::getLogType, logType);
64
-        if (username != null) w.like(SysLog::getUsername, username);
65
-        return logMapper.selectList(w.orderByDesc(SysLog::getCreatedTime).last("LIMIT 100"));
69
+        LogQueryRequest r = new LogQueryRequest();
70
+        r.setLogType(logType);
71
+        r.setUsername(username);
72
+        r.setPageNum(1);
73
+        r.setPageSize(100);
74
+        return logService.pageLogs(r).getRecords();
66 75
     }
67 76
 }

+ 37
- 0
wm-system/src/main/java/com/water/system/service/UserService.java Parādīt failu

@@ -0,0 +1,37 @@
1
+package com.water.system.service;
2
+
3
+import com.baomidou.mybatisplus.core.metadata.IPage;
4
+import com.water.system.entity.SysUser;
5
+import com.water.system.entity.dto.UserCreateRequest;
6
+import com.water.system.entity.dto.UserUpdateRequest;
7
+import java.util.List;
8
+
9
+/**
10
+ * 用户管理服务接口
11
+ */
12
+public interface UserService {
13
+
14
+    /** 分页查询用户列表 */
15
+    IPage<SysUser> pageUsers(Integer pageNum, Integer pageSize, String keyword, Integer status, Long deptId);
16
+
17
+    /** 获取用户详情 */
18
+    SysUser getUserById(Long id);
19
+
20
+    /** 创建用户 */
21
+    Long createUser(UserCreateRequest request);
22
+
23
+    /** 更新用户 */
24
+    void updateUser(Long id, UserUpdateRequest request);
25
+
26
+    /** 删除用户 */
27
+    void deleteUser(Long id);
28
+
29
+    /** 停用/启用用户 */
30
+    void updateStatus(Long id, Integer status);
31
+
32
+    /** 重置密码 */
33
+    void resetPassword(Long id, String newPassword);
34
+
35
+    /** 根据用户名查询 */
36
+    SysUser getByUsername(String username);
37
+}

+ 102
- 0
wm-system/src/main/java/com/water/system/service/impl/DepartmentServiceImpl.java Parādīt failu

@@ -0,0 +1,102 @@
1
+package com.water.system.service.impl;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.water.common.core.exception.BusinessException;
5
+import com.water.system.entity.SysDepartment;
6
+import com.water.system.entity.dto.DeptRequest;
7
+import com.water.system.mapper.SysDepartmentMapper;
8
+import com.water.system.service.DepartmentService;
9
+import lombok.RequiredArgsConstructor;
10
+import org.springframework.stereotype.Service;
11
+import java.util.ArrayList;
12
+import java.util.List;
13
+import java.util.Map;
14
+import java.util.stream.Collectors;
15
+
16
+@Service
17
+@RequiredArgsConstructor
18
+public class DepartmentServiceImpl implements DepartmentService {
19
+
20
+    private final SysDepartmentMapper deptMapper;
21
+
22
+    @Override
23
+    public List<SysDepartment> getDeptTree() {
24
+        List<SysDepartment> allDepts = deptMapper.selectList(
25
+            new LambdaQueryWrapper<SysDepartment>().orderByAsc(SysDepartment::getSort)
26
+        );
27
+        return buildTree(allDepts, 0L);
28
+    }
29
+
30
+    @Override
31
+    public SysDepartment getDeptById(Long id) {
32
+        SysDepartment dept = deptMapper.selectById(id);
33
+        if (dept == null) throw new BusinessException("部门不存在");
34
+        return dept;
35
+    }
36
+
37
+    @Override
38
+    public Long createDept(DeptRequest request) {
39
+        SysDepartment dept = new SysDepartment();
40
+        dept.setDeptName(request.getDeptName());
41
+        dept.setLeader(request.getLeader());
42
+        dept.setPhone(request.getPhone());
43
+        dept.setSort(request.getSort() != null ? request.getSort() : 0);
44
+        dept.setStatus(request.getStatus() != null ? request.getStatus() : 1);
45
+
46
+        Long parentId = request.getParentId() != null ? request.getParentId() : 0L;
47
+        dept.setParentId(parentId);
48
+
49
+        // 设置层级路径
50
+        if (parentId > 0) {
51
+            SysDepartment parent = getDeptById(parentId);
52
+            String ancestors = parent.getAncestors() != null ? parent.getAncestors() + "," + parentId : String.valueOf(parentId);
53
+            dept.setAncestors(ancestors);
54
+        } else {
55
+            dept.setAncestors("0");
56
+        }
57
+
58
+        deptMapper.insert(dept);
59
+        return dept.getId();
60
+    }
61
+
62
+    @Override
63
+    public void updateDept(Long id, DeptRequest request) {
64
+        SysDepartment dept = getDeptById(id);
65
+        if (request.getDeptName() != null) dept.setDeptName(request.getDeptName());
66
+        if (request.getLeader() != null) dept.setLeader(request.getLeader());
67
+        if (request.getPhone() != null) dept.setPhone(request.getPhone());
68
+        if (request.getSort() != null) dept.setSort(request.getSort());
69
+        if (request.getStatus() != null) dept.setStatus(request.getStatus());
70
+        deptMapper.updateById(dept);
71
+    }
72
+
73
+    @Override
74
+    public void deleteDept(Long id) {
75
+        // 检查是否有子部门
76
+        long childCount = deptMapper.selectCount(
77
+            new LambdaQueryWrapper<SysDepartment>().eq(SysDepartment::getParentId, id)
78
+        );
79
+        if (childCount > 0) {
80
+            throw new BusinessException("该部门下有子部门,不允许删除");
81
+        }
82
+        deptMapper.deleteById(id);
83
+    }
84
+
85
+    @Override
86
+    public List<SysDepartment> getChildDepts(Long parentId) {
87
+        SysDepartment parent = getDeptById(parentId);
88
+        String ancestorPath = parent.getAncestors() != null ? parent.getAncestors() + "," + parentId : String.valueOf(parentId);
89
+        return deptMapper.selectByAncestorPath(ancestorPath);
90
+    }
91
+
92
+    private List<SysDepartment> buildTree(List<SysDepartment> depts, Long parentId) {
93
+        Map<Long, List<SysDepartment>> grouped = depts.stream()
94
+            .collect(Collectors.groupingBy(SysDepartment::getParentId));
95
+
96
+        List<SysDepartment> roots = grouped.getOrDefault(parentId, new ArrayList<>());
97
+        for (SysDepartment root : roots) {
98
+            root.setChildren(buildTree(depts, root.getId()));
99
+        }
100
+        return roots;
101
+    }
102
+}

+ 116
- 0
wm-system/src/main/java/com/water/system/service/impl/DictionaryServiceImpl.java Parādīt failu

@@ -0,0 +1,116 @@
1
+package com.water.system.service.impl;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.water.common.core.exception.BusinessException;
5
+import com.water.system.entity.DataDictionary;
6
+import com.water.system.entity.DataDictionaryItem;
7
+import com.water.system.entity.dto.DictItemRequest;
8
+import com.water.system.entity.dto.DictRequest;
9
+import com.water.system.mapper.DataDictionaryMapper;
10
+import com.water.system.mapper.DataDictionaryItemMapper;
11
+import com.water.system.service.DictionaryService;
12
+import lombok.RequiredArgsConstructor;
13
+import org.springframework.stereotype.Service;
14
+import java.util.List;
15
+
16
+@Service
17
+@RequiredArgsConstructor
18
+public class DictionaryServiceImpl implements DictionaryService {
19
+
20
+    private final DataDictionaryMapper dictMapper;
21
+    private final DataDictionaryItemMapper itemMapper;
22
+
23
+    @Override
24
+    public List<DataDictionary> listDictionaries(String keyword) {
25
+        LambdaQueryWrapper<DataDictionary> wrapper = new LambdaQueryWrapper<>();
26
+        if (keyword != null && !keyword.isBlank()) {
27
+            wrapper.like(DataDictionary::getDictCode, keyword)
28
+                .or().like(DataDictionary::getDictName, keyword);
29
+        }
30
+        wrapper.orderByAsc(DataDictionary::getDictCode);
31
+        return dictMapper.selectList(wrapper);
32
+    }
33
+
34
+    @Override
35
+    public DataDictionary getDictionaryById(Long id) {
36
+        DataDictionary dict = dictMapper.selectById(id);
37
+        if (dict == null) throw new BusinessException("字典不存在");
38
+        return dict;
39
+    }
40
+
41
+    @Override
42
+    public Long createDictionary(DictRequest request) {
43
+        DataDictionary existing = dictMapper.selectByDictCode(request.getDictCode());
44
+        if (existing != null) throw new BusinessException("字典编码已存在");
45
+
46
+        DataDictionary dict = new DataDictionary();
47
+        dict.setDictCode(request.getDictCode());
48
+        dict.setDictName(request.getDictName());
49
+        dict.setDescription(request.getDescription());
50
+        dict.setStatus(request.getStatus() != null ? request.getStatus() : 1);
51
+        dictMapper.insert(dict);
52
+        return dict.getId();
53
+    }
54
+
55
+    @Override
56
+    public void updateDictionary(Long id, DictRequest request) {
57
+        DataDictionary dict = getDictionaryById(id);
58
+        if (request.getDictName() != null) dict.setDictName(request.getDictName());
59
+        if (request.getDescription() != null) dict.setDescription(request.getDescription());
60
+        if (request.getStatus() != null) dict.setStatus(request.getStatus());
61
+        dictMapper.updateById(dict);
62
+    }
63
+
64
+    @Override
65
+    public void deleteDictionary(Long id) {
66
+        // 同时删除字典项
67
+        itemMapper.delete(new LambdaQueryWrapper<DataDictionaryItem>()
68
+            .eq(DataDictionaryItem::getDictId, id));
69
+        dictMapper.deleteById(id);
70
+    }
71
+
72
+    @Override
73
+    public List<DataDictionaryItem> listItemsByDictId(Long dictId) {
74
+        return itemMapper.selectList(
75
+            new LambdaQueryWrapper<DataDictionaryItem>()
76
+                .eq(DataDictionaryItem::getDictId, dictId)
77
+                .orderByAsc(DataDictionaryItem::getSort)
78
+        );
79
+    }
80
+
81
+    @Override
82
+    public List<DataDictionaryItem> listItemsByDictCode(String dictCode) {
83
+        return itemMapper.selectByDictCode(dictCode);
84
+    }
85
+
86
+    @Override
87
+    public Long createItem(DictItemRequest request) {
88
+        DataDictionaryItem item = new DataDictionaryItem();
89
+        item.setDictId(request.getDictId());
90
+        item.setItemCode(request.getItemCode());
91
+        item.setItemName(request.getItemName());
92
+        item.setItemValue(request.getItemValue());
93
+        item.setSort(request.getSort() != null ? request.getSort() : 0);
94
+        item.setStatus(request.getStatus() != null ? request.getStatus() : 1);
95
+        item.setRemark(request.getRemark());
96
+        itemMapper.insert(item);
97
+        return item.getId();
98
+    }
99
+
100
+    @Override
101
+    public void updateItem(Long id, DictItemRequest request) {
102
+        DataDictionaryItem item = itemMapper.selectById(id);
103
+        if (item == null) throw new BusinessException("字典项不存在");
104
+        if (request.getItemName() != null) item.setItemName(request.getItemName());
105
+        if (request.getItemValue() != null) item.setItemValue(request.getItemValue());
106
+        if (request.getSort() != null) item.setSort(request.getSort());
107
+        if (request.getStatus() != null) item.setStatus(request.getStatus());
108
+        if (request.getRemark() != null) item.setRemark(request.getRemark());
109
+        itemMapper.updateById(item);
110
+    }
111
+
112
+    @Override
113
+    public void deleteItem(Long id) {
114
+        itemMapper.deleteById(id);
115
+    }
116
+}

+ 62
- 0
wm-system/src/main/java/com/water/system/service/impl/LogServiceImpl.java Parādīt failu

@@ -0,0 +1,62 @@
1
+package com.water.system.service.impl;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.baomidou.mybatisplus.core.metadata.IPage;
5
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
6
+import com.water.system.entity.SysLog;
7
+import com.water.system.entity.dto.LogQueryRequest;
8
+import com.water.system.mapper.SysLogMapper;
9
+import com.water.system.service.LogService;
10
+import lombok.RequiredArgsConstructor;
11
+import org.springframework.stereotype.Service;
12
+import java.time.LocalDate;
13
+import java.time.LocalDateTime;
14
+import java.time.format.DateTimeFormatter;
15
+
16
+@Service
17
+@RequiredArgsConstructor
18
+public class LogServiceImpl implements LogService {
19
+
20
+    private final SysLogMapper logMapper;
21
+    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
22
+
23
+    @Override
24
+    public IPage<SysLog> pageLogs(LogQueryRequest request) {
25
+        Page<SysLog> page = new Page<>(request.getPageNum(), request.getPageSize());
26
+        LambdaQueryWrapper<SysLog> wrapper = new LambdaQueryWrapper<>();
27
+
28
+        if (request.getLogType() != null && !request.getLogType().isBlank()) {
29
+            wrapper.eq(SysLog::getLogType, request.getLogType());
30
+        }
31
+        if (request.getUsername() != null && !request.getUsername().isBlank()) {
32
+            wrapper.like(SysLog::getUsername, request.getUsername());
33
+        }
34
+        if (request.getModule() != null && !request.getModule().isBlank()) {
35
+            wrapper.eq(SysLog::getModule, request.getModule());
36
+        }
37
+        if (request.getStatus() != null) {
38
+            wrapper.eq(SysLog::getStatus, request.getStatus());
39
+        }
40
+        if (request.getStartTime() != null && !request.getStartTime().isBlank()) {
41
+            LocalDateTime start = LocalDate.parse(request.getStartTime(), DATE_FMT).atStartOfDay();
42
+            wrapper.ge(SysLog::getCreatedTime, start);
43
+        }
44
+        if (request.getEndTime() != null && !request.getEndTime().isBlank()) {
45
+            LocalDateTime end = LocalDate.parse(request.getEndTime(), DATE_FMT).plusDays(1).atStartOfDay();
46
+            wrapper.lt(SysLog::getCreatedTime, end);
47
+        }
48
+
49
+        wrapper.orderByDesc(SysLog::getCreatedTime);
50
+        return logMapper.selectPage(page, wrapper);
51
+    }
52
+
53
+    @Override
54
+    public SysLog getLogById(Long id) {
55
+        return logMapper.selectById(id);
56
+    }
57
+
58
+    @Override
59
+    public void saveLog(SysLog log) {
60
+        logMapper.insert(log);
61
+    }
62
+}

+ 103
- 0
wm-system/src/main/java/com/water/system/service/impl/RoleServiceImpl.java Parādīt failu

@@ -0,0 +1,103 @@
1
+package com.water.system.service.impl;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.water.common.core.exception.BusinessException;
5
+import com.water.system.entity.SysRole;
6
+import com.water.system.entity.dto.RoleRequest;
7
+import com.water.system.mapper.SysRoleMapper;
8
+import com.water.system.service.RoleService;
9
+import com.fasterxml.jackson.core.JsonProcessingException;
10
+import com.fasterxml.jackson.databind.ObjectMapper;
11
+import lombok.RequiredArgsConstructor;
12
+import org.springframework.stereotype.Service;
13
+import java.util.List;
14
+
15
+@Service
16
+@RequiredArgsConstructor
17
+public class RoleServiceImpl implements RoleService {
18
+
19
+    private final SysRoleMapper roleMapper;
20
+    private final ObjectMapper objectMapper;
21
+
22
+    @Override
23
+    public List<SysRole> listRoles() {
24
+        return roleMapper.selectList(
25
+            new LambdaQueryWrapper<SysRole>().orderByAsc(SysRole::getLevel, SysRole::getSort)
26
+        );
27
+    }
28
+
29
+    @Override
30
+    public SysRole getRoleById(Long id) {
31
+        SysRole role = roleMapper.selectById(id);
32
+        if (role == null) throw new BusinessException("角色不存在");
33
+        return role;
34
+    }
35
+
36
+    @Override
37
+    public Long createRole(RoleRequest request) {
38
+        // 检查编码唯一
39
+        long count = roleMapper.selectCount(
40
+            new LambdaQueryWrapper<SysRole>().eq(SysRole::getRoleCode, request.getRoleCode())
41
+        );
42
+        if (count > 0) throw new BusinessException("角色编码已存在");
43
+
44
+        SysRole role = new SysRole();
45
+        role.setRoleCode(request.getRoleCode());
46
+        role.setRoleName(request.getRoleName());
47
+        role.setDescription(request.getDescription());
48
+        role.setLevel(request.getLevel());
49
+        role.setDataScope(request.getDataScope() != null ? request.getDataScope() : "SELF");
50
+        role.setPermissions(toJson(request.getPermissions()));
51
+        role.setStatus(1);
52
+        role.setSort(request.getSort() != null ? request.getSort() : 0);
53
+        roleMapper.insert(role);
54
+        return role.getId();
55
+    }
56
+
57
+    @Override
58
+    public void updateRole(Long id, RoleRequest request) {
59
+        SysRole role = getRoleById(id);
60
+        role.setRoleName(request.getRoleName());
61
+        role.setDescription(request.getDescription());
62
+        role.setLevel(request.getLevel());
63
+        if (request.getDataScope() != null) role.setDataScope(request.getDataScope());
64
+        if (request.getPermissions() != null) role.setPermissions(toJson(request.getPermissions()));
65
+        if (request.getSort() != null) role.setSort(request.getSort());
66
+        roleMapper.updateById(role);
67
+    }
68
+
69
+    @Override
70
+    public void deleteRole(Long id) {
71
+        SysRole role = getRoleById(id);
72
+        if (role.getLevel() != null && role.getLevel() <= 2) {
73
+            throw new BusinessException("超级管理员和系统管理员角色不允许删除");
74
+        }
75
+        roleMapper.deleteById(id);
76
+    }
77
+
78
+    @Override
79
+    public void assignPermissions(Long id, List<String> permissions) {
80
+        SysRole role = getRoleById(id);
81
+        role.setPermissions(toJson(permissions));
82
+        roleMapper.updateById(role);
83
+    }
84
+
85
+    @Override
86
+    public List<SysRole> getRolesByUserId(Long userId) {
87
+        return roleMapper.selectRolesByUserId(userId);
88
+    }
89
+
90
+    @Override
91
+    public List<SysRole> getRolesByMaxLevel(Integer level) {
92
+        return roleMapper.selectRolesByMaxLevel(level);
93
+    }
94
+
95
+    private String toJson(Object obj) {
96
+        if (obj == null) return null;
97
+        try {
98
+            return objectMapper.writeValueAsString(obj);
99
+        } catch (JsonProcessingException e) {
100
+            throw new BusinessException("JSON序列化失败: " + e.getMessage());
101
+        }
102
+    }
103
+}

+ 115
- 0
wm-system/src/main/java/com/water/system/service/impl/UserServiceImpl.java Parādīt failu

@@ -0,0 +1,115 @@
1
+package com.water.system.service.impl;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.baomidou.mybatisplus.core.metadata.IPage;
5
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
6
+import com.water.common.core.exception.BusinessException;
7
+import com.water.system.entity.SysUser;
8
+import com.water.system.entity.dto.UserCreateRequest;
9
+import com.water.system.entity.dto.UserUpdateRequest;
10
+import com.water.system.mapper.SysUserMapper;
11
+import com.water.system.service.UserService;
12
+import lombok.RequiredArgsConstructor;
13
+import org.springframework.stereotype.Service;
14
+import org.springframework.util.DigestUtils;
15
+import java.nio.charset.StandardCharsets;
16
+
17
+@Service
18
+@RequiredArgsConstructor
19
+public class UserServiceImpl implements UserService {
20
+
21
+    private final SysUserMapper userMapper;
22
+
23
+    @Override
24
+    public IPage<SysUser> pageUsers(Integer pageNum, Integer pageSize, String keyword, Integer status, Long deptId) {
25
+        Page<SysUser> page = new Page<>(pageNum, pageSize);
26
+        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
27
+        if (keyword != null && !keyword.isBlank()) {
28
+            wrapper.and(w -> w.like(SysUser::getUsername, keyword)
29
+                .or().like(SysUser::getRealName, keyword)
30
+                .or().like(SysUser::getPhone, keyword));
31
+        }
32
+        if (status != null) wrapper.eq(SysUser::getStatus, status);
33
+        if (deptId != null) wrapper.eq(SysUser::getDeptId, deptId);
34
+        wrapper.orderByDesc(SysUser::getCreatedTime);
35
+
36
+        IPage<SysUser> result = userMapper.selectPage(page, wrapper);
37
+        // 清除密码字段
38
+        result.getRecords().forEach(u -> u.setPassword(null));
39
+        return result;
40
+    }
41
+
42
+    @Override
43
+    public SysUser getUserById(Long id) {
44
+        SysUser user = userMapper.selectById(id);
45
+        if (user == null) throw new BusinessException("用户不存在");
46
+        user.setPassword(null);
47
+        return user;
48
+    }
49
+
50
+    @Override
51
+    public Long createUser(UserCreateRequest request) {
52
+        // 检查用户名唯一
53
+        SysUser existing = userMapper.selectByUsername(request.getUsername());
54
+        if (existing != null) throw new BusinessException("用户名已存在");
55
+
56
+        SysUser user = new SysUser();
57
+        user.setUsername(request.getUsername());
58
+        user.setPassword(encryptPassword(request.getPassword()));
59
+        user.setRealName(request.getRealName());
60
+        user.setPhone(request.getPhone());
61
+        user.setEmail(request.getEmail());
62
+        user.setDeptId(request.getDeptId());
63
+        user.setRoleId(request.getRoleId());
64
+        user.setStatus(1);
65
+        userMapper.insert(user);
66
+        return user.getId();
67
+    }
68
+
69
+    @Override
70
+    public void updateUser(Long id, UserUpdateRequest request) {
71
+        SysUser user = userMapper.selectById(id);
72
+        if (user == null) throw new BusinessException("用户不存在");
73
+        if (request.getRealName() != null) user.setRealName(request.getRealName());
74
+        if (request.getPhone() != null) user.setPhone(request.getPhone());
75
+        if (request.getEmail() != null) user.setEmail(request.getEmail());
76
+        if (request.getDeptId() != null) user.setDeptId(request.getDeptId());
77
+        if (request.getRoleId() != null) user.setRoleId(request.getRoleId());
78
+        userMapper.updateById(user);
79
+    }
80
+
81
+    @Override
82
+    public void deleteUser(Long id) {
83
+        SysUser user = userMapper.selectById(id);
84
+        if (user == null) throw new BusinessException("用户不存在");
85
+        if ("admin".equals(user.getUsername())) {
86
+            throw new BusinessException("系统管理员账号不允许删除");
87
+        }
88
+        userMapper.deleteById(id);
89
+    }
90
+
91
+    @Override
92
+    public void updateStatus(Long id, Integer status) {
93
+        SysUser user = userMapper.selectById(id);
94
+        if (user == null) throw new BusinessException("用户不存在");
95
+        user.setStatus(status);
96
+        userMapper.updateById(user);
97
+    }
98
+
99
+    @Override
100
+    public void resetPassword(Long id, String newPassword) {
101
+        SysUser user = userMapper.selectById(id);
102
+        if (user == null) throw new BusinessException("用户不存在");
103
+        user.setPassword(encryptPassword(newPassword));
104
+        userMapper.updateById(user);
105
+    }
106
+
107
+    @Override
108
+    public SysUser getByUsername(String username) {
109
+        return userMapper.selectByUsername(username);
110
+    }
111
+
112
+    private String encryptPassword(String password) {
113
+        return DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
114
+    }
115
+}

+ 136
- 0
wm-system/src/main/resources/db/V2__system_enhance.sql Parādīt failu

@@ -0,0 +1,136 @@
1
+-- V2__system_enhance.sql
2
+-- 系统管理增强: 角色/用户/部门/日志/字典
3
+
4
+-- 1. 增强 sys_role 表: 新增 level, data_scope, permissions, updated_time 字段
5
+ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS level INT DEFAULT 4;
6
+ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS data_scope VARCHAR(20) DEFAULT 'SELF';
7
+ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS permissions TEXT;
8
+ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS updated_time TIMESTAMP DEFAULT NOW();
9
+ALTER TABLE sys_role ADD COLUMN IF NOT EXISTS deleted INT DEFAULT 0;
10
+
11
+COMMENT ON COLUMN sys_role.level IS '角色级别: 1-超级管理员 2-系统管理员 3-部门经理 4-普通员工 5-访客';
12
+COMMENT ON COLUMN sys_role.data_scope IS '数据权限: ALL/DEPT/DEPT_BELOW/SELF';
13
+COMMENT ON COLUMN sys_role.permissions IS '权限配置(JSON数组)';
14
+
15
+-- 2. 增强 sys_user 表: 新增 role_id, last_login_time, last_login_ip, deleted
16
+ALTER TABLE sys_user ADD COLUMN IF NOT EXISTS role_id BIGINT;
17
+ALTER TABLE sys_user ADD COLUMN IF NOT EXISTS last_login_time TIMESTAMP;
18
+ALTER TABLE sys_user ADD COLUMN IF NOT EXISTS last_login_ip VARCHAR(50);
19
+ALTER TABLE sys_user ADD COLUMN IF NOT EXISTS deleted INT DEFAULT 0;
20
+
21
+-- 3. 增强 sys_department 表: 新增 ancestors, created_time, updated_time, deleted
22
+ALTER TABLE sys_department ADD COLUMN IF NOT EXISTS ancestors VARCHAR(500) DEFAULT '0';
23
+ALTER TABLE sys_department ADD COLUMN IF NOT EXISTS created_time TIMESTAMP DEFAULT NOW();
24
+ALTER TABLE sys_department ADD COLUMN IF NOT EXISTS updated_time TIMESTAMP DEFAULT NOW();
25
+ALTER TABLE sys_department ADD COLUMN IF NOT EXISTS deleted INT DEFAULT 0;
26
+
27
+COMMENT ON COLUMN sys_department.ancestors IS '层级路径(如: 0,1,5)';
28
+
29
+-- 4. 增强 sys_log 表: 新增 status, error_msg
30
+ALTER TABLE sys_log ADD COLUMN IF NOT EXISTS status INT DEFAULT 1;
31
+ALTER TABLE sys_log ADD COLUMN IF NOT EXISTS error_msg TEXT;
32
+
33
+COMMENT ON COLUMN sys_log.log_type IS '日志类型: LOGIN/OPERATE/EXCEPTION';
34
+COMMENT ON COLUMN sys_log.status IS '状态: 0-失败 1-成功';
35
+
36
+-- 5. 新增数据字典表
37
+CREATE TABLE IF NOT EXISTS sys_data_dictionary (
38
+    id BIGSERIAL PRIMARY KEY,
39
+    dict_code VARCHAR(100) UNIQUE NOT NULL,
40
+    dict_name VARCHAR(200) NOT NULL,
41
+    description VARCHAR(500),
42
+    status INT DEFAULT 1,
43
+    deleted INT DEFAULT 0,
44
+    created_time TIMESTAMP DEFAULT NOW(),
45
+    updated_time TIMESTAMP DEFAULT NOW()
46
+);
47
+
48
+COMMENT ON TABLE sys_data_dictionary IS '数据字典表';
49
+COMMENT ON COLUMN sys_data_dictionary.dict_code IS '字典编码(唯一)';
50
+COMMENT ON COLUMN sys_data_dictionary.dict_name IS '字典名称';
51
+
52
+-- 6. 新增数据字典项表
53
+CREATE TABLE IF NOT EXISTS sys_data_dictionary_item (
54
+    id BIGSERIAL PRIMARY KEY,
55
+    dict_id BIGINT NOT NULL,
56
+    item_code VARCHAR(100) NOT NULL,
57
+    item_name VARCHAR(200) NOT NULL,
58
+    item_value VARCHAR(500),
59
+    sort INT DEFAULT 0,
60
+    status INT DEFAULT 1,
61
+    remark VARCHAR(500),
62
+    deleted INT DEFAULT 0,
63
+    created_time TIMESTAMP DEFAULT NOW(),
64
+    updated_time TIMESTAMP DEFAULT NOW()
65
+);
66
+
67
+COMMENT ON TABLE sys_data_dictionary_item IS '数据字典项表';
68
+COMMENT ON COLUMN sys_data_dictionary_item.dict_id IS '所属字典ID';
69
+COMMENT ON COLUMN sys_data_dictionary_item.item_code IS '字典项编码';
70
+COMMENT ON COLUMN sys_data_dictionary_item.item_name IS '字典项名称';
71
+COMMENT ON COLUMN sys_data_dictionary_item.item_value IS '字典项值';
72
+
73
+-- 索引
74
+CREATE INDEX IF NOT EXISTS idx_dict_item_dict_id ON sys_data_dictionary_item(dict_id);
75
+CREATE INDEX IF NOT EXISTS idx_dict_item_code ON sys_data_dictionary_item(item_code);
76
+CREATE INDEX IF NOT EXISTS idx_sys_user_dept_id ON sys_user(dept_id);
77
+CREATE INDEX IF NOT EXISTS idx_sys_user_role_id ON sys_user(role_id);
78
+CREATE INDEX IF NOT EXISTS idx_sys_user_status ON sys_user(status);
79
+CREATE INDEX IF NOT EXISTS idx_sys_role_level ON sys_role(level);
80
+CREATE INDEX IF NOT EXISTS idx_sys_dept_parent_id ON sys_department(parent_id);
81
+CREATE INDEX IF NOT EXISTS idx_sys_dept_ancestors ON sys_department(ancestors);
82
+CREATE INDEX IF NOT EXISTS idx_sys_log_type ON sys_log(log_type);
83
+CREATE INDEX IF NOT EXISTS idx_sys_log_user ON sys_log(username);
84
+CREATE INDEX IF NOT EXISTS idx_sys_log_created ON sys_log(created_time);
85
+CREATE INDEX IF NOT EXISTS idx_sys_log_module ON sys_log(module);
86
+CREATE INDEX IF NOT EXISTS idx_sys_log_status ON sys_log(status);
87
+
88
+-- 初始化5级角色数据
89
+INSERT INTO sys_role (role_code, role_name, description, level, data_scope, sort, created_time) VALUES
90
+('SUPER_ADMIN', '超级管理员', '拥有系统所有权限', 1, 'ALL', 1, NOW()),
91
+('SYS_ADMIN', '系统管理员', '系统管理权限', 2, 'ALL', 2, NOW()),
92
+('DEPT_MANAGER', '部门经理', '本部门管理权限', 3, 'DEPT', 3, NOW()),
93
+('EMPLOYEE', '普通员工', '基础操作权限', 4, 'SELF', 4, NOW()),
94
+('GUEST', '访客', '仅查看权限', 5, 'SELF', 5, NOW())
95
+ON CONFLICT (role_code) DO NOTHING;
96
+
97
+-- 初始化数据字典示例
98
+INSERT INTO sys_data_dictionary (dict_code, dict_name, description, status, created_time) VALUES
99
+('sys_user_status', '用户状态', '用户账号状态枚举', 1, NOW()),
100
+('sys_log_type', '日志类型', '系统日志类型枚举', 1, NOW()),
101
+('sys_role_level', '角色级别', '系统角色级别枚举', 1, NOW())
102
+ON CONFLICT (dict_code) DO NOTHING;
103
+
104
+INSERT INTO sys_data_dictionary_item (dict_id, item_code, item_name, item_value, sort, status, created_time)
105
+SELECT d.id, item_code, item_name, item_value, sort, 1, NOW()
106
+FROM sys_data_dictionary d
107
+CROSS JOIN (VALUES
108
+    ('sys_user_status', 'DISABLED', '停用', '0', 1),
109
+    ('sys_user_status', 'ENABLED', '正常', '1', 2)
110
+) AS v(dict_code, item_code, item_name, item_value, sort)
111
+WHERE d.dict_code = v.dict_code
112
+AND NOT EXISTS (SELECT 1 FROM sys_data_dictionary_item i WHERE i.dict_id = d.id AND i.item_code = v.item_code);
113
+
114
+INSERT INTO sys_data_dictionary_item (dict_id, item_code, item_name, item_value, sort, status, created_time)
115
+SELECT d.id, item_code, item_name, item_value, sort, 1, NOW()
116
+FROM sys_data_dictionary d
117
+CROSS JOIN (VALUES
118
+    ('sys_log_type', 'LOGIN', '登录日志', 'LOGIN', 1),
119
+    ('sys_log_type', 'OPERATE', '操作日志', 'OPERATE', 2),
120
+    ('sys_log_type', 'EXCEPTION', '异常日志', 'EXCEPTION', 3)
121
+) AS v(dict_code, item_code, item_name, item_value, sort)
122
+WHERE d.dict_code = v.dict_code
123
+AND NOT EXISTS (SELECT 1 FROM sys_data_dictionary_item i WHERE i.dict_id = d.id AND i.item_code = v.item_code);
124
+
125
+INSERT INTO sys_data_dictionary_item (dict_id, item_code, item_name, item_value, sort, status, created_time)
126
+SELECT d.id, item_code, item_name, item_value, sort, 1, NOW()
127
+FROM sys_data_dictionary d
128
+CROSS JOIN (VALUES
129
+    ('sys_role_level', 'LEVEL_1', '超级管理员', '1', 1),
130
+    ('sys_role_level', 'LEVEL_2', '系统管理员', '2', 2),
131
+    ('sys_role_level', 'LEVEL_3', '部门经理', '3', 3),
132
+    ('sys_role_level', 'LEVEL_4', '普通员工', '4', 4),
133
+    ('sys_role_level', 'LEVEL_5', '访客', '5', 5)
134
+) AS v(dict_code, item_code, item_name, item_value, sort)
135
+WHERE d.dict_code = v.dict_code
136
+AND NOT EXISTS (SELECT 1 FROM sys_data_dictionary_item i WHERE i.dict_id = d.id AND i.item_code = v.item_code);

+ 93
- 0
wm-system/src/test/java/com/water/system/service/DepartmentServiceImplTest.java Parādīt failu

@@ -0,0 +1,93 @@
1
+package com.water.system.service;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.water.common.core.exception.BusinessException;
5
+import com.water.system.entity.SysDepartment;
6
+import com.water.system.entity.dto.DeptRequest;
7
+import com.water.system.mapper.SysDepartmentMapper;
8
+import com.water.system.service.impl.DepartmentServiceImpl;
9
+import org.junit.jupiter.api.Test;
10
+import org.junit.jupiter.api.extension.ExtendWith;
11
+import org.mockito.InjectMocks;
12
+import org.mockito.Mock;
13
+import org.mockito.junit.jupiter.MockitoExtension;
14
+import java.util.Arrays;
15
+import java.util.List;
16
+import static org.junit.jupiter.api.Assertions.*;
17
+import static org.mockito.ArgumentMatchers.any;
18
+import static org.mockito.Mockito.*;
19
+
20
+@ExtendWith(MockitoExtension.class)
21
+class DepartmentServiceImplTest {
22
+
23
+    @Mock
24
+    private SysDepartmentMapper deptMapper;
25
+
26
+    @InjectMocks
27
+    private DepartmentServiceImpl departmentService;
28
+
29
+    @Test
30
+    void getDeptTree_shouldBuildTree() {
31
+        SysDepartment root = new SysDepartment();
32
+        root.setId(1L);
33
+        root.setParentId(0L);
34
+        root.setDeptName("总公司");
35
+
36
+        SysDepartment child1 = new SysDepartment();
37
+        child1.setId(2L);
38
+        child1.setParentId(1L);
39
+        child1.setDeptName("技术部");
40
+
41
+        SysDepartment child2 = new SysDepartment();
42
+        child2.setId(3L);
43
+        child2.setParentId(1L);
44
+        child2.setDeptName("市场部");
45
+
46
+        when(deptMapper.selectList(any(LambdaQueryWrapper.class)))
47
+            .thenReturn(Arrays.asList(root, child1, child2));
48
+
49
+        List<SysDepartment> tree = departmentService.getDeptTree();
50
+
51
+        assertEquals(1, tree.size());
52
+        assertEquals("总公司", tree.get(0).getDeptName());
53
+        assertEquals(2, tree.get(0).getChildren().size());
54
+    }
55
+
56
+    @Test
57
+    void createDept_shouldSetAncestors() {
58
+        SysDepartment parent = new SysDepartment();
59
+        parent.setId(1L);
60
+        parent.setAncestors("0");
61
+        parent.setDeptName("总公司");
62
+
63
+        when(deptMapper.selectById(1L)).thenReturn(parent);
64
+        when(deptMapper.insert(any(SysDepartment.class))).thenAnswer(inv -> {
65
+            SysDepartment d = inv.getArgument(0);
66
+            d.setId(10L);
67
+            assertEquals("0,1", d.getAncestors());
68
+            return 1;
69
+        });
70
+
71
+        DeptRequest request = new DeptRequest();
72
+        request.setDeptName("研发部");
73
+        request.setParentId(1L);
74
+
75
+        Long id = departmentService.createDept(request);
76
+        assertEquals(10L, id);
77
+    }
78
+
79
+    @Test
80
+    void deleteDept_shouldFailWithChildren() {
81
+        when(deptMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(2L);
82
+
83
+        assertThrows(BusinessException.class, () -> departmentService.deleteDept(1L));
84
+    }
85
+
86
+    @Test
87
+    void deleteDept_shouldSucceedWithoutChildren() {
88
+        when(deptMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(0L);
89
+
90
+        assertDoesNotThrow(() -> departmentService.deleteDept(5L));
91
+        verify(deptMapper).deleteById(5L);
92
+    }
93
+}

+ 104
- 0
wm-system/src/test/java/com/water/system/service/DictionaryServiceImplTest.java Parādīt failu

@@ -0,0 +1,104 @@
1
+package com.water.system.service;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.water.common.core.exception.BusinessException;
5
+import com.water.system.entity.DataDictionary;
6
+import com.water.system.entity.DataDictionaryItem;
7
+import com.water.system.entity.dto.DictItemRequest;
8
+import com.water.system.entity.dto.DictRequest;
9
+import com.water.system.mapper.DataDictionaryMapper;
10
+import com.water.system.mapper.DataDictionaryItemMapper;
11
+import com.water.system.service.impl.DictionaryServiceImpl;
12
+import org.junit.jupiter.api.Test;
13
+import org.junit.jupiter.api.extension.ExtendWith;
14
+import org.mockito.InjectMocks;
15
+import org.mockito.Mock;
16
+import org.mockito.junit.jupiter.MockitoExtension;
17
+import java.util.Arrays;
18
+import java.util.List;
19
+import static org.junit.jupiter.api.Assertions.*;
20
+import static org.mockito.ArgumentMatchers.any;
21
+import static org.mockito.Mockito.*;
22
+
23
+@ExtendWith(MockitoExtension.class)
24
+class DictionaryServiceImplTest {
25
+
26
+    @Mock
27
+    private DataDictionaryMapper dictMapper;
28
+
29
+    @Mock
30
+    private DataDictionaryItemMapper itemMapper;
31
+
32
+    @InjectMocks
33
+    private DictionaryServiceImpl dictionaryService;
34
+
35
+    @Test
36
+    void createDictionary_shouldSucceed() {
37
+        DictRequest request = new DictRequest();
38
+        request.setDictCode("water_quality_level");
39
+        request.setDictName("水质等级");
40
+
41
+        when(dictMapper.selectByDictCode("water_quality_level")).thenReturn(null);
42
+        when(dictMapper.insert(any(DataDictionary.class))).thenAnswer(inv -> {
43
+            DataDictionary d = inv.getArgument(0);
44
+            d.setId(10L);
45
+            return 1;
46
+        });
47
+
48
+        Long id = dictionaryService.createDictionary(request);
49
+        assertEquals(10L, id);
50
+    }
51
+
52
+    @Test
53
+    void createDictionary_shouldFailOnDuplicate() {
54
+        DictRequest request = new DictRequest();
55
+        request.setDictCode("sys_user_status");
56
+
57
+        DataDictionary existing = new DataDictionary();
58
+        existing.setDictCode("sys_user_status");
59
+        when(dictMapper.selectByDictCode("sys_user_status")).thenReturn(existing);
60
+
61
+        assertThrows(BusinessException.class, () -> dictionaryService.createDictionary(request));
62
+    }
63
+
64
+    @Test
65
+    void listItemsByDictCode_shouldReturnItems() {
66
+        DataDictionaryItem item1 = new DataDictionaryItem();
67
+        item1.setItemCode("ENABLED");
68
+        item1.setItemName("正常");
69
+        DataDictionaryItem item2 = new DataDictionaryItem();
70
+        item2.setItemCode("DISABLED");
71
+        item2.setItemName("停用");
72
+
73
+        when(itemMapper.selectByDictCode("sys_user_status")).thenReturn(Arrays.asList(item1, item2));
74
+
75
+        List<DataDictionaryItem> items = dictionaryService.listItemsByDictCode("sys_user_status");
76
+        assertEquals(2, items.size());
77
+    }
78
+
79
+    @Test
80
+    void deleteDictionary_shouldDeleteItemsToo() {
81
+        dictionaryService.deleteDictionary(1L);
82
+
83
+        verify(itemMapper).delete(any(LambdaQueryWrapper.class));
84
+        verify(dictMapper).deleteById(1L);
85
+    }
86
+
87
+    @Test
88
+    void createItem_shouldSucceed() {
89
+        DictItemRequest request = new DictItemRequest();
90
+        request.setDictId(1L);
91
+        request.setItemCode("NEW_ITEM");
92
+        request.setItemName("新项");
93
+        request.setItemValue("value");
94
+
95
+        when(itemMapper.insert(any(DataDictionaryItem.class))).thenAnswer(inv -> {
96
+            DataDictionaryItem i = inv.getArgument(0);
97
+            i.setId(20L);
98
+            return 1;
99
+        });
100
+
101
+        Long id = dictionaryService.createItem(request);
102
+        assertEquals(20L, id);
103
+    }
104
+}

+ 93
- 0
wm-system/src/test/java/com/water/system/service/LogServiceImplTest.java Parādīt failu

@@ -0,0 +1,93 @@
1
+package com.water.system.service;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.baomidou.mybatisplus.core.metadata.IPage;
5
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
6
+import com.water.system.entity.SysLog;
7
+import com.water.system.entity.dto.LogQueryRequest;
8
+import com.water.system.mapper.SysLogMapper;
9
+import com.water.system.service.impl.LogServiceImpl;
10
+import org.junit.jupiter.api.Test;
11
+import org.junit.jupiter.api.extension.ExtendWith;
12
+import org.mockito.InjectMocks;
13
+import org.mockito.Mock;
14
+import org.mockito.junit.jupiter.MockitoExtension;
15
+import java.time.LocalDateTime;
16
+import java.util.Arrays;
17
+import static org.junit.jupiter.api.Assertions.*;
18
+import static org.mockito.ArgumentMatchers.any;
19
+import static org.mockito.Mockito.*;
20
+
21
+@ExtendWith(MockitoExtension.class)
22
+class LogServiceImplTest {
23
+
24
+    @Mock
25
+    private SysLogMapper logMapper;
26
+
27
+    @InjectMocks
28
+    private LogServiceImpl logService;
29
+
30
+    @Test
31
+    void pageLogs_shouldReturnPagedResult() {
32
+        Page<SysLog> page = new Page<>(1, 10);
33
+        SysLog log1 = new SysLog();
34
+        log1.setId(1L);
35
+        log1.setLogType("LOGIN");
36
+        log1.setUsername("admin");
37
+        log1.setCreatedTime(LocalDateTime.now());
38
+
39
+        SysLog log2 = new SysLog();
40
+        log2.setId(2L);
41
+        log2.setLogType("OPERATE");
42
+        log2.setUsername("user1");
43
+        log2.setModule("用户管理");
44
+
45
+        page.setRecords(Arrays.asList(log1, log2));
46
+        page.setTotal(2);
47
+
48
+        when(logMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
49
+
50
+        LogQueryRequest request = new LogQueryRequest();
51
+        request.setPageNum(1);
52
+        request.setPageSize(10);
53
+
54
+        IPage<SysLog> result = logService.pageLogs(request);
55
+
56
+        assertEquals(2, result.getTotal());
57
+        assertEquals("LOGIN", result.getRecords().get(0).getLogType());
58
+    }
59
+
60
+    @Test
61
+    void pageLogs_withFilters_shouldApplyAllConditions() {
62
+        Page<SysLog> page = new Page<>(1, 10);
63
+        page.setRecords(Arrays.asList());
64
+        page.setTotal(0);
65
+
66
+        when(logMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
67
+
68
+        LogQueryRequest request = new LogQueryRequest();
69
+        request.setLogType("OPERATE");
70
+        request.setUsername("admin");
71
+        request.setModule("系统管理");
72
+        request.setStatus(1);
73
+        request.setStartTime("2024-01-01");
74
+        request.setEndTime("2024-12-31");
75
+
76
+        IPage<SysLog> result = logService.pageLogs(request);
77
+
78
+        assertNotNull(result);
79
+        verify(logMapper).selectPage(any(Page.class), any(LambdaQueryWrapper.class));
80
+    }
81
+
82
+    @Test
83
+    void saveLog_shouldInsert() {
84
+        SysLog log = new SysLog();
85
+        log.setLogType("LOGIN");
86
+        log.setUsername("testuser");
87
+        log.setIp("127.0.0.1");
88
+
89
+        logService.saveLog(log);
90
+
91
+        verify(logMapper).insert(log);
92
+    }
93
+}

+ 115
- 0
wm-system/src/test/java/com/water/system/service/RoleServiceImplTest.java Parādīt failu

@@ -0,0 +1,115 @@
1
+package com.water.system.service;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.water.common.core.exception.BusinessException;
5
+import com.water.system.entity.SysRole;
6
+import com.water.system.entity.dto.RoleRequest;
7
+import com.water.system.mapper.SysRoleMapper;
8
+import com.water.system.service.impl.RoleServiceImpl;
9
+import com.fasterxml.jackson.databind.ObjectMapper;
10
+import org.junit.jupiter.api.BeforeEach;
11
+import org.junit.jupiter.api.Test;
12
+import org.junit.jupiter.api.extension.ExtendWith;
13
+import org.mockito.InjectMocks;
14
+import org.mockito.Mock;
15
+import org.mockito.junit.jupiter.MockitoExtension;
16
+import java.util.Arrays;
17
+import java.util.List;
18
+import static org.junit.jupiter.api.Assertions.*;
19
+import static org.mockito.ArgumentMatchers.any;
20
+import static org.mockito.Mockito.*;
21
+
22
+@ExtendWith(MockitoExtension.class)
23
+class RoleServiceImplTest {
24
+
25
+    @Mock
26
+    private SysRoleMapper roleMapper;
27
+
28
+    @InjectMocks
29
+    private RoleServiceImpl roleService;
30
+
31
+    private final ObjectMapper objectMapper = new ObjectMapper();
32
+
33
+    @BeforeEach
34
+    void setUp() {
35
+        // ObjectMapper is final, inject manually via reflection or just let it be used
36
+    }
37
+
38
+    @Test
39
+    void listRoles_shouldReturnAllRoles() {
40
+        SysRole admin = new SysRole();
41
+        admin.setId(1L);
42
+        admin.setRoleCode("SUPER_ADMIN");
43
+        admin.setLevel(1);
44
+
45
+        SysRole user = new SysRole();
46
+        user.setId(4L);
47
+        user.setRoleCode("EMPLOYEE");
48
+        user.setLevel(4);
49
+
50
+        when(roleMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(Arrays.asList(admin, user));
51
+
52
+        List<SysRole> roles = roleService.listRoles();
53
+
54
+        assertEquals(2, roles.size());
55
+        assertEquals("SUPER_ADMIN", roles.get(0).getRoleCode());
56
+    }
57
+
58
+    @Test
59
+    void createRole_shouldSucceed() {
60
+        RoleRequest request = new RoleRequest();
61
+        request.setRoleCode("TEST_ROLE");
62
+        request.setRoleName("测试角色");
63
+        request.setLevel(3);
64
+        request.setPermissions(Arrays.asList("user:read", "user:write"));
65
+
66
+        when(roleMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(0L);
67
+        when(roleMapper.insert(any(SysRole.class))).thenAnswer(inv -> {
68
+            SysRole r = inv.getArgument(0);
69
+            r.setId(10L);
70
+            return 1;
71
+        });
72
+
73
+        Long id = roleService.createRole(request);
74
+
75
+        assertEquals(10L, id);
76
+        verify(roleMapper).insert(any(SysRole.class));
77
+    }
78
+
79
+    @Test
80
+    void createRole_shouldFailOnDuplicateCode() {
81
+        RoleRequest request = new RoleRequest();
82
+        request.setRoleCode("SUPER_ADMIN");
83
+        request.setRoleName("重复角色");
84
+        request.setLevel(1);
85
+
86
+        when(roleMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(1L);
87
+
88
+        assertThrows(BusinessException.class, () -> roleService.createRole(request));
89
+    }
90
+
91
+    @Test
92
+    void deleteRole_shouldFailForAdminRoles() {
93
+        SysRole admin = new SysRole();
94
+        admin.setId(1L);
95
+        admin.setLevel(1);
96
+        admin.setRoleCode("SUPER_ADMIN");
97
+
98
+        when(roleMapper.selectById(1L)).thenReturn(admin);
99
+
100
+        assertThrows(BusinessException.class, () -> roleService.deleteRole(1L));
101
+    }
102
+
103
+    @Test
104
+    void deleteRole_shouldSucceedForNormalRoles() {
105
+        SysRole role = new SysRole();
106
+        role.setId(5L);
107
+        role.setLevel(4);
108
+        role.setRoleCode("EMPLOYEE");
109
+
110
+        when(roleMapper.selectById(5L)).thenReturn(role);
111
+
112
+        assertDoesNotThrow(() -> roleService.deleteRole(5L));
113
+        verify(roleMapper).deleteById(5L);
114
+    }
115
+}

+ 120
- 0
wm-system/src/test/java/com/water/system/service/UserServiceImplTest.java Parādīt failu

@@ -0,0 +1,120 @@
1
+package com.water.system.service;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.baomidou.mybatisplus.core.metadata.IPage;
5
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
6
+import com.water.common.core.exception.BusinessException;
7
+import com.water.system.entity.SysUser;
8
+import com.water.system.entity.dto.UserCreateRequest;
9
+import com.water.system.entity.dto.UserUpdateRequest;
10
+import com.water.system.mapper.SysUserMapper;
11
+import com.water.system.service.impl.UserServiceImpl;
12
+import org.junit.jupiter.api.Test;
13
+import org.junit.jupiter.api.extension.ExtendWith;
14
+import org.mockito.InjectMocks;
15
+import org.mockito.Mock;
16
+import org.mockito.junit.jupiter.MockitoExtension;
17
+import java.util.Arrays;
18
+import static org.junit.jupiter.api.Assertions.*;
19
+import static org.mockito.ArgumentMatchers.any;
20
+import static org.mockito.Mockito.*;
21
+
22
+@ExtendWith(MockitoExtension.class)
23
+class UserServiceImplTest {
24
+
25
+    @Mock
26
+    private SysUserMapper userMapper;
27
+
28
+    @InjectMocks
29
+    private UserServiceImpl userService;
30
+
31
+    @Test
32
+    void createUser_shouldSucceed() {
33
+        UserCreateRequest request = new UserCreateRequest();
34
+        request.setUsername("testuser");
35
+        request.setPassword("test123");
36
+        request.setRealName("测试用户");
37
+        request.setPhone("13800138000");
38
+
39
+        when(userMapper.selectByUsername("testuser")).thenReturn(null);
40
+        when(userMapper.insert(any(SysUser.class))).thenAnswer(inv -> {
41
+            SysUser u = inv.getArgument(0);
42
+            u.setId(100L);
43
+            return 1;
44
+        });
45
+
46
+        Long id = userService.createUser(request);
47
+
48
+        assertEquals(100L, id);
49
+        verify(userMapper).insert(any(SysUser.class));
50
+    }
51
+
52
+    @Test
53
+    void createUser_shouldFailOnDuplicateUsername() {
54
+        UserCreateRequest request = new UserCreateRequest();
55
+        request.setUsername("admin");
56
+        request.setPassword("pass");
57
+
58
+        SysUser existing = new SysUser();
59
+        existing.setUsername("admin");
60
+        when(userMapper.selectByUsername("admin")).thenReturn(existing);
61
+
62
+        assertThrows(BusinessException.class, () -> userService.createUser(request));
63
+    }
64
+
65
+    @Test
66
+    void updateStatus_shouldSucceed() {
67
+        SysUser user = new SysUser();
68
+        user.setId(1L);
69
+        user.setStatus(1);
70
+        when(userMapper.selectById(1L)).thenReturn(user);
71
+
72
+        userService.updateStatus(1L, 0);
73
+
74
+        verify(userMapper).updateById(argThat(u -> u.getStatus() == 0));
75
+    }
76
+
77
+    @Test
78
+    void deleteUser_shouldFailForAdmin() {
79
+        SysUser admin = new SysUser();
80
+        admin.setId(1L);
81
+        admin.setUsername("admin");
82
+        when(userMapper.selectById(1L)).thenReturn(admin);
83
+
84
+        assertThrows(BusinessException.class, () -> userService.deleteUser(1L));
85
+    }
86
+
87
+    @Test
88
+    void resetPassword_shouldSucceed() {
89
+        SysUser user = new SysUser();
90
+        user.setId(5L);
91
+        user.setUsername("testuser");
92
+        when(userMapper.selectById(5L)).thenReturn(user);
93
+
94
+        assertDoesNotThrow(() -> userService.resetPassword(5L, "newPass123"));
95
+        verify(userMapper).updateById(argThat(u -> u.getPassword() != null));
96
+    }
97
+
98
+    @Test
99
+    void pageUsers_shouldReturnPagedResult() {
100
+        Page<SysUser> page = new Page<>(1, 10);
101
+        SysUser user1 = new SysUser();
102
+        user1.setId(1L);
103
+        user1.setUsername("user1");
104
+        user1.setPassword("hashed");
105
+        SysUser user2 = new SysUser();
106
+        user2.setId(2L);
107
+        user2.setUsername("user2");
108
+        user2.setPassword("hashed");
109
+        page.setRecords(Arrays.asList(user1, user2));
110
+        page.setTotal(2);
111
+
112
+        when(userMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
113
+
114
+        IPage<SysUser> result = userService.pageUsers(1, 10, null, null, null);
115
+
116
+        assertEquals(2, result.getTotal());
117
+        // 密码应被清除
118
+        assertNull(result.getRecords().get(0).getPassword());
119
+    }
120
+}