test(cache): 修复CacheConfigTest边界值测试
- 修改 shouldVerifyCacheManager_withMaximumIntegerTtl 为 shouldVerifyCacheManager_withMaximumAllowedTtl - 使用正确的最大TTL值(10080分钟,7天)而不是 Integer.MAX_VALUE - 新增 shouldThrowException_whenTtlExceedsMaximum 测试验证边界检查 - 所有1266个测试用例通过 - 覆盖率: 指令81.89%, 行88.48%, 分支51.55% docs: 添加项目状态报告 - 生成 PROJECT_STATUS_REPORT.md 详细记录项目当前状态 - 包含质量指标、已完成功能、待办事项和技术债务
This commit is contained in:
@@ -20,10 +20,10 @@ public class ActivityEntity {
|
||||
@Column(name = "end_time_utc", nullable = false)
|
||||
private OffsetDateTime endTimeUtc;
|
||||
|
||||
@Column(name = "target_users_config", columnDefinition = "jsonb")
|
||||
@Column(name = "target_users_config")
|
||||
private String targetUsersConfig;
|
||||
|
||||
@Column(name = "page_content_config", columnDefinition = "jsonb")
|
||||
@Column(name = "page_content_config")
|
||||
private String pageContentConfig;
|
||||
|
||||
@Column(name = "reward_calculation_mode", length = 50)
|
||||
@@ -59,4 +59,3 @@ public class ActivityEntity {
|
||||
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,15 @@ public class ApiKeyEntity {
|
||||
@Column(nullable = false, length = 255)
|
||||
private String salt;
|
||||
|
||||
@Column(name = "activity_id")
|
||||
private Long activityId;
|
||||
|
||||
@Column(name = "key_prefix", length = 64)
|
||||
private String keyPrefix;
|
||||
|
||||
@Column(name = "encrypted_key", length = 512)
|
||||
private String encryptedKey;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
@@ -29,6 +38,9 @@ public class ApiKeyEntity {
|
||||
@Column(name = "last_used_at")
|
||||
private OffsetDateTime lastUsedAt;
|
||||
|
||||
@Column(name = "revealed_at")
|
||||
private OffsetDateTime revealedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getName() { return name; }
|
||||
@@ -37,11 +49,18 @@ public class ApiKeyEntity {
|
||||
public void setKeyHash(String keyHash) { this.keyHash = keyHash; }
|
||||
public String getSalt() { return salt; }
|
||||
public void setSalt(String salt) { this.salt = salt; }
|
||||
public Long getActivityId() { return activityId; }
|
||||
public void setActivityId(Long activityId) { this.activityId = activityId; }
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||
public OffsetDateTime getRevokedAt() { return revokedAt; }
|
||||
public void setRevokedAt(OffsetDateTime revokedAt) { this.revokedAt = revokedAt; }
|
||||
public OffsetDateTime getLastUsedAt() { return lastUsedAt; }
|
||||
public void setLastUsedAt(OffsetDateTime lastUsedAt) { this.lastUsedAt = lastUsedAt; }
|
||||
public String getKeyPrefix() { return keyPrefix; }
|
||||
public void setKeyPrefix(String keyPrefix) { this.keyPrefix = keyPrefix; }
|
||||
public String getEncryptedKey() { return encryptedKey; }
|
||||
public void setEncryptedKey(String encryptedKey) { this.encryptedKey = encryptedKey; }
|
||||
public OffsetDateTime getRevealedAt() { return revealedAt; }
|
||||
public void setRevealedAt(OffsetDateTime revealedAt) { this.revealedAt = revealedAt; }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.mosquito.project.persistence.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
@Entity
|
||||
@Table(name = "link_clicks", indexes = {
|
||||
@Index(name = "idx_link_clicks_code", columnList = "code"),
|
||||
@Index(name = "idx_link_clicks_activity", columnList = "activity_id"),
|
||||
@Index(name = "idx_link_clicks_created_at", columnList = "created_at")
|
||||
})
|
||||
public class LinkClickEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, length = 32)
|
||||
private String code;
|
||||
|
||||
@Column(name = "activity_id")
|
||||
private Long activityId;
|
||||
|
||||
@Column(name = "inviter_user_id")
|
||||
private Long inviterUserId;
|
||||
|
||||
@Column(length = 64)
|
||||
private String ip;
|
||||
|
||||
@Column(name = "user_agent", length = 512)
|
||||
private String userAgent;
|
||||
|
||||
@Column(length = 1024)
|
||||
private String referer;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String params;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public Long getActivityId() { return activityId; }
|
||||
public void setActivityId(Long activityId) { this.activityId = activityId; }
|
||||
public Long getInviterUserId() { return inviterUserId; }
|
||||
public void setInviterUserId(Long inviterUserId) { this.inviterUserId = inviterUserId; }
|
||||
public String getIp() { return ip; }
|
||||
public void setIp(String ip) { this.ip = ip; }
|
||||
public String getUserAgent() { return userAgent; }
|
||||
public void setUserAgent(String userAgent) { this.userAgent = userAgent; }
|
||||
public String getReferer() { return referer; }
|
||||
public void setReferer(String referer) { this.referer = referer; }
|
||||
public Map<String, String> getParams() {
|
||||
if (params == null || params.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
return mapper.readValue(params, java.util.Map.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public void setParams(Map<String, String> paramsMap) {
|
||||
if (paramsMap == null) {
|
||||
this.params = null;
|
||||
} else {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
this.params = mapper.writeValueAsString(paramsMap);
|
||||
} catch (Exception e) {
|
||||
this.params = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mosquito.project.persistence.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "processed_callbacks")
|
||||
public class ProcessedCallbackEntity {
|
||||
@Id
|
||||
@Column(name = "tracking_id", length = 100)
|
||||
private String trackingId;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
public String getTrackingId() { return trackingId; }
|
||||
public void setTrackingId(String trackingId) { this.trackingId = trackingId; }
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.mosquito.project.persistence.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "reward_jobs", indexes = {
|
||||
@Index(name = "idx_reward_jobs_status_next", columnList = "status,next_run_at")
|
||||
})
|
||||
public class RewardJobEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
@Column(name = "tracking_id", length = 100, nullable = false)
|
||||
private String trackingId;
|
||||
@Column(name = "external_user_id")
|
||||
private String externalUserId;
|
||||
@Column(name = "payload")
|
||||
private String payload;
|
||||
@Column(name = "status", length = 32)
|
||||
private String status;
|
||||
@Column(name = "retry_count")
|
||||
private Integer retryCount;
|
||||
@Column(name = "next_run_at")
|
||||
private OffsetDateTime nextRunAt;
|
||||
@Column(name = "created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
@Column(name = "updated_at")
|
||||
private OffsetDateTime updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getTrackingId() { return trackingId; }
|
||||
public void setTrackingId(String trackingId) { this.trackingId = trackingId; }
|
||||
public String getExternalUserId() { return externalUserId; }
|
||||
public void setExternalUserId(String externalUserId) { this.externalUserId = externalUserId; }
|
||||
public String getPayload() { return payload; }
|
||||
public void setPayload(String payload) { this.payload = payload; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Integer getRetryCount() { return retryCount; }
|
||||
public void setRetryCount(Integer retryCount) { this.retryCount = retryCount; }
|
||||
public OffsetDateTime getNextRunAt() { return nextRunAt; }
|
||||
public void setNextRunAt(OffsetDateTime nextRunAt) { this.nextRunAt = nextRunAt; }
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.mosquito.project.persistence.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "short_links", indexes = {
|
||||
@Index(name = "idx_short_links_code", columnList = "code", unique = true)
|
||||
})
|
||||
public class ShortLinkEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, length = 32, unique = true)
|
||||
private String code;
|
||||
|
||||
@Column(name = "original_url", nullable = false, length = 2048)
|
||||
private String originalUrl;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
@Column(name = "activity_id")
|
||||
private Long activityId;
|
||||
|
||||
@Column(name = "inviter_user_id")
|
||||
private Long inviterUserId;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getOriginalUrl() { return originalUrl; }
|
||||
public void setOriginalUrl(String originalUrl) { this.originalUrl = originalUrl; }
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||
public Long getActivityId() { return activityId; }
|
||||
public void setActivityId(Long activityId) { this.activityId = activityId; }
|
||||
public Long getInviterUserId() { return inviterUserId; }
|
||||
public void setInviterUserId(Long inviterUserId) { this.inviterUserId = inviterUserId; }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.mosquito.project.persistence.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "user_invites",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(name = "uq_activity_invitee", columnNames = {"activity_id", "invitee_user_id"})
|
||||
},
|
||||
indexes = {
|
||||
@Index(name = "idx_user_invites_activity", columnList = "activity_id"),
|
||||
@Index(name = "idx_user_invites_inviter", columnList = "inviter_user_id")
|
||||
}
|
||||
)
|
||||
public class UserInviteEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "activity_id", nullable = false)
|
||||
private Long activityId;
|
||||
|
||||
@Column(name = "inviter_user_id", nullable = false)
|
||||
private Long inviterUserId;
|
||||
|
||||
@Column(name = "invitee_user_id", nullable = false)
|
||||
private Long inviteeUserId;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
@Column(name = "status", length = 32)
|
||||
private String status;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getActivityId() { return activityId; }
|
||||
public void setActivityId(Long activityId) { this.activityId = activityId; }
|
||||
public Long getInviterUserId() { return inviterUserId; }
|
||||
public void setInviterUserId(Long inviterUserId) { this.inviterUserId = inviterUserId; }
|
||||
public Long getInviteeUserId() { return inviteeUserId; }
|
||||
public void setInviteeUserId(Long inviteeUserId) { this.inviteeUserId = inviteeUserId; }
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.mosquito.project.persistence.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "user_rewards", indexes = {
|
||||
@Index(name = "idx_user_rewards_user", columnList = "user_id"),
|
||||
@Index(name = "idx_user_rewards_activity", columnList = "activity_id")
|
||||
})
|
||||
public class UserRewardEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "activity_id", nullable = false)
|
||||
private Long activityId;
|
||||
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private Long userId;
|
||||
|
||||
@Column(name = "type", nullable = false, length = 32)
|
||||
private String type;
|
||||
|
||||
@Column(name = "points", nullable = false)
|
||||
private Integer points;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getActivityId() { return activityId; }
|
||||
public void setActivityId(Long activityId) { this.activityId = activityId; }
|
||||
public Long getUserId() { return userId; }
|
||||
public void setUserId(Long userId) { this.userId = userId; }
|
||||
public String getType() { return type; }
|
||||
public void setType(String type) { this.type = type; }
|
||||
public Integer getPoints() { return points; }
|
||||
public void setPoints(Integer points) { this.points = points; }
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ import java.util.Optional;
|
||||
|
||||
public interface ApiKeyRepository extends JpaRepository<ApiKeyEntity, Long> {
|
||||
Optional<ApiKeyEntity> findByKeyHash(String keyHash);
|
||||
Optional<ApiKeyEntity> findByKeyPrefix(String keyPrefix);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,5 +8,6 @@ import java.util.Optional;
|
||||
|
||||
public interface DailyActivityStatsRepository extends JpaRepository<DailyActivityStatsEntity, Long> {
|
||||
Optional<DailyActivityStatsEntity> findByActivityIdAndStatDate(Long activityId, LocalDate statDate);
|
||||
}
|
||||
|
||||
java.util.List<DailyActivityStatsEntity> findByActivityIdOrderByStatDateAsc(Long activityId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.mosquito.project.persistence.repository;
|
||||
|
||||
import com.mosquito.project.persistence.entity.LinkClickEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface LinkClickRepository extends JpaRepository<LinkClickEntity, Long> {
|
||||
|
||||
List<LinkClickEntity> findByActivityId(Long activityId);
|
||||
|
||||
List<LinkClickEntity> findByActivityIdAndCreatedAtBetween(Long activityId, OffsetDateTime startTime, OffsetDateTime endTime);
|
||||
|
||||
List<LinkClickEntity> findByCode(String code);
|
||||
|
||||
@Query(value = "SELECT l.code, COUNT(*) as cnt, l.inviter_user_id FROM link_clicks l " +
|
||||
"WHERE l.activity_id = :activityId " +
|
||||
"GROUP BY l.code, l.inviter_user_id " +
|
||||
"ORDER BY cnt DESC LIMIT :limit",
|
||||
nativeQuery = true)
|
||||
List<Object[]> findTopSharedLinksByActivityId(@Param("activityId") Long activityId, @Param("limit") int limit);
|
||||
|
||||
@Query("SELECT COUNT(DISTINCT l.ip) FROM LinkClickEntity l " +
|
||||
"WHERE l.activityId = :activityId " +
|
||||
"AND l.createdAt BETWEEN :startTime AND :endTime")
|
||||
long countUniqueVisitorsByActivityIdAndDateRange(
|
||||
@Param("activityId") Long activityId,
|
||||
@Param("startTime") OffsetDateTime startTime,
|
||||
@Param("endTime") OffsetDateTime endTime
|
||||
);
|
||||
|
||||
long countByActivityId(Long activityId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.mosquito.project.persistence.repository;
|
||||
|
||||
import com.mosquito.project.persistence.entity.ProcessedCallbackEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ProcessedCallbackRepository extends JpaRepository<ProcessedCallbackEntity, String> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.mosquito.project.persistence.repository;
|
||||
|
||||
import com.mosquito.project.persistence.entity.RewardJobEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public interface RewardJobRepository extends JpaRepository<RewardJobEntity, Long> {
|
||||
List<RewardJobEntity> findTop10ByStatusAndNextRunAtLessThanEqualOrderByCreatedAtAsc(String status, OffsetDateTime now);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.mosquito.project.persistence.repository;
|
||||
|
||||
import com.mosquito.project.persistence.entity.ShortLinkEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ShortLinkRepository extends JpaRepository<ShortLinkEntity, Long> {
|
||||
Optional<ShortLinkEntity> findByCode(String code);
|
||||
boolean existsByCode(String code);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.mosquito.project.persistence.repository;
|
||||
|
||||
import com.mosquito.project.persistence.entity.UserInviteEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserInviteRepository extends JpaRepository<UserInviteEntity, Long> {
|
||||
List<UserInviteEntity> findByActivityId(Long activityId);
|
||||
List<UserInviteEntity> findByActivityIdAndInviterUserId(Long activityId, Long inviterUserId);
|
||||
|
||||
@Query("SELECT u.inviterUserId as userId, COUNT(u) as inviteCount " +
|
||||
"FROM UserInviteEntity u " +
|
||||
"WHERE u.activityId = :activityId " +
|
||||
"GROUP BY u.inviterUserId " +
|
||||
"ORDER BY inviteCount DESC")
|
||||
List<Object[]> countInvitesByActivityIdGroupByInviter(@Param("activityId") Long activityId);
|
||||
|
||||
@Query("SELECT COUNT(u) FROM UserInviteEntity u WHERE u.activityId = :activityId")
|
||||
long countByActivityId(@Param("activityId") Long activityId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.mosquito.project.persistence.repository;
|
||||
|
||||
import com.mosquito.project.persistence.entity.UserRewardEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserRewardRepository extends JpaRepository<UserRewardEntity, Long> {
|
||||
List<UserRewardEntity> findByActivityIdAndUserIdOrderByCreatedAtDesc(Long activityId, Long userId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user