refactor(security): 清理 security-biz 中旧的 vsp 文件,更新测试

- 删除 security-biz/integration/vsp/ 旧目录(已迁移至 ops-biz/infrastructure/vsp/)
- 更新 SecurityOrderEventListenerVspTest import 路径

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-03-25 11:30:24 +08:00
parent fbc88cd620
commit c7bdf7d773
9 changed files with 4 additions and 351 deletions

View File

@@ -1,21 +0,0 @@
package com.viewsh.module.ops.security.integration.vsp;
import com.viewsh.module.ops.security.integration.vsp.dto.VspSendCardReqDTO;
import com.viewsh.module.ops.security.integration.vsp.dto.VspSyncStatusReqDTO;
public interface VspNotifyClient {
/**
* 发送企微工单卡片
*
* @param req 卡片内容
*/
void sendCard(VspSendCardReqDTO req);
/**
* 同步工单状态到 vsp-service
*
* @param req 状态信息
*/
void syncStatus(VspSyncStatusReqDTO req);
}

View File

@@ -1,65 +0,0 @@
package com.viewsh.module.ops.security.integration.vsp;
import com.viewsh.module.ops.security.integration.vsp.config.VspNotifyProperties;
import com.viewsh.module.ops.security.integration.vsp.dto.VspResponseDTO;
import com.viewsh.module.ops.security.integration.vsp.dto.VspSendCardReqDTO;
import com.viewsh.module.ops.security.integration.vsp.dto.VspSyncStatusReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Component
public class VspNotifyClientImpl implements VspNotifyClient {
private final RestTemplate restTemplate;
private final VspNotifyProperties properties;
public VspNotifyClientImpl(@Qualifier("vspRestTemplate") RestTemplate restTemplate,
VspNotifyProperties properties) {
this.restTemplate = restTemplate;
this.properties = properties;
}
@Override
public void sendCard(VspSendCardReqDTO req) {
if (!properties.isEnabled()) return;
String url = properties.getBaseUrl() + "/api/wechat/notify/send-card";
executeWithRetry(url, req, "sendCard", req.getOrderId(), req.getAlarmId());
}
@Override
public void syncStatus(VspSyncStatusReqDTO req) {
if (!properties.isEnabled()) return;
String url = properties.getBaseUrl() + "/api/wechat/notify/sync-status";
executeWithRetry(url, req, "syncStatus", req.getOrderId(), req.getAlarmId());
}
private void executeWithRetry(String url, Object req,
String action, String orderId, String alarmId) {
for (int i = 0; i <= properties.getMaxRetry(); i++) {
try {
VspResponseDTO resp = restTemplate.postForObject(url, req, VspResponseDTO.class);
if (resp != null && resp.isSuccess()) {
log.info("[{}] vsp通知成功, orderId={}, alarmId={}", action, orderId, alarmId);
return;
}
// 业务失败不重试
log.warn("[{}] vsp返回失败: code={}, msg={}, orderId={}",
action, resp != null ? resp.getCode() : null,
resp != null ? resp.getMsg() : null, orderId);
return;
} catch (RestClientException e) {
if (i < properties.getMaxRetry()) {
log.warn("[{}] 第{}次调用失败, orderId={}, 将重试",
action, i + 1, orderId);
} else {
log.error("[{}] 调用vsp失败, 重试耗尽, orderId={}, alarmId={}",
action, orderId, alarmId, e);
}
}
}
}
}

View File

@@ -1,21 +0,0 @@
package com.viewsh.module.ops.security.integration.vsp.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
@EnableConfigurationProperties(VspNotifyProperties.class)
public class VspNotifyConfig {
@Bean("vspRestTemplate")
public RestTemplate vspRestTemplate(VspNotifyProperties properties) {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(properties.getConnectTimeout());
factory.setReadTimeout(properties.getReadTimeout());
RestTemplate restTemplate = new RestTemplate(factory);
return restTemplate;
}
}

View File

@@ -1,19 +0,0 @@
package com.viewsh.module.ops.security.integration.vsp.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "viewsh.ops.vsp-notify")
public class VspNotifyProperties {
/** 是否启用 vsp 通知 */
private boolean enabled = true;
/** vsp-service 基础 URL */
private String baseUrl = "http://124.221.55.225:8000";
/** 连接超时(ms) */
private int connectTimeout = 5000;
/** 读取超时(ms) */
private int readTimeout = 10000;
/** 最大重试次数(网络异常时) */
private int maxRetry = 2;
}

View File

@@ -1,13 +0,0 @@
package com.viewsh.module.ops.security.integration.vsp.dto;
import lombok.Data;
@Data
public class VspResponseDTO {
private Integer code;
private String msg;
public boolean isSuccess() {
return code != null && code == 0;
}
}

View File

@@ -1,28 +0,0 @@
package com.viewsh.module.ops.security.integration.vsp.dto;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Data
@Builder
public class VspSendCardReqDTO {
/** 关联告警ID */
private String alarmId;
/** 工单ID */
private String orderId;
/** 接收人企微 userId 列表 */
private List<String> userIds;
/** 卡片标题(告警类型) */
private String title;
/** 区域名称 */
private String areaName;
/** 摄像头名称 */
private String cameraName;
/** 告警时间,格式 MM-dd HH:mm */
private String eventTime;
/** 告警级别0紧急 1重要 2普通 3轻微 */
private Integer level;
/** 告警截图URL */
private String snapshotUrl;
}

View File

@@ -1,19 +0,0 @@
package com.viewsh.module.ops.security.integration.vsp.dto;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class VspSyncStatusReqDTO {
/** 关联告警ID */
private String alarmId;
/** 工单ID */
private String orderId;
/** 工单状态 */
private String status;
/** 操作人企微 userId */
private String operator;
/** 备注 */
private String remark;
}

