refactor: 优化 Android gRPC 推送服务日志记录和错误处理
- 在 `AndroidPushGrpcService` 中添加详细的日志记录,包括连接、心跳、ACK 和错误处理 - 更新 `sendError` 方法以包含设备信息,并在日志中记录错误响应 - 在 `LegacyAuthController` 中添加租户 ID 设置逻辑 - 更新 `MeetingSummaryPromptAssembler` 中的提示词,明确关键词基于会议转写生成 - 移除 `AndroidAuthServiceImplTest` 测试类dev_na
parent
5b4304a4b2
commit
c0e973e5a9
|
|
@ -1,5 +1,6 @@
|
|||
package com.imeeting.controller.android.legacy;
|
||||
|
||||
import com.google.protobuf.ServiceException;
|
||||
import com.imeeting.dto.android.legacy.LegacyApiResponse;
|
||||
import com.imeeting.dto.android.legacy.LegacyLoginResponse;
|
||||
import com.imeeting.dto.android.legacy.LegacyLoginUserResponse;
|
||||
|
|
@ -40,6 +41,11 @@ public class LegacyAuthController {
|
|||
} catch (Exception e) {
|
||||
return LegacyApiResponse.error("400",e.getMessage());
|
||||
}
|
||||
try {
|
||||
tokenResponse.getUser().setTenantId(tokenResponse.getAvailableTenants().stream().findFirst().orElseThrow(()-> new ServiceException("未绑定租户")).getTenantId());
|
||||
} catch (ServiceException e) {
|
||||
return LegacyApiResponse.error("400",e.getMessage());
|
||||
}
|
||||
return LegacyApiResponse.ok(new LegacyLoginResponse(
|
||||
tokenResponse.getAccessToken(),
|
||||
tokenResponse.getRefreshToken(),
|
||||
|
|
@ -68,6 +74,7 @@ public class LegacyAuthController {
|
|||
SysRoleDTO primaryRole = resolvePrimaryRole(user);
|
||||
return new LegacyLoginUserResponse(
|
||||
user.getUserId(),
|
||||
user.getTenantId(),
|
||||
user.getUsername(),
|
||||
user.getDisplayName(),
|
||||
user.getAvatarUrl(),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import java.time.LocalDateTime;
|
|||
@AllArgsConstructor
|
||||
public class LegacyLoginUserResponse {
|
||||
private Long user_id;
|
||||
private Long tenant_id;
|
||||
private String username;
|
||||
private String caption;
|
||||
private String avatar_url;
|
||||
|
|
|
|||
|
|
@ -13,11 +13,16 @@ import lombok.RequiredArgsConstructor;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase implements BindableService {
|
||||
|
||||
private static final DateTimeFormatter LOG_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private final AndroidAuthService androidAuthService;
|
||||
private final AndroidDeviceSessionService androidDeviceSessionService;
|
||||
private final AndroidGatewayPushService androidGatewayPushService;
|
||||
|
|
@ -28,6 +33,8 @@ public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase
|
|||
return new StreamObserver<>() {
|
||||
private String connectionId;
|
||||
private String deviceId;
|
||||
private String appVersion;
|
||||
private String platform;
|
||||
private boolean connected;
|
||||
|
||||
@Override
|
||||
|
|
@ -37,32 +44,69 @@ public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase
|
|||
case CONNECT -> handleConnect(message.getConnect());
|
||||
case HEARTBEAT -> handleHeartbeat(message.getHeartbeat());
|
||||
case ACK -> handleAck(message.getAck());
|
||||
case PAYLOAD_NOT_SET -> sendError(responseObserver, "PUSH_BAD_REQUEST", "Missing push payload", false);
|
||||
case PAYLOAD_NOT_SET -> {
|
||||
log.info(buildLog("gRPC请求", "收到空的客户端消息体", deviceId, appVersion, platform));
|
||||
sendError(responseObserver, "PUSH_BAD_REQUEST", "Missing push payload", false, deviceId, appVersion, platform);
|
||||
}
|
||||
}
|
||||
} catch (BusinessException ex) {
|
||||
log.info(buildLog("gRPC业务拒绝",
|
||||
"gRPC推送请求被业务规则拒绝,连接ID=" + safe(connectionId) + ",原因=" + safe(ex.getMessage()),
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
log.warn("Android push gRPC business rejection, connectionId={}", connectionId, ex);
|
||||
sendError(responseObserver, ex.getCode(), ex.getMessage(), false);
|
||||
sendError(responseObserver, ex.getCode(), ex.getMessage(), false, deviceId, appVersion, platform);
|
||||
} catch (Exception ex) {
|
||||
log.info(buildLog("gRPC处理异常",
|
||||
"gRPC推送请求处理失败,连接ID=" + safe(connectionId) + ",异常=" + ex.getClass().getSimpleName(),
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
log.warn("Android push gRPC request handling failed, connectionId={}", connectionId, ex);
|
||||
sendError(responseObserver, "PUSH_PROCESSING_ERROR", ex.getMessage(), false);
|
||||
sendError(responseObserver, "PUSH_PROCESSING_ERROR", ex.getMessage(), false, deviceId, appVersion, platform);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
log.info(buildLog("gRPC异常断开",
|
||||
"gRPC推送流异常断开,连接ID=" + safe(connectionId) + ",异常=" + throwable.getClass().getSimpleName(),
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
log.warn("Android push gRPC stream failed, connectionId={}", connectionId, throwable);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
log.info(buildLog("gRPC主动完成",
|
||||
"客户端正常关闭gRPC推送流,连接ID=" + safe(connectionId),
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
cleanup();
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
|
||||
private void handleConnect(ConnectRequest request) {
|
||||
String requestDeviceId = request.getDeviceId();
|
||||
String requestAppVersion = request.getAppVersion();
|
||||
String requestPlatform = resolvePlatform(request.getPlatform());
|
||||
log.info(buildLog("gRPC连接请求",
|
||||
"收到Android推送连接请求,请求连接ID=" + safe(request.getConnectionId()),
|
||||
requestDeviceId,
|
||||
requestAppVersion,
|
||||
requestPlatform));
|
||||
if (connected) {
|
||||
sendError(responseObserver, "PUSH_ALREADY_CONNECTED", "Push connection already established", false);
|
||||
log.info(buildLog("gRPC连接拒绝",
|
||||
"重复发起连接,当前连接已建立,连接ID=" + safe(connectionId),
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
sendError(responseObserver, "PUSH_ALREADY_CONNECTED", "Push connection already established", false,
|
||||
deviceId, appVersion, platform);
|
||||
return;
|
||||
}
|
||||
AndroidAuthContext authContext = androidAuthService.authenticateGrpc(
|
||||
|
|
@ -75,12 +119,24 @@ public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase
|
|||
AndroidDeviceSessionState sessionState = androidDeviceSessionService.openSession(authContext, request.getConnectionId());
|
||||
connectionId = sessionState.getConnectionId();
|
||||
deviceId = sessionState.getDeviceId();
|
||||
appVersion = authContext.getAppVersion();
|
||||
platform = authContext.getPlatform();
|
||||
deviceOnlineManagementService.recordConnected(authContext);
|
||||
connected = true;
|
||||
String replacedConnectionId = androidGatewayPushService.register(connectionId, deviceId, responseObserver);
|
||||
if (replacedConnectionId != null && !replacedConnectionId.equals(connectionId)) {
|
||||
log.info(buildLog("gRPC连接替换",
|
||||
"同设备旧连接被新连接替换,旧连接ID=" + replacedConnectionId + ",新连接ID=" + connectionId,
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
androidDeviceSessionService.closeSession(replacedConnectionId);
|
||||
}
|
||||
log.info(buildLog("gRPC连接成功",
|
||||
"Android推送连接建立成功,连接ID=" + connectionId,
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
responseObserver.onNext(ServerMessage.newBuilder()
|
||||
.setConnectAck(ConnectResponse.newBuilder()
|
||||
.setSuccess(true)
|
||||
|
|
@ -94,11 +150,13 @@ public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase
|
|||
return;
|
||||
}
|
||||
if (!request.getConnectionId().isBlank() && !request.getConnectionId().equals(connectionId)) {
|
||||
sendError(responseObserver, "PUSH_CONNECTION_MISMATCH", "Connection id does not match active session", false);
|
||||
sendError(responseObserver, "PUSH_CONNECTION_MISMATCH", "Connection id does not match active session", false,
|
||||
deviceId, appVersion, platform);
|
||||
return;
|
||||
}
|
||||
if (!request.getDeviceId().isBlank() && !request.getDeviceId().equals(deviceId)) {
|
||||
sendError(responseObserver, "PUSH_DEVICE_MISMATCH", "Device id does not match active session", false);
|
||||
sendError(responseObserver, "PUSH_DEVICE_MISMATCH", "Device id does not match active session", false,
|
||||
deviceId, appVersion, platform);
|
||||
return;
|
||||
}
|
||||
AndroidDeviceSessionState state = androidDeviceSessionService.refreshHeartbeat(connectionId, request.getTimestamp());
|
||||
|
|
@ -115,19 +173,43 @@ public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase
|
|||
return;
|
||||
}
|
||||
if (!request.getConnectionId().isBlank() && !request.getConnectionId().equals(connectionId)) {
|
||||
sendError(responseObserver, "PUSH_CONNECTION_MISMATCH", "Connection id does not match active session", false);
|
||||
log.info(buildLog("gRPC确认拒绝",
|
||||
"ACK连接ID与当前活动连接不一致,请求连接ID=" + request.getConnectionId() + ",当前连接ID=" + connectionId,
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
sendError(responseObserver, "PUSH_CONNECTION_MISMATCH", "Connection id does not match active session", false,
|
||||
deviceId, appVersion, platform);
|
||||
return;
|
||||
}
|
||||
if (!request.getDeviceId().isBlank() && !request.getDeviceId().equals(deviceId)) {
|
||||
sendError(responseObserver, "PUSH_DEVICE_MISMATCH", "Device id does not match active session", false);
|
||||
log.info(buildLog("gRPC确认拒绝",
|
||||
"ACK设备ID与当前活动设备不一致,请求设备ID=" + request.getDeviceId() + ",当前设备ID=" + deviceId,
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
sendError(responseObserver, "PUSH_DEVICE_MISMATCH", "Device id does not match active session", false,
|
||||
deviceId, appVersion, platform);
|
||||
return;
|
||||
}
|
||||
log.info(buildLog("gRPC消息确认",
|
||||
"收到客户端ACK确认,消息ID=" + safe(request.getMessageId()) + ",连接ID=" + connectionId,
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
}
|
||||
|
||||
private boolean validateConnected() {
|
||||
if (connected) {
|
||||
return true;
|
||||
}
|
||||
sendError(responseObserver, "PUSH_NOT_CONNECTED", "Push connection has not been established", false);
|
||||
log.info(buildLog("gRPC请求拒绝",
|
||||
"连接尚未建立即发送后续消息",
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
sendError(responseObserver, "PUSH_NOT_CONNECTED", "Push connection has not been established", false,
|
||||
deviceId, appVersion, platform);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -139,14 +221,32 @@ public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase
|
|||
androidGatewayPushService.unregister(connectionId);
|
||||
androidDeviceSessionService.closeSession(connectionId);
|
||||
deviceOnlineManagementService.recordDisconnected(deviceId, state == null ? null : state.getLastSeenAt());
|
||||
log.info(buildLog("gRPC连接关闭",
|
||||
"Android推送连接已关闭,连接ID=" + connectionId,
|
||||
deviceId,
|
||||
state == null ? appVersion : state.getAppVersion(),
|
||||
state == null ? platform : state.getPlatform()));
|
||||
connectionId = null;
|
||||
deviceId = null;
|
||||
appVersion = null;
|
||||
platform = null;
|
||||
connected = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void sendError(StreamObserver<ServerMessage> responseObserver, String code, String message, boolean retryable) {
|
||||
private void sendError(StreamObserver<ServerMessage> responseObserver,
|
||||
String code,
|
||||
String message,
|
||||
boolean retryable,
|
||||
String deviceId,
|
||||
String appVersion,
|
||||
String platform) {
|
||||
log.info(buildLog("gRPC错误响应",
|
||||
"向客户端返回错误,错误码=" + safe(code) + ",原因=" + safe(message),
|
||||
deviceId,
|
||||
appVersion,
|
||||
platform));
|
||||
responseObserver.onNext(ServerMessage.newBuilder()
|
||||
.setError(ErrorEvent.newBuilder()
|
||||
.setCode(code)
|
||||
|
|
@ -156,6 +256,20 @@ public class AndroidPushGrpcService extends PushServiceGrpc.PushServiceImplBase
|
|||
.build());
|
||||
}
|
||||
|
||||
private String buildLog(String actionType, String action, String deviceId, String appVersion, String platform) {
|
||||
return String.format("[%s] %s %s 设备=%s 版本=%s 平台=%s",
|
||||
safe(actionType),
|
||||
LocalDateTime.now().format(LOG_TIME_FORMATTER),
|
||||
safe(action),
|
||||
safe(deviceId),
|
||||
safe(appVersion),
|
||||
safe(platform));
|
||||
}
|
||||
|
||||
private String safe(String value) {
|
||||
return value == null || value.isBlank() ? "-" : value.trim();
|
||||
}
|
||||
|
||||
private String resolvePlatform(Platform platform) {
|
||||
return switch (platform) {
|
||||
case IOS -> "ios";
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import org.springframework.data.redis.core.StringRedisTemplate;
|
|||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -20,6 +22,8 @@ import java.util.UUID;
|
|||
@RequiredArgsConstructor
|
||||
public class AndroidDeviceSessionServiceImpl implements AndroidDeviceSessionService {
|
||||
|
||||
private static final DateTimeFormatter LOG_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final GrpcServerProperties grpcServerProperties;
|
||||
|
|
@ -35,6 +39,11 @@ public class AndroidDeviceSessionServiceImpl implements AndroidDeviceSessionServ
|
|||
state.setPlatform(nonBlank(authContext.getPlatform(), "android"));
|
||||
state.setTenantCode(authContext.getTenantCode());
|
||||
writeState(state);
|
||||
log.info(buildLog("gRPC会话创建",
|
||||
"创建Android设备会话,连接ID=" + state.getConnectionId(),
|
||||
state.getDeviceId(),
|
||||
state.getAppVersion(),
|
||||
state.getPlatform()));
|
||||
return state;
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +118,11 @@ public class AndroidDeviceSessionServiceImpl implements AndroidDeviceSessionServ
|
|||
redisTemplate.delete(RedisKeys.androidDeviceOnlineKey(state.getDeviceId()));
|
||||
}
|
||||
redisTemplate.delete(RedisKeys.androidDeviceConnectionKey(connectionId));
|
||||
log.info(buildLog("gRPC会话关闭",
|
||||
"关闭Android设备会话,连接ID=" + connectionId,
|
||||
state.getDeviceId(),
|
||||
state.getAppVersion(),
|
||||
state.getPlatform()));
|
||||
}
|
||||
|
||||
private void writeState(AndroidDeviceSessionState state) {
|
||||
|
|
@ -126,4 +140,18 @@ public class AndroidDeviceSessionServiceImpl implements AndroidDeviceSessionServ
|
|||
private String nonBlank(String value, String defaultValue) {
|
||||
return value != null && !value.isBlank() ? value : defaultValue;
|
||||
}
|
||||
|
||||
private String buildLog(String actionType, String action, String deviceId, String appVersion, String platform) {
|
||||
return String.format("[%s] %s %s 设备=%s 版本=%s 平台=%s",
|
||||
safe(actionType),
|
||||
LocalDateTime.now().format(LOG_TIME_FORMATTER),
|
||||
safe(action),
|
||||
safe(deviceId),
|
||||
safe(appVersion),
|
||||
safe(platform));
|
||||
}
|
||||
|
||||
private String safe(String value) {
|
||||
return value == null || value.isBlank() ? "-" : value.trim();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ public class MeetingSummaryPromptAssembler {
|
|||
.append("}\n")
|
||||
.append("要求:\n")
|
||||
.append("1. `summaryContent` 必须优先遵循模板提示词中的结构、标题层级、章节顺序和写作风格。\n")
|
||||
.append("2. `analysis.keywords` 必须基于完整转写内容生成,不得脱离上下文。并且在转录中能找到对应的原文\n")
|
||||
.append("2. `analysis.keywords` 必须基于完整转写内容生成,不得脱离上下文。并且在会议转写中能找到对应的原文\n")
|
||||
// .append("3. 若无待办事项,`todos` 返回空数组。\n")
|
||||
.append("3. 仅输出 JSON。\n")
|
||||
.append("\n")
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
package com.imeeting.service.android.impl;
|
||||
|
||||
import com.imeeting.config.grpc.AndroidGrpcAuthProperties;
|
||||
import com.imeeting.dto.android.AndroidAuthContext;
|
||||
import com.unisbase.dto.InternalAuthCheckResponse;
|
||||
import com.unisbase.service.TokenValidationService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class AndroidAuthServiceImplTest {
|
||||
|
||||
@AfterEach
|
||||
void clearSecurityContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticateHttpShouldResolveBearerTokenAndIdentity() {
|
||||
AndroidGrpcAuthProperties properties = new AndroidGrpcAuthProperties();
|
||||
TokenValidationService tokenValidationService = mock(TokenValidationService.class);
|
||||
AndroidAuthServiceImpl service = new AndroidAuthServiceImpl(properties, tokenValidationService);
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
|
||||
when(request.getHeader("Authorization")).thenReturn("Bearer access-token");
|
||||
when(request.getHeader("X-Android-Device-Id")).thenReturn("device-01");
|
||||
when(request.getHeader("X-Android-App-Id")).thenReturn("imeeting");
|
||||
when(request.getHeader("X-Android-App-Version")).thenReturn("1.0.0");
|
||||
when(request.getHeader("X-Android-Platform")).thenReturn("android");
|
||||
|
||||
InternalAuthCheckResponse authResult = new InternalAuthCheckResponse();
|
||||
authResult.setValid(true);
|
||||
authResult.setUserId(11L);
|
||||
authResult.setTenantId(22L);
|
||||
authResult.setUsername("alice");
|
||||
authResult.setPlatformAdmin(false);
|
||||
authResult.setTenantAdmin(true);
|
||||
authResult.setPermissions(Set.of("meeting:create"));
|
||||
when(tokenValidationService.validateAccessToken("access-token")).thenReturn(authResult);
|
||||
|
||||
AndroidAuthContext context = service.authenticateHttp(request);
|
||||
|
||||
assertFalse(context.isAnonymous());
|
||||
assertEquals("USER_JWT", context.getAuthMode());
|
||||
assertEquals("device-01", context.getDeviceId());
|
||||
assertEquals(11L, context.getUserId());
|
||||
assertEquals(22L, context.getTenantId());
|
||||
assertEquals("alice", context.getUsername());
|
||||
assertEquals("alice", context.getDisplayName());
|
||||
assertTrue(context.getTenantAdmin());
|
||||
assertEquals(Set.of("meeting:create"), context.getPermissions());
|
||||
assertEquals("access-token", context.getAccessToken());
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticateHttpShouldAllowAnonymousWhenConfigured() {
|
||||
AndroidGrpcAuthProperties properties = new AndroidGrpcAuthProperties();
|
||||
properties.setAllowAnonymous(true);
|
||||
TokenValidationService tokenValidationService = mock(TokenValidationService.class);
|
||||
AndroidAuthServiceImpl service = new AndroidAuthServiceImpl(properties, tokenValidationService);
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
|
||||
when(request.getHeader("Authorization")).thenReturn(null);
|
||||
when(request.getHeader("X-Android-Device-Id")).thenReturn("device-anon");
|
||||
when(request.getHeader("X-Android-App-Id")).thenReturn("imeeting");
|
||||
when(request.getHeader("X-Android-App-Version")).thenReturn("1.0.0");
|
||||
when(request.getHeader("X-Android-Platform")).thenReturn("android");
|
||||
|
||||
AndroidAuthContext context = service.authenticateHttp(request);
|
||||
|
||||
assertTrue(context.isAnonymous());
|
||||
assertEquals("NONE", context.getAuthMode());
|
||||
assertEquals("device-anon", context.getDeviceId());
|
||||
assertNull(context.getUserId());
|
||||
assertNull(context.getTenantId());
|
||||
assertNull(context.getAccessToken());
|
||||
}
|
||||
}
|
||||
|
|
@ -327,7 +327,7 @@ export default function Logs() {
|
|||
{selectedLog && (
|
||||
<Descriptions bordered column={1} size="small">
|
||||
{isPlatformAdmin && <Descriptions.Item label={t("users.tenant")}><Text>{selectedLog.tenantName || t("logsExt.platform")}</Text></Descriptions.Item>}
|
||||
{selectedLog.logType === "OPERATION" && <Descriptions.Item label={t("logsExt.module")}>{selectedLog.moduleName || t("logsExt.uncategorized")}</Descriptions.Item>}
|
||||
{selectedLog.logType === "OPERATION" && <Descriptions.Item style={{ width: 50 }} label={t("logsExt.module")}>{selectedLog.moduleName || t("logsExt.uncategorized")}</Descriptions.Item>}
|
||||
{selectedLog.logType === "OPERATION" && <Descriptions.Item label={t("logsExt.actionLabel")}>{selectedLog.actionName || selectedLog.operation}</Descriptions.Item>}
|
||||
<Descriptions.Item label={t("logs.opDetail")}>{selectedLog.operation}</Descriptions.Item>
|
||||
<Descriptions.Item label={t("logs.method")}><Tag color="blue">{selectedLog.method || "N/A"}</Tag></Descriptions.Item>
|
||||
|
|
|
|||
Loading…
Reference in New Issue