refactor: 重构模块结构,将设备/运维相关代码移至module-asset和module-wo
- 将EquipmentController移至module-asset - 将EquipmentHealthController移至module-asset - 将OwnershipEntityController移至module-asset - 将维保工单相关移至module-wo - 删除module-ops空模块 - 更新API路径为/api/asset/*和/api/ops/*
This commit is contained in:
parent
b24e0818f0
commit
ee3cf66c90
|
|
@ -23,6 +23,12 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.ether</groupId>
|
||||
<artifactId>module-mdm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
|
|
@ -53,5 +59,23 @@
|
|||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
package com.ether.pms.asset.controller;
|
||||
|
||||
import com.ether.pms.asset.entity.Equipment;
|
||||
import com.ether.pms.asset.entity.EquipmentElevator;
|
||||
import com.ether.pms.asset.entity.EquipmentEnergy;
|
||||
import com.ether.pms.asset.entity.EquipmentFire;
|
||||
import com.ether.pms.asset.entity.EquipmentHvac;
|
||||
import com.ether.pms.asset.enums.EquipmentType;
|
||||
import com.ether.pms.asset.enums.OwnershipType;
|
||||
import com.ether.pms.asset.service.*;
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/asset/equipment")
|
||||
@RequiredArgsConstructor
|
||||
public class EquipmentController {
|
||||
|
||||
private final EquipmentService equipmentService;
|
||||
private final EquipmentElevatorService elevatorService;
|
||||
private final EquipmentHvacService hvacService;
|
||||
private final EquipmentEnergyService energyService;
|
||||
private final EquipmentFireService fireService;
|
||||
|
||||
// ==================== 设备主表 CRUD ====================
|
||||
|
||||
@PostMapping
|
||||
public ApiResponse<Equipment> createEquipment(@RequestBody Equipment equipment) {
|
||||
return ApiResponse.success(equipmentService.createEquipment(equipment));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ApiResponse<Equipment> getEquipment(@PathVariable UUID id) {
|
||||
return ApiResponse.success(equipmentService.getEquipmentById(id));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ApiResponse<Equipment> updateEquipment(@PathVariable UUID id, @RequestBody Equipment equipment) {
|
||||
return ApiResponse.success(equipmentService.updateEquipment(id, equipment));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResponse<Void> deleteEquipment(@PathVariable UUID id) {
|
||||
equipmentService.deleteEquipment(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/batch-delete")
|
||||
public ApiResponse<Void> deleteEquipmentBatch(@RequestBody List<UUID> ids) {
|
||||
equipmentService.deleteEquipmentBatch(ids);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
public ApiResponse<Map<String, Object>> importEquipment(@RequestParam("file") MultipartFile file, @RequestParam UUID projectId) {
|
||||
return ApiResponse.success(equipmentService.importFromExcel(file, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
public ResponseEntity<byte[]> exportEquipment(@RequestParam UUID projectId) {
|
||||
byte[] data = equipmentService.exportToExcel(projectId);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
||||
headers.setContentDispositionFormData("attachment", "设备列表.xlsx");
|
||||
return ResponseEntity.ok().headers(headers).body(data);
|
||||
}
|
||||
|
||||
@GetMapping("/by-project/{projectId}")
|
||||
public ApiResponse<List<Equipment>> getEquipmentsByProject(@PathVariable UUID projectId) {
|
||||
return ApiResponse.success(equipmentService.getEquipmentsByProject(projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/by-space/{spaceNodeId}")
|
||||
public ApiResponse<List<Equipment>> getEquipmentsBySpace(@PathVariable UUID spaceNodeId) {
|
||||
return ApiResponse.success(equipmentService.getEquipmentsBySpaceNode(spaceNodeId));
|
||||
}
|
||||
|
||||
@GetMapping("/by-type")
|
||||
public ApiResponse<List<Equipment>> getEquipmentsByType(@RequestParam EquipmentType type) {
|
||||
return ApiResponse.success(equipmentService.getEquipmentsByType(type));
|
||||
}
|
||||
|
||||
@GetMapping("/by-ownership")
|
||||
public ApiResponse<List<Equipment>> getEquipmentsByOwnership(@RequestParam OwnershipType ownership) {
|
||||
return ApiResponse.success(equipmentService.getEquipmentsByOwnership(ownership));
|
||||
}
|
||||
|
||||
// ==================== 设备统计 ====================
|
||||
|
||||
@GetMapping("/stats/by-type/{projectId}")
|
||||
public ApiResponse<Map<String, Long>> getStatsByType(@PathVariable UUID projectId) {
|
||||
return ApiResponse.success(equipmentService.getEquipmentStatsByType(projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/stats/by-ownership/{projectId}")
|
||||
public ApiResponse<Map<String, Long>> getStatsByOwnership(@PathVariable UUID projectId) {
|
||||
return ApiResponse.success(equipmentService.getEquipmentStatsByOwnership(projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/stats/count/{projectId}")
|
||||
public ApiResponse<Long> countByProject(@PathVariable UUID projectId) {
|
||||
return ApiResponse.success(equipmentService.countByProject(projectId));
|
||||
}
|
||||
|
||||
// ==================== 电梯扩展 ====================
|
||||
|
||||
@GetMapping("/{id}/elevator")
|
||||
public ApiResponse<EquipmentElevator> getElevator(@PathVariable UUID id) {
|
||||
return ApiResponse.success(
|
||||
elevatorService.getByEquipmentId(id).orElse(null)
|
||||
);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/elevator")
|
||||
public ApiResponse<EquipmentElevator> updateElevator(@PathVariable UUID id, @RequestBody EquipmentElevator elevator) {
|
||||
elevator.setEquipmentId(id);
|
||||
return ApiResponse.success(elevatorService.saveOrUpdate(elevator));
|
||||
}
|
||||
|
||||
// ==================== 暖通扩展 ====================
|
||||
|
||||
@GetMapping("/{id}/hvac")
|
||||
public ApiResponse<EquipmentHvac> getHvac(@PathVariable UUID id) {
|
||||
return ApiResponse.success(
|
||||
hvacService.getByEquipmentId(id).orElse(null)
|
||||
);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/hvac")
|
||||
public ApiResponse<EquipmentHvac> updateHvac(@PathVariable UUID id, @RequestBody EquipmentHvac hvac) {
|
||||
hvac.setEquipmentId(id);
|
||||
return ApiResponse.success(hvacService.saveOrUpdate(hvac));
|
||||
}
|
||||
|
||||
// ==================== 能源计量扩展 ====================
|
||||
|
||||
@GetMapping("/{id}/energy")
|
||||
public ApiResponse<EquipmentEnergy> getEnergy(@PathVariable UUID id) {
|
||||
return ApiResponse.success(
|
||||
energyService.getByEquipmentId(id).orElse(null)
|
||||
);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/energy")
|
||||
public ApiResponse<EquipmentEnergy> updateEnergy(@PathVariable UUID id, @RequestBody EquipmentEnergy energy) {
|
||||
energy.setEquipmentId(id);
|
||||
return ApiResponse.success(energyService.saveOrUpdate(energy));
|
||||
}
|
||||
|
||||
// ==================== 消防扩展 ====================
|
||||
|
||||
@GetMapping("/{id}/fire")
|
||||
public ApiResponse<EquipmentFire> getFire(@PathVariable UUID id) {
|
||||
return ApiResponse.success(
|
||||
fireService.getByEquipmentId(id).orElse(null)
|
||||
);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/fire")
|
||||
public ApiResponse<EquipmentFire> updateFire(@PathVariable UUID id, @RequestBody EquipmentFire fire) {
|
||||
fire.setEquipmentId(id);
|
||||
return ApiResponse.success(fireService.saveOrUpdate(fire));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
package com.ether.pms.mdm.controller;
|
||||
package com.ether.pms.asset.controller;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentFailureHistory;
|
||||
import com.ether.pms.asset.entity.EquipmentHealthScore;
|
||||
import com.ether.pms.asset.service.EquipmentHealthService;
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
import com.ether.pms.mdm.entity.EquipmentFailureHistory;
|
||||
import com.ether.pms.mdm.entity.EquipmentHealthScore;
|
||||
import com.ether.pms.mdm.service.EquipmentHealthService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -14,56 +14,38 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/ops")
|
||||
@RequestMapping("/api/asset/equipment-health")
|
||||
@RequiredArgsConstructor
|
||||
public class EquipmentHealthController {
|
||||
|
||||
private final EquipmentHealthService equipmentHealthService;
|
||||
|
||||
/**
|
||||
* 获取设备健康度
|
||||
*/
|
||||
@GetMapping("/equipment-health/{equipmentId}")
|
||||
@GetMapping("/{equipmentId}")
|
||||
public ApiResponse<EquipmentHealthScore> getEquipmentHealth(@PathVariable UUID equipmentId) {
|
||||
return ApiResponse.success(equipmentHealthService.getLatestHealthScore(equipmentId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备健康度历史
|
||||
*/
|
||||
@GetMapping("/equipment-health/{equipmentId}/history")
|
||||
@GetMapping("/{equipmentId}/history")
|
||||
public ApiResponse<List<EquipmentHealthScore>> getHealthHistory(@PathVariable UUID equipmentId) {
|
||||
return ApiResponse.success(equipmentHealthService.getHealthHistory(equipmentId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算设备健康度
|
||||
*/
|
||||
@PostMapping("/equipment-health/calculate")
|
||||
@PostMapping("/calculate")
|
||||
public ApiResponse<EquipmentHealthScore> calculateHealthScore(@RequestBody CalculateHealthRequest request) {
|
||||
return ApiResponse.success(equipmentHealthService.calculateHealthScore(request.getEquipmentId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录故障
|
||||
*/
|
||||
@PostMapping("/equipment-failure-history")
|
||||
@PostMapping("/failure-history")
|
||||
public ApiResponse<EquipmentFailureHistory> recordFailure(@Valid @RequestBody EquipmentFailureHistory failure) {
|
||||
return ApiResponse.success(equipmentHealthService.recordFailure(failure));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备故障历史
|
||||
*/
|
||||
@GetMapping("/equipment-failure-history/{equipmentId}")
|
||||
@GetMapping("/failure-history/{equipmentId}")
|
||||
public ApiResponse<List<EquipmentFailureHistory>> getFailureHistory(@PathVariable UUID equipmentId) {
|
||||
return ApiResponse.success(equipmentHealthService.getFailureHistory(equipmentId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备MTBF(平均故障间隔时间)
|
||||
*/
|
||||
@GetMapping("/equipment-mtbf/{equipmentId}")
|
||||
@GetMapping("/mtbf/{equipmentId}")
|
||||
public ApiResponse<MTBFResponse> getEquipmentMTBF(
|
||||
@PathVariable UUID equipmentId,
|
||||
@RequestParam(required = false, defaultValue = "30") Integer days) {
|
||||
|
|
@ -75,10 +57,7 @@ public class EquipmentHealthController {
|
|||
return ApiResponse.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备MTTR(平均修复时间)
|
||||
*/
|
||||
@GetMapping("/equipment-mttr/{equipmentId}")
|
||||
@GetMapping("/mttr/{equipmentId}")
|
||||
public ApiResponse<MTTRResponse> getEquipmentMTTR(
|
||||
@PathVariable UUID equipmentId,
|
||||
@RequestParam(required = false, defaultValue = "30") Integer days) {
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.ether.pms.asset.controller;
|
||||
|
||||
import com.ether.pms.asset.entity.OwnershipEntity;
|
||||
import com.ether.pms.asset.repository.OwnershipEntityRepository;
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/asset/ownership-entity")
|
||||
@RequiredArgsConstructor
|
||||
public class OwnershipEntityController {
|
||||
|
||||
private final OwnershipEntityRepository ownershipEntityRepository;
|
||||
|
||||
@PostMapping
|
||||
public ApiResponse<OwnershipEntity> create(@RequestBody OwnershipEntity entity) {
|
||||
return ApiResponse.success(ownershipEntityRepository.save(entity));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ApiResponse<OwnershipEntity> getById(@PathVariable UUID id) {
|
||||
return ApiResponse.success(
|
||||
ownershipEntityRepository.findByIdAndIsDeletedFalse(id).orElse(null)
|
||||
);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ApiResponse<OwnershipEntity> update(@PathVariable UUID id, @RequestBody OwnershipEntity entity) {
|
||||
OwnershipEntity existing = ownershipEntityRepository.findByIdAndIsDeletedFalse(id)
|
||||
.orElseThrow(() -> new RuntimeException("归属主体不存在: " + id));
|
||||
existing.setEntityName(entity.getEntityName());
|
||||
existing.setContactPerson(entity.getContactPerson());
|
||||
existing.setContactPhone(entity.getContactPhone());
|
||||
existing.setContactAddress(entity.getContactAddress());
|
||||
existing.setBusinessLicense(entity.getBusinessLicense());
|
||||
existing.setLegalRepresentative(entity.getLegalRepresentative());
|
||||
existing.setContractNo(entity.getContractNo());
|
||||
existing.setContractStartDate(entity.getContractStartDate());
|
||||
existing.setContractEndDate(entity.getContractEndDate());
|
||||
existing.setRentalFee(entity.getRentalFee());
|
||||
existing.setStatus(entity.getStatus());
|
||||
return ApiResponse.success(ownershipEntityRepository.save(existing));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResponse<Void> delete(@PathVariable UUID id) {
|
||||
OwnershipEntity entity = ownershipEntityRepository.findByIdAndIsDeletedFalse(id)
|
||||
.orElseThrow(() -> new RuntimeException("归属主体不存在: " + id));
|
||||
entity.setIsDeleted(true);
|
||||
ownershipEntityRepository.save(entity);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@GetMapping("/by-type")
|
||||
public ApiResponse<List<OwnershipEntity>> getByType(@RequestParam OwnershipEntity.EntityType type) {
|
||||
return ApiResponse.success(ownershipEntityRepository.findByEntityTypeAndIsDeletedFalse(type));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ApiResponse<List<OwnershipEntity>> getAll() {
|
||||
return ApiResponse.success(ownershipEntityRepository.findByIsDeletedFalse());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.ether.pms.asset.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
public class EquipmentCreateDTO {
|
||||
|
||||
@NotBlank(message = "设备名称不能为空")
|
||||
private String name;
|
||||
|
||||
@NotNull(message = "项目ID不能为空")
|
||||
private UUID projectId;
|
||||
|
||||
private UUID spaceNodeId;
|
||||
|
||||
private Boolean isEquipment = true;
|
||||
|
||||
private Integer designLifeYears;
|
||||
|
||||
private BigDecimal ratedPower;
|
||||
|
||||
private BigDecimal ratedVoltage;
|
||||
|
||||
private BigDecimal ratedCurrent;
|
||||
|
||||
private String maintenanceVendor;
|
||||
|
||||
private String maintenanceVendorContact;
|
||||
|
||||
private String maintenanceVendorPhone;
|
||||
|
||||
private String maintenanceContractNo;
|
||||
|
||||
private LocalDate maintenanceContractStart;
|
||||
|
||||
private LocalDate maintenanceContractEnd;
|
||||
|
||||
private String specialEquipmentType;
|
||||
|
||||
private String specialEquipmentCert;
|
||||
|
||||
private Integer inspectionCycle;
|
||||
|
||||
private LocalDate nextInspectionDate;
|
||||
|
||||
private LocalDate lastInspectionDate;
|
||||
|
||||
private String lastInspectionResult;
|
||||
|
||||
private BigDecimal energyConsumptionStandard;
|
||||
|
||||
private String installationEnvironment;
|
||||
|
||||
private String protectionLevel;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.ether.pms.asset.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
public class SpaceNodeDTO {
|
||||
private UUID id;
|
||||
private String nodeName;
|
||||
private String nodeCode;
|
||||
private String nodeType;
|
||||
private UUID parentId;
|
||||
private Integer floor;
|
||||
private String building;
|
||||
private String unit;
|
||||
private String roomNo;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.ether.pms.asset.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SpaceNodeEquipmentDTO extends SpaceNodeDTO {
|
||||
|
||||
private Boolean isEquipment;
|
||||
private Integer designLifeYears;
|
||||
private BigDecimal ratedPower;
|
||||
private BigDecimal ratedVoltage;
|
||||
private BigDecimal ratedCurrent;
|
||||
private String maintenanceVendor;
|
||||
private String maintenanceVendorContact;
|
||||
private String maintenanceVendorPhone;
|
||||
private String maintenanceContractNo;
|
||||
private LocalDate maintenanceContractStart;
|
||||
private LocalDate maintenanceContractEnd;
|
||||
private String specialEquipmentType;
|
||||
private String specialEquipmentCert;
|
||||
private Integer inspectionCycle;
|
||||
private LocalDate nextInspectionDate;
|
||||
private LocalDate lastInspectionDate;
|
||||
private String lastInspectionResult;
|
||||
private List<SparePartInfo> commonSpareParts;
|
||||
private BigDecimal energyConsumptionStandard;
|
||||
private String installationEnvironment;
|
||||
private String protectionLevel;
|
||||
|
||||
@Data
|
||||
public static class SparePartInfo {
|
||||
private String name;
|
||||
private String model;
|
||||
private Integer quantity;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
package com.ether.pms.asset.entity;
|
||||
|
||||
import com.ether.pms.asset.enums.EquipmentStatus;
|
||||
import com.ether.pms.asset.enums.EquipmentType;
|
||||
import com.ether.pms.asset.enums.OwnershipType;
|
||||
import com.ether.pms.asset.enums.SystemType;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "asset_equipment", indexes = {
|
||||
@Index(name = "idx_equipment_project", columnList = "project_id"),
|
||||
@Index(name = "idx_equipment_space", columnList = "space_node_id"),
|
||||
@Index(name = "idx_equipment_type", columnList = "equipment_type"),
|
||||
@Index(name = "idx_equipment_ownership", columnList = "ownership_type"),
|
||||
@Index(name = "idx_equipment_code", columnList = "equipment_code"),
|
||||
@Index(name = "idx_equipment_status", columnList = "status")
|
||||
})
|
||||
@Data
|
||||
public class Equipment {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "project_id")
|
||||
private UUID projectId;
|
||||
|
||||
@Column(name = "space_node_id")
|
||||
private UUID spaceNodeId;
|
||||
|
||||
@Column(name = "equipment_code", unique = true, nullable = false, length = 50)
|
||||
private String equipmentCode;
|
||||
|
||||
@Column(name = "equipment_name", nullable = false, length = 100)
|
||||
private String equipmentName;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "equipment_type", nullable = false, length = 30)
|
||||
private EquipmentType equipmentType;
|
||||
|
||||
@Column(name = "equipment_category", length = 50)
|
||||
private String equipmentCategory;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "system_type", length = 50)
|
||||
private SystemType systemType;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "ownership_type", nullable = false, length = 20)
|
||||
private OwnershipType ownershipType = OwnershipType.PROJECT;
|
||||
|
||||
@Column(name = "owning_entity_id")
|
||||
private UUID owningEntityId;
|
||||
|
||||
@Column(name = "owning_entity_name", length = 100)
|
||||
private String owningEntityName;
|
||||
|
||||
@Column(name = "asset_code", length = 50)
|
||||
private String assetCode;
|
||||
|
||||
@Column(name = "serial_number", length = 100)
|
||||
private String serialNumber;
|
||||
|
||||
@Column(length = 100)
|
||||
private String model;
|
||||
|
||||
@Column(length = 100)
|
||||
private String manufacturer;
|
||||
|
||||
@Column(length = 100)
|
||||
private String supplier;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(length = 20)
|
||||
private EquipmentStatus status = EquipmentStatus.ACTIVE;
|
||||
|
||||
@Column(name = "operation_status", length = 20)
|
||||
private String operationStatus;
|
||||
|
||||
@Column(name = "installation_location", length = 200)
|
||||
private String installationLocation;
|
||||
|
||||
@Column(name = "installation_date")
|
||||
private LocalDate installationDate;
|
||||
|
||||
@Column(name = "design_life_years")
|
||||
private Integer designLifeYears;
|
||||
|
||||
@Column(name = "rated_power", precision = 10, scale = 2)
|
||||
private BigDecimal ratedPower;
|
||||
|
||||
@Column(name = "rated_voltage", length = 20)
|
||||
private String ratedVoltage;
|
||||
|
||||
@Column(name = "rated_current", precision = 10, scale = 2)
|
||||
private BigDecimal ratedCurrent;
|
||||
|
||||
@Column(name = "maintenance_vendor", length = 100)
|
||||
private String maintenanceVendor;
|
||||
|
||||
@Column(name = "maintenance_vendor_contact", length = 50)
|
||||
private String maintenanceVendorContact;
|
||||
|
||||
@Column(name = "maintenance_vendor_phone", length = 20)
|
||||
private String maintenanceVendorPhone;
|
||||
|
||||
@Column(name = "maintenance_contract_no", length = 50)
|
||||
private String maintenanceContractNo;
|
||||
|
||||
@Column(name = "maintenance_contract_start")
|
||||
private LocalDate maintenanceContractStart;
|
||||
|
||||
@Column(name = "maintenance_contract_end")
|
||||
private LocalDate maintenanceContractEnd;
|
||||
|
||||
@Column(name = "purchase_date")
|
||||
private LocalDate purchaseDate;
|
||||
|
||||
@Column(name = "purchase_price", precision = 12, scale = 2)
|
||||
private BigDecimal purchasePrice;
|
||||
|
||||
@Column(name = "warranty_expire_date")
|
||||
private LocalDate warrantyExpireDate;
|
||||
|
||||
@Column(name = "energy_consumption_standard", precision = 12, scale = 2)
|
||||
private BigDecimal energyConsumptionStandard;
|
||||
|
||||
@Column(name = "inspection_cycle")
|
||||
private Integer inspectionCycle;
|
||||
|
||||
@Column(name = "next_inspection_date")
|
||||
private LocalDate nextInspectionDate;
|
||||
|
||||
@Column(name = "last_inspection_date")
|
||||
private LocalDate lastInspectionDate;
|
||||
|
||||
@Column(name = "last_inspection_result", length = 20)
|
||||
private String lastInspectionResult;
|
||||
|
||||
@Column(name = "special_equipment_type", length = 50)
|
||||
private String specialEquipmentType;
|
||||
|
||||
@Column(name = "special_equipment_cert", length = 100)
|
||||
private String specialEquipmentCert;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
@Column(name = "attributes", columnDefinition = "text")
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
@Column(name = "photos", columnDefinition = "jsonb")
|
||||
private List<EquipmentPhoto> photos;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
@Column(name = "documents", columnDefinition = "jsonb")
|
||||
private List<EquipmentDocument> documents;
|
||||
|
||||
@Column(name = "manual_url", length = 500)
|
||||
private String manualUrl;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "is_deleted")
|
||||
private Boolean isDeleted = false;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Column(name = "created_by")
|
||||
private UUID createdBy;
|
||||
|
||||
@Column(name = "updated_by")
|
||||
private UUID updatedBy;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
if (this.status == null) {
|
||||
this.status = EquipmentStatus.ACTIVE;
|
||||
}
|
||||
if (this.ownershipType == null) {
|
||||
this.ownershipType = OwnershipType.PROJECT;
|
||||
}
|
||||
if (this.isDeleted == null) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class EquipmentPhoto {
|
||||
private String type;
|
||||
private String url;
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class EquipmentDocument {
|
||||
private String name;
|
||||
private String url;
|
||||
private Long size;
|
||||
private String type;
|
||||
private String remark;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.ether.pms.asset.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "asset_equipment_elevator")
|
||||
@Data
|
||||
public class EquipmentElevator {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "equipment_id", unique = true, nullable = false)
|
||||
private UUID equipmentId;
|
||||
|
||||
@Column(name = "elevator_type", length = 30)
|
||||
private String elevatorType;
|
||||
|
||||
@Column(name = "elevator_model", length = 50)
|
||||
private String elevatorModel;
|
||||
|
||||
@Column(name = "load_capacity")
|
||||
private Integer loadCapacity;
|
||||
|
||||
@Column(precision = 10, scale = 2)
|
||||
private BigDecimal speed;
|
||||
|
||||
@Column(name = "floor_count")
|
||||
private Integer floorCount;
|
||||
|
||||
@Column(name = "shaft_dimensions", length = 50)
|
||||
private String shaftDimensions;
|
||||
|
||||
@Column(name = "pit_depth", precision = 10, scale = 2)
|
||||
private BigDecimal pitDepth;
|
||||
|
||||
@Column(name = "overhead_height", precision = 10, scale = 2)
|
||||
private BigDecimal overheadHeight;
|
||||
|
||||
@Column(name = "registration_no", length = 50)
|
||||
private String registrationNo;
|
||||
|
||||
@Column(name = "inspection_certificate", length = 100)
|
||||
private String inspectionCertificate;
|
||||
|
||||
@Column(name = "next_inspection_date")
|
||||
private LocalDate nextInspectionDate;
|
||||
|
||||
@Column(name = "energy_consumption", precision = 12, scale = 2)
|
||||
private BigDecimal energyConsumption;
|
||||
|
||||
@Column(name = "maintenance_level", length = 20)
|
||||
private String maintenanceLevel;
|
||||
|
||||
@Column(name = "rescue_plan", columnDefinition = "TEXT")
|
||||
private String rescuePlan;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package com.ether.pms.asset.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "asset_equipment_energy")
|
||||
@Data
|
||||
public class EquipmentEnergy {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "equipment_id", unique = true, nullable = false)
|
||||
private UUID equipmentId;
|
||||
|
||||
@Column(name = "meter_type", length = 30)
|
||||
private String meterType;
|
||||
|
||||
@Column(name = "energy_type", length = 30)
|
||||
private String energyType;
|
||||
|
||||
@Column(name = "meter_model", length = 50)
|
||||
private String meterModel;
|
||||
|
||||
@Column(name = "meter_specification", length = 50)
|
||||
private String meterSpecification;
|
||||
|
||||
@Column(name = "meter_constant", precision = 10, scale = 4)
|
||||
private BigDecimal meterConstant;
|
||||
|
||||
@Column(name = "accuracy_class", length = 10)
|
||||
private String accuracyClass;
|
||||
|
||||
@Column(name = "reading_type", length = 20)
|
||||
private String readingType;
|
||||
|
||||
@Column(name = "last_reading_date")
|
||||
private LocalDate lastReadingDate;
|
||||
|
||||
@Column(name = "last_reading_value", precision = 12, scale = 2)
|
||||
private BigDecimal lastReadingValue;
|
||||
|
||||
@Column(name = "current_reading_value", precision = 12, scale = 2)
|
||||
private BigDecimal currentReadingValue;
|
||||
|
||||
@Column(name = "unit_price", precision = 10, scale = 4)
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
@Column(name = "billing_type", length = 20)
|
||||
private String billingType;
|
||||
|
||||
@Column(name = "communication_type", length = 30)
|
||||
private String communicationType;
|
||||
|
||||
@Column(name = "communication_address", length = 50)
|
||||
private String communicationAddress;
|
||||
|
||||
@Column(name = "verification_cycle")
|
||||
private Integer verificationCycle;
|
||||
|
||||
@Column(name = "next_verification_date")
|
||||
private LocalDate nextVerificationDate;
|
||||
|
||||
@Column(name = "verification_certificate", length = 100)
|
||||
private String verificationCertificate;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ether.pms.mdm.entity;
|
||||
package com.ether.pms.asset.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
|
@ -54,7 +54,7 @@ public class EquipmentFailureHistory {
|
|||
@Column(name = "repair_end_time")
|
||||
private LocalDateTime repairEndTime;
|
||||
|
||||
@Column(name = "repair_duration_hours", precision = 10, scale = 2)
|
||||
@Column(name = "repair_duration_hours")
|
||||
private Double repairDurationHours;
|
||||
|
||||
@Column(name = "repair_person", length = 100)
|
||||
|
|
@ -64,7 +64,7 @@ public class EquipmentFailureHistory {
|
|||
@Enumerated(EnumType.STRING)
|
||||
private RepairResult repairResult;
|
||||
|
||||
@Column(name = "downtime_hours", precision = 10, scale = 2)
|
||||
@Column(name = "downtime_hours")
|
||||
private Double downtimeHours;
|
||||
|
||||
@Column(name = "maintenance_cost", precision = 12, scale = 2)
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.ether.pms.asset.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "asset_equipment_fire")
|
||||
@Data
|
||||
public class EquipmentFire {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "equipment_id", unique = true, nullable = false)
|
||||
private UUID equipmentId;
|
||||
|
||||
@Column(name = "fire_equipment_type", length = 30)
|
||||
private String fireEquipmentType;
|
||||
|
||||
@Column(name = "installation_area", precision = 10, scale = 2)
|
||||
private BigDecimal installationArea;
|
||||
|
||||
@Column(name = "installation_height", precision = 10, scale = 2)
|
||||
private BigDecimal installationHeight;
|
||||
|
||||
@Column(name = "detection_range", precision = 10, scale = 2)
|
||||
private BigDecimal detectionRange;
|
||||
|
||||
@Column(name = "system_type", length = 30)
|
||||
private String systemType;
|
||||
|
||||
@Column(name = "zone_number", length = 20)
|
||||
private String zoneNumber;
|
||||
|
||||
@Column(name = "loop_number", length = 20)
|
||||
private String loopNumber;
|
||||
|
||||
@Column(name = "linkage_enabled")
|
||||
private Boolean linkageEnabled;
|
||||
|
||||
@Column(name = "linkage_action", length = 100)
|
||||
private String linkageAction;
|
||||
|
||||
@Column(name = "inspection_cycle")
|
||||
private Integer inspectionCycle;
|
||||
|
||||
@Column(name = "last_inspection_date")
|
||||
private LocalDate lastInspectionDate;
|
||||
|
||||
@Column(name = "next_inspection_date")
|
||||
private LocalDate nextInspectionDate;
|
||||
|
||||
@Column(name = "inspection_result", length = 20)
|
||||
private String inspectionResult;
|
||||
|
||||
@Column(name = "special_requirement", columnDefinition = "TEXT")
|
||||
private String specialRequirement;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ether.pms.mdm.entity;
|
||||
package com.ether.pms.asset.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.ether.pms.asset.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "asset_equipment_hvac")
|
||||
@Data
|
||||
public class EquipmentHvac {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "equipment_id", unique = true, nullable = false)
|
||||
private UUID equipmentId;
|
||||
|
||||
@Column(name = "hvac_type", length = 30)
|
||||
private String hvacType;
|
||||
|
||||
@Column(name = "cooling_capacity", precision = 12, scale = 2)
|
||||
private BigDecimal coolingCapacity;
|
||||
|
||||
@Column(name = "heating_capacity", precision = 12, scale = 2)
|
||||
private BigDecimal heatingCapacity;
|
||||
|
||||
@Column(name = "air_flow", precision = 12, scale = 2)
|
||||
private BigDecimal airFlow;
|
||||
|
||||
@Column(name = "refrigerant_type", length = 30)
|
||||
private String refrigerantType;
|
||||
|
||||
@Column(name = "refrigerant_charge", precision = 10, scale = 2)
|
||||
private BigDecimal refrigerantCharge;
|
||||
|
||||
@Column(name = "energy_efficiency_ratio", precision = 10, scale = 2)
|
||||
private BigDecimal energyEfficiencyRatio;
|
||||
|
||||
@Column(name = "coefficient_of_performance", precision = 10, scale = 2)
|
||||
private BigDecimal coefficientOfPerformance;
|
||||
|
||||
@Column(name = "installation_date")
|
||||
private LocalDate installationDate;
|
||||
|
||||
@Column(name = "warranty_expire_date")
|
||||
private LocalDate warrantyExpireDate;
|
||||
|
||||
@Column(name = "filter_replacement_cycle")
|
||||
private Integer filterReplacementCycle;
|
||||
|
||||
@Column(name = "last_filter_replacement")
|
||||
private LocalDate lastFilterReplacement;
|
||||
|
||||
@Column(name = "duct_type", length = 30)
|
||||
private String ductType;
|
||||
|
||||
@Column(name = "duct_dimensions", length = 50)
|
||||
private String ductDimensions;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package com.ether.pms.asset.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "asset_ownership_entity", indexes = {
|
||||
@Index(name = "idx_ownership_entity_type", columnList = "entity_type"),
|
||||
@Index(name = "idx_ownership_entity_code", columnList = "entity_code")
|
||||
})
|
||||
@Data
|
||||
public class OwnershipEntity {
|
||||
|
||||
public enum EntityType {
|
||||
COMPANY("公司"),
|
||||
OWNER("业主"),
|
||||
RENTAL_COMPANY("租赁公司");
|
||||
|
||||
private final String description;
|
||||
|
||||
EntityType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "entity_type", nullable = false, length = 20)
|
||||
private EntityType entityType;
|
||||
|
||||
@Column(name = "entity_name", nullable = false, length = 100)
|
||||
private String entityName;
|
||||
|
||||
@Column(name = "entity_code", length = 50)
|
||||
private String entityCode;
|
||||
|
||||
@Column(name = "contact_person", length = 50)
|
||||
private String contactPerson;
|
||||
|
||||
@Column(name = "contact_phone", length = 20)
|
||||
private String contactPhone;
|
||||
|
||||
@Column(name = "contact_address", length = 255)
|
||||
private String contactAddress;
|
||||
|
||||
@Column(name = "business_license", length = 50)
|
||||
private String businessLicense;
|
||||
|
||||
@Column(name = "legal_representative", length = 50)
|
||||
private String legalRepresentative;
|
||||
|
||||
@Column(name = "contract_no", length = 50)
|
||||
private String contractNo;
|
||||
|
||||
@Column(name = "contract_start_date")
|
||||
private LocalDate contractStartDate;
|
||||
|
||||
@Column(name = "contract_end_date")
|
||||
private LocalDate contractEndDate;
|
||||
|
||||
@Column(name = "rental_fee", precision = 12, scale = 2)
|
||||
private BigDecimal rentalFee;
|
||||
|
||||
@Column(length = 20)
|
||||
private String status = "ACTIVE";
|
||||
|
||||
@Column(name = "is_deleted")
|
||||
private Boolean isDeleted = false;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
if (this.status == null) {
|
||||
this.status = "ACTIVE";
|
||||
}
|
||||
if (this.isDeleted == null) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.ether.pms.asset.enums;
|
||||
|
||||
public enum EquipmentStatus {
|
||||
ACTIVE("在用"),
|
||||
INACTIVE("停用"),
|
||||
MAINTENANCE("维保中"),
|
||||
SCRAPPED("已报废");
|
||||
|
||||
private final String description;
|
||||
|
||||
EquipmentStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.ether.pms.asset.enums;
|
||||
|
||||
public enum EquipmentType {
|
||||
ELEVATOR("电梯系统"),
|
||||
HVAC("暖通空调"),
|
||||
FIRE_PROTECTION("消防系统"),
|
||||
PLUMBING("给排水系统"),
|
||||
ELECTRICAL("电气系统"),
|
||||
ENERGY_METER("能源计量"),
|
||||
SECURITY("弱电系统"),
|
||||
LANDSCAPE("景观绿化"),
|
||||
KITCHEN("厨余设备"),
|
||||
OTHER("其他设备");
|
||||
|
||||
private final String description;
|
||||
|
||||
EquipmentType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.ether.pms.asset.enums;
|
||||
|
||||
public enum OwnershipType {
|
||||
PROJECT("项目自有"),
|
||||
COMPANY("公司统筹"),
|
||||
OWNER("业主自置"),
|
||||
RENTAL("租赁设备");
|
||||
|
||||
private final String description;
|
||||
|
||||
OwnershipType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.ether.pms.asset.enums;
|
||||
|
||||
/**
|
||||
* 商业地产8大系统分类枚举
|
||||
*/
|
||||
public enum SystemType {
|
||||
HVAC("暖通空调"),
|
||||
FIRE("消防系统"),
|
||||
ELEVATOR("电梯系统"),
|
||||
ELECTRICAL("电气系统"),
|
||||
PLUMBING("给排水"),
|
||||
BAS("弱电智能化"),
|
||||
KITCHEN("餐饮厨房"),
|
||||
LANDSCAPE("景观"),
|
||||
OTHER("其他");
|
||||
|
||||
private final String description;
|
||||
|
||||
SystemType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentElevator;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface EquipmentElevatorRepository extends JpaRepository<EquipmentElevator, UUID> {
|
||||
|
||||
Optional<EquipmentElevator> findByEquipmentId(UUID equipmentId);
|
||||
|
||||
void deleteByEquipmentId(UUID equipmentId);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentEnergy;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface EquipmentEnergyRepository extends JpaRepository<EquipmentEnergy, UUID> {
|
||||
|
||||
Optional<EquipmentEnergy> findByEquipmentId(UUID equipmentId);
|
||||
|
||||
void deleteByEquipmentId(UUID equipmentId);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.ether.pms.mdm.repository;
|
||||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.mdm.entity.EquipmentFailureHistory;
|
||||
import com.ether.pms.asset.entity.EquipmentFailureHistory;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentFire;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface EquipmentFireRepository extends JpaRepository<EquipmentFire, UUID> {
|
||||
|
||||
Optional<EquipmentFire> findByEquipmentId(UUID equipmentId);
|
||||
|
||||
void deleteByEquipmentId(UUID equipmentId);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.ether.pms.mdm.repository;
|
||||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.mdm.entity.EquipmentHealthScore;
|
||||
import com.ether.pms.asset.entity.EquipmentHealthScore;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentHvac;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface EquipmentHvacRepository extends JpaRepository<EquipmentHvac, UUID> {
|
||||
|
||||
Optional<EquipmentHvac> findByEquipmentId(UUID equipmentId);
|
||||
|
||||
void deleteByEquipmentId(UUID equipmentId);
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.asset.entity.Equipment;
|
||||
import com.ether.pms.asset.enums.EquipmentStatus;
|
||||
import com.ether.pms.asset.enums.EquipmentType;
|
||||
import com.ether.pms.asset.enums.OwnershipType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface EquipmentRepository extends JpaRepository<Equipment, UUID> {
|
||||
|
||||
List<Equipment> findByProjectIdAndIsDeletedFalse(UUID projectId);
|
||||
|
||||
List<Equipment> findByProjectIdAndStatusAndIsDeletedFalse(UUID projectId, EquipmentStatus status);
|
||||
|
||||
List<Equipment> findBySpaceNodeIdAndIsDeletedFalse(UUID spaceNodeId);
|
||||
|
||||
List<Equipment> findByEquipmentTypeAndIsDeletedFalse(EquipmentType equipmentType);
|
||||
|
||||
List<Equipment> findByOwnershipTypeAndIsDeletedFalse(OwnershipType ownershipType);
|
||||
|
||||
Optional<Equipment> findByEquipmentCode(String equipmentCode);
|
||||
|
||||
Optional<Equipment> findByIdAndIsDeletedFalse(UUID id);
|
||||
|
||||
boolean existsByEquipmentCode(String equipmentCode);
|
||||
|
||||
@Query("SELECT e FROM Equipment e WHERE e.projectId = :projectId AND e.isDeleted = false")
|
||||
List<Equipment> findAllByProject(@Param("projectId") UUID projectId);
|
||||
|
||||
@Query("SELECT e FROM Equipment e WHERE e.spaceNodeId = :spaceNodeId AND e.isDeleted = false")
|
||||
List<Equipment> findBySpaceNode(@Param("spaceNodeId") UUID spaceNodeId);
|
||||
|
||||
@Query("SELECT COUNT(e) FROM Equipment e WHERE e.projectId = :projectId AND e.isDeleted = false")
|
||||
long countByProject(@Param("projectId") UUID projectId);
|
||||
|
||||
@Query("SELECT e.equipmentType, COUNT(e) FROM Equipment e WHERE e.projectId = :projectId AND e.isDeleted = false GROUP BY e.equipmentType")
|
||||
List<Object[]> countByType(@Param("projectId") UUID projectId);
|
||||
|
||||
@Query("SELECT e.ownershipType, COUNT(e) FROM Equipment e WHERE e.projectId = :projectId AND e.isDeleted = false GROUP BY e.ownershipType")
|
||||
List<Object[]> countByOwnership(@Param("projectId") UUID projectId);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.asset.entity.OwnershipEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface OwnershipEntityRepository extends JpaRepository<OwnershipEntity, UUID> {
|
||||
|
||||
List<OwnershipEntity> findByEntityTypeAndIsDeletedFalse(OwnershipEntity.EntityType entityType);
|
||||
|
||||
Optional<OwnershipEntity> findByEntityCode(String entityCode);
|
||||
|
||||
Optional<OwnershipEntity> findByIdAndIsDeletedFalse(UUID id);
|
||||
|
||||
boolean existsByEntityCode(String entityCode);
|
||||
|
||||
List<OwnershipEntity> findByIsDeletedFalse();
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.ether.pms.asset.service;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentElevator;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EquipmentElevatorService {
|
||||
|
||||
EquipmentElevator saveOrUpdate(EquipmentElevator elevator);
|
||||
|
||||
Optional<EquipmentElevator> getByEquipmentId(UUID equipmentId);
|
||||
|
||||
void deleteByEquipmentId(UUID equipmentId);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.ether.pms.asset.service;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentEnergy;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EquipmentEnergyService {
|
||||
|
||||
EquipmentEnergy saveOrUpdate(EquipmentEnergy energy);
|
||||
|
||||
Optional<EquipmentEnergy> getByEquipmentId(UUID equipmentId);
|
||||
|
||||
void deleteByEquipmentId(UUID equipmentId);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.ether.pms.asset.service;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentFire;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EquipmentFireService {
|
||||
|
||||
EquipmentFire saveOrUpdate(EquipmentFire fire);
|
||||
|
||||
Optional<EquipmentFire> getByEquipmentId(UUID equipmentId);
|
||||
|
||||
void deleteByEquipmentId(UUID equipmentId);
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.ether.pms.mdm.service;
|
||||
package com.ether.pms.asset.service;
|
||||
|
||||
import com.ether.pms.mdm.entity.EquipmentFailureHistory;
|
||||
import com.ether.pms.mdm.entity.EquipmentHealthScore;
|
||||
import com.ether.pms.asset.entity.EquipmentFailureHistory;
|
||||
import com.ether.pms.asset.entity.EquipmentHealthScore;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.ether.pms.asset.service;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentHvac;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EquipmentHvacService {
|
||||
|
||||
EquipmentHvac saveOrUpdate(EquipmentHvac hvac);
|
||||
|
||||
Optional<EquipmentHvac> getByEquipmentId(UUID equipmentId);
|
||||
|
||||
void deleteByEquipmentId(UUID equipmentId);
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.ether.pms.asset.service;
|
||||
|
||||
import com.ether.pms.asset.entity.Equipment;
|
||||
import com.ether.pms.asset.enums.EquipmentType;
|
||||
import com.ether.pms.asset.enums.OwnershipType;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EquipmentService {
|
||||
|
||||
Equipment createEquipment(Equipment equipment);
|
||||
|
||||
Equipment updateEquipment(UUID id, Equipment equipment);
|
||||
|
||||
void deleteEquipment(UUID id);
|
||||
|
||||
Equipment getEquipmentById(UUID id);
|
||||
|
||||
List<Equipment> getEquipmentsByProject(UUID projectId);
|
||||
|
||||
List<Equipment> getEquipmentsBySpaceNode(UUID spaceNodeId);
|
||||
|
||||
List<Equipment> getEquipmentsByType(EquipmentType equipmentType);
|
||||
|
||||
List<Equipment> getEquipmentsByOwnership(OwnershipType ownershipType);
|
||||
|
||||
Map<String, Long> getEquipmentStatsByType(UUID projectId);
|
||||
|
||||
Map<String, Long> getEquipmentStatsByOwnership(UUID projectId);
|
||||
|
||||
long countByProject(UUID projectId);
|
||||
|
||||
void deleteEquipmentBatch(List<UUID> ids);
|
||||
|
||||
Map<String, Object> importFromExcel(MultipartFile file, UUID projectId);
|
||||
|
||||
byte[] exportToExcel(UUID projectId);
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.ether.pms.asset.service.impl;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentElevator;
|
||||
import com.ether.pms.asset.repository.EquipmentElevatorRepository;
|
||||
import com.ether.pms.asset.service.EquipmentElevatorService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EquipmentElevatorServiceImpl implements EquipmentElevatorService {
|
||||
|
||||
private final EquipmentElevatorRepository elevatorRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public EquipmentElevator saveOrUpdate(EquipmentElevator elevator) {
|
||||
Optional<EquipmentElevator> existing = elevatorRepository.findByEquipmentId(elevator.getEquipmentId());
|
||||
if (existing.isPresent()) {
|
||||
elevator.setId(existing.get().getId());
|
||||
}
|
||||
return elevatorRepository.save(elevator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<EquipmentElevator> getByEquipmentId(UUID equipmentId) {
|
||||
return elevatorRepository.findByEquipmentId(equipmentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteByEquipmentId(UUID equipmentId) {
|
||||
elevatorRepository.deleteByEquipmentId(equipmentId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.ether.pms.asset.service.impl;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentEnergy;
|
||||
import com.ether.pms.asset.repository.EquipmentEnergyRepository;
|
||||
import com.ether.pms.asset.service.EquipmentEnergyService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EquipmentEnergyServiceImpl implements EquipmentEnergyService {
|
||||
|
||||
private final EquipmentEnergyRepository energyRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public EquipmentEnergy saveOrUpdate(EquipmentEnergy energy) {
|
||||
Optional<EquipmentEnergy> existing = energyRepository.findByEquipmentId(energy.getEquipmentId());
|
||||
if (existing.isPresent()) {
|
||||
energy.setId(existing.get().getId());
|
||||
}
|
||||
return energyRepository.save(energy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<EquipmentEnergy> getByEquipmentId(UUID equipmentId) {
|
||||
return energyRepository.findByEquipmentId(equipmentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteByEquipmentId(UUID equipmentId) {
|
||||
energyRepository.deleteByEquipmentId(equipmentId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.ether.pms.asset.service.impl;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentFire;
|
||||
import com.ether.pms.asset.repository.EquipmentFireRepository;
|
||||
import com.ether.pms.asset.service.EquipmentFireService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EquipmentFireServiceImpl implements EquipmentFireService {
|
||||
|
||||
private final EquipmentFireRepository fireRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public EquipmentFire saveOrUpdate(EquipmentFire fire) {
|
||||
Optional<EquipmentFire> existing = fireRepository.findByEquipmentId(fire.getEquipmentId());
|
||||
if (existing.isPresent()) {
|
||||
fire.setId(existing.get().getId());
|
||||
}
|
||||
return fireRepository.save(fire);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<EquipmentFire> getByEquipmentId(UUID equipmentId) {
|
||||
return fireRepository.findByEquipmentId(equipmentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteByEquipmentId(UUID equipmentId) {
|
||||
fireRepository.deleteByEquipmentId(equipmentId);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,13 @@
|
|||
package com.ether.pms.mdm.service.impl;
|
||||
package com.ether.pms.asset.service.impl;
|
||||
|
||||
import com.ether.pms.common.BusinessException;
|
||||
import com.ether.pms.mdm.entity.EquipmentFailureHistory;
|
||||
import com.ether.pms.mdm.entity.EquipmentHealthScore;
|
||||
import com.ether.pms.mdm.entity.MaintenanceTask;
|
||||
import com.ether.pms.asset.entity.EquipmentFailureHistory;
|
||||
import com.ether.pms.asset.entity.EquipmentHealthScore;
|
||||
import com.ether.pms.mdm.entity.SpaceNode;
|
||||
import com.ether.pms.mdm.repository.EquipmentFailureHistoryRepository;
|
||||
import com.ether.pms.mdm.repository.EquipmentHealthScoreRepository;
|
||||
import com.ether.pms.mdm.repository.MaintenanceTaskRepository;
|
||||
import com.ether.pms.asset.repository.EquipmentFailureHistoryRepository;
|
||||
import com.ether.pms.asset.repository.EquipmentHealthScoreRepository;
|
||||
import com.ether.pms.mdm.repository.SpaceNodeRepository;
|
||||
import com.ether.pms.mdm.service.EquipmentHealthService;
|
||||
import com.ether.pms.asset.service.EquipmentHealthService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -32,7 +30,8 @@ public class EquipmentHealthServiceImpl implements EquipmentHealthService {
|
|||
private final EquipmentFailureHistoryRepository failureHistoryRepository;
|
||||
private final EquipmentHealthScoreRepository healthScoreRepository;
|
||||
private final SpaceNodeRepository spaceNodeRepository;
|
||||
private final MaintenanceTaskRepository maintenanceTaskRepository;
|
||||
// TODO: 需要改为从 ops 模块查询工单数据
|
||||
// private final MaintenanceTaskRepository maintenanceTaskRepository;
|
||||
|
||||
private static final BigDecimal BASE_SCORE = new BigDecimal("100");
|
||||
private static final BigDecimal FAILURE_DEDUCTION_PER_COUNT = new BigDecimal("5");
|
||||
|
|
@ -58,20 +57,10 @@ public class EquipmentHealthServiceImpl implements EquipmentHealthService {
|
|||
// 计算故障率扣分
|
||||
BigDecimal failureDeduction = FAILURE_DEDUCTION_PER_COUNT.multiply(BigDecimal.valueOf(failureCount30d));
|
||||
|
||||
// 计算维保完成率
|
||||
List<MaintenanceTask> tasks = maintenanceTaskRepository.findByEquipmentIdAndStatusNot(equipmentId, MaintenanceTask.Status.CANCELLED);
|
||||
long totalTasks = tasks.size();
|
||||
long completedTasks = tasks.stream()
|
||||
.filter(t -> t.getStatus() == MaintenanceTask.Status.COMPLETED)
|
||||
.count();
|
||||
|
||||
// TODO: 从 ops 模块查询工单数据计算维保完成率
|
||||
// 暂时跳过维保完成率计算
|
||||
BigDecimal maintenanceCompletionRate = BigDecimal.ONE;
|
||||
if (totalTasks > 0) {
|
||||
maintenanceCompletionRate = BigDecimal.valueOf(completedTasks)
|
||||
.divide(BigDecimal.valueOf(totalTasks), 4, RoundingMode.HALF_UP);
|
||||
}
|
||||
BigDecimal maintenanceDeduction = BigDecimal.ONE.subtract(maintenanceCompletionRate)
|
||||
.multiply(MAINTENANCE_DEDUCTION_FACTOR);
|
||||
BigDecimal maintenanceDeduction = BigDecimal.ZERO;
|
||||
|
||||
// 计算年龄扣分
|
||||
BigDecimal equipmentAgeYears = calculateEquipmentAge(equipment);
|
||||
|
|
@ -97,9 +86,8 @@ public class EquipmentHealthServiceImpl implements EquipmentHealthService {
|
|||
|
||||
// 创建健康度记录
|
||||
EquipmentHealthScore health = new EquipmentHealthScore();
|
||||
health.setProjectId(equipment.getProjectId());
|
||||
health.setProjectId(UUID.fromString("00000000-0000-0000-0000-000000000000")); // 需要根据projectCode查询
|
||||
health.setEquipmentId(equipmentId);
|
||||
health.setEquipmentCode(equipment.getCode());
|
||||
health.setEquipmentName(equipment.getName());
|
||||
health.setHealthScore(healthScore.setScale(2, RoundingMode.HALF_UP));
|
||||
health.setFailureDeduction(failureDeduction.setScale(2, RoundingMode.HALF_UP));
|
||||
|
|
@ -188,13 +176,10 @@ public class EquipmentHealthServiceImpl implements EquipmentHealthService {
|
|||
|
||||
// 设置项目ID
|
||||
if (failure.getProjectId() == null) {
|
||||
failure.setProjectId(equipment.getProjectId());
|
||||
failure.setProjectId(UUID.fromString("00000000-0000-0000-0000-000000000000")); // 需要根据projectCode查询
|
||||
}
|
||||
|
||||
// 设置设备信息
|
||||
if (failure.getEquipmentCode() == null) {
|
||||
failure.setEquipmentCode(equipment.getCode());
|
||||
}
|
||||
if (failure.getEquipmentName() == null) {
|
||||
failure.setEquipmentName(equipment.getName());
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.ether.pms.asset.service.impl;
|
||||
|
||||
import com.ether.pms.asset.entity.EquipmentHvac;
|
||||
import com.ether.pms.asset.repository.EquipmentHvacRepository;
|
||||
import com.ether.pms.asset.service.EquipmentHvacService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EquipmentHvacServiceImpl implements EquipmentHvacService {
|
||||
|
||||
private final EquipmentHvacRepository hvacRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public EquipmentHvac saveOrUpdate(EquipmentHvac hvac) {
|
||||
Optional<EquipmentHvac> existing = hvacRepository.findByEquipmentId(hvac.getEquipmentId());
|
||||
if (existing.isPresent()) {
|
||||
hvac.setId(existing.get().getId());
|
||||
}
|
||||
return hvacRepository.save(hvac);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<EquipmentHvac> getByEquipmentId(UUID equipmentId) {
|
||||
return hvacRepository.findByEquipmentId(equipmentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteByEquipmentId(UUID equipmentId) {
|
||||
hvacRepository.deleteByEquipmentId(equipmentId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,405 @@
|
|||
package com.ether.pms.asset.service.impl;
|
||||
|
||||
import com.ether.pms.common.BusinessException;
|
||||
import com.ether.pms.common.ErrorCode;
|
||||
import com.ether.pms.asset.entity.Equipment;
|
||||
import com.ether.pms.asset.enums.EquipmentStatus;
|
||||
import com.ether.pms.asset.enums.EquipmentType;
|
||||
import com.ether.pms.asset.enums.OwnershipType;
|
||||
import com.ether.pms.asset.enums.SystemType;
|
||||
import com.ether.pms.asset.repository.EquipmentRepository;
|
||||
import com.ether.pms.asset.service.EquipmentService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EquipmentServiceImpl implements EquipmentService {
|
||||
|
||||
private final EquipmentRepository equipmentRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Equipment createEquipment(Equipment equipment) {
|
||||
if (equipment.getEquipmentCode() == null || equipment.getEquipmentCode().isEmpty()) {
|
||||
equipment.setEquipmentCode(generateEquipmentCode());
|
||||
}
|
||||
if (equipment.getStatus() == null) {
|
||||
equipment.setStatus(EquipmentStatus.ACTIVE);
|
||||
}
|
||||
if (equipment.getOwnershipType() == null) {
|
||||
equipment.setOwnershipType(OwnershipType.PROJECT);
|
||||
}
|
||||
return equipmentRepository.save(equipment);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Equipment updateEquipment(UUID id, Equipment equipment) {
|
||||
Equipment existing = equipmentRepository.findByIdAndIsDeletedFalse(id)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND, "设备不存在"));
|
||||
updateFields(existing, equipment);
|
||||
return equipmentRepository.save(existing);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteEquipment(UUID id) {
|
||||
Equipment equipment = equipmentRepository.findByIdAndIsDeletedFalse(id)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND, "设备不存在"));
|
||||
equipment.setIsDeleted(true);
|
||||
equipment.setStatus(EquipmentStatus.INACTIVE);
|
||||
equipmentRepository.save(equipment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Equipment getEquipmentById(UUID id) {
|
||||
return equipmentRepository.findByIdAndIsDeletedFalse(id)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND, "设备不存在"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Equipment> getEquipmentsByProject(UUID projectId) {
|
||||
return equipmentRepository.findByProjectIdAndIsDeletedFalse(projectId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Equipment> getEquipmentsBySpaceNode(UUID spaceNodeId) {
|
||||
return equipmentRepository.findBySpaceNodeIdAndIsDeletedFalse(spaceNodeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Equipment> getEquipmentsByType(EquipmentType equipmentType) {
|
||||
return equipmentRepository.findByEquipmentTypeAndIsDeletedFalse(equipmentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Equipment> getEquipmentsByOwnership(OwnershipType ownershipType) {
|
||||
return equipmentRepository.findByOwnershipTypeAndIsDeletedFalse(ownershipType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Long> getEquipmentStatsByType(UUID projectId) {
|
||||
List<Object[]> results = equipmentRepository.countByType(projectId);
|
||||
Map<String, Long> stats = new HashMap<>();
|
||||
for (Object[] result : results) {
|
||||
EquipmentType type = (EquipmentType) result[0];
|
||||
Long count = (Long) result[1];
|
||||
stats.put(type.name(), count);
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Long> getEquipmentStatsByOwnership(UUID projectId) {
|
||||
List<Object[]> results = equipmentRepository.countByOwnership(projectId);
|
||||
Map<String, Long> stats = new HashMap<>();
|
||||
for (Object[] result : results) {
|
||||
OwnershipType type = (OwnershipType) result[0];
|
||||
Long count = (Long) result[1];
|
||||
stats.put(type.name(), count);
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByProject(UUID projectId) {
|
||||
return equipmentRepository.countByProject(projectId);
|
||||
}
|
||||
|
||||
private String generateEquipmentCode() {
|
||||
return "EQC-" + LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
}
|
||||
|
||||
private void updateFields(Equipment existing, Equipment updated) {
|
||||
existing.setEquipmentName(updated.getEquipmentName());
|
||||
existing.setEquipmentType(updated.getEquipmentType());
|
||||
existing.setEquipmentCategory(updated.getEquipmentCategory());
|
||||
existing.setSystemType(updated.getSystemType());
|
||||
existing.setOwnershipType(updated.getOwnershipType());
|
||||
existing.setOwningEntityId(updated.getOwningEntityId());
|
||||
existing.setOwningEntityName(updated.getOwningEntityName());
|
||||
existing.setAssetCode(updated.getAssetCode());
|
||||
existing.setSerialNumber(updated.getSerialNumber());
|
||||
existing.setModel(updated.getModel());
|
||||
existing.setManufacturer(updated.getManufacturer());
|
||||
existing.setSupplier(updated.getSupplier());
|
||||
existing.setStatus(updated.getStatus());
|
||||
existing.setOperationStatus(updated.getOperationStatus());
|
||||
existing.setInstallationLocation(updated.getInstallationLocation());
|
||||
existing.setInstallationDate(updated.getInstallationDate());
|
||||
existing.setDesignLifeYears(updated.getDesignLifeYears());
|
||||
existing.setRatedPower(updated.getRatedPower());
|
||||
existing.setRatedVoltage(updated.getRatedVoltage());
|
||||
existing.setRatedCurrent(updated.getRatedCurrent());
|
||||
existing.setMaintenanceVendor(updated.getMaintenanceVendor());
|
||||
existing.setMaintenanceVendorContact(updated.getMaintenanceVendorContact());
|
||||
existing.setMaintenanceVendorPhone(updated.getMaintenanceVendorPhone());
|
||||
existing.setMaintenanceContractNo(updated.getMaintenanceContractNo());
|
||||
existing.setMaintenanceContractStart(updated.getMaintenanceContractStart());
|
||||
existing.setMaintenanceContractEnd(updated.getMaintenanceContractEnd());
|
||||
existing.setEnergyConsumptionStandard(updated.getEnergyConsumptionStandard());
|
||||
existing.setInspectionCycle(updated.getInspectionCycle());
|
||||
existing.setNextInspectionDate(updated.getNextInspectionDate());
|
||||
existing.setLastInspectionDate(updated.getLastInspectionDate());
|
||||
existing.setLastInspectionResult(updated.getLastInspectionResult());
|
||||
existing.setSpecialEquipmentType(updated.getSpecialEquipmentType());
|
||||
existing.setSpecialEquipmentCert(updated.getSpecialEquipmentCert());
|
||||
existing.setAttributes(updated.getAttributes());
|
||||
existing.setRemarks(updated.getRemarks());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteEquipmentBatch(List<UUID> ids) {
|
||||
for (UUID id : ids) {
|
||||
try {
|
||||
deleteEquipment(id);
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ErrorCode.BAD_REQUEST, "删除设备失败: " + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Map<String, Object> importFromExcel(MultipartFile file, UUID projectId) {
|
||||
List<String> successList = new ArrayList<>();
|
||||
List<String> failList = new ArrayList<>();
|
||||
|
||||
try (Workbook workbook = new XSSFWorkbook(file.getInputStream())) {
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
if (sheet == null) {
|
||||
throw new BusinessException(ErrorCode.BAD_REQUEST, "Excel文件为空");
|
||||
}
|
||||
|
||||
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
|
||||
Row row = sheet.getRow(i);
|
||||
if (row == null || isRowEmpty(row)) continue;
|
||||
|
||||
try {
|
||||
Equipment equipment = parseEquipmentFromRow(row, projectId);
|
||||
equipmentRepository.save(equipment);
|
||||
successList.add(equipment.getEquipmentName());
|
||||
} catch (Exception e) {
|
||||
failList.add("第" + (i + 1) + "行: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException(ErrorCode.BAD_REQUEST, "文件解析失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("successCount", successList.size());
|
||||
result.put("failCount", failList.size());
|
||||
result.put("failDetails", failList);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportToExcel(UUID projectId) {
|
||||
List<Equipment> equipmentList = equipmentRepository.findByProjectIdAndIsDeletedFalse(projectId);
|
||||
|
||||
try (Workbook workbook = new XSSFWorkbook();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
Sheet sheet = workbook.createSheet("设备列表");
|
||||
|
||||
String[] headers = {"设备编码", "设备名称", "设备类型", "系统类型", "归属类型",
|
||||
"型号", "厂商", "额定功率(kW)", "额定电压(V)", "安装位置",
|
||||
"维保商", "维保电话", "年检周期(月)", "购置日期", "购置价格", "保修到期"};
|
||||
Row headerRow = sheet.createRow(0);
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
cell.setCellStyle(createHeaderStyle(workbook));
|
||||
}
|
||||
|
||||
int rowNum = 1;
|
||||
for (Equipment eq : equipmentList) {
|
||||
Row row = sheet.createRow(rowNum++);
|
||||
row.createCell(0).setCellValue(eq.getEquipmentCode() != null ? eq.getEquipmentCode() : "");
|
||||
row.createCell(1).setCellValue(eq.getEquipmentName() != null ? eq.getEquipmentName() : "");
|
||||
row.createCell(2).setCellValue(eq.getEquipmentType() != null ? eq.getEquipmentType().getDescription() : "");
|
||||
row.createCell(3).setCellValue(eq.getSystemType() != null ? eq.getSystemType().getDescription() : "");
|
||||
row.createCell(4).setCellValue(eq.getOwnershipType() != null ? eq.getOwnershipType().getDescription() : "");
|
||||
row.createCell(5).setCellValue(eq.getModel() != null ? eq.getModel() : "");
|
||||
row.createCell(6).setCellValue(eq.getManufacturer() != null ? eq.getManufacturer() : "");
|
||||
row.createCell(7).setCellValue(eq.getRatedPower() != null ? eq.getRatedPower().doubleValue() : 0);
|
||||
row.createCell(8).setCellValue(eq.getRatedVoltage() != null ? eq.getRatedVoltage() : "");
|
||||
row.createCell(9).setCellValue(eq.getInstallationLocation() != null ? eq.getInstallationLocation() : "");
|
||||
row.createCell(10).setCellValue(eq.getMaintenanceVendor() != null ? eq.getMaintenanceVendor() : "");
|
||||
row.createCell(11).setCellValue(eq.getMaintenanceVendorPhone() != null ? eq.getMaintenanceVendorPhone() : "");
|
||||
row.createCell(12).setCellValue(eq.getInspectionCycle() != null ? eq.getInspectionCycle() : 0);
|
||||
row.createCell(13).setCellValue(eq.getPurchaseDate() != null ? eq.getPurchaseDate().toString() : "");
|
||||
row.createCell(14).setCellValue(eq.getPurchasePrice() != null ? eq.getPurchasePrice().doubleValue() : 0);
|
||||
row.createCell(15).setCellValue(eq.getWarrantyExpireDate() != null ? eq.getWarrantyExpireDate().toString() : "");
|
||||
}
|
||||
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
|
||||
workbook.write(out);
|
||||
return out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "导出Excel失败");
|
||||
}
|
||||
}
|
||||
|
||||
private Equipment parseEquipmentFromRow(Row row, UUID projectId) {
|
||||
Equipment equipment = new Equipment();
|
||||
equipment.setProjectId(projectId);
|
||||
equipment.setEquipmentName(getCellStringValue(row.getCell(0)));
|
||||
equipment.setEquipmentCode(getCellStringValue(row.getCell(1)));
|
||||
equipment.setEquipmentType(parseEquipmentType(getCellStringValue(row.getCell(2))));
|
||||
equipment.setSystemType(parseSystemType(getCellStringValue(row.getCell(3))));
|
||||
equipment.setOwnershipType(parseOwnershipType(getCellStringValue(row.getCell(4))));
|
||||
equipment.setModel(getCellStringValue(row.getCell(5)));
|
||||
equipment.setManufacturer(getCellStringValue(row.getCell(6)));
|
||||
equipment.setRatedPower(getCellNumericValue(row.getCell(7)));
|
||||
equipment.setRatedVoltage(getCellStringValue(row.getCell(8)));
|
||||
equipment.setInstallationLocation(getCellStringValue(row.getCell(9)));
|
||||
equipment.setMaintenanceVendor(getCellStringValue(row.getCell(10)));
|
||||
equipment.setMaintenanceVendorPhone(getCellStringValue(row.getCell(11)));
|
||||
equipment.setInspectionCycle(getCellIntegerValue(row.getCell(12)));
|
||||
equipment.setPurchaseDate(getCellDateValue(row.getCell(13)));
|
||||
equipment.setPurchasePrice(getCellNumericValue(row.getCell(14)));
|
||||
equipment.setWarrantyExpireDate(getCellDateValue(row.getCell(15)));
|
||||
equipment.setStatus(EquipmentStatus.ACTIVE);
|
||||
if (equipment.getEquipmentCode() == null || equipment.getEquipmentCode().isEmpty()) {
|
||||
equipment.setEquipmentCode(generateEquipmentCode());
|
||||
}
|
||||
if (equipment.getOwnershipType() == null) {
|
||||
equipment.setOwnershipType(OwnershipType.PROJECT);
|
||||
}
|
||||
return equipment;
|
||||
}
|
||||
|
||||
private String getCellStringValue(Cell cell) {
|
||||
if (cell == null) return null;
|
||||
return switch (cell.getCellType()) {
|
||||
case STRING -> cell.getStringCellValue().trim();
|
||||
case NUMERIC -> String.valueOf((long) cell.getNumericCellValue());
|
||||
case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private BigDecimal getCellNumericValue(Cell cell) {
|
||||
if (cell == null) return null;
|
||||
return switch (cell.getCellType()) {
|
||||
case NUMERIC -> BigDecimal.valueOf(cell.getNumericCellValue());
|
||||
case STRING -> {
|
||||
try {
|
||||
yield new BigDecimal(cell.getStringCellValue().trim());
|
||||
} catch (NumberFormatException e) {
|
||||
yield null;
|
||||
}
|
||||
}
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private Integer getCellIntegerValue(Cell cell) {
|
||||
if (cell == null) return null;
|
||||
return switch (cell.getCellType()) {
|
||||
case NUMERIC -> (int) cell.getNumericCellValue();
|
||||
case STRING -> {
|
||||
try {
|
||||
yield Integer.parseInt(cell.getStringCellValue().trim());
|
||||
} catch (NumberFormatException e) {
|
||||
yield null;
|
||||
}
|
||||
}
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private LocalDate getCellDateValue(Cell cell) {
|
||||
if (cell == null) return null;
|
||||
return switch (cell.getCellType()) {
|
||||
case STRING -> {
|
||||
String val = cell.getStringCellValue().trim();
|
||||
if (!val.isEmpty()) {
|
||||
try {
|
||||
yield LocalDate.parse(val);
|
||||
} catch (Exception e) {
|
||||
yield null;
|
||||
}
|
||||
} else {
|
||||
yield null;
|
||||
}
|
||||
}
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isRowEmpty(Row row) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Cell cell = row.getCell(i);
|
||||
if (cell != null && cell.getCellType() != CellType.BLANK) {
|
||||
String val = getCellStringValue(cell);
|
||||
if (val != null && !val.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private EquipmentType parseEquipmentType(String value) {
|
||||
if (value == null || value.isEmpty()) return null;
|
||||
for (EquipmentType type : EquipmentType.values()) {
|
||||
if (type.getDescription().equals(value) || type.name().equalsIgnoreCase(value)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private SystemType parseSystemType(String value) {
|
||||
if (value == null || value.isEmpty()) return null;
|
||||
for (SystemType type : SystemType.values()) {
|
||||
if (type.getDescription().equals(value) || type.name().equalsIgnoreCase(value)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private OwnershipType parseOwnershipType(String value) {
|
||||
if (value == null || value.isEmpty()) return OwnershipType.PROJECT;
|
||||
for (OwnershipType type : OwnershipType.values()) {
|
||||
if (type.getDescription().equals(value) || type.name().equalsIgnoreCase(value)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return OwnershipType.PROJECT;
|
||||
}
|
||||
|
||||
private CellStyle createHeaderStyle(Workbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
style.setFont(font);
|
||||
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.ether.pms.asset;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class TestApplication {
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
package com.ether.pms.asset.repository;
|
||||
|
||||
import com.ether.pms.asset.TestApplication;
|
||||
import com.ether.pms.asset.entity.Equipment;
|
||||
import com.ether.pms.asset.enums.EquipmentStatus;
|
||||
import com.ether.pms.asset.enums.EquipmentType;
|
||||
import com.ether.pms.asset.enums.OwnershipType;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* EquipmentRepository 测试类 - TDD方式
|
||||
*
|
||||
* 使用 @DataJpaTest 切片测试,只加载 JPA 相关组件
|
||||
*/
|
||||
@DataJpaTest
|
||||
@ContextConfiguration(classes = TestApplication.class)
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
class EquipmentRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private TestEntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private EquipmentRepository equipmentRepository;
|
||||
|
||||
private UUID projectId;
|
||||
private UUID spaceNodeId;
|
||||
private Equipment testEquipment;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
projectId = UUID.randomUUID();
|
||||
spaceNodeId = UUID.randomUUID();
|
||||
|
||||
testEquipment = new Equipment();
|
||||
testEquipment.setProjectId(projectId);
|
||||
testEquipment.setSpaceNodeId(spaceNodeId);
|
||||
testEquipment.setEquipmentName("测试设备");
|
||||
testEquipment.setEquipmentCode("EQ-001");
|
||||
testEquipment.setEquipmentType(EquipmentType.ELEVATOR);
|
||||
testEquipment.setOwnershipType(OwnershipType.PROJECT);
|
||||
testEquipment.setStatus(EquipmentStatus.ACTIVE);
|
||||
testEquipment.setIsDeleted(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void save_shouldPersistEquipment() {
|
||||
Equipment saved = equipmentRepository.save(testEquipment);
|
||||
entityManager.flush();
|
||||
|
||||
assertNotNull(saved.getId());
|
||||
assertEquals("测试设备", saved.getEquipmentName());
|
||||
assertEquals("EQ-001", saved.getEquipmentCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findById_shouldReturnEquipment() {
|
||||
entityManager.persist(testEquipment);
|
||||
entityManager.flush();
|
||||
|
||||
Optional<Equipment> found = equipmentRepository.findById(testEquipment.getId());
|
||||
|
||||
assertTrue(found.isPresent());
|
||||
assertEquals("测试设备", found.get().getEquipmentName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findById_withDeletedEquipment_shouldReturnEmpty() {
|
||||
testEquipment.setIsDeleted(true);
|
||||
entityManager.persist(testEquipment);
|
||||
entityManager.flush();
|
||||
|
||||
Optional<Equipment> found = equipmentRepository.findByIdAndIsDeletedFalse(testEquipment.getId());
|
||||
|
||||
assertTrue(found.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByIdAndIsDeletedFalse_shouldReturnNonDeletedEquipment() {
|
||||
entityManager.persist(testEquipment);
|
||||
entityManager.flush();
|
||||
|
||||
Optional<Equipment> found = equipmentRepository.findByIdAndIsDeletedFalse(testEquipment.getId());
|
||||
|
||||
assertTrue(found.isPresent());
|
||||
assertFalse(found.get().getIsDeleted());
|
||||
}
|
||||
|
||||
@Test
|
||||
void delete_shouldSoftDeleteEquipment() {
|
||||
entityManager.persist(testEquipment);
|
||||
entityManager.flush();
|
||||
|
||||
testEquipment.setIsDeleted(true);
|
||||
equipmentRepository.save(testEquipment);
|
||||
entityManager.flush();
|
||||
|
||||
Optional<Equipment> found = equipmentRepository.findByIdAndIsDeletedFalse(testEquipment.getId());
|
||||
assertTrue(found.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByProjectIdAndIsDeletedFalse_shouldReturnEquipments() {
|
||||
Equipment equipment1 = createEquipment("设备1", "EQ-001");
|
||||
Equipment equipment2 = createEquipment("设备2", "EQ-002");
|
||||
entityManager.persist(equipment1);
|
||||
entityManager.persist(equipment2);
|
||||
entityManager.flush();
|
||||
|
||||
List<Equipment> result = equipmentRepository.findByProjectIdAndIsDeletedFalse(projectId);
|
||||
|
||||
assertEquals(2, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByProjectIdAndIsDeletedFalse_withDifferentProject_shouldReturnEmpty() {
|
||||
Equipment equipment = createEquipment("设备1", "EQ-001");
|
||||
entityManager.persist(equipment);
|
||||
entityManager.flush();
|
||||
|
||||
List<Equipment> result = equipmentRepository.findByProjectIdAndIsDeletedFalse(UUID.randomUUID());
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByEquipmentTypeAndIsDeletedFalse_shouldReturnEquipmentsByType() {
|
||||
Equipment elevator = createEquipment("电梯", "EQ-EL-001");
|
||||
elevator.setEquipmentType(EquipmentType.ELEVATOR);
|
||||
|
||||
Equipment hvac = createEquipment("空调", "EQ-HV-001");
|
||||
hvac.setEquipmentType(EquipmentType.HVAC);
|
||||
|
||||
entityManager.persist(elevator);
|
||||
entityManager.persist(hvac);
|
||||
entityManager.flush();
|
||||
|
||||
List<Equipment> elevators = equipmentRepository.findByEquipmentTypeAndIsDeletedFalse(EquipmentType.ELEVATOR);
|
||||
|
||||
assertEquals(1, elevators.size());
|
||||
assertEquals(EquipmentType.ELEVATOR, elevators.get(0).getEquipmentType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByOwnershipTypeAndIsDeletedFalse_shouldReturnEquipmentsByOwnership() {
|
||||
Equipment projectOwned = createEquipment("项目自有设备", "EQ-PJ-001");
|
||||
projectOwned.setOwnershipType(OwnershipType.PROJECT);
|
||||
|
||||
Equipment ownerOwned = createEquipment("业主设备", "EQ-OW-001");
|
||||
ownerOwned.setOwnershipType(OwnershipType.OWNER);
|
||||
|
||||
entityManager.persist(projectOwned);
|
||||
entityManager.persist(ownerOwned);
|
||||
entityManager.flush();
|
||||
|
||||
List<Equipment> projectOwnedEquipments = equipmentRepository.findByOwnershipTypeAndIsDeletedFalse(OwnershipType.PROJECT);
|
||||
|
||||
assertEquals(1, projectOwnedEquipments.size());
|
||||
assertEquals(OwnershipType.PROJECT, projectOwnedEquipments.get(0).getOwnershipType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findBySpaceNodeIdAndIsDeletedFalse_shouldReturnEquipmentsBySpace() {
|
||||
Equipment equipment1 = createEquipment("位置1设备", "EQ-SP-001");
|
||||
equipment1.setSpaceNodeId(spaceNodeId);
|
||||
|
||||
entityManager.persist(equipment1);
|
||||
entityManager.flush();
|
||||
|
||||
List<Equipment> result = equipmentRepository.findBySpaceNodeIdAndIsDeletedFalse(spaceNodeId);
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(spaceNodeId, result.get(0).getSpaceNodeId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void countByProject_shouldReturnCorrectCount() {
|
||||
entityManager.persist(createEquipment("设备1", "EQ-001"));
|
||||
entityManager.persist(createEquipment("设备2", "EQ-002"));
|
||||
entityManager.flush();
|
||||
|
||||
long count = equipmentRepository.countByProject(projectId);
|
||||
|
||||
assertEquals(2, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
void existsByEquipmentCode_withExistingCode_shouldReturnTrue() {
|
||||
entityManager.persist(testEquipment);
|
||||
entityManager.flush();
|
||||
|
||||
boolean exists = equipmentRepository.existsByEquipmentCode("EQ-001");
|
||||
|
||||
assertTrue(exists);
|
||||
}
|
||||
|
||||
@Test
|
||||
void existsByEquipmentCode_withNonExistingCode_shouldReturnFalse() {
|
||||
boolean exists = equipmentRepository.existsByEquipmentCode("NON-EXISTENT");
|
||||
|
||||
assertFalse(exists);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByEquipmentCode_shouldReturnEquipment() {
|
||||
entityManager.persist(testEquipment);
|
||||
entityManager.flush();
|
||||
|
||||
Optional<Equipment> found = equipmentRepository.findByEquipmentCode("EQ-001");
|
||||
|
||||
assertTrue(found.isPresent());
|
||||
assertEquals("EQ-001", found.get().getEquipmentCode());
|
||||
}
|
||||
|
||||
private Equipment createEquipment(String name, String code) {
|
||||
Equipment equipment = new Equipment();
|
||||
equipment.setProjectId(projectId);
|
||||
equipment.setEquipmentName(name);
|
||||
equipment.setEquipmentCode(code);
|
||||
equipment.setEquipmentType(EquipmentType.OTHER);
|
||||
equipment.setOwnershipType(OwnershipType.PROJECT);
|
||||
equipment.setStatus(EquipmentStatus.ACTIVE);
|
||||
equipment.setIsDeleted(false);
|
||||
return equipment;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: sa
|
||||
password:
|
||||
driver-class-name: org.h2.Driver
|
||||
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.H2Dialect
|
||||
|
||||
h2:
|
||||
console:
|
||||
enabled: false
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL
|
||||
username: sa
|
||||
password:
|
||||
driver-class-name: org.h2.Driver
|
||||
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.H2Dialect
|
||||
|
||||
h2:
|
||||
console:
|
||||
enabled: false
|
||||
|
|
@ -27,6 +27,8 @@ import org.springframework.security.web.SecurityFilterChain;
|
|||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -40,6 +42,11 @@ public class SecurityConfig {
|
|||
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
|
|
@ -50,7 +57,7 @@ public class SecurityConfig {
|
|||
.securityContextRepository(securityContextRepository())
|
||||
)
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/api/auth/login", "/api/auth/refresh").permitAll()
|
||||
.requestMatchers("/api/auth/login", "/api/auth/logout", "/api/auth/refresh").permitAll()
|
||||
.requestMatchers("/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
|
||||
.requestMatchers("/actuator/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
package com.ether.pms.auth.controller;
|
||||
|
||||
import com.ether.pms.auth.annotation.OperationLog;
|
||||
import com.ether.pms.auth.controller.dto.DeptDTO;
|
||||
import com.ether.pms.auth.controller.dto.DeptVO;
|
||||
import com.ether.pms.auth.controller.dto.UserVO;
|
||||
import com.ether.pms.auth.entity.AuditLog;
|
||||
import com.ether.pms.auth.entity.Dept;
|
||||
import com.ether.pms.auth.entity.User;
|
||||
import com.ether.pms.auth.service.DeptService;
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 部门管理控制器
|
||||
*
|
||||
* <p>提供部门相关的RESTful API接口,包括部门树查询、创建部门、获取部门成员等功能。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/auth/depts")
|
||||
@RequiredArgsConstructor
|
||||
public class DeptController {
|
||||
|
||||
private final DeptService deptService;
|
||||
|
||||
/**
|
||||
* 获取部门树
|
||||
*/
|
||||
@GetMapping("/tree")
|
||||
public ApiResponse<List<DeptVO>> getDeptTree() {
|
||||
List<Dept> depts = deptService.getDeptTree();
|
||||
List<DeptVO> tree = buildDeptTree(depts, null);
|
||||
return ApiResponse.success(tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有启用的部门列表
|
||||
*/
|
||||
@GetMapping
|
||||
public ApiResponse<List<Dept>> getAllDepts() {
|
||||
return ApiResponse.success(deptService.getActiveDepts());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取部门
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public ApiResponse<Dept> getById(@PathVariable UUID id) {
|
||||
return deptService.getById(id)
|
||||
.map(ApiResponse::success)
|
||||
.orElse(ApiResponse.error("部门不存在"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建部门
|
||||
*/
|
||||
@PostMapping
|
||||
@OperationLog(operation = "创建部门", module = "DEPT", action = AuditLog.ActionType.CREATE)
|
||||
public ApiResponse<Dept> createDept(@RequestBody @Valid DeptDTO dto) {
|
||||
Dept dept = new Dept();
|
||||
dept.setDeptName(dto.getDeptName());
|
||||
dept.setDeptCode(dto.getDeptCode());
|
||||
dept.setParentId(dto.getParentId());
|
||||
dept.setDeptType(dto.getDeptType());
|
||||
dept.setDefaultRoleCode(dto.getDefaultRoleCode());
|
||||
dept.setLeaderId(dto.getLeaderId());
|
||||
dept.setSortOrder(dto.getSortOrder());
|
||||
dept.setStatus("ACTIVE");
|
||||
return ApiResponse.success(deptService.createDept(dept));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新部门
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@OperationLog(operation = "更新部门", module = "DEPT", action = AuditLog.ActionType.UPDATE)
|
||||
public ApiResponse<Dept> updateDept(@PathVariable UUID id, @RequestBody @Valid DeptDTO dto) {
|
||||
Dept dept = new Dept();
|
||||
dept.setDeptName(dto.getDeptName());
|
||||
dept.setDeptCode(dto.getDeptCode());
|
||||
dept.setParentId(dto.getParentId());
|
||||
dept.setDeptType(dto.getDeptType());
|
||||
dept.setDefaultRoleCode(dto.getDefaultRoleCode());
|
||||
dept.setLeaderId(dto.getLeaderId());
|
||||
dept.setSortOrder(dto.getSortOrder());
|
||||
dept.setStatus(dto.getStatus());
|
||||
return ApiResponse.success(deptService.updateDept(id, dept));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@OperationLog(operation = "删除部门", module = "DEPT", action = AuditLog.ActionType.DELETE)
|
||||
public ApiResponse<Void> deleteDept(@PathVariable UUID id) {
|
||||
deptService.deleteDept(id);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门成员
|
||||
*/
|
||||
@GetMapping("/{deptId}/members")
|
||||
public ApiResponse<List<UserVO>> getDeptMembers(@PathVariable UUID deptId) {
|
||||
List<User> members = deptService.getDeptEmployees(deptId);
|
||||
return ApiResponse.success(members.stream().map(UserVO::fromEntity).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门类型查询部门
|
||||
*/
|
||||
@GetMapping("/by-type/{deptType}")
|
||||
public ApiResponse<List<Dept>> getByType(@PathVariable String deptType) {
|
||||
return ApiResponse.success(deptService.getByType(deptType));
|
||||
}
|
||||
|
||||
private List<DeptVO> buildDeptTree(List<Dept> depts, UUID parentId) {
|
||||
return depts.stream()
|
||||
.filter(d -> (parentId == null && d.getParentId() == null) ||
|
||||
(parentId != null && parentId.equals(d.getParentId())))
|
||||
.map(d -> {
|
||||
DeptVO vo = DeptVO.fromEntity(d);
|
||||
vo.setChildren(buildDeptTree(depts, d.getId()));
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
@ -10,43 +10,116 @@ import org.springframework.web.bind.annotation.*;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 权限管理REST接口控制器
|
||||
*
|
||||
* <p>提供权限(Permission)相关的HTTP API接口,包括权限的增删改查、类型筛选、菜单权限获取等功能。
|
||||
* 所有接口均遵循RESTful设计规范,返回统一的ApiResponse格式。</p>
|
||||
*
|
||||
* <p>主要接口:</p>
|
||||
* <ul>
|
||||
* <li>GET /api/auth/permissions - 查询所有权限</li>
|
||||
* <li>GET /api/auth/permissions/{id} - 根据ID查询权限</li>
|
||||
* <li>GET /api/auth/permissions/type/{type} - 根据类型查询权限</li>
|
||||
* <li>GET /api/auth/permissions/menus - 查询所有菜单权限</li>
|
||||
* <li>POST /api/auth/permissions - 创建权限</li>
|
||||
* <li>PUT /api/auth/permissions/{id} - 更新权限</li>
|
||||
* <li>DELETE /api/auth/permissions/{id} - 删除权限</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/permissions")
|
||||
@RequestMapping("/api/auth/permissions")
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionController {
|
||||
|
||||
/** 权限业务逻辑服务 */
|
||||
private final PermissionService permissionService;
|
||||
|
||||
/**
|
||||
* 查询所有权限
|
||||
*
|
||||
* @return 包含所有权限列表的响应
|
||||
*/
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<List<Permission>>> findAll() {
|
||||
return ResponseEntity.ok(ApiResponse.success(permissionService.findAll()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据权限ID查询权限
|
||||
*
|
||||
* @param id 权限ID
|
||||
* @return 包含权限信息的响应
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<Permission>> findById(@PathVariable UUID id) {
|
||||
return ResponseEntity.ok(ApiResponse.success(permissionService.findById(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据权限类型查询权限列表
|
||||
*
|
||||
* <p>按权限类型筛选,如MENU、BUTTON、API等。</p>
|
||||
*
|
||||
* @param type 权限类型
|
||||
* @return 包含该类型权限列表的响应
|
||||
*/
|
||||
@GetMapping("/type/{type}")
|
||||
public ResponseEntity<ApiResponse<List<Permission>>> findByType(@PathVariable String type) {
|
||||
return ResponseEntity.ok(ApiResponse.success(permissionService.findByType(type)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有菜单权限
|
||||
*
|
||||
* <p>获取type为MENU的所有权限,通常用于前端菜单渲染。</p>
|
||||
*
|
||||
* @return 包含菜单权限列表的响应
|
||||
*/
|
||||
@GetMapping("/menus")
|
||||
public ResponseEntity<ApiResponse<List<Permission>>> findMenus() {
|
||||
return ResponseEntity.ok(ApiResponse.success(permissionService.findMenuPermissions()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新权限
|
||||
*
|
||||
* <p>创建一个新的权限。</p>
|
||||
*
|
||||
* @param permission 要创建的权限信息(请求体)
|
||||
* @return 包含创建后权限信息的响应
|
||||
*/
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<Permission>> create(@RequestBody Permission permission) {
|
||||
return ResponseEntity.ok(ApiResponse.success(permissionService.create(permission)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新权限信息
|
||||
*
|
||||
* <p>更新指定权限的信息。</p>
|
||||
*
|
||||
* @param id 要更新的权限ID
|
||||
* @param permission 包含新数据的权限信息(请求体)
|
||||
* @return 包含更新后权限信息的响应
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<Permission>> update(@PathVariable UUID id, @RequestBody Permission permission) {
|
||||
return ResponseEntity.ok(ApiResponse.success(permissionService.update(id, permission)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除权限
|
||||
*
|
||||
* <p>删除指定的权限。</p>
|
||||
*
|
||||
* @param id 要删除的权限ID
|
||||
* @return 空响应(表示操作成功)
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<Void>> delete(@PathVariable UUID id) {
|
||||
permissionService.delete(id);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
package com.ether.pms.auth.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.ether.pms.auth.annotation.OperationLog;
|
||||
import com.ether.pms.auth.controller.dto.AddProjectMemberDTO;
|
||||
import com.ether.pms.auth.controller.dto.PageResponse;
|
||||
import com.ether.pms.auth.controller.dto.ProjectMemberVO;
|
||||
import com.ether.pms.auth.controller.dto.UserVO;
|
||||
import com.ether.pms.auth.entity.AuditLog;
|
||||
import com.ether.pms.auth.entity.ProjectStaff;
|
||||
import com.ether.pms.auth.entity.User;
|
||||
import com.ether.pms.auth.service.UserManagementService;
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 项目成员管理控制器
|
||||
*
|
||||
* <p>提供项目成员相关的RESTful API接口,用于管理项目成员列表、添加成员、移除成员等功能。</p>
|
||||
*
|
||||
* <p>所有接口路径前缀为/api/auth/projects/{projectId}/members。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/auth/projects")
|
||||
@RequiredArgsConstructor
|
||||
public class ProjectMemberController {
|
||||
|
||||
/** 用户管理服务 */
|
||||
private final UserManagementService userManagementService;
|
||||
|
||||
/**
|
||||
* 查询项目成员列表
|
||||
*
|
||||
* <p>分页返回指定项目下的所有成员信息,包含角色信息。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @param page 页码,从1开始
|
||||
* @param size 每页数量
|
||||
* @return 包含分页成员信息的成功响应
|
||||
*/
|
||||
@GetMapping("/{projectId}/members")
|
||||
public ApiResponse<PageResponse<ProjectMemberVO>> getProjectMembers(
|
||||
@PathVariable UUID projectId,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "20") int size) {
|
||||
List<ProjectStaff> staffList = userManagementService.getProjectStaffsWithRoles(projectId);
|
||||
// 分页处理(支持0-based和1-based)
|
||||
int pageIndex = page > 0 ? page - 1 : page;
|
||||
int start = pageIndex * size;
|
||||
// 边界检查
|
||||
if (start < 0) start = 0;
|
||||
if (start > staffList.size()) start = staffList.size();
|
||||
int end = Math.min(start + size, staffList.size());
|
||||
List<ProjectMemberVO> pageData = staffList.subList(start, end).stream()
|
||||
.map(ProjectMemberVO::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
PageResponse<ProjectMemberVO> response = new PageResponse<>(pageData, (long) staffList.size(), pageIndex + 1, size);
|
||||
return ApiResponse.success(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可添加到项目的成员列表(企业员工)
|
||||
*
|
||||
* <p>返回尚未加入该项目的所有企业员工列表,支持模糊搜索用户。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @param search 搜索关键字(可选),支持用户名和真实姓名模糊匹配
|
||||
* @return 包含可用成员列表的成功响应
|
||||
*/
|
||||
@GetMapping("/{projectId}/available-members")
|
||||
public ApiResponse<List<UserVO>> getAvailableMembers(
|
||||
@PathVariable UUID projectId,
|
||||
@RequestParam(required = false) String search) {
|
||||
List<User> users = userManagementService.findEnterpriseUsers();
|
||||
|
||||
// 支持模糊搜索
|
||||
if (search != null && !search.isBlank()) {
|
||||
String keyword = search.toLowerCase();
|
||||
users = users.stream()
|
||||
.filter(u -> u.getUsername().toLowerCase().contains(keyword)
|
||||
|| (u.getRealName() != null && u.getRealName().toLowerCase().contains(keyword)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return ApiResponse.success(users.stream()
|
||||
.map(UserVO::fromEntity)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加项目成员
|
||||
*
|
||||
* <p>将指定用户添加到项目中,并分配多个角色。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @param dto 添加项目成员的请求数据
|
||||
* @return 成功响应
|
||||
*/
|
||||
@PostMapping("/{projectId}/members")
|
||||
@OperationLog(operation = "添加项目成员", module = "PROJECT_MEMBER", action = AuditLog.ActionType.CREATE)
|
||||
public ApiResponse<Void> addProjectMember(
|
||||
@PathVariable UUID projectId,
|
||||
@RequestBody AddProjectMemberDTO dto) {
|
||||
userManagementService.assignStaffToProject(
|
||||
dto.getUserId(),
|
||||
projectId,
|
||||
dto.getStaffType(),
|
||||
dto.getRoleIds());
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除项目成员
|
||||
*
|
||||
* <p>将指定用户从项目中移除。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 成功响应
|
||||
*/
|
||||
@DeleteMapping("/{projectId}/members/{userId}")
|
||||
@OperationLog(operation = "移除项目成员", module = "PROJECT_MEMBER", action = AuditLog.ActionType.DELETE)
|
||||
public ApiResponse<Void> removeProjectMember(
|
||||
@PathVariable UUID projectId,
|
||||
@PathVariable UUID userId) {
|
||||
userManagementService.removeStaffFromProject(userId, projectId);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
}
|
||||
|
|
@ -14,40 +14,108 @@ import org.springframework.web.bind.annotation.*;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 角色管理REST接口控制器
|
||||
*
|
||||
* <p>提供角色(Role)相关的HTTP API接口,包括角色的增删改查、权限分配、用户关联等功能。
|
||||
* 所有接口均遵循RESTful设计规范,返回统一的ApiResponse格式。</p>
|
||||
*
|
||||
* <p>主要接口:</p>
|
||||
* <ul>
|
||||
* <li>GET /api/auth/roles - 查询所有角色</li>
|
||||
* <li>GET /api/auth/roles/{id} - 根据ID查询角色</li>
|
||||
* <li>GET /api/auth/roles/project/{projectId} - 根据项目ID查询角色</li>
|
||||
* <li>POST /api/auth/roles - 创建角色</li>
|
||||
* <li>PUT /api/auth/roles/{id} - 更新角色</li>
|
||||
* <li>DELETE /api/auth/roles/{id} - 删除角色</li>
|
||||
* <li>POST /api/auth/roles/{id}/permissions - 为角色分配权限</li>
|
||||
* <li>GET /api/auth/roles/{id}/permissions - 获取角色的权限</li>
|
||||
* <li>GET /api/auth/roles/{id}/users - 获取拥有某角色的用户</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/roles")
|
||||
@RequestMapping("/api/auth/roles")
|
||||
@RequiredArgsConstructor
|
||||
public class RoleController {
|
||||
|
||||
/** 角色业务逻辑服务 */
|
||||
private final RoleService roleService;
|
||||
|
||||
/**
|
||||
* 查询所有角色
|
||||
*
|
||||
* @return 包含所有角色列表的响应
|
||||
*/
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<List<Role>>> findAll() {
|
||||
return ResponseEntity.ok(ApiResponse.success(roleService.findAll()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色ID查询角色
|
||||
*
|
||||
* @param id 角色ID
|
||||
* @return 包含角色信息的响应
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<Role>> findById(@PathVariable UUID id) {
|
||||
return ResponseEntity.ok(ApiResponse.success(roleService.findById(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据项目ID查询角色列表
|
||||
*
|
||||
* <p>获取指定项目下的所有角色。</p>
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 包含该项目角色列表的响应
|
||||
*/
|
||||
@GetMapping("/project/{projectId}")
|
||||
public ResponseEntity<ApiResponse<List<Role>>> findByProjectId(@PathVariable String projectId) {
|
||||
return ResponseEntity.ok(ApiResponse.success(roleService.findByProjectId(projectId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新角色
|
||||
*
|
||||
* <p>创建一个新的角色,记录审计日志。</p>
|
||||
*
|
||||
* @param role 要创建的角色信息(请求体)
|
||||
* @return 包含创建后角色信息的响应
|
||||
*/
|
||||
@PostMapping
|
||||
@OperationLog(operation = "创建角色", module = "ROLE", action = AuditLog.ActionType.CREATE)
|
||||
public ResponseEntity<ApiResponse<Role>> create(@RequestBody Role role) {
|
||||
return ResponseEntity.ok(ApiResponse.success(roleService.create(role)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色信息
|
||||
*
|
||||
* <p>更新指定角色的信息,记录审计日志。</p>
|
||||
*
|
||||
* @param id 要更新的角色ID
|
||||
* @param role 包含新数据的角色信息(请求体)
|
||||
* @return 包含更新后角色信息的响应
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@OperationLog(operation = "更新角色", module = "ROLE", action = AuditLog.ActionType.UPDATE)
|
||||
public ResponseEntity<ApiResponse<Role>> update(@PathVariable UUID id, @RequestBody Role role) {
|
||||
return ResponseEntity.ok(ApiResponse.success(roleService.update(id, role)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
*
|
||||
* <p>删除指定的角色,记录审计日志。</p>
|
||||
*
|
||||
* @param id 要删除的角色ID
|
||||
* @return 空响应(表示操作成功)
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@OperationLog(operation = "删除角色", module = "ROLE", action = AuditLog.ActionType.DELETE)
|
||||
public ResponseEntity<ApiResponse<Void>> delete(@PathVariable UUID id) {
|
||||
|
|
@ -55,6 +123,15 @@ public class RoleController {
|
|||
return ResponseEntity.ok(ApiResponse.success());
|
||||
}
|
||||
|
||||
/**
|
||||
* 为角色分配权限
|
||||
*
|
||||
* <p>替换角色原有的所有权限,记录审计日志。</p>
|
||||
*
|
||||
* @param id 角色ID
|
||||
* @param permissionIds 要分配的权限ID列表(请求体)
|
||||
* @return 空响应(表示操作成功)
|
||||
*/
|
||||
@PostMapping("/{id}/permissions")
|
||||
@OperationLog(operation = "分配权限", module = "ROLE", action = AuditLog.ActionType.ASSIGN)
|
||||
public ResponseEntity<ApiResponse<Void>> assignPermissions(
|
||||
|
|
@ -64,11 +141,27 @@ public class RoleController {
|
|||
return ResponseEntity.ok(ApiResponse.success());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色的所有权限
|
||||
*
|
||||
* <p>查询指定角色关联的所有权限。</p>
|
||||
*
|
||||
* @param id 角色ID
|
||||
* @return 包含角色权限列表的响应
|
||||
*/
|
||||
@GetMapping("/{id}/permissions")
|
||||
public ResponseEntity<ApiResponse<List<Permission>>> getPermissions(@PathVariable UUID id) {
|
||||
return ResponseEntity.ok(ApiResponse.success(roleService.getPermissions(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拥有某角色的所有用户
|
||||
*
|
||||
* <p>查询所有拥有指定角色ID的用户。</p>
|
||||
*
|
||||
* @param id 角色ID
|
||||
* @return 包含用户列表的响应
|
||||
*/
|
||||
@GetMapping("/{id}/users")
|
||||
public ResponseEntity<ApiResponse<List<User>>> getUsersByRoleId(@PathVariable UUID id) {
|
||||
return ResponseEntity.ok(ApiResponse.success(roleService.getUsersByRoleId(id)));
|
||||
|
|
|
|||
|
|
@ -13,25 +13,54 @@ import org.springframework.web.bind.annotation.*;
|
|||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 系统配置控制器
|
||||
* 提供系统配置的 RESTful API 接口
|
||||
* 包括配置的查询、更新等操作
|
||||
*
|
||||
* @author Ether Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/config")
|
||||
@RequiredArgsConstructor
|
||||
public class SysConfigController {
|
||||
|
||||
/**
|
||||
* 系统配置服务对象
|
||||
*/
|
||||
private final SysConfigService sysConfigService;
|
||||
|
||||
/**
|
||||
* 获取所有配置项
|
||||
* 返回系统中所有配置的键值对
|
||||
*
|
||||
* @return 配置键值对 Map
|
||||
*/
|
||||
@GetMapping
|
||||
@OperationLog(operation = "查看系统设置", module = "SYSTEM", action = AuditLog.ActionType.VIEW)
|
||||
public ResponseEntity<ApiResponse<Map<String, String>>> getAllConfigs() {
|
||||
return ResponseEntity.ok(ApiResponse.success(sysConfigService.getAllConfigs()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置键获取单个配置项
|
||||
*
|
||||
* @param configKey 配置键,如:property_company_name
|
||||
* @return 配置项实体
|
||||
*/
|
||||
@GetMapping("/{configKey}")
|
||||
@OperationLog(operation = "查看系统设置", module = "SYSTEM", action = AuditLog.ActionType.VIEW)
|
||||
public ResponseEntity<ApiResponse<SysConfig>> getConfig(@PathVariable String configKey) {
|
||||
return ResponseEntity.ok(ApiResponse.success(sysConfigService.getConfig(configKey)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新单个配置项
|
||||
* 根据配置键更新对应的配置值,并记录审计日志
|
||||
*
|
||||
* @param configKey 配置键
|
||||
* @param request 更新请求对象,包含新的配置值
|
||||
* @return 更新后的配置项实体
|
||||
*/
|
||||
@PutMapping("/{configKey}")
|
||||
@OperationLog(operation = "更新系统设置", module = "SYSTEM", action = AuditLog.ActionType.UPDATE)
|
||||
public ResponseEntity<ApiResponse<SysConfig>> updateConfig(
|
||||
|
|
@ -40,14 +69,28 @@ public class SysConfigController {
|
|||
return ResponseEntity.ok(ApiResponse.success(sysConfigService.updateConfig(configKey, request.getConfigValue())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新配置项
|
||||
* 同时更新多个配置键值对,并记录审计日志
|
||||
*
|
||||
* @param configs 配置键值对 Map
|
||||
* @return 更新后的配置键值对 Map
|
||||
*/
|
||||
@PutMapping
|
||||
@OperationLog(operation = "更新系统设置", module = "SYSTEM", action = AuditLog.ActionType.UPDATE)
|
||||
public ResponseEntity<ApiResponse<Map<String, String>>> updateConfigs(@RequestBody Map<String, String> configs) {
|
||||
return ResponseEntity.ok(ApiResponse.success(sysConfigService.updateConfigs(configs)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置更新请求对象
|
||||
* 用于接收单个配置项的更新请求
|
||||
*/
|
||||
@Data
|
||||
public static class ConfigUpdateRequest {
|
||||
/**
|
||||
* 新的配置值
|
||||
*/
|
||||
private String configValue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ package com.ether.pms.auth.controller;
|
|||
|
||||
import com.ether.pms.auth.annotation.OperationLog;
|
||||
import com.ether.pms.auth.controller.dto.UserProjectRequest;
|
||||
import com.ether.pms.auth.controller.dto.UserVO;
|
||||
import com.ether.pms.auth.entity.AuditLog;
|
||||
import com.ether.pms.auth.entity.User;
|
||||
import com.ether.pms.auth.entity.UserProject;
|
||||
import com.ether.pms.auth.service.UserManagementService;
|
||||
import com.ether.pms.auth.service.UserProjectService;
|
||||
import com.ether.pms.auth.service.UserService;
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
|
|
@ -15,37 +17,93 @@ import org.springframework.web.bind.annotation.*;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户管理控制器
|
||||
*
|
||||
* <p>提供用户相关的RESTful API接口,包括用户CRUD、密码管理、角色分配、项目关联等功能。</p>
|
||||
*
|
||||
* <p>所有接口路径前缀为/api/auth/users,使用JSON格式进行数据交互。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/users")
|
||||
@RequestMapping("/api/auth/users")
|
||||
@RequiredArgsConstructor
|
||||
public class UserController {
|
||||
|
||||
/** 用户服务 */
|
||||
private final UserService userService;
|
||||
/** 用户项目关联服务 */
|
||||
private final UserProjectService userProjectService;
|
||||
/** 用户管理服务 */
|
||||
private final UserManagementService userManagementService;
|
||||
|
||||
/**
|
||||
* 获取所有用户列表
|
||||
*
|
||||
* <p>返回系统中所有用户的信息,包含关联的角色列表。</p>
|
||||
*
|
||||
* @return 包含所有用户的成功响应
|
||||
*/
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<List<User>>> findAll() {
|
||||
return ResponseEntity.ok(ApiResponse.success(userService.findAll()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取用户信息
|
||||
*
|
||||
* <p>根据用户唯一标识符查询指定用户的详细信息。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @return 包含用户信息的成功响应
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<User>> findById(@PathVariable UUID id) {
|
||||
return ResponseEntity.ok(ApiResponse.success(userService.findById(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新用户
|
||||
*
|
||||
* <p>创建一个新的用户账号,需要提供用户名、密码等基本信息。</p>
|
||||
*
|
||||
* @param user 待创建的用户信息
|
||||
* @return 包含创建成功的用户信息的成功响应
|
||||
*/
|
||||
@PostMapping
|
||||
@OperationLog(operation = "创建用户", module = "USER", action = AuditLog.ActionType.CREATE)
|
||||
public ResponseEntity<ApiResponse<User>> create(@RequestBody User user) {
|
||||
return ResponseEntity.ok(ApiResponse.success(userService.create(user)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* <p>更新指定用户的个人信息,如真实姓名、手机号、邮箱等。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @param user 包含更新信息的用户对象
|
||||
* @return 包含更新后用户信息的成功响应
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@OperationLog(operation = "更新用户", module = "USER", action = AuditLog.ActionType.UPDATE)
|
||||
public ResponseEntity<ApiResponse<User>> update(@PathVariable UUID id, @RequestBody User user) {
|
||||
return ResponseEntity.ok(ApiResponse.success(userService.update(id, user)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*
|
||||
* <p>根据用户ID删除指定用户账号。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @return 成功响应
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@OperationLog(operation = "删除用户", module = "USER", action = AuditLog.ActionType.DELETE)
|
||||
public ResponseEntity<ApiResponse<Void>> delete(@PathVariable UUID id) {
|
||||
|
|
@ -53,6 +111,15 @@ public class UserController {
|
|||
return ResponseEntity.ok(ApiResponse.success());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户密码
|
||||
*
|
||||
* <p>用户修改自己的密码,需要提供原密码和新密码。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @param request 包含原密码和新密码的请求对象
|
||||
* @return 成功响应
|
||||
*/
|
||||
@PutMapping("/{id}/password")
|
||||
@OperationLog(operation = "修改密码", module = "USER", action = AuditLog.ActionType.UPDATE)
|
||||
public ResponseEntity<ApiResponse<Void>> updatePassword(
|
||||
|
|
@ -62,6 +129,15 @@ public class UserController {
|
|||
return ResponseEntity.ok(ApiResponse.success());
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配用户角色
|
||||
*
|
||||
* <p>为指定用户分配一个或多个角色,替换现有的角色。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @param roleIds 要分配的角色ID列表
|
||||
* @return 成功响应
|
||||
*/
|
||||
@PostMapping("/{id}/roles")
|
||||
@OperationLog(operation = "分配角色", module = "USER", action = AuditLog.ActionType.ASSIGN)
|
||||
public ResponseEntity<ApiResponse<Void>> assignRoles(
|
||||
|
|
@ -71,11 +147,28 @@ public class UserController {
|
|||
return ResponseEntity.ok(ApiResponse.success());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户参与的项目列表
|
||||
*
|
||||
* <p>查询指定用户参与的所有项目及其在项目中的角色。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @return 包含用户参与项目列表的成功响应
|
||||
*/
|
||||
@GetMapping("/{id}/projects")
|
||||
public ResponseEntity<ApiResponse<List<UserProject>>> getUserProjects(@PathVariable UUID id) {
|
||||
return ResponseEntity.ok(ApiResponse.success(userProjectService.getUserProjects(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户到项目
|
||||
*
|
||||
* <p>将指定用户添加到某个项目中,并设置用户在项目中的角色。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @param request 包含项目ID和角色信息的请求对象
|
||||
* @return 成功响应
|
||||
*/
|
||||
@PostMapping("/{id}/projects")
|
||||
public ResponseEntity<ApiResponse<Void>> addUserToProject(
|
||||
@PathVariable UUID id,
|
||||
|
|
@ -84,6 +177,15 @@ public class UserController {
|
|||
return ResponseEntity.ok(ApiResponse.success());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从项目中移除用户
|
||||
*
|
||||
* <p>将指定用户从某个项目中移除,删除用户与项目的关联关系。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 成功响应
|
||||
*/
|
||||
@DeleteMapping("/{id}/projects/{projectId}")
|
||||
public ResponseEntity<ApiResponse<Void>> removeUserFromProject(
|
||||
@PathVariable UUID id,
|
||||
|
|
@ -92,9 +194,29 @@ public class UserController {
|
|||
return ResponseEntity.ok(ApiResponse.success());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业员工列表(用于项目管理添加成员)
|
||||
*
|
||||
* <p>返回所有企业员工的用户信息列表。</p>
|
||||
*
|
||||
* @return 包含企业员工列表的成功响应
|
||||
*/
|
||||
@GetMapping("/enterprise")
|
||||
public ResponseEntity<ApiResponse<List<UserVO>>> getEnterpriseUsers() {
|
||||
List<User> users = userManagementService.findEnterpriseUsers();
|
||||
return ResponseEntity.ok(ApiResponse.success(users.stream().map(UserVO::fromEntity).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码修改请求对象
|
||||
*
|
||||
* <p>包含修改密码所需的原密码和新密码。</p>
|
||||
*/
|
||||
@Data
|
||||
public static class PasswordRequest {
|
||||
/** 原密码 */
|
||||
private String oldPassword;
|
||||
/** 新密码 */
|
||||
private String newPassword;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.ether.pms.auth.controller.dto;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 添加项目成员请求DTO
|
||||
*
|
||||
* <p>用于接收将用户添加到项目的请求数据。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class AddProjectMemberDTO {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
* 要添加到项目的用户唯一标识符
|
||||
*/
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private UUID userId;
|
||||
|
||||
/**
|
||||
* 员工类型
|
||||
* 标识员工的工作类型:SECURITY(保安)、CLEANING(保洁)、GARDEN(绿化)、MAINTENANCE(维修)、CUSTOMER_SERVICE(客服)、GENERAL(普通员工)
|
||||
*/
|
||||
private String staffType;
|
||||
|
||||
/**
|
||||
* 角色ID列表
|
||||
* 可选参数,分配的角色ID列表
|
||||
*/
|
||||
private List<UUID> roleIds;
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package com.ether.pms.auth.controller.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 创建企业用户请求DTO
|
||||
*
|
||||
* <p>用于接收创建企业用户的请求数据,包含用户基础信息和企业扩展信息。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class CreateEnterpriseUserDTO {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
* 用于登录系统
|
||||
*/
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
* 用户登录密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 真实姓名
|
||||
* 用户的真实姓名
|
||||
*/
|
||||
private String realName;
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
* 中国大陆手机号
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 电子邮箱
|
||||
* 用户的邮箱地址
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 员工工号
|
||||
* 企业内部员工编号
|
||||
*/
|
||||
private String employeeNo;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
* 用户所属的部门标识符
|
||||
*/
|
||||
private UUID deptId;
|
||||
|
||||
/**
|
||||
* 职位
|
||||
* 用户在企业中的职位
|
||||
*/
|
||||
private String position;
|
||||
|
||||
/**
|
||||
* 入职日期
|
||||
* 员工加入企业的日期
|
||||
*/
|
||||
private LocalDate entryDate;
|
||||
|
||||
/**
|
||||
* 员工类别
|
||||
* 标识员工类型:ENTERPRISE(普通员工)、MANAGEMENT(管理层)
|
||||
*/
|
||||
private String userCategory;
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.ether.pms.auth.controller.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 部门数据传输对象
|
||||
*
|
||||
* <p>用于接收创建或更新部门的请求数据。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class DeptDTO {
|
||||
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
@NotBlank(message = "部门名称不能为空")
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 部门编码
|
||||
*/
|
||||
private String deptCode;
|
||||
|
||||
/**
|
||||
* 上级部门ID
|
||||
*/
|
||||
private UUID parentId;
|
||||
|
||||
/**
|
||||
* 部门类型
|
||||
* ADMIN: 行政管理
|
||||
* ENGINEERING: 工程部
|
||||
* SECURITY: 安保部
|
||||
* CS: 客服部
|
||||
* CLEANING: 保洁部
|
||||
*/
|
||||
private String deptType = "ADMIN";
|
||||
|
||||
/**
|
||||
* 部门默认角色编码
|
||||
* 属于该部门的员工默认拥有的系统角色
|
||||
*/
|
||||
private String defaultRoleCode;
|
||||
|
||||
/**
|
||||
* 部门负责人ID
|
||||
*/
|
||||
private UUID leaderId;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
private Integer sortOrder;
|
||||
|
||||
private String status = "ACTIVE";
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.ether.pms.auth.controller.dto;
|
||||
|
||||
import com.ether.pms.auth.entity.Dept;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 部门视图对象
|
||||
*
|
||||
* <p>用于返回部门信息的视图对象,包含部门基本信息和子部门列表。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class DeptVO {
|
||||
|
||||
/**
|
||||
* 部门唯一标识符
|
||||
*/
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 上级部门ID
|
||||
*/
|
||||
private UUID parentId;
|
||||
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 部门编码
|
||||
*/
|
||||
private String deptCode;
|
||||
|
||||
/**
|
||||
* 部门类型
|
||||
*/
|
||||
private String deptType;
|
||||
|
||||
/**
|
||||
* 部门类型描述
|
||||
*/
|
||||
private String deptTypeDesc;
|
||||
|
||||
/**
|
||||
* 部门默认角色编码
|
||||
*/
|
||||
private String defaultRoleCode;
|
||||
|
||||
/**
|
||||
* 部门负责人ID
|
||||
*/
|
||||
private UUID leaderId;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
private Integer sortOrder;
|
||||
|
||||
/**
|
||||
* 部门状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 子部门列表
|
||||
*/
|
||||
private List<DeptVO> children = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 将部门实体转换为视图对象
|
||||
*
|
||||
* @param dept 部门实体
|
||||
* @return 部门视图对象
|
||||
*/
|
||||
public static DeptVO fromEntity(Dept dept) {
|
||||
DeptVO vo = new DeptVO();
|
||||
vo.setId(dept.getId());
|
||||
vo.setParentId(dept.getParentId());
|
||||
vo.setDeptName(dept.getDeptName());
|
||||
vo.setDeptCode(dept.getDeptCode());
|
||||
vo.setDeptType(dept.getDeptType());
|
||||
vo.setDeptTypeDesc(getDeptTypeDesc(dept.getDeptType()));
|
||||
vo.setDefaultRoleCode(dept.getDefaultRoleCode());
|
||||
vo.setLeaderId(dept.getLeaderId());
|
||||
vo.setSortOrder(dept.getSortOrder());
|
||||
vo.setStatus(dept.getStatus());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private static String getDeptTypeDesc(String deptType) {
|
||||
if (deptType == null) {
|
||||
return "行政管理";
|
||||
}
|
||||
switch (deptType) {
|
||||
case "ENGINEERING":
|
||||
return "工程部";
|
||||
case "SECURITY":
|
||||
return "安保部";
|
||||
case "CS":
|
||||
return "客服部";
|
||||
case "CLEANING":
|
||||
return "保洁部";
|
||||
default:
|
||||
return "行政管理";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.ether.pms.auth.controller.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页响应DTO
|
||||
*
|
||||
* <p>用于返回分页数据的统一格式,包含内容列表、分页信息和总数。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PageResponse<T> {
|
||||
|
||||
/**
|
||||
* 分页数据内容列表
|
||||
*/
|
||||
private List<T> content;
|
||||
|
||||
/**
|
||||
* 当前页数据条数
|
||||
*/
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* 当前页码
|
||||
*/
|
||||
private int page;
|
||||
|
||||
/**
|
||||
* 数据总条数
|
||||
*/
|
||||
private long totalElements;
|
||||
|
||||
/**
|
||||
* 构造分页响应对象
|
||||
*
|
||||
* @param content 数据内容列表
|
||||
* @param totalElements 数据总条数
|
||||
* @param page 当前页码
|
||||
* @param size 每页条数
|
||||
*/
|
||||
public PageResponse(List<T> content, long totalElements, int page, int size) {
|
||||
this.content = content;
|
||||
this.totalElements = totalElements;
|
||||
this.page = page;
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package com.ether.pms.auth.controller.dto;
|
||||
|
||||
import com.ether.pms.auth.entity.ProjectStaff;
|
||||
import com.ether.pms.auth.entity.ProjectStaffRole;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 项目成员视图对象
|
||||
*
|
||||
* <p>用于返回项目成员信息,包含用户基本信息、角色信息和加入时间。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class ProjectMemberVO {
|
||||
|
||||
/**
|
||||
* 用户唯一标识符
|
||||
*/
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 真实姓名
|
||||
*/
|
||||
private String realName;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private String userType;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 员工类型
|
||||
*/
|
||||
private String staffType;
|
||||
|
||||
/**
|
||||
* 项目中的角色列表
|
||||
*/
|
||||
private List<String> roles;
|
||||
|
||||
/**
|
||||
* 角色名称列表(用于显示)
|
||||
*/
|
||||
private String roleNames;
|
||||
|
||||
/**
|
||||
* 加入时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 将 ProjectStaff 实体转换为视图对象
|
||||
*
|
||||
* @param staff 项目员工实体
|
||||
* @return 项目成员视图对象
|
||||
*/
|
||||
public static ProjectMemberVO fromEntity(ProjectStaff staff) {
|
||||
ProjectMemberVO vo = new ProjectMemberVO();
|
||||
vo.setId(staff.getUser().getId());
|
||||
vo.setUsername(staff.getUser().getUsername());
|
||||
vo.setRealName(staff.getUser().getRealName());
|
||||
vo.setPhone(staff.getUser().getPhone());
|
||||
vo.setEmail(staff.getUser().getEmail());
|
||||
vo.setUserType(staff.getUser().getUserType());
|
||||
vo.setStatus(staff.getUser().getStatus() != null ? staff.getUser().getStatus().name() : null);
|
||||
vo.setStaffType(staff.getStaffType());
|
||||
vo.setCreatedAt(staff.getCreatedAt());
|
||||
|
||||
// 提取角色信息:优先使用项目角色,其次使用全局角色
|
||||
List<ProjectStaffRole> staffRoles = staff.getStaffRoles();
|
||||
List<String> roleCodes = new ArrayList<>();
|
||||
List<String> roleNames = new ArrayList<>();
|
||||
|
||||
if (staffRoles != null && !staffRoles.isEmpty()) {
|
||||
// 使用项目角色
|
||||
for (ProjectStaffRole psr : staffRoles) {
|
||||
if (psr.getRole() != null) {
|
||||
roleCodes.add(psr.getRole().getCode());
|
||||
roleNames.add(psr.getRole().getName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 回退到全局角色
|
||||
List<com.ether.pms.auth.entity.Role> userRoles = staff.getUser().getRoles();
|
||||
if (userRoles != null && !userRoles.isEmpty()) {
|
||||
for (com.ether.pms.auth.entity.Role role : userRoles) {
|
||||
roleCodes.add(role.getCode());
|
||||
roleNames.add(role.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vo.setRoles(roleCodes);
|
||||
vo.setRoleNames(String.join("、", roleNames));
|
||||
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package com.ether.pms.auth.controller.dto;
|
||||
|
||||
import com.ether.pms.auth.entity.User;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户视图对象
|
||||
*
|
||||
* <p>用于返回用户信息的简化视图,包含用户基本信息、部门信息和角色列表。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
public class UserVO {
|
||||
|
||||
/**
|
||||
* 用户唯一标识符
|
||||
*/
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 真实姓名
|
||||
*/
|
||||
private String realName;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private String userType;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
private UUID deptId;
|
||||
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 角色列表
|
||||
*/
|
||||
private List<RoleInfo> roles;
|
||||
|
||||
/**
|
||||
* 角色名称列表(逗号分隔)
|
||||
*/
|
||||
private String roleNames;
|
||||
|
||||
/**
|
||||
* 角色信息内部类
|
||||
*/
|
||||
@Data
|
||||
public static class RoleInfo {
|
||||
private UUID id;
|
||||
private String code;
|
||||
private String name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户实体转换为视图对象
|
||||
*
|
||||
* @param user 用户实体
|
||||
* @return 用户视图对象
|
||||
*/
|
||||
public static UserVO fromEntity(User user) {
|
||||
UserVO vo = new UserVO();
|
||||
vo.setId(user.getId());
|
||||
vo.setUsername(user.getUsername());
|
||||
vo.setRealName(user.getRealName());
|
||||
vo.setPhone(user.getPhone());
|
||||
vo.setEmail(user.getEmail());
|
||||
vo.setUserType(user.getUserType());
|
||||
vo.setStatus(user.getStatus() != null ? user.getStatus().name() : null);
|
||||
vo.setDeptId(user.getDeptId());
|
||||
|
||||
// 转换角色信息
|
||||
if (user.getRoles() != null && !user.getRoles().isEmpty()) {
|
||||
List<RoleInfo> roleInfos = user.getRoles().stream()
|
||||
.map(role -> {
|
||||
RoleInfo info = new RoleInfo();
|
||||
info.setId(role.getId());
|
||||
info.setCode(role.getCode());
|
||||
info.setName(role.getName());
|
||||
return info;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
vo.setRoles(roleInfos);
|
||||
vo.setRoleNames(roleInfos.stream().map(RoleInfo::getName).collect(Collectors.joining("、")));
|
||||
}
|
||||
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
package com.ether.pms.auth.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 部门实体类
|
||||
*
|
||||
* <p>表示组织架构中的部门信息,包含部门名称、编码、负责人、类型等。</p>
|
||||
*
|
||||
* <p>支持树形结构,通过parentId指向上级部门。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "dept")
|
||||
@Data
|
||||
public class Dept {
|
||||
|
||||
/**
|
||||
* 部门唯一标识符
|
||||
* 采用UUID策略自动生成
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 上级部门ID
|
||||
* 指向父部门的ID,用于构建部门树形结构,顶级部门此字段为空
|
||||
*/
|
||||
private UUID parentId;
|
||||
|
||||
/**
|
||||
* 部门名称
|
||||
* 部门的显示名称
|
||||
*/
|
||||
@Column(nullable = false, length = 100)
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 部门编码
|
||||
* 部门的唯一编码,用于系统间对接
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String deptCode;
|
||||
|
||||
/**
|
||||
* 部门类型
|
||||
* 标识部门所属的业务类型
|
||||
* ADMIN: 行政管理
|
||||
* ENGINEERING: 工程部
|
||||
* SECURITY: 安保部
|
||||
* CS: 客服部
|
||||
* CLEANING: 保洁部
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String deptType = "ADMIN";
|
||||
|
||||
/**
|
||||
* 部门默认角色编码
|
||||
* 属于该部门的员工默认拥有的系统角色
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String defaultRoleCode;
|
||||
|
||||
/**
|
||||
* 部门负责人ID
|
||||
* 部门负责人的用户ID
|
||||
*/
|
||||
private UUID leaderId;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
* 部门在同一级别中的排序顺序,数字越小越靠前
|
||||
*/
|
||||
private Integer sortOrder;
|
||||
|
||||
/**
|
||||
* 部门状态
|
||||
* 标识部门的状态:ACTIVE-正常、DISABLED-停用
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String status = "ACTIVE";
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门类型枚举
|
||||
*/
|
||||
public enum DeptType {
|
||||
ADMIN("行政管理"),
|
||||
ENGINEERING("工程部"),
|
||||
SECURITY("安保部"),
|
||||
CS("客服部"),
|
||||
CLEANING("保洁部");
|
||||
|
||||
private final String desc;
|
||||
|
||||
DeptType(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package com.ether.pms.auth.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 企业用户实体类
|
||||
*
|
||||
* <p>表示企业类型的用户信息,包含员工的职位、入职日期等企业相关信息。</p>
|
||||
*
|
||||
* <p>与User实体为一对一关系,共享主键。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "enterprise_user")
|
||||
@Data
|
||||
public class EnterpriseUser {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
* 主键,也是外键指向auth_user表
|
||||
*/
|
||||
@Id
|
||||
private UUID userId;
|
||||
|
||||
/**
|
||||
* 员工工号
|
||||
* 企业内部员工编号
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String employeeNo;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
* 用户所属的部门标识符
|
||||
*/
|
||||
private UUID deptId;
|
||||
|
||||
/**
|
||||
* 职位
|
||||
* 用户在企业中的职位
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String position;
|
||||
|
||||
/**
|
||||
* 入职日期
|
||||
* 员工加入企业的日期
|
||||
*/
|
||||
private LocalDate entryDate;
|
||||
|
||||
/**
|
||||
* 员工类别
|
||||
* 标识员工类型:ENTERPRISE(普通员工)、MANAGEMENT(管理层)
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String userCategory;
|
||||
|
||||
/**
|
||||
* 关联的User实体
|
||||
* 采用一对一关系,共享主键
|
||||
*/
|
||||
@OneToOne
|
||||
@MapsId
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
}
|
||||
|
|
@ -10,47 +10,101 @@ import java.time.LocalDateTime;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 权限实体类
|
||||
*
|
||||
* <p>用于定义系统中的权限信息,包括权限代码、名称、类型、资源路径等属性。
|
||||
* 权限是系统安全的基础单元,用于控制用户对具体功能的访问能力。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "auth_permission")
|
||||
@Data
|
||||
public class Permission {
|
||||
|
||||
/**
|
||||
* 权限唯一标识符
|
||||
* 使用UUID自动生成
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 权限代码
|
||||
* 系统内部识别符,必须唯一
|
||||
* 格式:只能包含字母、数字、冒号和下划线,长度2-100位
|
||||
* 例如:system:user:create, menu:project:view
|
||||
*/
|
||||
@NotNull(message = "权限代码不能为空")
|
||||
@Size(min = 2, max = 100, message = "权限代码长度必须在2-100位之间")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_:]+$", message = "权限代码只能包含字母、数字、冒号和下划线")
|
||||
@Column(unique = true, nullable = false, length = 100)
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 权限名称
|
||||
* 用于前端展示和用户理解,长度2-100位
|
||||
*/
|
||||
@NotNull(message = "权限名称不能为空")
|
||||
@Size(min = 2, max = 100, message = "权限名称长度必须在2-100位之间")
|
||||
@Column(nullable = false, length = 100)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 权限类型
|
||||
* 用于分类权限,如:MENU(菜单)、BUTTON(按钮)、API(接口)等
|
||||
*/
|
||||
@Size(max = 20, message = "权限类型长度不能超过20位")
|
||||
@Column(length = 20)
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 资源路径
|
||||
* 对于API类型的权限,表示对应的接口路径
|
||||
*/
|
||||
@Size(max = 50, message = "资源路径长度不能超过50位")
|
||||
@Column(length = 50)
|
||||
private String resource;
|
||||
|
||||
/**
|
||||
* 请求方法
|
||||
* 对于API类型的权限,表示对应的HTTP方法(如GET、POST、PUT、DELETE)
|
||||
*/
|
||||
@Size(max = 50, message = "请求方法长度不能超过50位")
|
||||
@Column(length = 50)
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 权限描述
|
||||
* 对权限的详细说明,用于帮助理解权限用途,长度不超过200位
|
||||
*/
|
||||
@Size(max = 200, message = "权限描述长度不能超过200位")
|
||||
@Column(length = 200)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 父权限代码
|
||||
* 用于构建权限树形结构,支持层级管理
|
||||
* 顶级权限的父代码为空
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String parentCode;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
* 用于前端展示时的排序,数字越小越靠前
|
||||
*/
|
||||
private Integer sortOrder;
|
||||
|
||||
/**
|
||||
* 权限关联的角色列表
|
||||
* 多对多关系,通过auth_role_permission关联表维护
|
||||
* JsonIgnoreProperties避免循环序列化
|
||||
*/
|
||||
@JsonIgnoreProperties({"permissions", "roles"})
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(
|
||||
|
|
@ -60,16 +114,32 @@ public class Permission {
|
|||
)
|
||||
private List<Role> roles;
|
||||
|
||||
/**
|
||||
* 权限创建时间
|
||||
* 自动设置,记录创建时刻
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 权限更新时间
|
||||
* 自动设置,每次更新时自动修改
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 持久化前回调
|
||||
* 自动设置创建时间和更新时间
|
||||
*/
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前回调
|
||||
* 自动设置更新时间
|
||||
*/
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
package com.ether.pms.auth.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 项目员工实体类
|
||||
*
|
||||
* <p>表示项目类型的员工信息,包含所属项目、员工类型、班次等信息。</p>
|
||||
*
|
||||
* <p>与User实体为一对一关系,共享主键。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "project_staff")
|
||||
@Data
|
||||
public class ProjectStaff {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Column(name = "id")
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
* 外键指向auth_user表
|
||||
*/
|
||||
@Column(name = "user_id", insertable = false, updatable = false)
|
||||
private UUID userId;
|
||||
|
||||
/**
|
||||
* 所属项目ID
|
||||
* 员工所属项目的唯一标识符
|
||||
*/
|
||||
private UUID projectId;
|
||||
|
||||
/**
|
||||
* 所属部门ID
|
||||
* 员工所属部门的唯一标识符,用于组织架构管理
|
||||
*/
|
||||
private UUID deptId;
|
||||
|
||||
/**
|
||||
* 员工类型
|
||||
* 标识员工的工作类型:SECURITY(保安)、CLEANING(保洁)、GARDEN(绿化)、MAINTENANCE(维修)、CUSTOMER_SERVICE(客服)、GENERAL(普通员工)
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String staffType;
|
||||
|
||||
/**
|
||||
* 班次类型
|
||||
* 标识员工的班次:DAY(白班)、NIGHT(夜班)、ROTATION(轮班)
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String shiftType;
|
||||
|
||||
/**
|
||||
* 班组长ID
|
||||
* 员工所属班组长的用户ID
|
||||
*/
|
||||
private UUID leaderId;
|
||||
|
||||
/**
|
||||
* 在岗状态
|
||||
* 标识员工的在岗状态:ASSIGNED(已分配)、ON_LEAVE(请假)、TRANSFERRED(已调离)
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String assignmentStatus;
|
||||
|
||||
/**
|
||||
* 员工角色列表
|
||||
*/
|
||||
@OneToMany(mappedBy = "staff", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<ProjectStaffRole> staffRoles = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Column(name = "created_at", updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 关联的User实体
|
||||
* 采用一对一关系
|
||||
*/
|
||||
@OneToOne
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.ether.pms.auth.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
|
||||
@Entity
|
||||
@Table(name = "project_staff_role")
|
||||
@Data
|
||||
public class ProjectStaffRole {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "staff_id", nullable = false)
|
||||
private ProjectStaff staff;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "role_id", nullable = false)
|
||||
private Role role;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package com.ether.pms.auth.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 住户实体类
|
||||
*
|
||||
* <p>表示住户类型的用户信息,包含身份证、认证状态等住户相关信息。</p>
|
||||
*
|
||||
* <p>与User实体为一对一关系,共享主键。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "resident")
|
||||
@Data
|
||||
public class Resident {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
* 主键,也是外键指向auth_user表
|
||||
*/
|
||||
@Id
|
||||
private UUID userId;
|
||||
|
||||
/**
|
||||
* 身份证号码
|
||||
* 住户的身份证号,用于身份验证
|
||||
*/
|
||||
@Column(length = 18)
|
||||
private String idCard;
|
||||
|
||||
/**
|
||||
* 住户类型
|
||||
* 标识住户类型:OWNER(业主)、FAMILY(家属)、TENANT(租户)
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String residentType;
|
||||
|
||||
/**
|
||||
* 认证状态
|
||||
* 标识住户的认证状态:UNVERIFIED(未认证)、PENDING(待审核)、VERIFIED(已认证)、REJECTED(已拒绝)
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String verificationStatus;
|
||||
|
||||
/**
|
||||
* 认证时间
|
||||
* 记录住户认证通过的时间
|
||||
*/
|
||||
private LocalDateTime verifiedAt;
|
||||
|
||||
/**
|
||||
* 认证人ID
|
||||
* 执行认证操作的管理员用户ID
|
||||
*/
|
||||
private UUID verifiedBy;
|
||||
|
||||
/**
|
||||
* 关联的User实体
|
||||
* 采用一对一关系,共享主键
|
||||
*/
|
||||
@OneToOne
|
||||
@MapsId
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package com.ether.pms.auth.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 住户房屋关联实体类
|
||||
*
|
||||
* <p>表示住户与房屋之间的关联关系,记录住户与房屋的绑定信息。</p>
|
||||
*
|
||||
* <p>支持业主、家属、租户等多种关系类型,以及绑定状态管理。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "resident_space")
|
||||
@Data
|
||||
public class ResidentSpace {
|
||||
|
||||
/**
|
||||
* 关联记录唯一标识符
|
||||
* 采用UUID策略自动生成
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
* 关联的用户唯一标识符
|
||||
*/
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private UUID userId;
|
||||
|
||||
/**
|
||||
* 房屋ID
|
||||
* 关联的房屋唯一标识符
|
||||
*/
|
||||
@Column(name = "space_id", nullable = false)
|
||||
private UUID spaceId;
|
||||
|
||||
/**
|
||||
* 关系类型
|
||||
* 标识住户与房屋的关系:OWNER(业主)、FAMILY(家属)、TENANT(租户)
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String relationType;
|
||||
|
||||
/**
|
||||
* 绑定状态
|
||||
* 标识关联的状态:PENDING(待生效)、ACTIVE(生效中)、EXPIRED(已过期)、CANCELLED(已取消)
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String bindingStatus;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
* 关联关系的生效日期
|
||||
*/
|
||||
private LocalDate startDate;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
* 关联关系的失效日期,永久关联则为空
|
||||
*/
|
||||
private LocalDate endDate;
|
||||
}
|
||||
|
|
@ -9,45 +9,92 @@ import java.time.LocalDateTime;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 角色实体类
|
||||
*
|
||||
* <p>用于定义系统中的角色信息,包括角色代码、名称、描述、类型、数据范围等属性。
|
||||
* 角色与权限通过多对多关联,一个角色可以拥有多个权限。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "auth_role")
|
||||
@Data
|
||||
public class Role {
|
||||
|
||||
/**
|
||||
* 角色唯一标识符
|
||||
* 使用UUID自动生成
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 角色代码
|
||||
* 用于系统内部识别,必须唯一
|
||||
* 格式:只能包含字母、数字和下划线,长度2-50位
|
||||
*/
|
||||
@NotNull(message = "角色代码不能为空")
|
||||
@Size(min = 2, max = 50, message = "角色代码长度必须在2-50位之间")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "角色代码只能包含字母、数字和下划线")
|
||||
@Column(unique = true, nullable = false, length = 50)
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 角色名称
|
||||
* 用于前端展示,长度2-50位
|
||||
*/
|
||||
@NotNull(message = "角色名称不能为空")
|
||||
@Size(min = 2, max = 50, message = "角色名称长度必须在2-50位之间")
|
||||
@Column(nullable = false, length = 50)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 角色描述
|
||||
* 对角色的详细说明,长度不超过200位
|
||||
*/
|
||||
@Size(max = 200, message = "角色描述长度不能超过200位")
|
||||
@Column(length = 200)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 角色类型
|
||||
* 区分系统级、项目级、部门级角色
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(length = 20)
|
||||
private RoleType type;
|
||||
|
||||
/**
|
||||
* 数据范围
|
||||
* 定义角色可访问的数据范围,默认为本人数据
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(length = 20)
|
||||
private DataScope dataScope = DataScope.SELF;
|
||||
|
||||
/**
|
||||
* 所属项目ID
|
||||
* 用于项目级角色的项目归属
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String projectId;
|
||||
|
||||
/**
|
||||
* 角色状态
|
||||
* 启用或禁用,默认为启用
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(length = 20)
|
||||
private RoleStatus status = RoleStatus.ENABLED;
|
||||
|
||||
/**
|
||||
* 角色关联的权限列表
|
||||
* 多对多关系,通过auth_role_permission关联表维护
|
||||
*/
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(
|
||||
name = "auth_role_permission",
|
||||
|
|
@ -56,24 +103,47 @@ public class Role {
|
|||
)
|
||||
private List<Permission> permissions;
|
||||
|
||||
/**
|
||||
* 角色创建时间
|
||||
* 自动设置,记录创建时刻
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 角色更新时间
|
||||
* 自动设置,每次更新时自动修改
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 持久化前回调
|
||||
* 自动设置创建时间和更新时间
|
||||
*/
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前回调
|
||||
* 自动设置更新时间
|
||||
*/
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色类型枚举
|
||||
* 定义角色的层级分类
|
||||
*/
|
||||
public enum RoleType {
|
||||
/** 系统级角色,可访问所有项目数据 */
|
||||
SYSTEM("系统级"),
|
||||
/** 项目级角色,仅可访问指定项目数据 */
|
||||
PROJECT("项目级"),
|
||||
/** 部门级角色,仅可访问本部门数据 */
|
||||
DEPARTMENT("部门级");
|
||||
|
||||
private final String desc;
|
||||
|
|
@ -83,10 +153,18 @@ public class Role {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据范围枚举
|
||||
* 定义角色可查看的数据范围级别
|
||||
*/
|
||||
public enum DataScope {
|
||||
/** 全部数据,可查看所有数据 */
|
||||
ALL("全部"),
|
||||
/** 本项目数据,仅可查看所属项目数据 */
|
||||
PROJECT("本项目"),
|
||||
/** 本部门数据,仅可查看本部门数据 */
|
||||
DEPARTMENT("本部门"),
|
||||
/** 本 人数据,仅可查看本人数据 */
|
||||
SELF("本人");
|
||||
|
||||
private final String desc;
|
||||
|
|
@ -96,8 +174,14 @@ public class Role {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色状态枚举
|
||||
* 定义角色的启用/禁用状态
|
||||
*/
|
||||
public enum RoleStatus {
|
||||
/** 角色已启用,可以正常使用 */
|
||||
ENABLED("启用"),
|
||||
/** 角色已禁用,不可使用 */
|
||||
DISABLED("禁用");
|
||||
|
||||
private final String desc;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package com.ether.pms.auth.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 房屋空间实体类
|
||||
*
|
||||
* <p>表示项目中的房屋或空间信息,包含楼栋、单元、房号等。</p>
|
||||
*
|
||||
* <p>支持住宅和商业两种类型的空间管理。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "space")
|
||||
@Data
|
||||
public class Space {
|
||||
|
||||
/**
|
||||
* 空间唯一标识符
|
||||
* 采用UUID策略自动生成
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 所属项目ID
|
||||
* 空间所属项目的唯一标识符
|
||||
*/
|
||||
private UUID projectId;
|
||||
|
||||
/**
|
||||
* 楼栋
|
||||
* 房屋所在的楼栋号或名称
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String building;
|
||||
|
||||
/**
|
||||
* 单元
|
||||
* 房屋所在的单元号
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String unit;
|
||||
|
||||
/**
|
||||
* 房号
|
||||
* 房屋的房间号
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String roomNo;
|
||||
|
||||
/**
|
||||
* 房屋类型
|
||||
* 标识房屋类型:RESIDENTIAL(住宅)、COMMERCIAL(商业)
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String spaceType;
|
||||
|
||||
/**
|
||||
* 楼层
|
||||
* 房屋所在的楼层
|
||||
*/
|
||||
private Integer floor;
|
||||
|
||||
/**
|
||||
* 建筑面积
|
||||
* 房屋的建筑面积,单位平方米
|
||||
*/
|
||||
private BigDecimal unitArea;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
* 标识房屋的状态:正常、空置、已售等
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String status;
|
||||
}
|
||||
|
|
@ -11,6 +11,13 @@ import org.hibernate.annotations.UpdateTimestamp;
|
|||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 系统配置实体类
|
||||
* 用于存储系统的键值对配置信息,如物业企业名称等
|
||||
*
|
||||
* @author Ether Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sys_config")
|
||||
@Data
|
||||
|
|
@ -19,23 +26,47 @@ import java.util.UUID;
|
|||
@Builder
|
||||
public class SysConfig {
|
||||
|
||||
/**
|
||||
* 配置项唯一标识
|
||||
* 使用 UUID 自动生成
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 配置键
|
||||
* 唯一标识一个配置项,如:property_company_name
|
||||
*/
|
||||
@Column(name = "config_key", nullable = false, unique = true, length = 128)
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* 配置值
|
||||
* 使用 TEXT 类型存储,支持长文本
|
||||
*/
|
||||
@Column(name = "config_value", columnDefinition = "TEXT")
|
||||
private String configValue;
|
||||
|
||||
/**
|
||||
* 配置描述
|
||||
* 用于说明该配置项的用途
|
||||
*/
|
||||
@Column(name = "description", length = 256)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
* 自动填充,不可更新
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
* 自动填充,更新时自动修改
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
|
|
|||
|
|
@ -10,48 +10,120 @@ import java.time.LocalDateTime;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 用户实体类
|
||||
*
|
||||
* <p>表示系统中的用户信息,包含用户的基本认证信息和个人资料。</p>
|
||||
*
|
||||
* <p>使用JPA注解映射到auth_user表,支持基本的CRUD操作。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "auth_user")
|
||||
@Data
|
||||
public class User {
|
||||
|
||||
/**
|
||||
* 用户唯一标识符
|
||||
* 采用UUID策略自动生成
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
* 用于用户登录,唯一标识,长度3-50位,只能包含字母、数字和下划线
|
||||
*/
|
||||
@NotNull(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 50, message = "用户名长度必须在3-50位之间")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
|
||||
@Column(unique = true, nullable = false, length = 50)
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码(加密存储)
|
||||
* 使用BCrypt加密后的密码原文
|
||||
*/
|
||||
@NotNull(message = "密码不能为空")
|
||||
@Column(nullable = false, length = 255)
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 密码盐值
|
||||
* 用于增强密码加密的安全性
|
||||
*/
|
||||
private String salt;
|
||||
|
||||
/**
|
||||
* 真实姓名
|
||||
* 用户的真实姓名,用于显示
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String realName;
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
* 中国大陆手机号格式,11位数字
|
||||
*/
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
@Column(length = 20)
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 电子邮箱
|
||||
* 用户的邮箱地址,用于通知和验证
|
||||
*/
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Column(length = 100)
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
* 用户头像的存储路径或URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
* 标识用户的当前状态:正常、锁定或禁用
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(length = 20)
|
||||
private UserStatus status = UserStatus.ACTIVE;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
* 标识用户的类型:ENTERPRISE(企业用户)、PROJECT_STAFF(项目员工)、RESIDENT(住户)、CUSTOMER(客户)
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String userType;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
* 用户所属的部门标识符
|
||||
*/
|
||||
private UUID deptId;
|
||||
|
||||
/**
|
||||
* 最后登录时间
|
||||
* 记录用户最后一次成功登录的时间
|
||||
*/
|
||||
private LocalDateTime lastLoginTime;
|
||||
|
||||
/**
|
||||
* 最后登录IP
|
||||
* 记录用户最后一次成功登录的IP地址
|
||||
*/
|
||||
private String lastLoginIp;
|
||||
|
||||
/**
|
||||
* 用户关联的角色列表
|
||||
* 采用懒加载方式获取用户的所有角色
|
||||
*/
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(
|
||||
name = "auth_user_role",
|
||||
|
|
@ -60,34 +132,72 @@ public class User {
|
|||
)
|
||||
private List<Role> roles;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
* 记录用户账号的创建时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
* 记录用户信息的最后修改时间
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
* 记录创建该用户的管理员或系统ID
|
||||
*/
|
||||
private UUID createdBy;
|
||||
|
||||
/**
|
||||
* 持久化前回调
|
||||
* 在用户对象首次保存到数据库前自动设置创建时间和更新时间
|
||||
*/
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前回调
|
||||
* 在用户对象更新前自动设置更新时间
|
||||
*/
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户状态枚举
|
||||
* 定义用户的三种状态:正常、锁定、禁用
|
||||
*/
|
||||
public enum UserStatus {
|
||||
/** 正常状态 - 用户可以正常登录和使用系统 */
|
||||
ACTIVE("正常"),
|
||||
/** 锁定状态 - 用户被锁定,暂时无法登录 */
|
||||
LOCKED("锁定"),
|
||||
/** 禁用状态 - 用户被禁用,无法使用系统 */
|
||||
DISABLED("禁用");
|
||||
|
||||
/** 状态描述 */
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param desc 状态描述
|
||||
*/
|
||||
UserStatus(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态描述
|
||||
*
|
||||
* @return 状态的中文描述
|
||||
*/
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,27 +5,62 @@ import lombok.Data;
|
|||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 用户项目关联实体类
|
||||
*
|
||||
* <p>表示用户与项目之间的关联关系,记录用户在特定项目中的角色和加入时间。</p>
|
||||
*
|
||||
* <p>一个用户可以关联多个项目,一个项目也可以关联多个用户,形成多对多关系。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "user_project")
|
||||
@Data
|
||||
public class UserProject {
|
||||
|
||||
/**
|
||||
* 关联记录唯一标识符
|
||||
* 采用UUID策略自动生成
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
* 关联的用户唯一标识符
|
||||
*/
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private UUID userId;
|
||||
|
||||
/**
|
||||
* 项目ID
|
||||
* 关联的项目唯一标识符
|
||||
*/
|
||||
@Column(name = "project_id", nullable = false)
|
||||
private UUID projectId;
|
||||
|
||||
/**
|
||||
* 在项目中的角色
|
||||
* 记录用户在关联项目中的角色,默认值为"member"(成员)
|
||||
*/
|
||||
@Column(name = "role_in_project", nullable = false)
|
||||
private String roleInProject = "member";
|
||||
|
||||
/**
|
||||
* 加入时间
|
||||
* 记录用户加入项目的时间,默认为当前时间
|
||||
*/
|
||||
@Column(name = "joined_at", nullable = false)
|
||||
private LocalDateTime joinedAt = LocalDateTime.now();
|
||||
|
||||
/**
|
||||
* 持久化前回调
|
||||
* 在关联记录首次保存前,如果加入时间为空则设置为当前时间
|
||||
*/
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
if (this.joinedAt == null) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
package com.ether.pms.auth.repository;
|
||||
|
||||
import com.ether.pms.auth.entity.Dept;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 部门数据访问层接口
|
||||
*
|
||||
* <p>提供部门数据的持久化操作,继承Spring Data JPA的JpaRepository接口。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface DeptRepository extends JpaRepository<Dept, UUID> {
|
||||
|
||||
/**
|
||||
* 查询顶级部门列表
|
||||
*
|
||||
* <p>查询没有上级部门的部门,即树形结构的根节点。</p>
|
||||
*
|
||||
* @return 顶级部门列表
|
||||
*/
|
||||
List<Dept> findByParentIdIsNullOrderBySortOrder();
|
||||
|
||||
/**
|
||||
* 根据父部门ID查询子部门列表
|
||||
*
|
||||
* @param parentId 父部门唯一标识符
|
||||
* @return 该父部门下的所有子部门列表
|
||||
*/
|
||||
List<Dept> findByParentIdOrderBySortOrder(UUID parentId);
|
||||
|
||||
/**
|
||||
* 根据部门编码查询部门
|
||||
*
|
||||
* @param deptCode 部门编码
|
||||
* @return 包含部门的Optional对象
|
||||
*/
|
||||
Optional<Dept> findByDeptCode(String deptCode);
|
||||
|
||||
/**
|
||||
* 根据部门类型查询部门列表
|
||||
*
|
||||
* @param deptType 部门类型
|
||||
* @return 该类型的所有部门列表
|
||||
*/
|
||||
List<Dept> findByDeptTypeOrderBySortOrder(String deptType);
|
||||
|
||||
/**
|
||||
* 查询所有启用的部门
|
||||
*
|
||||
* @return 所有状态为ACTIVE的部门
|
||||
*/
|
||||
List<Dept> findByStatusOrderBySortOrder(String status);
|
||||
|
||||
/**
|
||||
* 根据部门负责人查询部门
|
||||
*
|
||||
* @param leaderId 负责人用户ID
|
||||
* @return 该负责人管理的部门列表
|
||||
*/
|
||||
List<Dept> findByLeaderId(UUID leaderId);
|
||||
|
||||
/**
|
||||
* 查询所有部门(用于树形结构构建)
|
||||
*
|
||||
* @return 所有部门列表
|
||||
*/
|
||||
@Query("SELECT d FROM Dept d ORDER BY d.sortOrder")
|
||||
List<Dept> findAllForTree();
|
||||
|
||||
/**
|
||||
* 检查部门编码是否存在
|
||||
*
|
||||
* @param deptCode 部门编码
|
||||
* @return 存在返回true
|
||||
*/
|
||||
boolean existsByDeptCode(String deptCode);
|
||||
|
||||
/**
|
||||
* 根据ID查询部门及其默认角色
|
||||
*
|
||||
* @param id 部门ID
|
||||
* @return 部门的默认角色编码
|
||||
*/
|
||||
@Query("SELECT d.defaultRoleCode FROM Dept d WHERE d.id = :id")
|
||||
String findDefaultRoleCodeById(@Param("id") UUID id);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.ether.pms.auth.repository;
|
||||
|
||||
import com.ether.pms.auth.entity.EnterpriseUser;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 企业用户数据访问层接口
|
||||
*
|
||||
* <p>提供企业用户数据的持久化操作,继承Spring Data JPA的JpaRepository接口。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface EnterpriseUserRepository extends JpaRepository<EnterpriseUser, UUID> {
|
||||
|
||||
/**
|
||||
* 根据用户ID查询企业用户
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 包含企业用户的Optional对象
|
||||
*/
|
||||
Optional<EnterpriseUser> findByUserId(UUID userId);
|
||||
|
||||
/**
|
||||
* 根据部门ID查询企业用户列表
|
||||
*
|
||||
* @param deptId 部门唯一标识符
|
||||
* @return 该部门下的所有企业用户列表
|
||||
*/
|
||||
List<EnterpriseUser> findByDeptId(UUID deptId);
|
||||
}
|
||||
|
|
@ -6,12 +6,46 @@ import org.springframework.stereotype.Repository;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 权限数据访问层接口
|
||||
*
|
||||
* <p>提供权限(Permission)实体的数据库操作方法,继承Spring Data JPA的JpaRepository。
|
||||
* 支持按权限类型、父权限代码等条件查询。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface PermissionRepository extends JpaRepository<Permission, UUID> {
|
||||
|
||||
/**
|
||||
* 根据权限类型查询权限列表
|
||||
*
|
||||
* <p>按权限类型筛选,如MENU(菜单)、BUTTON(按钮)、API(接口)等。</p>
|
||||
*
|
||||
* @param type 权限类型
|
||||
* @return 该类型的所有权限列表
|
||||
*/
|
||||
List<Permission> findByType(String type);
|
||||
|
||||
/**
|
||||
* 根据父权限代码查询子权限列表
|
||||
*
|
||||
* <p>用于构建权限树形结构,查找属于某个父权限的所有子权限。</p>
|
||||
*
|
||||
* @param parentCode 父权限代码
|
||||
* @return 该父权限下的所有子权限列表
|
||||
*/
|
||||
List<Permission> findByParentCode(String parentCode);
|
||||
|
||||
/**
|
||||
* 检查权限代码是否存在
|
||||
*
|
||||
* <p>用于创建权限时的唯一性校验。</p>
|
||||
*
|
||||
* @param code 权限代码
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
boolean existsByCode(String code);
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package com.ether.pms.auth.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ether.pms.auth.entity.ProjectStaff;
|
||||
|
||||
/**
|
||||
* 项目员工数据访问层接口
|
||||
*
|
||||
* <p>提供项目员工数据的持久化操作,继承Spring Data JPA的JpaRepository接口。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface ProjectStaffRepository extends JpaRepository<ProjectStaff, UUID> {
|
||||
|
||||
/**
|
||||
* 根据用户ID查询项目员工
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 包含项目员工的Optional对象
|
||||
*/
|
||||
Optional<ProjectStaff> findByUserId(UUID userId);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询所有项目员工记录
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 该用户的所有项目员工记录列表
|
||||
*/
|
||||
List<ProjectStaff> findAllByUserId(UUID userId);
|
||||
|
||||
/**
|
||||
* 根据项目ID查询所有项目员工
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 该项目下的所有员工列表
|
||||
*/
|
||||
List<ProjectStaff> findByProjectId(UUID projectId);
|
||||
|
||||
/**
|
||||
* 根据项目ID查询所有项目员工(包含角色信息)
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 该项目下的所有员工列表(包含角色信息)
|
||||
*/
|
||||
@Query("SELECT DISTINCT ps FROM ProjectStaff ps " +
|
||||
"LEFT JOIN FETCH ps.staffRoles sr " +
|
||||
"LEFT JOIN FETCH sr.role " +
|
||||
"LEFT JOIN FETCH ps.user " +
|
||||
"WHERE ps.projectId = :projectId")
|
||||
List<ProjectStaff> findByProjectIdWithRoles(UUID projectId);
|
||||
|
||||
/**
|
||||
* 根据用户ID和项目ID删除项目员工关联
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
*/
|
||||
void deleteByUserIdAndProjectId(UUID projectId, UUID userId);
|
||||
|
||||
/**
|
||||
* 根据用户ID和项目ID查询项目员工
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 包含项目员工的Optional对象
|
||||
*/
|
||||
Optional<ProjectStaff> findByUserIdAndProjectId(UUID userId, UUID projectId);
|
||||
|
||||
/**
|
||||
* 根据项目ID统计成员数量
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 该项目的成员数量
|
||||
*/
|
||||
long countByProjectId(UUID projectId);
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.ether.pms.auth.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ether.pms.auth.entity.ProjectStaffRole;
|
||||
|
||||
@Repository
|
||||
public interface ProjectStaffRoleRepository extends JpaRepository<ProjectStaffRole, UUID> {
|
||||
|
||||
/**
|
||||
* 根据员工ID查询所有角色关联
|
||||
*
|
||||
* @param staffId 员工ID
|
||||
* @return 角色关联列表
|
||||
*/
|
||||
List<ProjectStaffRole> findByStaff_Id(UUID staffId);
|
||||
|
||||
/**
|
||||
* 根据员工ID删除所有角色关联
|
||||
*
|
||||
* @param staffId 员工ID
|
||||
*/
|
||||
void deleteByStaff_Id(UUID staffId);
|
||||
|
||||
/**
|
||||
* 检查是否存在指定员工和角色的关联
|
||||
*
|
||||
* @param staffId 员工ID
|
||||
* @param roleId 角色ID
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByStaff_IdAndRole_Id(UUID staffId, UUID roleId);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.ether.pms.auth.repository;
|
||||
|
||||
import com.ether.pms.auth.entity.Resident;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 住户数据访问层接口
|
||||
*
|
||||
* <p>提供住户数据的持久化操作,继承Spring Data JPA的JpaRepository接口。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface ResidentRepository extends JpaRepository<Resident, UUID> {
|
||||
|
||||
/**
|
||||
* 根据用户ID查询住户
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 包含住户的Optional对象
|
||||
*/
|
||||
Optional<Resident> findByUserId(UUID userId);
|
||||
|
||||
/**
|
||||
* 根据认证状态查询住户列表
|
||||
*
|
||||
* @param status 认证状态
|
||||
* @return 该状态下的所有住户列表
|
||||
*/
|
||||
List<Resident> findByVerificationStatus(String status);
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.ether.pms.auth.repository;
|
||||
|
||||
import com.ether.pms.auth.entity.ResidentSpace;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 住户房屋关联数据访问层接口
|
||||
*
|
||||
* <p>提供住户房屋关联数据的持久化操作,继承Spring Data JPA的JpaRepository接口。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface ResidentSpaceRepository extends JpaRepository<ResidentSpace, UUID> {
|
||||
|
||||
/**
|
||||
* 根据用户ID查询所有住户房屋关联
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 该用户的所有住户房屋关联列表
|
||||
*/
|
||||
List<ResidentSpace> findByUserId(UUID userId);
|
||||
|
||||
/**
|
||||
* 根据房屋ID查询所有住户房屋关联
|
||||
*
|
||||
* @param spaceId 房屋唯一标识符
|
||||
* @return 该房屋的所有住户关联列表
|
||||
*/
|
||||
List<ResidentSpace> findBySpaceId(UUID spaceId);
|
||||
|
||||
/**
|
||||
* 根据用户ID和房屋ID查询住户房屋关联
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @param spaceId 房屋唯一标识符
|
||||
* @return 包含住户房屋关联的Optional对象
|
||||
*/
|
||||
Optional<ResidentSpace> findByUserIdAndSpaceId(UUID userId, UUID spaceId);
|
||||
}
|
||||
|
|
@ -8,17 +8,68 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 角色数据访问层接口
|
||||
*
|
||||
* <p>提供角色(Role)实体的数据库操作方法,继承Spring Data JPA的JpaRepository。
|
||||
* 支持按角色代码、项目ID、类型等条件查询,以及带权限信息的复杂查询。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface RoleRepository extends JpaRepository<Role, UUID> {
|
||||
|
||||
/**
|
||||
* 根据角色代码查询角色
|
||||
*
|
||||
* <p>角色代码在系统中唯一,用于精确查找特定角色。</p>
|
||||
*
|
||||
* @param code 角色代码
|
||||
* @return 角色对象(如果存在)
|
||||
*/
|
||||
Optional<Role> findByCode(String code);
|
||||
|
||||
/**
|
||||
* 检查角色代码是否存在
|
||||
*
|
||||
* <p>用于创建角色时的唯一性校验。</p>
|
||||
*
|
||||
* @param code 角色代码
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
boolean existsByCode(String code);
|
||||
|
||||
/**
|
||||
* 根据项目ID查询角色列表
|
||||
*
|
||||
* <p>获取指定项目下的所有角色,通常用于项目级别的角色筛选。</p>
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 该项目下的角色列表
|
||||
*/
|
||||
List<Role> findByProjectId(String projectId);
|
||||
|
||||
/**
|
||||
* 根据角色类型查询角色列表
|
||||
*
|
||||
* <p>按角色类型(如系统级、项目级、部门级)筛选角色。</p>
|
||||
*
|
||||
* @param type 角色类型枚举
|
||||
* @return 该类型的所有角色列表
|
||||
*/
|
||||
List<Role> findByType(Role.RoleType type);
|
||||
|
||||
/**
|
||||
* 根据角色ID查询角色及其关联的权限信息
|
||||
*
|
||||
* <p>使用EntityGraph eagerly加载permissions关联数据,
|
||||
* 避免N+1查询问题,提升查询性能。</p>
|
||||
*
|
||||
* @param id 角色ID
|
||||
* @return 包含权限信息的角色对象(如果存在)
|
||||
*/
|
||||
@EntityGraph(attributePaths = {"permissions"})
|
||||
Optional<Role> findWithPermissionsById(UUID id);
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.ether.pms.auth.repository;
|
||||
|
||||
import com.ether.pms.auth.entity.Space;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 房屋空间数据访问层接口
|
||||
*
|
||||
* <p>提供房屋空间数据的持久化操作,继承Spring Data JPA的JpaRepository接口。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface SpaceRepository extends JpaRepository<Space, UUID> {
|
||||
|
||||
/**
|
||||
* 根据项目ID查询所有房屋空间
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 该项目下的所有房屋空间列表
|
||||
*/
|
||||
List<Space> findByProjectId(UUID projectId);
|
||||
|
||||
/**
|
||||
* 根据项目ID、楼栋、单元和房号查询房屋空间
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @param building 楼栋
|
||||
* @param unit 单元
|
||||
* @param roomNo 房号
|
||||
* @return 包含房屋空间的Optional对象
|
||||
*/
|
||||
Optional<Space> findByProjectIdAndBuildingAndUnitAndRoomNo(UUID projectId, String building, String unit, String roomNo);
|
||||
}
|
||||
|
|
@ -7,8 +7,21 @@ import org.springframework.stereotype.Repository;
|
|||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 系统配置数据访问层
|
||||
* 提供对 sys_config 表的 CRUD 操作
|
||||
*
|
||||
* @author Ether Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Repository
|
||||
public interface SysConfigRepository extends JpaRepository<SysConfig, UUID> {
|
||||
|
||||
/**
|
||||
* 根据配置键查询配置项
|
||||
*
|
||||
* @param configKey 配置键,如:property_company_name
|
||||
* @return 配置项 Optional 包装
|
||||
*/
|
||||
Optional<SysConfig> findByConfigKey(String configKey);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,19 +10,85 @@ import org.springframework.stereotype.Repository;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 用户项目关联数据访问层接口
|
||||
*
|
||||
* <p>提供用户与项目关联关系的持久化操作,继承Spring Data JPA的JpaRepository接口。</p>
|
||||
*
|
||||
* <p>支持用户项目关系查询、分页、批量操作等功能。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface UserProjectRepository extends JpaRepository<UserProject, UUID> {
|
||||
|
||||
/**
|
||||
* 根据用户ID查询该用户关联的所有项目
|
||||
*
|
||||
* <p>返回指定用户参与的所有项目关联记录列表。</p>
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 该用户关联的所有项目列表
|
||||
*/
|
||||
List<UserProject> findByUserId(UUID userId);
|
||||
|
||||
/**
|
||||
* 根据项目ID查询该项目的所有关联用户
|
||||
*
|
||||
* <p>返回参与指定项目的所有用户关联记录列表。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 该项目关联的所有用户列表
|
||||
*/
|
||||
List<UserProject> findByProjectId(UUID projectId);
|
||||
|
||||
/**
|
||||
* 根据项目ID分页查询该项目的所有关联用户
|
||||
*
|
||||
* <p>支持分页展示项目成员列表。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @param pageable 分页参数,包含页码和每页大小
|
||||
* @return 分页的用户项目关联列表
|
||||
*/
|
||||
Page<UserProject> findByProjectId(UUID projectId, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询该用户关联的所有项目ID
|
||||
*
|
||||
* <p>仅返回项目ID列表,用于快速判断用户参与的项目。</p>
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 该用户关联的所有项目ID列表
|
||||
*/
|
||||
@Query("SELECT up.projectId FROM UserProject up WHERE up.userId = :userId")
|
||||
List<UUID> findProjectIdsByUserId(@Param("userId") UUID userId);
|
||||
|
||||
/**
|
||||
* 检查用户与项目的关联是否存在
|
||||
*
|
||||
* <p>用于判断用户是否参与了指定项目。</p>
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
boolean existsByUserIdAndProjectId(UUID userId, UUID projectId);
|
||||
|
||||
/**
|
||||
* 删除用户与项目的关联
|
||||
*
|
||||
* <p>根据用户ID和项目ID删除关联记录,用于用户退出项目或移除项目成员。</p>
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
*/
|
||||
void deleteByUserIdAndProjectId(UUID userId, UUID projectId);
|
||||
|
||||
/**
|
||||
* 统计项目下的成员数量
|
||||
*/
|
||||
long countByProjectId(UUID projectId);
|
||||
}
|
||||
|
|
@ -9,24 +9,121 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 用户数据访问层接口
|
||||
*
|
||||
* <p>提供用户数据的持久化操作,继承Spring Data JPA的JpaRepository接口。</p>
|
||||
*
|
||||
* <p>包含用户查询、角色关联查询等数据库操作方法。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, UUID> {
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户
|
||||
*
|
||||
* <p>最基础的按用户名查询方法,返回Optional包装的用户对象。</p>
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 包含用户的Optional对象,如果不存在则为空
|
||||
*/
|
||||
Optional<User> findByUsername(String username);
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户及其关联的角色
|
||||
*
|
||||
* <p>使用LEFT JOIN FETCH预加载用户的角色信息,避免N+1查询问题。</p>
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 包含用户及其角色的Optional对象
|
||||
*/
|
||||
@Query("SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.username = :username")
|
||||
Optional<User> findByUsernameWithRoles(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 查询所有用户及其关联的角色
|
||||
*
|
||||
* <p>一次性加载所有用户及其角色信息,适用于管理后台用户列表。</p>
|
||||
*
|
||||
* @return 包含所有用户及其角色的列表
|
||||
*/
|
||||
@Query("SELECT u FROM User u LEFT JOIN FETCH u.roles")
|
||||
List<User> findAllWithRoles();
|
||||
|
||||
/**
|
||||
* 根据ID查询用户及其关联的角色
|
||||
*
|
||||
* <p>使用LEFT JOIN FETCH预加载用户的角色信息。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @return 包含用户及其角色的Optional对象
|
||||
*/
|
||||
@Query("SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.id = :id")
|
||||
Optional<User> findByIdWithRoles(@Param("id") UUID id);
|
||||
|
||||
/**
|
||||
* 根据角色ID查询所有拥有该角色的用户
|
||||
*
|
||||
* <p>用于查询具有特定角色的所有用户列表。</p>
|
||||
*
|
||||
* @param roleId 角色唯一标识符
|
||||
* @return 拥有该角色的所有用户列表
|
||||
*/
|
||||
@Query("SELECT u FROM User u JOIN u.roles r WHERE r.id = :roleId")
|
||||
List<User> findByRoleId(@Param("roleId") UUID roleId);
|
||||
|
||||
/**
|
||||
* 检查用户名是否存在
|
||||
*
|
||||
* <p>用于用户注册时验证用户名唯一性。</p>
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
boolean existsByUsername(String username);
|
||||
|
||||
/**
|
||||
* 检查手机号是否存在
|
||||
*
|
||||
* <p>用于用户注册或更新时验证手机号唯一性。</p>
|
||||
*
|
||||
* @param phone 手机号码
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
boolean existsByPhone(String phone);
|
||||
|
||||
/**
|
||||
* 根据用户类型查询用户列表
|
||||
*
|
||||
* <p>用于查询特定类型的用户,如ENTERPRISE、PROJECT_STAFF等。</p>
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @return 该类型的所有用户列表
|
||||
*/
|
||||
List<User> findByUserType(String userType);
|
||||
|
||||
/**
|
||||
* 根据部门ID查询用户列表
|
||||
*
|
||||
* <p>用于查询属于特定部门的所有用户。</p>
|
||||
*
|
||||
* @param deptId 部门唯一标识符
|
||||
* @return 该部门下的所有用户列表
|
||||
*/
|
||||
List<User> findByDeptId(UUID deptId);
|
||||
|
||||
/**
|
||||
* 根据项目ID查询项目员工
|
||||
*
|
||||
* <p>通过关联ProjectStaff表查询属于指定项目的所有员工用户。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return该项目下的所有员工用户列表
|
||||
*/
|
||||
@Query("SELECT u FROM User u JOIN ProjectStaff ps ON u.id = ps.userId WHERE ps.projectId = :projectId")
|
||||
List<User> findProjectStaffsByProjectId(@Param("projectId") UUID projectId);
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package com.ether.pms.auth.service;
|
||||
|
||||
import com.ether.pms.auth.entity.Dept;
|
||||
import com.ether.pms.auth.entity.User;
|
||||
import com.ether.pms.auth.repository.DeptRepository;
|
||||
import com.ether.pms.auth.repository.UserRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 部门服务层
|
||||
*
|
||||
* <p>提供部门相关的业务逻辑处理,包括部门树查询、部门CRUD、部门员工查询等功能。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DeptService {
|
||||
|
||||
private final DeptRepository deptRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* 获取部门树
|
||||
*
|
||||
* <p>返回所有部门的列表,支持前端构建树形结构。</p>
|
||||
*
|
||||
* @return 所有部门的列表
|
||||
*/
|
||||
public List<Dept> getDeptTree() {
|
||||
return deptRepository.findAllForTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有启用的部门列表
|
||||
*
|
||||
* @return 启用的部门列表
|
||||
*/
|
||||
public List<Dept> getActiveDepts() {
|
||||
return deptRepository.findByStatusOrderBySortOrder("ACTIVE");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取部门
|
||||
*
|
||||
* @param id 部门ID
|
||||
* @return 部门对象
|
||||
*/
|
||||
public Optional<Dept> getById(UUID id) {
|
||||
return deptRepository.findById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门及下属员工
|
||||
*
|
||||
* <p>根据部门ID查询该部门下的所有员工用户。</p>
|
||||
*
|
||||
* @param deptId 部门唯一标识符
|
||||
* @return 该部门下的所有员工用户列表
|
||||
*/
|
||||
public List<User> getDeptEmployees(UUID deptId) {
|
||||
return userRepository.findByDeptId(deptId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建部门
|
||||
*
|
||||
* @param dept 待创建的部门对象
|
||||
* @return 创建成功的部门对象
|
||||
*/
|
||||
@Transactional
|
||||
public Dept createDept(Dept dept) {
|
||||
if (dept.getSortOrder() == null) {
|
||||
dept.setSortOrder(0);
|
||||
}
|
||||
if (dept.getStatus() == null) {
|
||||
dept.setStatus("ACTIVE");
|
||||
}
|
||||
if (dept.getDeptType() == null) {
|
||||
dept.setDeptType("ADMIN");
|
||||
}
|
||||
return deptRepository.save(dept);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新部门
|
||||
*
|
||||
* @param id 部门ID
|
||||
* @param dept 更新后的部门信息
|
||||
* @return 更新后的部门对象
|
||||
*/
|
||||
@Transactional
|
||||
public Dept updateDept(UUID id, Dept dept) {
|
||||
Dept existing = deptRepository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("部门不存在: " + id));
|
||||
|
||||
existing.setDeptName(dept.getDeptName());
|
||||
existing.setDeptCode(dept.getDeptCode());
|
||||
existing.setParentId(dept.getParentId());
|
||||
existing.setDeptType(dept.getDeptType());
|
||||
existing.setDefaultRoleCode(dept.getDefaultRoleCode());
|
||||
existing.setLeaderId(dept.getLeaderId());
|
||||
existing.setSortOrder(dept.getSortOrder());
|
||||
existing.setStatus(dept.getStatus());
|
||||
|
||||
return deptRepository.save(existing);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
*
|
||||
* @param id 部门ID
|
||||
*/
|
||||
@Transactional
|
||||
public void deleteDept(UUID id) {
|
||||
Dept dept = deptRepository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("部门不存在: " + id));
|
||||
|
||||
List<Dept> children = deptRepository.findByParentIdOrderBySortOrder(id);
|
||||
if (!children.isEmpty()) {
|
||||
throw new IllegalStateException("请先删除子部门");
|
||||
}
|
||||
|
||||
deptRepository.delete(dept);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门类型查询部门
|
||||
*
|
||||
* @param deptType 部门类型
|
||||
* @return 该类型的部门列表
|
||||
*/
|
||||
public List<Dept> getByType(String deptType) {
|
||||
return deptRepository.findByDeptTypeOrderBySortOrder(deptType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查部门编码是否存在
|
||||
*
|
||||
* @param deptCode 部门编码
|
||||
* @return 存在返回true
|
||||
*/
|
||||
public boolean existsByCode(String deptCode) {
|
||||
return deptRepository.existsByDeptCode(deptCode);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.ether.pms.auth.service;
|
||||
|
||||
import com.ether.pms.auth.entity.ProjectStaff;
|
||||
import com.ether.pms.auth.entity.User;
|
||||
import com.ether.pms.auth.repository.ProjectStaffRepository;
|
||||
import com.ether.pms.auth.repository.UserRepository;
|
||||
import com.ether.pms.auth.util.JwtTokenProvider;
|
||||
import com.ether.pms.common.BusinessException;
|
||||
|
|
@ -9,14 +11,15 @@ import lombok.RequiredArgsConstructor;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class LoginService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final ProjectStaffRepository projectStaffRepository;
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
private final PasswordService passwordService;
|
||||
private final LoginAttemptService loginAttemptService;
|
||||
|
|
@ -45,13 +48,26 @@ public class LoginService {
|
|||
|
||||
loginAttemptService.recordSuccess(username);
|
||||
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
Set<String> allRoles = new HashSet<>();
|
||||
if (user.getRoles() != null) {
|
||||
claims.put("roles", user.getRoles().stream()
|
||||
allRoles.addAll(user.getRoles().stream()
|
||||
.map(r -> r.getCode())
|
||||
.toList());
|
||||
}
|
||||
|
||||
List<ProjectStaff> projectStaffs = projectStaffRepository.findAllByUserId(user.getId());
|
||||
for (ProjectStaff staff : projectStaffs) {
|
||||
if (staff.getStaffRoles() != null) {
|
||||
allRoles.addAll(staff.getStaffRoles().stream()
|
||||
.filter(sr -> sr.getRole() != null)
|
||||
.map(sr -> sr.getRole().getCode())
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("roles", new ArrayList<>(allRoles));
|
||||
|
||||
String token = jwtTokenProvider.generateToken(user.getId(), user.getUsername(), claims);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
|
@ -59,6 +75,7 @@ public class LoginService {
|
|||
result.put("userId", user.getId());
|
||||
result.put("username", user.getUsername());
|
||||
result.put("realName", user.getRealName());
|
||||
result.put("roles", new ArrayList<>(allRoles));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,15 +44,15 @@ public class PasswordService {
|
|||
throw new IllegalArgumentException("密码长度必须在" + minLength + "-" + maxLength + "位之间");
|
||||
}
|
||||
|
||||
if (requireUppercase && !containsAny(password, 'A', 'Z')) {
|
||||
if (requireUppercase && !Pattern.compile("[A-Z]").matcher(password).find()) {
|
||||
throw new IllegalArgumentException("密码必须包含大写字母");
|
||||
}
|
||||
|
||||
if (requireLowercase && !containsAny(password, 'a', 'z')) {
|
||||
if (requireLowercase && !Pattern.compile("[a-z]").matcher(password).find()) {
|
||||
throw new IllegalArgumentException("密码必须包含小写字母");
|
||||
}
|
||||
|
||||
if (requireDigit && !containsAny(password, '0', '9')) {
|
||||
if (requireDigit && !Pattern.compile("[0-9]").matcher(password).find()) {
|
||||
throw new IllegalArgumentException("密码必须包含数字");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,33 +11,91 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 权限业务逻辑服务类
|
||||
*
|
||||
* <p>提供权限(Permission)相关的业务操作,包括权限的增删改查、类型筛选、菜单权限获取等功能。
|
||||
* 所有写操作均支持事务管理,确保数据一致性。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionService {
|
||||
|
||||
/** 权限数据访问仓库 */
|
||||
private final PermissionRepository permissionRepository;
|
||||
|
||||
/**
|
||||
* 查询所有权限
|
||||
*
|
||||
* @return 所有权限的列表
|
||||
*/
|
||||
public List<Permission> findAll() {
|
||||
return permissionRepository.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据权限ID查询权限
|
||||
*
|
||||
* <p>如果权限不存在,抛出PERMISSION_002业务异常。</p>
|
||||
*
|
||||
* @param id 权限ID
|
||||
* @return 权限对象
|
||||
* @throws BusinessException 如果权限不存在
|
||||
*/
|
||||
public Permission findById(UUID id) {
|
||||
return permissionRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.PERMISSION_002));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据权限类型查询权限列表
|
||||
*
|
||||
* <p>按权限类型筛选,如MENU、BUTTON、API等。</p>
|
||||
*
|
||||
* @param type 权限类型
|
||||
* @return 该类型的所有权限列表
|
||||
*/
|
||||
public List<Permission> findByType(String type) {
|
||||
return permissionRepository.findByType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父权限代码查询子权限列表
|
||||
*
|
||||
* <p>用于获取某个父权限下的所有子权限,构建权限树。</p>
|
||||
*
|
||||
* @param parentCode 父权限代码
|
||||
* @return 该父权限下的所有子权限列表
|
||||
*/
|
||||
public List<Permission> findByParentCode(String parentCode) {
|
||||
return permissionRepository.findByParentCode(parentCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有菜单类型权限
|
||||
*
|
||||
* <p>获取type为MENU的所有权限,通常用于前端菜单渲染。</p>
|
||||
*
|
||||
* @return 所有菜单权限列表
|
||||
*/
|
||||
public List<Permission> findMenuPermissions() {
|
||||
return permissionRepository.findByType("MENU");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新权限
|
||||
*
|
||||
* <p>如果权限代码已存在,抛出PERMISSION_001业务异常。
|
||||
* 创建时会自动设置创建时间和更新时间。</p>
|
||||
*
|
||||
* @param permission 要创建的权限对象
|
||||
* @return 创建后的权限对象(包含数据库生成的主键)
|
||||
* @throws BusinessException 如果权限代码已存在
|
||||
*/
|
||||
@Transactional
|
||||
public Permission create(Permission permission) {
|
||||
if (permissionRepository.existsByCode(permission.getCode())) {
|
||||
|
|
@ -46,6 +104,17 @@ public class PermissionService {
|
|||
return permissionRepository.save(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新权限信息
|
||||
*
|
||||
* <p>仅更新传入参数中非空的字段,保持其他字段不变。
|
||||
* 如果权限不存在,抛出PERMISSION_002业务异常。</p>
|
||||
*
|
||||
* @param id 要更新的权限ID
|
||||
* @param permission 包含新数据的权限对象
|
||||
* @return 更新后的权限对象
|
||||
* @throws BusinessException 如果权限不存在
|
||||
*/
|
||||
@Transactional
|
||||
public Permission update(UUID id, Permission permission) {
|
||||
Permission existing = findById(id);
|
||||
|
|
@ -75,6 +144,13 @@ public class PermissionService {
|
|||
return permissionRepository.save(existing);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除权限
|
||||
*
|
||||
* <p>根据权限ID删除权限记录。</p>
|
||||
*
|
||||
* @param id 要删除的权限ID
|
||||
*/
|
||||
@Transactional
|
||||
public void delete(UUID id) {
|
||||
permissionRepository.deleteById(id);
|
||||
|
|
|
|||
|
|
@ -15,32 +15,88 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 角色业务逻辑服务类
|
||||
*
|
||||
* <p>提供角色(Role)相关的业务操作,包括角色的增删改查、权限分配、用户关联等功能。
|
||||
* 所有操作均支持事务管理,确保数据一致性。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoleService {
|
||||
|
||||
/** 角色数据访问仓库 */
|
||||
private final RoleRepository roleRepository;
|
||||
|
||||
/** 权限数据访问仓库 */
|
||||
private final PermissionRepository permissionRepository;
|
||||
|
||||
/** 用户数据访问仓库 */
|
||||
private final UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* 查询所有角色
|
||||
*
|
||||
* @return 所有角色的列表
|
||||
*/
|
||||
public List<Role> findAll() {
|
||||
return roleRepository.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色ID查询角色
|
||||
*
|
||||
* <p>如果角色不存在,抛出ROLE_002业务异常。</p>
|
||||
*
|
||||
* @param id 角色ID
|
||||
* @return 角色对象
|
||||
* @throws BusinessException 如果角色不存在
|
||||
*/
|
||||
public Role findById(UUID id) {
|
||||
return roleRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ROLE_002));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色代码查询角色
|
||||
*
|
||||
* <p>如果角色不存在,抛出ROLE_002业务异常。</p>
|
||||
*
|
||||
* @param code 角色代码
|
||||
* @return 角色对象
|
||||
* @throws BusinessException 如果角色不存在
|
||||
*/
|
||||
public Role findByCode(String code) {
|
||||
return roleRepository.findByCode(code)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ROLE_002));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据项目ID查询角色列表
|
||||
*
|
||||
* <p>获取指定项目下的所有角色。</p>
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 该项目下的角色列表
|
||||
*/
|
||||
public List<Role> findByProjectId(String projectId) {
|
||||
return roleRepository.findByProjectId(projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新角色
|
||||
*
|
||||
* <p>如果角色代码已存在,抛出ROLE_001业务异常。
|
||||
* 创建时会自动设置创建时间和更新时间。</p>
|
||||
*
|
||||
* @param role 要创建的角色对象
|
||||
* @return 创建后的角色对象(包含数据库生成的主键)
|
||||
* @throws BusinessException 如果角色代码已存在
|
||||
*/
|
||||
@Transactional
|
||||
public Role create(Role role) {
|
||||
if (roleRepository.existsByCode(role.getCode())) {
|
||||
|
|
@ -49,6 +105,17 @@ public class RoleService {
|
|||
return roleRepository.save(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色信息
|
||||
*
|
||||
* <p>仅更新传入参数中非空的字段,保持其他字段不变。
|
||||
* 如果角色不存在,抛出ROLE_002业务异常。</p>
|
||||
*
|
||||
* @param id 要更新的角色ID
|
||||
* @param role 包含新数据的角色对象
|
||||
* @return 更新后的角色对象
|
||||
* @throws BusinessException 如果角色不存在
|
||||
*/
|
||||
@Transactional
|
||||
public Role update(UUID id, Role role) {
|
||||
Role existing = findById(id);
|
||||
|
|
@ -75,6 +142,16 @@ public class RoleService {
|
|||
return roleRepository.save(existing);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为角色分配权限
|
||||
*
|
||||
* <p>替换角色原有的所有权限为新分配的权限列表。
|
||||
* 如果角色或权限不存在,抛出相应业务异常。</p>
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @param permissionIds 要分配的权限ID列表
|
||||
* @throws BusinessException 如果角色或权限不存在
|
||||
*/
|
||||
@Transactional
|
||||
public void assignPermissions(UUID roleId, List<UUID> permissionIds) {
|
||||
Role role = findById(roleId);
|
||||
|
|
@ -83,18 +160,60 @@ public class RoleService {
|
|||
roleRepository.save(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
*
|
||||
* <p>根据角色ID删除角色记录。</p>
|
||||
*
|
||||
* @param id 要删除的角色ID
|
||||
*/
|
||||
@Transactional
|
||||
public void delete(UUID id) {
|
||||
roleRepository.deleteById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色的所有权限
|
||||
*
|
||||
* <p>使用EntityGraph eagerly加载权限信息,避免N+1查询。
|
||||
* 如果角色不存在,抛出ROLE_002业务异常。</p>
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @return 该角色关联的所有权限列表
|
||||
* @throws BusinessException 如果角色不存在
|
||||
*/
|
||||
public List<Permission> getPermissions(UUID roleId) {
|
||||
Role role = roleRepository.findWithPermissionsById(roleId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ROLE_002));
|
||||
return role.getPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色ID查询关联的用户列表
|
||||
*
|
||||
* <p>获取拥有该角色的所有用户。</p>
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @return 拥有该角色的用户列表
|
||||
*/
|
||||
public List<User> getUsersByRoleId(UUID roleId) {
|
||||
return userRepository.findByRoleId(roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为用户分配角色
|
||||
*
|
||||
* <p>根据角色编码查找角色并分配给用户。</p>
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param roleCode 角色编码
|
||||
*/
|
||||
@Transactional
|
||||
public void assignRoleToUser(UUID userId, String roleCode) {
|
||||
Role role = findByCode(roleCode);
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.USER_003));
|
||||
user.getRoles().add(role);
|
||||
userRepository.save(user);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,12 +11,28 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 系统配置服务层
|
||||
* 提供系统配置的查询、更新等业务逻辑处理
|
||||
*
|
||||
* @author Ether Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SysConfigService {
|
||||
|
||||
/**
|
||||
* 系统配置数据访问对象
|
||||
*/
|
||||
private final SysConfigRepository sysConfigRepository;
|
||||
|
||||
/**
|
||||
* 获取所有配置项
|
||||
* 将配置项列表转换为键值对 Map 返回
|
||||
*
|
||||
* @return 配置键值对 Map,key 为 configKey,value 为 configValue
|
||||
*/
|
||||
public Map<String, String> getAllConfigs() {
|
||||
List<SysConfig> configs = sysConfigRepository.findAll();
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
|
@ -24,10 +40,25 @@ public class SysConfigService {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置键获取单个配置项
|
||||
*
|
||||
* @param configKey 配置键,如:property_company_name
|
||||
* @return 配置项实体,不存在时返回 null
|
||||
*/
|
||||
public SysConfig getConfig(String configKey) {
|
||||
return sysConfigRepository.findByConfigKey(configKey).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新单个配置项
|
||||
* 根据配置键查找并更新对应的配置值
|
||||
*
|
||||
* @param configKey 配置键
|
||||
* @param configValue 新的配置值
|
||||
* @return 更新后的配置项实体
|
||||
* @throws RuntimeException 当配置项不存在时抛出
|
||||
*/
|
||||
@Transactional
|
||||
public SysConfig updateConfig(String configKey, String configValue) {
|
||||
SysConfig config = sysConfigRepository.findByConfigKey(configKey)
|
||||
|
|
@ -36,6 +67,14 @@ public class SysConfigService {
|
|||
return sysConfigRepository.save(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新配置项
|
||||
* 同时更新多个配置键值对
|
||||
*
|
||||
* @param configs 配置键值对 Map
|
||||
* @return 更新后的配置键值对 Map
|
||||
* @throws RuntimeException 当某个配置项不存在时抛出
|
||||
*/
|
||||
@Transactional
|
||||
public Map<String, String> updateConfigs(Map<String, String> configs) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,273 @@
|
|||
package com.ether.pms.auth.service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.ether.pms.auth.controller.dto.CreateEnterpriseUserDTO;
|
||||
import com.ether.pms.auth.entity.EnterpriseUser;
|
||||
import com.ether.pms.auth.entity.ProjectStaff;
|
||||
import com.ether.pms.auth.entity.ProjectStaffRole;
|
||||
import com.ether.pms.auth.entity.Role;
|
||||
import com.ether.pms.auth.entity.User;
|
||||
import com.ether.pms.auth.repository.DeptRepository;
|
||||
import com.ether.pms.auth.repository.EnterpriseUserRepository;
|
||||
import com.ether.pms.auth.repository.ProjectStaffRepository;
|
||||
import com.ether.pms.auth.repository.ProjectStaffRoleRepository;
|
||||
import com.ether.pms.auth.repository.ResidentRepository;
|
||||
import com.ether.pms.auth.repository.UserRepository;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户管理服务层
|
||||
*
|
||||
* <p>提供用户管理的核心业务逻辑,包括企业员工管理、项目员工分配等功能。</p>
|
||||
*
|
||||
* <p>作为用户管理的核心服务,供其他模块调用。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserManagementService {
|
||||
|
||||
/** 用户数据访问接口 */
|
||||
private final UserRepository userRepository;
|
||||
|
||||
/** 企业用户数据访问接口 */
|
||||
private final EnterpriseUserRepository enterpriseUserRepository;
|
||||
|
||||
/** 项目员工数据访问接口 */
|
||||
private final ProjectStaffRepository projectStaffRepository;
|
||||
|
||||
/** 项目员工角色数据访问接口 */
|
||||
private final ProjectStaffRoleRepository projectStaffRoleRepository;
|
||||
|
||||
/** 角色服务 */
|
||||
private final RoleService roleService;
|
||||
|
||||
/** 部门数据访问接口 */
|
||||
private final DeptRepository deptRepository;
|
||||
|
||||
/** 住户数据访问接口 */
|
||||
private final ResidentRepository residentRepository;
|
||||
|
||||
/** 密码加密器 */
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* 查询所有企业员工
|
||||
*
|
||||
* <p>返回用户类型为ENTERPRISE的所有用户列表。</p>
|
||||
*
|
||||
* @return 所有企业用户的列表
|
||||
*/
|
||||
public List<User> findEnterpriseUsers() {
|
||||
return userRepository.findByUserType("ENTERPRISE");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目员工
|
||||
*
|
||||
* <p>根据项目ID查询该项目下的所有员工。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 该项目下的所有员工列表
|
||||
*/
|
||||
public List<User> findProjectStaffs(UUID projectId) {
|
||||
return userRepository.findProjectStaffsByProjectId(projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建企业员工
|
||||
*
|
||||
* <p>创建基础用户信息和企业员工扩展信息,包括工号、部门、职位等。
|
||||
* 自动分配该部门的默认角色到用户。</p>
|
||||
*
|
||||
* @param dto 创建企业用户的请求数据
|
||||
* @return 创建成功的企业用户对象
|
||||
*/
|
||||
@Transactional
|
||||
public User createEnterpriseUser(CreateEnterpriseUserDTO dto) {
|
||||
// 1. 创建基础用户
|
||||
User user = new User();
|
||||
user.setUsername(dto.getUsername());
|
||||
user.setPassword(passwordEncoder.encode(dto.getPassword()));
|
||||
user.setRealName(dto.getRealName());
|
||||
user.setPhone(dto.getPhone());
|
||||
user.setEmail(dto.getEmail());
|
||||
user.setUserType("ENTERPRISE");
|
||||
user.setDeptId(dto.getDeptId());
|
||||
user.setStatus(User.UserStatus.ACTIVE);
|
||||
user = userRepository.save(user);
|
||||
|
||||
// 2. 创建企业员工扩展信息
|
||||
EnterpriseUser eu = new EnterpriseUser();
|
||||
eu.setUser(user);
|
||||
eu.setEmployeeNo(dto.getEmployeeNo());
|
||||
eu.setDeptId(dto.getDeptId());
|
||||
eu.setPosition(dto.getPosition());
|
||||
eu.setEntryDate(dto.getEntryDate());
|
||||
eu.setUserCategory(dto.getUserCategory());
|
||||
enterpriseUserRepository.save(eu);
|
||||
|
||||
// 3. 自动分配部门的默认角色
|
||||
if (dto.getDeptId() != null) {
|
||||
String defaultRoleCode = deptRepository.findDefaultRoleCodeById(dto.getDeptId());
|
||||
if (defaultRoleCode != null && !defaultRoleCode.isEmpty()) {
|
||||
Role defaultRole = roleService.findByCode(defaultRoleCode);
|
||||
if (defaultRole != null) {
|
||||
assignRoleToUser(user.getId(), defaultRole.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为用户分配角色
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param roleId 角色ID
|
||||
*/
|
||||
@Transactional
|
||||
public void assignRoleToUser(UUID userId, UUID roleId) {
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("User not found: " + userId));
|
||||
Role role = roleService.findById(roleId);
|
||||
if (role == null) {
|
||||
throw new IllegalArgumentException("Role not found: " + roleId);
|
||||
}
|
||||
|
||||
if (user.getRoles() == null) {
|
||||
throw new IllegalStateException("User roles collection not initialized");
|
||||
}
|
||||
|
||||
boolean hasRole = user.getRoles().stream()
|
||||
.anyMatch(r -> r.getId().equals(roleId));
|
||||
if (!hasRole) {
|
||||
user.getRoles().add(role);
|
||||
userRepository.save(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配员工到项目
|
||||
*
|
||||
* <p>将用户分配到指定项目,设定员工类型和分配状态,支持多角色分配。</p>
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
* @param staffType 员工类型
|
||||
* @param roleIds 角色ID列表
|
||||
* @return 创建的项目员工关联记录
|
||||
*/
|
||||
@Transactional
|
||||
public ProjectStaff assignStaffToProject(UUID userId, UUID projectId, String staffType, List<UUID> roleIds) {
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("User not found: " + userId));
|
||||
|
||||
// 检查用户是否已经是该项目成员
|
||||
ProjectStaff staff = projectStaffRepository.findByUserIdAndProjectId(userId, projectId)
|
||||
.orElse(null);
|
||||
|
||||
if (staff == null) {
|
||||
// 不存在,创建新的项目员工记录
|
||||
staff = new ProjectStaff();
|
||||
staff.setUser(user);
|
||||
staff.setProjectId(projectId);
|
||||
staff.setStaffType(staffType != null ? staffType : "GENERAL");
|
||||
staff.setAssignmentStatus("ASSIGNED");
|
||||
staff.setCreatedAt(LocalDateTime.now());
|
||||
staff.setUpdatedAt(LocalDateTime.now());
|
||||
staff = projectStaffRepository.save(staff);
|
||||
} else {
|
||||
// 用户已在项目中,更新员工类型(只有当staffType变化时才更新)
|
||||
if (staffType != null && !staffType.equals(staff.getStaffType())) {
|
||||
staff.setStaffType(staffType);
|
||||
staff.setUpdatedAt(LocalDateTime.now());
|
||||
staff = projectStaffRepository.save(staff);
|
||||
}
|
||||
}
|
||||
|
||||
// 角色关联:先删除旧角色,再添加新角色(覆盖模式)
|
||||
if (roleIds != null) {
|
||||
// 删除该员工在该项目下的所有旧角色关联
|
||||
List<ProjectStaffRole> existingRoles = staff.getStaffRoles();
|
||||
if (existingRoles != null && !existingRoles.isEmpty()) {
|
||||
projectStaffRoleRepository.deleteAll(existingRoles);
|
||||
staff.getStaffRoles().clear();
|
||||
}
|
||||
// 添加新角色
|
||||
if (!roleIds.isEmpty()) {
|
||||
for (UUID roleId : roleIds) {
|
||||
Role role = roleService.findById(roleId);
|
||||
if (role != null) {
|
||||
ProjectStaffRole staffRole = new ProjectStaffRole();
|
||||
staffRole.setStaff(staff);
|
||||
staffRole.setRole(role);
|
||||
projectStaffRoleRepository.save(staffRole);
|
||||
staff.getStaffRoles().add(staffRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return staff;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目成员
|
||||
*
|
||||
* <p>根据项目ID查询该项目下的所有成员用户。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 该项目下的所有成员用户列表
|
||||
*/
|
||||
public List<User> getProjectMembers(UUID projectId) {
|
||||
return userRepository.findProjectStaffsByProjectId(projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目员工列表(包含角色信息)
|
||||
*
|
||||
* <p>根据项目ID查询该项目下的所有员工信息,包含角色信息。</p>
|
||||
*
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 该项目下的所有员工列表
|
||||
*/
|
||||
public List<ProjectStaff> getProjectStaffsWithRoles(UUID projectId) {
|
||||
List<ProjectStaff> staffs = projectStaffRepository.findByProjectIdWithRoles(projectId);
|
||||
for (ProjectStaff staff : staffs) {
|
||||
Hibernate.initialize(staff.getUser().getRoles());
|
||||
}
|
||||
return staffs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从项目移除员工
|
||||
*
|
||||
* <p>将用户从指定项目中移除,删除项目员工关联关系。</p>
|
||||
*
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
*/
|
||||
@Transactional
|
||||
public void removeStaffFromProject(UUID userId, UUID projectId) {
|
||||
Optional<ProjectStaff> staff = projectStaffRepository.findByUserIdAndProjectId(userId, projectId);
|
||||
staff.ifPresent(s -> {
|
||||
// 级联删除角色关联(通过 orphanRemoval)
|
||||
projectStaffRepository.delete(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -8,20 +8,52 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 用户项目关联服务层
|
||||
*
|
||||
* <p>提供用户与项目关联关系的业务逻辑处理,包括关联查询、添加、移除等功能。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserProjectService {
|
||||
|
||||
/** 用户项目关联数据访问接口 */
|
||||
private final UserProjectRepository userProjectRepository;
|
||||
|
||||
/**
|
||||
* 获取用户参与的所有项目
|
||||
*
|
||||
* <p>根据用户ID查询该用户参与的所有项目关联记录。</p *
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 用户参与的所有项目关联列表
|
||||
*/
|
||||
public List<UserProject> getUserProjects(UUID userId) {
|
||||
return userProjectRepository.findByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户参与的所有项目ID
|
||||
*
|
||||
* <p>根据用户ID查询该用户参与的所有项目ID列表,用于权限验证等场景。</p *
|
||||
* @param userId 用户唯一标识符
|
||||
* @return 用户参与的所有项目ID列表
|
||||
*/
|
||||
public List<UUID> getUserProjectIds(UUID userId) {
|
||||
return userProjectRepository.findProjectIdsByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户到项目
|
||||
*
|
||||
* <p>创建用户与项目之间的关联关系。如果已存在关联则不重复创建。</p *
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
* @param role 用户在项目中的角色,默认为"member"
|
||||
*/
|
||||
@Transactional
|
||||
public void addUserToProject(UUID userId, UUID projectId, String role) {
|
||||
if (userProjectRepository.existsByUserIdAndProjectId(userId, projectId)) {
|
||||
|
|
@ -34,11 +66,26 @@ public class UserProjectService {
|
|||
userProjectRepository.save(up);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从项目中移除用户
|
||||
*
|
||||
* <p>删除用户与项目之间的关联关系。</p *
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
*/
|
||||
@Transactional
|
||||
public void removeUserFromProject(UUID userId, UUID projectId) {
|
||||
userProjectRepository.deleteByUserIdAndProjectId(userId, projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否在项目中
|
||||
*
|
||||
* <p>判断用户与项目之间是否存在关联关系。</p *
|
||||
* @param userId 用户唯一标识符
|
||||
* @param projectId 项目唯一标识符
|
||||
* @return 存在关联返回true,否则返回false
|
||||
*/
|
||||
public boolean isUserInProject(UUID userId, UUID projectId) {
|
||||
return userProjectRepository.existsByUserIdAndProjectId(userId, projectId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,28 +14,76 @@ import java.time.LocalDateTime;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 用户服务层
|
||||
*
|
||||
* <p>提供用户相关的业务逻辑处理,包括用户CRUD、密码管理、角色分配等功能。</p>
|
||||
*
|
||||
* <p>所有涉及数据修改的操作均添加事务注解保证数据一致性。</p>
|
||||
*
|
||||
* @author Ether开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserService {
|
||||
|
||||
/** 用户数据访问接口 */
|
||||
private final UserRepository userRepository;
|
||||
/** 角色数据访问接口 */
|
||||
private final RoleRepository roleRepository;
|
||||
/** 密码服务接口 */
|
||||
private final PasswordService passwordService;
|
||||
|
||||
/**
|
||||
* 查询所有用户
|
||||
*
|
||||
* <p>返回所有用户及其关联的角色信息。</p>
|
||||
*
|
||||
* @return 所有用户的列表
|
||||
*/
|
||||
public List<User> findAll() {
|
||||
return userRepository.findAllWithRoles();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询用户
|
||||
*
|
||||
* <p>根据用户唯一标识符查询用户信息。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @return 用户对象
|
||||
* @throws BusinessException 如果用户不存在,抛出业务异常
|
||||
*/
|
||||
public User findById(UUID id) {
|
||||
return userRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.USER_003));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户
|
||||
*
|
||||
* <p>根据用户名精确查询用户信息,用于登录验证等场景。</p>
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 用户对象
|
||||
* @throws BusinessException 如果用户不存在,抛出业务异常
|
||||
*/
|
||||
public User findByUsername(String username) {
|
||||
return userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.USER_003));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新用户
|
||||
*
|
||||
* <p>创建新用户并对密码进行加密存储,同时验证用户名和手机号的唯一性。</p>
|
||||
*
|
||||
* @param user 待创建的用户对象
|
||||
* @return 创建成功的用户对象(包含数据库生成的ID)
|
||||
* @throws BusinessException 如果用户名已存在、手机号已存在或密码不符合要求
|
||||
*/
|
||||
@Transactional
|
||||
public User create(User user) {
|
||||
if (userRepository.existsByUsername(user.getUsername())) {
|
||||
|
|
@ -60,6 +108,16 @@ public class UserService {
|
|||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* <p>根据用户ID更新用户信息,仅更新非空字段。</p>
|
||||
*
|
||||
* @param id 用户唯一标识符
|
||||
* @param user 包含更新信息的用户对象
|
||||
* @return 更新后的用户对象
|
||||
* @throws BusinessException 如果用户不存在或手机号已被其他用户使用
|
||||
*/
|
||||
@Transactional
|
||||
public User update(UUID id, User user) {
|
||||
User existing = findById(id);
|
||||
|
|
@ -86,6 +144,15 @@ public class UserService {
|
|||
return userRepository.save(existing);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户密码
|
||||
*
|
||||
* <p>用户主动修改密码,需验证原密码正确性,新密码需符合强度要求。</p *
|
||||
* @param id 用户唯一标识符
|
||||
* @param oldPassword 原密码
|
||||
* @param newPassword 新密码
|
||||
* @throws BusinessException 如果原密码错误或新密码不符合要求
|
||||
*/
|
||||
@Transactional
|
||||
public void updatePassword(UUID id, String oldPassword, String newPassword) {
|
||||
User user = findById(id);
|
||||
|
|
@ -108,6 +175,14 @@ public class UserService {
|
|||
userRepository.save(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
*
|
||||
* <p>管理员重置用户密码,新密码需符合强度要求。</p *
|
||||
* @param id 用户唯一标识符
|
||||
* @param newPassword 新密码
|
||||
* @throws BusinessException 如果新密码不符合要求
|
||||
*/
|
||||
@Transactional
|
||||
public void resetPassword(UUID id, String newPassword) {
|
||||
User user = findById(id);
|
||||
|
|
@ -126,6 +201,14 @@ public class UserService {
|
|||
userRepository.save(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配用户角色
|
||||
*
|
||||
* <p>为用户分配一个或多个角色,替换用户现有的所有角色。</p *
|
||||
* @param userId 用户唯一标识符
|
||||
* @param roleIds 要分配的角色ID列表
|
||||
* @throws BusinessException 如果用户不存在或任何角色ID无效
|
||||
*/
|
||||
@Transactional
|
||||
public void assignRoles(UUID userId, List<UUID> roleIds) {
|
||||
User user = findById(userId);
|
||||
|
|
@ -134,11 +217,24 @@ public class UserService {
|
|||
userRepository.save(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*
|
||||
* <p>根据用户ID删除用户记录。</p *
|
||||
* @param id 用户唯一标识符
|
||||
*/
|
||||
@Transactional
|
||||
public void delete(UUID id) {
|
||||
userRepository.deleteById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户登录信息
|
||||
*
|
||||
* <p>记录用户登录的时间戳和IP地址,用于安全审计。</p *
|
||||
* @param userId 用户唯一标识符
|
||||
* @param ip 登录IP地址
|
||||
*/
|
||||
public void updateLoginInfo(UUID userId, String ip) {
|
||||
User user = findById(userId);
|
||||
user.setLastLoginTime(LocalDateTime.now());
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import java.util.UUID;
|
|||
@Component
|
||||
public class JwtTokenProvider {
|
||||
|
||||
@Value("${jwt.secret:ether-pms-secret-key-must-be-at-least-256-bits}")
|
||||
@Value("${jwt.secret:#{systemEnvironment['JWT_SECRET'] ?: 'ether-pms-secret-key-must-be-at-least-256-bits'}}")
|
||||
private String secret;
|
||||
|
||||
@Value("${jwt.expiration:86400000}")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,248 @@
|
|||
package com.ether.pms.auth.controller.dto;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.ether.pms.auth.entity.ProjectStaff;
|
||||
import com.ether.pms.auth.entity.ProjectStaffRole;
|
||||
import com.ether.pms.auth.entity.Role;
|
||||
import com.ether.pms.auth.entity.User;
|
||||
|
||||
class ProjectMemberVOTest {
|
||||
|
||||
@Test
|
||||
void fromEntity_shouldMapAllFieldsCorrectly() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
UUID roleId = UUID.randomUUID();
|
||||
LocalDateTime createdAt = LocalDateTime.of(2026, 4, 5, 10, 30);
|
||||
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
user.setUsername("testuser");
|
||||
user.setRealName("测试用户");
|
||||
user.setPhone("13800138000");
|
||||
user.setEmail("test@example.com");
|
||||
user.setUserType("ENTERPRISE");
|
||||
user.setStatus(User.UserStatus.ACTIVE);
|
||||
|
||||
Role role = new Role();
|
||||
role.setId(roleId);
|
||||
role.setCode("PROJECT_ADMIN");
|
||||
role.setName("项目管理员");
|
||||
|
||||
ProjectStaffRole staffRole = new ProjectStaffRole();
|
||||
staffRole.setRole(role);
|
||||
|
||||
ProjectStaff staff = new ProjectStaff();
|
||||
staff.setId(UUID.randomUUID());
|
||||
staff.setUser(user);
|
||||
staff.setProjectId(projectId);
|
||||
staff.setStaffType("SECURITY");
|
||||
staff.setCreatedAt(createdAt);
|
||||
staff.setStaffRoles(new ArrayList<>(List.of(staffRole)));
|
||||
|
||||
// When
|
||||
ProjectMemberVO vo = ProjectMemberVO.fromEntity(staff);
|
||||
|
||||
// Then
|
||||
assertNotNull(vo);
|
||||
assertEquals(userId, vo.getId());
|
||||
assertEquals("testuser", vo.getUsername());
|
||||
assertEquals("测试用户", vo.getRealName());
|
||||
assertEquals("13800138000", vo.getPhone());
|
||||
assertEquals("test@example.com", vo.getEmail());
|
||||
assertEquals("ENTERPRISE", vo.getUserType());
|
||||
assertEquals("ACTIVE", vo.getStatus());
|
||||
assertEquals("SECURITY", vo.getStaffType());
|
||||
assertEquals(createdAt, vo.getCreatedAt());
|
||||
assertEquals(List.of("PROJECT_ADMIN"), vo.getRoles());
|
||||
assertEquals("项目管理员", vo.getRoleNames());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromEntity_shouldHandleMultipleRoles() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
user.setUsername("testuser");
|
||||
user.setRealName("测试用户");
|
||||
user.setStatus(User.UserStatus.ACTIVE);
|
||||
|
||||
Role role1 = new Role();
|
||||
role1.setId(UUID.randomUUID());
|
||||
role1.setCode("PROJECT_ADMIN");
|
||||
role1.setName("项目管理员");
|
||||
|
||||
Role role2 = new Role();
|
||||
role2.setId(UUID.randomUUID());
|
||||
role2.setCode("SECURITY_LEAD");
|
||||
role2.setName("保安队长");
|
||||
|
||||
ProjectStaffRole staffRole1 = new ProjectStaffRole();
|
||||
staffRole1.setRole(role1);
|
||||
|
||||
ProjectStaffRole staffRole2 = new ProjectStaffRole();
|
||||
staffRole2.setRole(role2);
|
||||
|
||||
ProjectStaff staff = new ProjectStaff();
|
||||
staff.setUser(user);
|
||||
staff.setStaffRoles(new ArrayList<>(List.of(staffRole1, staffRole2)));
|
||||
|
||||
// When
|
||||
ProjectMemberVO vo = ProjectMemberVO.fromEntity(staff);
|
||||
|
||||
// Then
|
||||
assertNotNull(vo);
|
||||
assertEquals(2, vo.getRoles().size());
|
||||
assertTrue(vo.getRoles().contains("PROJECT_ADMIN"));
|
||||
assertTrue(vo.getRoles().contains("SECURITY_LEAD"));
|
||||
assertEquals("项目管理员、保安队长", vo.getRoleNames());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromEntity_shouldHandleEmptyRoles() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
user.setUsername("testuser");
|
||||
user.setRealName("测试用户");
|
||||
user.setStatus(User.UserStatus.ACTIVE);
|
||||
|
||||
ProjectStaff staff = new ProjectStaff();
|
||||
staff.setUser(user);
|
||||
staff.setStaffRoles(new ArrayList<>());
|
||||
|
||||
// When
|
||||
ProjectMemberVO vo = ProjectMemberVO.fromEntity(staff);
|
||||
|
||||
// Then
|
||||
assertNotNull(vo);
|
||||
assertTrue(vo.getRoles().isEmpty());
|
||||
assertEquals("", vo.getRoleNames());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromEntity_shouldHandleNullRoles() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
user.setUsername("testuser");
|
||||
user.setRealName("测试用户");
|
||||
user.setStatus(User.UserStatus.ACTIVE);
|
||||
|
||||
ProjectStaff staff = new ProjectStaff();
|
||||
staff.setUser(user);
|
||||
staff.setStaffRoles(null);
|
||||
|
||||
// When
|
||||
ProjectMemberVO vo = ProjectMemberVO.fromEntity(staff);
|
||||
|
||||
// Then
|
||||
assertNotNull(vo);
|
||||
assertTrue(vo.getRoles().isEmpty());
|
||||
assertEquals("", vo.getRoleNames());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromEntity_shouldUseGlobalRoles_whenProjectRolesEmpty() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
user.setUsername("testuser");
|
||||
user.setRealName("测试用户");
|
||||
user.setStatus(User.UserStatus.ACTIVE);
|
||||
|
||||
Role globalRole = new Role();
|
||||
globalRole.setId(UUID.randomUUID());
|
||||
globalRole.setCode("SYS_ADMIN");
|
||||
globalRole.setName("系统管理员");
|
||||
user.setRoles(new ArrayList<>(List.of(globalRole)));
|
||||
|
||||
ProjectStaff staff = new ProjectStaff();
|
||||
staff.setUser(user);
|
||||
staff.setStaffRoles(new ArrayList<>()); // 空的项目角色
|
||||
|
||||
// When
|
||||
ProjectMemberVO vo = ProjectMemberVO.fromEntity(staff);
|
||||
|
||||
// Then
|
||||
assertNotNull(vo);
|
||||
assertEquals(List.of("SYS_ADMIN"), vo.getRoles());
|
||||
assertEquals("系统管理员", vo.getRoleNames());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromEntity_shouldHandleNullUserFields() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
user.setUsername("testuser");
|
||||
// 其他字段为null
|
||||
|
||||
ProjectStaff staff = new ProjectStaff();
|
||||
staff.setUser(user);
|
||||
staff.setStaffRoles(new ArrayList<>());
|
||||
|
||||
// When
|
||||
ProjectMemberVO vo = ProjectMemberVO.fromEntity(staff);
|
||||
|
||||
// Then
|
||||
assertNotNull(vo);
|
||||
assertEquals(userId, vo.getId());
|
||||
assertEquals("testuser", vo.getUsername());
|
||||
assertNull(vo.getRealName());
|
||||
assertNull(vo.getPhone());
|
||||
assertNull(vo.getEmail());
|
||||
assertNull(vo.getUserType());
|
||||
// status 字段在 User 实体中有默认值 ACTIVE
|
||||
assertEquals("ACTIVE", vo.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromEntity_shouldHandleNullRoleInStaffRole() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
user.setUsername("testuser");
|
||||
user.setRealName("测试用户");
|
||||
user.setStatus(User.UserStatus.ACTIVE);
|
||||
|
||||
ProjectStaffRole staffRoleWithNullRole = new ProjectStaffRole();
|
||||
staffRoleWithNullRole.setRole(null);
|
||||
|
||||
ProjectStaff staff = new ProjectStaff();
|
||||
staff.setUser(user);
|
||||
staff.setStaffRoles(new ArrayList<>(List.of(staffRoleWithNullRole)));
|
||||
|
||||
// When
|
||||
ProjectMemberVO vo = ProjectMemberVO.fromEntity(staff);
|
||||
|
||||
// Then
|
||||
assertNotNull(vo);
|
||||
assertTrue(vo.getRoles().isEmpty());
|
||||
assertEquals("", vo.getRoleNames());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,427 @@
|
|||
package com.ether.pms.auth.service;
|
||||
|
||||
import com.ether.pms.auth.entity.ProjectStaff;
|
||||
import com.ether.pms.auth.entity.ProjectStaffRole;
|
||||
import com.ether.pms.auth.entity.Role;
|
||||
import com.ether.pms.auth.entity.User;
|
||||
import com.ether.pms.auth.repository.ProjectStaffRepository;
|
||||
import com.ether.pms.auth.repository.ProjectStaffRoleRepository;
|
||||
import com.ether.pms.auth.repository.UserRepository;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserManagementServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private ProjectStaffRepository projectStaffRepository;
|
||||
|
||||
@Mock
|
||||
private ProjectStaffRoleRepository projectStaffRoleRepository;
|
||||
|
||||
@Mock
|
||||
private RoleService roleService;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@InjectMocks
|
||||
private UserManagementService userManagementService;
|
||||
|
||||
@Test
|
||||
void assignStaffToProject_shouldCreateNewStaff_whenUserNotInProject() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
UUID roleId = UUID.randomUUID();
|
||||
String staffType = "GENERAL";
|
||||
List<UUID> roleIds = List.of(roleId);
|
||||
|
||||
User mockUser = new User();
|
||||
mockUser.setId(userId);
|
||||
mockUser.setUsername("testuser");
|
||||
|
||||
Role mockRole = new Role();
|
||||
mockRole.setId(roleId);
|
||||
mockRole.setCode("PROJECT_ADMIN");
|
||||
mockRole.setName("项目管理员");
|
||||
|
||||
ProjectStaff savedStaff = new ProjectStaff();
|
||||
savedStaff.setId(UUID.randomUUID());
|
||||
savedStaff.setUser(mockUser);
|
||||
savedStaff.setProjectId(projectId);
|
||||
savedStaff.setStaffType(staffType);
|
||||
savedStaff.setStaffRoles(new ArrayList<>());
|
||||
|
||||
when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.empty());
|
||||
when(projectStaffRepository.save(any(ProjectStaff.class))).thenReturn(savedStaff);
|
||||
when(roleService.findById(roleId)).thenReturn(mockRole);
|
||||
when(projectStaffRoleRepository.save(any(ProjectStaffRole.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// When
|
||||
ProjectStaff result = userManagementService.assignStaffToProject(userId, projectId, staffType, roleIds);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(mockUser, result.getUser());
|
||||
assertEquals(projectId, result.getProjectId());
|
||||
assertEquals(staffType, result.getStaffType());
|
||||
verify(projectStaffRepository).save(any(ProjectStaff.class));
|
||||
verify(projectStaffRoleRepository).save(any(ProjectStaffRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignStaffToProject_shouldReuseExistingStaffAndUpdateStaffType_whenUserAlreadyInProject() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
UUID roleId = UUID.randomUUID();
|
||||
String staffType = "SECURITY";
|
||||
List<UUID> roleIds = List.of(roleId);
|
||||
|
||||
User mockUser = new User();
|
||||
mockUser.setId(userId);
|
||||
mockUser.setUsername("testuser");
|
||||
|
||||
Role mockRole = new Role();
|
||||
mockRole.setId(roleId);
|
||||
mockRole.setCode("PROJECT_ADMIN");
|
||||
mockRole.setName("项目管理员");
|
||||
|
||||
ProjectStaff existingStaff = new ProjectStaff();
|
||||
existingStaff.setId(UUID.randomUUID());
|
||||
existingStaff.setUser(mockUser);
|
||||
existingStaff.setProjectId(projectId);
|
||||
existingStaff.setStaffType("GENERAL");
|
||||
existingStaff.setStaffRoles(new ArrayList<>());
|
||||
|
||||
when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.of(existingStaff));
|
||||
when(projectStaffRepository.save(any(ProjectStaff.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(roleService.findById(roleId)).thenReturn(mockRole);
|
||||
when(projectStaffRoleRepository.save(any(ProjectStaffRole.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// When
|
||||
ProjectStaff result = userManagementService.assignStaffToProject(userId, projectId, staffType, roleIds);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(existingStaff.getId(), result.getId());
|
||||
assertEquals("SECURITY", result.getStaffType());
|
||||
verify(projectStaffRepository, times(1)).save(any(ProjectStaff.class));
|
||||
verify(projectStaffRoleRepository).save(any(ProjectStaffRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignStaffToProject_shouldClearRolesAndAddNewRoles_whenRoleIdsProvided() {
|
||||
// Given: 用户已有一个角色
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
UUID newRoleId = UUID.randomUUID();
|
||||
String staffType = "GENERAL";
|
||||
List<UUID> newRoleIds = List.of(newRoleId);
|
||||
|
||||
User mockUser = new User();
|
||||
mockUser.setId(userId);
|
||||
|
||||
Role newRole = new Role();
|
||||
newRole.setId(newRoleId);
|
||||
newRole.setCode("NEW_ROLE");
|
||||
|
||||
ProjectStaff existingStaff = new ProjectStaff();
|
||||
existingStaff.setId(UUID.randomUUID());
|
||||
existingStaff.setUser(mockUser);
|
||||
existingStaff.setProjectId(projectId);
|
||||
existingStaff.setStaffType("GENERAL"); // staffType相同,不会触发staff.save
|
||||
existingStaff.setStaffRoles(new ArrayList<>()); // 空的,不需要删除
|
||||
|
||||
when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.of(existingStaff));
|
||||
when(roleService.findById(newRoleId)).thenReturn(newRole);
|
||||
when(projectStaffRoleRepository.save(any(ProjectStaffRole.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// When
|
||||
ProjectStaff result = userManagementService.assignStaffToProject(userId, projectId, staffType, newRoleIds);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
verify(projectStaffRoleRepository, never()).deleteAll(anyList()); // 没有旧角色,不需要删除
|
||||
verify(projectStaffRoleRepository, times(1)).save(any(ProjectStaffRole.class));
|
||||
assertEquals(1, result.getStaffRoles().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignStaffToProject_shouldThrowException_whenUserNotFound() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
List<UUID> roleIds = List.of();
|
||||
|
||||
when(userRepository.findById(userId)).thenReturn(Optional.empty());
|
||||
|
||||
// Then
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
userManagementService.assignStaffToProject(userId, projectId, "GENERAL", roleIds)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignStaffToProject_shouldHandleMultipleRoles() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
UUID roleId1 = UUID.randomUUID();
|
||||
UUID roleId2 = UUID.randomUUID();
|
||||
String staffType = "GENERAL";
|
||||
List<UUID> roleIds = List.of(roleId1, roleId2);
|
||||
|
||||
User mockUser = new User();
|
||||
mockUser.setId(userId);
|
||||
|
||||
Role mockRole1 = new Role();
|
||||
mockRole1.setId(roleId1);
|
||||
mockRole1.setCode("PROJECT_ADMIN");
|
||||
mockRole1.setName("项目管理员");
|
||||
|
||||
Role mockRole2 = new Role();
|
||||
mockRole2.setId(roleId2);
|
||||
mockRole2.setCode("SECURITY_LEAD");
|
||||
mockRole2.setName("保安队长");
|
||||
|
||||
ProjectStaff savedStaff = new ProjectStaff();
|
||||
savedStaff.setId(UUID.randomUUID());
|
||||
savedStaff.setUser(mockUser);
|
||||
savedStaff.setProjectId(projectId);
|
||||
savedStaff.setStaffRoles(new ArrayList<>());
|
||||
|
||||
when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.empty());
|
||||
when(projectStaffRepository.save(any(ProjectStaff.class))).thenReturn(savedStaff);
|
||||
when(roleService.findById(roleId1)).thenReturn(mockRole1);
|
||||
when(roleService.findById(roleId2)).thenReturn(mockRole2);
|
||||
when(projectStaffRoleRepository.save(any(ProjectStaffRole.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// When
|
||||
ProjectStaff result = userManagementService.assignStaffToProject(userId, projectId, staffType, roleIds);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
// 应该添加两个角色
|
||||
verify(projectStaffRoleRepository, times(2)).save(any(ProjectStaffRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignStaffToProject_shouldHandleEmptyRoleIds() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
String staffType = "GENERAL";
|
||||
List<UUID> roleIds = List.of();
|
||||
|
||||
User mockUser = new User();
|
||||
mockUser.setId(userId);
|
||||
|
||||
ProjectStaff savedStaff = new ProjectStaff();
|
||||
savedStaff.setId(UUID.randomUUID());
|
||||
savedStaff.setUser(mockUser);
|
||||
savedStaff.setProjectId(projectId);
|
||||
savedStaff.setStaffRoles(new ArrayList<>());
|
||||
|
||||
when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.empty());
|
||||
when(projectStaffRepository.save(any(ProjectStaff.class))).thenReturn(savedStaff);
|
||||
|
||||
// When
|
||||
ProjectStaff result = userManagementService.assignStaffToProject(userId, projectId, staffType, roleIds);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
// 不应该添加任何角色
|
||||
verify(projectStaffRoleRepository, never()).save(any(ProjectStaffRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignStaffToProject_shouldUseDefaultStaffType_whenStaffTypeIsNull() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
List<UUID> roleIds = List.of();
|
||||
|
||||
User mockUser = new User();
|
||||
mockUser.setId(userId);
|
||||
|
||||
ProjectStaff savedStaff = new ProjectStaff();
|
||||
savedStaff.setId(UUID.randomUUID());
|
||||
savedStaff.setUser(mockUser);
|
||||
savedStaff.setProjectId(projectId);
|
||||
|
||||
when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.empty());
|
||||
when(projectStaffRepository.save(any(ProjectStaff.class))).thenAnswer(invocation -> {
|
||||
ProjectStaff staff = invocation.getArgument(0);
|
||||
staff.setId(UUID.randomUUID());
|
||||
return staff;
|
||||
});
|
||||
|
||||
// When
|
||||
ProjectStaff result = userManagementService.assignStaffToProject(userId, projectId, null, roleIds);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals("GENERAL", result.getStaffType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignStaffToProject_shouldReplaceRoles_whenCalledAgain() {
|
||||
// Given: 用户已有一个角色
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
UUID oldRoleId = UUID.randomUUID();
|
||||
UUID newRoleId = UUID.randomUUID();
|
||||
String staffType = "GENERAL";
|
||||
List<UUID> newRoleIds = List.of(newRoleId);
|
||||
|
||||
User mockUser = new User();
|
||||
mockUser.setId(userId);
|
||||
|
||||
Role oldRole = new Role();
|
||||
oldRole.setId(oldRoleId);
|
||||
oldRole.setCode("OLD_ROLE");
|
||||
|
||||
Role newRole = new Role();
|
||||
newRole.setId(newRoleId);
|
||||
newRole.setCode("NEW_ROLE");
|
||||
|
||||
ProjectStaffRole existingStaffRole = new ProjectStaffRole();
|
||||
existingStaffRole.setRole(oldRole);
|
||||
|
||||
ProjectStaff existingStaff = new ProjectStaff();
|
||||
existingStaff.setId(UUID.randomUUID());
|
||||
existingStaff.setUser(mockUser);
|
||||
existingStaff.setProjectId(projectId);
|
||||
existingStaff.setStaffType("GENERAL"); // staffType相同,不会触发staff.save
|
||||
existingStaff.setStaffRoles(new ArrayList<>(List.of(existingStaffRole)));
|
||||
|
||||
when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.of(existingStaff));
|
||||
when(roleService.findById(newRoleId)).thenReturn(newRole);
|
||||
when(projectStaffRoleRepository.save(any(ProjectStaffRole.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// When: 重新传入新的角色列表
|
||||
ProjectStaff result = userManagementService.assignStaffToProject(userId, projectId, staffType, newRoleIds);
|
||||
|
||||
// Then: 旧角色应该被删除,新角色应该被添加
|
||||
assertNotNull(result);
|
||||
verify(projectStaffRoleRepository, times(1)).deleteAll(anyList());
|
||||
verify(projectStaffRoleRepository, times(1)).save(any(ProjectStaffRole.class));
|
||||
assertEquals(1, result.getStaffRoles().size());
|
||||
assertEquals(newRole, result.getStaffRoles().get(0).getRole());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProjectStaffsWithRoles_shouldReturnStaffList_withRoles() {
|
||||
// Given
|
||||
UUID projectId = UUID.randomUUID();
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID roleId = UUID.randomUUID();
|
||||
|
||||
User mockUser = new User();
|
||||
mockUser.setId(userId);
|
||||
mockUser.setUsername("testuser");
|
||||
mockUser.setRoles(new ArrayList<>());
|
||||
|
||||
Role mockRole = new Role();
|
||||
mockRole.setId(roleId);
|
||||
mockRole.setCode("PROJECT_ADMIN");
|
||||
mockRole.setName("项目管理员");
|
||||
|
||||
ProjectStaffRole staffRole = new ProjectStaffRole();
|
||||
staffRole.setRole(mockRole);
|
||||
|
||||
ProjectStaff staff = new ProjectStaff();
|
||||
staff.setId(UUID.randomUUID());
|
||||
staff.setUser(mockUser);
|
||||
staff.setProjectId(projectId);
|
||||
staff.setStaffRoles(new ArrayList<>(List.of(staffRole)));
|
||||
|
||||
when(projectStaffRepository.findByProjectIdWithRoles(projectId)).thenReturn(List.of(staff));
|
||||
|
||||
// When
|
||||
List<ProjectStaff> result = userManagementService.getProjectStaffsWithRoles(projectId);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(mockUser, result.get(0).getUser());
|
||||
assertEquals(1, result.get(0).getStaffRoles().size());
|
||||
assertEquals(mockRole, result.get(0).getStaffRoles().get(0).getRole());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProjectStaffsWithRoles_shouldReturnEmptyList_whenNoStaffInProject() {
|
||||
// Given
|
||||
UUID projectId = UUID.randomUUID();
|
||||
|
||||
when(projectStaffRepository.findByProjectIdWithRoles(projectId)).thenReturn(List.of());
|
||||
|
||||
// When
|
||||
List<ProjectStaff> result = userManagementService.getProjectStaffsWithRoles(projectId);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeStaffFromProject_shouldDeleteStaff_whenExists() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
|
||||
ProjectStaff existingStaff = new ProjectStaff();
|
||||
existingStaff.setId(UUID.randomUUID());
|
||||
existingStaff.setProjectId(projectId);
|
||||
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.of(existingStaff));
|
||||
doNothing().when(projectStaffRepository).delete(existingStaff);
|
||||
|
||||
// When
|
||||
userManagementService.removeStaffFromProject(userId, projectId);
|
||||
|
||||
// Then
|
||||
verify(projectStaffRepository).delete(existingStaff);
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeStaffFromProject_shouldDoNothing_whenStaffNotExists() {
|
||||
// Given
|
||||
UUID userId = UUID.randomUUID();
|
||||
UUID projectId = UUID.randomUUID();
|
||||
|
||||
when(projectStaffRepository.findByUserIdAndProjectId(userId, projectId)).thenReturn(Optional.empty());
|
||||
|
||||
// When
|
||||
userManagementService.removeStaffFromProject(userId, projectId);
|
||||
|
||||
// Then
|
||||
verify(projectStaffRepository, never()).delete(any());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
package com.ether.pms.common;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
|
|
@ -13,16 +14,32 @@ public class GlobalExceptionHandler {
|
|||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleBusinessException(BusinessException e) {
|
||||
log.warn("业务异常: code={}, message={}", e.getCode(), e.getMessage());
|
||||
HttpStatus status = mapErrorCodeToHttpStatus(e.getCode());
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.OK)
|
||||
.status(status)
|
||||
.body(ApiResponse.error(e.getCode(), e.getMessage()));
|
||||
}
|
||||
|
||||
private HttpStatus mapErrorCodeToHttpStatus(int errorCode) {
|
||||
if (errorCode >= 400 && errorCode < 500) {
|
||||
return HttpStatus.valueOf(errorCode);
|
||||
}
|
||||
return HttpStatus.OK;
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleIllegalArgumentException(IllegalArgumentException e) {
|
||||
log.warn("参数异常: {}", e.getMessage());
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.OK)
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.body(ApiResponse.error(400, e.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalStateException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleIllegalStateException(IllegalStateException e) {
|
||||
log.warn("状态异常: {}", e.getMessage());
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.body(ApiResponse.error(400, e.getMessage()));
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +47,7 @@ public class GlobalExceptionHandler {
|
|||
public ResponseEntity<ApiResponse<Void>> handleException(Exception e) {
|
||||
log.error("系统异常", e);
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.OK)
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error(ErrorCode.SYSTEM_ERROR.getCode(), "系统错误,请稍后重试"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,12 @@
|
|||
<artifactId>mapstruct</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import java.util.Map;
|
|||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/ops")
|
||||
@RequestMapping("/api/ops/energy")
|
||||
@RequiredArgsConstructor
|
||||
public class EnergyController {
|
||||
|
||||
|
|
@ -27,12 +27,12 @@ public class EnergyController {
|
|||
|
||||
// ==================== 计量点管理 ====================
|
||||
|
||||
@PostMapping("/energy-meters")
|
||||
@PostMapping("/meters")
|
||||
public ApiResponse<EnergyMeter> createMeter(@Valid @RequestBody EnergyMeter meter) {
|
||||
return ApiResponse.success(energyMeterService.createMeter(meter));
|
||||
}
|
||||
|
||||
@GetMapping("/energy-meters")
|
||||
@GetMapping("/meters")
|
||||
public ApiResponse<List<EnergyMeter>> getMeters(
|
||||
@RequestParam UUID projectId,
|
||||
@RequestParam(required = false) EnergyMeter.EnergyType energyType) {
|
||||
|
|
@ -45,17 +45,17 @@ public class EnergyController {
|
|||
return ApiResponse.success(meters);
|
||||
}
|
||||
|
||||
@GetMapping("/energy-meters/{id}")
|
||||
@GetMapping("/meters/{id}")
|
||||
public ApiResponse<EnergyMeter> getMeter(@PathVariable UUID id) {
|
||||
return ApiResponse.success(energyMeterService.getMeterById(id));
|
||||
}
|
||||
|
||||
@PutMapping("/energy-meters/{id}")
|
||||
@PutMapping("/meters/{id}")
|
||||
public ApiResponse<EnergyMeter> updateMeter(@PathVariable UUID id, @Valid @RequestBody EnergyMeter meter) {
|
||||
return ApiResponse.success(energyMeterService.updateMeter(id, meter));
|
||||
}
|
||||
|
||||
@DeleteMapping("/energy-meters/{id}")
|
||||
@DeleteMapping("/meters/{id}")
|
||||
public ApiResponse<Void> deleteMeter(@PathVariable UUID id) {
|
||||
energyMeterService.deleteMeter(id);
|
||||
return ApiResponse.success(null);
|
||||
|
|
@ -63,14 +63,14 @@ public class EnergyController {
|
|||
|
||||
// ==================== 能耗记录 ====================
|
||||
|
||||
@PostMapping("/energy-consumption")
|
||||
@PostMapping("/consumption")
|
||||
public ApiResponse<EnergyConsumption> recordConsumption(@RequestBody RecordConsumptionRequest request) {
|
||||
EnergyConsumption consumption = energyConsumptionService.recordConsumption(
|
||||
request.getMeterId(), request.getCurrentReading(), request.getRecordedBy());
|
||||
return ApiResponse.success(consumption);
|
||||
}
|
||||
|
||||
@GetMapping("/energy-consumption/{meterId}")
|
||||
@GetMapping("/consumption/{meterId}")
|
||||
public ApiResponse<List<EnergyConsumption>> getConsumption(
|
||||
@PathVariable UUID meterId,
|
||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||
|
|
@ -86,14 +86,14 @@ public class EnergyController {
|
|||
|
||||
// ==================== 能耗统计 ====================
|
||||
|
||||
@GetMapping("/energy-statistics/by-type")
|
||||
@GetMapping("/statistics/by-type")
|
||||
public ApiResponse<Map<EnergyMeter.EnergyType, BigDecimal>> getConsumptionByType(
|
||||
@RequestParam UUID projectId,
|
||||
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate month) {
|
||||
return ApiResponse.success(energyConsumptionService.getConsumptionByType(projectId, month));
|
||||
}
|
||||
|
||||
@GetMapping("/energy-statistics/unit-consumption")
|
||||
@GetMapping("/statistics/unit-consumption")
|
||||
public ApiResponse<BigDecimal> getUnitConsumption(
|
||||
@RequestParam UUID projectId,
|
||||
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate month) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package com.ether.pms.mdm.controller;
|
||||
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
import com.ether.pms.mdm.entity.InspectionItem;
|
||||
import com.ether.pms.mdm.service.InspectionItemService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 巡检标准项控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/mdm/inspection-items")
|
||||
@RequiredArgsConstructor
|
||||
public class InspectionItemController {
|
||||
|
||||
private final InspectionItemService inspectionItemService;
|
||||
|
||||
@PostMapping
|
||||
public ApiResponse<InspectionItem> createItem(@RequestBody InspectionItem item) {
|
||||
return ApiResponse.success(inspectionItemService.createItem(item));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ApiResponse<List<InspectionItem>> getItems(
|
||||
@RequestParam(required = false) String equipmentType,
|
||||
@RequestParam(required = false) String systemType,
|
||||
@RequestParam(required = false) Boolean activeOnly) {
|
||||
List<InspectionItem> items;
|
||||
if (activeOnly != null && activeOnly) {
|
||||
items = inspectionItemService.getActiveItems();
|
||||
} else if (equipmentType != null && systemType != null) {
|
||||
items = inspectionItemService.getItemsByEquipmentTypeAndSystemType(equipmentType, systemType);
|
||||
} else if (equipmentType != null) {
|
||||
items = inspectionItemService.getItemsByEquipmentType(equipmentType);
|
||||
} else if (systemType != null) {
|
||||
items = inspectionItemService.getItemsBySystemType(systemType);
|
||||
} else {
|
||||
items = inspectionItemService.getAllItems();
|
||||
}
|
||||
return ApiResponse.success(items);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ApiResponse<InspectionItem> getItem(@PathVariable UUID id) {
|
||||
return ApiResponse.success(inspectionItemService.getItemById(id));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ApiResponse<InspectionItem> updateItem(@PathVariable UUID id, @RequestBody InspectionItem item) {
|
||||
return ApiResponse.success(inspectionItemService.updateItem(id, item));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResponse<Void> deleteItem(@PathVariable UUID id) {
|
||||
inspectionItemService.deleteItem(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package com.ether.pms.mdm.controller;
|
||||
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
import com.ether.pms.mdm.entity.InspectionRecord;
|
||||
import com.ether.pms.mdm.service.InspectionRecordService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 巡检记录控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/mdm/inspection-records")
|
||||
@RequiredArgsConstructor
|
||||
public class InspectionRecordController {
|
||||
|
||||
private final InspectionRecordService inspectionRecordService;
|
||||
|
||||
@PostMapping
|
||||
public ApiResponse<InspectionRecord> createRecord(@RequestBody InspectionRecord record) {
|
||||
return ApiResponse.success(inspectionRecordService.createRecord(record));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ApiResponse<List<InspectionRecord>> getRecords(
|
||||
@RequestParam(required = false) UUID equipmentId,
|
||||
@RequestParam(required = false) UUID planId,
|
||||
@RequestParam(required = false) String inspector,
|
||||
@RequestParam(required = false) InspectionRecord.CheckStatus status,
|
||||
@RequestParam(required = false) LocalDate startDate,
|
||||
@RequestParam(required = false) LocalDate endDate) {
|
||||
List<InspectionRecord> records;
|
||||
if (equipmentId != null && startDate != null && endDate != null) {
|
||||
records = inspectionRecordService.getRecordsByEquipmentAndDateRange(equipmentId, startDate, endDate);
|
||||
} else if (equipmentId != null) {
|
||||
records = inspectionRecordService.getRecordsByEquipment(equipmentId);
|
||||
} else if (planId != null) {
|
||||
records = inspectionRecordService.getRecordsByPlan(planId);
|
||||
} else if (inspector != null) {
|
||||
records = inspectionRecordService.getRecordsByInspector(inspector);
|
||||
} else if (status != null) {
|
||||
records = inspectionRecordService.getRecordsByStatus(status);
|
||||
} else if (startDate != null && endDate != null) {
|
||||
records = inspectionRecordService.getRecordsByDateRange(startDate, endDate);
|
||||
} else {
|
||||
records = inspectionRecordService.getAllRecords();
|
||||
}
|
||||
return ApiResponse.success(records);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ApiResponse<InspectionRecord> getRecord(@PathVariable UUID id) {
|
||||
return ApiResponse.success(inspectionRecordService.getRecordById(id));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ApiResponse<InspectionRecord> updateRecord(@PathVariable UUID id, @RequestBody InspectionRecord record) {
|
||||
return ApiResponse.success(inspectionRecordService.updateRecord(id, record));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResponse<Void> deleteRecord(@PathVariable UUID id) {
|
||||
inspectionRecordService.deleteRecord(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/complete")
|
||||
public ApiResponse<InspectionRecord> completeRecord(@PathVariable UUID id) {
|
||||
return ApiResponse.success(inspectionRecordService.completeRecord(id));
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/ops/inspection-templates")
|
||||
@RequestMapping("/api/ops/inspection-templates")
|
||||
@RequiredArgsConstructor
|
||||
public class InspectionTemplateController {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
package com.ether.pms.mdm.controller;
|
||||
|
||||
import com.ether.pms.common.ApiResponse;
|
||||
import com.ether.pms.mdm.entity.MaintenancePlan;
|
||||
import com.ether.pms.mdm.service.MaintenancePlanService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/ops/maintenance-plans")
|
||||
@RequiredArgsConstructor
|
||||
public class MaintenancePlanController {
|
||||
|
||||
private final MaintenancePlanService maintenancePlanService;
|
||||
|
||||
@PostMapping
|
||||
public ApiResponse<MaintenancePlan> createPlan(@Valid @RequestBody MaintenancePlan plan) {
|
||||
MaintenancePlan created = maintenancePlanService.createPlan(plan);
|
||||
return ApiResponse.success(created);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ApiResponse<List<MaintenancePlan>> getPlans(
|
||||
@RequestParam UUID projectId,
|
||||
@RequestParam(required = false) MaintenancePlan.TriggerType triggerType) {
|
||||
List<MaintenancePlan> plans;
|
||||
if (triggerType != null) {
|
||||
plans = maintenancePlanService.getPlansByTriggerType(triggerType);
|
||||
} else {
|
||||
plans = maintenancePlanService.getActivePlansByProject(projectId);
|
||||
}
|
||||
return ApiResponse.success(plans);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ApiResponse<MaintenancePlan> getPlan(@PathVariable UUID id) {
|
||||
return ApiResponse.success(maintenancePlanService.getPlanById(id));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ApiResponse<MaintenancePlan> updatePlan(@PathVariable UUID id, @Valid @RequestBody MaintenancePlan plan) {
|
||||
return ApiResponse.success(maintenancePlanService.updatePlan(id, plan));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResponse<Void> deactivatePlan(@PathVariable UUID id) {
|
||||
maintenancePlanService.deactivatePlan(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue