package com.caliverse.admin.domain.service; import com.caliverse.admin.domain.RabbitMq.RabbitMqUtils; import com.caliverse.admin.domain.RabbitMq.message.AuthAdminLevelType; import com.caliverse.admin.domain.dao.admin.AdminMapper; import com.caliverse.admin.domain.datacomponent.MetaDataHandler; import com.caliverse.admin.domain.entity.metadata.MetaQuestData; import com.caliverse.admin.domain.entity.*; import com.caliverse.admin.domain.request.LandRequest; import com.caliverse.admin.domain.request.UserReportRequest; import com.caliverse.admin.domain.response.UserReportResponse; import com.caliverse.admin.domain.response.UsersResponse; import com.caliverse.admin.dynamodb.service.DynamoDBOperations; import com.caliverse.admin.dynamodb.domain.atrrib.LandAuctionActivityAttrib; import com.caliverse.admin.dynamodb.domain.atrrib.LandAuctionHighestBidUserAttrib; import com.caliverse.admin.dynamodb.domain.atrrib.LandAuctionRegistryAttrib; import com.caliverse.admin.dynamodb.domain.doc.LandAuctionActivityDoc; import com.caliverse.admin.dynamodb.domain.doc.LandAuctionHighestBidUserDoc; import com.caliverse.admin.dynamodb.domain.doc.LandAuctionRegistryDoc; import com.caliverse.admin.dynamodb.entity.ELandAuctionResult; import com.caliverse.admin.global.common.annotation.DynamoDBTransaction; import com.caliverse.admin.global.common.code.CommonCode; import com.caliverse.admin.global.common.code.ErrorCode; import com.caliverse.admin.global.common.constants.CommonConstants; import com.caliverse.admin.global.common.constants.DynamoDBConstants; import com.caliverse.admin.global.common.exception.RestApiException; import com.caliverse.admin.global.common.utils.CommonUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; import software.amazon.awssdk.enhanced.dynamodb.Key; import software.amazon.awssdk.enhanced.dynamodb.TableSchema; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.*; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.stream.IntStream; import java.util.stream.Stream; @Slf4j @Service public class DynamoDBService { @Value("${amazon.dynamodb.metaTable}") private String metaTable; private final DynamoDbClient dynamoDbClient; private final DynamoDbEnhancedClient enhancedClient; private final DynamoDBOperations DynamoDBOperations; private final AdminMapper adminMapper; private final MetaDataHandler metaDataHandler; //private final HistoryService historyService; private final ObjectMapper mapper = new ObjectMapper(); @Autowired public DynamoDBService(DynamoDbClient dynamoDbClient, AdminMapper adminMapper, HistoryService historyService, MetaDataHandler metaDataHandler, DynamoDbEnhancedClient enhancedClient, DynamoDBOperations DynamoDBOperations) { this.dynamoDbClient = dynamoDbClient; this.adminMapper = adminMapper; this.metaDataHandler = metaDataHandler; this.enhancedClient = enhancedClient; this.DynamoDBOperations = DynamoDBOperations; } // guid check public boolean isGuidChecked(String guid) { Map item = getItem("user_base#"+guid,"empty"); return item.isEmpty(); } // nickname > guid public String getNickNameByGuid(String primaryKey) { Map resMap = new HashMap<>(); Map key = new HashMap<>(); key.put("PK", AttributeValue.builder().s("user_nickname_registry#global").build()); key.put("SK", AttributeValue.builder().s(primaryKey.toLowerCase(Locale.ENGLISH)).build()); // GetItem 요청을 만듭니다. GetItemRequest getItemRequest = GetItemRequest.builder() .tableName(metaTable) .key(key) .build(); try { // GetItem 요청을 실행하고 응답을 받습니다. GetItemResponse response = dynamoDbClient.getItem(getItemRequest); // 응답에서 원하는 속성을 가져옵니다. AttributeValue attributeValue = response.item().get("UserNicknameRegistryAttrib"); if (attributeValue != null) { String attrJsonString = attributeValue.s(); // JSON 문자열을 파싱합니다. ObjectMapper objectMapper = new ObjectMapper(); JsonNode attrJson = objectMapper.readTree(attrJsonString); // 원하는 필드를 추출합니다. return attrJson.get("user_guid").asText(); } return primaryKey; } catch (Exception e) { log.error("getNickNameByGuid exception: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // guid > nickname public String getGuidByName(String guid){ Map item = getItem("nickname#"+guid,"empty"); if(item.isEmpty()) return null; Map attrMap = CommonUtils.stringByObject(item.get("NicknameAttrib").s()); return CommonUtils.objectToString(attrMap.get("nickname")); } // guid > account_id public String getGuidByAccountId(String guid){ Map item = getItem("user_base#"+guid,"empty"); if(item.isEmpty()) return null; Map attrMap = CommonUtils.stringByObject(item.get("UserBaseAttrib").s()); return CommonUtils.objectToString(attrMap.get("account_id")); } // account_id > guid public String getAccountIdByGuid(Long id){ Map item = getItem("account_base#"+id,"empty"); if(item.isEmpty()) return null; Map attrMap = CommonUtils.stringByObject(item.get("AccountBaseAttrib").s()); return CommonUtils.objectToString(attrMap.get("user_guid")); } // 유저 언어타입 public String getUserLanguage(String guid){ String account_id = getGuidByAccountId(guid); Map item = getItem("account_base#" + account_id,"empty"); if(item.isEmpty()) return null; Map attrMap = CommonUtils.stringByObject(item.get("AccountBaseAttrib").s()); return CommonUtils.objectToString(attrMap.get("language_type")); } // 유저조회 타입별 분기 public Map findUsersBykey(String searchType, String searchKey){ Map resultMap = new HashMap<>(); Map key = new HashMap<>(); try { if(searchType.equals(SEARCHTYPE.NAME.name())){ return getUsersByName(searchKey.toLowerCase()); //nickname은 무조건 소문자 }else if(searchType.equals(SEARCHTYPE.GUID.name())){ return getUsersByGuid(searchKey); }else if(searchType.equals(SEARCHTYPE.ACCOUNT.name())){ return getUsersByAccountId(searchKey); } //else if(searchType.equals(SEARCHTYPE.TEMP_DATA.name())){ // return historyService.insertTempMetaData(); //} return resultMap; } catch (Exception e) { log.error("findUsersBykey exception: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 유저조회 닉네임 // return guid, 닉네임 public Map getUsersByName(String searchKey){ Map resultMap = null; QueryRequest queryRequest = QueryRequest.builder() .tableName(metaTable) .keyConditionExpression("PK = :pkValue AND SK = :skValue") // 파티션 키와 조건식 설정 // .expressionAttributeValues(Map.of(":pkValue", AttributeValue.builder().s("nickname#"+searchKey).build() // ,":skValue", AttributeValue.builder().s("nickname#").build())) .expressionAttributeValues(Map.of(":pkValue", AttributeValue.builder().s("user_nickname_registry#global").build() ,":skValue", AttributeValue.builder().s(searchKey).build())) .build(); try{ // 쿼리 실행 QueryResponse response = dynamoDbClient.query(queryRequest); // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { // AttributeValue attrValue = item.get("Attr"); AttributeValue attrValue = item.get("UserNicknameRegistryAttrib"); if (attrValue != null) { resultMap = new HashMap<>(); // "Attr" 속성의 값을 읽어옵니다. String attrJson = attrValue.s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() { }); // resultMap.put("guid", (String) attrMap.get("AccountGuid")); // resultMap.put("nickname", (String) attrMap.get("AccountId")); resultMap.put("guid", (String) attrMap.get("user_guid")); resultMap.put("nickname", searchKey); } } return resultMap; }catch (Exception e){ log.error("getUsersByName exception: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 유저조회 guid // return guid, account_id public Map getUsersByGuid(String searchKey){ Map resultMap = null; QueryRequest queryRequest = QueryRequest.builder() .tableName(metaTable) .keyConditionExpression("PK = :pkValue AND SK = :skValue") // 파티션 키와 조건식 설정 .expressionAttributeValues(Map.of(":pkValue", AttributeValue.builder().s("user_base#"+searchKey).build() ,":skValue", AttributeValue.builder().s("empty").build())) .build(); try{ // 쿼리 실행 QueryResponse response = dynamoDbClient.query(queryRequest); // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { AttributeValue attrValue = item.get("UserBaseAttrib"); if (attrValue != null) { resultMap = new HashMap<>(); // "Attr" 속성의 값을 읽어옵니다. String attrJson = attrValue.s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() { }); resultMap.put("guid", searchKey); resultMap.put("nickname", (String) attrMap.get("account_id")); } } return resultMap; }catch (Exception e){ log.error("getUsersByGuid exception: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } //유저조회 account_id //return guid, account_id public Map getUsersByAccountId(String searchKey){ Map resultMap = null; QueryRequest queryRequest = QueryRequest.builder() .tableName(metaTable) .keyConditionExpression("PK = :pkValue AND SK = :skValue") // 파티션 키와 조건식 설정 .expressionAttributeValues(Map.of(":pkValue", AttributeValue.builder().s("account_base#"+searchKey).build() ,":skValue", AttributeValue.builder().s("empty").build())) .build(); try{ // 쿼리 실행 QueryResponse response = dynamoDbClient.query(queryRequest); // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { AttributeValue attrValue = item.get("AccountBaseAttrib"); if (attrValue != null) { resultMap = new HashMap<>(); // "Attr" 속성의 값을 읽어옵니다. String attrJson = attrValue.s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() { }); resultMap.put("guid", (String) attrMap.get("user_guid")); resultMap.put("nickname", (String) attrMap.get("account_id")); } } return resultMap; }catch (Exception e){ log.error("getUsersByAccountId exception: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } public Map getAccountInfo(String guid){ Map resMap = new HashMap<>(); String key = "PK = :pkValue AND SK = :skValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("account_base#"+guid).build() ,":skValue", AttributeValue.builder().s("empty").build()); try { // 쿼리 실행 QueryResponse response = executeQuery(key, values); // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { AttributeValue attrValue = item.get("AccountBaseAttrib"); if (attrValue != null) { // "Attr" 속성의 값을 읽어옵니다. String attrJson = attrValue.s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() {}); resMap.put("userInfo", UsersResponse.UserInfo.builder() .aid(CommonUtils.objectToString(attrMap.get("user_guid"))) .userId(CommonUtils.objectToString(attrMap.get("account_id"))) .nation(LANGUAGETYPE.values()[CommonUtils.objectToInteger(attrMap.get("language_type"))]) // .membership(CommonUtils.objectToString(null)) //todo 친구 추천 코드 임시 null 값으로 셋팅 23.09.20 .friendCode(CommonUtils.objectToString(null)) .createDt(CommonUtils.objectToString(attrMap.get("created_datetime"))) .accessDt(CommonUtils.objectToString(attrMap.get("login_datetime"))) .endDt(CommonUtils.objectToString(attrMap.get("logout_datetime"))) .walletUrl(CommonUtils.objectToString(attrMap.get("connect_facewallet"))) .adminLevel(CommonUtils.objectToString(attrMap.get("auth_amdin_level_type"))) //todo 예비슬롯 임시 null 값으로 셋팅 23.09.20 .spareSlot(CommonUtils.objectToString(null)) .build()); } } log.info("getAccountInfo UserInfo: {}", resMap); return resMap; } catch (Exception e) { throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 유저조회 - 기본정보 public Map getCharInfo(String guid){ Map resMap = new HashMap<>(); String key = "PK = :pkValue AND SK = :skValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("money#"+guid).build() ,":skValue", AttributeValue.builder().s("empty").build()); try { // 쿼리 실행 QueryResponse response = executeQuery(key, values); // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { //캐릭터 CharInfo 조회 AttributeValue charValue = item.get("MoneyAttrib"); if (charValue != null) { // "Attr" 속성의 값을 읽어옵니다. Map attrMap = charValue.m(); resMap.put("charInfo", UsersResponse.CharInfo.builder() .characterName(getGuidByName(guid)) .level(CommonUtils.objectToString(null)) .goldCali(CommonUtils.objectToString(attrMap.get("gold").n())) .redCali(CommonUtils.objectToString(attrMap.get("calium").n())) .blackCali(CommonUtils.objectToString(attrMap.get("ruby").n())) .blueCali(CommonUtils.objectToString(attrMap.get("sapphire").n())) .build()); } } log.info("getCharInfo CharInfo: {}", resMap); return resMap; } catch (Exception e) { throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 유저조회 - 아바타 public Map getAvatarInfo(String guid){ Map resMap = new HashMap<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("character_base#"+guid).build()); try { excuteItems(executeQuery(key, values), "CharacterBaseAttrib") .forEach(attrMap -> { Map profile = (Map) attrMap.get("appearance_profile"); //Object customValue = attrMap.get("CustomValue"); resMap.put("avatarInfo", UsersResponse.AvatarInfo.builder() .characterId(CommonUtils.objectToString(attrMap.get("character_guid"))) .basicstyle(CommonUtils.objectToString(profile.get("basic_style"))) .hairstyle(CommonUtils.objectToString(profile.get("hair_style"))) .facesCustomizing(CommonUtils.objectToIntArray(profile.get("custom_values"))) .bodyshape(CommonUtils.objectToString(profile.get("body_shape"))) .build()); }); log.info("getAvatarInfo AvatarInfo: {}", resMap); return resMap; } catch (Exception e) { throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } //퀘스트 조회 public List getQuest(String guid){ List res = new ArrayList<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("quest#"+guid).build()); try { excuteItems(executeQuery(key, values), "QuestAttrib") .forEach(attrMap -> { Integer questId = (Integer) attrMap.get("quest_id"); Integer current_task_no = (Integer)attrMap.get("current_task_num"); List metaQuests = metaDataHandler.getMetaQuestData(questId); // 상세보기 퀘스트 전체 리스트 List detailQuests = metaQuests.stream() .map(metaData -> UsersResponse.Quest.builder() .questId(metaData.getQuestId()) .taskNo(metaData.getTaskNum()) .questName(metaDataHandler.getTextStringData(metaData.getTaskName())) .counter(metaData.getCounter()) .status(current_task_no > metaData.getTaskNum() ? "완료" : "미완료" ) .build()) .toList(); //퀘스트 명칭 String taskName = metaQuests.stream() .filter(attr -> attr.getTaskNum().equals(current_task_no)) .map(MetaQuestData::getTaskName) .findFirst().orElse(null); UsersResponse.QuestInfo questInfo = UsersResponse.QuestInfo.builder() .questId(questId) .questName(metaDataHandler.getTextStringData(taskName)) .status(CommonUtils.objectToString(attrMap.get("is_complete"))) .assignTime((String) attrMap.get("quest_assign_time")) .type((String) attrMap.get("quest_type")) .startTime((String) attrMap.get("task_start_time")) .completeTime((String) attrMap.get("quest_complete_time")) .currentTaskNum((Integer) attrMap.get("current_task_num")) .detailQuest(detailQuests) .build(); res.add(questInfo); }); log.info("getQuest QuestInfo: {}", res); return res; } catch (Exception e) { throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } //guid 로 item 테이블 조회 public Map getItemTable(String guid){ QueryRequest queryRequest = QueryRequest.builder() .tableName(metaTable) .keyConditionExpression("PK = :pkValue AND SK = :skValue") // 파티션 키와 조건식 설정 .expressionAttributeValues( Map.of(":pkValue", AttributeValue.builder().s("item#"+guid).build() ,":skValue", AttributeValue.builder().s("item#"+guid).build())) .build(); try { // 쿼리 실행 QueryResponse response = dynamoDbClient.query(queryRequest); // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { AttributeValue attrValue = item.get("Attr"); if (attrValue != null) { // "Attr" 속성의 값을 읽어옵니다. String attrJson = attrValue.s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); // JSON 문자열을 Map으로 파싱 Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() {}); return attrMap; } } return null; } catch (Exception e) { throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } public void insertUpdateData(String guid, String type, boolean flag) { // 업데이트할 데이터 맵 생성 Map key = new HashMap<>(); key.put("PK", AttributeValue.builder().s("char#"+guid).build()); key.put("SK", AttributeValue.builder().s("char#"+guid).build()); Map attributeUpdates = new HashMap<>(); attributeUpdates.put(type, AttributeValueUpdate.builder() .action(AttributeAction.PUT) .value(AttributeValue.builder().bool(flag).build()) .build()); // UpdateItem 요청 작성 UpdateItemRequest updateItemRequest = UpdateItemRequest.builder() .tableName(metaTable) .key(key) .attributeUpdates(attributeUpdates) .build(); // 데이터 업데이트 또는 인서트 요청 dynamoDbClient.updateItem(updateItemRequest); } // dynamoDB 쿼리 리턴 public QueryResponse executeQuery(String key, Map values) { QueryRequest getItemRequest = QueryRequest.builder() .tableName(metaTable) .keyConditionExpression(key) .expressionAttributeValues(values) .build(); return dynamoDbClient.query(getItemRequest); } public Map getItem(String partitionKey, String sortKey) { Map keyMap = new HashMap<>(); keyMap.put("PK", AttributeValue.builder().s(partitionKey).build()); keyMap.put("SK", AttributeValue.builder().s(sortKey).build()); try{ // GetItem 요청 작성 GetItemRequest getItemRequest = GetItemRequest.builder() .tableName(metaTable) .key(keyMap) .build(); // 아이템 가져오기 GetItemResponse getItemResponse = dynamoDbClient.getItem(getItemRequest); return getItemResponse.item(); }catch (Exception e){ log.error("getItem Fail: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } public Stream> excuteItems (QueryResponse response, String attrip){ return response.items().stream() .map(item -> item.get(attrip)) .filter(Objects::nonNull) .map(AttributeValue::s) .map(attrJson -> { ObjectMapper objectMapper = new ObjectMapper(); try { return objectMapper.readValue(attrJson, new TypeReference>() {}); } catch (JsonProcessingException e) { throw new RuntimeException("JSON parsing error", e); } }); } public boolean isWhiteOrBlackUser(String accountId){ String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("account_base#"+accountId).build()); AtomicBoolean isFlag = new AtomicBoolean(false); excuteItems(executeQuery(key, values), "AccountBaseAttrib") .forEach(attrMap -> { String[] blockPolicy = CommonUtils.objectToStringArray(attrMap.get("block_policy")); if(Arrays.stream(blockPolicy).findAny().isEmpty()){ isFlag.set(false); }else{ isFlag.set(true); } }); return isFlag.get(); // if (attributeValue == null || attributeValue.s() == null) { // // 속성 값이 없거나 문자열이 아닌 경우에 대한 처리 // return false; // } // // // 속성 값을 문자열로 가져옴 // String isFlag = attributeValue.s(); // // // "true" 문자열과 대소문자 구분 없이 비교하여 boolean 값으로 반환 // return isFlag.equalsIgnoreCase("true"); // if(attr == null) return false; } public void updateBlockUserStart(BlackList blockUser){ try{ String accountId = getGuidByAccountId(blockUser.getGuid()); SANCTIONS reasonType = blockUser.getSanctions(); // SANCTIONSTYPE policyType = blockUser.getType(); // List listPolicyType = new ArrayList<>(); // listPolicyType.add(policyType); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS"); String startTime = blockUser.getStartDt().format(formatter); String endTime = blockUser.getEndDt().format(formatter); Map item = getItem("account_base#" + accountId,"empty"); String InfoJson = item.get("AccountBaseAttrib").s(); log.info("updateBlockUserStart Before AccountBaseAttrib: {}", InfoJson); ObjectMapper objectMapper = new ObjectMapper(); JsonNode infoNode = objectMapper.readTree(InfoJson); ((ObjectNode) infoNode).put("block_start_datetime", startTime); ((ObjectNode) infoNode).put("block_end_datetime", endTime); ArrayNode policyArray = objectMapper.createArrayNode(); policyArray.add(blockUser.getType().toString()); ((ObjectNode) infoNode).set("block_policy", policyArray); // ((ObjectNode) infoNode).put("block_policy", listPolicyType.toString()); ((ObjectNode) infoNode).put("block_reason", reasonType.toString()); String updatedInfoJson = infoNode.toString(); String nowDateTime = LocalDateTime.now().format(formatter); log.info("updateBlockUserStart Tobe AccountBaseAttrib: {}", updatedInfoJson); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newAttrib", AttributeValue.builder().s(updatedInfoJson).build()); expressionAttributeValues.put(":nowDate", AttributeValue.builder().s(nowDateTime).build()); String updateExpression = "SET AccountBaseAttrib = :newAttrib, UpdatedDateTime = :nowDate"; UpdateItemRequest updateRequest = UpdateItemRequest.builder() .tableName(metaTable) .key(Map.of( "PK", AttributeValue.builder().s("account_base#" + accountId).build(), "SK", AttributeValue.builder().s("empty").build())) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(updateRequest); }catch(Exception e){ log.error("updateBlockUserStart: " + e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage() ); } } public void updateBlockUserEnd(String guid){ try{ String accountId = getGuidByAccountId(guid); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS"); String endTime = LocalDateTime.of(9999, 12, 31, 23, 59, 59, 999999900).format(formatter); Map item = getItem("account_base#" + accountId,"empty"); String InfoJson = item.get("AccountBaseAttrib").s(); log.info("updateBlockUserEnd Before AccountBaseAttrib: {}", InfoJson); ObjectMapper objectMapper = new ObjectMapper(); JsonNode infoNode = objectMapper.readTree(InfoJson); ((ObjectNode) infoNode).put("block_start_datetime", endTime); ((ObjectNode) infoNode).put("block_end_datetime", endTime); ((ObjectNode) infoNode).put("block_policy", new ArrayList<>().toString()); ((ObjectNode) infoNode).put("block_reason", ""); String updatedInfoJson = infoNode.toString(); String nowDateTime = LocalDateTime.now().format(formatter); log.info("updateBlockUserEnd Tobe AccountBaseAttrib: {}", updatedInfoJson); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newAttrib", AttributeValue.builder().s(updatedInfoJson).build()); expressionAttributeValues.put(":nowDate", AttributeValue.builder().s(nowDateTime).build()); String updateExpression = "SET AccountBaseAttrib = :newAttrib, UpdatedDateTime = :nowDate"; UpdateItemRequest updateRequest = UpdateItemRequest.builder() .tableName(metaTable) .key(Map.of( "PK", AttributeValue.builder().s("account_base#" + accountId).build(), "SK", AttributeValue.builder().s("empty").build())) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(updateRequest); }catch(Exception e){ log.error("updateBlockUserEnd: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage() ); } } // 닉네임 변경 public void updateNickname(String guid,String nickname,String newNickname){ try{ // char#guid 에서 CharInfo-> DisplayName 변경 updateCharInfo(guid, newNickname); // nickname#xxx 에서 Attr-> AccountId 변경 createNewNickName(guid,newNickname); // 기존 nickname 항목 삭제 처리 deleteNickname(nickname); }catch (Exception e){ log.error("updateNickname: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage() ); } } // GM 권한 변경 public void updateAdminLevel(String guid, String type){ AuthAdminLevelType adminLevel = RabbitMqUtils.getUserAdminLevelType(type); try{ String accountId = getGuidByAccountId(guid); Map item = getItem("account_base#" + accountId, "empty"); String InfoJson = item.get("AccountBaseAttrib").s(); log.info("updateAdminLevel Before AccountBaseAttrib: {}", InfoJson); ObjectMapper objectMapper = new ObjectMapper(); JsonNode infoNode = objectMapper.readTree(InfoJson); ((ObjectNode) infoNode).put("auth_amdin_level_type", adminLevel.getNumber()); String updatedInfoJson = infoNode.toString(); log.info("updateAdminLevel Tobe AccountBaseAttrib: {}", updatedInfoJson); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newAttrib", AttributeValue.builder().s(updatedInfoJson).build()); String updateExpression = "SET AccountBaseAttrib = :newAttrib"; UpdateItemRequest updateRequest = UpdateItemRequest.builder() .tableName(metaTable) .key(Map.of( "PK", AttributeValue.builder().s("account_base#" + accountId).build(), "SK", AttributeValue.builder().s("empty").build())) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(updateRequest); }catch (Exception e){ log.error("updateAdminLevel: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage() ); } } public void updateCharInfo(String guid, String newNickname) throws JsonProcessingException { // 기존 CharInfo 값 가져오기 Map item = getItem("char#" + guid, "char#" + guid); String charInfoJson = item.get("CharInfo").s(); // CharInfo JSON 문자열을 파싱 ObjectMapper objectMapper = new ObjectMapper(); JsonNode charInfoNode = objectMapper.readTree(charInfoJson); // 원하는 속성 변경 ((ObjectNode) charInfoNode).put("DisplayName", newNickname); // 변경된 CharInfo JSON 문자열 String updatedCharInfoJson = charInfoNode.toString(); // 업데이트할 내용을 정의 Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newNickName", AttributeValue.builder().s(updatedCharInfoJson).build()); // 업데이트 표현식을 정의 String updateExpression = "SET CharInfo = :newNickName"; // 업데이트 요청 생성 UpdateItemRequest updateRequest = UpdateItemRequest.builder() .tableName(metaTable) .key(Map.of( "PK", AttributeValue.builder().s("char#" + guid).build(), "SK", AttributeValue.builder().s("char#" + guid).build())) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(updateRequest); } public void createNewNickName(String guid, String newNickname) { String attrJson = String.format("{\"AccountGuid\":\"%s\",\"AccountId\":\"%s\"}", guid, newNickname); Map itemAttributes = new HashMap<>(); itemAttributes.put("PK", AttributeValue.builder().s("user_nickname_registry#global").build()); itemAttributes.put("SK", AttributeValue.builder().s(newNickname).build()); itemAttributes.put("Attr", AttributeValue.builder().s(attrJson).build()); itemAttributes.put("Type", AttributeValue.builder().s("NickName").build()); PutItemRequest item = PutItemRequest.builder() .tableName(metaTable) .item(itemAttributes) .build(); dynamoDbClient.putItem(item); } public void deleteNickname(String nickname) { Map itemAttributes = new HashMap<>(); itemAttributes.put("PK", AttributeValue.builder().s("nickname#" + nickname).build()); itemAttributes.put("SK", AttributeValue.builder().s("nickname#").build()); DeleteItemRequest request = DeleteItemRequest.builder() .tableName(metaTable) .key(itemAttributes) .build(); dynamoDbClient.deleteItem(request); } //신고 내역 조회 public List getUserReportList(Map requestParam) { List list = new ArrayList<>(); String startTime = CommonUtils.objectToString(requestParam.get("start_dt")); String endTime = CommonUtils.objectToString(requestParam.get("end_dt")); String expression = "PK = :pkValue and SK BETWEEN :skStartDt AND :skEndDt"; /* LocalDateTime startDt =CommonUtils.stringToTime(startTime); LocalDateTime endDt = CommonUtils.stringToTime(endTime); int months = CommonUtils.calculateMonths(startDt, endDt); Map expressionAttributeValues = new HashMap<>(); for (int i = 0 ; i < months; i++){ expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":pkValue", AttributeValue.builder().s("userReport#" + startDt.getYear() +String.format("%02d", startDt.plusMonths(i).getMonthValue())).build()); expressionAttributeValues.put(":skStartDt", AttributeValue.builder().s("report#" + startDt).build()); expressionAttributeValues.put(":skEndDt", AttributeValue.builder().s("report#" + endDt).build()); QueryRequest queryRequest = QueryRequest.builder() .tableName(metaTable) .keyConditionExpression(expression) .expressionAttributeValues(expressionAttributeValues) .build(); try { QueryResponse response = dynamoDbClient.query(queryRequest); // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { AttributeValue attrValue = item.get("ReportInfo"); if (attrValue != null) { // "Attr" 속성의 값을 읽어옵니다. String attrJson = attrValue.s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); // JSON 문자열을 Map으로 파싱 Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() {}); //담당자 검색 Map replyInfoMap = null; if(item.get("ReplyInfo")!= null){ //담당자 검색 String replyInfo = item.get("ReplyInfo").s(); replyInfoMap = objectMapper.readValue(replyInfo, new TypeReference>() {}); }; Map createTime = (Map)attrMap.get("CreateTime"); Map reTime = (Map)attrMap.get("ResolutionTime"); // "Seconds" 값을 Instant으로 변환하고 "Nanos" 값을 더함 Instant createInstant = Instant.ofEpochSecond( Long.valueOf(CommonUtils.objectToString(createTime.get("Seconds"))) ).plusNanos( Integer.valueOf(CommonUtils.objectToString(createTime.get("Nanos"))) ); Instant reInstant = Instant.ofEpochSecond( Long.valueOf(CommonUtils.objectToString(reTime.get("Seconds"))) ).plusNanos( Integer.valueOf(CommonUtils.objectToString(reTime.get("Nanos"))) ); UserReportResponse.Report report = UserReportResponse.Report.builder() .pk(item.get("PK").s()) .sk(item.get("SK").s()) .reporterGuid(attrMap.get("ReporterGuid").toString()) .reporterNickName(attrMap.get("ReporterNickName").toString()) .targetGuid(attrMap.get("TargetGuid").toString()) .targetNickName(attrMap.get("TargetNickName").toString()) .reportType(Arrays.stream(REPORTTYPE.values()) .filter(r->r.getName().equals(attrMap.get("Reason").toString())) .findFirst().orElse(null)) .title(attrMap.get("Title").toString()) .detail(attrMap.get("Detail").toString()) .state(attrMap.get("State").toString().equals("1")? STATUS.UNRESOLVED:STATUS.RESOLVED) .createTime(createInstant.atZone(ZoneOffset.UTC).toLocalDateTime()) .resolutionTime(Integer.valueOf(reTime.get("Seconds").toString()) == 0?null: reInstant.atZone(ZoneOffset.UTC).toLocalDateTime()) .managerEmail(item.get("ReplyInfo")!= null? CommonUtils.objectToString(replyInfoMap.get("ManagerEmail")):"" ) .build(); list.add(report); } } }catch (JsonProcessingException jpe){ log.error("getUserReportList JsonProcessingException: {}", jpe.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); }catch (Exception e){ log.error("getUserReportList: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } }*/ return list; } //신고내역 상세보기 public UserReportResponse.ResultData getUserReportDetail(Map requestParam){ UserReportResponse.ResultData resultData = UserReportResponse.ResultData.builder().build(); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":pkValue", AttributeValue.builder().s(requestParam.get("pk")).build()); expressionAttributeValues.put(":skValue", AttributeValue.builder().s(requestParam.get("sk")).build()); QueryRequest queryRequest = QueryRequest.builder() .tableName(metaTable) .keyConditionExpression("PK = :pkValue and SK = :skValue") .expressionAttributeValues(expressionAttributeValues) .build(); try{ QueryResponse response = dynamoDbClient.query(queryRequest); for (Map item : response.items()) { AttributeValue ReportInfo = item.get("ReportInfo"); if (ReportInfo != null) { // "Attr" 속성의 값을 읽어옵니다. String attrJson = ReportInfo.s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() {}); Map createTime = (Map)attrMap.get("CreateTime"); Map reTime = (Map)attrMap.get("ResolutionTime"); // "Seconds" 값을 Instant으로 변환하고 "Nanos" 값을 더함 Instant createInstant = Instant.ofEpochSecond( Long.valueOf(CommonUtils.objectToString(createTime.get("Seconds"))) ).plusNanos( Integer.valueOf(CommonUtils.objectToString(createTime.get("Nanos"))) ); Instant reInstant = Instant.ofEpochSecond( Long.valueOf(CommonUtils.objectToString(reTime.get("Seconds"))) ).plusNanos( Integer.valueOf(CommonUtils.objectToString(reTime.get("Nanos"))) ); resultData.setReport(UserReportResponse.Report.builder() .reporterGuid(attrMap.get("ReporterGuid").toString()) .reporterNickName(attrMap.get("ReporterNickName").toString()) .targetGuid(attrMap.get("TargetGuid").toString()) .targetNickName(attrMap.get("TargetNickName").toString()) .reportType(Arrays.stream(REPORTTYPE.values()) .filter(r -> r.getName().equals(attrMap.get("Reason").toString())) .findFirst().orElse(null)) .title(attrMap.get("Title").toString()) .detail(attrMap.get("Detail").toString()) .state(attrMap.get("State").toString().equals("1") ? STATUS.UNRESOLVED : STATUS.RESOLVED) .createTime(createInstant.atZone(ZoneOffset.UTC).toLocalDateTime()) .resolutionTime(Integer.valueOf(reTime.get("Seconds").toString()) == 0 ? null : reInstant.atZone(ZoneOffset.UTC).toLocalDateTime()) .build()); } } return resultData; }catch (JsonProcessingException jpe){ log.error("getUserReportDetail JsonProcessingException: {}", jpe.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); }catch (Exception e){ log.error("getUserReportDetail: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } public UserReportResponse.ResultData getUserReplyDetail(Map requestParam){ UserReportResponse.ResultData resultData = UserReportResponse.ResultData.builder().build(); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":pkValue", AttributeValue.builder().s(requestParam.get("pk")).build()); expressionAttributeValues.put(":skValue", AttributeValue.builder().s(requestParam.get("sk")).build()); QueryRequest queryRequest = QueryRequest.builder() .tableName(metaTable) .keyConditionExpression("PK = :pkValue and SK = :skValue") .expressionAttributeValues(expressionAttributeValues) .build(); try{ QueryResponse response = dynamoDbClient.query(queryRequest); for (Map item : response.items()) { AttributeValue ReplyInfo = item.get("ReplyInfo"); if(ReplyInfo != null){ //담당자 검색 String replyInfo = item.get("ReplyInfo").s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); Map replyInfoMap = objectMapper.readValue(replyInfo, new TypeReference>() {}); resultData.setReply( UserReportResponse.Reply.builder() .title(replyInfoMap.get("Title").toString()) .detail(replyInfoMap.get("Detail").toString()) .managerEmail(replyInfoMap.get("ManagerEmail").toString()) .managerNickName(replyInfoMap.get("ManagerNickName").toString()) .reporterNickName(replyInfoMap.get("ReporterNickName").toString()) .build() ); }; } return resultData; }catch (JsonProcessingException jpe){ log.error("getUserReplyDetail JsonProcessingException: {}", jpe.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); }catch (Exception e){ log.error("getUserReplyDetail: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } //신고 내역 답장 public void reportReply(UserReportRequest userReportRequest){ String replyInfo = String.format("{\"Title\":\"%s\",\"Detail\":\"%s\"" + ",\"ManagerEmail\":\"%s\",\"ManagerNickName\":\"%s\"" + ",\"ReporterNickName\":\"%s\"}" , userReportRequest.getTitle(), userReportRequest.getDetail() , CommonUtils.getAdmin().getEmail(), adminMapper.findByEmail(CommonUtils.getAdmin().getEmail()).get().getName() , userReportRequest.getReporterNickName()); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newValue", AttributeValue.builder().s(replyInfo).build()); // 업데이트 표현식을 정의 String updateExpression = "SET ReplyInfo = :newValue"; UpdateItemRequest item = UpdateItemRequest.builder() .tableName(metaTable) .key(Map.of( "PK", AttributeValue.builder().s(userReportRequest.getPk()).build(), "SK", AttributeValue.builder().s(userReportRequest.getSk()).build())) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(item); } public void changeReportStatus(UserReportRequest userReportRequest){ try { // 기존 CharInfo 값 가져오기 Map item = getItem(userReportRequest.getPk(), userReportRequest.getSk()); String reportInfoJson = item.get("ReportInfo").s(); // ReportInfo JSON 문자열을 파싱 ObjectMapper objectMapper = new ObjectMapper(); JsonNode reportInfoNode = objectMapper.readTree(reportInfoJson); Instant now = Instant.now(); long seconds = now.getEpochSecond(); int nanos = now.getNano(); // 원하는 속성 변경 ((ObjectNode) reportInfoNode).put("State", 2); ((ObjectNode) reportInfoNode.get("ResolutionTime")).put("Seconds", seconds); ((ObjectNode) reportInfoNode.get("ResolutionTime")).put("Nanos", nanos); // 변경된 ReportInfo JSON 문자열 String updatedInfoJson = reportInfoNode.toString(); // 업데이트할 내용을 정의 Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newValue", AttributeValue.builder().s(updatedInfoJson).build()); // 업데이트 표현식을 정의 String updateExpression = "SET ReportInfo = :newValue"; // 업데이트 요청 생성 UpdateItemRequest updateRequest = UpdateItemRequest.builder() .tableName(metaTable) .key(Map.of( "PK", AttributeValue.builder().s(userReportRequest.getPk()).build(), "SK", AttributeValue.builder().s(userReportRequest.getSk()).build())) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(updateRequest); }catch (JsonProcessingException jpe){ log.error("changeReportStatus JsonProcessingException: {}", jpe.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); }catch (Exception e){ log.error("changeReportStatus: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } public void dummy(Map map){ Instant now = Instant.now(); // 현재 시간을 얻습니다. LocalDateTime createTime = LocalDateTime.parse(map.get("CreateTime"), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'")); Instant instant = createTime.atZone(ZoneId.of("UTC")).toInstant(); // 현재 시간을 초로 얻습니다. String replyInfo = String.format("{\"ReporterGuid\":\"%s\",\"ReporterNickName\":\"%s\"" + ",\"TargetGuid\":\"%s\",\"TargetNickName\":\"%s\"" + ",\"Reason\":\"%s\",\"Title\":\"%s\"" + ",\"Detail\":\"%s\",\"State\":\"%s\"" + ",\"CreateTime\":{\"Seconds\":%s,\"Nanos\":%s}" + ",\"ResolutionTime\":{\"Seconds\":%s,\"Nanos\":%s}}" , map.get("ReporterGuid"), map.get("ReporterNickName") , map.get("TargetGuid"), map.get("TargetNickName") , map.get("Reason"), map.get("Title") , map.get("Detail"), map.get("State") , instant.getEpochSecond(), instant.getNano() , 0, 0); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newValue", AttributeValue.builder().s(replyInfo).build()); // 업데이트 표현식을 정의 String updateExpression = "SET ReportInfo = :newValue"; UpdateItemRequest item = UpdateItemRequest.builder() .tableName(metaTable) .key(Map.of( "PK", AttributeValue.builder().s(map.get("pk")).build(), "SK", AttributeValue.builder().s(map.get("sk")).build())) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(item); } //아이템 내역 조회 public List getItems(String guid){ List list = new ArrayList<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("item#"+guid).build()); // QueryRequest queryRequest = QueryRequest.builder() // .tableName(metaTable) // .keyConditionExpression("PK = :pkValue") // 파티션 키와 조건식 설정 // .expressionAttributeValues(Map.of(":pkValue", AttributeValue.builder().s("item#"+guid).build())) // .build(); try { // 쿼리 실행 QueryResponse response = executeQuery(key, values); int row = 1; // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { AttributeValue attrValue = item.get("ItemAttrib"); if (attrValue != null) { // "Attr" 속성의 값을 읽어옵니다. String attrJson = attrValue.s(); // JSON 문자열을 파싱하여 Map 또는 다른 객체로 변환합니다. ObjectMapper objectMapper = new ObjectMapper(); Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() { }); String item_nm = metaDataHandler.getMetaItemNameData(CommonUtils.objectToInteger(attrMap.get("item_meta_id"))); ItemList itemInfo = ItemList.builder() .rowNum((long) row) .guid(guid) .itemId(attrMap.get("item_meta_id").toString()) .itemName(metaDataHandler.getTextStringData(item_nm)) .status(ItemList.STATUS.PERMITTED) .restoreType("") .createBy(item.get("CreatedDateTime").s()).build(); list.add(itemInfo); row++; } } log.info("getItems Response ItemInfo: {}", list); } catch (JsonProcessingException jpe){ log.error("getItems JsonProcessingException: {}", jpe.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); }catch (Exception e){ log.error("getItems: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } return list; } //아이템 - 의상 조회 public Map getCloth(String guid){ Map resultMap = new HashMap<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("item#"+guid).build()); UsersResponse.ClothInfo.ClothInfoBuilder clothInfo = UsersResponse.ClothInfo.builder(); Map> setterMap = new HashMap<>(); setterMap.put(CLOTHSMALLTYPE.SHIRT, UsersResponse.ClothInfo.ClothInfoBuilder::clothShirt); setterMap.put(CLOTHSMALLTYPE.DRESS, UsersResponse.ClothInfo.ClothInfoBuilder::clothDress); setterMap.put(CLOTHSMALLTYPE.OUTER, UsersResponse.ClothInfo.ClothInfoBuilder::clothOuter); setterMap.put(CLOTHSMALLTYPE.PANTS, UsersResponse.ClothInfo.ClothInfoBuilder::clothPants); setterMap.put(CLOTHSMALLTYPE.GLOVES, UsersResponse.ClothInfo.ClothInfoBuilder::clothGloves); setterMap.put(CLOTHSMALLTYPE.RING, UsersResponse.ClothInfo.ClothInfoBuilder::clothRing); setterMap.put(CLOTHSMALLTYPE.BRACELET, UsersResponse.ClothInfo.ClothInfoBuilder::clothBracelet); setterMap.put(CLOTHSMALLTYPE.BAG, UsersResponse.ClothInfo.ClothInfoBuilder::clothBag); setterMap.put(CLOTHSMALLTYPE.BACKPACK, UsersResponse.ClothInfo.ClothInfoBuilder::clothBackpack); setterMap.put(CLOTHSMALLTYPE.CAP, UsersResponse.ClothInfo.ClothInfoBuilder::clothCap); setterMap.put(CLOTHSMALLTYPE.MASK, UsersResponse.ClothInfo.ClothInfoBuilder::clothMask); setterMap.put(CLOTHSMALLTYPE.GLASSES, UsersResponse.ClothInfo.ClothInfoBuilder::clothGlasses); setterMap.put(CLOTHSMALLTYPE.EARRING, UsersResponse.ClothInfo.ClothInfoBuilder::clothEarring); setterMap.put(CLOTHSMALLTYPE.NECKLACE, UsersResponse.ClothInfo.ClothInfoBuilder::clothNecklace); setterMap.put(CLOTHSMALLTYPE.SHOES, UsersResponse.ClothInfo.ClothInfoBuilder::clothShoes); setterMap.put(CLOTHSMALLTYPE.SOCKS, UsersResponse.ClothInfo.ClothInfoBuilder::clothSocks); setterMap.put(CLOTHSMALLTYPE.ANKLET, UsersResponse.ClothInfo.ClothInfoBuilder::clothAnklet); try { excuteItems(executeQuery(key, values), "ItemAttrib") .filter(attrMap -> attrMap.containsKey("equiped_inven_type") && (int) attrMap.get("equiped_inven_type") == 1) .forEach(attrMap -> { int pos = (int) attrMap.get("equiped_pos"); String smallType = metaDataHandler.getMetaClothSmallTypeData(pos); int item_id = CommonUtils.objectToInteger(attrMap.get("item_meta_id")); UsersResponse.ClothItem clothItem = UsersResponse.ClothItem.builder() .cloth(CommonUtils.objectToString(attrMap.get("item_meta_id"))) .clothName(metaDataHandler.getTextStringData(metaDataHandler.getMetaItemNameData(item_id))) .slotType(metaDataHandler.getMetaClothSlotTypeData(pos)) .smallType(smallType) .build(); // ClothItem을 CLOTHSMALLTYPE 위치에 넣어 준다 setterMap.getOrDefault(CLOTHSMALLTYPE.valueOf(smallType), (builder, item) -> {}) .accept(clothInfo, clothItem); }); resultMap.put("clothInfo", clothInfo.build()); log.info("getCloth Response clothInfo: {}", clothInfo); }catch (Exception e){ log.error("getCloth: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } return resultMap; } //아이템 - 도구 조회 public Map getTools(String guid){ Map resultMap = new HashMap<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("item#"+guid).build()); UsersResponse.SlotInfo.SlotInfoBuilder slotInfo = UsersResponse.SlotInfo.builder(); Map> setterMap = Map.of( 1, UsersResponse.SlotInfo.SlotInfoBuilder::Slot1, 2, UsersResponse.SlotInfo.SlotInfoBuilder::Slot2, 3, UsersResponse.SlotInfo.SlotInfoBuilder::Slot3, 4, UsersResponse.SlotInfo.SlotInfoBuilder::Slot4 ); try { excuteItems(executeQuery(key, values), "ItemAttrib") .filter(attrMap -> attrMap.containsKey("equiped_inven_type") && (int) attrMap.get("equiped_inven_type") == 2) .forEach(attrMap -> { int pos = (int) attrMap.get("equiped_pos"); // String smallType = metaDataHandler.getMetaClothSmallTypeData(pos); int item_id = CommonUtils.objectToInteger(attrMap.get("item_meta_id")); String item_nm = metaDataHandler.getTextStringData(metaDataHandler.getMetaItemNameData(item_id)); UsersResponse.ToolItem toolItem = UsersResponse.ToolItem.builder() .toolId(CommonUtils.objectToString(attrMap.get("item_meta_id"))) // .toolName(metaDataHandler.getMetaToolData(CommonUtils.objectToInteger(attrMap.get("item_meta_id"))).getToolName()) .toolName(item_nm) .build(); // ClothItem을 CLOTHSMALLTYPE 위치에 넣어 준다 setterMap.getOrDefault(pos, (builder, item) -> {}) .accept(slotInfo, toolItem); }); resultMap.put("toolSlotInfo", slotInfo.build()); log.info("getTools Response toolSlotInfo: {}", slotInfo); }catch (Exception e){ log.error("getTools: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } return resultMap; } //아이템 - 인벤토리 조회 public UsersResponse.InventoryInfo getInvenItems(String guid){ String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("item#"+guid).build()); List clothList = new ArrayList<>(); List propList = new ArrayList<>(); List beautyList = new ArrayList<>(); List tattooList = new ArrayList<>(); List currencyList = new ArrayList<>(); List etcList = new ArrayList<>(); try { excuteItems(executeQuery(key, values), "ItemAttrib") .filter(attrMap -> attrMap.containsKey("equiped_inven_type") && (int) attrMap.get("equiped_inven_type") == 0) .forEach(attrMap -> { int item_id = (int) attrMap.get("item_meta_id"); String item_nm = metaDataHandler.getTextStringData(metaDataHandler.getMetaItemNameData(item_id)); String item_type = metaDataHandler.getMetaItemLargeTypeData(item_id); UsersResponse.Item inventory = UsersResponse.Item.builder() .itemId(CommonUtils.objectToString(item_id)) .itemName(item_nm) .count(CommonUtils.objectToDouble(attrMap.get("item_stack_count"))) .itemGuid(CommonUtils.objectToString(attrMap.get("item_guid"))) .build(); if(item_type.isEmpty()) { etcList.add(inventory); }else{ switch (ITEMLARGETYPE.valueOf(item_type)){ case CLOTH -> clothList.add(inventory); case PROP -> propList.add(inventory); case BEAUTY -> beautyList.add(inventory); case TATTOO -> tattooList.add(inventory); case CURRENCY -> currencyList.add(inventory); default -> etcList.add(inventory); }} }); log.info("getInvenItems Response cloth: {}, prop: {}, beauty: {}, tattoo: {}, currency: {}, etc: {}", clothList, propList, beautyList, tattooList, currencyList, etcList); return UsersResponse.InventoryInfo.builder() .cloth(clothList) .prop(propList) .beauty(beautyList) .tattoo(tattooList) .currency(currencyList) .etc(etcList) .build(); }catch (Exception e){ log.error("getInvenItems: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 유저 조회 - 메일 public List getMail(String guid, String type){ List resList = new ArrayList<>(); String key = "PK = :pkValue"; Map values = null; if(type.equals(SEARCHTYPE.SEND.name())){ values = Map.of(":pkValue", AttributeValue.builder().s("sent_mail#"+guid).build()); }else{ values = Map.of(":pkValue", AttributeValue.builder().s("recv_mail#"+guid).build()); } try { excuteItems(executeQuery(key, values), "MailAttrib") .forEach(attrMap -> { List itemList = new ArrayList<>(); for (Map val : (List>)attrMap.get("item_list")){ UsersResponse.MailItem item = new UsersResponse.MailItem(); item.setItemId(CommonUtils.objectToString(val.get("ItemId"))); item.setCount(CommonUtils.objectToDouble(val.get("Count"))); String item_nm = metaDataHandler.getMetaItemNameData(CommonUtils.objectToInteger(val.get("ItemId"))); item.setItemName(metaDataHandler.getTextStringData(item_nm)); itemList.add(item); } UsersResponse.Mail mail = UsersResponse.Mail.builder() .mailGuid(CommonUtils.objectToString(attrMap.get("mail_guid"))) .createDt(CommonUtils.objectToString(attrMap.get("create_time"))) .title(CommonUtils.objectToString(attrMap.get("title"))) .content(CommonUtils.objectToString(attrMap.get("text"))) .receiveNickname(CommonUtils.objectToString(attrMap.get("receiver_nickname"))) .senderNickname(CommonUtils.objectToString(attrMap.get("sender_nickname"))) .isGetItem((boolean)attrMap.get("is_get_item")) .status((boolean) attrMap.get("is_read")) .isSystemMail((boolean) attrMap.get("is_system_mail")) .mailItemList(itemList) .build(); resList.add(mail); }); log.info("getMail Response MailInfo: {}", resList); return resList; } catch (Exception e) { log.error("getMail: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 유저 조회 - 우편 삭제 public String deleteMail(String type, String guid, String mail_guid) { Map itemAttributes = new HashMap<>(); if(type.equals("SEND")){ itemAttributes.put("PK", AttributeValue.builder().s("sent_mail#" + guid).build()); }else{ itemAttributes.put("PK", AttributeValue.builder().s("recv_mail#" + guid).build()); } itemAttributes.put("SK", AttributeValue.builder().s(mail_guid).build()); try { Map item = null; if(type.equals("SEND")){ item = getItem("sent_mail#" + guid, mail_guid); log.info("deleteMail PK: {}, SK: {}", "sent_mail#" + guid, mail_guid); }else{ item = getItem("recv_mail#" + guid, mail_guid); log.info("deleteMail PK: {}, SK: {}", "recv_mail#" + guid, mail_guid); } DeleteItemRequest request = DeleteItemRequest.builder() .tableName(metaTable) .key(itemAttributes) .build(); DeleteItemResponse response = dynamoDbClient.deleteItem(request); if(response.sdkHttpResponse().isSuccessful()) return item.toString(); return ""; }catch (ConditionalCheckFailedException e) { log.error("deleteUsersMail Conditional check failed: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); }catch(Exception e){ log.error("deleteUsersMail: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 유저 조회 - 우편 아이템 삭제 public JSONObject updateMailItem(String type, String guid, String mail_guid, Long itemId, double count, double newCount) { try { Map item = null; Map key = new HashMap<>(); if(type.equals("SEND")){ item = getItem("sent_mail#" + guid, mail_guid); key = Map.of("PK", AttributeValue.builder().s("sent_mail#" + guid).build(),"SK", AttributeValue.builder().s(mail_guid).build()); }else{ item = getItem("recv_mail#" + guid, mail_guid); key = Map.of("PK", AttributeValue.builder().s("recv_mail#" + guid).build(),"SK", AttributeValue.builder().s(mail_guid).build()); } String InfoJson = item.get("MailAttrib").s(); ObjectMapper objectMapper = new ObjectMapper(); JsonNode infoNode = objectMapper.readTree(InfoJson); log.info("updateMailItem Before updatedInfoJson: {}", infoNode.toString()); ArrayNode itemListNode = (ArrayNode) infoNode.get("item_list"); // Java 17 스타일의 IntStream을 사용하여 itemId를 찾고 처리 OptionalInt indexOpt = IntStream.range(0, itemListNode.size()) .filter(i -> itemListNode.get(i).get("ItemId").asInt() == itemId) .findFirst(); if (indexOpt.isEmpty()) { log.error("updateMailItem mail item not found"); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } int index = indexOpt.getAsInt(); JsonNode itemNode = itemListNode.get(index); if (count > newCount) { // count 수정 ((ObjectNode) itemNode).put("Count", count - newCount); } else { // item 삭제 itemListNode.remove(index); } String updatedInfoJson = infoNode.toString(); log.info("updateMailItem Tobe updatedInfoJson: {}", updatedInfoJson); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newAttrib", AttributeValue.builder().s(updatedInfoJson).build()); String updateExpression = "SET MailAttrib = :newAttrib"; UpdateItemRequest updateRequest = UpdateItemRequest.builder() .tableName(metaTable) .key(key) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(updateRequest); JSONObject jsonObject = new JSONObject(); jsonObject.put("data(before)", InfoJson); jsonObject.put("data(after)", updatedInfoJson); return jsonObject; }catch (ConditionalCheckFailedException e) { log.error("deleteUsersMail Conditional check failed: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); }catch(Exception e){ log.error("deleteUsersMail: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 아이템 삭제 public String deleteItem(String guid, String item_guid) { Map itemAttributes = new HashMap<>(); itemAttributes.put("PK", AttributeValue.builder().s("item#" + guid).build()); itemAttributes.put("SK", AttributeValue.builder().s(item_guid).build()); try { Map item = getItem("item#" + guid, item_guid); log.info("deleteItem PK: {}, SK: {}", "item#" + guid, item_guid); DeleteItemRequest request = DeleteItemRequest.builder() .tableName(metaTable) .key(itemAttributes) .build(); DeleteItemResponse response = dynamoDbClient.deleteItem(request); if(response.sdkHttpResponse().isSuccessful()) return item.toString(); return ""; }catch (ConditionalCheckFailedException e) { log.error("deleteItem Conditional check failed: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); }catch(Exception e){ log.error("deleteItem: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } } // 아이템 정보 수정 public JSONObject updateItem(String guid, String item_guid, int cnt) { try{ Map item = getItem("item#" + guid, item_guid); String InfoJson = item.get("ItemAttrib").s(); ObjectMapper objectMapper = new ObjectMapper(); JsonNode infoNode = objectMapper.readTree(InfoJson); log.info("updateItem Before UpdateInfo : {}", infoNode.toString()); ((ObjectNode) infoNode).put("item_stack_count", cnt); String updatedInfoJson = infoNode.toString(); log.info("updateItem Tobe UpdateInfo : {}", updatedInfoJson); Map expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":newAttrib", AttributeValue.builder().s(updatedInfoJson).build()); String updateExpression = "SET ItemAttrib = :newAttrib"; UpdateItemRequest updateRequest = UpdateItemRequest.builder() .tableName(metaTable) .key(Map.of( "PK", AttributeValue.builder().s("item#" + guid).build(), "SK", AttributeValue.builder().s(item_guid).build())) .updateExpression(updateExpression) .expressionAttributeValues(expressionAttributeValues) .returnValues(ReturnValue.ALL_NEW) // 업데이트 후의 값을 반환하려면 지정 .build(); dynamoDbClient.updateItem(updateRequest); JSONObject jsonObject = new JSONObject(); jsonObject.put("data(before)", InfoJson); jsonObject.put("data(after)", updatedInfoJson); return jsonObject; }catch(Exception e){ log.error("updateItem: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage() ); } } // 아이템 - 타투 죄회 public List getTattoo(String guid){Map resultMap = new HashMap<>(); List resTatto = new ArrayList<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("item#"+guid).build()); try { excuteItems(executeQuery(key, values), "ItemAttrib") .filter(attrMap -> attrMap.containsKey("equiped_inven_type") && (int) attrMap.get("equiped_inven_type") == 3) .forEach(attrMap -> { int pos = CommonUtils.objectToInteger(attrMap.get("equiped_pos")); if(pos == 0) return; UsersResponse.Tattoo tattoo = new UsersResponse.Tattoo(); Long item_id = CommonUtils.objectToLong(attrMap.get("item_meta_id")); tattoo.setItemId(item_id); tattoo.setItemGuid(CommonUtils.objectToString(attrMap.get("Item_guid"))); tattoo.setLevel(Integer.valueOf(CommonUtils.objectToString(attrMap.get("level")))); tattoo.setItemName(metaDataHandler.getTextStringData(metaDataHandler.getMetaItemNameData(item_id.intValue()))); tattoo.setSlot(pos); resTatto.add(tattoo); }); log.info("getTattoo Response TattoInfo: {}", resTatto); }catch (Exception e){ log.error("getTattoo: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } return resTatto; } // 친구 목록 public List getFriendList(String guid){ List resList = new ArrayList<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("friend#"+guid).build()); AtomicInteger idx = new AtomicInteger(1); try { excuteItems(executeQuery(key, values), "FriendAttrib") .forEach(attrMap -> { UsersResponse.Friend friend = new UsersResponse.Friend(); friend.setRowNum(idx.getAndIncrement()); String friend_guid = CommonUtils.objectToString(attrMap.get("friend_guid")); friend.setFriendGuid(friend_guid); friend.setFriendName(getGuidByName(friend_guid)); friend.setReceiveDt(CommonUtils.objectToString(attrMap.get("create_time"))); friend.setLanguage(LANGUAGETYPE.values()[Integer.parseInt(getUserLanguage(friend_guid))].toString()); resList.add(friend); }); log.info("getFriendList FriendInfo: {}", resList); }catch (Exception e){ log.error("getFriendList: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } return resList; } // 유저 차단 목록 public List getUserBlockList(String guid){ List resList = new ArrayList<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("block#"+guid).build()); AtomicInteger idx = new AtomicInteger(1); try { QueryResponse response = executeQuery(key, values); // 응답에서 원하는 속성을 가져옵니다. for (Map item : response.items()) { AttributeValue attrValue = item.get("BlockUserAttrib"); if (attrValue != null) { String attrJson = attrValue.s(); ObjectMapper objectMapper = new ObjectMapper(); Map attrMap = objectMapper.readValue(attrJson, new TypeReference>() { }); UsersResponse.Friend friend = new UsersResponse.Friend(); friend.setRowNum(idx.getAndIncrement()); String block_guid = CommonUtils.objectToString(attrMap.get("guid")); friend.setFriendGuid(block_guid); friend.setFriendName(getGuidByName(block_guid)); friend.setReceiveDt(CommonUtils.objectToString(item.get("CreatedDateTime").s())); friend.setLanguage(LANGUAGETYPE.values()[Integer.parseInt(getUserLanguage(block_guid))].toString()); resList.add(friend); } } log.info("getUserBlockList FriendInfo: {}", resList); }catch (Exception e){ log.error("getUserBlockList: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } return resList; } //마이홈 public UsersResponse.Myhome getMyhome(String guid){ UsersResponse.Myhome myhome = new UsersResponse.Myhome(); List itemList = new ArrayList<>(); String key = "PK = :pkValue"; Map values = Map.of(":pkValue", AttributeValue.builder().s("my_home#" + guid).build()); try { excuteItems(executeQuery(key, values), "MyHomeAttrib") .forEach(attrMap -> { String myhome_guid = CommonUtils.objectToString(attrMap.get("myhome_guid")); myhome.setMyhomeGuid(myhome_guid); myhome.setMyhomeName(CommonUtils.objectToString(attrMap.get("myhome_name"))); String second_key = "PK = :pkValue"; Map second_values = Map.of(":pkValue", AttributeValue.builder().s("item#"+myhome_guid).build()); excuteItems(executeQuery(second_key, second_values), "ItemAttrib").forEach(attrMap2 -> { String item_id = CommonUtils.objectToString(attrMap2.get("item_meta_id")); String item_name = metaDataHandler.getMetaItemNameData(Integer.parseInt(item_id)); UsersResponse.Item item = UsersResponse.Item.builder() .itemId(item_id) .itemName(metaDataHandler.getTextStringData(item_name)) .count(CommonUtils.objectToDouble(attrMap2.get("item_stack_count"))) .itemGuid(CommonUtils.objectToString(attrMap2.get("item_guid"))) .build(); itemList.add(item); }); myhome.setPropList(itemList); }); log.info("getMyhome myhomedInfo: {}", myhome); }catch (Exception e){ log.error("getMyhome: {}", e.getMessage()); throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage()); } return myhome; } }