新增系统配置表及默认配置,更新链接生成请求DTO以支持链接数量参数,重构链接生成服务逻辑,添加链接状态查询和有效性检查接口,优化日志记录。

This commit is contained in:
zyh
2025-08-26 10:33:26 +08:00
parent 7317866f98
commit 599ec0a36b
73 changed files with 1829 additions and 50 deletions

View File

@@ -0,0 +1,120 @@
package com.gameplatform.server.controller.admin;
import com.gameplatform.server.model.dto.common.PageResult;
import com.gameplatform.server.model.entity.admin.SystemConfig;
import com.gameplatform.server.model.dto.admin.SystemConfigRequest;
import com.gameplatform.server.model.dto.admin.SystemConfigResponse;
import com.gameplatform.server.model.dto.admin.SystemConfigConverter;
import com.gameplatform.server.service.admin.SystemConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/admin/config")
@Tag(name = "系统配置管理", description = "系统配置的增删改查接口")
public class SystemConfigController {
@Autowired
private SystemConfigService systemConfigService;
@GetMapping("/list")
@Operation(summary = "获取配置列表", description = "分页获取所有系统配置")
public ResponseEntity<PageResult<SystemConfigResponse>> getConfigList(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
int offset = (page - 1) * size;
List<SystemConfig> configs = systemConfigService.getAllConfigs(size, offset);
long total = systemConfigService.getConfigCount();
List<SystemConfigResponse> responses = configs.stream()
.map(SystemConfigConverter::toResponse)
.toList();
PageResult<SystemConfigResponse> result = new PageResult<>();
result.setItems(responses);
result.setTotal(total);
result.setPage(page);
result.setSize(size);
return ResponseEntity.ok(result);
}
@GetMapping("/key/{configKey}")
@Operation(summary = "根据键获取配置", description = "根据配置键获取配置信息")
public ResponseEntity<SystemConfigResponse> getConfigByKey(@PathVariable String configKey) {
SystemConfig config = systemConfigService.getConfigByKey(configKey);
if (config == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(SystemConfigConverter.toResponse(config));
}
@GetMapping("/type/{configType}")
@Operation(summary = "根据类型获取配置", description = "根据配置类型获取配置列表")
public ResponseEntity<List<SystemConfigResponse>> getConfigsByType(@PathVariable String configType) {
List<SystemConfig> configs = systemConfigService.getConfigsByType(configType);
List<SystemConfigResponse> responses = configs.stream()
.map(SystemConfigConverter::toResponse)
.toList();
return ResponseEntity.ok(responses);
}
@PostMapping
@Operation(summary = "创建配置", description = "创建新的系统配置")
public ResponseEntity<Boolean> createConfig(@RequestBody SystemConfigRequest request) {
SystemConfig systemConfig = SystemConfigConverter.toEntity(request);
boolean success = systemConfigService.createConfig(systemConfig);
return ResponseEntity.ok(success);
}
@PutMapping("/{id}")
@Operation(summary = "更新配置", description = "更新指定ID的系统配置")
public ResponseEntity<Boolean> updateConfig(@PathVariable Long id, @RequestBody SystemConfigRequest request) {
SystemConfig systemConfig = SystemConfigConverter.toEntity(request);
systemConfig.setId(id);
boolean success = systemConfigService.updateConfig(systemConfig);
return ResponseEntity.ok(success);
}
@DeleteMapping("/{id}")
@Operation(summary = "删除配置", description = "删除指定ID的系统配置")
public ResponseEntity<Boolean> deleteConfig(@PathVariable Long id) {
boolean success = systemConfigService.deleteConfig(id);
return ResponseEntity.ok(success);
}
@DeleteMapping("/key/{configKey}")
@Operation(summary = "根据键删除配置", description = "根据配置键删除系统配置")
public ResponseEntity<Boolean> deleteConfigByKey(@PathVariable String configKey) {
boolean success = systemConfigService.deleteConfigByKey(configKey);
return ResponseEntity.ok(success);
}
@GetMapping("/link/defaults")
@Operation(summary = "获取链接默认配置", description = "获取链接生成相关的默认配置")
public ResponseEntity<Object> getLinkDefaults() {
return ResponseEntity.ok(new Object() {
public final Integer defaultQuantity = systemConfigService.getDefaultQuantity();
public final Integer refreshInterval = systemConfigService.getRefreshInterval();
public final Integer qrExpireTime = systemConfigService.getQrExpireTime();
public final Integer maxTimesPerBatch = systemConfigService.getMaxTimesPerBatch();
public final Integer minQuantity = systemConfigService.getMinQuantity();
public final Integer maxQuantity = systemConfigService.getMaxQuantity();
});
}
@GetMapping("/script/config")
@Operation(summary = "获取脚本配置", description = "获取脚本服务器相关配置")
public ResponseEntity<Object> getScriptConfig() {
return ResponseEntity.ok(new Object() {
public final String serverUrl = systemConfigService.getScriptServerUrl();
public final String qrPathTemplate = systemConfigService.getQrPathTemplate();
});
}
}

View File

@@ -2,7 +2,9 @@ package com.gameplatform.server.controller.link;
import com.gameplatform.server.model.dto.link.LinkGenerateRequest;
import com.gameplatform.server.model.dto.link.LinkGenerateResponse;
import com.gameplatform.server.model.dto.link.LinkStatusResponse;
import com.gameplatform.server.service.link.LinkGenerationService;
import com.gameplatform.server.service.link.LinkStatusService;
import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -24,9 +26,11 @@ public class LinkController {
private static final Logger log = LoggerFactory.getLogger(LinkController.class);
private final LinkGenerationService linkGenerationService;
private final LinkStatusService linkStatusService;
public LinkController(LinkGenerationService linkGenerationService) {
public LinkController(LinkGenerationService linkGenerationService, LinkStatusService linkStatusService) {
this.linkGenerationService = linkGenerationService;
this.linkStatusService = linkStatusService;
}
@PostMapping("/generate")
@@ -35,8 +39,8 @@ public class LinkController {
public Mono<LinkGenerateResponse> generateLinks(@Valid @RequestBody LinkGenerateRequest request,
Authentication authentication) {
log.info("=== 开始处理链接生成请求 ===");
log.info("请求参数: times={}, perTimeQuantity={}",
request.getTimes(), request.getPerTimeQuantity());
log.info("请求参数: times={}, linkCount={}",
request.getTimes(), request.getLinkCount());
log.info("=== 开始检查认证信息 ===");
log.info("Authentication: {}", authentication);
@@ -92,11 +96,11 @@ public class LinkController {
log.info("=== 用户认证信息完整,开始处理业务逻辑 ===");
log.info("操作者信息: operatorId={}, operatorType={}, username={}",
operatorId, operatorType, username);
log.info("业务参数: times={}, perTimeQuantity={}",
request.getTimes(), request.getPerTimeQuantity());
log.info("业务参数: times={}, linkCount={}",
request.getTimes(), request.getLinkCount());
return linkGenerationService.generateLinks(operatorId, operatorType,
request.getTimes(), request.getPerTimeQuantity())
request.getTimes(), request.getLinkCount())
.map(result -> {
log.info("链接生成成功batchId: {}, 扣除积分: {}, 过期时间: {}",
result.getBatchId(), result.getNeedPoints(), result.getExpireAt());
@@ -111,6 +115,36 @@ public class LinkController {
log.error("链接生成失败: {}", error.getMessage(), error);
});
}
@GetMapping("/{codeNo}/status")
@Operation(summary = "获取链接状态", description = "根据链接编号获取链接的详细状态信息,包括过期时间、奖励点数、当前状态等")
public Mono<LinkStatusResponse> getLinkStatus(@PathVariable("codeNo") String codeNo) {
log.info("=== 开始查询链接状态 ===");
log.info("链接编号: {}", codeNo);
return linkStatusService.getLinkStatus(codeNo)
.doOnSuccess(response -> {
log.info("链接状态查询成功: codeNo={}, status={}, isExpired={}",
codeNo, response.getStatus(), response.getIsExpired());
})
.doOnError(error -> {
log.error("链接状态查询失败: codeNo={}, error={}", codeNo, error.getMessage(), error);
});
}
@GetMapping("/{codeNo}/exists")
@Operation(summary = "检查链接是否存在", description = "检查指定链接编号是否存在")
public Mono<Boolean> isLinkExists(@PathVariable("codeNo") String codeNo) {
log.debug("检查链接是否存在: codeNo={}", codeNo);
return linkStatusService.isLinkExists(codeNo);
}
@GetMapping("/{codeNo}/valid")
@Operation(summary = "检查链接是否有效", description = "检查指定链接是否有效(未过期且状态正常)")
public Mono<Boolean> isLinkValid(@PathVariable("codeNo") String codeNo) {
log.debug("检查链接是否有效: codeNo={}", codeNo);
return linkStatusService.isLinkValid(codeNo);
}
}

View File

@@ -0,0 +1,27 @@
package com.gameplatform.server.mapper.admin;
import com.gameplatform.server.model.entity.admin.SystemConfig;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface SystemConfigMapper {
SystemConfig findById(@Param("id") Long id);
SystemConfig findByKey(@Param("configKey") String configKey);
List<SystemConfig> findByType(@Param("configType") String configType);
List<SystemConfig> findAll(@Param("size") int size,
@Param("offset") int offset);
long countAll();
int insert(SystemConfig systemConfig);
int update(SystemConfig systemConfig);
int deleteById(@Param("id") Long id);
int deleteByKey(@Param("configKey") String configKey);
}

View File

@@ -0,0 +1,35 @@
package com.gameplatform.server.model.dto.admin;
import com.gameplatform.server.model.entity.admin.SystemConfig;
public class SystemConfigConverter {
public static SystemConfigResponse toResponse(SystemConfig entity) {
if (entity == null) return null;
SystemConfigResponse response = new SystemConfigResponse();
response.setId(entity.getId());
response.setConfigKey(entity.getConfigKey());
response.setConfigValue(entity.getConfigValue());
response.setConfigType(entity.getConfigType());
response.setDescription(entity.getDescription());
response.setIsSystem(entity.getIsSystem());
response.setCreatedAt(entity.getCreatedAt());
response.setUpdatedAt(entity.getUpdatedAt());
return response;
}
public static SystemConfig toEntity(SystemConfigRequest request) {
if (request == null) return null;
SystemConfig entity = new SystemConfig();
entity.setConfigKey(request.getConfigKey());
entity.setConfigValue(request.getConfigValue());
entity.setConfigType(request.getConfigType());
entity.setDescription(request.getDescription());
entity.setIsSystem(request.getIsSystem());
return entity;
}
}

View File

@@ -0,0 +1,24 @@
package com.gameplatform.server.model.dto.admin;
public class SystemConfigRequest {
private String configKey;
private String configValue;
private String configType;
private String description;
private Boolean isSystem;
public String getConfigKey() { return configKey; }
public void setConfigKey(String configKey) { this.configKey = configKey; }
public String getConfigValue() { return configValue; }
public void setConfigValue(String configValue) { this.configValue = configValue; }
public String getConfigType() { return configType; }
public void setConfigType(String configType) { this.configType = configType; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Boolean getIsSystem() { return isSystem; }
public void setIsSystem(Boolean isSystem) { this.isSystem = isSystem; }
}

View File

@@ -0,0 +1,38 @@
package com.gameplatform.server.model.dto.admin;
import java.time.LocalDateTime;
public class SystemConfigResponse {
private Long id;
private String configKey;
private String configValue;
private String configType;
private String description;
private Boolean isSystem;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getConfigKey() { return configKey; }
public void setConfigKey(String configKey) { this.configKey = configKey; }
public String getConfigValue() { return configValue; }
public void setConfigValue(String configValue) { this.configValue = configValue; }
public String getConfigType() { return configType; }
public void setConfigType(String configType) { this.configType = configType; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Boolean getIsSystem() { return isSystem; }
public void setIsSystem(Boolean isSystem) { this.isSystem = isSystem; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}

View File

@@ -5,13 +5,13 @@ import io.swagger.v3.oas.annotations.media.Schema;
public class LinkGenerateRequest {
@Schema(description = "本次打脚本的次数", example = "10")
private Integer times;
@Schema(description = "每次打的数量", example = "5")
private Integer perTimeQuantity;
@Schema(description = "生成多少个链接", example = "5")
private Integer linkCount = 1; // 默认值为1
public Integer getTimes() { return times; }
public void setTimes(Integer times) { this.times = times; }
public Integer getPerTimeQuantity() { return perTimeQuantity; }
public void setPerTimeQuantity(Integer perTimeQuantity) { this.perTimeQuantity = perTimeQuantity; }
public Integer getLinkCount() { return linkCount; }
public void setLinkCount(Integer linkCount) { this.linkCount = linkCount; }
}

View File

@@ -0,0 +1,101 @@
package com.gameplatform.server.model.dto.link;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
@Schema(description = "链接状态响应")
public class LinkStatusResponse {
@Schema(description = "链接编号", example = "ABC12345")
private String codeNo;
@Schema(description = "批次ID", example = "123")
private Long batchId;
@Schema(description = "链接状态", example = "NEW", allowableValues = {"NEW", "USING", "LOGGED_IN", "REFUNDED", "EXPIRED"})
private String status;
@Schema(description = "链接状态描述", example = "新建")
private String statusDesc;
@Schema(description = "过期时间", example = "2024-01-15T16:30:00")
private LocalDateTime expireAt;
@Schema(description = "是否已过期", example = "false")
private Boolean isExpired;
@Schema(description = "剩余有效时间(秒)", example = "3600")
private Long remainingSeconds;
@Schema(description = "每次副本奖励点数", example = "50")
private Integer quantity;
@Schema(description = "打副本次数", example = "10")
private Integer times;
@Schema(description = "总奖励点数", example = "500")
private Integer totalPoints;
@Schema(description = "区域", example = "Q", allowableValues = {"Q", "V"})
private String region;
@Schema(description = "机器ID", example = "MACHINE001")
private String machineId;
@Schema(description = "登录时间", example = "2024-01-15T14:30:00")
private LocalDateTime loginAt;
@Schema(description = "创建时间", example = "2024-01-15T12:00:00")
private LocalDateTime createdAt;
@Schema(description = "更新时间", example = "2024-01-15T14:30:00")
private LocalDateTime updatedAt;
// Getters and Setters
public String getCodeNo() { return codeNo; }
public void setCodeNo(String codeNo) { this.codeNo = codeNo; }
public Long getBatchId() { return batchId; }
public void setBatchId(Long batchId) { this.batchId = batchId; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getStatusDesc() { return statusDesc; }
public void setStatusDesc(String statusDesc) { this.statusDesc = statusDesc; }
public LocalDateTime getExpireAt() { return expireAt; }
public void setExpireAt(LocalDateTime expireAt) { this.expireAt = expireAt; }
public Boolean getIsExpired() { return isExpired; }
public void setIsExpired(Boolean isExpired) { this.isExpired = isExpired; }
public Long getRemainingSeconds() { return remainingSeconds; }
public void setRemainingSeconds(Long remainingSeconds) { this.remainingSeconds = remainingSeconds; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public Integer getTimes() { return times; }
public void setTimes(Integer times) { this.times = times; }
public Integer getTotalPoints() { return totalPoints; }
public void setTotalPoints(Integer totalPoints) { this.totalPoints = totalPoints; }
public String getRegion() { return region; }
public void setRegion(String region) { this.region = region; }
public String getMachineId() { return machineId; }
public void setMachineId(String machineId) { this.machineId = machineId; }
public LocalDateTime getLoginAt() { return loginAt; }
public void setLoginAt(LocalDateTime loginAt) { this.loginAt = loginAt; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}

View File

@@ -0,0 +1,38 @@
package com.gameplatform.server.model.entity.admin;
import java.time.LocalDateTime;
public class SystemConfig {
private Long id;
private String configKey;
private String configValue;
private String configType;
private String description;
private Boolean isSystem;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getConfigKey() { return configKey; }
public void setConfigKey(String configKey) { this.configKey = configKey; }
public String getConfigValue() { return configValue; }
public void setConfigValue(String configValue) { this.configValue = configValue; }
public String getConfigType() { return configType; }
public void setConfigType(String configType) { this.configType = configType; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Boolean getIsSystem() { return isSystem; }
public void setIsSystem(Boolean isSystem) { this.isSystem = isSystem; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}

View File

@@ -7,8 +7,6 @@ public class LinkBatch {
private Long agentId;
private Integer quantity;
private Integer times;
private Integer batchSize;
private Long deductPoints;
private Long operatorId;
private LocalDateTime createdAt;
@@ -24,12 +22,6 @@ public class LinkBatch {
public Integer getTimes() { return times; }
public void setTimes(Integer times) { this.times = times; }
public Integer getBatchSize() { return batchSize; }
public void setBatchSize(Integer batchSize) { this.batchSize = batchSize; }
public Long getDeductPoints() { return deductPoints; }
public void setDeductPoints(Long deductPoints) { this.deductPoints = deductPoints; }
public Long getOperatorId() { return operatorId; }
public void setOperatorId(Long operatorId) { this.operatorId = operatorId; }

View File

@@ -0,0 +1,101 @@
package com.gameplatform.server.service.admin;
import com.gameplatform.server.mapper.admin.SystemConfigMapper;
import com.gameplatform.server.model.entity.admin.SystemConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SystemConfigService {
@Autowired
private SystemConfigMapper systemConfigMapper;
public SystemConfig getConfigByKey(String configKey) {
return systemConfigMapper.findByKey(configKey);
}
public String getConfigValue(String configKey, String defaultValue) {
SystemConfig config = systemConfigMapper.findByKey(configKey);
return config != null ? config.getConfigValue() : defaultValue;
}
public Integer getConfigValueAsInt(String configKey, Integer defaultValue) {
String value = getConfigValue(configKey, null);
if (value == null) return defaultValue;
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
public Boolean getConfigValueAsBoolean(String configKey, Boolean defaultValue) {
String value = getConfigValue(configKey, null);
if (value == null) return defaultValue;
return Boolean.parseBoolean(value);
}
public List<SystemConfig> getAllConfigs(int size, int offset) {
return systemConfigMapper.findAll(size, offset);
}
public long getConfigCount() {
return systemConfigMapper.countAll();
}
public List<SystemConfig> getConfigsByType(String configType) {
return systemConfigMapper.findByType(configType);
}
public boolean createConfig(SystemConfig systemConfig) {
return systemConfigMapper.insert(systemConfig) > 0;
}
public boolean updateConfig(SystemConfig systemConfig) {
return systemConfigMapper.update(systemConfig) > 0;
}
public boolean deleteConfig(Long id) {
return systemConfigMapper.deleteById(id) > 0;
}
public boolean deleteConfigByKey(String configKey) {
return systemConfigMapper.deleteByKey(configKey) > 0;
}
// 获取链接相关的默认配置
public Integer getDefaultQuantity() {
return getConfigValueAsInt("link.default_quantity", 50);
}
public Integer getRefreshInterval() {
return getConfigValueAsInt("link.refresh_interval", 300);
}
public Integer getQrExpireTime() {
return getConfigValueAsInt("link.qr_expire_time", 600);
}
public Integer getMaxTimesPerBatch() {
return getConfigValueAsInt("link.max_times_per_batch", 100);
}
public Integer getMinQuantity() {
return getConfigValueAsInt("link.min_quantity", 10);
}
public Integer getMaxQuantity() {
return getConfigValueAsInt("link.max_quantity", 1000);
}
public String getScriptServerUrl() {
return getConfigValue("script.server_url", "http://36.138.184.60:12345");
}
public String getQrPathTemplate() {
return getConfigValue("script.qr_path_template", "/{machineId}/二维码.png");
}
}

View File

@@ -11,7 +11,8 @@ import com.gameplatform.server.model.entity.agent.LinkTask;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import com.gameplatform.server.service.admin.SystemConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Mono;
@@ -30,31 +31,31 @@ public class LinkGenerationService {
private final LinkBatchMapper linkBatchMapper;
private final LinkTaskMapper linkTaskMapper;
private final AgentPointsTxMapper agentPointsTxMapper;
private final int expireHours;
private final SystemConfigService systemConfigService;
public LinkGenerationService(UserAccountMapper userAccountMapper,
LinkBatchMapper linkBatchMapper,
LinkTaskMapper linkTaskMapper,
AgentPointsTxMapper agentPointsTxMapper,
@Value("${link.expire-hours:2}") int expireHours) {
SystemConfigService systemConfigService) {
this.userAccountMapper = userAccountMapper;
this.linkBatchMapper = linkBatchMapper;
this.linkTaskMapper = linkTaskMapper;
this.agentPointsTxMapper = agentPointsTxMapper;
this.expireHours = expireHours;
this.systemConfigService = systemConfigService;
}
@Transactional
public Mono<GenerateResult> generateLinks(Long operatorId, String operatorType,
int times, int perTimeQuantity) {
return Mono.fromCallable(() -> doGenerate(operatorId, operatorType, times, perTimeQuantity))
int times, int linkCount) {
return Mono.fromCallable(() -> doGenerate(operatorId, operatorType, times, linkCount))
.subscribeOn(Schedulers.boundedElastic());
}
private GenerateResult doGenerate(Long operatorId, String operatorType,
int times, int perTimeQuantity) {
if (times <= 0 || perTimeQuantity <= 0) {
throw new IllegalArgumentException("times 与 perTimeQuantity 必须为正整数");
int times, int linkCount) {
if (times <= 0 || linkCount <= 0) {
throw new IllegalArgumentException("times 与 linkCount 必须为正整数");
}
// 获取操作者账户信息
@@ -68,11 +69,16 @@ public class LinkGenerationService {
throw new IllegalArgumentException("非法操作者类型");
}
// 从配置表获取每次副本的奖励点数
int perTimeQuantity = systemConfigService.getDefaultQuantity();
long needPoints = (long) times * (long) perTimeQuantity;
int expireHours = systemConfigService.getConfigValueAsInt("link.expire-hours", 2);
if (log.isDebugEnabled()) {
log.debug("generateLinks operatorId={} operatorType={} times={} perTimeQuantity={} needPoints={} expireHours={}",
operatorId, operatorType, times, perTimeQuantity, needPoints, expireHours);
log.debug("generateLinks operatorId={} operatorType={} times={} linkCount={} perTimeQuantity={} needPoints={} expireHours={}",
operatorId, operatorType, times, linkCount, perTimeQuantity, needPoints, expireHours);
}
if (!isAdminOperator) {
// 代理商自操作,需扣点判断
long balance = operator.getPointsBalance() == null ? 0L : operator.getPointsBalance();
@@ -83,16 +89,14 @@ public class LinkGenerationService {
LinkBatch batch = new LinkBatch();
batch.setAgentId(operator.getId());
batch.setQuantity(times);
batch.setTimes(times);
batch.setBatchSize(perTimeQuantity);
batch.setDeductPoints(needPoints);
batch.setQuantity(perTimeQuantity); // 每次副本的奖励点数
batch.setTimes(times); // 打副本的次数
batch.setOperatorId(operatorId);
linkBatchMapper.insert(batch);
LocalDateTime expireAt = LocalDateTime.now().plusHours(expireHours);
List<LinkTask> tasks = new ArrayList<>();
for (int i = 0; i < times; i++) {
for (int i = 0; i < linkCount; i++) { // 生成linkCount个链接
LinkTask t = new LinkTask();
t.setBatchId(batch.getId());
t.setAgentId(operator.getId());

View File

@@ -0,0 +1,125 @@
package com.gameplatform.server.service.link;
import com.gameplatform.server.mapper.agent.LinkBatchMapper;
import com.gameplatform.server.mapper.agent.LinkTaskMapper;
import com.gameplatform.server.model.dto.link.LinkStatusResponse;
import com.gameplatform.server.model.entity.agent.LinkBatch;
import com.gameplatform.server.model.entity.agent.LinkTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
@Service
public class LinkStatusService {
private static final Logger log = LoggerFactory.getLogger(LinkStatusService.class);
private final LinkTaskMapper linkTaskMapper;
private final LinkBatchMapper linkBatchMapper;
// 状态描述映射
private static final Map<String, String> STATUS_DESC_MAP = new HashMap<>();
static {
STATUS_DESC_MAP.put("NEW", "新建");
STATUS_DESC_MAP.put("USING", "使用中");
STATUS_DESC_MAP.put("LOGGED_IN", "已登录");
STATUS_DESC_MAP.put("REFUNDED", "已退款");
STATUS_DESC_MAP.put("EXPIRED", "已过期");
}
public LinkStatusService(LinkTaskMapper linkTaskMapper, LinkBatchMapper linkBatchMapper) {
this.linkTaskMapper = linkTaskMapper;
this.linkBatchMapper = linkBatchMapper;
}
/**
* 根据链接编号获取链接状态
*/
public Mono<LinkStatusResponse> getLinkStatus(String codeNo) {
return Mono.fromCallable(() -> doGetLinkStatus(codeNo))
.subscribeOn(Schedulers.boundedElastic());
}
private LinkStatusResponse doGetLinkStatus(String codeNo) {
if (codeNo == null || codeNo.trim().isEmpty()) {
throw new IllegalArgumentException("链接编号不能为空");
}
log.debug("查询链接状态: codeNo={}", codeNo);
// 查询链接任务
LinkTask linkTask = linkTaskMapper.findByCodeNo(codeNo);
if (linkTask == null) {
throw new IllegalArgumentException("链接不存在: " + codeNo);
}
// 查询批次信息
LinkBatch linkBatch = linkBatchMapper.findById(linkTask.getBatchId());
if (linkBatch == null) {
throw new IllegalStateException("批次信息不存在: batchId=" + linkTask.getBatchId());
}
// 构建响应对象
LinkStatusResponse response = new LinkStatusResponse();
response.setCodeNo(linkTask.getCodeNo());
response.setBatchId(linkTask.getBatchId());
response.setStatus(linkTask.getStatus());
response.setStatusDesc(STATUS_DESC_MAP.getOrDefault(linkTask.getStatus(), "未知状态"));
response.setExpireAt(linkTask.getExpireAt());
response.setQuantity(linkBatch.getQuantity());
response.setTimes(linkBatch.getTimes());
response.setTotalPoints(linkBatch.getQuantity() * linkBatch.getTimes());
response.setRegion(linkTask.getRegion());
response.setMachineId(linkTask.getMachineId());
response.setLoginAt(linkTask.getLoginAt());
response.setCreatedAt(linkTask.getCreatedAt());
response.setUpdatedAt(linkTask.getUpdatedAt());
// 计算过期状态和剩余时间
LocalDateTime now = LocalDateTime.now();
boolean isExpired = now.isAfter(linkTask.getExpireAt());
response.setIsExpired(isExpired);
if (!isExpired) {
long remainingSeconds = ChronoUnit.SECONDS.between(now, linkTask.getExpireAt());
response.setRemainingSeconds(remainingSeconds);
} else {
response.setRemainingSeconds(0L);
}
log.debug("链接状态查询完成: codeNo={}, status={}, isExpired={}, remainingSeconds={}",
codeNo, linkTask.getStatus(), isExpired, response.getRemainingSeconds());
return response;
}
/**
* 检查链接是否存在
*/
public Mono<Boolean> isLinkExists(String codeNo) {
return Mono.fromCallable(() -> {
if (codeNo == null || codeNo.trim().isEmpty()) {
return false;
}
LinkTask linkTask = linkTaskMapper.findByCodeNo(codeNo);
return linkTask != null;
}).subscribeOn(Schedulers.boundedElastic());
}
/**
* 检查链接是否有效(未过期且状态正常)
*/
public Mono<Boolean> isLinkValid(String codeNo) {
return getLinkStatus(codeNo)
.map(response -> !response.getIsExpired() &&
("NEW".equals(response.getStatus()) || "USING".equals(response.getStatus())))
.onErrorReturn(false);
}
}

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gameplatform.server.mapper.admin.SystemConfigMapper">
<resultMap id="SystemConfigMap" type="com.gameplatform.server.model.entity.admin.SystemConfig">
<id property="id" column="id" />
<result property="configKey" column="config_key" />
<result property="configValue" column="config_value" />
<result property="configType" column="config_type" />
<result property="description" column="description" />
<result property="isSystem" column="is_system" />
<result property="createdAt" column="created_at" />
<result property="updatedAt" column="updated_at" />
</resultMap>
<select id="findById" parameterType="long" resultMap="SystemConfigMap">
SELECT id, config_key, config_value, config_type, description, is_system, created_at, updated_at
FROM system_config
WHERE id = #{id}
LIMIT 1
</select>
<select id="findByKey" parameterType="string" resultMap="SystemConfigMap">
SELECT id, config_key, config_value, config_type, description, is_system, created_at, updated_at
FROM system_config
WHERE config_key = #{configKey}
LIMIT 1
</select>
<select id="findByType" parameterType="string" resultMap="SystemConfigMap">
SELECT id, config_key, config_value, config_type, description, is_system, created_at, updated_at
FROM system_config
WHERE config_type = #{configType}
ORDER BY config_key ASC
</select>
<select id="findAll" resultMap="SystemConfigMap">
SELECT id, config_key, config_value, config_type, description, is_system, created_at, updated_at
FROM system_config
ORDER BY config_key ASC
LIMIT #{size} OFFSET #{offset}
</select>
<select id="countAll" resultType="long">
SELECT COUNT(1) FROM system_config
</select>
<insert id="insert" parameterType="com.gameplatform.server.model.entity.admin.SystemConfig" useGeneratedKeys="true" keyProperty="id">
INSERT INTO system_config (config_key, config_value, config_type, description, is_system)
VALUES (#{configKey}, #{configValue}, #{configType}, #{description}, #{isSystem})
</insert>
<update id="update" parameterType="com.gameplatform.server.model.entity.admin.SystemConfig">
UPDATE system_config
SET config_value = #{configValue},
config_type = #{configType},
description = #{description},
is_system = #{isSystem}
WHERE id = #{id}
</update>
<delete id="deleteById" parameterType="long">
DELETE FROM system_config WHERE id = #{id}
</delete>
<delete id="deleteByKey" parameterType="string">
DELETE FROM system_config WHERE config_key = #{configKey}
</delete>
</mapper>

View File

@@ -6,26 +6,24 @@
<result property="agentId" column="agent_id" />
<result property="quantity" column="quantity" />
<result property="times" column="times" />
<result property="batchSize" column="batch_size" />
<result property="deductPoints" column="deduct_points" />
<result property="operatorId" column="operator_id" />
<result property="createdAt" column="created_at" />
</resultMap>
<select id="findById" parameterType="long" resultMap="LinkBatchMap">
SELECT id, agent_id, quantity, times, batch_size, deduct_points, operator_id, created_at
SELECT id, agent_id, quantity, times, operator_id, created_at
FROM link_batch
WHERE id = #{id}
LIMIT 1
</select>
<insert id="insert" parameterType="com.gameplatform.server.model.entity.agent.LinkBatch" useGeneratedKeys="true" keyProperty="id">
INSERT INTO link_batch (agent_id, quantity, times, batch_size, deduct_points, operator_id)
VALUES (#{agentId}, #{quantity}, #{times}, #{batchSize}, #{deductPoints}, #{operatorId})
INSERT INTO link_batch (agent_id, quantity, times, operator_id)
VALUES (#{agentId}, #{quantity}, #{times}, #{operatorId})
</insert>
<select id="findByAgentId" resultMap="LinkBatchMap">
SELECT id, agent_id, quantity, times, batch_size, deduct_points, operator_id, created_at
SELECT id, agent_id, quantity, times, operator_id, created_at
FROM link_batch
WHERE agent_id = #{agentId}
ORDER BY created_at DESC
@@ -37,7 +35,7 @@
</select>
<select id="findAll" resultMap="LinkBatchMap">
SELECT id, agent_id, quantity, times, batch_size, deduct_points, operator_id, created_at
SELECT id, agent_id, quantity, times, operator_id, created_at
FROM link_batch
ORDER BY created_at DESC
LIMIT #{size} OFFSET #{offset}