feat(ops,iot): 保洁前端 API 层和区域管理新增

新增保洁业务前端 API 接口层(工牌、工单、仪表盘)和运营区域管理完整功能,包含 Service/Controller/Test 三层结构。

主要功能:

1. IoT 设备查询 API(RPC 接口)
   - IotDeviceQueryApi: 提供设备简化信息查询
   - IotDeviceSimpleRespDTO: 设备简化 DTO

2. 保洁工牌管理
   - CleanBadgeService/Impl: 工牌通知、优先级调整、手动完成
   - BadgeNotifyReqDTO/UpgradePriorityReqDTO/ManualCompleteOrderReqDTO

3. 保洁工单管理
   - CleanWorkOrderService/Impl: 工单时间线查询

4. 保洁仪表盘
   - CleanDashboardService/Impl: 快速统计(待处理/进行中/已完成/在线工牌数)
   - QuickStatsRespDTO: 快速统计 DTO

5. 运营区域管理(Ops Biz)
   - OpsBusAreaService/Impl: 区域 CRUD(支持树形结构、分页查询)
   - AreaDeviceRelationService/Impl: 区域设备关联管理(绑定/解绑/批量更新)
   - OpsBusAreaMapper/AreaDeviceRelationMapper: 扩展 MyBatis 批量方法
   - 7 个 VO 类:CreateReqVO/UpdateReqVO/PageReqVO/RespVO/BindReqVO/RelationRespVO/DeviceUpdateReqVO

6. 前端 Controller(Ops Server)
   - OpsBusAreaController: 区域管理 REST API(11 个接口)
   - AreaDeviceRelationController: 设备关联 REST API(8 个接口)
   - CleanBadgeController: 工牌管理 REST API(5 个接口)
   - CleanDashboardController: 仪表盘 REST API(1 个接口)
   - CleanDeviceController: 设备管理 REST API(2 个接口)
   - CleanWorkOrderController: 工单管理 REST API(2 个接口)

7. 测试覆盖
   - OpsBusAreaServiceTest: 区域服务测试(284 行)
   - AreaDeviceRelationServiceTest: 设备关联测试(240 行)
   - OpsBusAreaControllerTest: 区域 Controller 测试(186 行)
   - AreaDeviceRelationControllerTest: 设备关联 Controller 测试(182 行)

8. API 层扩展
   - ErrorCodeConstants: 错误码常量(区域、设备关联)
   - NotifyTypeEnum: 通知类型枚举(语音、文本、震动)
   - 4 个 Badge/Order DTO: BadgeStatusRespDTO/BadgeRealtimeStatusRespDTO/OrderTimelineRespDTO

9. RPC 配置
   - RpcConfiguration: 注入 IotDeviceQueryApi

影响模块:Ops API、Ops Biz、Ops Server、Ops Environment Biz、IoT API、IoT Server

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-02-02 22:42:45 +08:00
parent bdf5b640b0
commit 955c825e2c
43 changed files with 3312 additions and 1 deletions

View File

