peixue-dev/Archive/peidu-temp-files/docs/🚀预约与日历数据统一修复方案-2026-01-24.md

13 KiB
Raw Blame History

🚀 预约与日历数据统一修复方案

日期: 2026-01-24
问题: 预约页面显示62个待服务日历只显示6个
原因: 日历只查询当前月份的订单,其他月份的订单不显示


🔍 问题根源

当前实现:

日历页面查询逻辑:

// CalendarServiceImpl.java
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId)
           .ge("service_date", startDate)      // ❌ 只查询指定日期范围
           .le("service_date", endDate)        // ❌ 只查询指定日期范围
           .in("status", Arrays.asList(1, 2, 3, 4))
           .eq("deleted", 0);

问题:

  • 只查询 2026年1月 的订单
  • 其他月份的56个订单不会显示

预约列表页面查询逻辑:

// order/list.vue
const res = await request({
  url: '/api/order/list-full',
  method: 'get',
  params: {
    teacherId: this.teacherId,
    page: 1,
    size: 100  // ✅ 查询所有订单,不限日期
  }
})

结果:

  • 查询所有订单,包括所有月份
  • 显示62个待服务订单

解决方案

方案1: 日历页面添加"全部"视图(推荐)

在日历页面添加一个"全部订单"标签,显示所有月份的订单。

优点:

  • 用户可以选择查看当前月份或全部订单
  • 保持日历的月份视图功能
  • 与预约列表数据一致

实现步骤:

1. 修改日历页面,添加视图切换

<!-- user-package/pages/calendar/index.vue -->
<template>
  <view class="calendar-page">
    <!-- 视图切换 -->
    <view class="view-switcher">
      <view 
        class="switch-item"
        :class="{ active: viewMode === 'month' }"
        @click="viewMode = 'month'"
      >
        <text>月视图</text>
      </view>
      <view 
        class="switch-item"
        :class="{ active: viewMode === 'all' }"
        @click="viewMode = 'all'"
      >
        <text>全部订单</text>
      </view>
    </view>

    <!-- 月视图显示日历 -->
    <view v-if="viewMode === 'month'">
      <calendar 
        :scheduleData="scheduleData" 
        role="parent"
        @date-select="handleDateSelect"
        @month-change="handleMonthChange"
        @schedule-click="handleScheduleClick"
      />
    </view>

    <!-- 全部订单视图:显示列表 -->
    <view v-else>
      <view class="all-orders-list">
        <view 
          v-for="order in allOrders" 
          :key="order.id"
          class="order-item"
          @click="goToOrderDetail(order)"
        >
          <!-- 订单卡片内容 -->
        </view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      viewMode: 'month', // 'month' 或 'all'
      scheduleData: [],  // 当前月份的订单
      allOrders: [],     // 所有订单
    }
  },
  
  methods: {
    async loadScheduleData() {
      if (this.viewMode === 'month') {
        // 加载当前月份的订单
        const startDate = this.getMonthStart()
        const endDate = this.getMonthEnd()
        const res = await calendarApi.getAppointments(startDate, endDate)
        this.scheduleData = res.data || []
      } else {
        // 加载所有订单
        const res = await calendarApi.getAllAppointments()
        this.allOrders = res.data || []
      }
    }
  }
}
</script>

2. 添加后端接口 - 查询所有订单

// CalendarController.java
@GetMapping("/all-appointments")
@ApiOperation("获取所有预约记录(不限日期)")
public Result<List<AppointmentVO>> getAllAppointments(
        javax.servlet.http.HttpServletRequest request) {
    
    log.info("获取所有预约记录");
    
    try {
        Long userId = getCurrentUserId(request);
        
        List<AppointmentVO> appointments = calendarService.getAllAppointments(userId);
        
        log.info("查询到 {} 条预约记录", appointments.size());
        return Result.success(appointments);
        
    } catch (Exception e) {
        log.error("获取预约记录失败", e);
        return Result.error("获取预约记录失败: " + e.getMessage());
    }
}
// CalendarServiceImpl.java
@Override
public List<AppointmentVO> getAllAppointments(Long userId) {
    log.info("查询用户所有预约记录, userId: {}", userId);
    
    QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("user_id", userId)
               .in("status", Arrays.asList(1, 2, 3, 4))
               .eq("deleted", 0)
               .orderByAsc("service_date", "time_slot");
    
    List<Order> orders = orderMapper.selectList(queryWrapper);
    
    log.info("查询到 {} 条订单记录", orders.size());
    
    return orders.stream()
            .map(this::convertToAppointmentVO)
            .collect(Collectors.toList());
}