View File

@@ -10,11 +10,11 @@ import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder;
import com.viewsh.module.ops.security.dal.dataobject.workorder.OpsOrderSecurityExtDO;
import com.viewsh.module.ops.security.dal.mysql.workorder.OpsOrderSecurityExtMapper;
import com.viewsh.module.ops.security.integration.vsp.VspNotifyClient;
import com.viewsh.module.ops.security.integration.vsp.dto.VspSendCardReqDTO;
import com.viewsh.module.ops.security.integration.vsp.dto.VspSyncStatusReqDTO;
import com.viewsh.module.ops.infrastructure.vsp.VspNotifyClient;
import com.viewsh.module.ops.infrastructure.vsp.dto.VspSendCardReqDTO;
import com.viewsh.module.ops.infrastructure.vsp.dto.VspSyncStatusReqDTO;
import com.viewsh.module.ops.service.area.OpsBusAreaService;
import com.viewsh.module.ops.controller.admin.area.vo.OpsBusAreaRespVO;
import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaRespVO;
import com.viewsh.module.system.api.social.SocialUserApi;
import com.viewsh.module.system.api.social.dto.SocialUserRespDTO;
import org.junit.jupiter.api.Test;

View File

@@ -1,161 +0,0 @@
package com.viewsh.module.ops.security.integration.vsp;
import com.viewsh.module.ops.security.integration.vsp.config.VspNotifyProperties;
import com.viewsh.module.ops.security.integration.vsp.dto.VspResponseDTO;
import com.viewsh.module.ops.security.integration.vsp.dto.VspSendCardReqDTO;
import com.viewsh.module.ops.security.integration.vsp.dto.VspSyncStatusReqDTO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
/**
* VspNotifyClientImpl 单元测试
*
* @author lzh
*/
@ExtendWith(MockitoExtension.class)
public class VspNotifyClientImplTest {
@Mock
private RestTemplate restTemplate;
private VspNotifyProperties properties;
private VspNotifyClientImpl client;
private static final String BASE_URL = "http://localhost:8000";
@BeforeEach
void setUp() {
properties = new VspNotifyProperties();
properties.setEnabled(true);
properties.setBaseUrl(BASE_URL);
properties.setMaxRetry(2);
client = new VspNotifyClientImpl(restTemplate, properties);
}
// ==================== sendCard 测试 ====================
@Test
void sendCard_success() {
VspSendCardReqDTO req = buildSendCardReq();
VspResponseDTO resp = new VspResponseDTO();
resp.setCode(0);
resp.setMsg("ok");
when(restTemplate.postForObject(anyString(), any(), eq(VspResponseDTO.class)))
.thenReturn(resp);
// 执行
client.sendCard(req);
// 验证只调用一次
verify(restTemplate, times(1)).postForObject(
eq(BASE_URL + "/api/wechat/notify/send-card"),
eq(req),
eq(VspResponseDTO.class));
}
@Test
void sendCard_retryOnNetworkError() {
VspSendCardReqDTO req = buildSendCardReq();
when(restTemplate.postForObject(anyString(), any(), eq(VspResponseDTO.class)))
.thenThrow(new RestClientException("Connection refused"));
// 执行
client.sendCard(req);
// maxRetry=2总共调用 1 + 2 = 3 次
verify(restTemplate, times(3)).postForObject(
eq(BASE_URL + "/api/wechat/notify/send-card"),
eq(req),
eq(VspResponseDTO.class));
}
@Test
void sendCard_noRetryOnBusinessError() {
VspSendCardReqDTO req = buildSendCardReq();
VspResponseDTO resp = new VspResponseDTO();
resp.setCode(-1);
resp.setMsg("业务失败");
when(restTemplate.postForObject(anyString(), any(), eq(VspResponseDTO.class)))
.thenReturn(resp);
// 执行
client.sendCard(req);
// 业务失败不重试,只调用一次
verify(restTemplate, times(1)).postForObject(anyString(), any(), eq(VspResponseDTO.class));
}
@Test
void sendCard_disabled() {
properties.setEnabled(false);
VspSendCardReqDTO req = buildSendCardReq();
// 执行
client.sendCard(req);
// 未启用时不调用 RestTemplate
verify(restTemplate, never()).postForObject(anyString(), any(), any());
}
// ==================== syncStatus 测试 ====================
@Test
void syncStatus_success() {
VspSyncStatusReqDTO req = VspSyncStatusReqDTO.builder()
.alarmId("alarm-001")
.orderId("10001")
.status("confirmed")
.operator("wechat_user_1")
.remark("确认接单")
.build();
VspResponseDTO resp = new VspResponseDTO();
resp.setCode(0);
resp.setMsg("ok");
when(restTemplate.postForObject(anyString(), any(), eq(VspResponseDTO.class)))
.thenReturn(resp);
// 执行
client.syncStatus(req);
// 验证使用 sync-status URL
verify(restTemplate, times(1)).postForObject(
eq(BASE_URL + "/api/wechat/notify/sync-status"),
eq(req),
eq(VspResponseDTO.class));
}
// ==================== 辅助方法 ====================
private VspSendCardReqDTO buildSendCardReq() {
return VspSendCardReqDTO.builder()
.alarmId("alarm-001")
.orderId("10001")
.userIds(List.of("wechat_user_1"))
.title("入侵告警")
.areaName("A栋大堂")
.cameraName("大堂摄像头")
.eventTime("03-24 10:00")
.level(1)
.snapshotUrl("http://example.com/snapshot.jpg")
.build();
}
}