mirror of
https://github.com/carm-outsource/TimeReward.git
synced 2026-06-04 07:18:16 +08:00
fix(time): 修复可能出现的时间异常问题
This commit is contained in:
@@ -14,6 +14,7 @@ import cc.carm.plugin.timereward.listener.UserListener;
|
||||
import cc.carm.plugin.timereward.manager.RewardManager;
|
||||
import cc.carm.plugin.timereward.manager.UserManager;
|
||||
import cc.carm.plugin.timereward.storage.database.MySQLStorage;
|
||||
import cc.carm.plugin.timereward.util.DateTimeUtils;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
@@ -49,6 +50,8 @@ public class Main extends EasyPlugin {
|
||||
|
||||
this.rewardProvider = BukkitConfigFactory.from(new File(getDataFolder(), "rewards.yml")).build();
|
||||
this.rewardProvider.initialize(RewardsConfig.class);
|
||||
|
||||
DateTimeUtils.WEEK_START_DAY = PluginConfig.WEEK_FIRST_DAY::resolve;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,13 @@ public interface PluginConfig extends Configuration {
|
||||
})
|
||||
ConfiguredValue<Long> AUTO_SAVE = ConfiguredValue.of(Long.class, 60L);
|
||||
|
||||
@HeaderComments({
|
||||
"是否在保存时优先使用数据库中的数据。",
|
||||
"若启用,则会在保存数据时在数据库中重新读取数据计算时间,以确利用最新数据;",
|
||||
"若关闭,则保存时会直接采用进入服务器时所加载的数据进行计算。"
|
||||
})
|
||||
ConfiguredValue<Boolean> USE_STORAGE_DATA = ConfiguredValue.of(Boolean.class, true);
|
||||
|
||||
@HeaderComments("周起始日,用于判断周度奖励的结算日期。")
|
||||
ConfiguredValue<DayOfWeek> WEEK_FIRST_DAY = ConfiguredValue.builderOf(DayOfWeek.class)
|
||||
.from(Integer.class)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cc.carm.plugin.timereward.data;
|
||||
|
||||
import cc.carm.plugin.timereward.util.DateTimeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -11,62 +12,64 @@ import java.util.function.Predicate;
|
||||
public enum IntervalType {
|
||||
|
||||
TOTAL(
|
||||
0, time -> false,
|
||||
0, Duration.ofSeconds(4294967295L), time -> false,
|
||||
(timeRecord, join) -> Duration.between(join, LocalDateTime.now()).plus(timeRecord.getTotalTime())
|
||||
),
|
||||
|
||||
DAILY(
|
||||
1, time -> time.isBefore(TimeRecord.getTodayStart()),
|
||||
1, Duration.ofDays(1), time -> DateTimeUtils.sameDay(time.toLocalDate()),
|
||||
(timeRecord, join) -> {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (now.toLocalDate().isEqual(timeRecord.getDate())) {
|
||||
// 和记录还在同一天
|
||||
return Duration.between(join, now).plus(timeRecord.getDailyTime());
|
||||
} else if (now.toLocalDate().isEqual(join.toLocalDate())) {
|
||||
// 加入的时间和现在的时间在同一天
|
||||
return Duration.between(join, now);
|
||||
} else {
|
||||
if (!now.toLocalDate().isEqual(join.toLocalDate())) {
|
||||
// 加入的时间和现在的时间不在同一天
|
||||
return Duration.between(TimeRecord.getTodayStart(), now);
|
||||
return Duration.between(DateTimeUtils.todayStartTime(), now);
|
||||
}
|
||||
if (now.toLocalDate().isEqual(timeRecord.getDate())) { // 和记录还在同一天
|
||||
return Duration.between(join, now).plus(timeRecord.getDailyTime());
|
||||
}
|
||||
return Duration.between(join, now); // 加入的时间和现在的时间在同一天
|
||||
}
|
||||
),
|
||||
WEEKLY(
|
||||
2, time -> time.isBefore(TimeRecord.getThisWeekStart()),
|
||||
(timeRecord, join) -> {
|
||||
2, Duration.ofDays(7),
|
||||
time -> !DateTimeUtils.sameWeek(time),
|
||||
(r, join) -> {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (TimeRecord.isSameWeek(timeRecord.getDate(), now)) {
|
||||
return Duration.between(join, now).plus(timeRecord.getWeeklyTime());
|
||||
} else if (TimeRecord.isSameWeek(join, now)) {
|
||||
return Duration.between(join, now);
|
||||
} else {
|
||||
return Duration.between(TimeRecord.getThisWeekStart(), now);
|
||||
if (!DateTimeUtils.sameWeek(join, now)) {
|
||||
return Duration.between(DateTimeUtils.currentWeekStartTime(), now);
|
||||
}
|
||||
if (DateTimeUtils.sameWeek(r.getDate(), now)) {
|
||||
return Duration.between(join, now).plus(r.getWeeklyTime());
|
||||
}
|
||||
return Duration.between(join, now);
|
||||
}
|
||||
),
|
||||
|
||||
MONTHLY(
|
||||
3, time -> time.isBefore(TimeRecord.getThisMonthStart()),
|
||||
(timeRecord, join) -> {
|
||||
3, Duration.ofDays(31),
|
||||
time -> !DateTimeUtils.sameMonth(time.toLocalDate()),
|
||||
(r, join) -> {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (TimeRecord.isSameMonth(timeRecord.getDate(), now.toLocalDate())) {
|
||||
return Duration.between(join, now).plus(timeRecord.getMonthlyTime());
|
||||
} else if (TimeRecord.isSameMonth(join.toLocalDate(), now.toLocalDate())) {
|
||||
return Duration.between(join, now);
|
||||
} else {
|
||||
return Duration.between(TimeRecord.getThisMonthStart(), now);
|
||||
if (!DateTimeUtils.sameMonth(join.toLocalDate(), now.toLocalDate())) {
|
||||
return Duration.between(DateTimeUtils.currentMonthStartTime(), now);
|
||||
}
|
||||
if (DateTimeUtils.sameMonth(r.getDate(), now.toLocalDate())) {
|
||||
return Duration.between(join, now).plus(r.getMonthlyTime());
|
||||
}
|
||||
return Duration.between(join, now);
|
||||
}
|
||||
);
|
||||
|
||||
private final int id;
|
||||
private final @NotNull Duration maxDuration;
|
||||
private final @NotNull Predicate<LocalDateTime> periodChangePredicate;
|
||||
private final @NotNull BiFunction<TimeRecord, LocalDateTime, Duration> calculator;
|
||||
|
||||
IntervalType(int id,
|
||||
IntervalType(int id, @NotNull Duration maxDuration,
|
||||
@NotNull Predicate<LocalDateTime> periodChangePredicate,
|
||||
@NotNull BiFunction<TimeRecord, LocalDateTime, Duration> calculator) {
|
||||
this.id = id;
|
||||
this.maxDuration = maxDuration;
|
||||
this.periodChangePredicate = periodChangePredicate;
|
||||
this.calculator = calculator;
|
||||
}
|
||||
@@ -75,20 +78,26 @@ public enum IntervalType {
|
||||
return id;
|
||||
}
|
||||
|
||||
public @NotNull BiFunction<TimeRecord, LocalDateTime, Duration> getCalculator() {
|
||||
public @NotNull Duration getMaxDuration() {
|
||||
return maxDuration;
|
||||
}
|
||||
|
||||
public @NotNull BiFunction<TimeRecord, LocalDateTime, Duration> calculator() {
|
||||
return calculator;
|
||||
}
|
||||
|
||||
public @NotNull Predicate<LocalDateTime> getPreiodChangePredicate() {
|
||||
public @NotNull Predicate<LocalDateTime> changePredicator() {
|
||||
return periodChangePredicate;
|
||||
}
|
||||
|
||||
public Duration calculate(@NotNull TimeRecord timeRecord, @NotNull LocalDateTime joinTime) {
|
||||
return calculator.apply(timeRecord, joinTime);
|
||||
Duration result = calculator.apply(timeRecord, joinTime);
|
||||
// 如果超过最大值,则返回最大值
|
||||
return result.compareTo(maxDuration) > 0 ? maxDuration : result;
|
||||
}
|
||||
|
||||
public boolean isPeriodChanged(@NotNull LocalDateTime claimedDate) {
|
||||
return periodChangePredicate.test(claimedDate);
|
||||
return changePredicator().test(claimedDate);
|
||||
}
|
||||
|
||||
public static IntervalType parse(String input) {
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package cc.carm.plugin.timereward.data;
|
||||
|
||||
import cc.carm.plugin.timereward.conf.PluginConfig;
|
||||
import cc.carm.plugin.timereward.util.DateTimeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.temporal.WeekFields;
|
||||
|
||||
public class TimeRecord {
|
||||
|
||||
@@ -16,7 +12,6 @@ public class TimeRecord {
|
||||
return new TimeRecord(LocalDate.now(), 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
protected final @NotNull LocalDate date;
|
||||
|
||||
protected final @NotNull Duration daily;
|
||||
@@ -68,37 +63,14 @@ public class TimeRecord {
|
||||
|
||||
public boolean isWeekUpdated() {
|
||||
if (!isDayUpdated()) return false; // Same day always same week
|
||||
return !isSameWeek(LocalDate.now(), getDate());
|
||||
return !DateTimeUtils.sameWeek(LocalDate.now(), getDate());
|
||||
}
|
||||
|
||||
public boolean isMonthUpdated() {
|
||||
if (!isDayUpdated()) return false; // Same day always same month
|
||||
|
||||
// Predicate current month is after the month of the date
|
||||
return LocalDate.now().getMonth().compareTo(getDate().getMonth()) > 0;
|
||||
return !DateTimeUtils.sameMonth(LocalDate.now(), getDate());
|
||||
}
|
||||
|
||||
public static boolean isSameWeek(Temporal a, Temporal b) {
|
||||
TemporalField woy = WeekFields.of(PluginConfig.WEEK_FIRST_DAY.getNotNull(), 4).weekOfWeekBasedYear();
|
||||
return a.get(woy) == b.get(woy);
|
||||
}
|
||||
|
||||
public static boolean isSameMonth(LocalDate a, LocalDate b) {
|
||||
return a.getMonth().equals(b.getMonth());
|
||||
}
|
||||
|
||||
public static LocalDateTime getTodayStart() {
|
||||
return LocalDate.now().atTime(0, 0);
|
||||
}
|
||||
|
||||
public static LocalDateTime getThisWeekStart() {
|
||||
TemporalField woy = WeekFields.of(PluginConfig.WEEK_FIRST_DAY.getNotNull(), 4).weekOfWeekBasedYear();
|
||||
return LocalDate.now().with(woy, 1).atTime(0, 0);
|
||||
}
|
||||
|
||||
public static LocalDateTime getThisMonthStart() {
|
||||
return LocalDate.now().withDayOfMonth(1).atTime(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -22,7 +22,7 @@ public enum DatabaseTables implements SQLTable {
|
||||
|
||||
table.addColumn("daily_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户日在线时间(秒)
|
||||
table.addColumn("weekly_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户周在线时间(秒)
|
||||
table.addColumn("monthly_time", "INT UNSIGNED NOT NULL DEFAULT 0"); // 用户月在线时间(秒)
|
||||
table.addColumn("monthly_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户月在线时间(秒)
|
||||
table.addColumn("total_time", "INT UNSIGNED NOT NULL DEFAULT 0"); // 用户总在线时间(秒)
|
||||
|
||||
table.addColumn("update",
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package cc.carm.plugin.timereward.util;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DateTimeUtils {
|
||||
|
||||
public static Supplier<DayOfWeek> WEEK_START_DAY = () -> DayOfWeek.MONDAY;
|
||||
|
||||
public static boolean sameDay(LocalDate a) {
|
||||
return sameDay(a, LocalDate.now());
|
||||
}
|
||||
|
||||
public static boolean sameDay(LocalDate a, LocalDate b) {
|
||||
return a.getDayOfYear() == b.getDayOfYear() && a.getYear() == b.getYear();
|
||||
}
|
||||
|
||||
public static boolean sameWeek(Temporal a) {
|
||||
return sameWeek(WEEK_START_DAY.get(), a, LocalDate.now());
|
||||
}
|
||||
|
||||
public static boolean sameWeek(DayOfWeek weekStart, Temporal a) {
|
||||
return sameWeek(weekStart, a, LocalDate.now());
|
||||
}
|
||||
|
||||
public static boolean sameWeek(Temporal a, Temporal b) {
|
||||
return sameWeek(WEEK_START_DAY.get(), a, b);
|
||||
}
|
||||
|
||||
public static boolean sameWeek(DayOfWeek weekStart, Temporal a, Temporal b) {
|
||||
TemporalField woy = WeekFields.of(weekStart, 4).weekOfWeekBasedYear();
|
||||
return a.get(woy) == b.get(woy);
|
||||
}
|
||||
|
||||
public static boolean sameMonth(LocalDate a) {
|
||||
return sameMonth(a, LocalDate.now());
|
||||
}
|
||||
|
||||
public static boolean sameMonth(LocalDate a, LocalDate b) {
|
||||
return a.getMonth().equals(b.getMonth());
|
||||
}
|
||||
|
||||
public static LocalDateTime todayStartTime() {
|
||||
return LocalDate.now().atTime(0, 0);
|
||||
}
|
||||
|
||||
public static LocalDate currentWeekStartDay(DayOfWeek weekStart) {
|
||||
return LocalDate.now().with(TemporalAdjusters.previousOrSame(weekStart));
|
||||
}
|
||||
|
||||
public static LocalDate currentWeekStartDay() {
|
||||
return currentWeekStartDay(WEEK_START_DAY.get());
|
||||
}
|
||||
|
||||
public static LocalDateTime currentWeekStartTime(DayOfWeek weekStart) {
|
||||
return currentWeekStartDay(weekStart).atTime(0, 0);
|
||||
}
|
||||
|
||||
public static LocalDateTime currentWeekStartTime() {
|
||||
return currentWeekStartTime(WEEK_START_DAY.get());
|
||||
}
|
||||
|
||||
public static LocalDateTime currentMonthStartTime() {
|
||||
return LocalDate.now().withDayOfMonth(1).atTime(0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.temporal.WeekFields;
|
||||
|
||||
public class TimeTest {
|
||||
|
||||
LocalDate date = LocalDate.of(2023, 8, 30);
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
LocalDate date1 = LocalDate.of(2023, 9, 2);
|
||||
LocalDate date2 = LocalDate.of(2023, 8, 28);
|
||||
LocalDate date3 = LocalDate.of(2023, 8, 27);
|
||||
|
||||
System.out.println(isDayUpdated(date1));
|
||||
System.out.println(isWeekUpdated(date1));
|
||||
System.out.println(isMonthUpdated(date1));
|
||||
|
||||
System.out.println(isDayUpdated(date2));
|
||||
System.out.println(isWeekUpdated(date2));
|
||||
System.out.println(isMonthUpdated(date2));
|
||||
|
||||
System.out.println(isDayUpdated(date3));
|
||||
System.out.println(isWeekUpdated(date3));
|
||||
System.out.println(isMonthUpdated(date3));
|
||||
|
||||
System.out.println(isDayUpdated(date));
|
||||
|
||||
}
|
||||
|
||||
public @NotNull LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public boolean isDayUpdated(LocalDate d) {
|
||||
return !d.equals(getDate());
|
||||
}
|
||||
|
||||
public boolean isWeekUpdated(LocalDate d) {
|
||||
if (!isDayUpdated(d)) return false; // Same day always same week
|
||||
|
||||
TemporalField woy = WeekFields.of(DayOfWeek.MONDAY, 4).weekOfWeekBasedYear();
|
||||
return getDate().get(woy) != d.get(woy);
|
||||
}
|
||||
|
||||
public boolean isMonthUpdated(LocalDate d) {
|
||||
if (!isDayUpdated(d)) return false; // Same day always same month
|
||||
|
||||
// Predicate current month is after the month of the date
|
||||
return d.getMonth().compareTo(getDate().getMonth()) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package cc.carm.plugin.tests;
|
||||
|
||||
import cc.carm.plugin.timereward.data.IntervalType;
|
||||
import cc.carm.plugin.timereward.data.TimeRecord;
|
||||
import cc.carm.plugin.timereward.user.UserRewardData;
|
||||
import cc.carm.plugin.timereward.util.DateTimeUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DataTest {
|
||||
|
||||
DateTimeFormatter DATETIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
LocalDate date = LocalDate.of(2025, 5, 1);
|
||||
LocalDateTime dateTime = LocalDateTime.now().minusHours(10).minusSeconds(600);
|
||||
|
||||
System.out.println("-----------------------------------------");
|
||||
System.out.println(" DURATION: " + Duration.between(dateTime, LocalDateTime.now()).getSeconds());
|
||||
System.out.println("TODAY START: " + DATETIME.format(DateTimeUtils.todayStartTime()));
|
||||
System.out.println("WEEK START: " + DATETIME.format(DateTimeUtils.currentWeekStartTime()));
|
||||
System.out.println("MONTH START: " + DATETIME.format(DateTimeUtils.currentMonthStartTime()));
|
||||
System.out.println("-----------------------------------------");
|
||||
System.out.println("RECORD DATE: " + DateTimeFormatter.ofPattern("yyyy-MM-dd").format(date));
|
||||
System.out.println(" JOIN TIME: " + DATETIME.format(dateTime));
|
||||
System.out.println("-----------------------------------------");
|
||||
|
||||
UserRewardData data = createData(date, dateTime);
|
||||
for (IntervalType value : IntervalType.values()) {
|
||||
System.out.println(value.name() + " = " + data.getOnlineDuration(value).getSeconds());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static UserRewardData createData(LocalDate date, LocalDateTime join) {
|
||||
return new UserRewardData(
|
||||
UUID.randomUUID(), new HashMap<>(),
|
||||
new TimeRecord(date, 0, 0, 0, 0),
|
||||
join
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cc.carm.plugin.tests;
|
||||
|
||||
import cc.carm.plugin.timereward.data.IntervalType;
|
||||
import cc.carm.plugin.timereward.data.TimeRecord;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class TimeTest {
|
||||
|
||||
LocalDate date = LocalDate.of(2023, 8, 30);
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
TimeRecord record1 = new TimeRecord(LocalDate.now().minusMonths(1), 0, 0, 0, 0);
|
||||
TimeRecord record2 = new TimeRecord(LocalDate.now().minusWeeks(1), 0, 0, 0, 0);
|
||||
TimeRecord record3 = new TimeRecord(LocalDate.now().minusDays(1), 0, 0, 0, 0);
|
||||
|
||||
System.out.println(record1.isDayUpdated());
|
||||
System.out.println(record1.isWeekUpdated());
|
||||
System.out.println(record1.isMonthUpdated());
|
||||
|
||||
System.out.println(record2.isDayUpdated());
|
||||
System.out.println(record2.isWeekUpdated());
|
||||
System.out.println(record2.isMonthUpdated());
|
||||
|
||||
System.out.println(record3.isDayUpdated());
|
||||
System.out.println(record3.isWeekUpdated());
|
||||
System.out.println(record3.isMonthUpdated());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void durationTest() {
|
||||
for (IntervalType value : IntervalType.values()) {
|
||||
System.out.println(value.name() + " = <MAX> = " + value.getMaxDuration().getSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user