方案2: 统计信息显示所有月份(简单)

保持日历只显示当前月份,但在统计信息中显示所有月份的订单数量。

实现:

<!-- 统计信息 -->
<view class="statistics-card">
  <view class="stat-item">
    <text class="stat-value">{{ statistics.total }}</text>
    <text class="stat-label">本月课时</text>
  </view>
  <view class="stat-divider"></view>
  <view class="stat-item">
    <text class="stat-value">{{ statistics.allTotal }}</text>
    <text class="stat-label">全部课时</text>
  </view>
  <view class="stat-divider"></view>
  <view class="stat-item">
    <text class="stat-value">{{ statistics.upcoming }}</text>
    <text class="stat-label">待服务</text>
  </view>
</view>
// 加载全部统计
async loadAllStatistics() {
  const res = await calendarApi.getAllStats()
  this.statistics.allTotal = res.data.totalCount || 0
}

方案3: 日历支持跨月查看

允许用户切换到其他月份查看订单。

实现:

<!-- 月份导航 -->
<view class="month-nav">
  <view class="nav-btn" @click="prevMonth">上一月</view>
  <view class="month-title">{{ currentYear }}{{ currentMonth }}</view>
  <view class="nav-btn" @click="nextMonth">下一月</view>
</view>

<!-- 快速跳转 -->
<view class="quick-nav">
  <view 
    v-for="month in monthsWithOrders" 
    :key="month"
    class="month-tag"
    @click="jumpToMonth(month)"
  >
    {{ month }} ({{ getMonthOrderCount(month) }})
  </view>
</view>

🎯 推荐实施方案

最佳方案: 方案1 + 方案2 组合

  1. 添加视图切换 - 用户可以选择"月视图"或"全部订单"
  2. 统计信息显示全部 - 让用户知道总共有多少订单
  3. 保持月份切换 - 方便查看特定月份的订单

📝 实施步骤

步骤1: 添加后端接口

// 1. 在 CalendarService 接口中添加方法
List<AppointmentVO> getAllAppointments(Long userId);
Map<String, Object> getAllStats(Long userId);

// 2. 在 CalendarServiceImpl 中实现
@Override
public List<AppointmentVO> getAllAppointments(Long userId) {
    QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("user_id", userId)
               .in("status", Arrays.asList(1, 2, 3, 4))
               .eq("deleted", 0)
               .orderByAsc("service_date", "time_slot");
    
    List<Order> orders = orderMapper.selectList(queryWrapper);
    
    return orders.stream()
            .map(this::convertToAppointmentVO)
            .collect(Collectors.toList());
}

@Override
public Map<String, Object> getAllStats(Long userId) {
    QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("user_id", userId)
               .eq("deleted", 0);
    
    List<Order> orders = orderMapper.selectList(queryWrapper);
    
    Map<String, Object> stats = new HashMap<>();
    stats.put("totalCount", orders.size());
    stats.put("pendingCount", orders.stream()
            .filter(o -> o.getStatus() == 1 || o.getStatus() == 2)
            .count());
    stats.put("completedCount", orders.stream()
            .filter(o -> o.getStatus() == 4)
            .count());
    
    return stats;
}

// 3. 在 CalendarController 中添加接口
@GetMapping("/all-appointments")
public Result<List<AppointmentVO>> getAllAppointments(
        javax.servlet.http.HttpServletRequest request) {
    Long userId = getCurrentUserId(request);
    List<AppointmentVO> appointments = calendarService.getAllAppointments(userId);
    return Result.success(appointments);
}

@GetMapping("/all-stats")
public Result<Object> getAllStats(
        javax.servlet.http.HttpServletRequest request) {
    Long userId = getCurrentUserId(request);
    Object stats = calendarService.getAllStats(userId);
    return Result.success(stats);
}

步骤2: 修改前端日历页面

// user-package/pages/calendar/index.vue

data() {
  return {
    viewMode: 'month', // 'month' 或 'all'
    scheduleData: [],
    allOrders: [],
    statistics: {
      total: 0,        // 本月订单
      allTotal: 0,     // 全部订单
      completed: 0,
      upcoming: 0
    }
  }
},

methods: {
  async loadScheduleData() {
    if (this.viewMode === 'month') {
      // 加载当前月份
      const startDate = this.getMonthStart()
      const endDate = this.getMonthEnd()
      const res = await calendarApi.getAppointments(startDate, endDate)
      this.scheduleData = res.data || []
    } else {
      // 加载所有订单
      const res = await calendarApi.getAllAppointments()
      this.allOrders = res.data || []
    }
  },
  
  async loadStatistics() {
    // 加载本月统计
    const monthRes = await calendarApi.getMonthlyStats(this.currentYear, this.currentMonth)
    this.statistics.total = monthRes.data.totalCount || 0
    this.statistics.completed = monthRes.data.completedCount || 0
    this.statistics.upcoming = monthRes.data.pendingCount || 0
    
    // 加载全部统计
    const allRes = await calendarApi.getAllStats()
    this.statistics.allTotal = allRes.data.totalCount || 0
  }
}

步骤3: 添加API方法

// api/index.js 或 api/calendar.js

export const calendarApi = {
  // 获取指定日期范围的预约
  getAppointments(startDate, endDate) {
    return request({
      url: '/api/calendar/appointments',
      method: 'get',
      params: { startDate, endDate }
    })
  },
  
  // 获取所有预约(新增)
  getAllAppointments() {
    return request({
      url: '/api/calendar/all-appointments',
      method: 'get'
    })
  },
  
  // 获取月度统计
  getMonthlyStats(year, month) {
    return request({
      url: '/api/calendar/monthly-stats',
      method: 'get',
      params: { year, month }
    })
  },
  
  // 获取全部统计(新增)
  getAllStats() {
    return request({
      url: '/api/calendar/all-stats',
      method: 'get'
    })
  }
}

🧪 测试验证

测试步骤:

  1. 执行诊断SQL

    -- 查看订单分布
    SELECT 
        DATE_FORMAT(service_date, '%Y-%m') AS month,
        COUNT(*) AS count
    FROM `order`
    WHERE user_id = 1
    AND status = 1
    AND deleted = 0
    GROUP BY DATE_FORMAT(service_date, '%Y-%m');
    
  2. 测试月视图

    • 打开日历页面
    • 确认显示当前月份的订单
    • 切换到其他月份,确认数据正确
  3. 测试全部视图

    • 切换到"全部订单"
    • 确认显示所有62个待服务订单
    • 与预约列表页面对比,确认数量一致
  4. 测试统计信息

    • 确认"本月课时"显示当前月份数量
    • 确认"全部课时"显示所有月份数量
    • 确认"待服务"数量正确

📊 预期效果

修复前:

  • 日历页面: 显示 6 个订单仅1月
  • 预约列表: 显示 62 个订单(所有月份)
  • 数据不一致

修复后:

  • 日历月视图: 显示 6 个订单1月
  • 日历全部视图: 显示 62 个订单(所有月份)
  • 预约列表: 显示 62 个订单(所有月份)
  • 数据一致

💡 额外优化建议

1. 添加月份快速跳转

显示有订单的月份,点击快速跳转:

<view class="month-tags">
  <view 
    v-for="month in monthsWithOrders" 
    :key="month"
    @click="jumpToMonth(month)"
  >
    {{ month }} ({{ getMonthOrderCount(month) }})
  </view>
</view>

2. 添加日期范围筛选

允许用户自定义查看日期范围:

<view class="date-range-picker">
  <picker mode="date" @change="onStartDateChange">
    <text>{{ startDate }}</text>
  </picker>
  <text></text>
  <picker mode="date" @change="onEndDateChange">
    <text>{{ endDate }}</text>
  </picker>
</view>

3. 添加状态筛选

在全部视图中添加状态筛选:

<view class="status-filter">
  <view 
    v-for="status in statusList" 
    :key="status.value"
    :class="{ active: selectedStatus === status.value }"
    @click="selectedStatus = status.value"
  >
    {{ status.label }}
  </view>
</view>

总结

问题原因:

  • 日历只查询当前月份的订单
  • 预约列表查询所有订单
  • 导致数据不一致

解决方案:

  • 添加"全部订单"视图
  • 统计信息显示全部数量
  • 保持月份切换功能

实施优先级:

  1. 添加后端接口(必须)
  2. 修改前端页面(必须)
  3. 添加视图切换(推荐)
  4. 优化统计显示(推荐)
  5. 💡 添加快速跳转(可选)

修复时间: 2026-01-24
预计工作量: 2-3小时
影响范围: 家长端日历功能

🎉 修复后,日历和预约列表将显示一致的数据!