diff --git a/VOL.Ai/IServices/IAiSportsStationService.cs b/VOL.Ai/IServices/IAiSportsStationService.cs
new file mode 100644
index 0000000..93a5f87
--- /dev/null
+++ b/VOL.Ai/IServices/IAiSportsStationService.cs
@@ -0,0 +1,15 @@
+using VOL.Core.Extensions.AutofacManager;
+using VOL.Model;
+using VOL.Model.Ai.Request;
+using VOL.Model.Ai.Response;
+
+namespace VOL.Ai.IServices
+{
+ public interface IAiSportsStationService : IDependency
+ {
+ ///
+ /// 获取壹路AI运动站列表
+ ///
+ Task> GetAiSportsStationList(AiSportsStationListRequest paramDto);
+ }
+}
diff --git a/VOL.Ai/Services/AiSportsStationService.cs b/VOL.Ai/Services/AiSportsStationService.cs
new file mode 100644
index 0000000..c1738bd
--- /dev/null
+++ b/VOL.Ai/Services/AiSportsStationService.cs
@@ -0,0 +1,179 @@
+using Microsoft.EntityFrameworkCore;
+using VOL.Ai.IServices;
+using VOL.Core.EFDbContext;
+using VOL.Core.Extensions.AutofacManager;
+using VOL.Core.ManageUser;
+using VOL.Entity.DomainModels;
+using VOL.Entity.DomainModels.YiLu;
+using VOL.Entity.Enum;
+using VOL.Model;
+using VOL.Model.Ai.Request;
+using VOL.Model.Ai.Response;
+
+namespace VOL.Ai.Services
+{
+ public class AiSportsStationService : IAiSportsStationService, IDependency
+ {
+ private readonly VOLContext _dbContext;
+
+ private static readonly Dictionary CourseNames = new()
+ {
+ { 3, "引体向上" },
+ { 7, "立定跳远" },
+ { 8, "跳绳" },
+ { 9, "开合跳" },
+ { 10, "高抬腿" },
+ { 11, "深蹲" },
+ { 23, "坐位体前屈" },
+ { 24, "单脚跳" },
+ { 25, "动态臀桥" },
+ { 26, "左右跳" },
+ { 27, "打地鼠" },
+ { 28, "七上八下" },
+ { 29, "消灭蚊子" },
+ { 30, "石头剪刀布" },
+ { 31, "变脸" },
+ { 32, "打钉子" },
+ { 1001, "体重系数(BMI)" },
+ { 1002, "肺活量" }
+ };
+
+ public AiSportsStationService(VOLContext dbContext)
+ {
+ _dbContext = dbContext;
+ }
+
+ public async Task> GetAiSportsStationList(AiSportsStationListRequest paramDto)
+ {
+ paramDto.PageIndex = paramDto.PageIndex <= 0 ? 1 : paramDto.PageIndex;
+ paramDto.PageSize = paramDto.PageSize <= 0 ? 10 : paramDto.PageSize;
+
+ var currentUser = UserContext.Current;
+ var isAdmin = currentUser?.IsSuperAdmin == true
+ || string.Equals(currentUser?.UserName, "admin", StringComparison.OrdinalIgnoreCase);
+ var schoolCode = currentUser?.TenantId;
+ int? schoolId = null;
+
+ if (!isAdmin)
+ {
+ schoolId = await _dbContext.Set()
+ .Where(x => x.SchoolCode == schoolCode)
+ .Select(x => (int?)x.Id)
+ .FirstOrDefaultAsync();
+
+ if (schoolId == null)
+ {
+ return new PageDataDto
+ {
+ Total = 0,
+ TotalPages = 0,
+ PageIndex = paramDto.PageIndex,
+ PageSize = paramDto.PageSize,
+ Datas = new List()
+ };
+ }
+ }
+
+ var studentName = paramDto.StudentName?.Trim();
+ var projectName = paramDto.ProjectName?.Trim();
+ var sex = GetSexType(paramDto.Sex);
+
+ var query =
+ from train in _dbContext.Set().AsNoTracking()
+ join device in _dbContext.Set().AsNoTracking()
+ on train.DeviceSerial equals device.SerialNumber
+ join student in _dbContext.Set().AsNoTracking()
+ on train.UserSerial equals student.StudentNo into studentGroup
+ from student in studentGroup.DefaultIfEmpty()
+ select new
+ {
+ train.Id,
+ device.OrgId,
+ StudentSchoolCode = student == null ? schoolCode : student.SchoolCode,
+ train.CourseId,
+ train.StartTime,
+ train.EndTime,
+ train.UserSerial,
+ StudentName = student == null ? train.UserSerial : student.StudentName,
+ Sex = student == null ? (SexType?)null : student.Sex,
+ ClassId = student == null ? 0 : student.ClassId,
+ ClassName = student == null ? string.Empty : student.ClassName
+ };
+
+ if (!isAdmin)
+ {
+ var currentSchoolId = schoolId.GetValueOrDefault();
+ query = query.Where(x => x.OrgId == currentSchoolId && x.StudentSchoolCode == schoolCode);
+ }
+
+ if (!string.IsNullOrWhiteSpace(studentName))
+ query = query.Where(x => x.StudentName.Contains(studentName));
+
+ if (sex.HasValue)
+ query = query.Where(x => x.Sex == sex.Value);
+
+ if (paramDto.ClassId > 0)
+ query = query.Where(x => x.ClassId == paramDto.ClassId);
+
+ if (paramDto.StartTime.HasValue)
+ query = query.Where(x => x.StartTime >= paramDto.StartTime.Value);
+
+ if (paramDto.EndTime.HasValue)
+ query = query.Where(x => x.StartTime <= paramDto.EndTime.Value);
+
+ var rows = await query
+ .OrderByDescending(x => x.StartTime)
+ .ThenByDescending(x => x.Id)
+ .ToListAsync();
+
+ if (!string.IsNullOrWhiteSpace(projectName))
+ rows = rows.Where(x => GetCourseName(x.CourseId).Contains(projectName)).ToList();
+
+ var total = rows.Count;
+ var datas = rows
+ .Skip((paramDto.PageIndex - 1) * paramDto.PageSize)
+ .Take(paramDto.PageSize)
+ .Select((x, index) => new AiSportsStationDto
+ {
+ Ind = (paramDto.PageIndex - 1) * paramDto.PageSize + index + 1,
+ StudentName = x.StudentName,
+ Sex = GetSexName(x.Sex),
+ ClassName = x.ClassName,
+ ProjectName = GetCourseName(x.CourseId),
+ StartTime = x.StartTime,
+ EndTime = x.EndTime
+ })
+ .ToList();
+
+ return new PageDataDto
+ {
+ Total = total,
+ TotalPages = (int)Math.Ceiling(total / (double)paramDto.PageSize),
+ PageIndex = paramDto.PageIndex,
+ PageSize = paramDto.PageSize,
+ Datas = datas
+ };
+ }
+
+ private static string GetCourseName(int courseId)
+ {
+ return CourseNames.TryGetValue(courseId, out var name) ? name : courseId.ToString();
+ }
+
+ private static SexType? GetSexType(string sex)
+ {
+ return sex switch
+ {
+ "男" => SexType.Male,
+ "女" => SexType.Female,
+ _ => null
+ };
+ }
+
+ private static string GetSexName(SexType? sex)
+ {
+ if (sex == null) return string.Empty;
+ return sex == SexType.Male ? "男" : "女";
+ }
+ }
+}
diff --git a/VOL.Model/Ai/Request/AiSportsStationRequest.cs b/VOL.Model/Ai/Request/AiSportsStationRequest.cs
new file mode 100644
index 0000000..2d198ea
--- /dev/null
+++ b/VOL.Model/Ai/Request/AiSportsStationRequest.cs
@@ -0,0 +1,41 @@
+using System;
+using VOL.Model;
+
+namespace VOL.Model.Ai.Request
+{
+ ///
+ /// 壹路AI运动站列表查询
+ ///
+ public class AiSportsStationListRequest : PageDto
+ {
+ ///
+ /// 学生姓名
+ ///
+ public string StudentName { get; set; }
+
+ ///
+ /// 性别
+ ///
+ public string Sex { get; set; }
+
+ ///
+ /// 班级Id
+ ///
+ public int ClassId { get; set; }
+
+ ///
+ /// 课目名称
+ ///
+ public string ProjectName { get; set; }
+
+ ///
+ /// 运动开始时间
+ ///
+ public DateTime? StartTime { get; set; }
+
+ ///
+ /// 运动结束时间
+ ///
+ public DateTime? EndTime { get; set; }
+ }
+}
diff --git a/VOL.Model/Ai/Response/AiSportsStationDto.cs b/VOL.Model/Ai/Response/AiSportsStationDto.cs
new file mode 100644
index 0000000..ef97a07
--- /dev/null
+++ b/VOL.Model/Ai/Response/AiSportsStationDto.cs
@@ -0,0 +1,45 @@
+using System;
+
+namespace VOL.Model.Ai.Response
+{
+ ///
+ /// 壹路AI运动站列表数据
+ ///
+ public class AiSportsStationDto
+ {
+ ///
+ /// 序号
+ ///
+ public int Ind { get; set; }
+
+ ///
+ /// 学生姓名
+ ///
+ public string StudentName { get; set; }
+
+ ///
+ /// 性别
+ ///
+ public string Sex { get; set; }
+
+ ///
+ /// 班级名称
+ ///
+ public string ClassName { get; set; }
+
+ ///
+ /// 课目名称
+ ///
+ public string ProjectName { get; set; }
+
+ ///
+ /// 运动开始时间
+ ///
+ public DateTime? StartTime { get; set; }
+
+ ///
+ /// 运动结束时间
+ ///
+ public DateTime? EndTime { get; set; }
+ }
+}
diff --git a/VOL.WebApi/Controllers/AI/AiSportsStationController.cs b/VOL.WebApi/Controllers/AI/AiSportsStationController.cs
new file mode 100644
index 0000000..d766bf0
--- /dev/null
+++ b/VOL.WebApi/Controllers/AI/AiSportsStationController.cs
@@ -0,0 +1,36 @@
+using Microsoft.AspNetCore.Mvc;
+using System.Threading.Tasks;
+using VOL.Ai.IServices;
+using VOL.Core.Filters;
+using VOL.Model;
+using VOL.Model.Ai.Request;
+using VOL.Model.Ai.Response;
+
+namespace VOL.WebApi.Controllers.AI
+{
+ ///
+ /// 壹路AI运动站
+ ///
+ [Route("api/[controller]")]
+ [ApiController]
+ [ApiExplorerSettings(GroupName = "v6")]
+ [TypeFilter(typeof(CustomApiResponseFilter))]
+ public class AiSportsStationController : ControllerBase
+ {
+ private readonly IAiSportsStationService _aiSportsStationService;
+
+ public AiSportsStationController(IAiSportsStationService aiSportsStationService)
+ {
+ _aiSportsStationService = aiSportsStationService;
+ }
+
+ ///
+ /// 获取壹路AI运动站列表
+ ///
+ [HttpGet(nameof(GetAiSportsStationList))]
+ public async Task> GetAiSportsStationList([FromQuery] AiSportsStationListRequest paramDto)
+ {
+ return await _aiSportsStationService.GetAiSportsStationList(paramDto);
+ }
+ }
+}