@@ -0,0 +1,76 @@
package com.viewsh.module.ops.controller.admin.area;
import com.viewsh.framework.apilog.core.annotation.ApiAccessLog;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.module.ops.dal.dataobject.vo.area.AreaDeviceBindReqVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.AreaDeviceRelationRespVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.AreaDeviceUpdateReqVO;
import com.viewsh.module.ops.service.area.AreaDeviceRelationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.viewsh.framework.apilog.core.enums.OperateTypeEnum.*;
import static com.viewsh.framework.common.pojo.CommonResult.success;
/**
* 管理后台 - 区域设备关联管理
*
* @author lzh
*/
@Tag(name = "管理后台 - 区域设备关联")
@Slf4j
@RestController
@RequestMapping("/admin-api/ops/area/device-relation")
@Validated
public class AreaDeviceRelationController {
@Resource
private AreaDeviceRelationService areaDeviceRelationService;
@GetMapping("/list")
@Operation(summary = "查询区域已绑定设备列表")
@Parameter(name = "areaId", description = "区域ID", required = true)
// @PreAuthorize("@ss.hasPermission('ops:area:query')")
public CommonResult<List<AreaDeviceRelationRespVO>> listByAreaId(@RequestParam("areaId") Long areaId) {
List<AreaDeviceRelationRespVO> list = areaDeviceRelationService.listByAreaId(areaId);
return success(list);
}
@PostMapping("/bind")
@Operation(summary = "绑定设备到区域")
// @PreAuthorize("@ss.hasPermission('ops:area:bind-device')")
@ApiAccessLog(operateType = CREATE)
public CommonResult<Long> bindDevice(@Valid @RequestBody AreaDeviceBindReqVO bindReq) {
Long relationId = areaDeviceRelationService.bindDevice(bindReq);
return success(relationId);
}
@PutMapping("/update")
@Operation(summary = "更新设备关联配置")
// @PreAuthorize("@ss.hasPermission('ops:area:config-device')")
@ApiAccessLog(operateType = UPDATE)
public CommonResult<Boolean> updateRelation(@Valid @RequestBody AreaDeviceUpdateReqVO updateReq) {
areaDeviceRelationService.updateRelation(updateReq);
return success(true);
}
@DeleteMapping("/remove")
@Operation(summary = "解除设备绑定")
@Parameter(name = "id", description = "关联ID", required = true)
// @PreAuthorize("@ss.hasPermission('ops:area:bind-device')")
@ApiAccessLog(operateType = DELETE)
public CommonResult<Boolean> unbindDevice(@RequestParam("id") Long id) {
Boolean result = areaDeviceRelationService.unbindDevice(id);
return success(result);
}
}

View File

