如何在 Spring Boot 中設計和返回樹形結構的組織和部門信息
文章目錄
- 如何在 Spring Boot 中設計和返回樹形結構的組織和部門信息
- 1. 需求分析
- 一、數據庫表設計
- 1.1 `organization` 表設計
- 1.2 `department` 表設計
- 1.3 模擬數據
- 二、后端設計
- 2.1 實體類設計
- `Organization` 實體類
- `Department` 實體類
- 2.2 DTO 類設計
- `OrganizationDTO`
- `DepartmentDTO`
- 2.3 Service 層設計
- 2.4 Controller 層設計
- 三、前端請求與展示
- 總結
在企業管理系統中,組織和部門之間通常存在層級結構,每個組織和部門都可能有多個子級和父級。這種樹形結構的數據管理在很多業務場景下都十分常見,比如權限控制、員工管理等。
今天我們將深入探討如何在 Spring Boot 中設計組織和部門表,如何在后端生成并返回樹形結構數據,以及如何展示組織和部門的完整信息。
1. 需求分析
我們需要設計兩個表:組織表(organizations
)和部門表(departments
)。兩張表之間存在如下關系:
- 每個組織可以有一個父組織,也可以有多個子組織,形成樹形結構。
- 每個部門屬于某個組織,并且每個部門也有可能有子部門,部門之間同樣需要支持樹形結構。
- 前端需要通過接口獲取組織和部門的樹形結構數據,后端通過遞歸算法構建樹形層級。
一、數據庫表設計
首先,我們需要設計兩張表:organization
和 department
。organization
表用于存儲組織的信息,department
表用于存儲部門的信息。兩張表都需要支持樹形結構的關系。
1.1 organization
表設計
organization
表用于存儲組織的基本信息,并支持組織的樹形層級關系。- 每個組織可以有一個父組織,也可以有多個子組織。
- 通過
parent_id
字段,我們可以表示組織之間的父子關系。
CREATE TABLE `organization` (`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '組織ID',`name` VARCHAR(255) NOT NULL COMMENT '組織名稱',`description` VARCHAR(255) COMMENT '組織描述',`location` VARCHAR(255) COMMENT '組織位置',`parent_id` BIGINT DEFAULT NULL COMMENT '父組織ID',`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',CONSTRAINT `fk_parent_org` FOREIGN KEY (`parent_id`) REFERENCES `organization` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1.2 department
表設計
department
表用于存儲部門信息,支持部門的樹形層級關系。每個部門也可以有一個父部門,也可以有多個子部門。- 同時,每個部門屬于一個組織, 部門關聯到一個組織。
organization_id
是部門所屬的組織。parent_id
是部門的父級部門。
CREATE TABLE `department` (`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '部門ID',`name` VARCHAR(255) NOT NULL COMMENT '部門名稱',`description` VARCHAR(255) COMMENT '部門描述',`employee_count` INT DEFAULT 0 COMMENT '員工數量',`organization_id` BIGINT NOT NULL COMMENT '所屬組織ID',`parent_id` BIGINT DEFAULT NULL COMMENT '父部門ID',`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',CONSTRAINT `fk_org_id` FOREIGN KEY (`organization_id`) REFERENCES `organization` (`id`) ON DELETE CASCADE,CONSTRAINT `fk_parent_dep` FOREIGN KEY (`parent_id`) REFERENCES `department` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1.3 模擬數據
接下來我們可以插入一些模擬數據,以便于我們在后續實現中測試樹形結構。
-- 插入組織數據
INSERT INTO `organization` (`name`, `description`, `location`,`parent_id`) VALUES
('總公司', '總部組織', '上海', NULL);
('分公司A', '分公司A', '北京', 1);
('分公司B', '分公司B', '深圳', 1);
('分公司C', '分公司C', '杭州', 2);-- 插入部門數據
INSERT INTO `department` (`name`, `description`, `employee_count`, `organization_id`, `parent_id`) VALUES
('總部', '總部部門', 20, 1, NULL),
('人事部', '管理人力資源', 5, 1, 1),
('IT部', '技術支持部門', 10, 1, 1),
('市場部', '市場推廣部門', 5, 1, 1),
('分公司A', '分公司A部門', 30, 2, NULL),
('市場部A', '市場部A', 5, 2, 5);
二、后端設計
2.1 實體類設計
在 Spring Boot 中,我們需要根據數據庫表設計對應的實體類。
Organization
實體類
import javax.persistence.*;
import java.util.List;@Entity
public class Organization {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String description;private String location;@ManyToOne@JoinColumn(name = "parent_id")private Organization parent;@OneToMany(mappedBy = "parent")private List<Organization> children;@OneToMany(mappedBy = "organization")private List<Department> departments;private String createdAt;private String updatedAt;// Getters and setters
}
Department
實體類
import javax.persistence.*;
import java.util.List;@Entity
public class Department {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String description;private Integer employeeCount;@ManyToOne@JoinColumn(name = "organization_id")private Organization organization;@ManyToOne@JoinColumn(name = "parent_id")private Department parent;@OneToMany(mappedBy = "parent")private List<Department> children;private String createdAt;private String updatedAt;// Getters and setters
}
2.2 DTO 類設計
為了控制返回數據,我們使用 DTO(數據傳輸對象)來傳遞組織和部門的完整信息。
OrganizationDTO
import java.util.List;public class OrganizationDTO {private Long id;private String name;private String description;private String location;private String createdAt;private String updatedAt;private List<DepartmentDTO> departments;private List<OrganizationDTO> children;private OrganizationDetails details;public static class OrganizationDetails {private Long id;private String name;private String description;private String location;private String createdAt;private String updatedAt;}// Getters and setters
}
DepartmentDTO
import java.util.List;public class DepartmentDTO {private Long id;private String name;private String description;private Integer employeeCount;private String createdAt;private String updatedAt;private List<DepartmentDTO> children;private DepartmentDetails details;public static class DepartmentDetails {private Long id;private String name;private String description;private Integer employeeCount;private String createdAt;private String updatedAt;}// Getters and setters
}
2.3 Service 層設計
在 Service 層中,我們將組織和部門信息轉換為樹形結構,并填充每個節點的詳細信息。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.stream.Collectors;@Service
public class OrganizationService {@Autowiredprivate OrganizationRepository organizationRepository;@Autowiredprivate DepartmentRepository departmentRepository;// 獲取組織樹形結構public List<OrganizationDTO> getOrganizationTree() {List<Organization> rootOrganizations = organizationRepository.findByParentIsNull();return rootOrganizations.stream().map(this::convertToOrganizationDTO).collect(Collectors.toList());}// 轉換組織實體為組織DTOprivate OrganizationDTO convertToOrganizationDTO(Organization org) {OrganizationDTO orgDTO = new OrganizationDTO();orgDTO.setId(org.getId());orgDTO.setName(org.getName());orgDTO.setDescription(org.getDescription());orgDTO.setLocation(org.getLocation());orgDTO.setCreatedAt(org.getCreatedAt());orgDTO.setUpdatedAt(org.getUpdatedAt());// 填充子組織List<OrganizationDTO> children = org.getChildren().stream().map(this::convertToOrganizationDTO).collect(Collectors.toList());orgDTO.setChildren(children);// 填充部門樹List<DepartmentDTO> departments = org.getDepartments().stream().map(this::convertToDepartmentDTO).collect(Collectors.toList());orgDTO.setDepartments(departments);// 填充詳細信息OrganizationDTO.OrganizationDetails details = new OrganizationDTO.OrganizationDetails();details.setId(org.getId());details.setName(org.getName());details.setDescription(org.getDescription());details.setLocation(org.getLocation());details.setCreatedAt(org.getCreatedAt());details.setUpdatedAt(org.getUpdatedAt());orgDTO.setDetails(details);return orgDTO;}// 轉換部門實體為部門DTOprivate DepartmentDTO convertToDepartmentDTO(Department dep) {DepartmentDTO depDTO = new DepartmentDTO();depDTO.setId(dep.getId());depDTO.setName(dep.getName());depDTO.setDescription(dep.getDescription());depDTO.setEmployeeCount(dep.getEmployeeCount());depDTO.setCreatedAt(dep.getCreatedAt());depDTO.setUpdatedAt(dep.getUpdatedAt());// 填充子部門List<DepartmentDTO> children = dep.getChildren().stream().map(this::convertToDepartmentDTO).collect(Collectors.toList());depDTO.setChildren(children);// 填充詳細信息DepartmentDTO.DepartmentDetails details = new DepartmentDTO.DepartmentDetails();details.setId(dep.getId());details.setName(dep.getName());details.setDescription(dep.getDescription());details.setEmployeeCount(dep.getEmployeeCount());details.setCreatedAt(dep.getCreatedAt());details.setUpdatedAt(dep.getUpdatedAt());depDTO.setDetails(details);return depDTO;}
}
2.4 Controller 層設計
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/api/organizations")
public class OrganizationController {@Autowiredprivate OrganizationService organizationService;@GetMapping("/tree")public List<OrganizationDTO> getOrganizationTree() {return organizationService.getOrganizationTree();}
}
三、前端請求與展示
前端通過 GET /api/organizations/tree
請求接口,返回數據結構如下(嵌套完整組織部門信息):
[{"id": 1,"name": "總公司","description": "總部組織","location": "上海","createdAt": "2023-01-01T10:00:00","updatedAt": "2023-10-01T10:00:00","children": [{"id": 2,"name": "分公司A","description": "分公司A","location": "北京","createdAt": "2023-01-05T12:00:00","updatedAt": "2023-10-01T12:00:00","children": [],"departments": [{"id": 4,"name": "IT部","description": "技術支持部門","employeeCount": 10,"createdAt": "2023-01-10T09:00:00","updatedAt": "2023-10-01T09:00:00","children": [],"details": {"id": 4,"name": "IT部","description": "技術支持部門","employeeCount": 10,"createdAt": "2023-01-10T09:00:00","updatedAt": "2023-10-01T09:00:00"}}]}],"departments": [{"id": 1,"name": "總部","description": "總部部門","employeeCount": 20,"createdAt": "2023-01-01T10:00:00","updatedAt": "2023-10-01T10:00:00","children": [],"details": {"id": 1,"name": "總部","description": "總部部門","employeeCount": 20,"createdAt": "2023-01-01T10:00:00","updatedAt": "2023-10-01T10:00:00"}}]}
]
總結
在本文中,我們介紹了如何在 Spring Boot 中設計組織和部門表,如何處理樹形結構,并通過 DTO 設計將組織和部門的完整信息傳遞給前端。在后端,我們使用了遞歸方法來構建樹形數據結構,確保每個節點都包含該組織或部門的詳細信息。