commit
7def7464cc
@ -0,0 +1,56 @@
|
||||
package com.campus.water.controller;
|
||||
|
||||
import com.campus.water.entity.dto.request.StudentDrinkQueryDTO;
|
||||
import com.campus.water.entity.vo.StudentDrinkStatsVO;
|
||||
import com.campus.water.service.StudentDrinkStatsService;
|
||||
import com.campus.water.util.ResultVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 学生饮水量统计接口
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/student/drink-stats")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "学生端-饮水量统计", description = "学生查看本日/本周/本月饮水量")
|
||||
public class StudentDrinkStatsController {
|
||||
|
||||
private final StudentDrinkStatsService drinkStatsService;
|
||||
|
||||
@PostMapping("/today")
|
||||
@Operation(summary = "查询本日饮水量", description = "获取学生当日的饮水量、次数及明细")
|
||||
public ResultVO<StudentDrinkStatsVO> getTodayStats(@RequestBody StudentDrinkQueryDTO request) {
|
||||
// 手动校验学生ID非空
|
||||
if (request.getStudentId() == null || request.getStudentId().trim().isEmpty()) {
|
||||
return ResultVO.badRequest("学生ID不能为空");
|
||||
}
|
||||
StudentDrinkStatsVO stats = drinkStatsService.getTodayDrinkStats(request.getStudentId());
|
||||
return ResultVO.success(stats, "查询本日饮水量成功");
|
||||
}
|
||||
|
||||
@PostMapping("/this-week")
|
||||
@Operation(summary = "查询本周饮水量", description = "获取学生本周的饮水量、日均量及每日明细")
|
||||
public ResultVO<StudentDrinkStatsVO> getThisWeekStats(@RequestBody StudentDrinkQueryDTO request) {
|
||||
if (request.getStudentId() == null || request.getStudentId().trim().isEmpty()) {
|
||||
return ResultVO.badRequest("学生ID不能为空");
|
||||
}
|
||||
StudentDrinkStatsVO stats = drinkStatsService.getThisWeekDrinkStats(request.getStudentId());
|
||||
return ResultVO.success(stats, "查询本周饮水量成功");
|
||||
}
|
||||
|
||||
@PostMapping("/this-month")
|
||||
@Operation(summary = "查询本月饮水量", description = "获取学生本月的饮水量、日均量及每日明细")
|
||||
public ResultVO<StudentDrinkStatsVO> getThisMonthStats(@RequestBody StudentDrinkQueryDTO request) {
|
||||
if (request.getStudentId() == null || request.getStudentId().trim().isEmpty()) {
|
||||
return ResultVO.badRequest("学生ID不能为空");
|
||||
}
|
||||
StudentDrinkStatsVO stats = drinkStatsService.getThisMonthDrinkStats(request.getStudentId());
|
||||
return ResultVO.success(stats, "查询本月饮水量成功");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.campus.water.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 维修人员通知实体
|
||||
* 存储派单、系统通知等消息
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "notification")
|
||||
public class Notification {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 维修人员ID */
|
||||
@Column(name = "repairman_id", nullable = false, length = 50)
|
||||
private String repairmanId;
|
||||
|
||||
/** 关联工单ID */
|
||||
@Column(name = "order_id", length = 50)
|
||||
private String orderId;
|
||||
|
||||
/** 通知内容 */
|
||||
@Column(name = "content", nullable = false, length = 500)
|
||||
private String content;
|
||||
|
||||
/** 是否已读(默认未读) */
|
||||
@Column(name = "is_read")
|
||||
private boolean isRead = false;
|
||||
|
||||
/** 创建时间 */
|
||||
@Column(name = "created_time", nullable = false)
|
||||
private LocalDateTime createdTime = LocalDateTime.now();
|
||||
|
||||
/** 通知类型 */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type", nullable = false, length = 20)
|
||||
private NotificationType type;
|
||||
|
||||
/** 通知类型枚举 */
|
||||
public enum NotificationType {
|
||||
ORDER_ASSIGNED, // 派单通知
|
||||
ORDER_GRABBED, // 抢单通知
|
||||
ORDER_REJECTED, // 拒单通知
|
||||
SYSTEM // 系统通知
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.campus.water.entity.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 学生饮水量查询请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class StudentDrinkQueryDTO {
|
||||
/** 学生ID */
|
||||
private String studentId;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.campus.water.service;
|
||||
|
||||
import com.campus.water.entity.Notification;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 维修人员通知服务接口
|
||||
*/
|
||||
public interface NotificationService {
|
||||
/**
|
||||
* 发送派单通知
|
||||
* @param repairmanId 维修人员ID
|
||||
* @param orderId 工单ID
|
||||
* @param content 通知内容
|
||||
*/
|
||||
void sendOrderAssignedNotification(String repairmanId, String orderId, String content);
|
||||
|
||||
/**
|
||||
* 获取维修人员未读通知
|
||||
* @param repairmanId 维修人员ID
|
||||
* @return 未读通知列表
|
||||
*/
|
||||
List<Notification> getUnreadNotifications(String repairmanId);
|
||||
|
||||
/**
|
||||
* 获取维修人员所有通知
|
||||
* @param repairmanId 维修人员ID
|
||||
* @return 所有通知列表
|
||||
*/
|
||||
List<Notification> getAllNotifications(String repairmanId);
|
||||
|
||||
/**
|
||||
* 标记通知为已读
|
||||
* @param notificationId 通知ID
|
||||
*/
|
||||
void markAsRead(Long notificationId);
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package com.campus.water.service.impl;
|
||||
|
||||
import com.campus.water.entity.Notification;
|
||||
import com.campus.water.mapper.NotificationRepository;
|
||||
import com.campus.water.service.NotificationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通知服务实现类
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class NotificationServiceImpl implements NotificationService {
|
||||
|
||||
private final NotificationRepository notificationRepository;
|
||||
|
||||
/**
|
||||
* 发送派单通知
|
||||
*/
|
||||
@Override
|
||||
public void sendOrderAssignedNotification(String repairmanId, String orderId, String content) {
|
||||
Notification notification = new Notification();
|
||||
notification.setRepairmanId(repairmanId);
|
||||
notification.setOrderId(orderId);
|
||||
notification.setContent(content);
|
||||
notification.setType(Notification.NotificationType.ORDER_ASSIGNED);
|
||||
notificationRepository.save(notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读通知
|
||||
*/
|
||||
@Override
|
||||
public List<Notification> getUnreadNotifications(String repairmanId) {
|
||||
return notificationRepository.findByRepairmanIdAndIsReadFalseOrderByCreatedTimeDesc(repairmanId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有通知
|
||||
*/
|
||||
@Override
|
||||
public List<Notification> getAllNotifications(String repairmanId) {
|
||||
return notificationRepository.findByRepairmanIdOrderByCreatedTimeDesc(repairmanId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记通知为已读
|
||||
*/
|
||||
@Override
|
||||
public void markAsRead(Long notificationId) {
|
||||
notificationRepository.findById(notificationId).ifPresent(notification -> {
|
||||
notification.setRead(true);
|
||||
notificationRepository.save(notification);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package com.campus.water.service;
|
||||
|
||||
import com.campus.water.entity.DrinkRecord;
|
||||
import com.campus.water.entity.vo.DailyDrinkVO;
|
||||
import com.campus.water.entity.vo.StudentDrinkStatsVO;
|
||||
import com.campus.water.mapper.DrinkRecordRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.*;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 学生饮水量统计服务
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class StudentDrinkStatsService {
|
||||
|
||||
private final DrinkRecordRepository drinkRecordRepository;
|
||||
|
||||
/**
|
||||
* 查询学生本日饮水量统计
|
||||
*/
|
||||
public StudentDrinkStatsVO getTodayDrinkStats(String studentId) {
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDateTime start = today.atStartOfDay();
|
||||
LocalDateTime end = LocalDateTime.now();
|
||||
return calculateStats(studentId, start, end, "本日", "today");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询学生本周饮水量统计
|
||||
*/
|
||||
public StudentDrinkStatsVO getThisWeekDrinkStats(String studentId) {
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDateTime start = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).atStartOfDay();
|
||||
LocalDateTime end = LocalDateTime.now();
|
||||
return calculateStats(studentId, start, end, "本周", "thisWeek");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询学生本月饮水量统计
|
||||
*/
|
||||
public StudentDrinkStatsVO getThisMonthDrinkStats(String studentId) {
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDateTime start = today.with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay();
|
||||
LocalDateTime end = LocalDateTime.now();
|
||||
return calculateStats(studentId, start, end, "本月", "thisMonth");
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心统计逻辑
|
||||
*/
|
||||
private StudentDrinkStatsVO calculateStats(String studentId, LocalDateTime start, LocalDateTime end,
|
||||
String timeRangeDesc, String timeDimension) {
|
||||
// 1. 查询时间范围内的饮水记录
|
||||
List<DrinkRecord> records = drinkRecordRepository
|
||||
.findByStudentIdAndDrinkTimeBetweenOrdered(studentId, start, end);
|
||||
|
||||
// 2. 按日期分组统计
|
||||
Map<LocalDate, List<DrinkRecord>> dailyGroup = records.stream()
|
||||
.collect(Collectors.groupingBy(record -> record.getDrinkTime().toLocalDate()));
|
||||
|
||||
// 3. 构建每日明细
|
||||
List<DailyDrinkVO> dailyDetails = new ArrayList<>();
|
||||
dailyGroup.forEach((date, dailyRecords) -> {
|
||||
DailyDrinkVO dailyVO = new DailyDrinkVO();
|
||||
dailyVO.setDate(date.toString());
|
||||
// 当日总饮水量
|
||||
double dailyTotal = dailyRecords.stream()
|
||||
.map(DrinkRecord::getWaterConsumption)
|
||||
.filter(Objects::nonNull)
|
||||
.mapToDouble(BigDecimal::doubleValue)
|
||||
.sum();
|
||||
dailyVO.setConsumption(dailyTotal);
|
||||
dailyVO.setCount(dailyRecords.size());
|
||||
dailyDetails.add(dailyVO);
|
||||
});
|
||||
// 按日期排序
|
||||
dailyDetails.sort(Comparator.comparing(DailyDrinkVO::getDate));
|
||||
|
||||
// 4. 计算总饮水量、总次数、日均饮水量
|
||||
double totalConsumption = dailyDetails.stream()
|
||||
.mapToDouble(DailyDrinkVO::getConsumption)
|
||||
.sum();
|
||||
int totalCount = records.size();
|
||||
double avgDaily = dailyDetails.isEmpty() ? 0 : totalConsumption / dailyDetails.size();
|
||||
|
||||
// 5. 封装结果VO
|
||||
StudentDrinkStatsVO statsVO = new StudentDrinkStatsVO();
|
||||
statsVO.setStudentId(studentId);
|
||||
statsVO.setTimeDimension(timeDimension);
|
||||
statsVO.setTimeRange(timeRangeDesc + "(" + start.toLocalDate() + "~" + end.toLocalDate() + ")");
|
||||
statsVO.setTotalConsumption(totalConsumption);
|
||||
statsVO.setDrinkCount(totalCount);
|
||||
statsVO.setAvgDailyConsumption(avgDaily);
|
||||
statsVO.setDailyDetails(dailyDetails);
|
||||
statsVO.setDrinkRecords(records);
|
||||
|
||||
return statsVO;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
// src/services/studentDrinkStatsService.js
|
||||
import apiClient from '@/services/api'
|
||||
|
||||
export const studentDrinkStatsService = {
|
||||
/**
|
||||
* 获取今日饮水统计
|
||||
*/
|
||||
async getTodayStats(studentId) {
|
||||
try {
|
||||
const response = await apiClient.post('/api/student/drink-stats/today', {
|
||||
studentId: studentId
|
||||
})
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('获取今日饮水统计失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取本周饮水统计
|
||||
*/
|
||||
async getThisWeekStats(studentId) {
|
||||
try {
|
||||
const response = await apiClient.post('/api/student/drink-stats/this-week', {
|
||||
studentId: studentId
|
||||
})
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('获取本周饮水统计失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取本月饮水统计
|
||||
*/
|
||||
async getThisMonthStats(studentId) {
|
||||
try {
|
||||
const response = await apiClient.post('/api/student/drink-stats/this-month', {
|
||||
studentId: studentId
|
||||
})
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('获取本月饮水统计失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue