package com.xiaopeng.map.controller;
|
|
|
import cn.hutool.core.collection.CollUtil;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.xiaopeng.api.dto.operation.*;
|
import com.xiaopeng.api.model.operation.*;
|
import com.xiaopeng.api.query.operation.ElectronicPageListQuery;
|
import com.xiaopeng.api.vo.operation.ElectronicFenceDetailVo;
|
import com.xiaopeng.api.vo.operation.TestElectronicFenceDetailVo;
|
import com.xiaopeng.common.core.domain.R;
|
import com.xiaopeng.common.core.exception.ServiceException;
|
import com.xiaopeng.common.core.utils.bean.BeanUtils;
|
import com.xiaopeng.common.core.web.page.PageInfo;
|
import com.xiaopeng.map.constant.I18nKeyConstant;
|
import com.xiaopeng.map.service.*;
|
import com.xiaopeng.map.utils.OperationStrategyIndex;
|
import io.swagger.annotations.Api;
|
import io.swagger.annotations.ApiOperation;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
|
import org.apache.shardingsphere.transaction.core.TransactionType;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
import org.springframework.web.bind.annotation.*;
|
|
import javax.annotation.Resource;
|
import javax.validation.Valid;
|
import javax.validation.constraints.NotNull;
|
import java.time.LocalDateTime;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
import static com.xiaopeng.map.service.serviceimpl.XpElectronicFenceServiceImpl.getPinyinInitials;
|
|
/**
|
* <p>
|
* 电子围栏 前端控制器
|
* </p>
|
*
|
* @author mitao
|
* @since 2026-02-03
|
*/
|
@RestController
|
@Slf4j
|
@Api(tags = "测试demo电子围栏管理")
|
@RequestMapping("/api/v1/testElectronicFence")
|
public class XpTestElectronicFenceManagementController {
|
|
|
@Resource
|
private XpTestElectronicFenceService xpElectronicFenceService;
|
|
@Resource
|
private XpTestElectronicFenceAreaService xpElectronicFenceAreaService;
|
|
@Resource
|
private XpRegionService regionService;
|
@Autowired
|
private XpRegionService xpRegionService;
|
|
@Autowired
|
private XpPointService xpPointService;
|
/**
|
* 查询省市管理
|
*/
|
@ApiOperation(value = "查询省市区",response = XpRegion.class)
|
@GetMapping(value = "/getRegion")
|
public R<List<List<XpRegion>>> geXpRegion() {
|
return R.ok(regionService.getXpRegion());
|
}
|
|
|
@ApiOperation(value = "获取不分页的电子围栏列表",tags = {"管理后台-电子围栏"})
|
@PostMapping("/list")
|
R<List<XpTestElectronicFence>> getList(@RequestBody ElectronicPageListQuery query){
|
return R.ok(xpElectronicFenceService.getList(query));
|
}
|
|
|
@ApiOperation(value = "电子围栏管理列表", response = XpElectronicFence.class)
|
@PostMapping("/pageList")
|
public R<PageInfo<XpTestElectronicFence>> pageList(@RequestBody ElectronicPageListQuery query) {
|
PageInfo<XpTestElectronicFence> pageList = xpElectronicFenceService.pageList(query);
|
return R.ok(pageList);
|
}
|
|
|
/**
|
* 新增电子围栏
|
*/
|
@PostMapping("/add")
|
@ApiOperation(value = "新增电子围栏")
|
public R add(@Validated @RequestBody TestElectronicFenceAddDto dto) {
|
validateFenceTime(dto.getStatus(), dto.getEffectType(), dto.getEffectTime(), dto.getFailureType(), dto.getFailureTime());
|
return xpElectronicFenceService.addElectronic(dto);
|
}
|
|
|
|
/**
|
* 修改状态
|
*/
|
@PostMapping("/updateStatus")
|
@ResponseBody
|
@ApiOperation(value = "修改状态")
|
public R updateStatus(@RequestBody ElectronicFenceAreaUpdateStatusDto dto) {
|
XpTestElectronicFenceArea electronicFenceArea = xpElectronicFenceAreaService.getById(dto.getId());
|
if (electronicFenceArea == null) {
|
throw new ServiceException(
|
"围栏构成不存在,请刷新重试",
|
I18nKeyConstant.FENCE_STRUCTURE_NOT_EXIST
|
);
|
}
|
xpElectronicFenceAreaService.updateById(electronicFenceArea);
|
return R.ok();
|
}
|
|
/**
|
* 电子围栏详情
|
*/
|
@PostMapping("/queryById")
|
@ApiOperation(value = "电子围栏详情")
|
public R<TestElectronicFenceDetailVo> queryById(@RequestParam("id")Long id) {
|
TestElectronicFenceDetailVo res = new TestElectronicFenceDetailVo();
|
XpTestElectronicFence electronicFence = xpElectronicFenceService.getById(id);
|
if (electronicFence == null) {
|
throw new ServiceException(
|
"电子围栏不存在,请刷新重试",
|
I18nKeyConstant.FENCE_NOT_EXIST
|
);
|
}
|
BeanUtils.copyProperties(electronicFence, res);
|
List<XpTestElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpTestElectronicFenceArea::getTestElectronicId, id)
|
.list();
|
res.setElectronicFenceAreas(electronicFenceAreaList);
|
return R.ok(res);
|
}
|
|
/**
|
* 编辑电子围栏
|
*/
|
@PostMapping("/edit")
|
@ApiOperation(value = "编辑电子围栏")
|
@Transactional
|
@ShardingSphereTransactionType(TransactionType.BASE)
|
public R edit(@Validated @RequestBody TestElectronicFenceUpdateDto dto) {
|
validateFenceTime(dto.getStatus(), dto.getEffectType(), dto.getEffectTime(), dto.getFailureType(), dto.getFailureTime());
|
return xpElectronicFenceService.editElectronic(dto);
|
}
|
|
/**
|
* 校验编辑围栏时运营时间段缩短影响的点位数量
|
* <p>
|
* 统计:围栏下 operation_type=2(自定义运营时间段) 的点位中,
|
* 当围栏运营时间范围发生缩短时,不在新运营时间范围(Union)内的点位数量(需要停用)。
|
* </p>
|
*/
|
@PostMapping("/validateOperationStrategyShorten")
|
@ApiOperation(value = "校验围栏运营时间段缩短影响点位数量")
|
public R<Integer> validateOperationStrategyShorten(@Valid @RequestBody ElectronicFenceOperationStrategyValidateDto dto) {
|
XpTestElectronicFence fence = xpElectronicFenceService.getById(dto.getId());
|
if (fence == null) {
|
throw new ServiceException("围栏不存在", I18nKeyConstant.FENCE_NOT_EXIST);
|
}
|
|
OperationStrategyIndex oldIndex = OperationStrategyIndex.parse(fence.getOperationStrategy());
|
OperationStrategyIndex newIndex = OperationStrategyIndex.parse(dto.getOperationStrategy());
|
if (oldIndex.equalsNormalized(newIndex)) {
|
return R.ok(0);
|
}
|
if (!oldIndex.hasAnyShrinkComparedTo(newIndex)) {
|
return R.ok(0);
|
}
|
|
List<Long> fenceAreaIds = xpElectronicFenceAreaService.lambdaQuery()
|
.eq(XpTestElectronicFenceArea::getTestElectronicId, dto.getId())
|
.list()
|
.stream()
|
.map(XpTestElectronicFenceArea::getId)
|
.filter(Objects::nonNull)
|
.distinct()
|
.collect(Collectors.toList());
|
if (CollUtil.isEmpty(fenceAreaIds)) {
|
return R.ok(0);
|
}
|
|
List<XpPoint> customPoints = xpPointService.lambdaQuery()
|
.in(XpPoint::getElectronicAreaId, fenceAreaIds)
|
.eq(XpPoint::getPointType, 1)
|
.eq(XpPoint::getOperationType, 2)
|
.list();
|
if (CollUtil.isEmpty(customPoints)) {
|
return R.ok(0);
|
}
|
|
int disableCount = 0;
|
for (XpPoint point : customPoints) {
|
if (point == null || point.getEffectiveTimePeriod() == null || point.getEffectiveTimePeriod().isEmpty()) {
|
continue;
|
}
|
List<OperationStrategyIndex.TimeInterval> pointIntervals = OperationStrategyIndex.parsePointEffectiveTimePeriod(point.getEffectiveTimePeriod());
|
if (pointIntervals.isEmpty()) {
|
continue;
|
}
|
|
boolean shouldDisable = !newIndex.isPointScheduleWithin(pointIntervals);
|
if (shouldDisable) {
|
disableCount++;
|
}
|
}
|
return R.ok(disableCount);
|
}
|
|
/**
|
* 复制电子围栏
|
*/
|
@PostMapping("/copy")
|
@ApiOperation(value = "复制电子围栏")
|
@Transactional
|
@ShardingSphereTransactionType(TransactionType.BASE)
|
public R<Long> copy(@RequestParam("id") Integer id) {
|
XpTestElectronicFence electronicFence = xpElectronicFenceService.getById(id);
|
if (electronicFence.getStatus()==2){
|
throw new ServiceException(
|
"当前电子围栏状态为启用中,不可复制",
|
I18nKeyConstant.FENCE_STATUS_CANNOT_COPIED
|
);
|
}
|
electronicFence.setElectronicName(buildCopyFenceName(electronicFence.getElectronicName()));
|
electronicFence.setId(null);
|
electronicFence.setStatus(0);
|
List<XpTestElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpTestElectronicFenceArea::getTestElectronicId, id)
|
.list();
|
//根据经纬度获取当前城市code
|
// 生成围栏id 规则:城市首字母-类型简写-4位自增流水号(如 SZ-OP-001),支持复制;数据停用或删除后,流水号不回收
|
XpRegion xpRegion = xpRegionService.getOne(new LambdaQueryWrapper<XpRegion>()
|
.eq(XpRegion::getCode, electronicFence.getCityCode()));
|
String name = xpRegion.getName();
|
// 根据城市中文名,截取前两位的首字母并且大写
|
// 截取前两个字符
|
String prefix = name.substring(0, 2);
|
// 获取拼音首字母并大写
|
String result = getPinyinInitials(prefix).toUpperCase();
|
xpElectronicFenceService.save(electronicFence);
|
electronicFence.setElectronicNumber(result + "-OP-" + String.format("%04d", electronicFence.getId()));
|
xpElectronicFenceService.updateById(electronicFence);
|
for (XpTestElectronicFenceArea area : electronicFenceAreaList) {
|
area.setId(null);
|
area.setTestElectronicId(electronicFence.getId());
|
area.setAreaStatus(2);
|
area.setAreaName("副本_"+area.getAreaName());
|
}
|
xpElectronicFenceAreaService.saveBatch(electronicFenceAreaList);
|
return R.ok(electronicFence.getId());
|
}
|
|
private String buildCopyFenceName(String sourceName) {
|
Set<String> existingNames = new HashSet<>(xpElectronicFenceService.lambdaQuery()
|
.likeRight(XpTestElectronicFence::getElectronicName, sourceName)
|
.list()
|
.stream()
|
.map(XpTestElectronicFence::getElectronicName)
|
.collect(Collectors.toSet()));
|
|
String candidateName = sourceName + "_副本";
|
while (existingNames.contains(candidateName)) {
|
candidateName = candidateName + "_副本";
|
}
|
return candidateName;
|
}
|
|
/**
|
* 根据ID列表批量查询电子围栏
|
*
|
* @param electronicIdSet
|
* @return
|
*/
|
@PostMapping("/listByIds")
|
R<List<XpTestElectronicFence>> listByIds(@RequestBody Collection<Long> electronicIdSet) {
|
if (CollUtil.isEmpty(electronicIdSet)) {
|
return R.ok(Collections.emptyList());
|
}
|
return R.ok(xpElectronicFenceService.listByIds(electronicIdSet));
|
}
|
|
@ApiOperation(value = "获取已启用的电子围栏列表",tags = {"管理后台-电子围栏"})
|
@GetMapping("/available/list")
|
R<List<XpTestElectronicFence>> getAvailableList(){
|
return R.ok(xpElectronicFenceService.getAvailableList());
|
}
|
|
|
|
|
/**
|
* 批量查询电子围栏
|
* @param fenceIds
|
* @return
|
*/
|
@PostMapping("/inner/xpElectronicFence/selectBatchIds")
|
public R<List<XpTestElectronicFence>> selectBatchIds(@RequestBody List<String> fenceIds){
|
List<XpTestElectronicFence> xpElectronicFences = xpElectronicFenceService.listByIds(fenceIds);
|
return R.ok(xpElectronicFences);
|
}
|
|
|
/**
|
* 根据ID查询电子围栏
|
* @param fenceId
|
* @return
|
*/
|
@GetMapping("/inner/xpElectronicFence/selectById")
|
public R<XpTestElectronicFence> selectById(@RequestParam String fenceId){
|
XpTestElectronicFence xpElectronicFence = xpElectronicFenceService.getById(fenceId);
|
return R.ok(xpElectronicFence);
|
}
|
|
/**
|
* 校验围栏生效时间和失效时间的合法性:
|
* <ul>
|
* <li>生效类型不是立即生效(effectType != 1)时,生效时间不能早于当前时间</li>
|
* <li>失效类型不是永不失效(failureType != 1)时,失效时间不能早于当前时间</li>
|
* </ul>
|
*
|
* @param status
|
* @param effectType 生效类型(1=立即生效 2=定时生效)
|
* @param effectTime 生效时间
|
* @param failureType 失效类型(1=永不失效 2=定时失效)
|
* @param failureTime 失效时间
|
*/
|
private void validateFenceTime(@NotNull Integer status, Integer effectType, LocalDateTime effectTime,
|
Integer failureType, LocalDateTime failureTime) {
|
LocalDateTime now = LocalDateTime.now();
|
// 发布/启用时,定时生效的生效时间不能早于当前时间
|
if (requiresScheduleValidation(status) && effectType != null && effectType != 1) {
|
if (effectTime == null || effectTime.isBefore(now)) {
|
throw new ServiceException("围栏生效时间不合法,定时生效时生效时间不能早于当前时间",
|
I18nKeyConstant.FENCE_EFFECT_TIME_INVALID);
|
}
|
}
|
// 发布/启用时,定时失效的失效时间不能早于当前时间
|
if (requiresScheduleValidation(status) && failureType != null && failureType != 1) {
|
if (failureTime == null || failureTime.isBefore(now)) {
|
throw new ServiceException("围栏失效时间不合法,定时失效时失效时间不能早于当前时间",
|
I18nKeyConstant.FENCE_FAILURE_TIME_INVALID);
|
}
|
}
|
}
|
|
private boolean requiresScheduleValidation(Integer status) {
|
return status != null && (status == 1 || status == 2);
|
}
|
}
|