From b5fc043aaf2ba4bb07b44d6b53c9211f3303e77c Mon Sep 17 00:00:00 2001 From: gsd Date: Sun, 10 May 2026 13:55:25 +0300 Subject: [PATCH] extra graph --- .../controllers/user/PublicController.java | 22 ++++++ .../controllers/user/UsertimeController.java | 50 ++++++++++++ src/main/java/app/entities/SearchFilter.java | 26 +++++++ .../db/period/PerPeriodStatistic.java | 33 ++++++++ .../java/app/services/db/GraphService.java | 76 +++++++++++++++++++ 5 files changed, 207 insertions(+) create mode 100644 src/main/java/app/controllers/user/UsertimeController.java create mode 100644 src/main/java/app/entities/db/period/PerPeriodStatistic.java create mode 100644 src/main/java/app/services/db/GraphService.java diff --git a/src/main/java/app/controllers/user/PublicController.java b/src/main/java/app/controllers/user/PublicController.java index 14eb490..4822605 100644 --- a/src/main/java/app/controllers/user/PublicController.java +++ b/src/main/java/app/controllers/user/PublicController.java @@ -5,9 +5,11 @@ import app.annotations.enums.FirstTouch; import app.annotations.interfaces.CheckWebAccess; import app.annotations.interfaces.CollectStatistic; import app.annotations.interfaces.WaitAfterNext; +import app.entities.SearchFilter; import app.entities.db.Annonce; import app.entities.db.ban.Ban; import app.entities.db.ban.BanSearchFilter; +import app.entities.db.period.PerPeriodStatistic; import app.entities.report.Report; import app.entities.report.ReportSearchFilter; import app.repositories.AnnonceRepository; @@ -15,6 +17,7 @@ import app.repositories.BanRepository; import app.repositories.ReportRepository; import app.services.ProfileService; import app.services.db.BanService; +import app.services.db.GraphService; import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -24,6 +27,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.sql.Timestamp; +import java.time.Instant; import java.util.HashMap; import java.util.List; @@ -48,6 +53,9 @@ public class PublicController { @Autowired private ReportRepository reportRepository; + @Autowired + private GraphService graphService; + @Autowired public PublicController(BanService banService) { this.banService = banService; @@ -122,4 +130,18 @@ public class PublicController { public ResponseEntity> getAnnonces(Pageable pageable) { return ResponseEntity.ok(annonceRepository.getAnnonce(pageable)); } + + @PostMapping("/vip/graph") + @WaitAfterNext(order = "vipgraph") + public ResponseEntity> getTimeOnPeriod(@RequestBody(required = false) SearchFilter searchFilter) { + if (searchFilter == null) searchFilter = new SearchFilter(); + if (searchFilter.getEndTime() == null) + searchFilter.setEnd(Timestamp.from(Instant.now()).toLocalDateTime()); + if (searchFilter.getBeginTime() == null) + searchFilter.setBegin(Timestamp.from(Instant.now().minusSeconds(30 * 3600* 24)).toLocalDateTime()); + + return new ResponseEntity<>(graphService.getVipOnPeriod( + searchFilter + ), HttpStatus.OK); + } } diff --git a/src/main/java/app/controllers/user/UsertimeController.java b/src/main/java/app/controllers/user/UsertimeController.java new file mode 100644 index 0000000..0fca4b9 --- /dev/null +++ b/src/main/java/app/controllers/user/UsertimeController.java @@ -0,0 +1,50 @@ +package app.controllers.user; + +import app.annotations.enums.AuthMethod; +import app.annotations.interfaces.CheckWebAccess; +import app.annotations.interfaces.CollectStatistic; +import app.annotations.interfaces.WaitAfterNext; +import app.entities.SearchFilter; +import app.entities.db.period.PerPeriodStatistic; +import app.services.db.GraphService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.sql.Timestamp; +import java.time.Instant; +import java.util.List; + +/** + * POST http://localhost:8080/api/profile/usertime + * Content-Type: application/json + * + * { + * "accounts": ["76561198087598690"], + * "end": "2026-05-10T23:59:59.999Z", + * "begin": "2026-04-10T23:59:59.999Z" + * } + */ +@RestController +@RequestMapping("/api/profile/usertime") +public class UsertimeController { + @Autowired + private GraphService graphService; + + @PostMapping("/graph") + @CheckWebAccess(auth_method = AuthMethod.STEAM64) + @WaitAfterNext(order = "usertime") + @CollectStatistic + public ResponseEntity> getTimeOnPeriod(@RequestBody(required = false) SearchFilter searchFilter) { + if (searchFilter == null) searchFilter = new SearchFilter(); + if (searchFilter.getEndTime() == null) + searchFilter.setEnd(Timestamp.from(Instant.now()).toLocalDateTime()); + if (searchFilter.getBeginTime() == null) + searchFilter.setBegin(Timestamp.from(Instant.now().minusSeconds(30 * 3600* 24)).toLocalDateTime()); + + return new ResponseEntity<>(graphService.getUsertimeOnPeriod( + searchFilter + ), HttpStatus.OK); + } +} diff --git a/src/main/java/app/entities/SearchFilter.java b/src/main/java/app/entities/SearchFilter.java index 6902f50..928babf 100644 --- a/src/main/java/app/entities/SearchFilter.java +++ b/src/main/java/app/entities/SearchFilter.java @@ -7,6 +7,7 @@ import org.springframework.format.annotation.DateTimeFormat; import java.sql.Timestamp; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -21,6 +22,10 @@ public class SearchFilter { private String serverId = null; private Long utc = 0L; + public boolean isEmpty() { + return accounts == null || accounts.isEmpty() || begin == null || end == null; + } + /** * Выдает список из ид 3 версии [U:1:127332962] * @param profileService @@ -88,7 +93,28 @@ public class SearchFilter { return end == null ? null : (Timestamp.valueOf(end).getTime()/1000) + utc*60; } + public Timestamp getBeginTime() { + return begin == null ? null : Timestamp.valueOf(begin); + } + + public Timestamp getEndTime() { + return end == null ? null : Timestamp.valueOf(end); + } + public String getServerId() { return serverId == null || serverId.isEmpty() ? null : serverId; } + + public Long getDayDelta() { + if (begin != null && end != null) + return ChronoUnit.DAYS.between(begin, end); + else return 0L; + } + + public String getMaybePeriod() { + long delta = getDayDelta(); + if (delta <= 30) return "day"; + if (delta <= 365) return "month"; + else return "year"; + } } diff --git a/src/main/java/app/entities/db/period/PerPeriodStatistic.java b/src/main/java/app/entities/db/period/PerPeriodStatistic.java new file mode 100644 index 0000000..2629564 --- /dev/null +++ b/src/main/java/app/entities/db/period/PerPeriodStatistic.java @@ -0,0 +1,33 @@ +package app.entities.db.period; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.sql.Timestamp; +import java.text.SimpleDateFormat; + +@Data +public class PerPeriodStatistic { + private final static SimpleDateFormat simpleDaysDateFormat = new SimpleDateFormat("dd.MM.yyyy"); + private final static SimpleDateFormat simpleMonthDateFormat = new SimpleDateFormat("MM.yyyy"); + private final static SimpleDateFormat simpleYearDateFormat = new SimpleDateFormat("yyyy"); + private Timestamp ts; + private Long value; + private String srv_id; + private String period; + + @JsonIgnore + public Timestamp getTs() { + return ts; + } + + public String getDate() { + if (ts == null) return ""; + return switch (period) { + case "day" -> simpleDaysDateFormat.format(ts); + case "month" -> simpleMonthDateFormat.format(ts); + case "year" -> simpleYearDateFormat.format(ts); + default -> ""; + }; + } +} diff --git a/src/main/java/app/services/db/GraphService.java b/src/main/java/app/services/db/GraphService.java new file mode 100644 index 0000000..4688d69 --- /dev/null +++ b/src/main/java/app/services/db/GraphService.java @@ -0,0 +1,76 @@ +package app.services.db; + +import app.entities.SearchFilter; +import app.entities.db.period.PerPeriodStatistic; +import app.services.ProfileService; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class GraphService { + @Autowired + private ProfileService profileService; + + @Autowired + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + + /** + * 2021-03-08 00:00:00.000000 +00:00,3853,srv1 + * 2021-03-12 00:00:00.000000 +00:00,930,srv1 + * 2021-04-13 00:00:00.000000 +00:00,435,srv1 + * 2021-05-01 00:00:00.000000 +00:00,2706,srv1 + * 2021-05-02 00:00:00.000000 +00:00,2628,srv10 + * @param searchFilter + * @return + */ + public List getUsertimeOnPeriod(SearchFilter searchFilter) { + final String sql = "select date_trunc(:delta, timestamp) AS ts, :delta as period, sum(connect_duration) as value, srv_id from user_connections " + + "where (:account_ids_empty = true or account_id in (:account_ids)) and connect_duration != 0 and timestamp between :begindate and :enddate " + + "and (:srv_id like '' or srv_id like :srv_id) " + + "group by ts, srv_id"; + MapSqlParameterSource map = new MapSqlParameterSource(); + map.addValue("delta", searchFilter.getMaybePeriod()); + List profiles = searchFilter.getAccounts(profileService); + map.addValue("account_ids_empty", profiles.isEmpty()); + map.addValue("account_ids", profiles.isEmpty() ? List.of(1) : profiles); + map.addValue("begindate", searchFilter.getBeginTime()); + map.addValue("enddate", searchFilter.getEndTime()); + map.addValue("srv_id", searchFilter.getServerId() == null ? "" : searchFilter.getServerId()); + + return namedParameterJdbcTemplate.query(sql, map, new BeanPropertyRowMapper(PerPeriodStatistic.class)); + } + + @Data + public static class VipPerPeriodStatistic extends PerPeriodStatistic { + private Integer givemethod; + private Integer amount; + + @JsonIgnore + @Override + public String getSrv_id() { + return super.getSrv_id(); + } + } + + public List getVipOnPeriod(SearchFilter searchFilter) { + final String sql = "select date_trunc(:delta, timestamp) AS ts, :delta as period, count(*) as value, givemethod, amount from gived_vip " + + "where (:account_ids_empty = true or steam2 in (:account_ids)) and givemethod != 4 and timestamp between :begindate and :enddate " + + "group by ts, givemethod, amount"; + MapSqlParameterSource map = new MapSqlParameterSource(); + map.addValue("delta", searchFilter.getMaybePeriod()); + String profiles = searchFilter.getAccountsSteam2(profileService); + map.addValue("account_ids_empty", profiles.isEmpty()); + map.addValue("account_ids", profiles.isEmpty() ? "0" : profiles); + map.addValue("begindate", searchFilter.getBeginTime()); + map.addValue("enddate", searchFilter.getEndTime()); + + return namedParameterJdbcTemplate.query(sql, map, new BeanPropertyRowMapper(VipPerPeriodStatistic.class)); + } +}