@@ -0,0 +1,85 @@
package com.viewsh.module.ops.controller.admin.area;
import com.viewsh.framework.apilog.core.annotation.ApiAccessLog;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaCreateReqVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaPageReqVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaRespVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaUpdateReqVO;
import com.viewsh.module.ops.service.area.OpsBusAreaService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.viewsh.framework.apilog.core.enums.OperateTypeEnum.*;
import static com.viewsh.framework.common.pojo.CommonResult.success;
/**
* 管理后台 - 业务区域管理
*
* @author lzh
*/
@Tag(name = "管理后台 - 业务区域")
@Slf4j
@RestController
@RequestMapping("/admin-api/ops/area")
@Validated
public class OpsBusAreaController {
@Resource
private OpsBusAreaService opsBusAreaService;
@GetMapping("/tree")
@Operation(summary = "获取区域树(平铺列表,由前端自行组装)")
// @PreAuthorize("@ss.hasPermission('ops:area:query')")
public CommonResult<List<OpsBusAreaRespVO>> getAreaTree(OpsBusAreaPageReqVO reqVO) {
List<OpsBusAreaRespVO> list = opsBusAreaService.getAreaTree(reqVO);
return success(list);
}
@GetMapping("/get")
@Operation(summary = "获取区域详情")
@Parameter(name = "id", description = "区域ID", required = true)
// @PreAuthorize("@ss.hasPermission('ops:area:query')")
public CommonResult<OpsBusAreaRespVO> getArea(@RequestParam("id") Long id) {
OpsBusAreaRespVO area = opsBusAreaService.getArea(id);
return success(area);
}
@PostMapping("/create")
@Operation(summary = "新增区域")
// @PreAuthorize("@ss.hasPermission('ops:area:create')")
@ApiAccessLog(operateType = CREATE)
public CommonResult<Long> createArea(@Valid @RequestBody OpsBusAreaCreateReqVO createReq) {
Long areaId = opsBusAreaService.createArea(createReq);
return success(areaId);
}
@PutMapping("/update")
@Operation(summary = "更新区域")
// @PreAuthorize("@ss.hasPermission('ops:area:update')")
@ApiAccessLog(operateType = UPDATE)
public CommonResult<Boolean> updateArea(@Valid @RequestBody OpsBusAreaUpdateReqVO updateReq) {
opsBusAreaService.updateArea(updateReq);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除区域")
@Parameter(name = "id", description = "区域ID", required = true)
// @PreAuthorize("@ss.hasPermission('ops:area:delete')")
@ApiAccessLog(operateType = DELETE)
public CommonResult<Boolean> deleteArea(@RequestParam("id") Long id) {
Boolean result = opsBusAreaService.deleteArea(id);
return success(result);
}
}

View File

@@ -0,0 +1,60 @@
package com.viewsh.module.ops.controller.admin.clean;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.module.ops.api.clean.BadgeRealtimeStatusRespDTO;
import com.viewsh.module.ops.api.clean.BadgeStatusRespDTO;
import com.viewsh.module.ops.environment.dal.dataobject.BadgeNotifyReqDTO;
import com.viewsh.module.ops.environment.service.badge.CleanBadgeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.viewsh.framework.common.pojo.CommonResult.success;
/**
* 管理后台 - 保洁工牌 Controller
*
* @author lzh
*/
@Tag(name = "管理后台 - 保洁工牌")
@Slf4j
@RestController
@RequestMapping("/ops/clean/badge")
@Validated
public class CleanBadgeController {
@Resource
private CleanBadgeService cleanBadgeService;
@GetMapping("/list")
@Operation(summary = "工牌实时状态列表")
@Parameter(name = "areaId", description = "区域ID", required = false)
@Parameter(name = "status", description = "状态筛选IDLE/BUSY/OFFLINE/PAUSED", required = false)
@PreAuthorize("@ss.hasPermission('ops:clean:badge:query')")
public CommonResult<List<BadgeStatusRespDTO>> getBadgeList(
@RequestParam(value = "areaId", required = false) Long areaId,
@RequestParam(value = "status", required = false) String status) {
List<BadgeStatusRespDTO> result = cleanBadgeService.getBadgeStatusList(areaId, status);
return success(result);
}
@GetMapping("/realtime/{badgeId}")
@Operation(summary = "工牌实时状态详情")
@Parameter(name = "badgeId", description = "工牌设备ID", required = true)
@PreAuthorize("@ss.hasPermission('ops:clean:badge:query')")
public CommonResult<BadgeRealtimeStatusRespDTO> getBadgeRealtimeStatus(
@PathVariable("badgeId") Long badgeId) {
BadgeRealtimeStatusRespDTO result = cleanBadgeService.getBadgeRealtimeStatus(badgeId);
return success(result);
}
}

View File

@@ -0,0 +1,38 @@
package com.viewsh.module.ops.controller.admin.clean;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.module.ops.api.clean.QuickStatsRespDTO;
import com.viewsh.module.ops.environment.service.dashboard.CleanDashboardService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static com.viewsh.framework.common.pojo.CommonResult.success;
/**
* 管理后台 - 保洁仪表盘 Controller
*
* @author lzh
*/
@Tag(name = "管理后台 - 保洁仪表盘")
@Slf4j
@RestController
@RequestMapping("/ops/clean/dashboard")
@Validated
public class CleanDashboardController {
@Resource
private CleanDashboardService cleanDashboardService;
@GetMapping("/quick-stats")
@Operation(summary = "快速统计")
@PreAuthorize("@ss.hasPermission('ops:clean:dashboard:query')")
public CommonResult<QuickStatsRespDTO> getQuickStats() {
QuickStatsRespDTO result = cleanDashboardService.getQuickStats();
return success(result);
}
}

View File

@@ -0,0 +1,41 @@
package com.viewsh.module.ops.controller.admin.clean;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.module.ops.environment.dal.dataobject.BadgeNotifyReqDTO;
import com.viewsh.module.ops.environment.service.badge.CleanBadgeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static com.viewsh.framework.common.pojo.CommonResult.success;
/**
* 管后台 - 保洁设备 Controller
*
* @author lzh
*/
@Tag(name = "管理后台 - 保洁设备")
@Slf4j
@RestController
@RequestMapping("/ops/clean/device")
@Validated
public class CleanDeviceController {
@Resource
private CleanBadgeService cleanBadgeService;
@PostMapping("/notify")
@Operation(summary = "发送工牌通知(语音/震动)")
@PreAuthorize("@ss.hasPermission('ops:clean:device:notify')")
public CommonResult<Boolean> sendBadgeNotify(
@Valid @RequestBody BadgeNotifyReqDTO req) {
cleanBadgeService.sendBadgeNotify(req);
return success(true);
}
}

View File

@@ -0,0 +1,65 @@
package com.viewsh.module.ops.controller.admin.clean;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.module.ops.api.clean.OrderTimelineRespDTO;
import com.viewsh.module.ops.environment.dal.dataobject.ManualCompleteOrderReqDTO;
import com.viewsh.module.ops.environment.dal.dataobject.UpgradePriorityReqDTO;
import com.viewsh.module.ops.environment.service.cleanorder.CleanWorkOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static com.viewsh.framework.common.pojo.CommonResult.success;
/**
* 管理后台 - 保洁工单 Controller
*
* @author lzh
*/
@Tag(name = "管理后台 - 保洁工单")
@Slf4j
@RestController
@RequestMapping("/ops/clean/order")
@Validated
public class CleanWorkOrderController {
@Resource
private CleanWorkOrderService cleanWorkOrderService;
@GetMapping("/timeline/{orderId}")
@Operation(summary = "工单时间轴")
@Parameter(name = "orderId", description = "工单ID", required = true)
@PreAuthorize("@ss.hasPermission('ops:clean:order:query')")
public CommonResult<OrderTimelineRespDTO> getOrderTimeline(
@PathVariable("orderId") Long orderId) {
OrderTimelineRespDTO result = cleanWorkOrderService.getOrderTimeline(orderId);
return success(result);
}
@PostMapping("/manual-complete")
@Operation(summary = "手动完成工单")
@PreAuthorize("@ss.hasPermission('ops:clean:order:complete')")
public CommonResult<Boolean> manualCompleteOrder(
@Valid @RequestBody ManualCompleteOrderReqDTO req) {
cleanWorkOrderService.manualCompleteOrder(req);
return success(true);
}
@PostMapping("/upgrade-priority")
@Operation(summary = "升级工单优先级")
@PreAuthorize("@ss.hasPermission('ops:clean:order:upgrade')")
public CommonResult<Boolean> upgradePriority(
@Valid @RequestBody UpgradePriorityReqDTO req) {
cleanWorkOrderService.upgradePriority(req);
return success(true);
}
}

View File

@@ -1,6 +1,7 @@
package com.viewsh.module.ops.framework.rpc.config;
import com.viewsh.module.iot.api.device.IotDeviceControlApi;
import com.viewsh.module.iot.api.device.IotDeviceQueryApi;
import com.viewsh.module.iot.api.device.IotDeviceStatusQueryApi;
import com.viewsh.module.system.api.notify.NotifyMessageSendApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
@@ -10,6 +11,7 @@ import org.springframework.context.annotation.Configuration;
@EnableFeignClients(clients = {
NotifyMessageSendApi.class,
IotDeviceControlApi.class,
IotDeviceQueryApi.class,
IotDeviceStatusQueryApi.class
})
public class RpcConfiguration {

View File

@@ -0,0 +1,182 @@
package com.viewsh.module.ops.controller.admin.area;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.viewsh.module.ops.dal.dataobject.vo.area.AreaDeviceBindReqVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.AreaDeviceRelationRespVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.AreaDeviceUpdateReqVO;
import com.viewsh.module.ops.service.area.AreaDeviceRelationService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 区域设备关联管理 Controller 测试
*
* @author lzh
*/
@ExtendWith(MockitoExtension.class)
class AreaDeviceRelationControllerTest {
@Mock
private AreaDeviceRelationService areaDeviceRelationService;
@InjectMocks
private AreaDeviceRelationController controller;
private MockMvc mockMvc;
private ObjectMapper objectMapper;
private AreaDeviceRelationRespVO testRelationVO;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
objectMapper = new ObjectMapper();
testRelationVO = new AreaDeviceRelationRespVO();
testRelationVO.setId(1L);
testRelationVO.setAreaId(100L);
testRelationVO.setAreaName("A栋3层电梯厅");
testRelationVO.setDeviceId(50001L);
testRelationVO.setDeviceKey("TRAFFIC_COUNTER_001");
testRelationVO.setDeviceName("客流计数器001");
testRelationVO.setProductId(10L);
testRelationVO.setProductKey("traffic_counter_v1");
testRelationVO.setProductName("客流计数器");
testRelationVO.setRelationType("TRAFFIC_COUNTER");
testRelationVO.setConfigData(new HashMap<>());
testRelationVO.setEnabled(true);
}
@Test
void testListByAreaId_Success() throws Exception {
// Given
when(areaDeviceRelationService.listByAreaId(100L))
.thenReturn(Arrays.asList(testRelationVO));
// When & Then
mockMvc.perform(get("/admin-api/ops/area/device-relation/list")
.param("areaId", "100")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isArray())
.andExpect(jsonPath("$.data[0].deviceId").value(50001))
.andExpect(jsonPath("$.data[0].deviceName").value("客流计数器001"));
verify(areaDeviceRelationService).listByAreaId(100L);
}
@Test
void testListByAreaId_Empty_Success() throws Exception {
// Given
when(areaDeviceRelationService.listByAreaId(999L))
.thenReturn(Arrays.asList());
// When & Then
mockMvc.perform(get("/admin-api/ops/area/device-relation/list")
.param("areaId", "999")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isArray())
.andExpect(jsonPath("$.data").isEmpty());
}
@Test
void testBindDevice_Success() throws Exception {
// Given
AreaDeviceBindReqVO bindReq = new AreaDeviceBindReqVO();
bindReq.setAreaId(100L);
bindReq.setDeviceId(50001L);
bindReq.setRelationType("TRAFFIC_COUNTER");
Map<String, Object> config = new HashMap<>();
config.put("threshold", 100);
bindReq.setConfigData(config);
when(areaDeviceRelationService.bindDevice(any(AreaDeviceBindReqVO.class)))
.thenReturn(1L);
// When & Then
mockMvc.perform(post("/admin-api/ops/area/device-relation/bind")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(bindReq)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(1));
verify(areaDeviceRelationService).bindDevice(any(AreaDeviceBindReqVO.class));
}
@Test
void testUpdateRelation_Success() throws Exception {
// Given
AreaDeviceUpdateReqVO updateReq = new AreaDeviceUpdateReqVO();
updateReq.setId(1L);
Map<String, Object> config = new HashMap<>();
config.put("threshold", 150);
updateReq.setConfigData(config);
// When & Then
mockMvc.perform(put("/admin-api/ops/area/device-relation/update")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(updateReq)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0));
verify(areaDeviceRelationService).updateRelation(any(AreaDeviceUpdateReqVO.class));
}
@Test
void testUnbindDevice_Success() throws Exception {
// Given
when(areaDeviceRelationService.unbindDevice(1L)).thenReturn(true);
// When & Then
mockMvc.perform(delete("/admin-api/ops/area/device-relation/remove")
.param("id", "1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true));
verify(areaDeviceRelationService).unbindDevice(1L);
}
@Test
void testUnbindDevice_NotFound() throws Exception {
// Given
when(areaDeviceRelationService.unbindDevice(999L)).thenReturn(false);
// When & Then
mockMvc.perform(delete("/admin-api/ops/area/device-relation/remove")
.param("id", "999")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(false));
verify(areaDeviceRelationService).unbindDevice(999L);
}
}

View File

@@ -0,0 +1,186 @@
package com.viewsh.module.ops.controller.admin.area;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaCreateReqVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaPageReqVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaRespVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaUpdateReqVO;
import com.viewsh.module.ops.service.area.OpsBusAreaService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.Arrays;
import java.util.Collections;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 业务区域管理 Controller 测试
*
* @author lzh
*/
@ExtendWith(MockitoExtension.class)
class OpsBusAreaControllerTest {
@Mock
private OpsBusAreaService opsBusAreaService;
@InjectMocks
private OpsBusAreaController controller;
private MockMvc mockMvc;
private ObjectMapper objectMapper;
private OpsBusAreaRespVO testAreaResp;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
objectMapper = new ObjectMapper();
testAreaResp = new OpsBusAreaRespVO();
testAreaResp.setId(100L);
testAreaResp.setParentId(null);
testAreaResp.setParentPath(null);
testAreaResp.setAreaName("A园区");
testAreaResp.setAreaCode("PARK_A");
testAreaResp.setAreaType("PARK");
testAreaResp.setIsActive(true);
testAreaResp.setSort(1);
}
@Test
void testGetAreaTree_Success() throws Exception {
// Given
when(opsBusAreaService.getAreaTree(any(OpsBusAreaPageReqVO.class)))
.thenReturn(Arrays.asList(testAreaResp));
// When & Then
mockMvc.perform(get("/admin-api/ops/area/tree")
.param("areaType", "PARK")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isArray())
.andExpect(jsonPath("$.data[0].areaName").value("A园区"));
verify(opsBusAreaService).getAreaTree(any(OpsBusAreaPageReqVO.class));
}
@Test
void testGetAreaTree_Empty_Success() throws Exception {
// Given
when(opsBusAreaService.getAreaTree(any(OpsBusAreaPageReqVO.class)))
.thenReturn(Collections.emptyList());
// When & Then
mockMvc.perform(get("/admin-api/ops/area/tree")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isArray())
.andExpect(jsonPath("$.data").isEmpty());
}
@Test
void testGetArea_Success() throws Exception {
// Given
when(opsBusAreaService.getArea(100L)).thenReturn(testAreaResp);
// When & Then
mockMvc.perform(get("/admin-api/ops/area/get")
.param("id", "100")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data.id").value(100))
.andExpect(jsonPath("$.data.areaName").value("A园区"));
verify(opsBusAreaService).getArea(100L);
}
@Test
void testCreateArea_Success() throws Exception {
// Given
OpsBusAreaCreateReqVO createReq = new OpsBusAreaCreateReqVO();
createReq.setAreaName("A园区");
createReq.setAreaCode("PARK_A");
createReq.setAreaType("PARK");
when(opsBusAreaService.createArea(any(OpsBusAreaCreateReqVO.class)))
.thenReturn(100L);
// When & Then
mockMvc.perform(post("/admin-api/ops/area/create")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(createReq)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(100));
verify(opsBusAreaService).createArea(any(OpsBusAreaCreateReqVO.class));
}
@Test
void testUpdateArea_Success() throws Exception {
// Given
OpsBusAreaUpdateReqVO updateReq = new OpsBusAreaUpdateReqVO();
updateReq.setId(100L);
updateReq.setAreaName("A园区更新");
// When & Then
mockMvc.perform(put("/admin-api/ops/area/update")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(updateReq)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0));
verify(opsBusAreaService).updateArea(any(OpsBusAreaUpdateReqVO.class));
}
@Test
void testDeleteArea_Success() throws Exception {
// Given
when(opsBusAreaService.deleteArea(100L)).thenReturn(true);
// When & Then
mockMvc.perform(delete("/admin-api/ops/area/delete")
.param("id", "100")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true));
verify(opsBusAreaService).deleteArea(100L);
}
@Test
void testDeleteArea_NotFound() throws Exception {
// Given
when(opsBusAreaService.deleteArea(999L)).thenReturn(false);
// When & Then
mockMvc.perform(delete("/admin-api/ops/area/delete")
.param("id", "999")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(false));
verify(opsBusAreaService).deleteArea(999L);
}
}