feat(channel): add channel account event receiver endpoint

This commit is contained in:
2026-07-10 12:24:00 +08:00
parent 1fabc04d70
commit 302e0ab442
4 changed files with 127 additions and 1 deletions

View File

@@ -18,7 +18,8 @@ public class AuthInterceptor implements HandlerInterceptor {
|| ("POST".equals(method) && isExactPath(path, "/api/v1/account/tenant"))
|| ("POST".equals(method) && isExactPath(path, "/api/v1/account/user"))
|| isExactPath(path, "/api/v1/health")
|| ("POST".equals(method) && isExactPath(path, "/api/v1/channel-account/heartbeat"))) {
|| ("POST".equals(method) && isExactPath(path, "/api/v1/channel-account/heartbeat"))
|| ("POST".equals(method) && isExactPath(path, "/api/v1/channel-account/event"))) {
return true;
}

View File

@@ -0,0 +1,25 @@
package com.sino.mci.channel.controller;
import com.sino.mci.channel.dto.ChannelAccountEventRequest;
import com.sino.mci.channel.service.ChannelAccountEventService;
import com.sino.mci.shared.web.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/channel-account")
@RequiredArgsConstructor
public class ChannelAccountEventController {
private final ChannelAccountEventService eventService;
@PostMapping("/event")
public ApiResponse<?> receiveEvent(@Valid @RequestBody ChannelAccountEventRequest request) {
eventService.handleEvent(request);
return ApiResponse.ok(null);
}
}

View File

@@ -0,0 +1,87 @@
package com.sino.mci.channel.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sino.mci.channel.dto.ChannelAccountEventRequest;
import com.sino.mci.channel.entity.ChannelAccount;
import com.sino.mci.channel.repository.ChannelAccountMapper;
import com.sino.mci.channel.repository.ChannelEventRecordMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
@Sql(scripts = "/sql/test-channel-account.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
class ChannelAccountEventControllerTest {
@Autowired
private MockMvc mockMvc;
private final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private ChannelAccountMapper accountMapper;
@Autowired
private ChannelEventRecordMapper eventRecordMapper;
@Test
void shouldUpdateAccountOnHeartbeat() throws Exception {
ChannelAccountEventRequest request = new ChannelAccountEventRequest();
request.setEventId("evt-001");
request.setEventType("heartbeat");
request.setAccountId("wx-bot-01");
request.setTenantCode("rescue");
request.setData(Map.of(
"login_status", "success",
"online_status", "online",
"risk_level", 10));
mockMvc.perform(post("/msg-platform/api/v1/channel-account/event")
.contextPath("/msg-platform")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0));
ChannelAccount account = accountMapper.selectById(1L);
assertThat(account.getOnlineStatus()).isEqualTo(1);
assertThat(account.getRiskLevel()).isEqualTo(10);
assertThat(eventRecordMapper.selectById("evt-001")).isNotNull();
}
@Test
void shouldIgnoreDuplicateEvent() throws Exception {
ChannelAccountEventRequest request = new ChannelAccountEventRequest();
request.setEventId("evt-dup-001");
request.setEventType("heartbeat");
request.setAccountId("wx-bot-01");
request.setTenantCode("rescue");
request.setData(Map.of("online_status", "online"));
mockMvc.perform(post("/msg-platform/api/v1/channel-account/event")
.contextPath("/msg-platform")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk());
mockMvc.perform(post("/msg-platform/api/v1/channel-account/event")
.contextPath("/msg-platform")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk());
assertThat(eventRecordMapper.selectCount(null)).isEqualTo(1);
}
}

View File

@@ -0,0 +1,13 @@
SET FOREIGN_KEY_CHECKS = 0;
DELETE FROM mci_channel_event_record;
DELETE FROM mci_channel_account;
DELETE FROM mci_tenant;
SET FOREIGN_KEY_CHECKS = 1;
INSERT INTO mci_tenant (id, tenant_code, tenant_name, status, create_time)
VALUES (1, 'rescue', '救援', 1, NOW());
INSERT INTO mci_channel_account (id, tenant_code, account_type, channel_type, account_id, account_name, login_status, online_status, risk_level, status, create_time)
VALUES (1, 'rescue', 3, 'wechat_personal', 'wx-bot-01', '测试号', 0, 0, 0, 1, NOW());