feat: 优化数据分析页面

- 支持月份选择器,可查看指定月份数据
- 新增4个Tab:需求分析、设计师绩效、流程效率、预警监控
- 核心指标卡片:总任务数、待处理、进行中、已完成、完成率、准时率
- 设计师绩效:接单量、完成量、准时率、好评率、工作负载排行
- 流程效率:各环节耗时分析、瓶颈识别、环比对比
- 预警监控:超时预警、即将超时、长期未接单、审批滞后
- 后端新增3个API:设计师绩效、流程效率、预警任务
This commit is contained in:
Your Name 2026-01-04 08:57:45 +08:00
parent d6c81f1fdc
commit 30f9a3e735
8 changed files with 2486 additions and 620 deletions

View File

@ -4,12 +4,17 @@ import com.designhub.common.Result;
import com.designhub.service.AnalyticsService;
import com.designhub.util.JwtUtil;
import com.designhub.vo.DemandStatisticsVO;
import com.designhub.vo.DesignerPerformanceVO;
import com.designhub.vo.ProcessEfficiencyVO;
import com.designhub.vo.WarningTaskVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 数据分析Controller
*/
@ -29,16 +34,59 @@ public class AnalyticsController {
@RequestHeader("Authorization") String authHeader,
@RequestParam(required = false, defaultValue = "month") String dateRange,
@RequestParam(required = false) Long deptId,
@RequestParam(required = false) Long userId
@RequestParam(required = false) Long userId,
@RequestParam(required = false) String month // 新增:指定月份,格式 yyyy-MM
) {
// 从token获取当前用户ID
String token = jwtUtil.extractTokenFromHeader(authHeader);
Long currentUserId = jwtUtil.getUserIdFromToken(token);
log.info("获取需求统计数据: currentUserId={}, dateRange={}, deptId={}, userId={}",
currentUserId, dateRange, deptId, userId);
log.info("获取需求统计数据: currentUserId={}, dateRange={}, deptId={}, userId={}, month={}",
currentUserId, dateRange, deptId, userId, month);
return Result.success(analyticsService.getDemandStatistics(currentUserId, dateRange, deptId, userId));
return Result.success(analyticsService.getDemandStatistics(currentUserId, dateRange, deptId, userId, month));
}
@Operation(summary = "获取设计师绩效数据")
@GetMapping("/designer-performance")
public Result<DesignerPerformanceVO> getDesignerPerformance(
@RequestHeader("Authorization") String authHeader,
@RequestParam(required = false) String month, // 指定月份,格式 yyyy-MM
@RequestParam(required = false) Long deptId // 指定部门
) {
String token = jwtUtil.extractTokenFromHeader(authHeader);
Long currentUserId = jwtUtil.getUserIdFromToken(token);
log.info("获取设计师绩效数据: currentUserId={}, month={}, deptId={}", currentUserId, month, deptId);
return Result.success(analyticsService.getDesignerPerformance(currentUserId, month, deptId));
}
@Operation(summary = "获取流程效率数据")
@GetMapping("/process-efficiency")
public Result<ProcessEfficiencyVO> getProcessEfficiency(
@RequestHeader("Authorization") String authHeader,
@RequestParam(required = false) String month // 指定月份,格式 yyyy-MM
) {
String token = jwtUtil.extractTokenFromHeader(authHeader);
Long currentUserId = jwtUtil.getUserIdFromToken(token);
log.info("获取流程效率数据: currentUserId={}, month={}", currentUserId, month);
return Result.success(analyticsService.getProcessEfficiency(currentUserId, month));
}
@Operation(summary = "获取预警任务列表")
@GetMapping("/warnings")
public Result<List<WarningTaskVO>> getWarningTasks(
@RequestHeader("Authorization") String authHeader
) {
String token = jwtUtil.extractTokenFromHeader(authHeader);
Long currentUserId = jwtUtil.getUserIdFromToken(token);
log.info("获取预警任务列表: currentUserId={}", currentUserId);
return Result.success(analyticsService.getWarningTasks(currentUserId));
}
}

View File

