修复幸运礼物和系统推送
This commit is contained in:
parent
4396f34b3b
commit
1fd196094d
@ -83,10 +83,19 @@ public class SysPushRecordLogQryCmd extends PageCommand {
|
||||
*/
|
||||
private Integer failQuantity;
|
||||
|
||||
/**
|
||||
* 成功数量
|
||||
*/
|
||||
private Integer successQuantity;
|
||||
|
||||
|
||||
}
|
||||
/**
|
||||
* 成功数量
|
||||
*/
|
||||
private Integer successQuantity;
|
||||
|
||||
/**
|
||||
* 开始时间.
|
||||
*/
|
||||
private Long startTime;
|
||||
|
||||
/**
|
||||
* 结束时间.
|
||||
*/
|
||||
private Long endTime;
|
||||
|
||||
}
|
||||
|
||||
@ -1,24 +1,26 @@
|
||||
package com.red.circle.other.inner.model.cmd.sys;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 推送任务
|
||||
package com.red.circle.other.inner.model.cmd.sys;
|
||||
|
||||
import com.red.circle.framework.core.dto.PageCommand;
|
||||
import java.io.Serial;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 推送任务
|
||||
* </p>
|
||||
*
|
||||
* @author pengshigang
|
||||
* @since 2022-10-20
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class SysPushTaskQryCmd implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class SysPushTaskQryCmd extends PageCommand {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 0.下架 1.上架.
|
||||
|
||||
@ -1,65 +1,75 @@
|
||||
package com.red.circle.console.adapter.app.sys;
|
||||
|
||||
import com.red.circle.console.app.service.app.sys.SysPushService;
|
||||
import com.red.circle.framework.web.controller.BaseController;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
package com.red.circle.console.adapter.app.sys;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.sys.SysPushRecordLogCO;
|
||||
import com.red.circle.console.app.dto.clienobject.sys.SysPushTaskCO;
|
||||
import com.red.circle.console.app.service.app.sys.SysPushService;
|
||||
import com.red.circle.console.infra.annotations.OpsOperationReqLog;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.framework.web.controller.BaseController;
|
||||
import com.red.circle.other.inner.model.cmd.sys.CreateRecordCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushRecordLogQryCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushTaskCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushTaskQryCmd;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 后台推送 前端控制器
|
||||
*
|
||||
* @author pengliang
|
||||
* @since 2020-02-07
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping(value = "/push", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class PushRestController extends BaseController {
|
||||
|
||||
private final SysPushService sysPushService;
|
||||
/*
|
||||
*//**
|
||||
* 后台推送分页列表.
|
||||
*//*
|
||||
@GetMapping("/page")
|
||||
public PageResult<SysPushRecordLogCO> pageSysPushBack(SysPushRecordLogQryCmd query) {
|
||||
return sysPushService.pageLogs(query);
|
||||
}
|
||||
|
||||
@OpsOperationReqLog("发送推送信息")
|
||||
@PostMapping
|
||||
public void saveAndPush(@RequestBody CreateRecordCmd param) {
|
||||
param.setCreateUser(param.getReqUserId());
|
||||
// 设置代理,上线去除
|
||||
// System.setProperty("proxyHost", "localhost");
|
||||
// System.setProperty("proxyPort", "10809");
|
||||
sysPushService.create(param);
|
||||
}
|
||||
|
||||
@OpsOperationReqLog("保存定时消息推送任务")
|
||||
@PostMapping("/task/save")
|
||||
public void saveTask(@RequestBody @Validated SysPushTaskCmd param) {
|
||||
param.setCreateUser(param.getReqUserId());
|
||||
sysPushService.saveTask(param);
|
||||
}
|
||||
|
||||
*//**
|
||||
* 定时推送任务列表.
|
||||
*//*
|
||||
@GetMapping("/task/page")
|
||||
public PageResult<SysPushTaskCO> pageTask(SysPushTaskQryCmd query) {
|
||||
return sysPushService.pageTask(query);
|
||||
}
|
||||
|
||||
*//**
|
||||
* 定时推送任务列表.
|
||||
*//*
|
||||
@GetMapping("/task/delete/{id}")
|
||||
public void deletePushTask(@PathVariable("id") Long id) {
|
||||
sysPushService.deletePushTask(id);
|
||||
|
||||
}*/
|
||||
|
||||
}
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping(value = "/push", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class PushRestController extends BaseController {
|
||||
|
||||
private final SysPushService sysPushService;
|
||||
|
||||
/**
|
||||
* 后台推送分页列表.
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public PageResult<SysPushRecordLogCO> pageSysPushBack(SysPushRecordLogQryCmd query) {
|
||||
return sysPushService.pageLogs(query);
|
||||
}
|
||||
|
||||
@OpsOperationReqLog("发送推送信息")
|
||||
@PostMapping
|
||||
public void saveAndPush(@RequestBody CreateRecordCmd param) {
|
||||
param.setCreateUser(param.getReqUserId());
|
||||
sysPushService.create(param);
|
||||
}
|
||||
|
||||
@OpsOperationReqLog("保存定时消息推送任务")
|
||||
@PostMapping("/task/save")
|
||||
public void saveTask(@RequestBody @Validated SysPushTaskCmd param) {
|
||||
param.setCreateUser(param.getReqUserId());
|
||||
sysPushService.saveTask(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时推送任务列表.
|
||||
*/
|
||||
@GetMapping("/task/page")
|
||||
public PageResult<SysPushTaskCO> pageTask(SysPushTaskQryCmd query) {
|
||||
return sysPushService.pageTask(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除定时推送任务.
|
||||
*/
|
||||
@GetMapping("/task/delete/{id}")
|
||||
public void deletePushTask(@PathVariable("id") Long id) {
|
||||
sysPushService.deletePushTask(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,255 +1,419 @@
|
||||
package com.red.circle.console.app.service.app.sys;
|
||||
|
||||
import com.red.circle.console.infra.database.rds.service.admin.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author pengliang
|
||||
* @since 2020/3/6 15:53
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SysPushServiceImpl implements SysPushService {
|
||||
|
||||
private final UserService userService;
|
||||
/*private final ISysPushTaskService sysPushTaskService;
|
||||
private final IUserBaseInfoService userBaseInfoService;
|
||||
private final ISysPushRecordLogService sysPushRecordLogService;
|
||||
private final GoogleFirebasePushService googleFirebasePushService;
|
||||
private final ISysPushUserRecordLogService sysPushUserRecordLogService;
|
||||
private final IUserLatestMobileDeviceService userLatestMobileDeviceService;
|
||||
|
||||
@Override
|
||||
public void create(CreateRecordCmd createRecordParam) {
|
||||
|
||||
if (StringUtils.isBlank(createRecordParam.getSysOrigin())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (createRecordParam.isGoogleDevice()) {
|
||||
|
||||
pushGoogle(createRecordParam);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SysPushRecordLogCO> pageLogs(SysPushRecordLogQryCmd query) {
|
||||
|
||||
IPage<SysPushRecordLog> sysPushRecordLogPage = sysPushRecordLogService.query()
|
||||
.eq(StringUtils.isNotBlank(query.getBusinessScene()), SysPushRecordLog::getBusinessScene,
|
||||
query.getBusinessScene())
|
||||
.eq(StringUtils.isNotBlank(query.getPlatform()), SysPushRecordLog::getPlatform,
|
||||
query.getPlatform())
|
||||
.eq(StringUtils.isNotBlank(query.getDeviceType()), SysPushRecordLog::getDeviceType,
|
||||
query.getDeviceType())
|
||||
.eq(StringUtils.isNotBlank(query.getSysOrigin()), SysPushRecordLog::getSysOrigin,
|
||||
query.getSysOrigin())
|
||||
.ge(Objects.nonNull(query.getStartCreateDate()), SysPushRecordLog::getCreateTime,
|
||||
query.getStartCreateDate())
|
||||
.le(Objects.nonNull(query.getEndCreateDate()), SysPushRecordLog::getCreateTime,
|
||||
query.getEndCreateDate())
|
||||
.eq(Objects.nonNull(query.getPushStatus()) && query.isSuccess(),
|
||||
SysPushRecordLog::getPushStatus, ResultStatusEnum.SUCCESS)
|
||||
.in(Objects.nonNull(query.getPushStatus()) && !query.isSuccess(),
|
||||
SysPushRecordLog::getPushStatus, ResultStatusEnum.getFailKeys())
|
||||
.orderByDesc(SysPushRecordLog::getId)
|
||||
.page(query);
|
||||
|
||||
if (CollectionUtil.isEmpty(sysPushRecordLogPage.getRecords())) {
|
||||
return sysPushRecordLogPage
|
||||
.convert(sysPushRecordLog -> sysPushRecordLog.convert(SysPushRecordLogCO.class));
|
||||
}
|
||||
|
||||
Map<Long, User> userMap = userService.mapBackUser(getBackUserIds(sysPushRecordLogPage));
|
||||
Map<Long, UserBaseInfo> userBaseInfoMap = userBaseInfoService
|
||||
.mapByIds(getAppUserIds(sysPushRecordLogPage));
|
||||
|
||||
return sysPushRecordLogPage.convert(sysPushRecordLog -> {
|
||||
SysPushRecordLogCO sysPushRecordLogVO = sysPushRecordLog.convert(SysPushRecordLogCO.class);
|
||||
sysPushRecordLogVO
|
||||
.setBusinessScene(MessageBusinessSceneEnum.getDesc(sysPushRecordLog.getBusinessScene()));
|
||||
sysPushRecordLogVO.setPushStatus(ResultStatusEnum.getDesc(sysPushRecordLog.getPushStatus()));
|
||||
|
||||
if (PushOriginEnum.isBack(sysPushRecordLog.getOrigin())) {
|
||||
User user = userMap.get(sysPushRecordLog.getCreateUser());
|
||||
if (Objects.nonNull(user)) {
|
||||
sysPushRecordLogVO.setSendUserName(user.getNickname());
|
||||
}
|
||||
return sysPushRecordLogVO;
|
||||
}
|
||||
|
||||
UserBaseInfo userBaseInfo = userBaseInfoMap.get(sysPushRecordLog.getCreateUser());
|
||||
if (Objects.nonNull(userBaseInfo)) {
|
||||
sysPushRecordLogVO.setSendUserName(userBaseInfo.getUserNickname());
|
||||
}
|
||||
return sysPushRecordLogVO;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SysPushTaskCO> pageTask(SysPushTaskQryCmd query) {
|
||||
return sysPushTaskService.pageTask(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveTask(SysPushTaskCmd param) {
|
||||
sysPushTaskService.saveTask(param);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void deletePushTask(Long id) {
|
||||
sysPushTaskService.deletePushTask(id);
|
||||
}
|
||||
|
||||
private List<Long> getBackUserIds(PageResult<SysPushRecordLogCO> sysPushRecordLogPage) {
|
||||
return sysPushRecordLogPage.getRecords().stream().filter(sysPushRecordLog ->
|
||||
PushOriginEnum.isBack(sysPushRecordLog.getOrigin()) && sysPushRecordLog.getCreateUser() > 0
|
||||
).map(SysPushRecordLog::getCreateUser).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Set<Long> getAppUserIds(IPage<SysPushRecordLog> sysPushRecordLogPage) {
|
||||
return sysPushRecordLogPage.getRecords().stream().filter(sysPushRecordLog ->
|
||||
PushOriginEnum.isApp(sysPushRecordLog.getOrigin()) && sysPushRecordLog.getCreateUser() > 0
|
||||
).map(SysPushRecordLog::getCreateUser).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
*//**
|
||||
* 推送谷歌
|
||||
*//*
|
||||
private void pushGoogle(CreateRecordCmd createRecordParam) {
|
||||
|
||||
List<UserLatestMobileDevicePushDTO> users = userLatestMobileDeviceService
|
||||
.listPushUserByCondition(createRecordParam);
|
||||
|
||||
if (CollectionUtils.isEmpty(users)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPoolManager.getInstance().execute("pushGoogle", () -> {
|
||||
|
||||
List<String> deviceIds = users.stream().map(UserLatestMobileDevicePushDTO::getDeviceId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//推送并处理结果
|
||||
processPush(createRecordParam, users, googleFirebasePushService.sendMulticast(
|
||||
PushParam.builder()
|
||||
.sysOrigin(SysOriginPlatformEnum.valueOf(createRecordParam.getSysOrigin()))
|
||||
.androidDevices(deviceIds)
|
||||
.title(createRecordParam.getTitle())
|
||||
.body(createRecordParam.getContent())
|
||||
.build()
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
private void processPush(CreateRecordCmd createRecordParam,
|
||||
List<UserLatestMobileDevicePushDTO> users,
|
||||
FirebaseResponses responses) {
|
||||
|
||||
if (Objects.isNull(responses) || Objects.equals(responses.getFailCount(), users.size())) {
|
||||
//全部失败
|
||||
allPushFail(createRecordParam, users);
|
||||
return;
|
||||
}
|
||||
|
||||
createRecordParam.setPushStatus(ResultStatusEnum.SUCCESS.name());
|
||||
createRecordParam.setFailQuantity(responses.getFailCount());
|
||||
createRecordParam.setSuccessQuantity(responses.getSuccessCount());
|
||||
addPushLog(createRecordParam);
|
||||
//处理成功或失败
|
||||
processPushResult(createRecordParam.getId(), users, responses);
|
||||
}
|
||||
|
||||
private void processPushResult(Long pushId, List<UserLatestMobileDevicePushDTO> users,
|
||||
FirebaseResponses responses) {
|
||||
|
||||
List<String> successTokens = responses.getSuccessDevicesString();
|
||||
List<String> failTokens = responses.getFailDevicesString();
|
||||
|
||||
List<SysPushUserRecordLog> saveData = Lists.newArrayList();
|
||||
Timestamp date = TimestampUtils.now();
|
||||
|
||||
for (UserLatestMobileDevicePushDTO user : users) {
|
||||
|
||||
SysPushUserRecordLog pushUserRecord = new SysPushUserRecordLog();
|
||||
pushUserRecord.setId(IdWorkerUtils.getId());
|
||||
pushUserRecord.setPushId(pushId);
|
||||
pushUserRecord.setPushToken(user.getDeviceId());
|
||||
pushUserRecord.setUserId(user.getUserId());
|
||||
pushUserRecord.setCreateTime(date);
|
||||
pushUserRecord.setUpdateTime(date);
|
||||
|
||||
Boolean isSuccess = isSuccess(successTokens, failTokens, user);
|
||||
if (Objects.isNull(isSuccess)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pushUserRecord.setSuccess(isSuccess);
|
||||
saveData.add(pushUserRecord);
|
||||
}
|
||||
|
||||
List<List<SysPushUserRecordLog>> logList = Lists.partition(saveData, 200);
|
||||
logList.forEach(sysPushUserRecordLogService::saveBatch);
|
||||
}
|
||||
|
||||
private Boolean isSuccess(List<String> successTokens, List<String> failTokens,
|
||||
UserLatestMobileDevicePushDTO user) {
|
||||
|
||||
for (String successToken : successTokens) {
|
||||
if (Objects.equals(user.getDeviceId(), successToken)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
for (String failToken : failTokens) {
|
||||
if (Objects.equals(user.getDeviceId(), failToken)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void allPushFail(CreateRecordCmd createRecordParam,
|
||||
List<UserLatestMobileDevicePushDTO> users) {
|
||||
|
||||
createRecordParam.setPushStatus(ResultStatusEnum.FAIL.name());
|
||||
createRecordParam.setFailQuantity(users.size());
|
||||
createRecordParam.setSuccessQuantity(0);
|
||||
addPushLog(createRecordParam);
|
||||
|
||||
Timestamp date = TimestampUtils.now();
|
||||
|
||||
List<SysPushUserRecordLog> logs = users.stream().map(user -> {
|
||||
|
||||
SysPushUserRecordLog pushUserRecord = new SysPushUserRecordLog();
|
||||
pushUserRecord.setPushId(createRecordParam.getId());
|
||||
pushUserRecord.setPushToken(user.getDeviceId());
|
||||
pushUserRecord.setUserId(user.getUserId());
|
||||
pushUserRecord.setSuccess(Boolean.FALSE);
|
||||
pushUserRecord.setCreateTime(date);
|
||||
pushUserRecord.setUpdateTime(date);
|
||||
pushUserRecord.setCreateUser(createRecordParam.getCreateUser());
|
||||
return pushUserRecord;
|
||||
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
List<List<SysPushUserRecordLog>> logList = Lists.partition(logs, 200);
|
||||
logList.forEach(sysPushUserRecordLogService::saveBatch);
|
||||
}
|
||||
|
||||
private void addPushLog(CreateRecordCmd newPushDTO) {
|
||||
SysPushRecordLog sysPushRecordLog = newPushDTO.convert(SysPushRecordLog.class);
|
||||
sysPushRecordLog.setOrigin(PushOriginEnum.BACK.name());
|
||||
sysPushRecordLogService.save(sysPushRecordLog);
|
||||
newPushDTO.setId(sysPushRecordLog.getId());
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
package com.red.circle.console.app.service.app.sys;
|
||||
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.console.app.dto.clienobject.sys.SysPushRecordLogCO;
|
||||
import com.red.circle.console.app.dto.clienobject.sys.SysPushTaskCO;
|
||||
import com.red.circle.console.infra.database.rds.entity.sys.SysPushRecordLog;
|
||||
import com.red.circle.console.infra.database.rds.service.admin.UserService;
|
||||
import com.red.circle.console.infra.database.rds.service.sys.SysPushRecordLogService;
|
||||
import com.red.circle.external.inner.endpoint.push.GoogleFirebasePushClient;
|
||||
import com.red.circle.external.inner.model.cmd.message.google.GooglePushCmd;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.framework.core.dto.PageCommand;
|
||||
import com.red.circle.framework.core.response.CommonErrorCode;
|
||||
import com.red.circle.framework.core.response.ResponseErrorCode;
|
||||
import com.red.circle.framework.dto.PageQuery;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.other.inner.endpoint.user.device.RegisterDeviceClient;
|
||||
import com.red.circle.other.inner.enums.sys.MessageBusinessSceneEnum;
|
||||
import com.red.circle.other.inner.model.cmd.sys.CreateRecordCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushRecordLogQryCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushTaskCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushTaskQryCmd;
|
||||
import com.red.circle.other.inner.model.cmd.user.device.PageLatestMobileDeviceQryCmd;
|
||||
import com.red.circle.other.inner.model.dto.user.UserLatestMobileDeviceHistoryDTO;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import com.red.circle.tool.core.date.TimestampUtils;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 后台 Push 服务.
|
||||
*
|
||||
* <p>旧的推送实现已经失效,这里按当前可用的设备查询与推送客户端重建最小闭环:
|
||||
* 发送 Push、记录 Push 日志、为未迁移的定时任务返回明确的不可用状态。</p>
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SysPushServiceImpl implements SysPushService {
|
||||
|
||||
private static final int DEFAULT_PAGE_LIMIT = 20;
|
||||
private static final int DEVICE_PAGE_LIMIT = 500;
|
||||
private static final String PUSH_ORIGIN_BACK = "BACK";
|
||||
private static final String PUSH_STATUS_SUCCESS = "SUCCESS";
|
||||
private static final String PUSH_STATUS_FAIL = "FAIL";
|
||||
private static final Set<String> PUSH_FAIL_STATUSES = Set.of(
|
||||
"UNKNOWN",
|
||||
PUSH_STATUS_FAIL,
|
||||
"IOS_FAIL",
|
||||
"ANDROID_FAIL");
|
||||
|
||||
private final UserService userService;
|
||||
private final RegisterDeviceClient registerDeviceClient;
|
||||
private final GoogleFirebasePushClient googleFirebasePushClient;
|
||||
private final SysPushRecordLogService sysPushRecordLogService;
|
||||
|
||||
@Override
|
||||
public void create(CreateRecordCmd createRecordParam) {
|
||||
validateCreateParam(createRecordParam);
|
||||
|
||||
List<UserLatestMobileDeviceHistoryDTO> targetDevices = listTargetDevices(createRecordParam);
|
||||
if (CollectionUtils.isEmpty(targetDevices)) {
|
||||
ResponseAssert.failure(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
"No matching push devices.");
|
||||
}
|
||||
|
||||
SysPushRecordLog pushLog = buildPushLog(createRecordParam);
|
||||
try {
|
||||
pushGoogle(createRecordParam, targetDevices);
|
||||
pushLog.setPushStatus(PUSH_STATUS_SUCCESS);
|
||||
pushLog.setSuccessQuantity(targetDevices.size());
|
||||
pushLog.setFailQuantity(0);
|
||||
} catch (RuntimeException ex) {
|
||||
pushLog.setPushStatus(PUSH_STATUS_FAIL);
|
||||
pushLog.setSuccessQuantity(0);
|
||||
pushLog.setFailQuantity(targetDevices.size());
|
||||
log.warn("Push broadcast failed, sysOrigin={}, title={}", createRecordParam.getSysOrigin(),
|
||||
createRecordParam.getTitle(), ex);
|
||||
throw ex;
|
||||
} finally {
|
||||
ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE, !sysPushRecordLogService.save(pushLog));
|
||||
createRecordParam.setId(pushLog.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SysPushRecordLogCO> pageLogs(SysPushRecordLogQryCmd query) {
|
||||
PageResult<SysPushRecordLog> page = sysPushRecordLogService.query()
|
||||
.eq(StringUtils.isNotBlank(query.getBusinessScene()), SysPushRecordLog::getBusinessScene,
|
||||
query.getBusinessScene())
|
||||
.eq(StringUtils.isNotBlank(query.getPlatform()), SysPushRecordLog::getPlatform,
|
||||
query.getPlatform())
|
||||
.eq(StringUtils.isNotBlank(query.getDeviceType()), SysPushRecordLog::getDeviceType,
|
||||
query.getDeviceType())
|
||||
.eq(StringUtils.isNotBlank(query.getSysOrigin()), SysPushRecordLog::getSysOrigin,
|
||||
query.getSysOrigin())
|
||||
.ge(Objects.nonNull(query.getStartTime()), SysPushRecordLog::getCreateTime,
|
||||
new Timestamp(query.getStartTime()))
|
||||
.le(Objects.nonNull(query.getEndTime()), SysPushRecordLog::getCreateTime,
|
||||
new Timestamp(query.getEndTime()))
|
||||
.eq(isSuccessStatusQuery(query.getPushStatus()), SysPushRecordLog::getPushStatus,
|
||||
PUSH_STATUS_SUCCESS)
|
||||
.in(isFailStatusQuery(query.getPushStatus()), SysPushRecordLog::getPushStatus,
|
||||
PUSH_FAIL_STATUSES)
|
||||
.orderByDesc(SysPushRecordLog::getId)
|
||||
.page(ensurePageQuery(query));
|
||||
|
||||
if (CollectionUtils.isEmpty(page.getRecords())) {
|
||||
return page.convert(this::toPushLogCO);
|
||||
}
|
||||
|
||||
Map<Long, String> sendUserNameMap = userService.mapBackUserNickname(
|
||||
page.getRecords().stream()
|
||||
.map(SysPushRecordLog::getCreateUser)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
return page.convert(record -> toPushLogCO(record, sendUserNameMap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SysPushTaskCO> pageTask(SysPushTaskQryCmd query) {
|
||||
return PageResult.newPageResult(resolvePageLimit(ensurePageQuery(query)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveTask(SysPushTaskCmd param) {
|
||||
ResponseAssert.failure(CommonErrorCode.API_DEPRECATED,
|
||||
"Push task is not available in the migrated admin yet.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePushTask(Long id) {
|
||||
ResponseAssert.failure(CommonErrorCode.API_DEPRECATED,
|
||||
"Push task is not available in the migrated admin yet.");
|
||||
}
|
||||
|
||||
private void validateCreateParam(CreateRecordCmd createRecordParam) {
|
||||
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, createRecordParam.getTitle());
|
||||
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
createRecordParam.getContent());
|
||||
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
createRecordParam.getSysOrigin());
|
||||
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
createRecordParam.getDeviceType());
|
||||
ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
SysOriginPlatformEnum.toEnum(createRecordParam.getSysOrigin()));
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
!createRecordParam.isGoogleDevice());
|
||||
}
|
||||
|
||||
private void pushGoogle(CreateRecordCmd createRecordParam,
|
||||
List<UserLatestMobileDeviceHistoryDTO> targetDevices) {
|
||||
GooglePushCmd pushCmd = new GooglePushCmd()
|
||||
.setSysOrigin(SysOriginPlatformEnum.toEnum(createRecordParam.getSysOrigin()))
|
||||
.setTitle(createRecordParam.getTitle())
|
||||
.setBody(createRecordParam.getContent());
|
||||
|
||||
for (UserLatestMobileDeviceHistoryDTO device : targetDevices) {
|
||||
if (isIosPlatform(device.getRequestClient())) {
|
||||
pushCmd.addIosDevice(device.getDeviceId());
|
||||
continue;
|
||||
}
|
||||
pushCmd.addAndroidDevice(device.getDeviceId());
|
||||
}
|
||||
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
!pushCmd.isNotEmptyAndroidDevices() && !pushCmd.isNotEmptyIosDevices());
|
||||
ResponseAssert.checkSuccess(googleFirebasePushClient.broadcast(pushCmd));
|
||||
}
|
||||
|
||||
private List<UserLatestMobileDeviceHistoryDTO> listTargetDevices(CreateRecordCmd createRecordParam) {
|
||||
List<UserLatestMobileDeviceHistoryDTO> devices = createRecordParam.isFixedUserIdSend()
|
||||
? listFixedTargetDevices(createRecordParam)
|
||||
: listBroadcastTargetDevices(createRecordParam);
|
||||
return distinctByDeviceId(devices);
|
||||
}
|
||||
|
||||
private List<UserLatestMobileDeviceHistoryDTO> listFixedTargetDevices(
|
||||
CreateRecordCmd createRecordParam) {
|
||||
List<Long> fixedUserIds = createRecordParam.getQualifiedFixedUserIdList();
|
||||
if (CollectionUtils.isEmpty(fixedUserIds)) {
|
||||
return CollectionUtils.newArrayList();
|
||||
}
|
||||
|
||||
List<UserLatestMobileDeviceHistoryDTO> devices = new ArrayList<>(fixedUserIds.size());
|
||||
for (Long userId : fixedUserIds) {
|
||||
PageLatestMobileDeviceQryCmd query = buildDeviceQuery(createRecordParam);
|
||||
query.setUserId(userId);
|
||||
PageResult<UserLatestMobileDeviceHistoryDTO> page = ResponseAssert.requiredSuccess(
|
||||
registerDeviceClient.pageLatestMobileDevice(query));
|
||||
if (CollectionUtils.isEmpty(page.getRecords())) {
|
||||
continue;
|
||||
}
|
||||
UserLatestMobileDeviceHistoryDTO latestDevice = page.getRecords().iterator().next();
|
||||
if (matchesDevice(latestDevice, createRecordParam, true)) {
|
||||
devices.add(latestDevice);
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
private List<UserLatestMobileDeviceHistoryDTO> listBroadcastTargetDevices(
|
||||
CreateRecordCmd createRecordParam) {
|
||||
List<UserLatestMobileDeviceHistoryDTO> devices = new ArrayList<>();
|
||||
PageLatestMobileDeviceQryCmd query = buildDeviceQuery(createRecordParam);
|
||||
PageQuery pageQuery = query.getPageQuery();
|
||||
|
||||
while (true) {
|
||||
PageResult<UserLatestMobileDeviceHistoryDTO> page = ResponseAssert.requiredSuccess(
|
||||
registerDeviceClient.pageLatestMobileDevice(query));
|
||||
Collection<UserLatestMobileDeviceHistoryDTO> records = page.getRecords();
|
||||
if (CollectionUtils.isEmpty(records)) {
|
||||
break;
|
||||
}
|
||||
|
||||
records.stream()
|
||||
.filter(device -> matchesDevice(device, createRecordParam, false))
|
||||
.forEach(devices::add);
|
||||
|
||||
if (records.size() < resolvePageLimit(pageQuery)) {
|
||||
break;
|
||||
}
|
||||
pageQuery.setCursor(pageQuery.getCursor() + 1);
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
private PageLatestMobileDeviceQryCmd buildDeviceQuery(CreateRecordCmd createRecordParam) {
|
||||
PageLatestMobileDeviceQryCmd query = new PageLatestMobileDeviceQryCmd();
|
||||
query.setSysOrigin(createRecordParam.getSysOrigin());
|
||||
PageQuery pageQuery = new PageQuery();
|
||||
pageQuery.setCursor(1);
|
||||
pageQuery.setLimit(DEVICE_PAGE_LIMIT);
|
||||
query.setPageQuery(pageQuery);
|
||||
return query;
|
||||
}
|
||||
|
||||
private List<UserLatestMobileDeviceHistoryDTO> distinctByDeviceId(
|
||||
List<UserLatestMobileDeviceHistoryDTO> devices) {
|
||||
if (CollectionUtils.isEmpty(devices)) {
|
||||
return CollectionUtils.newArrayList();
|
||||
}
|
||||
|
||||
Map<String, UserLatestMobileDeviceHistoryDTO> distinctMap = new LinkedHashMap<>();
|
||||
for (UserLatestMobileDeviceHistoryDTO device : devices) {
|
||||
if (Objects.isNull(device) || StringUtils.isBlank(device.getDeviceId())) {
|
||||
continue;
|
||||
}
|
||||
distinctMap.putIfAbsent(device.getDeviceId(), device);
|
||||
}
|
||||
return new ArrayList<>(distinctMap.values());
|
||||
}
|
||||
|
||||
private boolean matchesDevice(UserLatestMobileDeviceHistoryDTO device,
|
||||
CreateRecordCmd createRecordParam, boolean ignoreLanguage) {
|
||||
if (Objects.isNull(device) || StringUtils.isBlank(device.getDeviceId())) {
|
||||
return false;
|
||||
}
|
||||
if (StringUtils.isNotBlank(createRecordParam.getDeviceType())
|
||||
&& !equalsIgnoreCase(createRecordParam.getDeviceType(), device.getDeviceType())) {
|
||||
return false;
|
||||
}
|
||||
if (StringUtils.isNotBlank(createRecordParam.getPlatform())
|
||||
&& !isSamePlatform(createRecordParam.getPlatform(), device.getRequestClient())) {
|
||||
return false;
|
||||
}
|
||||
return ignoreLanguage || StringUtils.isBlank(createRecordParam.getLanguage())
|
||||
|| equalsIgnoreCase(createRecordParam.getLanguage(), device.getLanguage());
|
||||
}
|
||||
|
||||
private boolean isSuccessStatusQuery(String pushStatus) {
|
||||
String normalized = normalizeStatusQuery(pushStatus);
|
||||
return Objects.equals(normalized, "0") || Objects.equals(normalized,
|
||||
PUSH_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
private boolean isFailStatusQuery(String pushStatus) {
|
||||
String normalized = normalizeStatusQuery(pushStatus);
|
||||
return Objects.equals(normalized, "1")
|
||||
|| (StringUtils.isNotBlank(normalized) && !Objects.equals(normalized,
|
||||
PUSH_STATUS_SUCCESS));
|
||||
}
|
||||
|
||||
private String normalizeStatusQuery(String pushStatus) {
|
||||
if (StringUtils.isBlank(pushStatus)) {
|
||||
return "";
|
||||
}
|
||||
String normalized = pushStatus.trim();
|
||||
if (Objects.equals(normalized, "0")) {
|
||||
return "0";
|
||||
}
|
||||
if (Objects.equals(normalized, "1")) {
|
||||
return "1";
|
||||
}
|
||||
return normalized.toUpperCase();
|
||||
}
|
||||
|
||||
private boolean isSamePlatform(String expected, String actual) {
|
||||
return Objects.equals(normalizePlatform(expected), normalizePlatform(actual));
|
||||
}
|
||||
|
||||
private boolean isIosPlatform(String platform) {
|
||||
return Objects.equals(normalizePlatform(platform), "ios");
|
||||
}
|
||||
|
||||
private String normalizePlatform(String platform) {
|
||||
if (StringUtils.isBlank(platform)) {
|
||||
return "";
|
||||
}
|
||||
String normalized = platform.trim().toLowerCase();
|
||||
if (Objects.equals(normalized, "ios")) {
|
||||
return "ios";
|
||||
}
|
||||
if (Objects.equals(normalized, "android")) {
|
||||
return "android";
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private boolean equalsIgnoreCase(String left, String right) {
|
||||
if (StringUtils.isBlank(left) || StringUtils.isBlank(right)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(left.trim().toUpperCase(), right.trim().toUpperCase());
|
||||
}
|
||||
|
||||
private SysPushRecordLog buildPushLog(CreateRecordCmd createRecordParam) {
|
||||
Timestamp now = TimestampUtils.now();
|
||||
SysPushRecordLog pushLog = new SysPushRecordLog();
|
||||
pushLog.setTitle(createRecordParam.getTitle());
|
||||
pushLog.setContent(createRecordParam.getContent());
|
||||
pushLog.setPlatform(createRecordParam.getPlatform());
|
||||
pushLog.setSysOrigin(createRecordParam.getSysOrigin());
|
||||
pushLog.setBusinessScene(createRecordParam.getBusinessScene());
|
||||
pushLog.setDeviceType(createRecordParam.getDeviceType());
|
||||
pushLog.setFixedUserIds(createRecordParam.getFixedUserIds());
|
||||
pushLog.setLink(createRecordParam.getLink());
|
||||
pushLog.setOrigin(PUSH_ORIGIN_BACK);
|
||||
pushLog.setCreateUser(createRecordParam.getCreateUser());
|
||||
pushLog.setUpdateUser(createRecordParam.getCreateUser());
|
||||
pushLog.setCreateTime(now);
|
||||
pushLog.setUpdateTime(now);
|
||||
return pushLog;
|
||||
}
|
||||
|
||||
private SysPushRecordLogCO toPushLogCO(SysPushRecordLog record) {
|
||||
return toPushLogCO(record, CollectionUtils.newHashMap());
|
||||
}
|
||||
|
||||
private SysPushRecordLogCO toPushLogCO(SysPushRecordLog record, Map<Long, String> sendUserNameMap) {
|
||||
SysPushRecordLogCO result = new SysPushRecordLogCO();
|
||||
result.setId(record.getId());
|
||||
result.setTitle(record.getTitle());
|
||||
result.setContent(record.getContent());
|
||||
result.setSysOrigin(record.getSysOrigin());
|
||||
result.setPlatform(record.getPlatform());
|
||||
result.setBusinessScene(getBusinessSceneDesc(record.getBusinessScene()));
|
||||
result.setFixedUserIds(record.getFixedUserIds());
|
||||
result.setLink(record.getLink());
|
||||
result.setOrigin(record.getOrigin());
|
||||
result.setPushStatus(getPushStatusDesc(record.getPushStatus()));
|
||||
result.setDeviceType(record.getDeviceType());
|
||||
result.setFailQuantity(record.getFailQuantity());
|
||||
result.setSuccessQuantity(record.getSuccessQuantity());
|
||||
result.setSendUserName(sendUserNameMap.get(record.getCreateUser()));
|
||||
result.setCreateTime(record.getCreateTime());
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getBusinessSceneDesc(String businessScene) {
|
||||
String desc = MessageBusinessSceneEnum.getDesc(businessScene);
|
||||
return StringUtils.isBlank(desc) ? businessScene : desc;
|
||||
}
|
||||
|
||||
private String getPushStatusDesc(String pushStatus) {
|
||||
if (Objects.equals(PUSH_STATUS_SUCCESS, pushStatus)) {
|
||||
return "成功";
|
||||
}
|
||||
if (PUSH_FAIL_STATUSES.contains(pushStatus)) {
|
||||
return "失败";
|
||||
}
|
||||
return pushStatus;
|
||||
}
|
||||
|
||||
private PageQuery ensurePageQuery(PageCommand pageCommand) {
|
||||
PageQuery pageQuery = pageCommand.getPageQuery();
|
||||
if (Objects.isNull(pageQuery)) {
|
||||
pageQuery = new PageQuery();
|
||||
pageCommand.setPageQuery(pageQuery);
|
||||
}
|
||||
if (Objects.isNull(pageQuery.getCursor()) || pageQuery.getCursor() < 1) {
|
||||
pageQuery.setCursor(1);
|
||||
}
|
||||
if (Objects.isNull(pageQuery.getLimit()) || pageQuery.getLimit() < 1) {
|
||||
pageQuery.setLimit(DEFAULT_PAGE_LIMIT);
|
||||
}
|
||||
return pageQuery;
|
||||
}
|
||||
|
||||
private int resolvePageLimit(PageQuery pageQuery) {
|
||||
if (Objects.isNull(pageQuery) || Objects.isNull(pageQuery.getLimit())
|
||||
|| pageQuery.getLimit() < 1) {
|
||||
return DEFAULT_PAGE_LIMIT;
|
||||
}
|
||||
return pageQuery.getLimit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
package com.red.circle.console.app.dto.clienobject.sys;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -89,9 +90,14 @@ public class SysPushRecordLogCO implements Serializable {
|
||||
*/
|
||||
private Integer successQuantity;
|
||||
|
||||
/**
|
||||
* 发送用户.
|
||||
*/
|
||||
private String sendUserName;
|
||||
|
||||
}
|
||||
/**
|
||||
* 发送用户.
|
||||
*/
|
||||
private String sendUserName;
|
||||
|
||||
/**
|
||||
* 创建时间.
|
||||
*/
|
||||
private Timestamp createTime;
|
||||
|
||||
}
|
||||
|
||||
@ -1,36 +1,43 @@
|
||||
package com.red.circle.console.app.service.app.sys;
|
||||
|
||||
|
||||
/**
|
||||
* 系统推送服务.
|
||||
*
|
||||
* @author pengliang
|
||||
* @since 2020/3/6 15:53
|
||||
*/
|
||||
public interface SysPushService {
|
||||
/*
|
||||
*//**
|
||||
* 创建推送.
|
||||
*//*
|
||||
void create(CreateRecordCmd createRecordParam);
|
||||
|
||||
*//**
|
||||
* 获取push日志分页列表
|
||||
*//*
|
||||
PageResult<SysPushRecordLogCO> pageLogs(SysPushRecordLogQryCmd query);
|
||||
|
||||
*//**
|
||||
* 定时推送任务分页列表
|
||||
*//*
|
||||
PageResult<SysPushTaskCO> pageTask(SysPushTaskQryCmd query);
|
||||
|
||||
*//**
|
||||
* 保存推送任务
|
||||
*//*
|
||||
void saveTask(SysPushTaskCmd param);
|
||||
|
||||
*//**
|
||||
* 删除任务
|
||||
*//*
|
||||
void deletePushTask(Long id);*/
|
||||
}
|
||||
package com.red.circle.console.app.service.app.sys;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.sys.SysPushRecordLogCO;
|
||||
import com.red.circle.console.app.dto.clienobject.sys.SysPushTaskCO;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.other.inner.model.cmd.sys.CreateRecordCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushRecordLogQryCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushTaskCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysPushTaskQryCmd;
|
||||
|
||||
/**
|
||||
* 系统推送服务.
|
||||
*
|
||||
* @author pengliang
|
||||
* @since 2020/3/6 15:53
|
||||
*/
|
||||
public interface SysPushService {
|
||||
|
||||
/**
|
||||
* 创建推送.
|
||||
*/
|
||||
void create(CreateRecordCmd createRecordParam);
|
||||
|
||||
/**
|
||||
* 获取push日志分页列表.
|
||||
*/
|
||||
PageResult<SysPushRecordLogCO> pageLogs(SysPushRecordLogQryCmd query);
|
||||
|
||||
/**
|
||||
* 定时推送任务分页列表.
|
||||
*/
|
||||
PageResult<SysPushTaskCO> pageTask(SysPushTaskQryCmd query);
|
||||
|
||||
/**
|
||||
* 保存推送任务.
|
||||
*/
|
||||
void saveTask(SysPushTaskCmd param);
|
||||
|
||||
/**
|
||||
* 删除任务.
|
||||
*/
|
||||
void deletePushTask(Long id);
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package com.red.circle.console.infra.database.rds.dao.sys;
|
||||
|
||||
import com.red.circle.console.infra.database.rds.entity.sys.SysPushRecordLog;
|
||||
import com.red.circle.framework.mybatis.dao.BaseDAO;
|
||||
|
||||
/**
|
||||
* Push 日志 DAO.
|
||||
*/
|
||||
public interface SysPushRecordLogDAO extends BaseDAO<SysPushRecordLog> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.red.circle.console.infra.database.rds.entity.sys;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.red.circle.framework.mybatis.entity.TimestampBaseEntity;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* Push 日志.
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("sys_push_record_log")
|
||||
public class SysPushRecordLog extends TimestampBaseEntity implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
@TableField("title")
|
||||
private String title;
|
||||
|
||||
@TableField("content")
|
||||
private String content;
|
||||
|
||||
@TableField("sys_origin")
|
||||
private String sysOrigin;
|
||||
|
||||
@TableField("platform")
|
||||
private String platform;
|
||||
|
||||
@TableField("business_scene")
|
||||
private String businessScene;
|
||||
|
||||
@TableField("fixed_user_ids")
|
||||
private String fixedUserIds;
|
||||
|
||||
@TableField("link")
|
||||
private String link;
|
||||
|
||||
@TableField("origin")
|
||||
private String origin;
|
||||
|
||||
@TableField("push_status")
|
||||
private String pushStatus;
|
||||
|
||||
@TableField("device_type")
|
||||
private String deviceType;
|
||||
|
||||
@TableField("fail_quantity")
|
||||
private Integer failQuantity;
|
||||
|
||||
@TableField("success_quantity")
|
||||
private Integer successQuantity;
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.red.circle.console.infra.database.rds.service.sys;
|
||||
|
||||
import com.red.circle.console.infra.database.rds.entity.sys.SysPushRecordLog;
|
||||
import com.red.circle.framework.mybatis.service.BaseService;
|
||||
|
||||
/**
|
||||
* Push 日志服务.
|
||||
*/
|
||||
public interface SysPushRecordLogService extends BaseService<SysPushRecordLog> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.red.circle.console.infra.database.rds.service.sys.impl;
|
||||
|
||||
import com.red.circle.console.infra.database.rds.dao.sys.SysPushRecordLogDAO;
|
||||
import com.red.circle.console.infra.database.rds.entity.sys.SysPushRecordLog;
|
||||
import com.red.circle.console.infra.database.rds.service.sys.SysPushRecordLogService;
|
||||
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Push 日志服务实现.
|
||||
*/
|
||||
@Service
|
||||
public class SysPushRecordLogServiceImpl extends
|
||||
BaseServiceImpl<SysPushRecordLogDAO, SysPushRecordLog> implements SysPushRecordLogService {
|
||||
|
||||
}
|
||||
@ -1,23 +1,28 @@
|
||||
package com.red.circle;
|
||||
|
||||
import com.red.circle.component.redis.RedisAutoConfiguration;
|
||||
import com.red.circle.framework.cloud.annotation.RedCircleCloudApplication;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
package com.red.circle;
|
||||
|
||||
import com.red.circle.component.redis.RedisAutoConfiguration;
|
||||
import com.red.circle.framework.cloud.annotation.RedCircleCloudApplication;
|
||||
import com.red.circle.live.adapter.app.LiveMicRestController;
|
||||
import com.red.circle.live.adapter.app.LiveRoomRestController;
|
||||
import com.red.circle.live.adapter.app.LiveUserRestController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* @author pengliang on 2023/12/7
|
||||
*/
|
||||
@Slf4j
|
||||
@EnableScheduling
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
@SpringBootApplication(exclude = RedisAutoConfiguration.class)
|
||||
public class LiveServiceApplication {
|
||||
@EnableScheduling
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
@SpringBootApplication(exclude = RedisAutoConfiguration.class)
|
||||
@Import({LiveMicRestController.class, LiveRoomRestController.class, LiveUserRestController.class})
|
||||
public class LiveServiceApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
log.info("Begin to start Spring Boot Application");
|
||||
|
||||
@ -126,11 +126,12 @@ public class GameLuckyGiftCommon {
|
||||
private final RankingActivityService rankingActivityService;
|
||||
private final GiftMqMessage giftMqMessage;
|
||||
private final GiftAppConvertor giftAppConvertor;
|
||||
private final ActivityRechargeTicketService activityRechargeTicketService;
|
||||
private final RoomDailyTaskProgressService roomDailyTaskProgressService;
|
||||
private final RoomMemberService roomMemberService;
|
||||
private final RedisService redisService;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
private final ActivityRechargeTicketService activityRechargeTicketService;
|
||||
private final RoomDailyTaskProgressService roomDailyTaskProgressService;
|
||||
private final RoomMemberService roomMemberService;
|
||||
private final RedisService redisService;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
private final LuckyGiftSendMetricsService luckyGiftSendMetricsService;
|
||||
|
||||
/**
|
||||
* 发送幸运礼物抽奖mq.
|
||||
@ -290,26 +291,35 @@ public class GameLuckyGiftCommon {
|
||||
|
||||
Integer totalAwardAmount = apiResult.getOrderResults().stream().mapToInt(OrderResult::getRewardNum).sum();
|
||||
|
||||
// 保存幸运礼物流水
|
||||
Long rewardAmount = Long.valueOf(String.valueOf(totalAwardAmount));
|
||||
saveLuckGiftCount(param, rewardAmount);
|
||||
|
||||
if (totalAwardAmount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送中奖金币
|
||||
BigDecimal balance = sendGold(param.getUserId(), param.getGiftId(), param.getSysOrigin(), rewardAmount);
|
||||
|
||||
// 发送中奖通知
|
||||
int multiple = getRewardMultiple(param, rewardAmount);
|
||||
sendMsg(param, multiple, balance, rewardAmount);
|
||||
|
||||
// 魔法礼物中奖后处理
|
||||
GiftConfigDTO giftConfigDTO = getGiftConfigDTO(cmd.getGiftId());
|
||||
if (GiftTabEnum.MAGIC.name().equals(giftConfigDTO.getGiftTab())) {
|
||||
String timeId = IdWorker.getIdStr();
|
||||
GiveAwayGiftBatchEvent giftBatchEvent = buildGiftBatchEvent(cmd, timeId, param, giftConfigDTO);
|
||||
GiftConfigDTO giftConfigDTO = getGiftConfigDTO(cmd.getGiftId());
|
||||
luckyGiftSendMetricsService.settle(
|
||||
"legacy:" + cmd.getId(),
|
||||
cmd.getRoomId(),
|
||||
cmd.getUserId(),
|
||||
giftConfigDTO.getGiftCandy(),
|
||||
cmd.getQuantity(),
|
||||
Objects.isNull(cmd.getAcceptUserId()) ? Collections.emptyList() : List.of(cmd.getAcceptUserId())
|
||||
);
|
||||
|
||||
// 保存幸运礼物流水
|
||||
Long rewardAmount = Long.valueOf(String.valueOf(totalAwardAmount));
|
||||
saveLuckGiftCount(param, rewardAmount);
|
||||
|
||||
if (totalAwardAmount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送中奖金币
|
||||
BigDecimal balance = sendGold(param.getUserId(), param.getGiftId(), param.getSysOrigin(), rewardAmount);
|
||||
|
||||
// 发送中奖通知
|
||||
int multiple = getRewardMultiple(param, rewardAmount);
|
||||
sendMsg(param, multiple, balance, rewardAmount);
|
||||
|
||||
// 魔法礼物中奖后处理
|
||||
if (GiftTabEnum.MAGIC.name().equals(giftConfigDTO.getGiftTab())) {
|
||||
String timeId = IdWorker.getIdStr();
|
||||
GiveAwayGiftBatchEvent giftBatchEvent = buildGiftBatchEvent(cmd, timeId, param, giftConfigDTO);
|
||||
|
||||
giftMqMessage.sendGift(timeId, giftBatchEvent);
|
||||
|
||||
|
||||
@ -58,6 +58,7 @@ public class LuckyGiftResultProcessor {
|
||||
private final RedisService redisService;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
private final GameLuckyGiftCommon gameLuckyGiftCommon;
|
||||
private final LuckyGiftSendMetricsService luckyGiftSendMetricsService;
|
||||
|
||||
public void process(GameLuckyGiftBusinessEvent event, LuckyGiftGoClient.LuckyGiftGoDrawResponse response) {
|
||||
GiftConfigDTO giftConfig = giftCacheService.getById(event.getGiftId());
|
||||
@ -85,6 +86,18 @@ public class LuckyGiftResultProcessor {
|
||||
long payAmount = resolvePayAmount(event, giftUnitPrice);
|
||||
BigDecimal balanceAfter = BigDecimal.valueOf(Objects.requireNonNullElse(response.getBalanceAfter(), 0L));
|
||||
|
||||
luckyGiftSendMetricsService.settle(
|
||||
event.getBusinessId(),
|
||||
event.getRoomId(),
|
||||
event.getUserId(),
|
||||
giftUnitPrice,
|
||||
event.getQuantity(),
|
||||
Optional.ofNullable(event.getUsers()).orElse(Collections.emptyList()).stream()
|
||||
.map(GameLuckyGiftUser::getAcceptUserId)
|
||||
.filter(Objects::nonNull)
|
||||
.toList()
|
||||
);
|
||||
|
||||
for (LuckyGiftGoClient.LuckyGiftGoDrawResult result : response.getResults()) {
|
||||
GameLuckyGiftUser user = userMap.get(result.getId());
|
||||
if (user == null) {
|
||||
|
||||
@ -0,0 +1,149 @@
|
||||
package com.red.circle.other.app.common.gift;
|
||||
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.other.domain.enums.VipBenefitType;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
|
||||
import com.red.circle.other.domain.model.user.ability.RegionConfig;
|
||||
import com.red.circle.other.infra.common.team.TeamTargetCommon;
|
||||
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
|
||||
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
|
||||
import com.red.circle.other.infra.database.rds.service.user.user.ConsumptionLevelService;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import com.red.circle.tool.core.num.ArithmeticUtils;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 幸运礼物送出后的统计补偿.
|
||||
* 只补房间流水、魅力值、主播收礼积分,固定按 10% 结算。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LuckyGiftSendMetricsService {
|
||||
|
||||
private static final String PROCESSED_KEY_PREFIX = "lucky_gift:send:metrics:processed:";
|
||||
private static final BigDecimal LUCKY_GIFT_METRIC_RATIO = new BigDecimal("0.1");
|
||||
|
||||
private final RedisService redisService;
|
||||
private final TeamTargetCommon teamTargetCommon;
|
||||
private final TeamMemberService teamMemberService;
|
||||
private final UserRegionGateway userRegionGateway;
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
private final ConsumptionLevelService consumptionLevelService;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
|
||||
public void settle(
|
||||
String idempotencyKey,
|
||||
Long roomId,
|
||||
Long sendUserId,
|
||||
BigDecimal giftUnitPrice,
|
||||
Integer quantity,
|
||||
List<Long> acceptUserIds
|
||||
) {
|
||||
if (StringUtils.isBlank(idempotencyKey) || sendUserId == null || giftUnitPrice == null
|
||||
|| quantity == null || quantity <= 0 || CollectionUtils.isEmpty(acceptUserIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!redisService.setIfAbsent(PROCESSED_KEY_PREFIX + idempotencyKey, "1", 7,
|
||||
TimeUnit.DAYS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal singleGiftAmount = giftUnitPrice.multiply(BigDecimal.valueOf(quantity));
|
||||
BigDecimal singleMetricAmount = calculateMetricAmount(singleGiftAmount);
|
||||
if (ArithmeticUtils.lteZero(singleMetricAmount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
settleRoomValue(roomId, sendUserId, singleGiftAmount, acceptUserIds.size());
|
||||
settleCharm(acceptUserIds, singleMetricAmount);
|
||||
settleHostPoints(sendUserId, acceptUserIds, singleMetricAmount);
|
||||
}
|
||||
|
||||
private void settleRoomValue(Long roomId, Long sendUserId, BigDecimal singleGiftAmount,
|
||||
int acceptSize) {
|
||||
if (roomId == null || acceptSize <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
RoomProfile roomProfile = roomProfileManagerService.getProfileById(roomId);
|
||||
if (roomProfile == null || !Objects.equals(roomProfile.getUserId(), sendUserId)
|
||||
|| teamMemberService.getByMemberId(sendUserId) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal roomMetricAmount = calculateMetricAmount(
|
||||
singleGiftAmount.multiply(BigDecimal.valueOf(acceptSize)));
|
||||
if (ArithmeticUtils.lteZero(roomMetricAmount)) {
|
||||
return;
|
||||
}
|
||||
teamTargetCommon.incrementRoomValue(sendUserId, roomMetricAmount);
|
||||
}
|
||||
|
||||
private void settleCharm(List<Long> acceptUserIds, BigDecimal singleMetricAmount) {
|
||||
for (Long acceptUserId : acceptUserIds) {
|
||||
if (acceptUserId == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BigDecimal vipBenefit = Objects.requireNonNullElse(
|
||||
userProfileGateway.getUserVipBenefit(acceptUserId, VipBenefitType.XP_RATE),
|
||||
VipBenefitType.XP_RATE.getDefaultValue()
|
||||
);
|
||||
BigDecimal charmAmount = singleMetricAmount.multiply(vipBenefit)
|
||||
.setScale(0, RoundingMode.DOWN);
|
||||
if (ArithmeticUtils.lteZero(charmAmount)) {
|
||||
continue;
|
||||
}
|
||||
consumptionLevelService.incrConsumptionDiamond(acceptUserId, charmAmount);
|
||||
}
|
||||
}
|
||||
|
||||
private void settleHostPoints(Long sendUserId, List<Long> acceptUserIds,
|
||||
BigDecimal singleMetricAmount) {
|
||||
RegionConfig sendRegion = userRegionGateway.getRegionConfigByUserId(sendUserId);
|
||||
if (sendRegion == null || StringUtils.isBlank(sendRegion.getRegionCode())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Long> anchorUserIds = new HashSet<>(
|
||||
teamMemberService.listMemberIds(new HashSet<>(acceptUserIds)));
|
||||
for (Long acceptUserId : acceptUserIds) {
|
||||
if (acceptUserId == null || !anchorUserIds.contains(acceptUserId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RegionConfig acceptRegion = userRegionGateway.getRegionConfigByUserId(acceptUserId);
|
||||
if (acceptRegion == null || StringUtils.isBlank(acceptRegion.getRegionCode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!userRegionGateway.checkBusinessRegionCode(sendRegion.getRegionCode(),
|
||||
acceptRegion.getRegionCode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
teamTargetCommon.incrementGiftValue(acceptUserId, singleMetricAmount);
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal calculateMetricAmount(BigDecimal amount) {
|
||||
if (ArithmeticUtils.lteZero(amount)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return amount.multiply(LUCKY_GIFT_METRIC_RATIO).setScale(0, RoundingMode.DOWN);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,205 @@
|
||||
package com.red.circle.other.app.common.gift;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
|
||||
import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
|
||||
import com.red.circle.mq.rocket.business.service.MessageSenderService;
|
||||
import com.red.circle.other.app.convertor.material.GiftAppConvertor;
|
||||
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
|
||||
import com.red.circle.other.app.service.activity.ActivityRechargeTicketService;
|
||||
import com.red.circle.other.app.service.activity.RankingActivityService;
|
||||
import com.red.circle.other.app.service.game.BaishunRuntimeConfigResolver;
|
||||
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.model.user.UserProfile;
|
||||
import com.red.circle.other.infra.config.LuckyGiftApiConfig;
|
||||
import com.red.circle.other.infra.database.cache.entity.game.luckgift.GameLuckyGiftInventoryCache;
|
||||
import com.red.circle.other.infra.database.cache.entity.game.luckgift.LuckyGiftInventoryTemplateCache;
|
||||
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
|
||||
import com.red.circle.other.infra.database.cache.service.other.GameLuckyGiftCacheService;
|
||||
import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
|
||||
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
|
||||
import com.red.circle.other.infra.database.rds.entity.game.GameLuckyGiftCount;
|
||||
import com.red.circle.other.infra.database.rds.service.game.GameLuckyGiftCountService;
|
||||
import com.red.circle.other.infra.database.rds.service.game.GameLuckyGiftRuleConfigService;
|
||||
import com.red.circle.other.infra.database.rds.service.game.LuckyGiftProbabilityDetailsService;
|
||||
import com.red.circle.other.infra.database.rds.service.game.LuckyGiftProbabilityService;
|
||||
import com.red.circle.other.infra.database.rds.service.live.RoomMemberService;
|
||||
import com.red.circle.other.infra.database.rds.service.sys.EnumConfigService;
|
||||
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
class GameLuckyGiftCommonTest {
|
||||
|
||||
@Test
|
||||
void run_shouldSettleLegacyMetricsWhenAwardIsZero() {
|
||||
TestFixture fixture = new TestFixture();
|
||||
fixture.mockGiftAndSender();
|
||||
|
||||
GameLuckyGiftParamCmd cmd = fixture.newCmd().setAcceptUserId(3001L);
|
||||
|
||||
fixture.common.run(cmd);
|
||||
|
||||
verify(fixture.luckyGiftSendMetricsService).settle(
|
||||
"legacy:9001",
|
||||
1001L,
|
||||
2001L,
|
||||
BigDecimal.valueOf(100),
|
||||
2,
|
||||
List.of(3001L)
|
||||
);
|
||||
|
||||
ArgumentCaptor<GameLuckyGiftCount> countCaptor = ArgumentCaptor.forClass(GameLuckyGiftCount.class);
|
||||
verify(fixture.gameLuckyGiftCountService).save(countCaptor.capture());
|
||||
assertEquals(Long.valueOf(0), countCaptor.getValue().getAwardAmount());
|
||||
assertEquals(Long.valueOf(200), countCaptor.getValue().getPayAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_shouldHandleNullAcceptUserIdWhenAwardIsZero() {
|
||||
TestFixture fixture = new TestFixture();
|
||||
fixture.mockGiftAndSender();
|
||||
|
||||
GameLuckyGiftParamCmd cmd = fixture.newCmd().setAcceptUserId(null);
|
||||
|
||||
assertDoesNotThrow(() -> fixture.common.run(cmd));
|
||||
verify(fixture.luckyGiftSendMetricsService).settle(
|
||||
"legacy:9001",
|
||||
1001L,
|
||||
2001L,
|
||||
BigDecimal.valueOf(100),
|
||||
2,
|
||||
Collections.emptyList()
|
||||
);
|
||||
}
|
||||
|
||||
private static final class TestFixture {
|
||||
|
||||
private final MessageSenderService senderService = mock(MessageSenderService.class);
|
||||
private final ImGroupClient imGroupClient = mock(ImGroupClient.class);
|
||||
private final WalletGoldClient walletGoldClient = mock(WalletGoldClient.class);
|
||||
private final GiftCacheService giftCacheService = mock(GiftCacheService.class);
|
||||
private final EnumConfigService enumConfigService = mock(EnumConfigService.class);
|
||||
private final UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
|
||||
private final EnumConfigCacheService enumConfigCacheService = mock(EnumConfigCacheService.class);
|
||||
private final UserProfileAppConvertor userProfileAppConvertor = mock(UserProfileAppConvertor.class);
|
||||
private final GameLuckyGiftCountService gameLuckyGiftCountService = mock(GameLuckyGiftCountService.class);
|
||||
private final GameLuckyGiftCacheService gameLuckyGiftCacheService = mock(GameLuckyGiftCacheService.class);
|
||||
private final LuckyGiftProbabilityService luckyGiftProbabilityService = mock(
|
||||
LuckyGiftProbabilityService.class);
|
||||
private final GameLuckyGiftRuleConfigService gameLuckyGiftRuleConfigService = mock(
|
||||
GameLuckyGiftRuleConfigService.class);
|
||||
private final LuckyGiftProbabilityDetailsService luckyGiftProbabilityDetailsService = mock(
|
||||
LuckyGiftProbabilityDetailsService.class);
|
||||
private final LuckyGiftApiConfig luckyGiftApiConfig = mock(LuckyGiftApiConfig.class);
|
||||
private final BaishunRuntimeConfigResolver baishunRuntimeConfigResolver = mock(
|
||||
BaishunRuntimeConfigResolver.class);
|
||||
private final RankingActivityService rankingActivityService = mock(RankingActivityService.class);
|
||||
private final GiftMqMessage giftMqMessage = mock(GiftMqMessage.class);
|
||||
private final GiftAppConvertor giftAppConvertor = mock(GiftAppConvertor.class);
|
||||
private final ActivityRechargeTicketService activityRechargeTicketService = mock(
|
||||
ActivityRechargeTicketService.class);
|
||||
private final RoomDailyTaskProgressService roomDailyTaskProgressService = mock(
|
||||
RoomDailyTaskProgressService.class);
|
||||
private final RoomMemberService roomMemberService = mock(RoomMemberService.class);
|
||||
private final RedisService redisService = mock(RedisService.class);
|
||||
private final RoomProfileManagerService roomProfileManagerService = mock(
|
||||
RoomProfileManagerService.class);
|
||||
private final LuckyGiftSendMetricsService luckyGiftSendMetricsService = mock(
|
||||
LuckyGiftSendMetricsService.class);
|
||||
|
||||
private final GameLuckyGiftCommon common = new GameLuckyGiftCommon(
|
||||
senderService,
|
||||
imGroupClient,
|
||||
walletGoldClient,
|
||||
giftCacheService,
|
||||
enumConfigService,
|
||||
userProfileGateway,
|
||||
enumConfigCacheService,
|
||||
userProfileAppConvertor,
|
||||
gameLuckyGiftCountService,
|
||||
gameLuckyGiftCacheService,
|
||||
luckyGiftProbabilityService,
|
||||
gameLuckyGiftRuleConfigService,
|
||||
luckyGiftProbabilityDetailsService,
|
||||
luckyGiftApiConfig,
|
||||
baishunRuntimeConfigResolver,
|
||||
rankingActivityService,
|
||||
giftMqMessage,
|
||||
giftAppConvertor,
|
||||
activityRechargeTicketService,
|
||||
roomDailyTaskProgressService,
|
||||
roomMemberService,
|
||||
redisService,
|
||||
roomProfileManagerService,
|
||||
luckyGiftSendMetricsService
|
||||
);
|
||||
|
||||
private void mockGiftAndSender() {
|
||||
GiftConfigDTO giftConfig = new GiftConfigDTO()
|
||||
.setId(11L)
|
||||
.setStandardId(99L)
|
||||
.setGiftCandy(BigDecimal.valueOf(100))
|
||||
.setGiftPhoto("gift.png");
|
||||
when(giftCacheService.getById(11L)).thenReturn(giftConfig);
|
||||
|
||||
LuckyGiftInventoryTemplateCache template = new LuckyGiftInventoryTemplateCache()
|
||||
.setGiftGiveTotal(0)
|
||||
.setComboLoss(0)
|
||||
.setCaches(List.of(new GameLuckyGiftInventoryCache().setMultiple(1).setQuantity(1)));
|
||||
when(gameLuckyGiftCacheService.getInventoryTemplate(99L, 2)).thenReturn(template);
|
||||
|
||||
UserProfile senderProfile = new UserProfile();
|
||||
senderProfile.setId(2001L);
|
||||
senderProfile.setAccount("sender001");
|
||||
when(userProfileGateway.getByUserId(2001L)).thenReturn(senderProfile);
|
||||
|
||||
UserProfileDTO senderProfileDTO = new UserProfileDTO();
|
||||
senderProfileDTO.setId(2001L);
|
||||
senderProfileDTO.setAccount("sender001");
|
||||
senderProfileDTO.setUserNickname("sender");
|
||||
senderProfileDTO.setUserAvatar("sender.png");
|
||||
when(userProfileAppConvertor.toUserProfileDTO(senderProfile)).thenReturn(senderProfileDTO);
|
||||
|
||||
LuckyGiftApiConfig.ApiAccount apiAccount = new LuckyGiftApiConfig.ApiAccount();
|
||||
apiAccount.setUrl("http://127.0.0.1:1");
|
||||
apiAccount.setAppId("test-app");
|
||||
apiAccount.setAppChannel("test-channel");
|
||||
apiAccount.setAppKey("test-key");
|
||||
when(baishunRuntimeConfigResolver.resolveLuckyGiftApiAccount("LIKEI", 99L))
|
||||
.thenReturn(apiAccount);
|
||||
}
|
||||
|
||||
private GameLuckyGiftParamCmd newCmd() {
|
||||
return new GameLuckyGiftParamCmd()
|
||||
.setId(9001L)
|
||||
.setUserId(2001L)
|
||||
.setRoomId(1001L)
|
||||
.setRoomAccount("room-account")
|
||||
.setSysOrigin("LIKEI")
|
||||
.setGiftId(11L)
|
||||
.setQuantity(2)
|
||||
.setGiftCombos(0)
|
||||
.setRegionCode("SA")
|
||||
.setGiftsCount(0)
|
||||
.setComboLossCount(0)
|
||||
.setOpenComboLoss(false)
|
||||
.setSysComboLoss(0)
|
||||
.setOpenGiftGiveTotal(false)
|
||||
.setSysGiftGiveTotal(0)
|
||||
.setStandardId(99L);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,7 @@ class LuckyGiftResultProcessorTest {
|
||||
RedisService redisService = mock(RedisService.class);
|
||||
RoomProfileManagerService roomProfileManagerService = mock(RoomProfileManagerService.class);
|
||||
GameLuckyGiftCommon gameLuckyGiftCommon = mock(GameLuckyGiftCommon.class);
|
||||
LuckyGiftSendMetricsService luckyGiftSendMetricsService = mock(LuckyGiftSendMetricsService.class);
|
||||
LuckyGiftResultProcessor processor = new LuckyGiftResultProcessor(
|
||||
gameLuckyGiftCountService,
|
||||
giftCacheService,
|
||||
@ -51,7 +52,8 @@ class LuckyGiftResultProcessorTest {
|
||||
roomMemberService,
|
||||
redisService,
|
||||
roomProfileManagerService,
|
||||
gameLuckyGiftCommon
|
||||
gameLuckyGiftCommon,
|
||||
luckyGiftSendMetricsService
|
||||
);
|
||||
|
||||
GiftConfigDTO giftConfig = new GiftConfigDTO()
|
||||
@ -110,6 +112,15 @@ class LuckyGiftResultProcessorTest {
|
||||
|
||||
processor.process(event, response);
|
||||
|
||||
verify(luckyGiftSendMetricsService).settle(
|
||||
"biz-001",
|
||||
3003L,
|
||||
1001L,
|
||||
BigDecimal.valueOf(173),
|
||||
4,
|
||||
List.of(2002L)
|
||||
);
|
||||
|
||||
ArgumentCaptor<GameLuckyGiftCount> countCaptor = ArgumentCaptor.forClass(GameLuckyGiftCount.class);
|
||||
verify(gameLuckyGiftCountService).saveOrUpdate(countCaptor.capture());
|
||||
GameLuckyGiftCount saved = countCaptor.getValue();
|
||||
|
||||
@ -0,0 +1,127 @@
|
||||
package com.red.circle.other.app.common.gift;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.other.domain.enums.VipBenefitType;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
|
||||
import com.red.circle.other.domain.model.user.ability.RegionConfig;
|
||||
import com.red.circle.other.infra.common.team.TeamTargetCommon;
|
||||
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
|
||||
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
|
||||
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
|
||||
import com.red.circle.other.infra.database.rds.service.user.user.ConsumptionLevelService;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class LuckyGiftSendMetricsServiceTest {
|
||||
|
||||
private static final long ROOM_ID = 1001L;
|
||||
private static final long SEND_USER_ID = 2001L;
|
||||
private static final long ACCEPT_ANCHOR_ID = 3001L;
|
||||
private static final long ACCEPT_OTHER_ANCHOR_ID = 3002L;
|
||||
|
||||
private RedisService redisService;
|
||||
private TeamTargetCommon teamTargetCommon;
|
||||
private TeamMemberService teamMemberService;
|
||||
private UserRegionGateway userRegionGateway;
|
||||
private UserProfileGateway userProfileGateway;
|
||||
private ConsumptionLevelService consumptionLevelService;
|
||||
private RoomProfileManagerService roomProfileManagerService;
|
||||
private LuckyGiftSendMetricsService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
redisService = mock(RedisService.class);
|
||||
teamTargetCommon = mock(TeamTargetCommon.class);
|
||||
teamMemberService = mock(TeamMemberService.class);
|
||||
userRegionGateway = mock(UserRegionGateway.class);
|
||||
userProfileGateway = mock(UserProfileGateway.class);
|
||||
consumptionLevelService = mock(ConsumptionLevelService.class);
|
||||
roomProfileManagerService = mock(RoomProfileManagerService.class);
|
||||
service = new LuckyGiftSendMetricsService(
|
||||
redisService,
|
||||
teamTargetCommon,
|
||||
teamMemberService,
|
||||
userRegionGateway,
|
||||
userProfileGateway,
|
||||
consumptionLevelService,
|
||||
roomProfileManagerService
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void settle_shouldAddRoomValueCharmAndHostPointsWithTenPercent() {
|
||||
when(redisService.setIfAbsent("lucky_gift:send:metrics:processed:biz-1", "1", 7,
|
||||
TimeUnit.DAYS)).thenReturn(true);
|
||||
|
||||
RoomProfile roomProfile = new RoomProfile();
|
||||
roomProfile.setId(ROOM_ID);
|
||||
roomProfile.setUserId(SEND_USER_ID);
|
||||
when(roomProfileManagerService.getProfileById(ROOM_ID)).thenReturn(roomProfile);
|
||||
when(teamMemberService.getByMemberId(SEND_USER_ID)).thenReturn(new TeamMember());
|
||||
when(teamMemberService.listMemberIds(Set.of(ACCEPT_ANCHOR_ID, ACCEPT_OTHER_ANCHOR_ID)))
|
||||
.thenReturn(List.of(ACCEPT_ANCHOR_ID, ACCEPT_OTHER_ANCHOR_ID));
|
||||
|
||||
when(userProfileGateway.getUserVipBenefit(ACCEPT_ANCHOR_ID, VipBenefitType.XP_RATE))
|
||||
.thenReturn(new BigDecimal("1.25"));
|
||||
when(userProfileGateway.getUserVipBenefit(ACCEPT_OTHER_ANCHOR_ID, VipBenefitType.XP_RATE))
|
||||
.thenReturn(BigDecimal.ONE);
|
||||
|
||||
RegionConfig sendRegion = new RegionConfig().setRegionCode("SA");
|
||||
RegionConfig sameRegion = new RegionConfig().setRegionCode("SA");
|
||||
RegionConfig diffRegion = new RegionConfig().setRegionCode("PK");
|
||||
when(userRegionGateway.getRegionConfigByUserId(SEND_USER_ID)).thenReturn(sendRegion);
|
||||
when(userRegionGateway.getRegionConfigByUserId(ACCEPT_ANCHOR_ID)).thenReturn(sameRegion);
|
||||
when(userRegionGateway.getRegionConfigByUserId(ACCEPT_OTHER_ANCHOR_ID)).thenReturn(diffRegion);
|
||||
when(userRegionGateway.checkBusinessRegionCode("SA", "SA")).thenReturn(true);
|
||||
when(userRegionGateway.checkBusinessRegionCode("SA", "PK")).thenReturn(false);
|
||||
|
||||
service.settle(
|
||||
"biz-1",
|
||||
ROOM_ID,
|
||||
SEND_USER_ID,
|
||||
BigDecimal.valueOf(100),
|
||||
2,
|
||||
List.of(ACCEPT_ANCHOR_ID, ACCEPT_OTHER_ANCHOR_ID)
|
||||
);
|
||||
|
||||
verify(teamTargetCommon).incrementRoomValue(SEND_USER_ID, BigDecimal.valueOf(40));
|
||||
verify(consumptionLevelService).incrConsumptionDiamond(ACCEPT_ANCHOR_ID,
|
||||
BigDecimal.valueOf(25));
|
||||
verify(consumptionLevelService).incrConsumptionDiamond(ACCEPT_OTHER_ANCHOR_ID,
|
||||
BigDecimal.valueOf(20));
|
||||
verify(teamTargetCommon).incrementGiftValue(ACCEPT_ANCHOR_ID, BigDecimal.valueOf(20));
|
||||
verify(teamTargetCommon, never()).incrementGiftValue(ACCEPT_OTHER_ANCHOR_ID,
|
||||
BigDecimal.valueOf(20));
|
||||
}
|
||||
|
||||
@Test
|
||||
void settle_shouldSkipWhenAlreadyProcessed() {
|
||||
when(redisService.setIfAbsent("lucky_gift:send:metrics:processed:biz-2", "1", 7,
|
||||
TimeUnit.DAYS)).thenReturn(false);
|
||||
|
||||
service.settle(
|
||||
"biz-2",
|
||||
ROOM_ID,
|
||||
SEND_USER_ID,
|
||||
BigDecimal.valueOf(100),
|
||||
1,
|
||||
List.of(ACCEPT_ANCHOR_ID)
|
||||
);
|
||||
|
||||
verify(teamTargetCommon, never()).incrementRoomValue(SEND_USER_ID, BigDecimal.TEN);
|
||||
verify(consumptionLevelService, never()).incrConsumptionDiamond(ACCEPT_ANCHOR_ID,
|
||||
BigDecimal.TEN);
|
||||
verify(teamTargetCommon, never()).incrementGiftValue(ACCEPT_ANCHOR_ID, BigDecimal.TEN);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user