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;
/**
*
* 电子围栏 前端控制器
*
*
* @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>> geXpRegion() {
return R.ok(regionService.getXpRegion());
}
@ApiOperation(value = "获取不分页的电子围栏列表",tags = {"管理后台-电子围栏"})
@PostMapping("/list")
R> getList(@RequestBody ElectronicPageListQuery query){
return R.ok(xpElectronicFenceService.getList(query));
}
@ApiOperation(value = "电子围栏管理列表", response = XpElectronicFence.class)
@PostMapping("/pageList")
public R> pageList(@RequestBody ElectronicPageListQuery query) {
PageInfo 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 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 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);
}
/**
* 校验编辑围栏时运营时间段缩短影响的点位数量
*
* 统计:围栏下 operation_type=2(自定义运营时间段) 的点位中,
* 当围栏运营时间范围发生缩短时,不在新运营时间范围(Union)内的点位数量(需要停用)。
*
*/
@PostMapping("/validateOperationStrategyShorten")
@ApiOperation(value = "校验围栏运营时间段缩短影响点位数量")
public R 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 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 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 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 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 electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpTestElectronicFenceArea::getTestElectronicId, id)
.list();
//根据经纬度获取当前城市code
// 生成围栏id 规则:城市首字母-类型简写-4位自增流水号(如 SZ-OP-001),支持复制;数据停用或删除后,流水号不回收
XpRegion xpRegion = xpRegionService.getOne(new LambdaQueryWrapper()
.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 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> listByIds(@RequestBody Collection electronicIdSet) {
if (CollUtil.isEmpty(electronicIdSet)) {
return R.ok(Collections.emptyList());
}
return R.ok(xpElectronicFenceService.listByIds(electronicIdSet));
}
@ApiOperation(value = "获取已启用的电子围栏列表",tags = {"管理后台-电子围栏"})
@GetMapping("/available/list")
R> getAvailableList(){
return R.ok(xpElectronicFenceService.getAvailableList());
}
/**
* 批量查询电子围栏
* @param fenceIds
* @return
*/
@PostMapping("/inner/xpElectronicFence/selectBatchIds")
public R> selectBatchIds(@RequestBody List fenceIds){
List xpElectronicFences = xpElectronicFenceService.listByIds(fenceIds);
return R.ok(xpElectronicFences);
}
/**
* 根据ID查询电子围栏
* @param fenceId
* @return
*/
@GetMapping("/inner/xpElectronicFence/selectById")
public R selectById(@RequestParam String fenceId){
XpTestElectronicFence xpElectronicFence = xpElectronicFenceService.getById(fenceId);
return R.ok(xpElectronicFence);
}
/**
* 校验围栏生效时间和失效时间的合法性:
*
* - 生效类型不是立即生效(effectType != 1)时,生效时间不能早于当前时间
* - 失效类型不是永不失效(failureType != 1)时,失效时间不能早于当前时间
*
*
* @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);
}
}