教学大屏
This commit is contained in:
@@ -242,6 +242,24 @@ public sealed class TimetableManagementController(
|
||||
return File(TimetablePdfExporter.Create(timetables), "application/pdf", fileName);
|
||||
}
|
||||
|
||||
[HttpPost("display-links/export.xlsx")]
|
||||
public async Task<ActionResult> ExportDisplayLinks(VenueDisplayLinkExportRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var classroomIds = request.ClassroomIds.Distinct().Take(200).ToArray();
|
||||
var buildingIds = request.BuildingIds.Distinct().Take(100).ToArray();
|
||||
if (classroomIds.Length + buildingIds.Length == 0)
|
||||
return ValidationProblem("请至少选择一个教室或教学楼。", statusCode: StatusCodes.Status400BadRequest);
|
||||
var classrooms = await db.Classrooms.AsNoTracking().WhereIn(classroomIds, x => x.Id)
|
||||
.OrderBy(x => x.Building!.Name).ThenBy(x => x.Code)
|
||||
.Select(x => new object?[] { "教室当天课程", x.Building!.Name + " · " + x.Name, "/venue-display/classroom/" + x.Id })
|
||||
.ToListAsync(cancellationToken);
|
||||
var buildings = await db.Buildings.AsNoTracking().WhereIn(buildingIds, x => x.Id).OrderBy(x => x.Name)
|
||||
.Select(x => new object?[] { "教学楼空余教室", x.Name, "/venue-display/building/" + x.Id })
|
||||
.ToListAsync(cancellationToken);
|
||||
var bytes = ExcelWorkbookHelper.Create("展牌链接", ["展牌类型", "场地", "相对链接"], classrooms.Concat(buildings).ToList());
|
||||
return File(bytes, ExcelWorkbookHelper.ContentType, "场地信息展牌链接.xlsx");
|
||||
}
|
||||
|
||||
private async Task<(TimetableData? Result, ActionResult? Error)> LoadAuthorizedAsync(
|
||||
TimetableResourceType resourceType,
|
||||
Guid resourceId,
|
||||
@@ -290,6 +308,8 @@ public sealed record TimetableBatchExportRequest(
|
||||
Guid AcademicTermId,
|
||||
Guid? SchedulePlanId);
|
||||
|
||||
public sealed record VenueDisplayLinkExportRequest(IReadOnlyCollection<Guid> ClassroomIds, IReadOnlyCollection<Guid> BuildingIds);
|
||||
|
||||
[ApiController]
|
||||
[Route("api/timetables")]
|
||||
public sealed class FreeClassroomsController(
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using Jiaowu.Api.Domain.Academic;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Jiaowu.Api.Infrastructure.Timetables;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Jiaowu.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[AllowAnonymous]
|
||||
[Route("api/venue-displays")]
|
||||
public sealed class VenueDisplaysController(
|
||||
AppDbContext db,
|
||||
TimetableDataService timetableDataService,
|
||||
ClassroomReservationAvailabilityService availabilityService) : ControllerBase
|
||||
{
|
||||
[HttpGet("classrooms/{classroomId:guid}")]
|
||||
public async Task<ActionResult> Classroom(Guid classroomId, CancellationToken cancellationToken)
|
||||
{
|
||||
var term = await CurrentTermAsync(cancellationToken);
|
||||
var classroom = await db.Classrooms.AsNoTracking()
|
||||
.Where(x => x.Id == classroomId && x.IsEnabled && x.Building!.IsEnabled)
|
||||
.Select(x => new { x.Id, x.Name, x.Code, BuildingName = x.Building!.Name, CampusName = x.Building.Campus!.Name })
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (classroom is null) return NotFound();
|
||||
if (term is null) return Ok(new { Classroom = classroom, Today = DateOnly.FromDateTime(DateTime.Now), Entries = Array.Empty<object>() });
|
||||
|
||||
var sheet = await timetableDataService.BuildAsync(TimetableResourceType.Classroom, classroomId,
|
||||
term.Id, null, false, null, null, cancellationToken);
|
||||
var today = DateOnly.FromDateTime(DateTime.Now);
|
||||
var (week, day) = ClassroomReservationAvailabilityService.ResolveTeachingWeek(term, today);
|
||||
var entries = sheet?.Entries.Where(x => x.DayOfWeek == day && x.StartWeek <= week && x.EndWeek >= week &&
|
||||
(x.WeekPattern == WeekPattern.All || x.WeekPattern == WeekPattern.Odd && week % 2 == 1 || x.WeekPattern == WeekPattern.Even && week % 2 == 0))
|
||||
.Select(x => new { x.CourseName, x.TeacherNames, x.ClassNames, x.StartPeriod, x.PeriodCount }) ?? [];
|
||||
return Ok(new { Classroom = classroom, Term = term.Name, Today = today, Week = week, Slots = sheet?.Slots ?? [], Entries = entries });
|
||||
}
|
||||
|
||||
[HttpGet("buildings/{buildingId:guid}")]
|
||||
public async Task<ActionResult> Building(Guid buildingId, CancellationToken cancellationToken)
|
||||
{
|
||||
var term = await CurrentTermAsync(cancellationToken);
|
||||
var building = await db.Buildings.AsNoTracking().Where(x => x.Id == buildingId && x.IsEnabled)
|
||||
.Select(x => new { x.Id, x.Name, x.Code, CampusName = x.Campus!.Name }).FirstOrDefaultAsync(cancellationToken);
|
||||
if (building is null) return NotFound();
|
||||
if (term is null) return Ok(new { Building = building, Today = DateOnly.FromDateTime(DateTime.Now), Slots = Array.Empty<object>(), Rooms = Array.Empty<object>() });
|
||||
|
||||
var today = DateOnly.FromDateTime(DateTime.Now);
|
||||
var slots = await db.ScheduleTimeSlots.AsNoTracking().Where(x => x.AcademicTermId == term.Id && x.IsEnabled)
|
||||
.OrderBy(x => x.PeriodNumber).Select(x => new { x.PeriodNumber, x.Name, x.StartsAt, x.EndsAt }).ToListAsync(cancellationToken);
|
||||
var rooms = await db.Classrooms.AsNoTracking().Where(x => x.BuildingId == buildingId && x.IsEnabled)
|
||||
.OrderBy(x => x.Code).Select(x => new { x.Id, x.Code, x.Name, x.Capacity }).ToListAsync(cancellationToken);
|
||||
var occupied = new Dictionary<int, HashSet<Guid>>();
|
||||
foreach (var slot in slots)
|
||||
occupied[slot.PeriodNumber] = await availabilityService.GetOccupiedClassroomIdsAsync(term, today, slot.PeriodNumber, 1, null, cancellationToken);
|
||||
return Ok(new { Building = building, Term = term.Name, Today = today, Slots = slots, Rooms = rooms.Select(room => new { room.Code, room.Name, room.Capacity, FreePeriods = slots.Where(slot => !occupied[slot.PeriodNumber].Contains(room.Id)).Select(slot => slot.PeriodNumber) }) });
|
||||
}
|
||||
|
||||
private Task<AcademicTerm?> CurrentTermAsync(CancellationToken cancellationToken) => db.AcademicTerms.AsNoTracking()
|
||||
.Where(x => x.IsEnabled && x.IsCurrent).FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
Reference in New Issue
Block a user