diff --git a/src/main/java/com/staketab/minanames/controller/ActivityController.java b/src/main/java/com/staketab/minanames/controller/ActivityController.java new file mode 100644 index 0000000..a86b275 --- /dev/null +++ b/src/main/java/com/staketab/minanames/controller/ActivityController.java @@ -0,0 +1,36 @@ +package com.staketab.minanames.controller; + +import com.staketab.minanames.dto.ActivityDTO; +import com.staketab.minanames.dto.request.BaseRequest; +import com.staketab.minanames.dto.request.sort.ActivitySortColumn; +import com.staketab.minanames.service.ActivityService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/activities") +@RequiredArgsConstructor +@Slf4j +public class ActivityController { + + private final ActivityService activityService; + + @GetMapping("/{address}") + @Operation(summary = "getActivities", description = "Get a page of all activities.") + public Page getActivities(@Valid @ParameterObject BaseRequest request, + @RequestParam @Schema(defaultValue = "TIMESTAMP", allowableValues = {"TIMESTAMP"}, + description = "Select sorting parameter.") ActivitySortColumn sortBy, + @PathVariable String address) { + return activityService.findAllByPageable(request.withSortColumn(sortBy), address); + } +} diff --git a/src/main/java/com/staketab/minanames/dto/ActivityDTO.java b/src/main/java/com/staketab/minanames/dto/ActivityDTO.java new file mode 100644 index 0000000..89a3dd5 --- /dev/null +++ b/src/main/java/com/staketab/minanames/dto/ActivityDTO.java @@ -0,0 +1,17 @@ +package com.staketab.minanames.dto; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ActivityDTO { + + private String id; + private String domainName; + private String ownerAddress; + private String transaction; + private String activity; + private String details; + private long timestamp; +} diff --git a/src/main/java/com/staketab/minanames/dto/request/BaseRequest.java b/src/main/java/com/staketab/minanames/dto/request/BaseRequest.java index 426108f..ccea8ba 100644 --- a/src/main/java/com/staketab/minanames/dto/request/BaseRequest.java +++ b/src/main/java/com/staketab/minanames/dto/request/BaseRequest.java @@ -25,7 +25,6 @@ public class BaseRequest { @Parameter(required = true, schema = @Schema(defaultValue = "DESC", allowableValues = {"ASC", "DESC"}), description = "Sorting method: from the lowest element to the highest (ASC) or from the highest element to the lowest (DESC).") private OrderBy orderBy; -// private String searchStr; @Parameter(hidden = true) private String sortByColumn; diff --git a/src/main/java/com/staketab/minanames/dto/request/sort/ActivitySortColumn.java b/src/main/java/com/staketab/minanames/dto/request/sort/ActivitySortColumn.java new file mode 100644 index 0000000..8af0141 --- /dev/null +++ b/src/main/java/com/staketab/minanames/dto/request/sort/ActivitySortColumn.java @@ -0,0 +1,12 @@ +package com.staketab.minanames.dto.request.sort; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ActivitySortColumn implements DbColumnProvider { + TIMESTAMP("timestamp"); + + private final String dbColumn; +} diff --git a/src/main/java/com/staketab/minanames/entity/ActivityEntity.java b/src/main/java/com/staketab/minanames/entity/ActivityEntity.java index e3efc39..c29d6aa 100644 --- a/src/main/java/com/staketab/minanames/entity/ActivityEntity.java +++ b/src/main/java/com/staketab/minanames/entity/ActivityEntity.java @@ -3,6 +3,7 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.Index; import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; @@ -18,7 +19,11 @@ @Builder @NoArgsConstructor @AllArgsConstructor -@Table(name = "activity") +@Table(name = "activity", indexes = { + @Index(name = "timestamp_index", columnList = "timestamp"), + @Index(name = "domain_name_index", columnList = "domain_name"), + @Index(name = "owner_address_index", columnList = "owner_address") +}) public class ActivityEntity { @Id @@ -37,8 +42,17 @@ public class ActivityEntity { @Column(name = "domain_name", columnDefinition = "TEXT") private String domainName; + @Column(name = "ipfs", columnDefinition = "TEXT") + private String ipfs; + private Long amount; - @CreationTimestamp - private LocalDateTime date; + @Column(name = "details") + private String details; + + @Column(name = "timestamp") + private long timestamp; + + @Column(name = "is_show") + private boolean isShow = false; } diff --git a/src/main/java/com/staketab/minanames/entity/ActivityStatus.java b/src/main/java/com/staketab/minanames/entity/ActivityStatus.java index 5297027..62b17b7 100644 --- a/src/main/java/com/staketab/minanames/entity/ActivityStatus.java +++ b/src/main/java/com/staketab/minanames/entity/ActivityStatus.java @@ -4,22 +4,28 @@ @Getter public enum ActivityStatus { - CREATE("Create new domain"), - CART_RESERVE("Reserve new domain for cart"), - DELETE_CART_RESERVE("Remove reserved domain for cart"), - APPLY_CART_RESERVED_DOMAINS("Apply cart reserved domains"), - SEND_DOMAIN_TO_ZK_CLOUD_WORKER("Send domain to zk cloud worker"), - SET_ACTIVE_STATUS_FOR_DOMAIN("Set active status for domain"), - UPDATE_DOMAIN("Update domain"), - FAILED("Failed tx"), - INCORRECT_AMOUNT("Incorrect amount"), - APPLIED("Applied tx"), - REMOVE_RESERVATION("Remove reservation"), - REMOVE_CART_RESERVATION("Remove cart reservation"); + CREATE("Create new domain", null, false), + CART_RESERVE("Reserve new domain for cart", null, false), + DELETE_CART_RESERVE("Remove reserved domain for cart", null, false), + APPLY_CART_RESERVED_DOMAINS("Apply cart reserved domains", null, false), + SEND_DOMAIN_TO_ZK_CLOUD_WORKER("Send domain to zk cloud worker", null, false), + SET_ACTIVE_STATUS_FOR_DOMAIN("Buy", "Price: %s MINA", true), + UPDATE_DOMAIN_IMAGE("Update record", "Image updated", true), + UPDATE_DOMAIN_DESCRIPTION("Update record", "Description updated", true), + FAILED("Failed tx", null, false), + INCORRECT_AMOUNT("Incorrect amount", null, false), + APPLIED("Applied tx", null, false), + REMOVE_RESERVATION("Remove reservation", null, false), + SET_DEFAULT_DOMAIN("Set as default domain", null, true), + REMOVE_CART_RESERVATION("Remove cart reservation", null, false); private final String message; + private final String details; + private final boolean isShow; - ActivityStatus(String message) { + ActivityStatus(String message, String details, boolean isShow) { this.message = message; + this.details = details; + this.isShow = isShow; } } diff --git a/src/main/java/com/staketab/minanames/entity/DomainEntity.java b/src/main/java/com/staketab/minanames/entity/DomainEntity.java index 8803371..39a5937 100644 --- a/src/main/java/com/staketab/minanames/entity/DomainEntity.java +++ b/src/main/java/com/staketab/minanames/entity/DomainEntity.java @@ -63,7 +63,7 @@ public class DomainEntity { @Column(name = "name", columnDefinition = "TEXT", unique = true) private String domainName; @Column(name = "img", columnDefinition = "TEXT") - private String domainImg; + private String domainImg; //remove? private Long amount; diff --git a/src/main/java/com/staketab/minanames/repository/ActivityRepository.java b/src/main/java/com/staketab/minanames/repository/ActivityRepository.java index b5008c9..702e62a 100644 --- a/src/main/java/com/staketab/minanames/repository/ActivityRepository.java +++ b/src/main/java/com/staketab/minanames/repository/ActivityRepository.java @@ -1,10 +1,20 @@ package com.staketab.minanames.repository; import com.staketab.minanames.entity.ActivityEntity; +import com.staketab.minanames.entity.DomainEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @Repository public interface ActivityRepository extends JpaRepository { + @Query(nativeQuery = true, + value = """ + select * + from activity + where owner_address = :address and is_show is true""") + Page findAllActivities(Pageable buildPageable, String address); } diff --git a/src/main/java/com/staketab/minanames/service/ActivityService.java b/src/main/java/com/staketab/minanames/service/ActivityService.java index b0fb9ac..1b941d7 100644 --- a/src/main/java/com/staketab/minanames/service/ActivityService.java +++ b/src/main/java/com/staketab/minanames/service/ActivityService.java @@ -1,13 +1,19 @@ package com.staketab.minanames.service; +import com.staketab.minanames.dto.ActivityDTO; +import com.staketab.minanames.dto.request.BaseRequest; import com.staketab.minanames.entity.DomainEntity; import com.staketab.minanames.entity.ActivityStatus; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import java.util.List; public interface ActivityService { - void saveActivity(DomainEntity domainEntity, ActivityStatus status); + Page findAllByPageable(BaseRequest baseRequest, String address); - void saveAllActivities(List domainEntities, ActivityStatus status); + void saveActivity(DomainEntity domainEntity, ActivityStatus status, String details); + + void saveAllActivities(List domainEntities, ActivityStatus status, String details); } diff --git a/src/main/java/com/staketab/minanames/service/impl/ActivityServiceImpl.java b/src/main/java/com/staketab/minanames/service/impl/ActivityServiceImpl.java index d3b4801..13e1a53 100644 --- a/src/main/java/com/staketab/minanames/service/impl/ActivityServiceImpl.java +++ b/src/main/java/com/staketab/minanames/service/impl/ActivityServiceImpl.java @@ -1,16 +1,22 @@ package com.staketab.minanames.service.impl; -import com.staketab.minanames.entity.DomainEntity; +import com.staketab.minanames.dto.ActivityDTO; +import com.staketab.minanames.dto.request.BaseRequest; import com.staketab.minanames.entity.ActivityEntity; import com.staketab.minanames.entity.ActivityStatus; +import com.staketab.minanames.entity.DomainEntity; import com.staketab.minanames.repository.ActivityRepository; import com.staketab.minanames.service.ActivityService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; +import static com.staketab.minanames.entity.ActivityStatus.SET_ACTIVE_STATUS_FOR_DOMAIN; + @Slf4j @Service @RequiredArgsConstructor @@ -19,26 +25,48 @@ public class ActivityServiceImpl implements ActivityService { private final ActivityRepository activityRepository; @Override - public void saveActivity(DomainEntity domainEntity, ActivityStatus status) { - activityRepository.save(buildActivityEntity(domainEntity, status)); + public Page findAllByPageable(BaseRequest baseRequest, String address) { + return activityRepository.findAllActivities(baseRequest.buildPageable(), address).map(this::buildActivityDTO); + } + + @Override + public void saveActivity(DomainEntity domainEntity, ActivityStatus status, String details) { + activityRepository.save(buildActivityEntity(domainEntity, status, details)); } @Override - public void saveAllActivities(List domainEntities, ActivityStatus status) { + public void saveAllActivities(List domainEntities, ActivityStatus status, String details) { List activityEntities = domainEntities .stream() - .map(domainEntity -> buildActivityEntity(domainEntity, status)) + .map(domainEntity -> buildActivityEntity(domainEntity, status, details)) .toList(); activityRepository.saveAll(activityEntities); } - private ActivityEntity buildActivityEntity(DomainEntity domainEntity, ActivityStatus status) { + private ActivityEntity buildActivityEntity(DomainEntity domainEntity, ActivityStatus status, String details) { + boolean equals = SET_ACTIVE_STATUS_FOR_DOMAIN.equals(status); return ActivityEntity.builder() .status(status.name()) - .txHash(domainEntity.getTransaction().getTxHash()) + .txHash(equals ? domainEntity.getTransaction().getTxHash() : null) .domainName(domainEntity.getDomainName()) - .amount(domainEntity.getAmount()) + .details(details) .ownerAddress(domainEntity.getOwnerAddress()) + .timestamp(System.currentTimeMillis()) + .ipfs(domainEntity.getIpfs()) + .amount(domainEntity.getAmount()) + .isShow(status.isShow()) + .build(); + } + + private ActivityDTO buildActivityDTO(ActivityEntity activityEntity) { + return ActivityDTO.builder() + .id(activityEntity.getId()) + .activity(ActivityStatus.valueOf(activityEntity.getStatus()).getMessage()) + .transaction(activityEntity.getTxHash()) + .details(activityEntity.getDetails()) + .timestamp(activityEntity.getTimestamp()) + .ownerAddress(activityEntity.getOwnerAddress()) + .domainName(activityEntity.getDomainName()) .build(); } } diff --git a/src/main/java/com/staketab/minanames/service/impl/DomainServiceImpl.java b/src/main/java/com/staketab/minanames/service/impl/DomainServiceImpl.java index 30b2e04..c01f94c 100644 --- a/src/main/java/com/staketab/minanames/service/impl/DomainServiceImpl.java +++ b/src/main/java/com/staketab/minanames/service/impl/DomainServiceImpl.java @@ -42,6 +42,7 @@ import static com.staketab.minanames.entity.ActivityStatus.DELETE_CART_RESERVE; import static com.staketab.minanames.entity.ActivityStatus.REMOVE_CART_RESERVATION; import static com.staketab.minanames.entity.ActivityStatus.REMOVE_RESERVATION; +import static com.staketab.minanames.entity.ActivityStatus.SET_DEFAULT_DOMAIN; import static com.staketab.minanames.entity.DomainStatus.PENDING; import static com.staketab.minanames.entity.DomainStatus.RESERVED; import static com.staketab.minanames.utils.Constants.DEFAULT_DENOMINATION; @@ -74,7 +75,7 @@ public DomainEntity create(DomainReservationDTO request) { throw new DuplicateKeyException(String.format("Domain already exist with name: %s", domainName)); }); DomainEntity domain = buildDomainEntity(request); - activityService.saveActivity(domain, CREATE); + activityService.saveActivity(domain, CREATE, null); return domainRepository.save(domain); } @@ -87,7 +88,7 @@ public DomainEntity reserve(DomainCartReservationDTO request) { throw new DuplicateKeyException(String.format("Domain already exist with name: %s", domainName)); }); DomainEntity domain = buildDomainEntityReserve(request); - activityService.saveActivity(domain, CART_RESERVE); + activityService.saveActivity(domain, CART_RESERVE, null); DomainEntity saved = domainRepository.save(domain); List domainEntities = domainRepository.findAllCartsReservedDomains(saved.getId()) .stream() @@ -130,7 +131,7 @@ public void applyReservedDomain(ApplyReservedDomainDTO domainRequest) { @Override public void removeReservedDomain(String id) { domainRepository.findById(id).ifPresent(domainEntity -> { - activityService.saveActivity(domainEntity, DELETE_CART_RESERVE); + activityService.saveActivity(domainEntity, DELETE_CART_RESERVE, null); domainRepository.delete(domainEntity); }); } @@ -162,7 +163,12 @@ public ReservedDomainDTO isNameReserved(String name) { @Override @Transactional public Boolean setDefaultDomain(String id) { - return domainRepository.setDefaultDomain(id) > 0; + boolean result = domainRepository.setDefaultDomain(id) > 0; + if (result) { + DomainEntity domainEntity = domainRepository.findById(id).get(); + activityService.saveActivity(domainEntity, SET_DEFAULT_DOMAIN, null); + } + return result; } @Override @@ -171,7 +177,7 @@ public void removeReservedDomains() { LocalDateTime localDateTime = LocalDateTime.now().minusDays(1); long currentTimestamp = Timestamp.valueOf(localDateTime).getTime(); List domainEntities = domainRepository.findAllByReservationTimestampLessThan(currentTimestamp, PENDING.name()); - activityService.saveAllActivities(domainEntities, REMOVE_RESERVATION); + activityService.saveAllActivities(domainEntities, REMOVE_RESERVATION, null); domainRepository.deleteAll(domainEntities); } @@ -180,7 +186,7 @@ public void removeCartReservedDomains() { LocalDateTime localDateTime = LocalDateTime.now().minusMinutes(10); long currentTimestamp = Timestamp.valueOf(localDateTime).getTime(); List domainEntities = domainRepository.findAllByReservationTimestampLessThan(currentTimestamp, RESERVED.name()); - activityService.saveAllActivities(domainEntities, REMOVE_CART_RESERVATION); + activityService.saveAllActivities(domainEntities, REMOVE_CART_RESERVATION, null); domainRepository.deleteAll(domainEntities); } @@ -196,7 +202,7 @@ private void saveUpdatedDomains(List domains, Map calculateTotalAmountByTransaction(List txs, ActivityStatus status) { List domainEntities = domainRepository.findAllByTransactionIn(txs); - activityService.saveAllActivities(domainEntities, status); + activityService.saveAllActivities(domainEntities, status, null); } private Map> generateMapOfFailedAndAppliedTxs(List payableTransactions) { diff --git a/src/main/java/com/staketab/minanames/service/impl/ZkCloudWorkerServiceImpl.java b/src/main/java/com/staketab/minanames/service/impl/ZkCloudWorkerServiceImpl.java index b8c78fa..1bb3c42 100644 --- a/src/main/java/com/staketab/minanames/service/impl/ZkCloudWorkerServiceImpl.java +++ b/src/main/java/com/staketab/minanames/service/impl/ZkCloudWorkerServiceImpl.java @@ -33,6 +33,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.List; @@ -44,7 +46,8 @@ import static com.staketab.minanames.entity.ActivityStatus.SEND_DOMAIN_TO_ZK_CLOUD_WORKER; import static com.staketab.minanames.entity.ActivityStatus.SET_ACTIVE_STATUS_FOR_DOMAIN; -import static com.staketab.minanames.entity.ActivityStatus.UPDATE_DOMAIN; +import static com.staketab.minanames.entity.ActivityStatus.UPDATE_DOMAIN_DESCRIPTION; +import static com.staketab.minanames.entity.ActivityStatus.UPDATE_DOMAIN_IMAGE; import static com.staketab.minanames.entity.DomainStatus.ACTIVE; import static com.staketab.minanames.entity.DomainStatus.PENDING; import static com.staketab.minanames.entity.ZkCloudWorkerDomainStatus.ACCEPTED; @@ -54,6 +57,7 @@ import static com.staketab.minanames.entity.ZkCloudWorkerTask.SEND_TRANSACTIONS; import static com.staketab.minanames.entity.ZkCloudWorkerTxOperation.ADD; import static com.staketab.minanames.entity.ZkCloudWorkerTxOperation.UPDATE; +import static com.staketab.minanames.utils.Constants.DEFAULT_DENOMINATION; @Slf4j @Service @@ -88,7 +92,7 @@ public void sendTxs(List appliedTxs) { ResponseEntity stringResponseEntity = zkCloudWorkerClient.sendToZkCloudWorker(zkCloudWorkerRequestDTO); setTxIds(stringResponseEntity, domainEntities); - activityService.saveAllActivities(domainEntities, SEND_DOMAIN_TO_ZK_CLOUD_WORKER); + activityService.saveAllActivities(domainEntities, SEND_DOMAIN_TO_ZK_CLOUD_WORKER, null); domainRepository.saveAll(entities); } @@ -201,7 +205,12 @@ private void activateDomains(Map m .collect(Collectors.toList()); domainEntities.removeAll(activeDomains); - activityService.saveAllActivities(activeDomains, SET_ACTIVE_STATUS_FOR_DOMAIN); + for (DomainEntity activeDomain : activeDomains) { + BigDecimal amount = BigDecimal.valueOf(activeDomain.getAmount()).divide(DEFAULT_DENOMINATION, RoundingMode.HALF_UP); + activityService.saveActivity(activeDomain, + SET_ACTIVE_STATUS_FOR_DOMAIN, + String.format(SET_ACTIVE_STATUS_FOR_DOMAIN.getDetails(), amount)); + } domainRepository.saveAll(activeDomains); } @@ -214,7 +223,7 @@ private void updateDomain(ZkCloudWorkerBlocksResponse finalBlock, String name, S setMetadata(newMetadata, domainEntity); domainEntity.setDomainMetadata(newMetadata); domainEntity.setBlockNumber(finalBlock.getBlockNumber()); - activityService.saveActivity(domainEntity, UPDATE_DOMAIN); + domainEntity.setIpfs(finalBlock.getIpfs()); domainRepository.save(domainEntity); } @@ -224,10 +233,12 @@ private void setMetadata(String newMetadata, DomainEntity domainEntity) { IpfsDomainMetadataNftMetadataZkDataDTO image = properties.get(IpfsMetadataCloudWorkerProperty.IMAGE.getName()); if (image != null) { domainEntity.setIpfsImg(mapIpfsImgToString(image)); + activityService.saveActivity(domainEntity, UPDATE_DOMAIN_IMAGE, UPDATE_DOMAIN_IMAGE.getDetails()); } IpfsDomainMetadataNftMetadataZkDataDTO description = properties.get(IpfsMetadataCloudWorkerProperty.DESCRIPTION.getName()); if (description != null) { domainEntity.setDescription(description.getLinkedObject().getText()); + activityService.saveActivity(domainEntity, UPDATE_DOMAIN_DESCRIPTION, UPDATE_DOMAIN_DESCRIPTION.getDetails()); } }