抽奖活动新增奖励配置信息接口

This commit is contained in:
tianfeng 2025-12-30 17:18:56 +08:00
parent 2f7a415a45
commit d279c549c1
11 changed files with 422 additions and 0 deletions

View File

@ -154,6 +154,11 @@ public enum OtherErrorCode implements IResponseErrorCode {
*/
SPECIAL_ID_ALREADY_EXISTS(3272, "Special numbers already exist"),
/**
* 用户充值金额无效
*/
USER_RECHARGE_INVALID(3273, "The recharge amount is insufficient to claim the reward"),
;
private final int code;

View File

@ -15,6 +15,8 @@ import com.red.circle.other.app.dto.clientobject.activity.LotteryWithdrawAmountC
import com.red.circle.other.app.dto.clientobject.activity.LotteryWithdrawCO;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRechargeRankCO;
import com.red.circle.other.app.dto.clientobject.activity.UserActivityRechargeCO;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRewardConfigCO;
import com.red.circle.other.app.dto.clientobject.activity.ClaimRewardResultCO;
import com.red.circle.other.app.dto.cmd.activity.LotteryDrawCmd;
import com.red.circle.other.app.dto.cmd.activity.LotteryMultiDrawCmd;
import com.red.circle.other.app.dto.cmd.activity.LotteryRankQryCmd;
@ -26,8 +28,11 @@ import com.red.circle.other.app.dto.cmd.activity.LotteryExchangeGoldCmd;
import com.red.circle.other.app.dto.cmd.activity.LotteryTransferCmd;
import com.red.circle.other.app.dto.cmd.activity.ActivityRechargeRankCmd;
import com.red.circle.other.app.dto.cmd.activity.UserActivityRechargeCmd;
import com.red.circle.other.app.dto.cmd.activity.ActivityRewardConfigCmd;
import com.red.circle.other.app.dto.cmd.activity.ClaimActivityRewardCmd;
import com.red.circle.other.app.service.activity.LotteryActivityRestService;
import com.red.circle.other.app.service.activity.ActivityRechargeService;
import com.red.circle.other.app.service.activity.ActivityRewardConfigService;
import java.util.List;
import lombok.RequiredArgsConstructor;
@ -55,6 +60,7 @@ public class LotteryActivityRestController extends BaseController {
private final LotteryActivityRestService lotteryActivityRestService;
private final ActivityRechargeService activityRechargeService;
private final ActivityRewardConfigService activityRewardConfigService;
/**
* 获取有效活动列表.
@ -278,4 +284,30 @@ public class LotteryActivityRestController extends BaseController {
return activityRechargeService.getUserRecharge(cmd);
}
/**
* 获取活动奖励配置列表.
*
* @eo.name 获取活动奖励配置列表.
* @eo.url /reward-configs
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/reward-configs")
public List<ActivityRewardConfigCO> getRewardConfigs(ActivityRewardConfigCmd cmd) {
return activityRewardConfigService.listRewardConfigs(cmd);
}
/**
* 领取活动奖励.
*
* @eo.name 领取活动奖励.
* @eo.url /claim-reward
* @eo.method post
* @eo.request-type json
*/
@PostMapping("/claim-reward")
public ClaimRewardResultCO claimReward(@RequestBody @Validated ClaimActivityRewardCmd cmd) {
return activityRewardConfigService.claimReward(cmd);
}
}

View File

@ -0,0 +1,88 @@
package com.red.circle.other.app.command.activity;
import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.Lists;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRewardConfigCO;
import com.red.circle.other.app.dto.cmd.activity.ActivityRewardConfigCmd;
import com.red.circle.other.domain.gateway.props.ActivitySourceGroupGateway;
import com.red.circle.other.inner.enums.activity.PropsActivityTypeEnum;
import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsGroup;
import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsRule;
import com.red.circle.other.inner.model.dto.activity.props.ActivityRewardProps;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 查询活动奖励配置执行器
*/
@Component
@RequiredArgsConstructor
public class ActivityRewardConfigQryExe {
private final ActivitySourceGroupGateway activitySourceGroupGateway;
public List<ActivityRewardConfigCO> execute(ActivityRewardConfigCmd cmd) {
PropsActivityTypeEnum activityType = PropsActivityTypeEnum.valueOf(cmd.getActivityType());
SysOriginPlatformEnum sysOrigin = SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin());
List<ActivityPropsRule> ruleConfigs = activitySourceGroupGateway.listRule(sysOrigin, activityType);
if (CollectionUtil.isEmpty(ruleConfigs)) {
return Lists.newArrayList();
}
return ruleConfigs.stream()
.map(rule -> buildRewardConfig(rule, cmd.getReqSysOrigin().getOrigin()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private ActivityRewardConfigCO buildRewardConfig(ActivityPropsRule rule, String sysOrigin) {
ActivityPropsGroup group = activitySourceGroupGateway.getActivityPropsGroup(
sysOrigin,
rule.getResourceGroupId()
);
if (Objects.isNull(group)) {
return null;
}
ActivityRewardConfigCO config = new ActivityRewardConfigCO()
.setRuleId(rule.getId())
.setSysOrigin(rule.getSysOrigin())
.setActivityType(rule.getActivityType())
.setResourceGroupId(rule.getResourceGroupId())
.setJsonData(rule.getJsonData())
.setSort(rule.getSort())
.setGroupId(group.getId())
.setGroupName(group.getName())
.setShelfStatus(group.getShelfStatus());
if (CollectionUtil.isNotEmpty(group.getActivityRewardProps())) {
List<ActivityRewardConfigCO.ActivityRewardPropsCO> propsList = group.getActivityRewardProps().stream()
.map(this::convertToRewardPropsCO)
.collect(Collectors.toList());
config.setRewardProps(propsList);
}
return config;
}
private ActivityRewardConfigCO.ActivityRewardPropsCO convertToRewardPropsCO(ActivityRewardProps props) {
return new ActivityRewardConfigCO.ActivityRewardPropsCO()
.setId(props.getId())
.setGroupId(props.getGroupId())
.setType(props.getType())
.setDetailType(props.getDetailType())
.setContent(props.getContent())
.setSort(props.getSort())
.setCover(props.getCover())
.setQuantity(props.getQuantity())
.setAmount(props.getAmount())
.setRemark(props.getRemark());
}
}

View File

@ -0,0 +1,97 @@
package com.red.circle.other.app.command.activity;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.dto.clientobject.activity.ClaimRewardResultCO;
import com.red.circle.other.app.dto.cmd.activity.ClaimActivityRewardCmd;
import com.red.circle.other.domain.gateway.props.ActivitySourceGroupGateway;
import com.red.circle.other.infra.database.rds.service.activity.UserActivityRechargeService;
import com.red.circle.other.inner.asserts.OtherErrorCode;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.enums.activity.PropsActivityTypeEnum;
import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsRule;
import com.red.circle.tool.core.json.JacksonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 领取活动奖励执行器
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ClaimActivityRewardExe {
private final ActivitySourceGroupGateway activitySourceGroupGateway;
private final UserActivityRechargeService userActivityRechargeService;
@Transactional(rollbackFor = Exception.class)
public ClaimRewardResultCO execute(ClaimActivityRewardCmd cmd) {
PropsActivityTypeEnum activityType = PropsActivityTypeEnum.valueOf(cmd.getActivityType());
SysOriginPlatformEnum sysOrigin = SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin());
List<ActivityPropsRule> ruleConfigs = activitySourceGroupGateway.listRule(sysOrigin, activityType);
ActivityPropsRule targetRule = ruleConfigs.stream()
.filter(rule -> Objects.equals(rule.getId(), cmd.getRuleId()))
.findFirst().orElse(null);
ResponseAssert.isTrue(CommonErrorCode.DATA_ERROR, targetRule != null);
BigDecimal requiredAmount = parseRequiredAmount(targetRule.getJsonData());
ResponseAssert.isTrue(CommonErrorCode.DATA_ERROR, requiredAmount != null);
BigDecimal userTotalAmount = userActivityRechargeService.getUserTotalAmount(
cmd.getActivityId(),
cmd.getReqUserId()
);
ResponseAssert.isFalse(OtherErrorCode.USER_RECHARGE_INVALID, Objects.isNull(userTotalAmount) || userTotalAmount.compareTo(requiredAmount) < 0);
// TODO: 这里需要实现实际的奖励发放逻辑
// 1. 记录用户已领取该奖励
// 2. 发放奖励到用户账户
// 3. 发送通知等
return new ClaimRewardResultCO()
.setSuccess(true)
.setMessage("Successfully claimed");
}
private BigDecimal parseRequiredAmount(String jsonData) {
if (StrUtil.isBlank(jsonData)) {
return null;
}
try {
Map<String, Object> dataMap = JacksonUtils.readValue(jsonData, Map.class);
Object quantity = dataMap.get("quantity");
if (Objects.isNull(quantity)) {
return null;
}
if (quantity instanceof Number) {
return new BigDecimal(quantity.toString());
}
if (quantity instanceof String) {
return new BigDecimal((String) quantity);
}
return null;
} catch (Exception e) {
log.error("解析jsonData失败: {}", jsonData, e);
return null;
}
}
}

View File

@ -0,0 +1,34 @@
package com.red.circle.other.app.service.activity.impl;
import com.red.circle.other.app.command.activity.ActivityRewardConfigQryExe;
import com.red.circle.other.app.command.activity.ClaimActivityRewardExe;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRewardConfigCO;
import com.red.circle.other.app.dto.clientobject.activity.ClaimRewardResultCO;
import com.red.circle.other.app.dto.cmd.activity.ActivityRewardConfigCmd;
import com.red.circle.other.app.dto.cmd.activity.ClaimActivityRewardCmd;
import com.red.circle.other.app.service.activity.ActivityRewardConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 活动奖励配置服务实现
*/
@Service
@RequiredArgsConstructor
public class ActivityRewardConfigServiceImpl implements ActivityRewardConfigService {
private final ActivityRewardConfigQryExe activityRewardConfigQryExe;
private final ClaimActivityRewardExe claimActivityRewardExe;
@Override
public List<ActivityRewardConfigCO> listRewardConfigs(ActivityRewardConfigCmd cmd) {
return activityRewardConfigQryExe.execute(cmd);
}
@Override
public ClaimRewardResultCO claimReward(ClaimActivityRewardCmd cmd) {
return claimActivityRewardExe.execute(cmd);
}
}

View File

@ -0,0 +1,68 @@
package com.red.circle.other.app.dto.clientobject.activity;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* 活动奖励配置CO
*/
@Data
@Accessors(chain = true)
public class ActivityRewardConfigCO implements Serializable {
@JsonSerialize(using = ToStringSerializer.class)
private Long ruleId;
private String sysOrigin;
private String activityType;
@JsonSerialize(using = ToStringSerializer.class)
private Long resourceGroupId;
private String jsonData;
private Integer sort;
@JsonSerialize(using = ToStringSerializer.class)
private Long groupId;
private String groupName;
private Boolean shelfStatus;
private List<ActivityRewardPropsCO> rewardProps;
@Data
@Accessors(chain = true)
public static class ActivityRewardPropsCO implements Serializable {
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@JsonSerialize(using = ToStringSerializer.class)
private Long groupId;
private String type;
private String detailType;
private String content;
private Integer sort;
private String cover;
private Integer quantity;
private BigDecimal amount;
private String remark;
}
}

View File

@ -0,0 +1,18 @@
package com.red.circle.other.app.dto.clientobject.activity;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 领取奖励结果CO
*/
@Data
@Accessors(chain = true)
public class ClaimRewardResultCO implements Serializable {
private Boolean success;
private String message;
}

View File

@ -0,0 +1,19 @@
package com.red.circle.other.app.dto.cmd.activity;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 查询活动奖励配置Cmd
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class ActivityRewardConfigCmd extends AppExtCommand {
@NotBlank(message = "活动类型不能为空")
private String activityType;
}

View File

@ -0,0 +1,26 @@
package com.red.circle.other.app.dto.cmd.activity;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 领取活动奖励Cmd
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class ClaimActivityRewardCmd extends AppExtCommand {
@NotBlank(message = "活动类型不能为空")
private String activityType;
@NotNull(message = "活动ID不能为空")
private Long activityId;
@NotNull(message = "规则ID不能为空")
private Long ruleId;
}

View File

@ -0,0 +1,24 @@
package com.red.circle.other.app.service.activity;
import com.red.circle.other.app.dto.clientobject.activity.ActivityRewardConfigCO;
import com.red.circle.other.app.dto.clientobject.activity.ClaimRewardResultCO;
import com.red.circle.other.app.dto.cmd.activity.ActivityRewardConfigCmd;
import com.red.circle.other.app.dto.cmd.activity.ClaimActivityRewardCmd;
import java.util.List;
/**
* 活动奖励配置服务
*/
public interface ActivityRewardConfigService {
/**
* 获取活动奖励配置列表
*/
List<ActivityRewardConfigCO> listRewardConfigs(ActivityRewardConfigCmd cmd);
/**
* 领取活动奖励
*/
ClaimRewardResultCO claimReward(ClaimActivityRewardCmd cmd);
}

View File

@ -11,6 +11,7 @@ import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.dto.ReqSysOrigin;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.other.app.common.props.SingleGroupRewardPoolCommon;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
import com.red.circle.other.app.enums.violation.GameOriginEnum;
@ -40,6 +41,7 @@ import com.red.circle.other.inner.enums.activity.PropsActivityTypeEnum;
import com.red.circle.other.inner.model.cmd.activity.SendActivityRewardCmd;
import com.red.circle.other.inner.model.cmd.user.ApprovalAccountCmd;
import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsRule;
import com.red.circle.other.inner.model.dto.activity.props.ActivityRewardPropsDTO;
import com.red.circle.other.inner.model.dto.user.UserOneTimeTaskDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.parse.DataTypeUtils;
@ -94,6 +96,15 @@ public class SpringTest {
private UserRegionGateway userRegionGateway;
@Autowired
private UserActivityRechargeService userActivityRechargeService;
@Autowired
private SingleGroupRewardPoolCommon singleGroupRewardPoolCommon;
@Test
public void testActivityRuleConfig() {
List<ActivityRewardPropsDTO> rewardList = singleGroupRewardPoolCommon.listRewardProps("LIKEI", PropsActivityTypeEnum.CONSUMPTION_ACTIVITY);
System.out.println(rewardList);
}
@Test
public void getDeviceFingerprintByUserId() {