feat: add M7 DeviceController + Callback device event linking

This commit is contained in:
2026-05-25 01:00:43 +08:00
parent f94f2b91e8
commit 75e6d6d5ec
2 changed files with 103 additions and 1 deletions
@@ -0,0 +1,80 @@
package cn.craftlabs.platform.api.device;
import cn.craftlabs.platform.api.service.DeviceService;
import cn.craftlabs.platform.api.web.dto.DeviceBindingResponse;
import cn.craftlabs.platform.api.web.dto.DeviceCreateRequest;
import cn.craftlabs.platform.api.web.dto.DeviceResponse;
import cn.craftlabs.platform.api.web.dto.DeviceSwapRequestDto;
import cn.craftlabs.platform.api.web.dto.PageResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@RestController
@RequestMapping("/api/v1/devices")
@Validated
public class DeviceController {
private final DeviceService deviceService;
public DeviceController(DeviceService deviceService) {
this.deviceService = deviceService;
}
@GetMapping
public PageResponse<DeviceResponse> list(
@RequestParam(value = "page", defaultValue = "0") @Min(0) int page,
@RequestParam(value = "size", defaultValue = "20") @Min(1) @Max(200) int size,
@RequestParam(value = "customerId", required = false) Long customerId,
@RequestParam(value = "site", required = false) String site,
@RequestParam(value = "snCode", required = false) String snCode) {
return deviceService.listDevices(page, size, customerId, site, snCode);
}
@GetMapping("/{id}")
public DeviceResponse get(@PathVariable("id") long id) {
DeviceResponse result = deviceService.getDevice(id);
if (result == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Device not found: " + id);
}
return result;
}
@PostMapping
public DeviceResponse create(@Valid @RequestBody DeviceCreateRequest request) {
return deviceService.createDevice(request);
}
@PutMapping("/{id}")
public DeviceResponse update(@PathVariable("id") long id, @Valid @RequestBody DeviceCreateRequest request) {
DeviceResponse result = deviceService.updateDevice(id, request);
if (result == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Device not found: " + id);
}
return result;
}
@GetMapping("/{id}/bindings")
public List<DeviceBindingResponse> bindings(@PathVariable("id") long id) {
return deviceService.getBindings(id);
}
@PostMapping("/{id}/swap-request")
public void swapRequest(@PathVariable("id") long id, @Valid @RequestBody DeviceSwapRequestDto request) {
request.setOldDeviceId(id);
deviceService.createSwapRequest(request);
}
}
@@ -33,16 +33,19 @@ public class CallbackEventIngestService {
private final PlatformLicenseSnMapper licenseSnMapper;
private final PlatformContractLineMapper contractLineMapper;
private final ObjectMapper objectMapper;
private final DeviceService deviceService;
public CallbackEventIngestService(
PlatformCallbackInboxMapper inboxMapper,
PlatformLicenseSnMapper licenseSnMapper,
PlatformContractLineMapper contractLineMapper,
ObjectMapper objectMapper) {
ObjectMapper objectMapper,
DeviceService deviceService) {
this.inboxMapper = inboxMapper;
this.licenseSnMapper = licenseSnMapper;
this.contractLineMapper = contractLineMapper;
this.objectMapper = objectMapper;
this.deviceService = deviceService;
}
@Transactional
@@ -107,6 +110,12 @@ public class CallbackEventIngestService {
}
throw e;
}
if (row.getEventType() != null && row.getEventType().startsWith("device:")) {
String mid = extractMid(request.getRawPayload());
if (mid != null && row.getLicenseSnId() != null) {
deviceService.handleDeviceEvent(mid, row.getLicenseSnId());
}
}
return new CallbackEventIngestResponse(row.getId(), false);
}
@@ -157,6 +166,19 @@ public class CallbackEventIngestService {
}
}
private static String extractMid(JsonNode payload) {
if (payload == null || !payload.isObject()) return null;
JsonNode data = payload.get("data");
if (data != null && data.isObject()) {
JsonNode mid = data.get("mid");
if (mid != null && mid.isTextual()) {
String t = mid.asText();
if (org.springframework.util.StringUtils.hasText(t)) return t.trim();
}
}
return null;
}
private static String extractSnCode(JsonNode payload) {
if (payload == null || !payload.isObject()) {
return null;