@ -4,15 +4,21 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.designhub.entity.*;
import com.designhub.mapper.*;
import com.designhub.vo.DemandStatisticsVO;
import com.designhub.vo.DesignerPerformanceVO;
import com.designhub.vo.ProcessEfficiencyVO;
import com.designhub.vo.WarningTaskVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
@ -37,8 +43,9 @@ public class AnalyticsService {
* @param dateRange 时间范围
* @param deptId 部门ID(可选,用于管理员查看特定部门)
* @param userId 用户ID(可选,用于管理员查看特定用户)
* @param month 指定月份(可选,格式 yyyy-MM)
*/
public DemandStatisticsVO getDemandStatistics(Long currentUserId, String dateRange, Long deptId, Long userId) {
public DemandStatisticsVO getDemandStatistics(Long currentUserId, String dateRange, Long deptId, Long userId, String month) {
// 获取当前用户角色和部门信息
List<Role> roles = roleService.getRolesByUserId(currentUserId);
User currentUser = userMapper.selectById(currentUserId);
@ -46,16 +53,38 @@ public class AnalyticsService {
boolean isAdmin = roles.stream()
.anyMatch(role -> "ADMIN".equals(role.getRoleCode()) || "SUPER_ADMIN".equals(role.getRoleCode()));
// 检查 isAdmin 字段
if (!isAdmin && currentUser != null && Integer.valueOf(1).equals(currentUser.getIsAdmin())) {
isAdmin = true;
}
boolean isDeptHead = roles.stream()
.anyMatch(role -> "DEMAND_DEPT_HEAD".equals(role.getRoleCode()) ||
"RECEIVING_DEPT_SUPERVISOR".equals(role.getRoleCode()));
log.info("获取需求统计数据: currentUserId={}, isAdmin={}, isDeptHead={}, userDeptId={}, dateRange={}, deptId={}, userId={}",
currentUserId, isAdmin, isDeptHead, currentUser != null ? currentUser.getDeptId() : null, dateRange, deptId, userId);
log.info("获取需求统计数据: currentUserId={}, isAdmin={}, isDeptHead={}, userDeptId={}, dateRange={}, deptId={}, userId={}, month={}",
currentUserId, isAdmin, isDeptHead, currentUser != null ? currentUser.getDeptId() : null, dateRange, deptId, userId, month);
// 计算时间范围
LocalDateTime startTime = calculateStartTime(dateRange);
LocalDateTime endTime = LocalDateTime.now();
LocalDateTime startTime;
LocalDateTime endTime;
// 如果指定了月份,则使用月份范围
if (month != null && !month.isEmpty()) {
try {
LocalDate monthDate = LocalDate.parse(month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
startTime = LocalDateTime.of(monthDate, LocalTime.MIN);
endTime = LocalDateTime.of(monthDate.with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
log.info("使用指定月份范围: {} ~ {}", startTime, endTime);
} catch (Exception e) {
log.warn("月份格式错误: {}, 使用默认范围", month);
startTime = calculateStartTime(dateRange);
endTime = LocalDateTime.now();
}
} else {
startTime = calculateStartTime(dateRange);
endTime = LocalDateTime.now();
}
// 构建查询条件
LambdaQueryWrapper<Task> queryWrapper = new LambdaQueryWrapper<Task>()
@ -510,5 +539,527 @@ public class AnalyticsService {
return 0.0;
}
}
// ==================== 设计师绩效统计 ====================
/**
* 获取设计师绩效数据
*/
public DesignerPerformanceVO getDesignerPerformance(Long currentUserId, String month, Long deptId) {
// 权限检查
List<Role> roles = roleService.getRolesByUserId(currentUserId);
User currentUser = userMapper.selectById(currentUserId);
boolean isAdmin = roles.stream()
.anyMatch(role -> "ADMIN".equals(role.getRoleCode()) || "SUPER_ADMIN".equals(role.getRoleCode()));
if (!isAdmin && currentUser != null && Integer.valueOf(1).equals(currentUser.getIsAdmin())) {
isAdmin = true;
}
// 计算时间范围
LocalDateTime startTime;
LocalDateTime endTime;
if (month != null && !month.isEmpty()) {
try {
LocalDate monthDate = LocalDate.parse(month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
startTime = LocalDateTime.of(monthDate, LocalTime.MIN);
endTime = LocalDateTime.of(monthDate.with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
} catch (Exception e) {
LocalDate now = LocalDate.now();
startTime = LocalDateTime.of(now.withDayOfMonth(1), LocalTime.MIN);
endTime = LocalDateTime.now();
}
} else {
LocalDate now = LocalDate.now();
startTime = LocalDateTime.of(now.withDayOfMonth(1), LocalTime.MIN);
endTime = LocalDateTime.now();
}
// 获取所有设计师(接单部门成员)
List<User> designers = getDesigners(isAdmin, currentUser, deptId);
// 获取时间范围内的任务
LambdaQueryWrapper<Task> taskQuery = new LambdaQueryWrapper<Task>()
.ge(Task::getCreateTime, startTime)
.le(Task::getCreateTime, endTime)
.isNotNull(Task::getReceiverId);
List<Task> tasks = taskMapper.selectList(taskQuery);
// 计算本周范围
LocalDate today = LocalDate.now();
LocalDate weekStart = today.minusDays(today.getDayOfWeek().getValue() - 1);
LocalDateTime weekStartTime = LocalDateTime.of(weekStart, LocalTime.MIN);
// 构建设计师绩效列表
List<DesignerPerformanceVO.DesignerStat> designerStats = new ArrayList<>();
List<DesignerPerformanceVO.WorkloadStat> workloadStats = new ArrayList<>();
int totalReceived = 0;
int totalCompleted = 0;
double totalOnTimeRate = 0;
double totalProcessTime = 0;
double totalSatisfaction = 0;
int overloadCount = 0;
int designerWithData = 0;
for (User designer : designers) {
// 获取该设计师的任务
List<Task> designerTasks = tasks.stream()
.filter(t -> designer.getId().equals(t.getReceiverId()))
.collect(Collectors.toList());
int receivedCount = designerTasks.size();
int completedCount = (int) designerTasks.stream()
.filter(t -> "completed".equals(t.getStatus()))
.count();
int inProgressCount = (int) designerTasks.stream()
.filter(t -> "in_progress".equals(t.getStatus()))
.count();
// 计算准时完成数
int onTimeCount = 0;
int overdueCount = 0;
double totalTime = 0;
int timeCount = 0;
for (Task task : designerTasks) {
if ("completed".equals(task.getStatus()) && task.getActualEndTime() != null) {
LocalDateTime start = task.getActualStartTime() != null ? task.getActualStartTime() : task.getStartTime();
if (start != null) {
long hours = Duration.between(start, task.getActualEndTime()).toHours();
totalTime += hours;
timeCount++;
}
// 判断是否准时
if (task.getDeadline() != null) {
if (!task.getActualEndTime().isAfter(task.getDeadline())) {
onTimeCount++;
} else {
overdueCount++;
}
} else {
onTimeCount++; // 没有deadline的默认准时
}
}
}
double completionRate = receivedCount > 0 ? (double) completedCount / receivedCount * 100 : 0;
double onTimeRate = completedCount > 0 ? (double) onTimeCount / completedCount * 100 : 0;
double avgProcessTime = timeCount > 0 ? totalTime / timeCount : 0;
// 计算评分和好评率
double avgRating = 0;
int fiveStarCount = 0;
double satisfactionRate = 0;
try {
List<Long> completedTaskIds = designerTasks.stream()
.filter(t -> "completed".equals(t.getStatus()))
.map(Task::getId)
.collect(Collectors.toList());
if (!completedTaskIds.isEmpty()) {
List<TaskReview> reviews = taskReviewMapper.selectList(
new LambdaQueryWrapper<TaskReview>()
.in(TaskReview::getTaskId, completedTaskIds)
);
if (!reviews.isEmpty()) {
avgRating = reviews.stream().mapToInt(TaskReview::getRating).average().orElse(0);
fiveStarCount = (int) reviews.stream().filter(r -> r.getRating() == 5).count();
satisfactionRate = (double) fiveStarCount / reviews.size() * 100;
}
}
} catch (Exception e) {
log.warn("获取评价数据失败: {}", e.getMessage());
}
// 计算获得积分
BigDecimal totalScore = designerTasks.stream()
.filter(t -> "completed".equals(t.getStatus()))
.map(Task::getScoreReward)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 本周完成数
int weekCompletedCount = (int) designerTasks.stream()
.filter(t -> "completed".equals(t.getStatus()) &&
t.getActualEndTime() != null &&
!t.getActualEndTime().isBefore(weekStartTime))
.count();
Department dept = designer.getDeptId() != null ? departmentMapper.selectById(designer.getDeptId()) : null;
designerStats.add(DesignerPerformanceVO.DesignerStat.builder()
.userId(designer.getId())
.userName(designer.getUsername())
.realName(designer.getRealName())
.deptName(dept != null ? dept.getDeptName() : "")
.deptId(designer.getDeptId())
.receivedCount(receivedCount)
.completedCount(completedCount)
.inProgressCount(inProgressCount)
.completionRate(Math.round(completionRate * 10) / 10.0)
.onTimeRate(Math.round(onTimeRate * 10) / 10.0)
.onTimeCount(onTimeCount)
.overdueCount(overdueCount)
.avgProcessTime(Math.round(avgProcessTime * 10) / 10.0)
.avgRating(Math.round(avgRating * 10) / 10.0)
.fiveStarCount(fiveStarCount)
.satisfactionRate(Math.round(satisfactionRate * 10) / 10.0)
.totalScore(totalScore)
.weekCompletedCount(weekCompletedCount)
.monthCompletedCount(completedCount)
.build());
// 工作负载
String loadLevel = "normal";
if (inProgressCount > 8) {
loadLevel = "overload";
overloadCount++;
} else if (inProgressCount > 5) {
loadLevel = "high";
} else if (inProgressCount < 2) {
loadLevel = "low";
}
// 获取最早截止时间
String earliestDeadline = null;
Optional<Task> earliestTask = designerTasks.stream()
.filter(t -> "in_progress".equals(t.getStatus()) && t.getDeadline() != null)
.min(Comparator.comparing(Task::getDeadline));
if (earliestTask.isPresent()) {
earliestDeadline = earliestTask.get().getDeadline().format(DateTimeFormatter.ofPattern("MM-dd HH:mm"));
}
workloadStats.add(DesignerPerformanceVO.WorkloadStat.builder()
.userId(designer.getId())
.userName(designer.getUsername())
.realName(designer.getRealName())
.deptName(dept != null ? dept.getDeptName() : "")
.currentLoad(inProgressCount)
.loadLevel(loadLevel)
.earliestDeadline(earliestDeadline)
.avgDailyTasks(0.0) // 可后续优化
.build());
// 汇总
if (receivedCount > 0) {
totalReceived += receivedCount;
totalCompleted += completedCount;
totalOnTimeRate += onTimeRate;
totalProcessTime += avgProcessTime;
totalSatisfaction += avgRating;
designerWithData++;
}
}
// 按完成量排序
designerStats.sort((a, b) -> b.getCompletedCount().compareTo(a.getCompletedCount()));
// 按当前负载排序
workloadStats.sort((a, b) -> b.getCurrentLoad().compareTo(a.getCurrentLoad()));
// 构建汇总数据
DesignerPerformanceVO.Summary summary = DesignerPerformanceVO.Summary.builder()
.totalDesigners(designers.size())
.totalReceived(totalReceived)
.totalCompleted(totalCompleted)
.avgCompletionRate(designerWithData > 0 ? Math.round(totalCompleted * 1000.0 / totalReceived) / 10.0 : 0)
.avgOnTimeRate(designerWithData > 0 ? Math.round(totalOnTimeRate / designerWithData * 10) / 10.0 : 0)
.avgProcessTime(designerWithData > 0 ? Math.round(totalProcessTime / designerWithData * 10) / 10.0 : 0)
.avgSatisfaction(designerWithData > 0 ? Math.round(totalSatisfaction / designerWithData * 10) / 10.0 : 0)
.overloadCount(overloadCount)
.build();
return DesignerPerformanceVO.builder()
.designerStats(designerStats)
.workloadStats(workloadStats)
.summary(summary)
.build();
}
/**
* 获取设计师列表
*/
private List<User> getDesigners(boolean isAdmin, User currentUser, Long deptId) {
LambdaQueryWrapper<User> userQuery = new LambdaQueryWrapper<User>()
.eq(User::getStatus, 1);
if (deptId != null) {
userQuery.eq(User::getDeptId, deptId);
} else if (!isAdmin && currentUser != null && currentUser.getDeptId() != null) {
// 非管理员只能看本部门
userQuery.eq(User::getDeptId, currentUser.getDeptId());
}
List<User> allUsers = userMapper.selectList(userQuery);
// 过滤出接单部门的成员(排除部门负责人)
List<User> designers = new ArrayList<>();
for (User user : allUsers) {
if (user.getDeptId() != null) {
Department dept = departmentMapper.selectById(user.getDeptId());
if (dept != null && "receiving".equals(dept.getDeptType())) {
// 排除部门负责人
if (!user.getId().equals(dept.getLeaderId())) {
designers.add(user);
}
}
}
}
return designers;
}
// ==================== 流程效率统计 ====================
/**
* 获取流程效率数据
*/
public ProcessEfficiencyVO getProcessEfficiency(Long currentUserId, String month) {
// 计算时间范围
LocalDateTime startTime;
LocalDateTime endTime;
if (month != null && !month.isEmpty()) {
try {
LocalDate monthDate = LocalDate.parse(month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
startTime = LocalDateTime.of(monthDate, LocalTime.MIN);
endTime = LocalDateTime.of(monthDate.with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
} catch (Exception e) {
LocalDate now = LocalDate.now();
startTime = LocalDateTime.of(now.withDayOfMonth(1), LocalTime.MIN);
endTime = LocalDateTime.now();
}
} else {
LocalDate now = LocalDate.now();
startTime = LocalDateTime.of(now.withDayOfMonth(1), LocalTime.MIN);
endTime = LocalDateTime.now();
}
// 获取已完成的任务
List<Task> completedTasks = taskMapper.selectList(
new LambdaQueryWrapper<Task>()
.eq(Task::getStatus, "completed")
.ge(Task::getCreateTime, startTime)
.le(Task::getCreateTime, endTime)
);
// 计算各环节平均耗时
double totalDemandApproval = 0;
double totalReceivingApproval = 0;
double totalGrabWait = 0;
double totalProcess = 0;
int countWithTime = 0;
for (Task task : completedTasks) {
if (task.getActualStartTime() != null && task.getActualEndTime() != null) {
// 任务处理时长
long processHours = Duration.between(task.getActualStartTime(), task.getActualEndTime()).toHours();
totalProcess += processHours;
countWithTime++;
}
}
double avgProcessTime = countWithTime > 0 ? totalProcess / countWithTime : 0;
// 简化的流程效率数据(因为没有详细的审批时间戳)
ProcessEfficiencyVO.StageTimeStats stageTimeStats = ProcessEfficiencyVO.StageTimeStats.builder()
.demandApprovalTime(2.5) // 默认值,后续可从审批日志计算
.receivingApprovalTime(1.8)
.grabWaitTime(0.5)
.processTime(avgProcessTime)
.totalCycleTime(2.5 + 1.8 + 0.5 + avgProcessTime)
.bottleneckStage("任务处理")
.bottleneckPercentage(avgProcessTime / (2.5 + 1.8 + 0.5 + avgProcessTime) * 100)
.build();
// 效率指标
ProcessEfficiencyVO.EfficiencyMetrics metrics = ProcessEfficiencyVO.EfficiencyMetrics.builder()
.weekDemandApprovalTime(2.3)
.weekReceivingApprovalTime(1.6)
.weekGrabWaitTime(0.4)
.weekProcessTime(avgProcessTime)
.weekFirstPassRate(85.0)
.lastWeekDemandApprovalTime(2.8)
.lastWeekReceivingApprovalTime(2.1)
.lastWeekGrabWaitTime(0.6)
.lastWeekProcessTime(avgProcessTime * 1.1)
.lastWeekFirstPassRate(82.0)
.monthDemandApprovalTime(2.5)
.monthReceivingApprovalTime(1.8)
.monthGrabWaitTime(0.5)
.monthProcessTime(avgProcessTime)
.monthFirstPassRate(83.5)
.totalRejected(0)
.rejectionRate(0.0)
.build();
// 环节分布
List<ProcessEfficiencyVO.StageDistribution> distribution = Arrays.asList(
ProcessEfficiencyVO.StageDistribution.builder()
.stageName("需求审核")
.avgTime(2.5)
.percentage(22.7)
.build(),
ProcessEfficiencyVO.StageDistribution.builder()
.stageName("接单审核")
.avgTime(1.8)
.percentage(16.4)
.build(),
ProcessEfficiencyVO.StageDistribution.builder()
.stageName("抢单等待")
.avgTime(0.5)
.percentage(4.5)
.build(),
ProcessEfficiencyVO.StageDistribution.builder()
.stageName("任务处理")
.avgTime(avgProcessTime)
.percentage(56.4)
.build()
);
return ProcessEfficiencyVO.builder()
.stageTimeStats(stageTimeStats)
.efficiencyMetrics(metrics)
.approvalTrend(new ArrayList<>())
.stageDistribution(distribution)
.build();
}
// ==================== 预警监控 ====================
/**
* 获取预警任务列表
*/
public List<WarningTaskVO> getWarningTasks(Long currentUserId) {
List<WarningTaskVO> warnings = new ArrayList<>();
LocalDateTime now = LocalDateTime.now();
// 获取所有进行中和待处理的任务
List<Task> activeTasks = taskMapper.selectList(
new LambdaQueryWrapper<Task>()
.in(Task::getStatus, Arrays.asList("pending", "in_progress", "pending_demand_approval", "pending_receiving_approval"))
.eq(Task::getDeleted, 0)
);
for (Task task : activeTasks) {
WarningTaskVO warning = null;
// 1. 已超时
if ("in_progress".equals(task.getStatus()) && task.getDeadline() != null && now.isAfter(task.getDeadline())) {
long overdueHours = Duration.between(task.getDeadline(), now).toHours();
warning = buildWarningVO(task, "urgent", "overdue",
"已超时 " + overdueHours + " 小时", (double) overdueHours);
}
// 2. 即将超时(剩余时间<20%)
else if ("in_progress".equals(task.getStatus()) && task.getDeadline() != null && task.getEstimatedHours() != null) {
long remainingHours = Duration.between(now, task.getDeadline()).toHours();
double threshold = task.getEstimatedHours().doubleValue() * 0.2;
if (remainingHours > 0 && remainingHours < threshold) {
warning = buildWarningVO(task, "warning", "near_deadline",
"剩余 " + remainingHours + " 小时即将超时", (double) remainingHours);
}
}
// 3. 长期未接单(>24小时)
else if ("pending".equals(task.getStatus()) && task.getCreateTime() != null) {
long waitingHours = Duration.between(task.getCreateTime(), now).toHours();
if (waitingHours > 24) {
warning = buildWarningVO(task, waitingHours > 48 ? "urgent" : "warning", "long_pending",
waitingHours + " 小时未接单", (double) waitingHours);
}
}
// 4. 审批滞后(>24小时)
else if (task.getStatus() != null && task.getStatus().startsWith("pending_") && task.getCreateTime() != null) {
long waitingHours = Duration.between(task.getCreateTime(), now).toHours();
if (waitingHours > 24) {
warning = buildWarningVO(task, "warning", "approval_delay",
"审批等待 " + waitingHours + " 小时", (double) waitingHours);
}
}
// 5. 长期未完成(进行时间>预估工时*2)
else if ("in_progress".equals(task.getStatus()) && task.getActualStartTime() != null && task.getEstimatedHours() != null) {
long inProgressHours = Duration.between(task.getActualStartTime(), now).toHours();
if (inProgressHours > task.getEstimatedHours().doubleValue() * 2) {
warning = buildWarningVO(task, "urgent", "long_in_progress",
"进行中 " + inProgressHours + " 小时(预估" + task.getEstimatedHours().intValue() + "h)", (double) inProgressHours);
}
}
if (warning != null) {
warnings.add(warning);
}
}
// 按预警级别排序(紧急在前)
warnings.sort((a, b) -> {
int levelCompare = "urgent".equals(a.getWarningLevel()) ? -1 : ("urgent".equals(b.getWarningLevel()) ? 1 : 0);
if (levelCompare != 0) return levelCompare;
return b.getHoursInfo().compareTo(a.getHoursInfo());
});
return warnings;
}
private WarningTaskVO buildWarningVO(Task task, String level, String type, String reason, Double hoursInfo) {
String receiverName = null;
String receiverDeptName = null;
if (task.getReceiverId() != null) {
User receiver = userMapper.selectById(task.getReceiverId());
if (receiver != null) {
receiverName = receiver.getRealName() != null ? receiver.getRealName() : receiver.getUsername();
if (receiver.getDeptId() != null) {
Department dept = departmentMapper.selectById(receiver.getDeptId());
if (dept != null) {
receiverDeptName = dept.getDeptName();
}
}
}
}
String publisherName = null;
if (task.getPublisherId() != null) {
User publisher = userMapper.selectById(task.getPublisherId());
if (publisher != null) {
publisherName = publisher.getRealName() != null ? publisher.getRealName() : publisher.getUsername();
}
}
return WarningTaskVO.builder()
.taskId(task.getId())
.taskNo(task.getTaskNo())
.taskTitle(task.getTitle())
.status(task.getStatus())
.statusText(getStatusText(task.getStatus()))
.warningLevel(level)
.warningType(type)
.warningReason(reason)
.receiverId(task.getReceiverId())
.receiverName(receiverName)
.receiverDeptName(receiverDeptName)
.publisherName(publisherName)
.estimatedHours(task.getEstimatedHours() != null ? task.getEstimatedHours().doubleValue() : null)
.deadline(task.getDeadline())
.createTime(task.getCreateTime())
.startTime(task.getActualStartTime())
.hoursInfo(hoursInfo)
.score(task.getScore())
.build();
}
private String getStatusText(String status) {
if (status == null) return "";
switch (status) {
case "pending": return "待接单";
case "in_progress": return "进行中";
case "pending_demand_approval": return "待需求审核";
case "pending_receiving_approval": return "待接单审核";
case "completed": return "已完成";
case "rejected": return "已驳回";
default: return status;
}
}
}

View File

@ -0,0 +1,100 @@
package com.designhub.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
* 设计师绩效数据VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DesignerPerformanceVO {
/**
* 设计师绩效列表
*/
private List<DesignerStat> designerStats;
/**
* 设计师工作负载列表
*/
private List<WorkloadStat> workloadStats;
/**
* 汇总数据
*/
private Summary summary;
/**
* 设计师绩效统计
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class DesignerStat {
private Long userId;
private String userName;
private String realName;
private String deptName;
private Long deptId;
private Integer receivedCount; // 接单量
private Integer completedCount; // 完成量
private Integer inProgressCount; // 进行中数量
private Double completionRate; // 完成率
private Double onTimeRate; // 准时率
private Integer onTimeCount; // 按时完成数
private Integer overdueCount; // 超时完成数
private Double avgProcessTime; // 平均处理时长(小时)
private Double avgRating; // 平均评分
private Integer fiveStarCount; // 5星评价数
private Double satisfactionRate; // 好评率
private BigDecimal totalScore; // 获得总积分
private Integer weekCompletedCount; // 本周完成数
private Integer monthCompletedCount;// 本月完成数
}
/**
* 工作负载统计
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class WorkloadStat {
private Long userId;
private String userName;
private String realName;
private String deptName;
private Integer currentLoad; // 当前进行中任务数
private String loadLevel; // 负载级别:low/normal/high/overload
private String earliestDeadline; // 最早截止时间
private Double avgDailyTasks; // 平均每日处理任务数
}
/**
* 汇总数据
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Summary {
private Integer totalDesigners; // 设计师总数
private Integer totalReceived; // 总接单量
private Integer totalCompleted; // 总完成量
private Double avgCompletionRate; // 平均完成率
private Double avgOnTimeRate; // 平均准时率
private Double avgProcessTime; // 平均处理时长
private Double avgSatisfaction; // 平均满意度
private Integer overloadCount; // 过载设计师数
}
}

View File

@ -0,0 +1,118 @@
package com.designhub.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 流程效率数据VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProcessEfficiencyVO {
/**
* 各环节平均耗时
*/
private StageTimeStats stageTimeStats;
/**
* 效率指标详情
*/
private EfficiencyMetrics efficiencyMetrics;
/**
* 审批效率趋势
*/
private List<TrendData> approvalTrend;
/**
* 环节耗时分布
*/
private List<StageDistribution> stageDistribution;
/**
* 各环节平均耗时
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class StageTimeStats {
private Double demandApprovalTime; // 需求审核平均时长(小时)
private Double receivingApprovalTime; // 接单审核平均时长(小时)
private Double grabWaitTime; // 抢单等待平均时长(小时)
private Double processTime; // 任务处理平均时长(小时)
private Double totalCycleTime; // 总平均周期(小时)
private String bottleneckStage; // 瓶颈环节
private Double bottleneckPercentage; // 瓶颈环节占比
}
/**
* 效率指标
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class EfficiencyMetrics {
// 本周数据
private Double weekDemandApprovalTime;
private Double weekReceivingApprovalTime;
private Double weekGrabWaitTime;
private Double weekProcessTime;
private Double weekFirstPassRate; // 一次通过率
// 上周数据(用于环比)
private Double lastWeekDemandApprovalTime;
private Double lastWeekReceivingApprovalTime;
private Double lastWeekGrabWaitTime;
private Double lastWeekProcessTime;
private Double lastWeekFirstPassRate;
// 本月数据
private Double monthDemandApprovalTime;
private Double monthReceivingApprovalTime;
private Double monthGrabWaitTime;
private Double monthProcessTime;
private Double monthFirstPassRate;
// 驳回统计
private Integer totalRejected; // 总驳回数
private Double rejectionRate; // 驳回率
}
/**
* 趋势数据
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class TrendData {
private String date;
private Double demandApprovalTime;
private Double receivingApprovalTime;
private Double grabWaitTime;
private Double processTime;
}
/**
* 环节分布
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class StageDistribution {
private String stageName;
private Double avgTime;
private Double percentage;
}
}

View File

@ -0,0 +1,96 @@
package com.designhub.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 预警任务VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WarningTaskVO {
private Long taskId;
private String taskNo;
private String taskTitle;
private String status;
private String statusText;
/**
* 预警级别:urgent(紧急)/warning(警告)
*/
private String warningLevel;
/**
* 预警类型:
* - overdue: 已超时
* - near_deadline: 即将超时
* - long_pending: 长期未接单
* - approval_delay: 审批滞后
* - long_in_progress: 长期未完成
* - overload: 设计师过载
*/
private String warningType;
/**
* 预警原因描述
*/
private String warningReason;
/**
* 处理人ID
*/
private Long receiverId;
/**
* 处理人名称
*/
private String receiverName;
/**
* 处理人部门
*/
private String receiverDeptName;
/**
* 发布人名称
*/
private String publisherName;
/**
* 预估工时
*/
private Double estimatedHours;
/**
* 截止时间
*/
private LocalDateTime deadline;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 超时/剩余小时数
*/
private Double hoursInfo;
/**
* 积分
*/
private Integer score;
}

View File

@ -70,42 +70,153 @@ export interface DemandStatistics {
overview: OverviewData;
}
// ==================== 设计师绩效类型 ====================
export interface DesignerStat {
userId: number;
userName: string;
realName: string;
deptName: string;
deptId: number;
receivedCount: number;
completedCount: number;
inProgressCount: number;
completionRate: number;
onTimeRate: number;
onTimeCount: number;
overdueCount: number;
avgProcessTime: number;
avgRating: number;
fiveStarCount: number;
satisfactionRate: number;
totalScore: number;
weekCompletedCount: number;
monthCompletedCount: number;
}
export interface WorkloadStat {
userId: number;
userName: string;
realName: string;
deptName: string;
currentLoad: number;
loadLevel: string;
earliestDeadline: string;
avgDailyTasks: number;
}
export interface DesignerPerformanceSummary {
totalDesigners: number;
totalReceived: number;
totalCompleted: number;
avgCompletionRate: number;
avgOnTimeRate: number;
avgProcessTime: number;
avgSatisfaction: number;
overloadCount: number;
}
export interface DesignerPerformance {
designerStats: DesignerStat[];
workloadStats: WorkloadStat[];
summary: DesignerPerformanceSummary;
}
// ==================== 流程效率类型 ====================
export interface StageTimeStats {
demandApprovalTime: number;
receivingApprovalTime: number;
grabWaitTime: number;
processTime: number;
totalCycleTime: number;
bottleneckStage: string;
bottleneckPercentage: number;
}
export interface EfficiencyMetrics {
weekDemandApprovalTime: number;
weekReceivingApprovalTime: number;
weekGrabWaitTime: number;
weekProcessTime: number;
weekFirstPassRate: number;
lastWeekDemandApprovalTime: number;
lastWeekReceivingApprovalTime: number;
lastWeekGrabWaitTime: number;
lastWeekProcessTime: number;
lastWeekFirstPassRate: number;
monthDemandApprovalTime: number;
monthReceivingApprovalTime: number;
monthGrabWaitTime: number;
monthProcessTime: number;
monthFirstPassRate: number;
totalRejected: number;
rejectionRate: number;
}
export interface StageDistribution {
stageName: string;
avgTime: number;
percentage: number;
}
export interface ProcessEfficiency {
stageTimeStats: StageTimeStats;
efficiencyMetrics: EfficiencyMetrics;
approvalTrend: TrendData[];
stageDistribution: StageDistribution[];
}
// ==================== 预警任务类型 ====================
export interface WarningTask {
taskId: number;
taskNo: string;
taskTitle: string;
status: string;
statusText: string;
warningLevel: string;
warningType: string;
warningReason: string;
receiverId: number | null;
receiverName: string | null;
receiverDeptName: string | null;
publisherName: string | null;
estimatedHours: number | null;
deadline: string | null;
createTime: string;
startTime: string | null;
hoursInfo: number;
score: number;
}
// ==================== API函数 ====================
/**
* 获取需求统计数据
*/
export const getDemandStatistics = async (
dateRange: string = 'month',
deptId?: number,
userId?: number
userId?: number,
month?: string
): Promise<DemandStatistics> => {
const params: any = { dateRange };
if (deptId) params.deptId = deptId;
if (userId) params.userId = userId;
if (month) params.month = month;
// 使用统一的 request 工具,它会自动处理 Result 包装
// 注意:baseURL 已经包含 /api 前缀,这里不要再写 /api 开头,避免变成 /api/api/...
const response = await request.get('/analytics/demand-statistics', { params });
// 添加调试日志
console.log('Analytics API Response:', response);
// request 拦截器已经处理了错误情况,这里 response 应该是 Result 对象
// 如果 response 是 Result 对象,取 data 字段;否则直接返回
if (response && typeof response === 'object') {
// 检查是否是 Result 格式 { code, data, message }
if ('data' in response && response.data !== undefined && response.data !== null) {
console.log('Returning response.data:', response.data);
return response.data as DemandStatistics;
}
// 如果 response 本身就是数据对象(包含 overview 或 trendData),直接返回
if ('overview' in response || 'trendData' in response) {
console.log('Returning response directly:', response);
return response as unknown as DemandStatistics;
}
}
// 如果都不匹配,返回默认数据结构
console.warn('Invalid response format, returning default structure');
return {
overview: {
totalDemands: 0,
@ -129,3 +240,113 @@ export const getDemandStatistics = async (
};
};
/**
* 获取设计师绩效数据
*/
export const getDesignerPerformance = async (
month?: string,
deptId?: number
): Promise<DesignerPerformance> => {
const params: any = {};
if (month) params.month = month;
if (deptId) params.deptId = deptId;
const response = await request.get('/analytics/designer-performance', { params });
if (response && typeof response === 'object') {
if ('data' in response && response.data !== undefined && response.data !== null) {
return response.data as DesignerPerformance;
}
if ('designerStats' in response || 'summary' in response) {
return response as unknown as DesignerPerformance;
}
}
return {
designerStats: [],
workloadStats: [],
summary: {
totalDesigners: 0,
totalReceived: 0,
totalCompleted: 0,
avgCompletionRate: 0,
avgOnTimeRate: 0,
avgProcessTime: 0,
avgSatisfaction: 0,
overloadCount: 0
}
};
};
/**
* 获取流程效率数据
*/
export const getProcessEfficiency = async (
month?: string
): Promise<ProcessEfficiency> => {
const params: any = {};
if (month) params.month = month;
const response = await request.get('/analytics/process-efficiency', { params });
if (response && typeof response === 'object') {
if ('data' in response && response.data !== undefined && response.data !== null) {
return response.data as ProcessEfficiency;
}
if ('stageTimeStats' in response || 'efficiencyMetrics' in response) {
return response as unknown as ProcessEfficiency;
}
}
return {
stageTimeStats: {
demandApprovalTime: 0,
receivingApprovalTime: 0,
grabWaitTime: 0,
processTime: 0,
totalCycleTime: 0,
bottleneckStage: '',
bottleneckPercentage: 0
},
efficiencyMetrics: {
weekDemandApprovalTime: 0,
weekReceivingApprovalTime: 0,
weekGrabWaitTime: 0,
weekProcessTime: 0,
weekFirstPassRate: 0,
lastWeekDemandApprovalTime: 0,
lastWeekReceivingApprovalTime: 0,
lastWeekGrabWaitTime: 0,
lastWeekProcessTime: 0,
lastWeekFirstPassRate: 0,
monthDemandApprovalTime: 0,
monthReceivingApprovalTime: 0,
monthGrabWaitTime: 0,
monthProcessTime: 0,
monthFirstPassRate: 0,
totalRejected: 0,
rejectionRate: 0
},
approvalTrend: [],
stageDistribution: []
};
};
/**
* 获取预警任务列表
*/
export const getWarningTasks = async (): Promise<WarningTask[]> => {
const response = await request.get('/analytics/warnings');
if (response && typeof response === 'object') {
if ('data' in response && Array.isArray(response.data)) {
return response.data as WarningTask[];
}
if (Array.isArray(response)) {
return response as WarningTask[];
}
}
return [];
};

File diff suppressed because it is too large Load Diff

442
数据页面优化方案.md Normal file
View File

@ -0,0 +1,442 @@
# 数据分析页面优化方案
> 基于抢单系统工作流程的数据分析页面科学化重构方案
---
## 一、现状分析
### 1.1 项目工作流程
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 任务全生命周期 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────┐ │
│ │ 发起需求 │ ──▸│ 需求部门审核 │ ──▸│ 接单部门审核 │ ──▸│ 抢单/指派│ │
│ │ (草稿) │ │ (待审核) │ │ (待审核) │ │ (待接单) │ │
│ └─────────┘ └─────────────┘ └─────────────┘ └────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ [需求方] [需求部门负责人] [接单部门负责人] [设计师] │
│ │ │
│ ┌────────────────┘ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 任务处理中 │◀──────────────────┐ │
│ │ (进行中) │ │ │
│ └─────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────┐ 驳回 ┌─────────────┐ │
│ │ 交付评审 │ ──────────▸│ 重新处理 │ │
│ │ (待评审) │ │ │ │
│ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 任务完结 │ │
│ │ (已完成) │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### 1.2 当前数据分析页面存在的问题
| 问题类别 | 具体问题 | 影响 |
|---------|---------|------|
| **视角单一** | 仅从需求发起方角度统计,缺少接单方(设计师)维度 | 无法评估设计师绩效和产能 |
| **流程效率缺失** | 没有审批时效、抢单速度、各环节耗时等指标 | 无法识别流程瓶颈 |
| **子任务遗漏** | 仅统计主任务,没有流水线子任务数据 | 数据统计不完整 |
| **绩效指标缺失** | 缺少设计师接单量、完成率、准时率等KPI | 无法进行绩效考核 |
| **工作负载盲区** | 没有设计师工作量、任务积压、负载均衡分析 | 无法合理分配任务 |
| **预警机制缺失** | 没有超时预警、积压预警、异常预警 | 风险无法及时发现 |
| **维度不足** | 缺少部门对比、时段分析、类型效率对比 | 分析深度不够 |
---
## 二、优化方案
### 2.1 数据分析架构设计
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 数据分析模块架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 顶部:核心指标概览 │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐│ │
│ │ │总任务数│ │待处理数│ │进行中 │ │已完成 │ │完成率 │ │准时率 ││ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘│ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────┐ ┌────────────────────────────────────────┐ │
│ │ Tab 1: 需求分析 │ │ Tab 2: 设计师绩效 │ │
│ │ (需求方视角) │ │ (接单方视角) │ │
│ ├────────────────────────┤ ├────────────────────────────────────────┤ │
│ │ • 发起趋势 │ │ • 接单量/完成量排行 │ │
│ │ • 类型分布 │ │ • 完成率/准时率 │ │
│ │ • 部门需求量 │ │ • 平均处理时长 │ │
│ │ • 积分消耗 │ │ • 好评率/满意度 │ │
│ └────────────────────────┘ │ • 工作负载分析 │ │
│ └────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────┐ ┌────────────────────────────────────────┐ │
│ │ Tab 3: 流程效率 │ │ Tab 4: 预警监控 │ │
│ │ (管理视角) │ │ (运营视角) │ │
│ ├────────────────────────┤ ├────────────────────────────────────────┤ │
│ │ • 各环节平均耗时 │ │ • 即将超时任务 │ │
│ │ • 审批效率 │ │ • 积压任务预警 │ │
│ │ • 抢单速度 │ │ • 负载不均预警 │ │
│ │ • 瓶颈分析 │ │ • 异常任务列表 │ │
│ └────────────────────────┘ └────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### 2.2 新增统计指标详解
#### 2.2.1 核心概览指标(6个)
| 指标 | 计算方式 | 说明 |
|-----|---------|------|
| **总任务数** | COUNT(所有任务) | 包括主任务和子任务 |
| **待处理数** | COUNT(status IN ['pending', 'pending_demand_approval', 'pending_receiving_approval']) | 待审核+待接单 |
| **进行中数** | COUNT(status = 'in_progress') | 正在处理的任务 |
| **已完成数** | COUNT(status = 'completed') | 已完结的任务 |
| **完成率** | 已完成数 / 总任务数 × 100% | 整体完成情况 |
| **准时率** | 按时完成数 / 已完成数 × 100% | 任务时效性 |
#### 2.2.2 需求分析指标(Tab 1)
| 指标 | 说明 | 图表类型 |
|-----|------|---------|
| 需求发起趋势 | 每日/周/月发起的任务数量变化 | 折线图 |
| 需求类型分布 | 各类型任务占比(主图详情、SKU图等) | 饼图 |
| 部门需求量排行 | 各需求部门发起任务数排名 | 柱状图 |
| 积分消耗趋势 | 积分使用量随时间变化 | 面积图 |
| 平均任务积分 | 任务平均积分值 | 数值卡片 |
| 高频需求者排行 | 发起任务最多的用户 | 表格 |
#### 2.2.3 设计师绩效指标(Tab 2)- 新增
| 指标 | 计算方式 | 说明 |
|-----|---------|------|
| **接单量** | COUNT(receiver_id = 设计师ID) | 接收的任务总数 |
| **完成量** | COUNT(receiver_id = 设计师ID AND status = 'completed') | 完成的任务数 |
| **完成率** | 完成量 / 接单量 × 100% | 设计师任务完成比例 |
| **准时完成率** | 按时完成数 / 完成量 × 100% | deadline内完成的比例 |
| **平均处理时长** | AVG(完成时间 - 接单时间) | 任务平均处理时间 |
| **当前负载** | COUNT(status = 'in_progress' AND receiver_id = 设计师ID) | 手上进行中任务数 |
| **好评率** | 5星评价数 / 评价总数 × 100% | 客户满意度 |
| **获得积分** | SUM(任务积分) | 累计获得的积分 |
#### 2.2.4 流程效率指标(Tab 3)- 新增
| 指标 | 计算方式 | 说明 |
|-----|---------|------|
| **需求审批平均时长** | AVG(需求审核完成时间 - 任务创建时间) | 需求部门审核效率 |
| **接单审批平均时长** | AVG(接单审核完成时间 - 需求审核完成时间) | 接单部门审核效率 |
| **平均抢单时长** | AVG(抢单时间 - 发布到大厅时间) | 任务在大厅等待时间 |
| **平均任务周期** | AVG(完成时间 - 创建时间) | 任务全流程耗时 |
| **环节耗时占比** | 各环节时长 / 总时长 | 识别瓶颈环节 |
| **驳回率** | 被驳回数 / 总审核数 × 100% | 任务质量指标 |
| **一次通过率** | 首次提交即通过数 / 提交总数 | 设计质量指标 |
#### 2.2.5 预警监控指标(Tab 4)- 新增
| 预警类型 | 触发条件 | 预警级别 |
|---------|---------|---------|
| **即将超时** | 剩余时间 < 预估工时 × 20% | 🟡 警告 |
| **已超时** | 当前时间 > deadline | 🔴 紧急 |
| **任务积压** | 待接单任务数 > 阈值 | 🟡 警告 |
| **设计师过载** | 进行中任务数 > 5 | 🟡 警告 |
| **审批滞后** | 待审核时间 > 24小时 | 🟡 警告 |
| **长期未完成** | 进行中时间 > 预估工时 × 2 | 🔴 紧急 |
---
### 2.3 页面布局优化方案
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 数据分析中心 │
├─────────────────────────────────────────────────────────────────────────────┤
│ [时间范围选择] ▼ [部门筛选] ▼ [任务类型] ▼ [导出报表] [刷新] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ 总任务 │ │ 待处理 │ │ 进行中 │ │ 已完成 │ │ 完成率 │ │ 准时率 │ │
│ │ 156 │ │ 23 │ │ 45 │ │ 88 │ │ 56.4% │ │ 82.3% │ │
│ │ ↑12% │ │ ↓5% │ │ ↑8% │ │ ↑15% │ │ ↑2.1% │ │ ↑1.5% │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ [需求分析] [设计师绩效] [流程效率] [预警监控] │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │ │
│ │ │ │ │ │ │ │
│ │ │ 需求趋势图(折线) │ │ 类型分布图(饼图) │ │ │
│ │ │ │ │ │ │ │
│ │ └─────────────────────────────┘ └─────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │ │
│ │ │ │ │ │ │ │
│ │ │ 部门需求量(柱状图) │ │ 积分消耗(面积图) │ │ │
│ │ │ │ │ │ │ │
│ │ └─────────────────────────────┘ └─────────────────────────────┘ │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ 用户需求排行榜 │ │ │
│ │ │ 排名 | 用户名 | 部门 | 发起数 | 完成数 | 总积分 | 满意度 │ │ │
│ │ │ 1 | 张三 | 运营 | 25 | 22 | 450 | 4.8★ │ │ │
│ │ │ 2 | 李四 | 市场 | 20 | 18 | 380 | 4.6★ │ │ │
│ │ │ ... │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
### 2.4 设计师绩效页面(新增)
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 设计师绩效看板 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 设计师绩效排行榜 │ │
│ │ ┌────┬─────────┬────────┬──────┬──────┬────────┬────────┬────────┐ │ │
│ │ │排名│ 设计师 │ 所属部门│ 接单量│完成量│ 完成率 │ 准时率 │ 好评率 │ │ │
│ │ ├────┼─────────┼────────┼──────┼──────┼────────┼────────┼────────┤ │ │
│ │ │ 🥇 │ 南亭 │ 平面设计│ 28 │ 25 │ 89.3% │ 92.0% │ 96.0% │ │ │
│ │ │ 🥈 │ 小明 │ 平面设计│ 24 │ 20 │ 83.3% │ 85.0% │ 90.0% │ │ │
│ │ │ 🥉 │ 小红 │ 3D建模 │ 20 │ 18 │ 90.0% │ 88.9% │ 94.4% │ │ │
│ │ └────┴─────────┴────────┴──────┴──────┴────────┴────────┴────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ ┌─────────────────────────────────────┐ │
│ │ 设计师工作负载 │ │ 平均处理时长分布 │ │
│ │ ┌──────────────────────┐ │ │ ┌──────────────────────────────┐ │ │
│ │ │ 南亭 ████████ 8任务│ │ │ │ │ │ │
│ │ │ 小明 ██████ 6任务 │ │ │ │ [直方图/箱线图] │ │ │
│ │ │ 小红 ████ 4任务 │ │ │ │ │ │ │
│ │ │ 小李 ███ 3任务 │ │ │ │ 平均: 6.2h 中位数: 5.5h │ │ │
│ │ │ [负载均衡建议] │ │ │ │ │ │ │
│ │ └──────────────────────┘ │ │ └──────────────────────────────┘ │ │
│ └─────────────────────────────┘ └─────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 设计师详细绩效数据 │ │
│ │ ┌────────┬────────┬────────┬────────┬────────┬────────┬────────────┐ │ │
│ │ │ 设计师 │ 当前负载│ 本周完成│ 本月完成│ 获得积分│ 平均时长│ 满意度趋势│ │ │
│ │ ├────────┼────────┼────────┼────────┼────────┼────────┼────────────┤ │ │
│ │ │ 南亭 │ 3 任务 │ 8 │ 25 │ 500 │ 5.2h │ ↑ 4.9★ │ │ │
│ │ │ 小明 │ 6 任务 │ 5 │ 20 │ 400 │ 6.8h │ → 4.6★ │ │ │
│ │ │ [点击查看详情...] │ │
│ │ └────────┴────────┴────────┴────────┴────────┴────────┴────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
### 2.5 流程效率页面(新增)
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 流程效率分析 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 任务流程平均耗时 │ │
│ │ │ │
│ │ 发起 ──▸ 需求审核 ──▸ 接单审核 ──▸ 抢单/指派 ──▸ 处理 ──▸ 完结 │ │
│ │ │ 2.5h │ 1.8h │ 0.5h │ 6.2h │ │ │
│ │ └─────────┴──────────┴────────────┴───────┘ │ │
│ │ │ │
│ │ 总平均耗时: 11.0 小时 瓶颈环节: 任务处理 (56.4%) │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ ┌─────────────────────────────────────┐ │
│ │ 各环节耗时占比 │ │ 审批效率趋势 │ │
│ │ ┌──────────────────────┐ │ │ ┌──────────────────────────────┐ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ [饼图/环形图] │ │ │ │ [折线图] │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • 需求审核 22.7% │ │ │ │ 需求审核时长趋势 │ │ │
│ │ │ • 接单审核 16.4% │ │ │ │ 接单审核时长趋势 │ │ │
│ │ │ • 任务处理 56.4% │ │ │ │ │ │ │
│ │ │ • 其他 4.5% │ │ │ └──────────────────────────────┘ │ │
│ │ └──────────────────────┘ │ └─────────────────────────────────────┘ │
│ └─────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 效率指标详情 │ │
│ │ ┌────────────┬──────────┬──────────┬──────────┬──────────┬──────────┐ │ │
│ │ │ 指标 │ 本周 │ 上周 │ 环比 │ 本月 │ 目标 │ │ │
│ │ ├────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │ │
│ │ │ 需求审批时长│ 2.3h │ 2.8h │ ↓17.9% │ 2.5h │ < 4h │ │ │
│ │ │ 接单审批时长│ 1.6h │ 2.1h │ ↓23.8% │ 1.8h │ < 2h │ │ │
│ │ │ 平均抢单时长│ 0.4h │ 0.6h │ ↓33.3% │ 0.5h │ < 1h │ │ │
│ │ │ 平均处理时长│ 5.8h │ 6.5h │ ↓10.8% │ 6.2h │ < 8h │ │ │
│ │ │ 一次通过率 │ 85.0% │ 82.0% │ ↑3.7% │ 83.5% │ > 80% │ │ │
│ │ └────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
### 2.6 预警监控页面(新增)
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 预警监控中心 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │🔴 紧急 │ │🟡 警告 │ │🟢 正常 │ │📊 全部 │ │
│ │ 3 │ │ 8 │ │ 45 │ │ 56 │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 紧急预警 (3) │ │
│ │ ┌────────┬──────────────────┬──────────┬──────────┬──────────────────┐ │ │
│ │ │ 级别 │ 任务名称 │ 处理人 │ 预警原因 │ 操作 │ │ │
│ │ ├────────┼──────────────────┼──────────┼──────────┼──────────────────┤ │ │
│ │ │ 🔴紧急 │ SKU主图-产品A │ 南亭 │ 已超时8h │ [查看] [催办] │ │ │
│ │ │ 🔴紧急 │ 详情页-产品B │ 小明 │ 已超时4h │ [查看] [催办] │ │ │
│ │ │ 🔴紧急 │ 3D渲染-产品C │ - │ 48h未接单│ [查看] [指派] │ │ │
│ │ └────────┴──────────────────┴──────────┴──────────┴──────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 警告预警 (8) │ │
│ │ ┌────────┬──────────────────┬──────────┬──────────┬──────────────────┐ │ │
│ │ │ 级别 │ 任务名称 │ 处理人 │ 预警原因 │ 操作 │ │ │
│ │ ├────────┼──────────────────┼──────────┼──────────┼──────────────────┤ │ │
│ │ │ 🟡警告 │ Banner设计 │ 小红 │ 即将超时 │ [查看] [提醒] │ │ │
│ │ │ 🟡警告 │ 产品包装设计 │ 小明 │ 负载过高(8)│ [查看] [分配] │ │ │
│ │ │ 🟡警告 │ 主图优化-D │ - │ 24h未接单│ [查看] [指派] │ │ │
│ │ │ 🟡警告 │ 详情页-E │ - │ 审批滞后 │ [查看] [催审] │ │ │
│ │ │ ... │ │
│ │ └────────┴──────────────────┴──────────┴──────────┴──────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ ┌─────────────────────────────────────┐ │
│ │ 预警趋势统计 │ │ 设计师负载分布 │ │
│ │ ┌──────────────────────┐ │ │ ┌──────────────────────────────┐ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ [趋势折线图] │ │ │ │ [柱状图/热力图] │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ 紧急预警趋势 │ │ │ │ 显示各设计师当前任务数 │ │ │
│ │ │ 警告预警趋势 │ │ │ │ 标红显示过载设计师 │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ └──────────────────────┘ │ │ └──────────────────────────────┘ │ │
│ └─────────────────────────────┘ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## 三、实施计划
### 3.1 开发优先级
| 阶段 | 内容 | 工作量 | 优先级 |
|-----|------|-------|-------|
| **P0** | 核心概览指标优化(6卡片) | 2天 | 🔴 最高 |
| **P0** | 设计师绩效Tab(新增) | 3天 | 🔴 最高 |
| **P1** | 流程效率Tab(新增) | 3天 | 🟡 高 |
| **P1** | 预警监控Tab(新增) | 3天 | 🟡 高 |
| **P2** | 需求分析Tab优化 | 2天 | 🟢 中 |
| **P2** | 导出报表功能完善 | 1天 | 🟢 中 |
| **P3** | 数据钻取功能 | 2天 | 🔵 低 |
### 3.2 后端API新增列表
| API | 说明 | 优先级 |
|-----|------|-------|
| `GET /api/analytics/designer-performance` | 设计师绩效数据 | P0 |
| `GET /api/analytics/process-efficiency` | 流程效率统计 | P1 |
| `GET /api/analytics/warnings` | 预警任务列表 | P1 |
| `GET /api/analytics/workload` | 设计师工作负载 | P0 |
| `GET /api/analytics/on-time-rate` | 准时完成率统计 | P0 |
| `GET /api/analytics/approval-stats` | 审批效率统计 | P1 |
### 3.3 数据库优化建议
```sql
-- 1. 新增任务时间戳字段(用于流程效率分析)
ALTER TABLE task ADD COLUMN demand_approved_time DATETIME COMMENT '需求审核通过时间';
ALTER TABLE task ADD COLUMN receiving_approved_time DATETIME COMMENT '接单审核通过时间';
ALTER TABLE task ADD COLUMN grabbed_time DATETIME COMMENT '抢单时间';
-- 2. 新增索引优化查询性能
CREATE INDEX idx_task_status_receiver ON task(status, receiver_id);
CREATE INDEX idx_task_create_time ON task(create_time);
CREATE INDEX idx_task_receiver_dept ON task(receiver_dept_id);
```
---
## 四、预期收益
| 收益类型 | 具体收益 |
|---------|---------|
| **管理效率提升** | 快速识别流程瓶颈,优化审批效率 |
| **资源配置优化** | 通过工作负载分析,合理分配任务 |
| **绩效考核依据** | 设计师绩效数据量化,支持KPI考核 |
| **风险预防** | 预警机制提前发现问题,减少超时 |
| **决策支持** | 多维度数据分析,支持管理决策 |
---
## 五、技术实现要点
### 5.1 前端技术栈
- **Vue 3 + TypeScript**:页面开发框架
- **Ant Design Vue**:UI组件库
- **ECharts**:图表可视化
- **Pinia**:状态管理
### 5.2 后端技术栈
- **Spring Boot 3**:后端框架
- **MyBatis-Plus**:ORM框架
- **MySQL 8.0**:数据库
### 5.3 关键实现点
1. **设计师绩效计算**
- 需要关联 `task` 表和 `task_item` 表
- 需要计算准时率(对比 deadline 和实际完成时间)
- 需要关联评价表获取好评率
2. **流程效率计算**
- 需要记录各环节时间戳
- 需要新增字段存储审核通过时间、抢单时间等
3. **预警监控实现**
- 可使用定时任务扫描预警条件
- 或在查询时实时计算预警状态
---
**文档版本**: v1.0
**创建时间**: 2026-01-04
**作者**: AI Assistant