diff --git a/pom.xml b/pom.xml index aa1e152..fca73a7 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,21 @@ validation-api 2.0.1.Final + + org.hibernate + hibernate-spatial + 5.4.32.Final + + + org.hibernate.validator + hibernate-validator + 6.2.0.Final + + + org.locationtech.jts + jts-core + 1.18.0 + diff --git a/src/main/java/com/waterquality/projectmanagement/controller/FacilityController.java b/src/main/java/com/waterquality/projectmanagement/controller/FacilityController.java new file mode 100644 index 0000000..89fa4db --- /dev/null +++ b/src/main/java/com/waterquality/projectmanagement/controller/FacilityController.java @@ -0,0 +1,58 @@ +package com.waterquality.projectmanagement.controller; + +import com.waterquality.projectmanagement.Response; +import com.waterquality.projectmanagement.dto.facility.FacilityCreateDTO; +import com.waterquality.projectmanagement.dto.facility.FacilityVO; +import com.waterquality.projectmanagement.entity.facility.FacilityStatus; +import com.waterquality.projectmanagement.entity.facility.FacilityType; +import com.waterquality.projectmanagement.service.FacilityService; +import jakarta.validation.constraints.NotNull; +import lombok.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +// FacilityController.java +@RestController +@RequestMapping("/api/facilities") +@RequiredArgsConstructor +public class FacilityController { + + private final FacilityService facilityService; + + @PostMapping + public ResponseEntity> create( + @Valid @RequestBody FacilityCreateDTO dto) { + return ResponseEntity.ok(Response.newSuccess(facilityService.createFacility(dto))); + } + + @GetMapping("/{id}") + public ResponseEntity> getById(@PathVariable Integer id) { + return ResponseEntity.ok(Response.newSuccess( + facilityService.convertToVO(facilityService.getFacilityById(id)))); + } + + @GetMapping("/search") + public ResponseEntity>> search( + @RequestParam(required = false) FacilityType type, + @RequestParam(required = false) FacilityStatus status, + @RequestParam(required = false) String area, + @PageableDefault Pageable pageable) { + return ResponseEntity.ok(Response.newSuccess( + facilityService.searchFacilities(type, status, area, pageable))); + } + + + @PatchMapping("/{id}/status") + public ResponseEntity> updateStatus( + @PathVariable Integer id, + @RequestParam @NotNull FacilityStatus status) { + facilityService.updateStatus(id, status); + return ResponseEntity.ok(Response.newSuccess(null)); + } +} \ No newline at end of file diff --git a/src/main/java/com/waterquality/projectmanagement/dto/facility/FacilityCreateDTO.java b/src/main/java/com/waterquality/projectmanagement/dto/facility/FacilityCreateDTO.java new file mode 100644 index 0000000..73179cf --- /dev/null +++ b/src/main/java/com/waterquality/projectmanagement/dto/facility/FacilityCreateDTO.java @@ -0,0 +1,37 @@ +package com.waterquality.projectmanagement.dto.facility; + +import com.waterquality.projectmanagement.entity.facility.FacilityType; +import lombok.*; +import org.hibernate.validator.constraints.Range; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.*; +import java.time.LocalDate; + +@Data +public class FacilityCreateDTO { + @NotBlank(message = "设施编码不能为空") + @Size(max = 30, message = "编码长度不能超过30字符") + private String code; + + @NotNull(message = "设施类型不能为空") + private FacilityType type; + + @NotNull(message = "经度不能为空") + @Range(min = -180, max = 180, message = "经度范围错误") + private Double longitude; + + @NotNull(message = "纬度不能为空") + @Range(min = -90, max = 90, message = "纬度范围错误") + private Double latitude; + + @NotBlank(message = "所属区域不能为空") + private String area; + + @NotNull(message = "管理部门不能为空") + private Integer deptId; + + @FutureOrPresent(message = "安装日期不能早于当前日期") + private LocalDate installDate; +} + diff --git a/src/main/java/com/waterquality/projectmanagement/dto/facility/FacilityVO.java b/src/main/java/com/waterquality/projectmanagement/dto/facility/FacilityVO.java new file mode 100644 index 0000000..614c3ef --- /dev/null +++ b/src/main/java/com/waterquality/projectmanagement/dto/facility/FacilityVO.java @@ -0,0 +1,22 @@ +package com.waterquality.projectmanagement.dto.facility; + +import com.waterquality.projectmanagement.entity.facility.FacilityStatus; +import com.waterquality.projectmanagement.entity.facility.FacilityType; +import lombok.Data; + +import java.time.LocalDate; + +// FacilityVO.java +@Data +public class FacilityVO { + private Integer id; + private String code; + private FacilityType type; + private String location; + private String area; + private FacilityStatus status; + private String deptName; + private LocalDate installDate; + private LocalDate lastMaintainDate; + +} diff --git a/src/main/java/com/waterquality/projectmanagement/entity/facility/Facility.java b/src/main/java/com/waterquality/projectmanagement/entity/facility/Facility.java index 9fdb850..249c1db 100644 --- a/src/main/java/com/waterquality/projectmanagement/entity/facility/Facility.java +++ b/src/main/java/com/waterquality/projectmanagement/entity/facility/Facility.java @@ -1,7 +1,11 @@ package com.waterquality.projectmanagement.entity.facility; +import com.waterquality.projectmanagement.entity.department.Department; import jakarta.persistence.*; import lombok.*; +import org.locationtech.jts.geom.Coordinate; + +import java.time.LocalDate; @Entity @Table(name = "facilities") @@ -22,8 +26,7 @@ public class Facility { @Column(name = "facility_type", nullable = false) private FacilityType type; - @Column(columnDefinition = "POINT SRID 4326 NOT NULL") - private Point location; + private String location; @Column(nullable = false, length = 100) private String area; @@ -33,7 +36,7 @@ public class Facility { private FacilityStatus status = FacilityStatus.NORMAL; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "responsible_dept", nullable = false) + @JoinColumn(name = "responsible_dept", nullable = true) private Department responsibleDept; @Column(name = "install_date") @@ -43,20 +46,6 @@ public class Facility { private LocalDate lastMaintainDate; // 空间坐标转换方法 - public void setCoordinates(Double lng, Double lat) { - this.location = GeometryUtils.createPoint(lng, lat); - } - public Coordinate getCoordinate() { - return this.location.getCoordinate(); - } } -// 空间工具类 -public class GeometryUtils { - private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(); - - public static Point createPoint(Double lng, Double lat) { - return GEOMETRY_FACTORY.createPoint(new Coordinate(lng, lat)); - } -} \ No newline at end of file diff --git a/src/main/java/com/waterquality/projectmanagement/repository/FacilityRepository.java b/src/main/java/com/waterquality/projectmanagement/repository/FacilityRepository.java new file mode 100644 index 0000000..84b62d5 --- /dev/null +++ b/src/main/java/com/waterquality/projectmanagement/repository/FacilityRepository.java @@ -0,0 +1,49 @@ +package com.waterquality.projectmanagement.repository; + +import com.waterquality.projectmanagement.entity.department.Department; +import com.waterquality.projectmanagement.entity.facility.Facility; +import com.waterquality.projectmanagement.entity.facility.FacilityStatus; +import com.waterquality.projectmanagement.entity.facility.FacilityType; +import org.locationtech.jts.geom.Polygon; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +// FacilityRepository.java +public interface FacilityRepository extends JpaRepository { + + // 根据编码查询 + Optional findByCode(String code); + + // 空间范围查询(使用HQL) + // @Query("SELECT f FROM Facility f WHERE within(f.location, :polygon) = true") + // List findWithinArea(@Param("polygon") Polygon polygon); + + // 状态统计(使用原生SQL) + @Query(value = "SELECT status, COUNT(*) as count FROM facilities GROUP BY status", + nativeQuery = true) + List> countByStatus(); + + // 分页查询 + Page findByResponsibleDept(Department dept, Pageable pageable); + + // 复杂条件查询 + @Query("SELECT f FROM Facility f WHERE " + + "(:type IS NULL OR f.type = :type) AND " + + "(:status IS NULL OR f.status = :status) AND " + + "(:area IS NULL OR f.area LIKE %:area%)") + Page search( + @Param("type") FacilityType type, + @Param("status") FacilityStatus status, + @Param("area") String area, + Pageable pageable + ); + + boolean existsByCode(String code); +} \ No newline at end of file diff --git a/src/main/java/com/waterquality/projectmanagement/service/DepartmentService.java b/src/main/java/com/waterquality/projectmanagement/service/DepartmentService.java index 5d18828..6fc4f19 100644 --- a/src/main/java/com/waterquality/projectmanagement/service/DepartmentService.java +++ b/src/main/java/com/waterquality/projectmanagement/service/DepartmentService.java @@ -53,4 +53,8 @@ public class DepartmentService { return departmentRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("部门不存在")); } + + public Optional getDepartmentById(Integer deptId) { + return departmentRepository.findById(deptId); + } } \ No newline at end of file diff --git a/src/main/java/com/waterquality/projectmanagement/service/FacilityService.java b/src/main/java/com/waterquality/projectmanagement/service/FacilityService.java new file mode 100644 index 0000000..27c83a3 --- /dev/null +++ b/src/main/java/com/waterquality/projectmanagement/service/FacilityService.java @@ -0,0 +1,89 @@ +package com.waterquality.projectmanagement.service; + +import com.waterquality.projectmanagement.dto.facility.FacilityCreateDTO; +import com.waterquality.projectmanagement.dto.facility.FacilityVO; +import com.waterquality.projectmanagement.entity.facility.Facility; +import com.waterquality.projectmanagement.entity.facility.FacilityStatus; +import com.waterquality.projectmanagement.entity.facility.FacilityType; +import com.waterquality.projectmanagement.exception.ResourceNotFoundException; +import com.waterquality.projectmanagement.repository.FacilityRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +// FacilityService.java +@Service +@RequiredArgsConstructor +@Slf4j +public class FacilityService { + + private final FacilityRepository facilityRepository; + + @Transactional + public FacilityVO createFacility(FacilityCreateDTO dto) { + // 校验编码唯一性 + if (facilityRepository.existsByCode(dto.getCode())) { + throw new ResourceNotFoundException("设施编码已存在"); + } + + // 构建实体 + Facility facility = new Facility(); + facility.setCode(dto.getCode()); + facility.setType(dto.getType()); + facility.setArea(dto.getArea()); + facility.setInstallDate(dto.getInstallDate()); + + + // 保存设施 + Facility saved = facilityRepository.save(facility); + return convertToVO(saved); + } + + @Transactional(readOnly = true) + public Page searchFacilities(FacilityType type, + FacilityStatus status, + String area, + Pageable pageable) { + return facilityRepository.search(type, status, area, pageable) + .map(this::convertToVO); + } + + @Transactional + public void updateStatus(Integer facilityId, FacilityStatus status) { + Facility facility = getFacilityById(facilityId); + if (facility.getStatus() != status) { + facility.setStatus(status); + facilityRepository.save(facility); + log.info("设施状态更新:{} -> {}", facility.getCode(), status); + } + } + + + public Facility getFacilityById(Integer id) { + return facilityRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("设施不存在")); + } + + public FacilityVO convertToVO(Facility facility) { + FacilityVO facilityVO = new FacilityVO(); + facilityVO.setId(facility.getId()); + facilityVO.setCode(facility.getCode()); + facilityVO.setType(facility.getType()); + + // 设置地理位置 + + facilityVO.setArea(facility.getArea()); + facilityVO.setStatus(facility.getStatus()); + facilityVO.setInstallDate(facility.getInstallDate()); + facilityVO.setLastMaintainDate(facility.getLastMaintainDate()); + + return facilityVO; + } +} \ No newline at end of file