diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskQueryCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskQueryCmdExe.java index fd415f29..39164463 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskQueryCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskQueryCmdExe.java @@ -3,10 +3,12 @@ package com.red.circle.other.app.command.admintask; import com.red.circle.other.domain.admintask.AdminTaskRecord; import com.red.circle.other.domain.gateway.AdminTaskGateway; import com.red.circle.other.app.dto.cmd.AdminTaskQueryCmd; +import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.List; /** @@ -22,21 +24,18 @@ public class AdminTaskQueryCmdExe { private final AdminTaskGateway adminTaskGateway; public List execute(AdminTaskQueryCmd cmd) { - LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() : getCurrentCycleStartDate(); + LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() : + LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd")); return adminTaskGateway.findByUserIdAndCycle(cmd.getUserId(), cycleStartDate); } /** - * 获取当前周期开始日期 + * 查询单个任务 */ - private LocalDate getCurrentCycleStartDate() { - LocalDate now = LocalDate.now(); - int dayOfMonth = now.getDayOfMonth(); - - if (dayOfMonth <= 15) { - return LocalDate.of(now.getYear(), now.getMonth(), 1); - } else { - return LocalDate.of(now.getYear(), now.getMonth(), 16); - } + public AdminTaskRecord findByUserIdAndTaskTypeAndCycle(Long userId, String taskType, LocalDate cycleStartDate) { + LocalDate queryDate = cycleStartDate != null ? cycleStartDate : + LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd")); + return adminTaskGateway.findByUserIdAndTaskTypeAndCycle(userId, taskType, queryDate); } + } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/task/TaskListener.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/task/TaskListener.java index b32fc673..cc44fdbd 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/task/TaskListener.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/task/TaskListener.java @@ -1,9 +1,6 @@ package com.red.circle.other.app.listener.task; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.google.common.base.Throwables; -import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; -import com.red.circle.common.business.enums.SendPropsOrigin; import com.red.circle.component.mq.MessageEventProcess; import com.red.circle.component.mq.MessageEventProcessDescribe; import com.red.circle.component.mq.config.RocketMqMessageListener; @@ -11,47 +8,23 @@ import com.red.circle.component.mq.service.Action; import com.red.circle.component.mq.service.ConsumerMessage; import com.red.circle.component.mq.service.MessageListener; import com.red.circle.component.redis.service.RedisService; -import com.red.circle.external.inner.endpoint.message.ImGroupClient; import com.red.circle.mq.business.model.event.task.TaskApprovalEvent; import com.red.circle.mq.rocket.business.streams.TaskSink; +import com.red.circle.other.app.dto.cmd.AdminTaskProgressUpdateCmd; import com.red.circle.other.app.dto.cmd.SpinsTaskProgressUpdateCmd; +import com.red.circle.other.app.service.AdminTaskService; import com.red.circle.other.app.service.SpinsUserTaskProgressService; -import com.red.circle.other.app.service.room.RoomProfileService; import com.red.circle.other.app.service.task.TaskService; import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils; -import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.admintask.AdminTaskType; import com.red.circle.other.domain.gateway.user.ability.InviteUserGateway; -import com.red.circle.other.domain.model.user.UserConsumptionLevel; -import com.red.circle.other.infra.database.rds.dao.h5.UserInviteTaskRecordDAO; -import com.red.circle.other.infra.database.rds.dao.user.user.UserInviteUserDAO; -import com.red.circle.other.infra.database.rds.entity.h5.UserInviteTaskRecord; -import com.red.circle.other.infra.database.rds.entity.user.user.LatestMobileDevice; -import com.red.circle.other.infra.database.rds.entity.user.user.UserInviteUser; -import com.red.circle.other.infra.database.rds.enums.h5.MemberActiveEnum; -import com.red.circle.other.infra.database.rds.service.family.FamilyMemberInfoService; -import com.red.circle.other.infra.database.rds.service.sys.EnumConfigService; -import com.red.circle.other.infra.database.rds.service.sys.GameListConfigService; -import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService; -import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.RelationshipFriendService; -import com.red.circle.other.inner.endpoint.activity.PropsActivityClient; -import com.red.circle.other.inner.model.cmd.activity.SendActivityRewardCmd; -import com.red.circle.tool.core.collection.CollectionUtils; -import com.red.circle.tool.core.date.LocalDateUtils; -import com.red.circle.tool.core.date.TimestampUtils; -import com.red.circle.tool.core.sequence.IdWorkerUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.math.BigDecimal; -import java.sql.Timestamp; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; @Slf4j @RocketMqMessageListener(groupId = TaskSink.INPUT, tag = TaskSink.TAG) @@ -63,6 +36,7 @@ public class TaskListener implements MessageListener { private final RelationshipFriendService relationshipFriendService; private final InviteUserGateway inviteUserGateway; private final SpinsUserTaskProgressService spinsUserTaskProgressService; + private final AdminTaskService adminTaskService; @Override public Action consume(ConsumerMessage message) { @@ -151,6 +125,16 @@ public class TaskListener implements MessageListener { // 处理 Spins 上麦任务进度 handleSpinsMicTask(eventBody.getUserId(), inc); + + // 处理管理员麦克风使用时长任务(每次心跳 = 1分钟,累积60次 = 1小时) + if (StringUtils.isNotEmpty(inc)) { + int micMinutes = Integer.parseInt(inc); + // 每60分钟算一次更新 + if (micMinutes % 60 == 0) { + int micHours = micMinutes / 60; + handleAdminMicUsageTask(eventBody.getUserId(), 1); + } + } } /** @@ -375,4 +359,99 @@ public class TaskListener implements MessageListener { updateTaskStatus(eventBody); } + + // ==================== 管理员任务处理 ==================== + + /** + * 处理管理员任务 - 麦克风使用时长 + */ + private void handleAdminMicUsageTask(Long userId, int micHours) { + try { + String MIC_TYPE = AdminTaskType.MIC_USAGE.name(); + if (adminTaskService.isTaskCompleted(userId, MIC_TYPE, null)) { + log.info("管理员麦克风使用时长任务已完成,跳过更新, userId={}", userId); + return; + } + + // 更新任务进度 + AdminTaskProgressUpdateCmd updateCmd = new AdminTaskProgressUpdateCmd(); + updateCmd.setUserId(userId); + updateCmd.setTaskType(MIC_TYPE); + updateCmd.setIncrementValue(micHours); + adminTaskService.updateTaskProgress(updateCmd); + log.info("管理员麦克风使用时长任务进度更新成功, userId={}, micHours={}", userId, micHours); + } catch (Exception e) { + log.error("处理管理员麦克风使用时长任务失败, userId={}, micHours={}", userId, micHours, e); + } + } + + /** + * 处理管理员任务 - 新绑定 BD Leader + */ + private void handleAdminNewBdLeaderTask(Long userId) { + try { + String BD_LEADER_TYPE = AdminTaskType.NEW_BD_LEADERS.name(); + // 检查任务是否已完成 + if (adminTaskService.isTaskCompleted(userId, BD_LEADER_TYPE, null)) { + log.info("管理员新绑定 BD Leader 任务已完成,跳过更新, userId={}", userId); + return; + } + + // 更新任务进度 + AdminTaskProgressUpdateCmd updateCmd = new AdminTaskProgressUpdateCmd(); + updateCmd.setUserId(userId); + updateCmd.setTaskType(BD_LEADER_TYPE); + updateCmd.setIncrementValue(1); + adminTaskService.updateTaskProgress(updateCmd); + log.info("管理员新绑定 BD Leader 任务进度更新成功, userId={}", userId); + } catch (Exception e) { + log.error("处理管理员新绑定 BD Leader 任务失败, userId={}", userId, e); + } + } + + /** + * 处理管理员任务 - 新绑定 BD + */ + private void handleAdminNewBdTask(Long userId) { + try { + String BD_TYPE = AdminTaskType.NEW_BDS.name(); + if (adminTaskService.isTaskCompleted(userId, BD_TYPE, null)) { + log.info("管理员新绑定 BD 任务已完成,跳过更新, userId={}", userId); + return; + } + + // 更新任务进度 + AdminTaskProgressUpdateCmd updateCmd = new AdminTaskProgressUpdateCmd(); + updateCmd.setUserId(userId); + updateCmd.setTaskType(BD_TYPE); + updateCmd.setIncrementValue(1); + adminTaskService.updateTaskProgress(updateCmd); + log.info("管理员新绑定 BD 任务进度更新成功, userId={}", userId); + } catch (Exception e) { + log.error("处理管理员新绑定 BD 任务失败, userId={}", userId, e); + } + } + + /** + * 处理管理员任务 - 新绑定代理 + */ + private void handleAdminNewAgencyTask(Long userId) { + try { + String AGENCY_TYPE = AdminTaskType.NEW_AGENCIES.name(); + if (adminTaskService.isTaskCompleted(userId, AGENCY_TYPE, null)) { + log.info("管理员新绑定代理任务已完成,跳过更新, userId={}", userId); + return; + } + + // 更新任务进度 + AdminTaskProgressUpdateCmd updateCmd = new AdminTaskProgressUpdateCmd(); + updateCmd.setUserId(userId); + updateCmd.setTaskType(AGENCY_TYPE); + updateCmd.setIncrementValue(1); + adminTaskService.updateTaskProgress(updateCmd); + log.info("管理员新绑定代理任务进度更新成功, userId={}", userId); + } catch (Exception e) { + log.error("处理管理员新绑定代理任务失败, userId={}", userId, e); + } + } } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/AdminTaskServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/AdminTaskServiceImpl.java index 77c5b108..745b3403 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/AdminTaskServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/AdminTaskServiceImpl.java @@ -10,10 +10,15 @@ import com.red.circle.other.app.dto.cmd.AdminTaskInitCmd; import com.red.circle.other.app.dto.cmd.AdminTaskProgressUpdateCmd; import com.red.circle.other.app.dto.cmd.AdminTaskQueryCmd; import com.red.circle.other.domain.admintask.AdminTaskRecord; +import com.red.circle.other.domain.gateway.AdminTaskGateway; +import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.stream.Collectors; @@ -23,6 +28,7 @@ import java.util.stream.Collectors; * @author tf * @date 2025-11-25 */ +@Slf4j @Service @RequiredArgsConstructor public class AdminTaskServiceImpl implements AdminTaskService { @@ -31,6 +37,7 @@ public class AdminTaskServiceImpl implements AdminTaskService { private final AdminTaskProgressUpdateCmdExe adminTaskProgressUpdateCmdExe; private final AdminTaskQueryCmdExe adminTaskQueryCmdExe; private final AdminTaskConvertor adminTaskConvertor; + private final AdminTaskGateway adminTaskGateway; @Override @Transactional(rollbackFor = Exception.class) @@ -41,13 +48,38 @@ public class AdminTaskServiceImpl implements AdminTaskService { @Override @Transactional(rollbackFor = Exception.class) public AdminTaskRecordCO updateTaskProgress(AdminTaskProgressUpdateCmd cmd) { + // 先检查当前周期是否已初始化,未初始化则自动初始化(兜底) + ensureTasksInitialized(cmd.getUserId(), cmd.getCycleStartDate()); + AdminTaskRecord record = adminTaskProgressUpdateCmdExe.execute(cmd); return adminTaskConvertor.toRecordCO(record); } @Override public AdminTaskSummaryCO queryTaskSummary(AdminTaskQueryCmd cmd) { + LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() : + LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd")); + List records = adminTaskQueryCmdExe.execute(cmd); + + if (records.isEmpty()) { + log.info("用户当前周期无任务记录,自动初始化, userId={}, cycleStart={}", + cmd.getUserId(), cycleStartDate); + + try { + LocalDate cycleEndDate = calculateCycleEndDate(cycleStartDate); + AdminTaskInitCmd initCmd = new AdminTaskInitCmd(); + initCmd.setUserId(cmd.getUserId()); + initCmd.setCycleStartDate(cycleStartDate); + initCmd.setCycleEndDate(cycleEndDate); + + adminTaskInitCmdExe.execute(initCmd); + records = adminTaskQueryCmdExe.execute(cmd); + } catch (Exception e) { + log.error("自动初始化任务失败, userId={}", cmd.getUserId(), e); + // 即使初始化失败,也返回空列表,避免接口报错 + } + } // 转换为 CO List taskCOList = records.stream() @@ -68,10 +100,60 @@ public class AdminTaskServiceImpl implements AdminTaskService { // 设置周期信息 if (!records.isEmpty()) { - summary.setCycleStartDate(records.get(0).getCycleStartDate()); - summary.setCycleEndDate(records.get(0).getCycleEndDate()); + summary.setCycleStartDate(records.get(0).getCycleStartDate().toString()); + summary.setCycleEndDate(records.get(0).getCycleEndDate().toString()); } return summary; } + + @Override + public boolean isTaskCompleted(Long userId, String taskType, LocalDate cycleStartDate) { + LocalDate queryDate = cycleStartDate != null ? cycleStartDate : + LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd")); + AdminTaskRecord record = adminTaskQueryCmdExe.findByUserIdAndTaskTypeAndCycle(userId, taskType, queryDate); + return record != null && record.isCompleted(); + } + + /** + * 确保任务已初始化(兜底逻辑) + */ + private void ensureTasksInitialized(Long userId, LocalDate cycleStartDate) { + LocalDate queryDate = cycleStartDate != null ? cycleStartDate : + LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd")); + + // 检查是否存在任务记录 + boolean exists = adminTaskGateway.existsByUserIdAndCycle(userId, queryDate); + if (!exists) { + log.info("任务更新时发现未初始化,自动初始化, userId={}, cycleStart={}", userId, queryDate); + + try { + LocalDate cycleEndDate = calculateCycleEndDate(queryDate); + AdminTaskInitCmd initCmd = new AdminTaskInitCmd(); + initCmd.setUserId(userId); + initCmd.setCycleStartDate(queryDate); + initCmd.setCycleEndDate(cycleEndDate); + + adminTaskInitCmdExe.execute(initCmd); + } catch (Exception e) { + log.error("任务更新时自动初始化失败, userId={}", userId, e); + throw e; // 更新时初始化失败应该抛异常,避免数据不一致 + } + } + } + + /** + * 计算周期结束日期 + */ + private LocalDate calculateCycleEndDate(LocalDate cycleStartDate) { + int dayOfMonth = cycleStartDate.getDayOfMonth(); + + if (dayOfMonth == 1) { + // 1号开始,结束于15号 + return LocalDate.of(cycleStartDate.getYear(), cycleStartDate.getMonth(), 15); + } else { + // 16号开始,结束于月末 + return cycleStartDate.withDayOfMonth(cycleStartDate.lengthOfMonth()); + } + } } diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/AdminTaskSummaryCO.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/AdminTaskSummaryCO.java index a3a80e25..8520833f 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/AdminTaskSummaryCO.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/AdminTaskSummaryCO.java @@ -1,5 +1,7 @@ package com.red.circle.other.app.dto.clientobject; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.Data; import java.time.LocalDate; @@ -22,12 +24,12 @@ public class AdminTaskSummaryCO { /** * 周期开始日期 */ - private LocalDate cycleStartDate; + private String cycleStartDate; /** * 周期结束日期 */ - private LocalDate cycleEndDate; + private String cycleEndDate; /** * 已完成任务数 diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/AdminTaskService.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/AdminTaskService.java index a6454910..f709de92 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/AdminTaskService.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/AdminTaskService.java @@ -6,6 +6,8 @@ import com.red.circle.other.app.dto.cmd.AdminTaskInitCmd; import com.red.circle.other.app.dto.cmd.AdminTaskProgressUpdateCmd; import com.red.circle.other.app.dto.cmd.AdminTaskQueryCmd; +import java.time.LocalDate; + /** * 管理员任务服务接口 * @@ -28,4 +30,14 @@ public interface AdminTaskService { * 查询任务汇总 */ AdminTaskSummaryCO queryTaskSummary(AdminTaskQueryCmd cmd); + + /** + * 查询单个任务是否已完成 + * + * @param userId 用户ID + * @param taskType 任务类型 + * @param cycleStartDate 周期开始日期(可选) + * @return 是否已完成 + */ + boolean isTaskCompleted(Long userId, String taskType, LocalDate cycleStartDate); } diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskRecord.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskRecord.java index 4108675f..c12004f7 100644 --- a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskRecord.java +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskRecord.java @@ -17,7 +17,7 @@ public class AdminTaskRecord { /** * 主键 ID */ - private Long id; + private String id; /** * 用户 ID diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/AdminTaskGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/AdminTaskGatewayImpl.java index b9ec0cdd..58388be9 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/AdminTaskGatewayImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/AdminTaskGatewayImpl.java @@ -122,7 +122,7 @@ public class AdminTaskGatewayImpl implements AdminTaskGateway { private AdminTaskRecord toDomain(AdminTaskRecordDO dobj) { AdminTaskRecord record = new AdminTaskRecord(); - record.setId(dobj.getId() != null ? Long.parseLong(dobj.getId()) : null); + record.setId(dobj.getId()); record.setUserId(dobj.getUserId()); record.setUserRole(dobj.getUserRole()); record.setCycleStartDate(dobj.getCycleStartDate());