diff --git a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/AdminTaskRestController.java b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/AdminTaskRestController.java new file mode 100644 index 00000000..05fe7521 --- /dev/null +++ b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/AdminTaskRestController.java @@ -0,0 +1,31 @@ +package com.red.circle.other.adapter.app; + +import com.red.circle.other.app.dto.clientobject.AdminTaskSummaryCO; +import com.red.circle.other.app.dto.cmd.AdminTaskQueryCmd; +import com.red.circle.other.app.service.AdminTaskService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 管理员任务控制器 + * + * @author tf + * @date 2025-11-25 + */ +@RestController +@RequestMapping("/admin/task") +@RequiredArgsConstructor +public class AdminTaskRestController { + + private final AdminTaskService adminTaskService; + + /** + * 查询任务进度 + */ + @PostMapping("/query") + public AdminTaskSummaryCO queryTaskSummary(@Validated @RequestBody AdminTaskQueryCmd cmd) { + AdminTaskSummaryCO summary = adminTaskService.queryTaskSummary(cmd); + return summary; + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskInitCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskInitCmdExe.java new file mode 100644 index 00000000..f8fd17e8 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskInitCmdExe.java @@ -0,0 +1,55 @@ +package com.red.circle.other.app.command.admintask; + +import com.red.circle.other.domain.admintask.AdminTaskRecord; +import com.red.circle.other.domain.admintask.AdminTaskStatus; +import com.red.circle.other.domain.admintask.AdminTaskType; +import com.red.circle.other.domain.gateway.AdminTaskGateway; +import com.red.circle.other.app.dto.cmd.AdminTaskInitCmd; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +/** + * 管理员任务初始化执行器 + * + * @author tf + * @date 2025-11-25 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class AdminTaskInitCmdExe { + + private final AdminTaskGateway adminTaskGateway; + + public void execute(AdminTaskInitCmd cmd) { + // 检查是否已初始化 + boolean exists = adminTaskGateway.existsByUserIdAndCycle(cmd.getUserId(), cmd.getCycleStartDate()); + if (exists) { + throw new RuntimeException("该周期任务已初始化"); + } + + // 批量创建5个任务记录 + List tasks = new ArrayList<>(); + for (AdminTaskType taskType : AdminTaskType.values()) { + AdminTaskRecord record = new AdminTaskRecord(); + record.setUserId(cmd.getUserId()); + record.setUserRole("ADMIN"); + record.setCycleStartDate(cmd.getCycleStartDate()); + record.setCycleEndDate(cmd.getCycleEndDate()); + record.setTaskType(taskType.getCode()); + record.setTargetCount(taskType.getDefaultTarget()); + record.setCompletedCount(0); + record.setStatus(AdminTaskStatus.IN_PROGRESS.getCode()); + record.setCreateTime(LocalDateTime.now()); + record.setUpdateTime(LocalDateTime.now()); + tasks.add(record); + } + + adminTaskGateway.batchInsert(tasks); + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskProgressUpdateCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskProgressUpdateCmdExe.java new file mode 100644 index 00000000..5b866a06 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskProgressUpdateCmdExe.java @@ -0,0 +1,87 @@ +package com.red.circle.other.app.command.admintask; + +import com.red.circle.other.domain.admintask.AdminTaskRecord; +import com.red.circle.other.domain.admintask.AdminTaskType; +import com.red.circle.other.domain.gateway.AdminTaskGateway; +import com.red.circle.other.app.dto.cmd.AdminTaskProgressUpdateCmd; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; + +/** + * 管理员任务进度更新执行器 + * + * @author tf + * @date 2025-11-25 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class AdminTaskProgressUpdateCmdExe { + + private final AdminTaskGateway adminTaskGateway; + + private static final int HOURS_PER_WORKING_DAY = 2; + + public AdminTaskRecord execute(AdminTaskProgressUpdateCmd cmd) { + LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() : getCurrentCycleStartDate(); + + // 校验任务类型 + AdminTaskType taskType = AdminTaskType.fromCode(cmd.getTaskType()); + if (taskType == null) { + throw new RuntimeException("无效的任务类型: " + cmd.getTaskType()); + } + + // 增量更新任务进度 + AdminTaskRecord updatedRecord = adminTaskGateway.incrementCompletedCount( + cmd.getUserId(), + cmd.getTaskType(), + cycleStartDate, + cmd.getIncrementValue() + ); + + if (updatedRecord == null) { + throw new RuntimeException("任务记录不存在"); + } + + // 检查是否达到目标,更新状态 + if (updatedRecord.reachedTarget() && !updatedRecord.isCompleted()) { + adminTaskGateway.markAsCompleted(cmd.getUserId(), cmd.getTaskType(), cycleStartDate); + updatedRecord.setStatus("COMPLETED"); + } + + // 特殊处理:麦克风使用时长影响工作天数 + if (AdminTaskType.MIC_USAGE.getCode().equals(cmd.getTaskType())) { + int workingDays = updatedRecord.getCompletedCount() / HOURS_PER_WORKING_DAY; + AdminTaskRecord workingDaysRecord = adminTaskGateway.updateCompletedCountAbsolute( + cmd.getUserId(), + AdminTaskType.WORKING_DAYS.getCode(), + cycleStartDate, + workingDays + ); + + // 检查工作天数是否达到目标 + if (workingDaysRecord != null && workingDaysRecord.reachedTarget() && !workingDaysRecord.isCompleted()) { + adminTaskGateway.markAsCompleted(cmd.getUserId(), AdminTaskType.WORKING_DAYS.getCode(), cycleStartDate); + } + } + + return updatedRecord; + } + + /** + * 获取当前周期开始日期 + */ + 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); + } + } +} 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 new file mode 100644 index 00000000..fd415f29 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/admintask/AdminTaskQueryCmdExe.java @@ -0,0 +1,42 @@ +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 lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.List; + +/** + * 管理员任务查询执行器 + * + * @author tf + * @date 2025-11-25 + */ +@Component +@RequiredArgsConstructor +public class AdminTaskQueryCmdExe { + + private final AdminTaskGateway adminTaskGateway; + + public List execute(AdminTaskQueryCmd cmd) { + LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() : getCurrentCycleStartDate(); + 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); + } + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/AdminTaskConvertor.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/AdminTaskConvertor.java new file mode 100644 index 00000000..22e3b034 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/AdminTaskConvertor.java @@ -0,0 +1,45 @@ +package com.red.circle.other.app.convertor; + +import com.red.circle.other.domain.admintask.AdminTaskRecord; +import com.red.circle.other.domain.admintask.AdminTaskStatus; +import com.red.circle.other.domain.admintask.AdminTaskType; +import com.red.circle.other.app.dto.clientobject.AdminTaskRecordCO; +import org.springframework.stereotype.Component; + +/** + * 管理员任务转换器 + * + * @author tf + * @date 2025-11-25 + */ +@Component +public class AdminTaskConvertor { + + public AdminTaskRecordCO toRecordCO(AdminTaskRecord record) { + if (record == null) { + return null; + } + + AdminTaskRecordCO co = new AdminTaskRecordCO(); + co.setTaskType(record.getTaskType()); + co.setTargetCount(record.getTargetCount()); + co.setCompletedCount(record.getCompletedCount()); + co.setStatus(record.getStatus()); + co.setCompletionRate(record.getCompletionRate()); + + // 设置任务名称和单位 + AdminTaskType taskType = AdminTaskType.fromCode(record.getTaskType()); + if (taskType != null) { + co.setTaskName(taskType.getName()); + co.setUnit(taskType.getUnit()); + } + + // 设置状态名称 + AdminTaskStatus status = AdminTaskStatus.fromCode(record.getStatus()); + if (status != null) { + co.setStatusName(status.getName()); + } + + return co; + } +} 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 new file mode 100644 index 00000000..77c5b108 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/AdminTaskServiceImpl.java @@ -0,0 +1,77 @@ +package com.red.circle.other.app.service; + +import com.red.circle.other.app.command.admintask.AdminTaskInitCmdExe; +import com.red.circle.other.app.command.admintask.AdminTaskProgressUpdateCmdExe; +import com.red.circle.other.app.command.admintask.AdminTaskQueryCmdExe; +import com.red.circle.other.app.convertor.AdminTaskConvertor; +import com.red.circle.other.app.dto.clientobject.AdminTaskRecordCO; +import com.red.circle.other.app.dto.clientobject.AdminTaskSummaryCO; +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 lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 管理员任务服务实现 + * + * @author tf + * @date 2025-11-25 + */ +@Service +@RequiredArgsConstructor +public class AdminTaskServiceImpl implements AdminTaskService { + + private final AdminTaskInitCmdExe adminTaskInitCmdExe; + private final AdminTaskProgressUpdateCmdExe adminTaskProgressUpdateCmdExe; + private final AdminTaskQueryCmdExe adminTaskQueryCmdExe; + private final AdminTaskConvertor adminTaskConvertor; + + @Override + @Transactional(rollbackFor = Exception.class) + public void initTasks(AdminTaskInitCmd cmd) { + adminTaskInitCmdExe.execute(cmd); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public AdminTaskRecordCO updateTaskProgress(AdminTaskProgressUpdateCmd cmd) { + AdminTaskRecord record = adminTaskProgressUpdateCmdExe.execute(cmd); + return adminTaskConvertor.toRecordCO(record); + } + + @Override + public AdminTaskSummaryCO queryTaskSummary(AdminTaskQueryCmd cmd) { + List records = adminTaskQueryCmdExe.execute(cmd); + + // 转换为 CO + List taskCOList = records.stream() + .map(adminTaskConvertor::toRecordCO) + .collect(Collectors.toList()); + + // 统计已完成任务数 + long completedCount = records.stream() + .filter(AdminTaskRecord::isCompleted) + .count(); + + // 构建汇总对象 + AdminTaskSummaryCO summary = new AdminTaskSummaryCO(); + summary.setUserId(cmd.getUserId()); + summary.setCompletedTaskCount((int) completedCount); + summary.setTotalTaskCount(records.size()); + summary.setTasks(taskCOList); + + // 设置周期信息 + if (!records.isEmpty()) { + summary.setCycleStartDate(records.get(0).getCycleStartDate()); + summary.setCycleEndDate(records.get(0).getCycleEndDate()); + } + + return summary; + } +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/AdminTaskRecordCO.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/AdminTaskRecordCO.java new file mode 100644 index 00000000..4c6857a3 --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/AdminTaskRecordCO.java @@ -0,0 +1,53 @@ +package com.red.circle.other.app.dto.clientobject; + +import lombok.Data; + +/** + * 管理员任务记录客户端对象 + * + * @author tf + * @date 2025-11-25 + */ +@Data +public class AdminTaskRecordCO { + + /** + * 任务类型编码 + */ + private String taskType; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 目标数量 + */ + private Integer targetCount; + + /** + * 已完成数量 + */ + private Integer completedCount; + + /** + * 单位 + */ + private String unit; + + /** + * 任务状态 + */ + private String status; + + /** + * 任务状态名称 + */ + private String statusName; + + /** + * 完成率(百分比) + */ + private Double completionRate; +} 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 new file mode 100644 index 00000000..a3a80e25 --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/AdminTaskSummaryCO.java @@ -0,0 +1,46 @@ +package com.red.circle.other.app.dto.clientobject; + +import lombok.Data; + +import java.time.LocalDate; +import java.util.List; + +/** + * 管理员任务汇总客户端对象 + * + * @author tf + * @date 2025-11-25 + */ +@Data +public class AdminTaskSummaryCO { + + /** + * 用户 ID + */ + private Long userId; + + /** + * 周期开始日期 + */ + private LocalDate cycleStartDate; + + /** + * 周期结束日期 + */ + private LocalDate cycleEndDate; + + /** + * 已完成任务数 + */ + private Integer completedTaskCount; + + /** + * 总任务数 + */ + private Integer totalTaskCount; + + /** + * 任务列表 + */ + private List tasks; +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskInitCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskInitCmd.java new file mode 100644 index 00000000..1101eba1 --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskInitCmd.java @@ -0,0 +1,37 @@ +package com.red.circle.other.app.dto.cmd; + +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDate; + +/** + * 管理员任务初始化命令 + * + * @author tf + * @date 2025-11-25 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class AdminTaskInitCmd extends AppExtCommand { + + /** + * 用户 ID + */ + @NotNull(message = "用户ID不能为空") + private Long userId; + + /** + * 周期开始日期 + */ + @NotNull(message = "周期开始日期不能为空") + private LocalDate cycleStartDate; + + /** + * 周期结束日期 + */ + @NotNull(message = "周期结束日期不能为空") + private LocalDate cycleEndDate; +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskProgressUpdateCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskProgressUpdateCmd.java new file mode 100644 index 00000000..ee7752b4 --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskProgressUpdateCmd.java @@ -0,0 +1,43 @@ +package com.red.circle.other.app.dto.cmd; + +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 java.time.LocalDate; + +/** + * 管理员任务进度更新命令 + * + * @author tf + * @date 2025-11-25 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class AdminTaskProgressUpdateCmd extends AppExtCommand { + + /** + * 用户 ID + */ + @NotNull(message = "用户ID不能为空") + private Long userId; + + /** + * 任务类型 + */ + @NotBlank(message = "任务类型不能为空") + private String taskType; + + /** + * 增量值 + */ + @NotNull(message = "增量值不能为空") + private Integer incrementValue; + + /** + * 周期开始日期(可选,默认当前周期) + */ + private LocalDate cycleStartDate; +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskQueryCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskQueryCmd.java new file mode 100644 index 00000000..6a13e097 --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/AdminTaskQueryCmd.java @@ -0,0 +1,27 @@ +package com.red.circle.other.app.dto.cmd; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDate; + +/** + * 管理员任务查询命令 + * + * @author tf + * @date 2025-11-25 + */ +@Data +public class AdminTaskQueryCmd { + + /** + * 用户 ID + */ + @NotNull(message = "用户ID不能为空") + private Long userId; + + /** + * 周期开始日期(可选,默认当前周期) + */ + private LocalDate cycleStartDate; +} 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 new file mode 100644 index 00000000..a6454910 --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/AdminTaskService.java @@ -0,0 +1,31 @@ +package com.red.circle.other.app.service; + +import com.red.circle.other.app.dto.clientobject.AdminTaskRecordCO; +import com.red.circle.other.app.dto.clientobject.AdminTaskSummaryCO; +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; + +/** + * 管理员任务服务接口 + * + * @author tf + * @date 2025-11-25 + */ +public interface AdminTaskService { + + /** + * 初始化任务(批量创建5个任务) + */ + void initTasks(AdminTaskInitCmd cmd); + + /** + * 更新任务进度 + */ + AdminTaskRecordCO updateTaskProgress(AdminTaskProgressUpdateCmd cmd); + + /** + * 查询任务汇总 + */ + AdminTaskSummaryCO queryTaskSummary(AdminTaskQueryCmd cmd); +} 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 new file mode 100644 index 00000000..4108675f --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskRecord.java @@ -0,0 +1,103 @@ +package com.red.circle.other.domain.admintask; + +import lombok.Data; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * 管理员任务记录领域对象 + * + * @author tf + * @date 2025-11-25 + */ +@Data +public class AdminTaskRecord { + + /** + * 主键 ID + */ + private Long id; + + /** + * 用户 ID + */ + private Long userId; + + /** + * 用户角色 + */ + private String userRole; + + /** + * 任务周期开始日期 + */ + private LocalDate cycleStartDate; + + /** + * 任务周期结束日期 + */ + private LocalDate cycleEndDate; + + /** + * 任务类型 + */ + private String taskType; + + /** + * 目标数量 + */ + private Integer targetCount; + + /** + * 已完成数量 + */ + private Integer completedCount; + + /** + * 任务状态 + */ + private String status; + + /** + * 完成时间 + */ + private LocalDateTime completedAt; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 判断任务是否完成 + */ + public boolean isCompleted() { + return AdminTaskStatus.COMPLETED.getCode().equals(status); + } + + /** + * 判断任务是否已达到目标 + */ + public boolean reachedTarget() { + return completedCount != null && targetCount != null && completedCount >= targetCount; + } + + /** + * 计算完成率 + */ + public double getCompletionRate() { + if (targetCount == null || targetCount == 0) { + return 0.0; + } + if (completedCount == null) { + return 0.0; + } + return Math.min(100.0, (completedCount * 100.0) / targetCount); + } +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskStatus.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskStatus.java new file mode 100644 index 00000000..37fd753c --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskStatus.java @@ -0,0 +1,55 @@ +package com.red.circle.other.domain.admintask; + +import lombok.Getter; + +/** + * 管理员任务状态枚举 + * + * @author tf + * @date 2025-11-25 + */ +@Getter +public enum AdminTaskStatus { + + /** + * 进行中 + */ + IN_PROGRESS("IN_PROGRESS", "进行中"), + + /** + * 已完成 + */ + COMPLETED("COMPLETED", "已完成"), + + /** + * 已过期 + */ + EXPIRED("EXPIRED", "已过期"); + + /** + * 状态编码 + */ + private final String code; + + /** + * 状态名称 + */ + private final String name; + + AdminTaskStatus(String code, String name) { + this.code = code; + this.name = name; + } + + /** + * 根据 code 获取枚举 + */ + public static AdminTaskStatus fromCode(String code) { + for (AdminTaskStatus status : values()) { + if (status.code.equals(code)) { + return status; + } + } + return null; + } +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskType.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskType.java new file mode 100644 index 00000000..1d76aaa1 --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/admintask/AdminTaskType.java @@ -0,0 +1,77 @@ +package com.red.circle.other.domain.admintask; + +import lombok.Getter; + +/** + * 管理员任务类型枚举 + * + * @author tf + * @date 2025-11-25 + */ +@Getter +public enum AdminTaskType { + + /** + * 麦克风使用时长 + */ + MIC_USAGE("MIC_USAGE", "麦克风使用时长", 20, "小时"), + + /** + * 工作天数(2小时算1天) + */ + WORKING_DAYS("WORKING_DAYS", "工作天数", 10, "天"), + + /** + * 新绑定 BD Leader + */ + NEW_BD_LEADERS("NEW_BD_LEADERS", "新绑定 BD Leader", 1, "个"), + + /** + * 新绑定 BD + */ + NEW_BDS("NEW_BDS", "新绑定 BD", 3, "个"), + + /** + * 新绑定代理 + */ + NEW_AGENCIES("NEW_AGENCIES", "新绑定代理", 5, "个"); + + /** + * 任务类型编码 + */ + private final String code; + + /** + * 任务名称 + */ + private final String name; + + /** + * 默认目标值 + */ + private final int defaultTarget; + + /** + * 单位 + */ + private final String unit; + + AdminTaskType(String code, String name, int defaultTarget, String unit) { + this.code = code; + this.name = name; + this.defaultTarget = defaultTarget; + this.unit = unit; + } + + /** + * 根据 code 获取枚举 + */ + public static AdminTaskType fromCode(String code) { + for (AdminTaskType type : values()) { + if (type.code.equals(code)) { + return type; + } + } + return null; + } +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/AdminTaskGateway.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/AdminTaskGateway.java new file mode 100644 index 00000000..ccd9ef4f --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/AdminTaskGateway.java @@ -0,0 +1,81 @@ +package com.red.circle.other.domain.gateway; + +import com.red.circle.other.domain.admintask.AdminTaskRecord; + +import java.time.LocalDate; +import java.util.List; + +/** + * 管理员任务网关接口 + * + * @author tf + * @date 2025-11-25 + */ +public interface AdminTaskGateway { + + /** + * 批量创建任务记录 + * + * @param records 任务记录列表 + */ + void batchInsert(List records); + + /** + * 查询用户指定周期的所有任务 + * + * @param userId 用户 ID + * @param cycleStartDate 周期开始日期 + * @return 任务记录列表 + */ + List findByUserIdAndCycle(Long userId, LocalDate cycleStartDate); + + /** + * 查询用户指定周期的特定任务类型 + * + * @param userId 用户 ID + * @param taskType 任务类型 + * @param cycleStartDate 周期开始日期 + * @return 任务记录 + */ + AdminTaskRecord findByUserIdAndTaskTypeAndCycle(Long userId, String taskType, LocalDate cycleStartDate); + + /** + * 增量更新任务完成数量(原子操作) + * + * @param userId 用户 ID + * @param taskType 任务类型 + * @param cycleStartDate 周期开始日期 + * @param incrementValue 增量值 + * @return 更新后的记录 + */ + AdminTaskRecord incrementCompletedCount(Long userId, String taskType, LocalDate cycleStartDate, int incrementValue); + + /** + * 绝对值更新任务完成数量(用于工作天数计算) + * + * @param userId 用户 ID + * @param taskType 任务类型 + * @param cycleStartDate 周期开始日期 + * @param absoluteValue 绝对值 + * @return 更新后的记录 + */ + AdminTaskRecord updateCompletedCountAbsolute(Long userId, String taskType, LocalDate cycleStartDate, int absoluteValue); + + /** + * 更新任务状态为已完成 + * + * @param userId 用户 ID + * @param taskType 任务类型 + * @param cycleStartDate 周期开始日期 + */ + void markAsCompleted(Long userId, String taskType, LocalDate cycleStartDate); + + /** + * 检查用户指定周期是否已初始化任务 + * + * @param userId 用户 ID + * @param cycleStartDate 周期开始日期 + * @return 是否存在 + */ + boolean existsByUserIdAndCycle(Long userId, LocalDate cycleStartDate); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/AdminTaskRecordDO.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/AdminTaskRecordDO.java new file mode 100644 index 00000000..8086a8de --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/AdminTaskRecordDO.java @@ -0,0 +1,66 @@ +package com.red.circle.other.infra.database.mongo.document; + +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.CompoundIndex; +import org.springframework.data.mongodb.core.index.CompoundIndexes; +import org.springframework.data.mongodb.core.index.Indexed; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * 管理员任务记录 MongoDB 文档 + * + * @author tf + * @date 2025-11-25 + */ +@Data +@Document(collection = "admin_task_record") +@CompoundIndexes({ + @CompoundIndex(name = "uk_user_cycle_type", def = "{'user_id': 1, 'cycle_start_date': 1, 'task_type': 1}", unique = true), + @CompoundIndex(name = "idx_user_role", def = "{'user_id': 1, 'user_role': 1}"), + @CompoundIndex(name = "idx_cycle", def = "{'cycle_start_date': 1, 'cycle_end_date': 1}") +}) +public class AdminTaskRecordDO { + + @Id + private String id; + + @Field("user_id") + private Long userId; + + @Field("user_role") + private String userRole; + + @Field("cycle_start_date") + private LocalDate cycleStartDate; + + @Field("cycle_end_date") + private LocalDate cycleEndDate; + + @Field("task_type") + @Indexed + private String taskType; + + @Field("target_count") + private Integer targetCount; + + @Field("completed_count") + private Integer completedCount; + + @Field("status") + @Indexed + private String status; + + @Field("completed_at") + private LocalDateTime completedAt; + + @Field("create_time") + private LocalDateTime createTime; + + @Field("update_time") + private LocalDateTime updateTime; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/AdminTaskRecordRepository.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/AdminTaskRecordRepository.java new file mode 100644 index 00000000..b5730c30 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/AdminTaskRecordRepository.java @@ -0,0 +1,34 @@ +package com.red.circle.other.infra.database.mongo.repository; + +import com.red.circle.other.infra.database.mongo.document.AdminTaskRecordDO; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +/** + * 管理员任务记录 Repository + * + * @author tf + * @date 2025-11-25 + */ +@Repository +public interface AdminTaskRecordRepository extends MongoRepository { + + /** + * 查询用户指定周期的所有任务 + */ + List findByUserIdAndCycleStartDate(Long userId, LocalDate cycleStartDate); + + /** + * 查询用户指定周期的特定任务类型 + */ + Optional findByUserIdAndTaskTypeAndCycleStartDate(Long userId, String taskType, LocalDate cycleStartDate); + + /** + * 检查是否存在 + */ + boolean existsByUserIdAndCycleStartDate(Long userId, LocalDate cycleStartDate); +} 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 new file mode 100644 index 00000000..b9ec0cdd --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/AdminTaskGatewayImpl.java @@ -0,0 +1,139 @@ +package com.red.circle.other.infra.gateway; + +import com.red.circle.other.domain.admintask.AdminTaskRecord; +import com.red.circle.other.domain.admintask.AdminTaskStatus; +import com.red.circle.other.domain.gateway.AdminTaskGateway; +import com.red.circle.other.infra.database.mongo.document.AdminTaskRecordDO; +import com.red.circle.other.infra.database.mongo.repository.AdminTaskRecordRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 管理员任务网关实现 + * + * @author tf + * @date 2025-11-25 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class AdminTaskGatewayImpl implements AdminTaskGateway { + + private final AdminTaskRecordRepository adminTaskRecordRepository; + private final MongoTemplate mongoTemplate; + + @Override + public void batchInsert(List records) { + List doList = records.stream() + .map(this::toDO) + .collect(Collectors.toList()); + adminTaskRecordRepository.saveAll(doList); + } + + @Override + public List findByUserIdAndCycle(Long userId, LocalDate cycleStartDate) { + List doList = adminTaskRecordRepository.findByUserIdAndCycleStartDate(userId, cycleStartDate); + return doList.stream() + .map(this::toDomain) + .collect(Collectors.toList()); + } + + @Override + public AdminTaskRecord findByUserIdAndTaskTypeAndCycle(Long userId, String taskType, LocalDate cycleStartDate) { + return adminTaskRecordRepository.findByUserIdAndTaskTypeAndCycleStartDate(userId, taskType, cycleStartDate) + .map(this::toDomain) + .orElse(null); + } + + @Override + public AdminTaskRecord incrementCompletedCount(Long userId, String taskType, LocalDate cycleStartDate, int incrementValue) { + Query query = Query.query(Criteria.where("user_id").is(userId) + .and("task_type").is(taskType) + .and("cycle_start_date").is(cycleStartDate)); + + Update update = new Update() + .inc("completed_count", incrementValue) + .set("update_time", LocalDateTime.now()); + + mongoTemplate.updateFirst(query, update, AdminTaskRecordDO.class); + + return findByUserIdAndTaskTypeAndCycle(userId, taskType, cycleStartDate); + } + + @Override + public AdminTaskRecord updateCompletedCountAbsolute(Long userId, String taskType, LocalDate cycleStartDate, int absoluteValue) { + Query query = Query.query(Criteria.where("user_id").is(userId) + .and("task_type").is(taskType) + .and("cycle_start_date").is(cycleStartDate)); + + Update update = new Update() + .set("completed_count", absoluteValue) + .set("update_time", LocalDateTime.now()); + + mongoTemplate.updateFirst(query, update, AdminTaskRecordDO.class); + + return findByUserIdAndTaskTypeAndCycle(userId, taskType, cycleStartDate); + } + + @Override + public void markAsCompleted(Long userId, String taskType, LocalDate cycleStartDate) { + Query query = Query.query(Criteria.where("user_id").is(userId) + .and("task_type").is(taskType) + .and("cycle_start_date").is(cycleStartDate)); + + Update update = new Update() + .set("status", AdminTaskStatus.COMPLETED.getCode()) + .set("completed_at", LocalDateTime.now()) + .set("update_time", LocalDateTime.now()); + + mongoTemplate.updateFirst(query, update, AdminTaskRecordDO.class); + } + + @Override + public boolean existsByUserIdAndCycle(Long userId, LocalDate cycleStartDate) { + return adminTaskRecordRepository.existsByUserIdAndCycleStartDate(userId, cycleStartDate); + } + + private AdminTaskRecordDO toDO(AdminTaskRecord record) { + AdminTaskRecordDO dobj = new AdminTaskRecordDO(); + dobj.setUserId(record.getUserId()); + dobj.setUserRole(record.getUserRole()); + dobj.setCycleStartDate(record.getCycleStartDate()); + dobj.setCycleEndDate(record.getCycleEndDate()); + dobj.setTaskType(record.getTaskType()); + dobj.setTargetCount(record.getTargetCount()); + dobj.setCompletedCount(record.getCompletedCount()); + dobj.setStatus(record.getStatus()); + dobj.setCompletedAt(record.getCompletedAt()); + dobj.setCreateTime(record.getCreateTime() != null ? record.getCreateTime() : LocalDateTime.now()); + dobj.setUpdateTime(record.getUpdateTime() != null ? record.getUpdateTime() : LocalDateTime.now()); + return dobj; + } + + private AdminTaskRecord toDomain(AdminTaskRecordDO dobj) { + AdminTaskRecord record = new AdminTaskRecord(); + record.setId(dobj.getId() != null ? Long.parseLong(dobj.getId()) : null); + record.setUserId(dobj.getUserId()); + record.setUserRole(dobj.getUserRole()); + record.setCycleStartDate(dobj.getCycleStartDate()); + record.setCycleEndDate(dobj.getCycleEndDate()); + record.setTaskType(dobj.getTaskType()); + record.setTargetCount(dobj.getTargetCount()); + record.setCompletedCount(dobj.getCompletedCount()); + record.setStatus(dobj.getStatus()); + record.setCompletedAt(dobj.getCompletedAt()); + record.setCreateTime(dobj.getCreateTime()); + record.setUpdateTime(dobj.getUpdateTime()); + return record; + } +}