Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package org.youngmonkeys.personal.admin.appender;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.tvd12.ezyfox.bean.annotation.EzySingleton;
import org.youngmonkeys.ezyplatform.admin.appender.AdminDataAppender;
import org.youngmonkeys.ezyplatform.admin.service.AdminSettingService;
import org.youngmonkeys.ezyplatform.time.ClockProxy;
import org.youngmonkeys.personal.admin.repo.AdminPersonalCoinPriceRepository;
import org.youngmonkeys.personal.admin.service.AminPersonalCoinPriceService;
import org.youngmonkeys.personal.entity.PersonalCoinPrice;
import org.youngmonkeys.personal.result.CoinPriceApiResult;
import org.youngmonkeys.personal.service.PersonalCoinPriceService;

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static org.youngmonkeys.personal.constant.PersonalConstants.COIN_SYMBOLS;

@EzySingleton
public class AdminPersonalCoinPriceDataAppender
extends AdminDataAppender<CoinPriceApiResult, PersonalCoinPrice, Long> {

private final ClockProxy clock;
private final AminPersonalCoinPriceService adminCoinPriceService;
private final PersonalCoinPriceService coinPriceService;
private final AdminPersonalCoinPriceRepository coinPriceRepository;
private long lastCallTime = System.currentTimeMillis();
private static final int PERIOD = 60 * 1000;

public AdminPersonalCoinPriceDataAppender(
ClockProxy clock,
ObjectMapper objectMapper,
AminPersonalCoinPriceService adminCoinPriceService,
PersonalCoinPriceService coinPriceService,
AdminSettingService settingService,
AdminPersonalCoinPriceRepository coinPriceRepository
) {
super(
objectMapper,
settingService
);
this.clock = clock;
this.adminCoinPriceService = adminCoinPriceService;
this.coinPriceService = coinPriceService;
this.coinPriceRepository = coinPriceRepository;
}

@Override
protected List<CoinPriceApiResult> getValueList(
Long lastTimestamp
) {
long callTime = lastCallTime + PERIOD;
long now = System.currentTimeMillis();
if (callTime < now) {
lastCallTime = now;
return Arrays.asList(adminCoinPriceService.fetchCoinPrice());
}
return Collections.emptyList();
}

@Override
protected PersonalCoinPrice toDataRecord(CoinPriceApiResult value) {
PersonalCoinPrice entity = new PersonalCoinPrice();
entity.setSymbol(value.getBaseSymbol());
entity.setName(value.getBaseName());
entity.setPrice(value.getPrice());
entity.setPriceChange(value.getPriceChange());
entity.setUpdatedAt(clock.nowDateTime());

return entity;
}

@Override
protected void addDataRecords(List<PersonalCoinPrice> dataRecords) {
List<CoinPriceApiResult> lastestResults =
new ArrayList<>(coinPriceService.getLatestPrices(COIN_SYMBOLS));
List<CoinPriceApiResult> incomingResults = dataRecords.stream()
.map(coin -> {
CoinPriceApiResult result = new CoinPriceApiResult();
result.setPrice(coin.getPrice());
result.setBaseSymbol(coin.getSymbol());
result.setBaseName(coin.getName());
result.setPriceChange(coin.getPriceChange());
return result;
}).collect(Collectors.toList());
if (adminCoinPriceService.isAnyCoinChanged(lastestResults, incomingResults)) {
coinPriceRepository.save(dataRecords);
}
}

@Override
protected Long extractNewLastPageToken(List<CoinPriceApiResult> list, Long aLong) {
return clock.nowDateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}

@Override
protected String getAppenderNamePrefix() {
return "personal_coin_price";
}

@Override
protected Long defaultPageToken() {
return 0L;
}

@Override
protected Class<Long> pageTokenType() {
return Long.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public ResponseEntity settingsPut(
request.isAllowCalculatePostReadTime()
)
)
.registerOperation(() ->
personalSettingService.setShowCoinWidget(
request.isShowCoinWidget()
)
)
.blockingExecute();
return ResponseEntity.noContent();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public View settingsGet() {
"allowCalculatePostReadTime",
personalSettingService.isAllowCalculatePostReadTime()
)
.addVariable(
"showCoinWidget",
personalSettingService.isShowCoinWidget()
)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.youngmonkeys.personal.admin.event;

import com.tvd12.ezyfox.bean.annotation.EzySingleton;
import lombok.AllArgsConstructor;
import org.youngmonkeys.ezyplatform.event.AbstractEventHandler;

import java.util.Map;

import static org.youngmonkeys.personal.constant.PersonalConstants.INTERNAL_EVENT_NAME_COIN_PRICE_UPDATE;

@EzySingleton
@AllArgsConstructor
public class AdminCoinPriceRequestEventHandler
extends AbstractEventHandler<Map<String, Object>, Void> {

@Override
public String getEventName() {
return INTERNAL_EVENT_NAME_COIN_PRICE_UPDATE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.youngmonkeys.personal.admin.repo;

import com.tvd12.ezydata.database.EzyDatabaseRepository;
import com.tvd12.ezyfox.database.annotation.EzyRepository;
import org.youngmonkeys.personal.entity.PersonalCoinPrice;
import org.youngmonkeys.personal.repo.PersonalCoinPriceRepository;

@EzyRepository
public interface AdminPersonalCoinPriceRepository extends
PersonalCoinPriceRepository, EzyDatabaseRepository<Long, PersonalCoinPrice> {}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@
@Setter
public class AdminPersonalSaveSettingsRequest {
private boolean allowCalculatePostReadTime;
private boolean showCoinWidget;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.youngmonkeys.personal.service.PersonalSettingService;

import static org.youngmonkeys.personal.constant.PersonalConstants.SETTING_KEY_ALLOW_CALCULATE_POST_READ_TIME;
import static org.youngmonkeys.personal.constant.PersonalConstants.SETTING_KEY_SHOW_COIN_PRICE;

@Service
public class AdminPersonalSettingService extends PersonalSettingService {
Expand All @@ -24,4 +25,11 @@ public void setAllowCalculatePostReadTime(boolean allow) {
allow
);
}

public void setShowCoinWidget(boolean allow) {
settingService.setBooleanValue(
SETTING_KEY_SHOW_COIN_PRICE,
allow
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.youngmonkeys.personal.admin.service;

import com.tvd12.ezyfox.bean.annotation.EzySingleton;
import com.tvd12.ezyfox.util.EzyLoggable;
import com.tvd12.ezyhttp.client.HttpClient;
import com.tvd12.ezyhttp.client.request.GetRequest;
import com.tvd12.ezyhttp.client.request.Request;
import lombok.AllArgsConstructor;
import org.youngmonkeys.personal.result.CoinPriceApiResult;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.youngmonkeys.personal.constant.PersonalConstants.API_URL_COIN_PRICE;

@EzySingleton
@AllArgsConstructor
public class AminPersonalCoinPriceService extends EzyLoggable {

private final HttpClient httpClient;

public CoinPriceApiResult[] fetchCoinPrice() {
try {
Request request = new GetRequest()
.setURL(API_URL_COIN_PRICE)
.setResponseType(200, CoinPriceApiResult[].class);
return httpClient.call(request);
} catch (Exception e) {
logger.warn("fetch coin price error", e);
}
return new CoinPriceApiResult[0];
}

private boolean isChanged(
CoinPriceApiResult oldValue,
CoinPriceApiResult newValue
) {
if (oldValue == null || newValue == null) {
return true;
}

return !Objects.equals(oldValue.getPrice(), newValue.getPrice());
}

public boolean isAnyCoinChanged(
List<CoinPriceApiResult> oldList,
List<CoinPriceApiResult> newList
) {
if (oldList == null || newList == null) {
return true;
}
Map<String, CoinPriceApiResult> oldMap =
oldList.stream()
.collect(Collectors.toMap(
CoinPriceApiResult::getBaseSymbol,
Function.identity()
));
for (CoinPriceApiResult newValue : newList) {
CoinPriceApiResult oldValue = oldMap.get(newValue.getBaseSymbol());

if (isChanged(oldValue, newValue)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dashboard=B\u1EA3ng \u0111i\u1EC1u khi\u1EC3n
dashboard_posts=C\u00E1c b\u00E0i vi\u1EBFt
post_word_count_appender=Tr\u00ECnh \u0111\u1EBFm s\u1ED1 t\u1EEB c\u1EE7a b\u00E0i vi\u1EBFt
coin_price_appender=Tr\u00ECnh hi\u1EC3n th\u1ECB gi\u00E1 coin
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS `personal_coin_price` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`symbol` varchar(10) COLLATE utf8mb4_unicode_520_ci NOT NULL,
`name` varchar(50) COLLATE utf8mb4_unicode_520_ci NOT NULL,
`price` char(85) COLLATE utf8mb4_unicode_520_ci NOT NULL,
`price_change` char(85) COLLATE utf8mb4_unicode_520_ci,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
<div class="col-md-9 col-7">
<th:block th:replace="~{fragments/data-appender :: actions(dataAppenderName='personal_post_word_count')}" />
</div>
<div class="col-12 mt-3"></div>
<div class="col-md-3 col-5">
<span>[[#{coin_price_appender}]]</span>
</div>
<div class="col-md-9 col-7">
<th:block th:replace="~{fragments/data-appender :: actions(dataAppenderName='personal_coin_price')}" />
</div>
<div class="col-md-12">
<hr class="mb-4"/>
</div>
Expand All @@ -31,6 +38,16 @@
<i class="far fa-times-circle"></i> <span id="allowCalculatePostReadTimeError">[[#{invalid}]]</span>
</label>
</div>
<div class="form-group row">
<label for="showCoinWidget" class="col-sm-3 col-form-label">[[#{coin_widget_enable}]]</label>
<div class="col-lg-7 col-md-6 d-flex align-items-center">
<input type="checkbox" name="showCoinWidget" id="showCoinWidget" class="wh-1rem"
th:checked="${showCoinWidget}">
</div>
<label id="showCoinWidgetErrorLabel" class="col-lg-2 col-md-3 col-form-label text-danger d-none" for="showCoinWidget">
<i class="far fa-times-circle"></i> <span id="showCoinWidgetError">[[#{invalid}]]</span>
</label>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-2 mb-2">
Expand All @@ -55,6 +72,8 @@
var formData = ezyadmin.formDataToObject('updateSettingsForm');
formData.allowCalculatePostReadTime = $('#allowCalculatePostReadTime')
.prop('checked');
formData.showCoinWidget = $('#showCoinWidget')
.prop('checked');
$.ajax({
type: 'PUT',
url: '/personal/api/v1/settings',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.youngmonkeys.personal.admin.it.repo;

import com.tvd12.ezyfox.bean.annotation.EzySingleton;
import com.tvd12.test.assertion.Asserts;
import lombok.AllArgsConstructor;
import org.youngmonkeys.devtools.InstanceRandom;
import org.youngmonkeys.ezyplatform.test.IntegrationTest;
import org.youngmonkeys.personal.admin.repo.AdminPersonalCoinPriceRepository;
import org.youngmonkeys.personal.entity.PersonalCoinPrice;

@EzySingleton
@AllArgsConstructor
public class AdminPersonalCoinPriceRepositoryIT implements IntegrationTest {

private final AdminPersonalCoinPriceRepository personalCoinPriceRepository;

@Override
public void test() throws Exception {
saveFindTest();
}

private void saveFindTest() {
// given
PersonalCoinPrice entity = new InstanceRandom().randomObject(PersonalCoinPrice.class);
entity.setSymbol("test");

// when
personalCoinPriceRepository.save(entity);
PersonalCoinPrice actual = personalCoinPriceRepository.findById(entity.getId());

// then
Asserts.assertNotNull(actual);
personalCoinPriceRepository.delete(entity.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,19 @@ public final class PersonalConstants {
public static final String SETTING_KEY_ALLOW_CALCULATE_POST_READ_TIME =
"allow_calculate_post_read_time";

public static final String TABLE_NAME_COIN_PRICE =
"personal_coin_price";

public static final String INTERNAL_EVENT_NAME_COIN_PRICE_UPDATE =
"personal_coin_price";

public static final String COIN_SYMBOLS = "BTC,ETH,BNB,LTC,XRP";

public static final String API_URL_COIN_PRICE =
"https://coinyep.com/api/v1/?list=" + COIN_SYMBOLS;

public static final String SETTING_KEY_SHOW_COIN_PRICE =
"show_coin_widget";

private PersonalConstants() {}
}
Loading