修改地区代码 #113

Merged
hnu202326010106 merged 3 commits from wanglei_branch into develop 2 weeks ago

@ -2,173 +2,152 @@ package com.campus.water.controller.web;
import com.campus.water.entity.Area;
import com.campus.water.service.AreaService;
import com.campus.water.util.ResultVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* //
* 使 ResultVO ResponseEntity
* Area areaId -
*/
@RestController
@RequestMapping("/api/web/area")
@RequiredArgsConstructor
@Tag(name = "区域管理接口", description = "校园、楼宇、区域的层级管理(增删改查)")
@RequestMapping("/api/area")
@CrossOrigin // 允许跨域(前端调用时需要)
public class AreaController {
private final AreaService areaService;
/**
* //
*
*/
@PostMapping("/add")
@PreAuthorize("hasRole('SUPER_ADMIN')")
@Operation(summary = "新增区域", description = "创建校园/楼宇/区域,严格校验层级关联规则")
public ResponseEntity<ResultVO<Area>> addArea(
@RequestBody @Parameter(description = "区域信息(名称/类型为必填)") Area area
) {
try {
Area newArea = areaService.addArea(area);
return ResponseEntity.ok(ResultVO.success(newArea, "新增区域成功"));
} catch (RuntimeException e) {
return ResponseEntity.ok(ResultVO.error(500, "新增失败:" + e.getMessage()));
}
// 构造器注入
public AreaController(AreaService areaService) {
this.areaService = areaService;
}
/**
*
*
*
* @param code 200400/500
* @param msg
* @param data
* @return Map
*/
@DeleteMapping("/delete/{areaId}")
@PreAuthorize("hasRole('SUPER_ADMIN')")
@Operation(summary = "删除区域", description = "删除指定区域,需确保无关联管理员和子区域")
public ResponseEntity<ResultVO<Void>> deleteArea(
@PathVariable @Parameter(description = "区域ID") String areaId
) {
try {
areaService.deleteArea(areaId);
return ResponseEntity.ok(ResultVO.success(null, "删除区域成功"));
} catch (RuntimeException e) {
return ResponseEntity.ok(ResultVO.error(500, "删除失败:" + e.getMessage()));
}
private Map<String, Object> buildResponse(int code, String msg, Object data) {
Map<String, Object> response = new HashMap<>();
response.put("code", code);
response.put("msg", msg);
response.put("data", data);
return response;
}
/**
*
*
* /
* @param area JSON
* @return
*/
@PutMapping("/update")
@PreAuthorize("hasRole('SUPER_ADMIN')")
@Operation(summary = "修改区域", description = "更新区域名称/父级/地址/负责人等信息,不允许修改区域类型")
public ResponseEntity<ResultVO<Area>> updateArea(
@RequestBody @Parameter(description = "区域信息areaId为必填") Area area
) {
@PostMapping("/add")
public ResponseEntity<Map<String, Object>> addArea(@RequestBody Area area) {
try {
if (area.getAreaId() == null) {
return ResponseEntity.ok(ResultVO.error(400, "区域ID不能为空"));
}
Area updatedArea = areaService.updateArea(area);
return ResponseEntity.ok(ResultVO.success(updatedArea, "修改区域成功"));
Area savedArea = areaService.addArea(area);
return ResponseEntity.ok(buildResponse(200, "新增成功", savedArea));
} catch (RuntimeException e) {
return ResponseEntity.ok(ResultVO.error(500, "修改失败:" + e.getMessage()));
// 业务异常400状态码 + 具体提示
return ResponseEntity.badRequest().body(buildResponse(400, e.getMessage(), null));
} catch (Exception e) {
// 系统异常500状态码 + 通用提示
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(buildResponse(500, "新增区域失败:" + e.getMessage(), null));
}
}
/**
* ID
* /
*
* @param areaId ID
* @param area
* @return
*/
@GetMapping("/detail/{areaId}")
@PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')")
@Operation(summary = "查询区域详情", description = "按ID查询单个区域的完整信息")
public ResponseEntity<ResultVO<Area>> getAreaDetail(
@PathVariable @Parameter(description = "区域ID") String areaId
) {
@PutMapping("/update/{areaId}")
public ResponseEntity<Map<String, Object>> updateArea(@PathVariable String areaId, @RequestBody Area area) {
try {
Area area = areaService.getAreaById(areaId);
return ResponseEntity.ok(ResultVO.success(area));
Area updatedArea = areaService.updateArea(areaId, area);
return ResponseEntity.ok(buildResponse(200, "修改成功", updatedArea));
} catch (RuntimeException e) {
return ResponseEntity.ok(ResultVO.error(500, "查询失败:" + e.getMessage()));
return ResponseEntity.badRequest().body(buildResponse(400, e.getMessage(), null));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(buildResponse(500, "修改区域失败:" + e.getMessage(), null));
}
}
/**
*
* /
*
* @param areaId ID
* @return
*/
@GetMapping("/list")
@PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')")
@Operation(summary = "查询区域列表", description = "支持按父级ID/区域类型/名称关键词筛选区域")
public ResponseEntity<ResultVO<List<Area>>> listAreas(
@RequestParam(required = false) @Parameter(description = "父级区域ID") String parentAreaId,
@RequestParam(required = false) @Parameter(description = "区域类型campus/building/zone") Area.AreaType areaType,
@RequestParam(required = false) @Parameter(description = "名称关键词(模糊匹配)") String keyword
) {
@DeleteMapping("/delete/{areaId}")
public ResponseEntity<Map<String, Object>> deleteArea(@PathVariable String areaId) {
try {
List<Area> areas = areaService.listAreas(parentAreaId, areaType, keyword);
return ResponseEntity.ok(ResultVO.success(areas));
areaService.deleteArea(areaId);
return ResponseEntity.ok(buildResponse(200, "删除成功", null));
} catch (RuntimeException e) {
return ResponseEntity.ok(ResultVO.error(500, "查询失败:" + e.getMessage()));
return ResponseEntity.badRequest().body(buildResponse(400, e.getMessage(), null));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(buildResponse(500, "删除区域失败:" + e.getMessage(), null));
}
}
/**
*
* /
*
* @return
*/
@GetMapping("/list-campus")
@PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')")
@Operation(summary = "查询所有校园", description = "快速获取所有校园级别的顶级区域")
public ResponseEntity<ResultVO<List<Area>>> listCampus() {
@GetMapping("/cities")
public ResponseEntity<Map<String, Object>> getAllCities() {
try {
List<Area> campusList = areaService.listAreas(null, Area.AreaType.campus, null);
return ResponseEntity.ok(ResultVO.success(campusList));
} catch (RuntimeException e) {
return ResponseEntity.ok(ResultVO.error(500, "查询失败:" + e.getMessage()));
List<Area> cities = areaService.getAllCities();
return ResponseEntity.ok(buildResponse(200, "查询成功", cities));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(buildResponse(500, "查询市区列表失败:" + e.getMessage(), null));
}
}
/**
*
* /
* ID
* @param cityId IDareaId
* @return
*/
@GetMapping("/list-building/{campusId}")
@PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')")
@Operation(summary = "查询校园下的楼宇", description = "按校园ID查询该校园下的所有楼宇")
public ResponseEntity<ResultVO<List<Area>>> listBuildingByCampus(
@PathVariable @Parameter(description = "校园ID") String campusId
) {
@GetMapping("/campuses/{cityId}")
public ResponseEntity<Map<String, Object>> getCampusesByCityId(@PathVariable String cityId) {
try {
List<Area> buildingList = areaService.listAreas(campusId, Area.AreaType.building, null);
return ResponseEntity.ok(ResultVO.success(buildingList));
List<Area> campuses = areaService.getCampusesByCityId(cityId);
return ResponseEntity.ok(buildResponse(200, "查询成功", campuses));
} catch (RuntimeException e) {
return ResponseEntity.ok(ResultVO.error(500, "查询失败:" + e.getMessage()));
return ResponseEntity.badRequest().body(buildResponse(400, e.getMessage(), null));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(buildResponse(500, "查询校园列表失败:" + e.getMessage(), null));
}
}
/**
*
* /
* ID
* 便
* @param areaId ID
* @return
*/
@GetMapping("/list-zone/{buildingId}")
@PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')")
@Operation(summary = "查询楼宇下的区域", description = "按楼宇ID查询该楼宇下的所有子区域")
public ResponseEntity<ResultVO<List<Area>>> listZoneByBuilding(
@PathVariable @Parameter(description = "楼宇ID") String buildingId
) {
@GetMapping("/{areaId}")
public ResponseEntity<Map<String, Object>> getAreaById(@PathVariable String areaId) {
try {
List<Area> zoneList = areaService.listAreas(buildingId, Area.AreaType.zone, null);
return ResponseEntity.ok(ResultVO.success(zoneList));
Area area = areaService.getAreaById(areaId);
return ResponseEntity.ok(buildResponse(200, "查询成功", area));
} catch (RuntimeException e) {
return ResponseEntity.ok(ResultVO.error(500, "查询失败:" + e.getMessage()));
return ResponseEntity.badRequest().body(buildResponse(400, e.getMessage(), null));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(buildResponse(500, "查询区域详情失败:" + e.getMessage(), null));
}
}
}

@ -45,19 +45,22 @@ public class Area {
@Column(name = "updated_time")
private LocalDateTime updatedTime = LocalDateTime.now();
// 枚举值与数据库完全匹配zone=市区campus=校园building=楼栋(暂时保留)
public enum AreaType {
campus("校园"),
building("楼宇"),
zone("区域");
zone("市区"), // 对应数据库的zone含义是市区
campus("校园"); // 对应数据库的campus含义是校园
private final String desc;
private final String desc;
AreaType(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
}

@ -2,39 +2,27 @@ package com.campus.water.mapper;
import com.campus.water.entity.Area;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface AreaRepository extends JpaRepository<Area, String> {
// 根据区域类型查询
List<Area> findByAreaType(Area.AreaType areaType);
// 根据父区域ID查询子区域
List<Area> findByParentAreaId(String parentAreaId);
// 修正将findByIdAndIsDeletedFalse改为findByAreaIdAndIsDeletedFalse如果有isDeleted字段
// 注意你的实体类目前没有isDeleted字段先删除该方法或补充字段后再用
Optional<Area> findByAreaId(String areaId);
// 新增按区域类型查询
List<Area> findByAreaTypeOrderByCreatedTimeDesc(Area.AreaType areaType);
// 查询所有市区根节点areaType=CITY 且 parentAreaId=null
List<Area> findByAreaTypeAndParentAreaIdIsNull(Area.AreaType areaType);
// 按父级ID+类型查询(如查询某校园下的所有楼宇
// 根据父级ID和区域类型查询指定市区下的校园
List<Area> findByParentAreaIdAndAreaType(String parentAreaId, Area.AreaType areaType);
// 统计指定父级ID下的区域数量
long countByParentAreaId(String parentAreaId);
// 按名称模糊查询
List<Area> findByAreaNameContaining(String keyword);
// 查询所有(按创建时间倒序)
List<Area> findAllByOrderByCreatedTimeDesc();
// 根据管理员姓名查询区域
List<Area> findByManager(String manager);
// 根据管理员手机号查询区域
List<Area> findByManagerPhone(String managerPhone);
// 查询指定类型的根级区域
@Query("SELECT a FROM Area a WHERE a.areaType = ?1 AND a.parentAreaId IS NULL")
List<Area> findRootAreasByType(Area.AreaType areaType);
// 校验区域ID是否存在
boolean existsByAreaId(String areaId);
}

@ -2,186 +2,171 @@ package com.campus.water.service;
import com.campus.water.entity.Area;
import com.campus.water.mapper.AreaRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
/**
*
* //
*
* Area areaId address/manager
*
*/
@Service
@RequiredArgsConstructor
public class AreaService {
private final AreaRepository areaRepository;
private final AdminService adminService; // 用于删除时校验管理员关联
// 构造器注入(推荐的注入方式)
public AreaService(AreaRepository areaRepository) {
this.areaRepository = areaRepository;
}
/**
* //
*
* 1.
* 2.
* 3.
* 4.
*
* @param area ID
* @return
*/
@Transactional(rollbackFor = Exception.class)
public Area addArea(Area area) {
// 基础字段校验
if (area.getAreaName() == null || area.getAreaName().trim().isEmpty()) {
throw new RuntimeException("区域名称不能为空");
}
if (area.getAreaType() == null) {
throw new RuntimeException("区域类型不能为空(校园/楼宇/区域)");
}
// 1. 基础参数校验
validateBaseParams(area);
// 层级关联校验
handleAreaLevelCheck(area);
// 2. 层级规则校验(核心)
validateAreaHierarchy(area);
// 初始化时间字段(兜底,防止手动修改
// 3. 补充基础字段createdTime/updatedTime 已默认赋值,可手动刷新)
area.setCreatedTime(LocalDateTime.now());
area.setUpdatedTime(LocalDateTime.now());
// 4. 保存数据
return areaRepository.save(area);
}
/**
*
*
* 1.
* 2.
* 3.
*
* @param areaId ID
* @param area
* @return
*/
public void deleteArea(String areaId) {
@Transactional(rollbackFor = Exception.class)
public Area updateArea(String areaId, Area area) {
// 1. 校验区域是否存在
Area existArea = areaRepository.findById(areaId)
.orElseThrow(() -> new RuntimeException("区域不存在" + areaId));
Area existingArea = areaRepository.findByAreaId(areaId)
.orElseThrow(() -> new RuntimeException("区域不存在ID" + areaId));
// 2. 校验是否有管理员关联该区域
List<com.campus.water.entity.Admin> relatedAdmins = adminService.getAdminsByAreaId(areaId);
if (!relatedAdmins.isEmpty()) {
throw new RuntimeException("该区域关联了" + relatedAdmins.size() + "个管理员,无法删除");
}
// 2. 基础参数校验
validateBaseParams(area);
// 3. 校验是否有下级子区域
List<Area> childAreas = areaRepository.findByParentAreaId(areaId);
if (!childAreas.isEmpty()) {
throw new RuntimeException("该区域包含" + childAreas.size() + "个子区域,无法删除(请先删除子区域)");
// 3. 层级规则校验(修改时需保持层级规则)
validateAreaHierarchy(area);
// 4. 覆盖可修改字段(适配你的实体类所有字段)
existingArea.setAreaName(area.getAreaName());
existingArea.setAreaType(area.getAreaType());
// 父级ID市区不允许修改校园必须指向市区
if (Area.AreaType.campus.equals(area.getAreaType())) {
existingArea.setParentAreaId(area.getParentAreaId());
}
// 新增字段赋值
existingArea.setAddress(area.getAddress());
existingArea.setManager(area.getManager());
existingArea.setManagerPhone(area.getManagerPhone());
// 更新时间
existingArea.setUpdatedTime(LocalDateTime.now());
// 执行删除
areaRepository.delete(existArea);
// 5. 保存修改
return areaRepository.save(existingArea);
}
/**
*
*
*
*
* @param areaId ID
*/
public Area updateArea(Area area) {
@Transactional(rollbackFor = Exception.class)
public void deleteArea(String areaId) {
// 1. 校验区域是否存在
Area existArea = areaRepository.findById(area.getAreaId())
.orElseThrow(() -> new RuntimeException("区域不存在:" + area.getAreaId()));
// 2. 基础字段校验
if (area.getAreaName() == null || area.getAreaName().trim().isEmpty()) {
throw new RuntimeException("区域名称不能为空");
}
// 3. 层级关联校验如果修改了父级ID
if (!equalsWithNull(existArea.getParentAreaId(), area.getParentAreaId())) {
handleAreaLevelCheck(area);
Area existingArea = areaRepository.findByAreaId(areaId)
.orElseThrow(() -> new RuntimeException("区域不存在ID" + areaId));
// 2. 校验删除规则:若为市区,需先删除其下所有校园
if (Area.AreaType.zone.equals(existingArea.getAreaType())) {
long campusCount = areaRepository.countByParentAreaId(areaId);
if (campusCount > 0) {
throw new RuntimeException("该市区下仍有 " + campusCount + " 个校园,无法删除,请先删除下属校园");
}
}
// 4. 赋值(仅更新允许修改的字段)
existArea.setAreaName(area.getAreaName());
existArea.setParentAreaId(area.getParentAreaId());
existArea.setAddress(area.getAddress());
existArea.setManager(area.getManager());
existArea.setManagerPhone(area.getManagerPhone());
existArea.setUpdatedTime(LocalDateTime.now());
// 5. 保存修改
return areaRepository.save(existArea);
// 3. 物理删除(若需逻辑删除,可参考后续说明添加 isDeleted 字段)
areaRepository.delete(existingArea);
}
/**
* ID
*
* @return
*/
public Area getAreaById(String areaId) {
return areaRepository.findById(areaId)
.orElseThrow(() -> new RuntimeException("区域不存在:" + areaId));
public List<Area> getAllCities() {
return areaRepository.findByAreaTypeAndParentAreaIdIsNull(Area.AreaType.zone);
}
/**
*
* ID
* ID
* @param cityId IDareaId
* @return
*/
public List<Area> listAreas(String parentAreaId, Area.AreaType areaType, String keyword) {
if (parentAreaId != null && !parentAreaId.trim().isEmpty()) {
// 按父级ID查询
if (areaType != null) {
// 父级ID + 类型
return areaRepository.findByParentAreaIdAndAreaType(parentAreaId, areaType);
} else {
// 仅父级ID
return areaRepository.findByParentAreaId(parentAreaId);
}
} else if (areaType != null) {
// 按类型查询
return areaRepository.findByAreaTypeOrderByCreatedTimeDesc(areaType);
} else if (keyword != null && !keyword.trim().isEmpty()) {
// 按名称模糊查询
return areaRepository.findByAreaNameContaining(keyword);
} else {
// 查询所有(按创建时间倒序)
return areaRepository.findAllByOrderByCreatedTimeDesc();
public List<Area> getCampusesByCityId(String cityId) {
// 校验市区是否存在
if (!areaRepository.existsByAreaId(cityId)) {
throw new RuntimeException("市区不存在ID" + cityId);
}
return areaRepository.findByParentAreaIdAndAreaType(cityId, Area.AreaType.campus );
}
/**
*
*
* @param area
*/
private void handleAreaLevelCheck(Area area) {
Area.AreaType type = area.getAreaType();
String parentId = area.getParentAreaId();
// 1. 校园(顶级节点):不允许设置父级
if (type == Area.AreaType.campus) {
if (parentId != null && !parentId.trim().isEmpty()) {
throw new RuntimeException("校园作为顶级节点,不允许关联父级区域");
}
return;
}
// 2. 楼宇/区域:必须设置父级
if (parentId == null || parentId.trim().isEmpty()) {
throw new RuntimeException(type.getDesc() + "必须关联父级区域");
}
// 3. 校验父级区域是否存在且类型匹配
Area parentArea = areaRepository.findById(parentId)
.orElseThrow(() -> new RuntimeException("父级区域不存在:" + parentId));
if (type == Area.AreaType.building && parentArea.getAreaType() != Area.AreaType.campus) {
throw new RuntimeException("楼宇的父级必须是校园");
private void validateBaseParams(Area area) {
if (area.getAreaName() == null || area.getAreaName().trim().isEmpty()) {
throw new RuntimeException("区域名称不能为空");
}
if (type == Area.AreaType.zone && parentArea.getAreaType() != Area.AreaType.building) {
throw new RuntimeException("区域的父级必须是楼宇");
if (area.getAreaType() == null) {
throw new RuntimeException("区域类型不能为空(市区/校园)");
}
}
/**
* null
*
* 1IDparentAreaId=null
* 2ID
*/
private boolean equalsWithNull(Object a, Object b) {
if (a == null && b == null) {
return true;
}
if (a == null || b == null) {
return false;
private void validateAreaHierarchy(Area area) {
if (Area.AreaType.zone.equals(area.getAreaType())) {
// 市区不允许设置父级ID
if (area.getParentAreaId() != null && !area.getParentAreaId().trim().isEmpty()) {
throw new RuntimeException("市区为根节点,不允许设置父级区域");
}
} else if (Area.AreaType.campus .equals(area.getAreaType())) {
// 校园必须设置父级ID
if (area.getParentAreaId() == null || area.getParentAreaId().trim().isEmpty()) {
throw new RuntimeException("校园必须关联市区作为父级区域");
}
// 校验父级区域是否存在且类型为市区
Optional<Area> parentAreaOpt = areaRepository.findByAreaId(area.getParentAreaId());
if (parentAreaOpt.isEmpty()) {
throw new RuntimeException("父级市区不存在ID" + area.getParentAreaId());
}
Area parentArea = parentAreaOpt.get();
if (!Area.AreaType.zone.equals(parentArea.getAreaType())) {
throw new RuntimeException("校园的父级区域必须是市区,当前父级类型为:" + parentArea.getAreaType().getDesc());
}
}
return a.equals(b);
}
public Area getAreaById(String areaId) {
return areaRepository.findByAreaId(areaId)
.orElseThrow(() -> new RuntimeException("区域不存在ID" + areaId));
}
}
Loading…
Cancel
Save