feat(dept): 创建部门管理后端业务层与路由
ISysDeptService + SysDeptService: CRUD + 子部门删除校验 + 审计日志; DeptCreateRequest/DeptUpdateRequest: 验证注解与前端 VALIDATION 对齐; SysDeptHandler: RESTful API (GET/POST/PUT/DELETE /api/depts); SystemRouter: 注册部门路由。
This commit is contained in:
+10
-1
@@ -7,6 +7,7 @@ import cn.novalon.manage.sys.handler.dictionary.DictionaryHandler;
|
||||
import cn.novalon.manage.sys.handler.dict.SysDictHandler;
|
||||
import cn.novalon.manage.sys.handler.log.SysLogHandler;
|
||||
import cn.novalon.manage.sys.handler.log.OperationLogHandler;
|
||||
import cn.novalon.manage.sys.handler.dept.SysDeptHandler;
|
||||
import cn.novalon.manage.sys.handler.menu.MenuHandler;
|
||||
import cn.novalon.manage.sys.handler.role.SysRoleHandler;
|
||||
import cn.novalon.manage.sys.handler.permission.SysPermissionHandler;
|
||||
@@ -51,7 +52,8 @@ public class SystemRouter {
|
||||
SysUserMessageHandler messageHandler,
|
||||
SysFileHandler fileHandler,
|
||||
SysPermissionHandler permissionHandler,
|
||||
PasswordDiagnosticHandler passwordDiagnosticHandler) {
|
||||
PasswordDiagnosticHandler passwordDiagnosticHandler,
|
||||
SysDeptHandler deptHandler) {
|
||||
|
||||
return route()
|
||||
// ========== 诊断路由 ==========
|
||||
@@ -115,6 +117,13 @@ public class SystemRouter {
|
||||
.PUT("/api/config/{id}", configHandler::updateConfig)
|
||||
.DELETE("/api/config/{id}", configHandler::deleteConfig)
|
||||
|
||||
// ========== 部门路由 ==========
|
||||
.GET("/api/depts", deptHandler::getAllDepts)
|
||||
.GET("/api/depts/{id}", deptHandler::getDeptById)
|
||||
.POST("/api/depts", deptHandler::createDept)
|
||||
.PUT("/api/depts/{id}", deptHandler::updateDept)
|
||||
.DELETE("/api/depts/{id}", deptHandler::deleteDept)
|
||||
|
||||
// ========== 日志路由 ==========
|
||||
.GET("/api/logs/login", logHandler::getAllLoginLogs)
|
||||
.GET("/api/logs/login/page", logHandler::getLoginLogsByPage)
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package cn.novalon.manage.sys.core.service;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.SysDept;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysDeptService {
|
||||
Flux<SysDept> findAll();
|
||||
Flux<SysDept> findByParentId(Long parentId);
|
||||
Mono<SysDept> findById(Long id);
|
||||
Mono<SysDept> save(SysDept dept);
|
||||
Mono<Void> deleteById(Long id);
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package cn.novalon.manage.sys.core.service.impl;
|
||||
|
||||
import cn.novalon.manage.sys.audit.AuditLogHelper;
|
||||
import cn.novalon.manage.sys.audit.service.IAuditLogService;
|
||||
import cn.novalon.manage.sys.core.domain.SysDept;
|
||||
import cn.novalon.manage.sys.core.repository.ISysDeptRepository;
|
||||
import cn.novalon.manage.sys.core.service.ISysDeptService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class SysDeptService implements ISysDeptService {
|
||||
|
||||
private final ISysDeptRepository repository;
|
||||
private final IAuditLogService auditLogService;
|
||||
|
||||
public SysDeptService(ISysDeptRepository repository, IAuditLogService auditLogService) {
|
||||
this.repository = repository;
|
||||
this.auditLogService = auditLogService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysDept> findAll() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysDept> findByParentId(Long parentId) {
|
||||
return repository.findByParentId(parentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysDept> findById(Long id) {
|
||||
return repository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysDept> save(SysDept dept) {
|
||||
return repository.save(dept)
|
||||
.flatMap(saved -> AuditLogHelper.record(auditLogService, "Dept", saved.getId(), "CREATE", saved)
|
||||
.thenReturn(saved));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteById(Long id) {
|
||||
return repository.findById(id)
|
||||
.flatMap(dept -> repository.countByParentId(id)
|
||||
.flatMap(count -> {
|
||||
if (count > 0) {
|
||||
return Mono.error(new IllegalArgumentException("该部门下存在子部门,无法删除"));
|
||||
}
|
||||
return repository.deleteById(id)
|
||||
.then(AuditLogHelper.record(auditLogService, "Dept", id, "DELETE", dept, null));
|
||||
}))
|
||||
.then();
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package cn.novalon.manage.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
@Schema(description = "部门创建请求")
|
||||
public class DeptCreateRequest {
|
||||
|
||||
@Schema(description = "上级部门ID", example = "0")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "部门名称", example = "研发部")
|
||||
@NotBlank(message = "部门名称不能为空")
|
||||
@Size(min = 1, max = 100, message = "部门名称长度必须在1-100之间")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "排序", example = "0")
|
||||
@Min(value = 0, message = "排序不能为负数")
|
||||
private Integer orderNum;
|
||||
|
||||
@Schema(description = "负责人", example = "张三")
|
||||
@Size(max = 50, message = "负责人长度不能超过50")
|
||||
private String leader;
|
||||
|
||||
@Schema(description = "手机号", example = "13800138000")
|
||||
@Size(max = 20, message = "手机号长度不能超过20")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "邮箱", example = "dept@example.com")
|
||||
@Size(max = 100, message = "邮箱长度不能超过100")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "状态:0-禁用,1-正常", example = "1")
|
||||
private Integer status;
|
||||
|
||||
public Long getParentId() { return parentId; }
|
||||
public void setParentId(Long parentId) { this.parentId = parentId; }
|
||||
public String getDeptName() { return deptName; }
|
||||
public void setDeptName(String deptName) { this.deptName = deptName; }
|
||||
public Integer getOrderNum() { return orderNum; }
|
||||
public void setOrderNum(Integer orderNum) { this.orderNum = orderNum; }
|
||||
public String getLeader() { return leader; }
|
||||
public void setLeader(String leader) { this.leader = leader; }
|
||||
public String getPhone() { return phone; }
|
||||
public void setPhone(String phone) { this.phone = phone; }
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
public Integer getStatus() { return status; }
|
||||
public void setStatus(Integer status) { this.status = status; }
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package cn.novalon.manage.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
@Schema(description = "部门更新请求")
|
||||
public class DeptUpdateRequest {
|
||||
|
||||
@Schema(description = "上级部门ID", example = "0")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "部门名称", example = "研发部")
|
||||
@Size(min = 1, max = 100, message = "部门名称长度必须在1-100之间")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "排序", example = "0")
|
||||
@Min(value = 0, message = "排序不能为负数")
|
||||
private Integer orderNum;
|
||||
|
||||
@Schema(description = "负责人", example = "张三")
|
||||
@Size(max = 50, message = "负责人长度不能超过50")
|
||||
private String leader;
|
||||
|
||||
@Schema(description = "手机号", example = "13800138000")
|
||||
@Size(max = 20, message = "手机号长度不能超过20")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "邮箱", example = "dept@example.com")
|
||||
@Size(max = 100, message = "邮箱长度不能超过100")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "状态:0-禁用,1-正常", example = "1")
|
||||
private Integer status;
|
||||
|
||||
public Long getParentId() { return parentId; }
|
||||
public void setParentId(Long parentId) { this.parentId = parentId; }
|
||||
public String getDeptName() { return deptName; }
|
||||
public void setDeptName(String deptName) { this.deptName = deptName; }
|
||||
public Integer getOrderNum() { return orderNum; }
|
||||
public void setOrderNum(Integer orderNum) { this.orderNum = orderNum; }
|
||||
public String getLeader() { return leader; }
|
||||
public void setLeader(String leader) { this.leader = leader; }
|
||||
public String getPhone() { return phone; }
|
||||
public void setPhone(String phone) { this.phone = phone; }
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
public Integer getStatus() { return status; }
|
||||
public void setStatus(Integer status) { this.status = status; }
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package cn.novalon.manage.sys.handler.dept;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.SysDept;
|
||||
import cn.novalon.manage.sys.core.service.ISysDeptService;
|
||||
import cn.novalon.manage.sys.dto.request.DeptCreateRequest;
|
||||
import cn.novalon.manage.sys.dto.request.DeptUpdateRequest;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@Tag(name = "部门管理", description = "部门树形结构相关操作")
|
||||
public class SysDeptHandler {
|
||||
|
||||
private final ISysDeptService deptService;
|
||||
|
||||
public SysDeptHandler(ISysDeptService deptService) {
|
||||
this.deptService = deptService;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有部门", description = "获取系统中所有部门列表(树形结构)")
|
||||
public Mono<ServerResponse> getAllDepts(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(deptService.findAll(), SysDept.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取部门", description = "根据部门ID获取详细信息")
|
||||
public Mono<ServerResponse> getDeptById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return deptService.findById(id)
|
||||
.flatMap(dept -> ServerResponse.ok().bodyValue(dept))
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "创建部门", description = "创建新部门")
|
||||
public Mono<ServerResponse> createDept(ServerRequest request) {
|
||||
return request.bodyToMono(DeptCreateRequest.class)
|
||||
.flatMap(req -> {
|
||||
SysDept dept = new SysDept();
|
||||
dept.setParentId(req.getParentId() != null ? req.getParentId() : 0L);
|
||||
dept.setDeptName(req.getDeptName());
|
||||
dept.setOrderNum(req.getOrderNum() != null ? req.getOrderNum() : 0);
|
||||
dept.setLeader(req.getLeader());
|
||||
dept.setPhone(req.getPhone());
|
||||
dept.setEmail(req.getEmail());
|
||||
dept.setStatus(req.getStatus() != null ? req.getStatus() : 1);
|
||||
return deptService.save(dept);
|
||||
})
|
||||
.flatMap(saved -> ServerResponse.status(HttpStatus.CREATED).bodyValue(saved))
|
||||
.onErrorResume(IllegalArgumentException.class, e -> badRequest(e.getMessage()));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新部门", description = "更新部门信息")
|
||||
public Mono<ServerResponse> updateDept(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return request.bodyToMono(DeptUpdateRequest.class)
|
||||
.flatMap(req -> deptService.findById(id)
|
||||
.flatMap(existing -> {
|
||||
if (req.getParentId() != null) existing.setParentId(req.getParentId());
|
||||
if (req.getDeptName() != null) existing.setDeptName(req.getDeptName());
|
||||
if (req.getOrderNum() != null) existing.setOrderNum(req.getOrderNum());
|
||||
if (req.getLeader() != null) existing.setLeader(req.getLeader());
|
||||
if (req.getPhone() != null) existing.setPhone(req.getPhone());
|
||||
if (req.getEmail() != null) existing.setEmail(req.getEmail());
|
||||
if (req.getStatus() != null) existing.setStatus(req.getStatus());
|
||||
existing.setUpdatedAt(LocalDateTime.now());
|
||||
return deptService.save(existing);
|
||||
}))
|
||||
.flatMap(updated -> ServerResponse.ok().bodyValue(updated))
|
||||
.switchIfEmpty(ServerResponse.notFound().build())
|
||||
.onErrorResume(IllegalArgumentException.class, e -> badRequest(e.getMessage()));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除部门", description = "删除指定部门(有子部门时拒绝)")
|
||||
public Mono<ServerResponse> deleteDept(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return deptService.deleteById(id)
|
||||
.then(ServerResponse.noContent().build())
|
||||
.onErrorResume(IllegalArgumentException.class, e -> badRequest(e.getMessage()));
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> badRequest(String message) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Map.of("code", HttpStatus.BAD_REQUEST.value(), "message", message, "timestamp", LocalDateTime.now()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user