[tag] 初步实现了设施管理功能

This commit is contained in:
LYC 2025-04-18 16:24:55 +08:00
parent 2cc4760175
commit db6e3f77a1
8 changed files with 280 additions and 17 deletions

15
pom.xml
View File

@ -59,6 +59,21 @@
<artifactId>validation-api</artifactId> <artifactId>validation-api</artifactId>
<version>2.0.1.Final</version> <version>2.0.1.Final</version>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>5.4.32.Final</version> <!-- 或其他合适的版本 -->
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version> <!-- 请检查最新版本 -->
</dependency>
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.18.0</version> <!-- 请使用最新版本 -->
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -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<Response<FacilityVO>> create(
@Valid @RequestBody FacilityCreateDTO dto) {
return ResponseEntity.ok(Response.newSuccess(facilityService.createFacility(dto)));
}
@GetMapping("/{id}")
public ResponseEntity<Response<FacilityVO>> getById(@PathVariable Integer id) {
return ResponseEntity.ok(Response.newSuccess(
facilityService.convertToVO(facilityService.getFacilityById(id))));
}
@GetMapping("/search")
public ResponseEntity<Response<Page<FacilityVO>>> 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<Response<Void>> updateStatus(
@PathVariable Integer id,
@RequestParam @NotNull FacilityStatus status) {
facilityService.updateStatus(id, status);
return ResponseEntity.ok(Response.newSuccess(null));
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,7 +1,11 @@
package com.waterquality.projectmanagement.entity.facility; package com.waterquality.projectmanagement.entity.facility;
import com.waterquality.projectmanagement.entity.department.Department;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.*; import lombok.*;
import org.locationtech.jts.geom.Coordinate;
import java.time.LocalDate;
@Entity @Entity
@Table(name = "facilities") @Table(name = "facilities")
@ -22,8 +26,7 @@ public class Facility {
@Column(name = "facility_type", nullable = false) @Column(name = "facility_type", nullable = false)
private FacilityType type; private FacilityType type;
@Column(columnDefinition = "POINT SRID 4326 NOT NULL") private String location;
private Point location;
@Column(nullable = false, length = 100) @Column(nullable = false, length = 100)
private String area; private String area;
@ -33,7 +36,7 @@ public class Facility {
private FacilityStatus status = FacilityStatus.NORMAL; private FacilityStatus status = FacilityStatus.NORMAL;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "responsible_dept", nullable = false) @JoinColumn(name = "responsible_dept", nullable = true)
private Department responsibleDept; private Department responsibleDept;
@Column(name = "install_date") @Column(name = "install_date")
@ -43,20 +46,6 @@ public class Facility {
private LocalDate lastMaintainDate; 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));
}
}

View File

@ -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<Facility, Integer> {
// 根据编码查询
Optional<Facility> findByCode(String code);
// 空间范围查询使用HQL
// @Query("SELECT f FROM Facility f WHERE within(f.location, :polygon) = true")
// List<Facility> findWithinArea(@Param("polygon") Polygon polygon);
// 状态统计使用原生SQL
@Query(value = "SELECT status, COUNT(*) as count FROM facilities GROUP BY status",
nativeQuery = true)
List<Map<String, Object>> countByStatus();
// 分页查询
Page<Facility> 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<Facility> search(
@Param("type") FacilityType type,
@Param("status") FacilityStatus status,
@Param("area") String area,
Pageable pageable
);
boolean existsByCode(String code);
}

View File

@ -53,4 +53,8 @@ public class DepartmentService {
return departmentRepository.findById(id) return departmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("部门不存在")); .orElseThrow(() -> new ResourceNotFoundException("部门不存在"));
} }
public Optional<Department> getDepartmentById(Integer deptId) {
return departmentRepository.findById(deptId);
}
} }

View File

@ -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<FacilityVO> 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;
}
}