feat: 优化数据分析页面
- 支持月份选择器,可查看指定月份数据 - 新增4个Tab:需求分析、设计师绩效、流程效率、预警监控 - 核心指标卡片:总任务数、待处理、进行中、已完成、完成率、准时率 - 设计师绩效:接单量、完成量、准时率、好评率、工作负载排行 - 流程效率:各环节耗时分析、瓶颈识别、环比对比 - 预警监控:超时预警、即将超时、长期未接单、审批滞后 - 后端新增3个API:设计师绩效、流程效率、预警任务
This commit is contained in:
parent
d6c81f1fdc
commit
30f9a3e735
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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; // 过载设计师数
|
||||
}
|
||||
}
|
||||
|
||||
118
backend/src/main/java/com/designhub/vo/ProcessEfficiencyVO.java
Normal file
118
backend/src/main/java/com/designhub/vo/ProcessEfficiencyVO.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
96
backend/src/main/java/com/designhub/vo/WarningTaskVO.java
Normal file
96
backend/src/main/java/com/designhub/vo/WarningTaskVO.java
Normal 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;
|
||||
}
|
||||
|
||||
@ -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
442
数据页面优化方案.md
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user