Your Name 3922d3653b feat: 所有页面积分显示支持小数点后两位
- Analytics.vue: 设计师绩效、用户需求、扣分明细、CSV导出
- DesignerHall.vue: 设计师卡片累计积分
- TaskHall.vue: 任务卡片积分、typeDetails积分
- LeaderWorkbench.vue: 任务积分
- MyTasks.vue: 任务积分
- CreateTask.vue: 预估积分、任务类型积分
- RewardRecords.vue: 累计积分、奖励积分
- Goals.vue: 奖励积分
- IncentiveCenter.vue: 用户积分、奖励积分
- Contests.vue: 竞赛得分、奖励积分
- CheckIn.vue: 历史奖励积分
- ScorePoolManagement.vue: 积分变动
- Achievements.vue: 奖励积分
2026-01-15 15:39:32 +08:00

558 lines
22 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.designhub.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.designhub.entity.Department;
import com.designhub.entity.Task;
import com.designhub.entity.TaskItem;
import com.designhub.entity.User;
import com.designhub.mapper.DepartmentMapper;
import com.designhub.mapper.TaskMapper;
import com.designhub.mapper.TaskItemMapper;
import com.designhub.mapper.UserMapper;
import com.designhub.vo.DesignerStatsVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 设计师统计Service
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class DesignerService {
private final UserMapper userMapper;
private final TaskMapper taskMapper;
private final TaskItemMapper taskItemMapper;
private final DepartmentMapper departmentMapper;
/**
* 获取所有设计师的统计信息
*/
public List<DesignerStatsVO> getAllDesignerStats() {
return getAllDesignerStats(null);
}
/**
* 获取所有设计师的统计信息(支持部门筛选)
*/
public List<DesignerStatsVO> getAllDesignerStats(Long deptId) {
List<Long> targetDeptIds;
if (deptId != null) {
// 如果指定了部门,获取该部门及其所有子部门
targetDeptIds = getDeptAndChildIds(deptId);
log.debug("筛选部门: deptId={}, targetDeptIds={}", deptId, targetDeptIds);
} else {
// 仅获取接单部门(designer/receiving)的用户
targetDeptIds = departmentMapper.selectList(
new LambdaQueryWrapper<Department>()
.eq(Department::getDeptType, "receiving")
.eq(Department::getStatus, 1)
).stream()
.map(Department::getId)
.collect(Collectors.toList());
}
if (targetDeptIds.isEmpty()) {
return new ArrayList<>();
}
List<User> designers = userMapper.selectList(
new LambdaQueryWrapper<User>()
.eq(User::getStatus, 1)
.in(User::getDeptId, targetDeptIds)
);
return designers.stream()
.map(this::buildDesignerStats)
.collect(Collectors.toList());
}
/**
* 递归获取部门及其所有子部门ID
*/
private List<Long> getDeptAndChildIds(Long deptId) {
List<Long> deptIds = new ArrayList<>();
deptIds.add(deptId);
List<Department> children = departmentMapper.selectList(
new LambdaQueryWrapper<Department>()
.eq(Department::getParentId, deptId)
.eq(Department::getStatus, 1)
);
for (Department child : children) {
deptIds.addAll(getDeptAndChildIds(child.getId()));
}
return deptIds;
}
/**
* 获取单个设计师的统计信息
*/
public DesignerStatsVO getDesignerStats(Long userId) {
User user = userMapper.selectById(userId);
if (user == null) {
return null;
}
return buildDesignerStats(user);
}
/**
* 获取排行榜数据(支持按月查询)
*/
public List<DesignerStatsVO> getLeaderboard(String type, Integer limit, String month) {
List<DesignerStatsVO> stats;
// 如果指定了月份,则查询该月份的数据
if (month != null && !month.isEmpty()) {
stats = getDesignerStatsByMonth(month);
} else {
// 否则查询全部数据
stats = getAllDesignerStats();
}
// 根据类型排序
switch (type) {
case "score":
// 积分榜
stats.sort((a, b) -> Integer.compare(
b.getTotalScore() != null ? b.getTotalScore() : 0,
a.getTotalScore() != null ? a.getTotalScore() : 0
));
break;
case "efficiency":
// 效率榜(完成率)
stats.sort((a, b) -> Double.compare(
b.getCompletionRate() != null ? b.getCompletionRate() : 0.0,
a.getCompletionRate() != null ? a.getCompletionRate() : 0.0
));
break;
case "quality":
// 好评榜
stats.sort((a, b) -> {
int countCompare = Integer.compare(
b.getGoodReviewCount() != null ? b.getGoodReviewCount() : 0,
a.getGoodReviewCount() != null ? a.getGoodReviewCount() : 0
);
if (countCompare == 0) {
// 好评数相同时,比较平均评分
return Double.compare(
b.getAvgRating() != null ? b.getAvgRating() : 0.0,
a.getAvgRating() != null ? a.getAvgRating() : 0.0
);
}
return countCompare;
});
break;
case "diligence":
// 勤勉榜(总任务数)
stats.sort((a, b) -> Integer.compare(
b.getTotalTaskCount() != null ? b.getTotalTaskCount() : 0,
a.getTotalTaskCount() != null ? a.getTotalTaskCount() : 0
));
break;
default:
// 默认按积分排序
stats.sort((a, b) -> Integer.compare(
b.getTotalScore() != null ? b.getTotalScore() : 0,
a.getTotalScore() != null ? a.getTotalScore() : 0
));
}
// 限制返回数量
if (limit != null && limit > 0 && stats.size() > limit) {
return stats.subList(0, limit);
}
return stats;
}
/**
* 构建设计师统计信息
*/
private DesignerStatsVO buildDesignerStats(User user) {
DesignerStatsVO stats = new DesignerStatsVO();
// 基本信息
stats.setUserId(user.getId());
stats.setUserName(user.getRealName());
stats.setAvatar(user.getAvatar());
stats.setDeptName(user.getDeptName());
// 查询任务统计
Long userId = user.getId();
// 总接收任务数(包括主任务和子任务)
Long mainTotalCount = taskMapper.selectCount(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getDeleted, 0)
);
Long subTotalCount = taskItemMapper.selectCount(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getReceiverId, userId)
);
Integer totalCount = (mainTotalCount != null ? mainTotalCount.intValue() : 0) +
(subTotalCount != null ? subTotalCount.intValue() : 0);
stats.setTotalTaskCount(totalCount);
// 进行中任务数(包括主任务和子任务)
Long mainInProgressCount = taskMapper.selectCount(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getStatus, "in_progress")
.eq(Task::getDeleted, 0)
);
// 同时查询子任务表中进行中的任务数
Long subInProgressCount = taskItemMapper.selectCount(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getReceiverId, userId)
.eq(TaskItem::getStatus, "in_progress")
);
Integer inProgressCount = (mainInProgressCount != null ? mainInProgressCount.intValue() : 0) +
(subInProgressCount != null ? subInProgressCount.intValue() : 0);
stats.setInProgressCount(inProgressCount);
log.debug("设计师进行中任务统计: userId={}, mainInProgress={}, subInProgress={}, total={}",
userId, mainInProgressCount, subInProgressCount, inProgressCount);
// 待接单任务数(队列)- 这里应该是用户已接收但还未开始的任务,实际上应该是0
// 因为一旦接收任务,状态就会变成 in_progress
// 队列数可以理解为用户当前可以接的任务数(这个需要从任务大厅查询,这里暂时设为0)
stats.setQueueCount(0);
// 已完成任务数(包括主任务和子任务)
Long mainCompletedCount = taskMapper.selectCount(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getStatus, "completed")
.eq(Task::getDeleted, 0)
);
Long subCompletedCount = taskItemMapper.selectCount(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getReceiverId, userId)
.eq(TaskItem::getStatus, "completed")
);
Integer completedCount = (mainCompletedCount != null ? mainCompletedCount.intValue() : 0) +
(subCompletedCount != null ? subCompletedCount.intValue() : 0);
stats.setCompletedCount(completedCount);
// 延误任务数
Long delayedCountLong = taskMapper.selectCount(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getIsDelayed, 1)
);
Integer delayedCount = delayedCountLong.intValue();
stats.setDelayedCount(delayedCount);
// 计算完成率
if (totalCount > 0) {
stats.setCompletionRate(completedCount * 100.0 / totalCount);
} else {
stats.setCompletionRate(0.0);
}
// 计算准时率
if (completedCount > 0) {
Integer ontimeCount = completedCount - delayedCount;
stats.setOntimeRate(ontimeCount * 100.0 / completedCount);
} else {
stats.setOntimeRate(0.0);
}
// 查询已完成任务的详细信息(用于计算积分和评分)
List<Task> completedTasks = taskMapper.selectList(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getStatus, "completed")
.eq(Task::getDeleted, 0)
);
// 查询已完成子任务(用于计算积分)
List<TaskItem> completedSubTasks = taskItemMapper.selectList(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getReceiverId, userId)
.eq(TaskItem::getStatus, "completed")
);
// 累计获得积分(主任务 + 子任务)
// 优先使用 scoreReward(实际获得积分),如果没有则使用 score(任务积分)
Integer mainTaskScore = completedTasks.stream()
.map(task -> {
if (task.getScoreReward() != null && task.getScoreReward().compareTo(java.math.BigDecimal.ZERO) > 0) {
return task.getScoreReward();
}
return task.getScore() != null ? task.getScore() : java.math.BigDecimal.ZERO;
})
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add)
.intValue();
Integer subTaskScore = completedSubTasks.stream()
.map(item -> item.getTotalScore() != null ? item.getTotalScore() : java.math.BigDecimal.ZERO)
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add)
.intValue();
Integer totalScore = mainTaskScore + subTaskScore;
stats.setTotalScore(totalScore);
log.debug("设计师积分统计: userId={}, mainTaskScore={}, subTaskScore={}, totalScore={}",
userId, mainTaskScore, subTaskScore, totalScore);
// 平均评分
List<Integer> ratings = completedTasks.stream()
.map(Task::getRating)
.filter(rating -> rating != null && rating > 0)
.collect(Collectors.toList());
if (!ratings.isEmpty()) {
double avgRating = ratings.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0.0);
stats.setAvgRating(avgRating);
// 好评数(5星)
long goodReviewCount = ratings.stream()
.filter(rating -> rating == 5)
.count();
stats.setGoodReviewCount((int) goodReviewCount);
} else {
stats.setAvgRating(0.0);
stats.setGoodReviewCount(0);
}
// 在线状态(简化处理,实际应该通过WebSocket或心跳机制维护)
stats.setOnlineStatus(1);
stats.setLastActiveTime("刚刚");
return stats;
}
/**
* 获取指定月份的设计师统计信息
*/
private List<DesignerStatsVO> getDesignerStatsByMonth(String month) {
// 解析月份 (YYYY-MM)
String[] parts = month.split("-");
if (parts.length != 2) {
return getAllDesignerStats(); // 格式错误时返回全部数据
}
int year = Integer.parseInt(parts[0]);
int monthNum = Integer.parseInt(parts[1]);
// 计算月份的起始和结束时间
String startDate = String.format("%d-%02d-01 00:00:00", year, monthNum);
String endDate;
if (monthNum == 12) {
endDate = String.format("%d-01-01 00:00:00", year + 1);
} else {
endDate = String.format("%d-%02d-01 00:00:00", year, monthNum + 1);
}
// 获取接单部门的设计师列表
List<Long> receivingDeptIds = departmentMapper.selectList(
new LambdaQueryWrapper<Department>()
.eq(Department::getDeptType, "receiving")
.eq(Department::getStatus, 1)
).stream()
.map(Department::getId)
.collect(Collectors.toList());
if (receivingDeptIds.isEmpty()) {
return new ArrayList<>();
}
List<User> designers = userMapper.selectList(
new LambdaQueryWrapper<User>()
.eq(User::getStatus, 1)
.in(User::getDeptId, receivingDeptIds)
);
// 构建每个设计师的月度统计
return designers.stream()
.map(user -> buildDesignerMonthStats(user, startDate, endDate))
.collect(Collectors.toList());
}
/**
* 构建设计师指定月份的统计信息
*/
private DesignerStatsVO buildDesignerMonthStats(User user, String startDate, String endDate) {
DesignerStatsVO stats = new DesignerStatsVO();
// 基本信息
stats.setUserId(user.getId());
stats.setUserName(user.getRealName());
stats.setAvatar(user.getAvatar());
stats.setDeptName(user.getDeptName());
Long userId = user.getId();
// 当月总接收任务数
Long totalCountLong = taskMapper.selectCount(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.ge(Task::getCreateTime, startDate)
.lt(Task::getCreateTime, endDate)
);
stats.setTotalTaskCount(totalCountLong.intValue());
// 当月进行中任务数(实际在月份查询时应该是0,因为查历史月份)
Long inProgressCountLong = taskMapper.selectCount(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getStatus, "in_progress")
.ge(Task::getCreateTime, startDate)
.lt(Task::getCreateTime, endDate)
);
stats.setInProgressCount(inProgressCountLong.intValue());
stats.setQueueCount(0);
// 当月已完成任务数
Long completedCountLong = taskMapper.selectCount(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getStatus, "completed")
.ge(Task::getCompleteTime, startDate)
.lt(Task::getCompleteTime, endDate)
);
stats.setCompletedCount(completedCountLong.intValue());
// 当月总积分(已完成任务的积分之和)
List<Task> completedTasks = taskMapper.selectList(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getStatus, "completed")
.ge(Task::getCompleteTime, startDate)
.lt(Task::getCompleteTime, endDate)
);
Integer totalScore = completedTasks.stream()
.map(task -> task.getScore() != null ? task.getScore() : java.math.BigDecimal.ZERO)
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add)
.intValue();
stats.setTotalScore(totalScore);
// 当月延期任务数
Long delayedCountLong = taskMapper.selectCount(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getIsDelayed, 1)
.ge(Task::getCompleteTime, startDate)
.lt(Task::getCompleteTime, endDate)
);
stats.setDelayedCount(delayedCountLong.intValue());
// 计算当月完成率
if (totalCountLong > 0) {
double completionRate = (completedCountLong.doubleValue() / totalCountLong.doubleValue()) * 100;
stats.setCompletionRate(completionRate);
} else {
stats.setCompletionRate(0.0);
}
// 计算当月按时完成率
if (completedCountLong > 0) {
long ontimeCount = completedCountLong - delayedCountLong;
double ontimeRate = (ontimeCount / (double)completedCountLong) * 100;
stats.setOntimeRate(ontimeRate);
} else {
stats.setOntimeRate(0.0);
}
// 当月平均评分
Double avgRating = completedTasks.stream()
.filter(task -> task.getRating() != null && task.getRating() > 0)
.mapToInt(Task::getRating)
.average()
.orElse(0.0);
stats.setAvgRating(avgRating);
// 当月好评数(评分>=4)
long goodReviewCount = completedTasks.stream()
.filter(task -> task.getRating() != null && task.getRating() >= 4)
.count();
stats.setGoodReviewCount((int)goodReviewCount);
// 在线状态(历史月份时设为离线)
stats.setOnlineStatus(0);
stats.setLastActiveTime(null);
return stats;
}
/**
* 获取设计师进行中的任务列表(包括主任务和子任务)
* 返回统一格式的 Map 列表
*/
public List<java.util.Map<String, Object>> getDesignerInProgressTasks(Long userId) {
List<java.util.Map<String, Object>> result = new ArrayList<>();
// 1. 查询主任务
List<Task> mainTasks = taskMapper.selectList(
new LambdaQueryWrapper<Task>()
.eq(Task::getReceiverId, userId)
.eq(Task::getStatus, "in_progress")
.eq(Task::getDeleted, 0)
.orderByDesc(Task::getCreateTime)
);
for (Task task : mainTasks) {
java.util.Map<String, Object> item = new java.util.LinkedHashMap<>();
item.put("id", task.getId());
item.put("taskNo", task.getTaskNo());
item.put("title", task.getTitle());
item.put("spuInfo", task.getSpuInfo()); // 添加SPU信息
item.put("status", task.getStatus());
item.put("progress", task.getProgress());
item.put("score", task.getScore());
// 使用实际接单时间作为开始时间
item.put("startTime", task.getActualStartTime() != null ? task.getActualStartTime() : task.getStartTime());
item.put("deadline", task.getDeadline());
item.put("createTime", task.getCreateTime());
item.put("isSubTask", false);
item.put("type", "主任务");
result.add(item);
}
// 2. 查询子任务
List<TaskItem> subTasks = taskItemMapper.selectList(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getReceiverId, userId)
.eq(TaskItem::getStatus, "in_progress")
.orderByDesc(TaskItem::getCreateTime)
);
for (TaskItem item : subTasks) {
java.util.Map<String, Object> taskItem = new java.util.LinkedHashMap<>();
taskItem.put("id", item.getId());
taskItem.put("taskNo", item.getItemNo());
// 获取主任务信息
Task mainTask = taskMapper.selectById(item.getTaskId());
// 优先显示主任务的SPU信息,否则显示子任务标题
String spuInfo = mainTask != null ? mainTask.getSpuInfo() : null;
taskItem.put("title", spuInfo != null ? spuInfo : ("子任务 #" + item.getGroupNo()));
taskItem.put("spuInfo", spuInfo);
taskItem.put("status", item.getStatus());
taskItem.put("progress", item.getProgress());
taskItem.put("score", item.getTotalScore());
taskItem.put("startTime", item.getCreateTime()); // 使用创建时间作为开始时间
taskItem.put("deadline", mainTask != null ? mainTask.getDeadline() : null);
taskItem.put("createTime", item.getCreateTime());
taskItem.put("isSubTask", true);
taskItem.put("type", "子任务");
taskItem.put("mainTaskId", item.getTaskId());
result.add(taskItem);
}
return result;
}
}