上传视频接口
This commit is contained in:
parent
020145b6d5
commit
7b4409de71
@ -86,4 +86,5 @@ namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
public string BestResult { get; set; }
|
||||
public string LastResult { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YD_YiLu.Commons.Dto.DeviceSync
|
||||
{
|
||||
public class DeviceVideoUploadRequestDto
|
||||
{
|
||||
public string DeviceSerial { get; set; }
|
||||
|
||||
public int DeviceType { get; set; }
|
||||
|
||||
public string VideoSerial { get; set; }
|
||||
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 视频文件(form-data)
|
||||
/// </summary>
|
||||
public IFormFile File { get; set; }
|
||||
}
|
||||
}
|
||||
@ -26,11 +26,13 @@ namespace YD_YiLu.Api.Context
|
||||
|
||||
public DbSet<XW_ProjectMode> XW_ProjectMode { get; set; }
|
||||
public DbSet<XW_TestingProject> XW_TestingProject { get; set; }
|
||||
public DbSet<YL_Device> YL_Device { get; set; }
|
||||
public DbSet<YL_Train> YL_Train { get; set; }
|
||||
public DbSet<XW_SportsTestData> XW_SportsTestData { get; set; }
|
||||
public DbSet<XW_TrainStudents> XW_TrainStudents { get; set; }
|
||||
public DbSet<XW_TrainSetting> XW_TrainSetting { get; set; }
|
||||
public DbSet<XW_TrainViolationsBO> XW_TrainViolationsBO { get; set; }
|
||||
public DbSet<YL_Device> YL_Device { get; set; }
|
||||
public DbSet<YL_Train> YL_Train { get; set; }
|
||||
public DbSet<YL_DeviceVideo> YL_DeviceVideo { get; set; }
|
||||
public DbSet<YL_AppVersion> YL_AppVersion { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,5 +138,17 @@ namespace YD_YiLu.Api.Controllers
|
||||
var res = await _deviceSyncService.MyRecordAsync(dto);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视频回放上传
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("device/sync/video/upload")]
|
||||
public async Task<bool> UploadVideo([FromForm] DeviceVideoUploadRequestDto dto)
|
||||
{
|
||||
var res = await _deviceSyncService.DeviceVideoUploadAsync(dto);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ using VOL.Entity.DomainModels.XinWei;
|
||||
using YD_YiLu.Api.Context;
|
||||
using YD_YiLu.Api.Services.Interface;
|
||||
using YD_YiLu.Api.SmartSportsEntitys;
|
||||
using YD_YiLu.Api.Utilities;
|
||||
using YD_YiLu.Commons;
|
||||
using YD_YiLu.Commons.Dto.DeviceSync;
|
||||
using YD_YiLu.Commons.Utils;
|
||||
@ -387,86 +388,87 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
|
||||
public async Task<List<DeviceSyncLastGradeItemDto>> LastGradeAsync(DeviceSyncLastGradeRequestDto dto)
|
||||
public async Task<List<DeviceSyncUserDto>> LastGradeAsync(DeviceSyncLastGradeRequestDto dto)
|
||||
{
|
||||
if (dto == null || dto.UserSerials == null || dto.UserSerials.Count == 0)
|
||||
return new List<DeviceSyncLastGradeItemDto>();
|
||||
if (dto?.UserSerials == null || dto.UserSerials.Count == 0)
|
||||
return new List<DeviceSyncUserDto>();
|
||||
|
||||
// 去掉空白、重复序列号
|
||||
var orderedUnique = dto.UserSerials
|
||||
var serials = dto.UserSerials
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Select(x => x.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (!orderedUnique.Any()) return new List<DeviceSyncLastGradeItemDto>();
|
||||
|
||||
// 查询学生信息
|
||||
var students = await _context.Student
|
||||
.Where(s => orderedUnique.Contains(s.StudentNo))
|
||||
.Where(s => serials.Contains(s.StudentNo))
|
||||
.ToListAsync();
|
||||
|
||||
S_Student MatchStudent(string serial) =>
|
||||
students.FirstOrDefault(s => string.Equals(s.StudentNo, serial, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// 获取学生最近一次训练记录
|
||||
var studentSerials = students.Select(s => s.StudentNo).ToList();
|
||||
var trainRows = await _context.YL_Train
|
||||
|
||||
var trains = await _context.YL_Train
|
||||
.Where(t => studentSerials.Contains(t.UserSerial))
|
||||
.GroupBy(t => t.UserSerial)
|
||||
.Select(g => g
|
||||
.OrderByDescending(t => t.EndTime)
|
||||
.FirstOrDefault())
|
||||
.ToListAsync();
|
||||
|
||||
var trainMap = trainRows.ToDictionary(t => t.UserSerial, t => t);
|
||||
|
||||
var result = new List<DeviceSyncLastGradeItemDto>();
|
||||
|
||||
foreach (var serial in orderedUnique)
|
||||
// 👉 人 + 课程 聚合
|
||||
var trainGroup = trains
|
||||
.GroupBy(t => new { t.UserSerial, t.CourseId })
|
||||
.ToDictionary(
|
||||
g => (g.Key.UserSerial, g.Key.CourseId),
|
||||
g => new
|
||||
{
|
||||
var st = MatchStudent(serial);
|
||||
if (st == null)
|
||||
{
|
||||
result.Add(new DeviceSyncLastGradeItemDto
|
||||
{
|
||||
UserSerial = serial,
|
||||
UserId = 0,
|
||||
Nickname = "",
|
||||
OrganizationId = 0,
|
||||
OrganizationName = "",
|
||||
Gender = 0,
|
||||
FacePictureUrl = "",
|
||||
Score = "0",
|
||||
UnStandardCounts = 0,
|
||||
StartTime = "",
|
||||
EndTime = "",
|
||||
CourseId = 0,
|
||||
CourseName = ""
|
||||
Best = g.Max(x => x.Score ?? 0),
|
||||
Last = g.OrderByDescending(x => x.EndTime).FirstOrDefault()?.Score ?? 0
|
||||
});
|
||||
continue;
|
||||
|
||||
// 👉 课程枚举(必须全返回)
|
||||
var courseIds = new List<int> { 2, 7, 8, 9, 10, 11 };
|
||||
|
||||
var result = new List<DeviceSyncUserDto>();
|
||||
|
||||
foreach (var serial in serials)
|
||||
{
|
||||
var st = students.FirstOrDefault(s => s.StudentNo == serial);
|
||||
|
||||
var user = new DeviceSyncUserDto
|
||||
{
|
||||
UserId = st?.Id ?? 0,
|
||||
UserType = 0, // 你自己定义
|
||||
Username = st?.StudentNo ?? serial,
|
||||
Nickname = st?.StudentName ?? "",
|
||||
OrganizationId = st?.ClassId ?? 0,
|
||||
OrganizationName = st?.ClassName ?? "",
|
||||
GradeName = "", // 没有就空
|
||||
FacePictureUrl = st?.Photo ?? "",
|
||||
Identity = "", // 可选
|
||||
Gender = st == null ? "" : (st.Sex == 1 ? "男" : "女"),
|
||||
Birthday = st?.Birthday?.ToString("yyyy-MM-dd") ?? "",
|
||||
UserSerial = serial
|
||||
};
|
||||
|
||||
// 👉 构造 trainLimitValues
|
||||
foreach (var cid in courseIds)
|
||||
{
|
||||
if (trainGroup.TryGetValue((serial, cid), out var val))
|
||||
{
|
||||
user.TrainLimitValues.Add(new TrainLimitValueDto
|
||||
{
|
||||
CourseId = cid,
|
||||
BestResult = Math.Round(val.Best).ToString("0"),
|
||||
LastResult = Math.Round(val.Last).ToString("0")
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
user.TrainLimitValues.Add(new TrainLimitValueDto
|
||||
{
|
||||
CourseId = cid,
|
||||
BestResult = "0",
|
||||
LastResult = "0"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
trainMap.TryGetValue(serial, out var train);
|
||||
|
||||
result.Add(new DeviceSyncLastGradeItemDto
|
||||
{
|
||||
UserSerial = st.StudentNo,
|
||||
UserId = st.Id,
|
||||
TrainId = train?.Id ?? 0,
|
||||
Nickname = st.StudentName,
|
||||
OrganizationId = st.ClassId ?? 0,
|
||||
OrganizationName = st.ClassName ?? "",
|
||||
Gender = st.Sex,
|
||||
FacePictureUrl = st.Photo ?? "",
|
||||
Score = train?.Score?.ToString("0") ?? "0",
|
||||
UnStandardCounts = train?.UnStandardCounts ?? 0,
|
||||
StartTime = train?.StartTime.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||||
EndTime = train?.EndTime.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||||
CourseId = train?.CourseId ?? 0,
|
||||
CourseName = Util.GetEnumDescription<CourseType>(train?.CourseId)
|
||||
});
|
||||
result.Add(user);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -678,6 +680,21 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
|
||||
|
||||
public async Task<AppVerifyResponseDto> AppVerifyAsync(AppVerifyRequestDto dto)
|
||||
{
|
||||
// 1️⃣ 查询最新版本
|
||||
var latest = await _context.Set<YL_AppVersion>()
|
||||
.Where(x => x.ApplyName == dto.ApplyName
|
||||
&& x.DeviceType == dto.DeviceType
|
||||
&& x.HardwareType == dto.HardwareType
|
||||
&& x.OperatingSystem == dto.OperatingSystem
|
||||
&& x.Status == 1
|
||||
&& x.PublishStatus == 1)
|
||||
.OrderByDescending(x => x.IsLatest)
|
||||
.ThenByDescending(x => x.VersionCode)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
// 2️⃣ 没有配置版本 → 不更新
|
||||
if (latest == null)
|
||||
{
|
||||
return new AppVerifyResponseDto
|
||||
{
|
||||
@ -689,6 +706,83 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
};
|
||||
}
|
||||
|
||||
// 3️⃣ 版本比较(必须用 VersionCode)
|
||||
int currentVersionCode = ParseVersionCode(dto.VersionNumber);
|
||||
|
||||
bool needUpdate = latest.VersionCode > currentVersionCode;
|
||||
|
||||
if (!needUpdate)
|
||||
{
|
||||
return new AppVerifyResponseDto
|
||||
{
|
||||
VersionNumber = dto.VersionNumber,
|
||||
NeedForcedUpdating = 0,
|
||||
UpdateUrl = null,
|
||||
HardwareType = dto.HardwareType,
|
||||
ApplyName = dto.ApplyName
|
||||
};
|
||||
}
|
||||
|
||||
// 4️⃣ 是否强制升级
|
||||
bool needForce = latest.NeedForcedUpdating == 1;
|
||||
|
||||
// 最低版本限制(强制升级)
|
||||
if (!string.IsNullOrEmpty(latest.MinSupportVersion))
|
||||
{
|
||||
if (CompareVersion(dto.VersionNumber, latest.MinSupportVersion) < 0)
|
||||
{
|
||||
needForce = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 5️⃣ 返回结果
|
||||
return new AppVerifyResponseDto
|
||||
{
|
||||
VersionNumber = latest.VersionNumber,
|
||||
NeedForcedUpdating = needForce ? 1 : 0,
|
||||
UpdateUrl = latest.UpdateUrl,
|
||||
HardwareType = latest.HardwareType,
|
||||
ApplyName = latest.ApplyName
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视频回放上传
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> DeviceVideoUploadAsync(DeviceVideoUploadRequestDto dto)
|
||||
{
|
||||
if (dto.File == null || dto.File.Length == 0)
|
||||
return false;
|
||||
|
||||
// 1️⃣ 生成OSS路径(按我之前给你的规范)
|
||||
var path = $"Upload/device/{dto.DeviceType}/{dto.DeviceSerial}/video/{dto.VideoSerial}/";
|
||||
|
||||
// 2️⃣ 上传到OSS
|
||||
var url = ALiYunOss.Upload(dto.File, path, dto.VideoSerial);
|
||||
|
||||
if (string.IsNullOrEmpty(url))
|
||||
return false;
|
||||
|
||||
// 3️⃣ 入库
|
||||
var entity = new YL_DeviceVideo
|
||||
{
|
||||
DeviceSerial = dto.DeviceSerial,
|
||||
DeviceType = dto.DeviceType,
|
||||
VideoSerial = dto.VideoSerial,
|
||||
VideoUrl = url,
|
||||
Timestamp = dto.Timestamp,
|
||||
CreateTime = DateTime.Now,
|
||||
Status = 1
|
||||
};
|
||||
|
||||
_context.Set<YL_DeviceVideo>().Add(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<DeviceSyncUserDto> GetUserDetailAsync(string deviceSerial, int userId)
|
||||
{
|
||||
var orgId = await GetOrgIdByDeviceAsync(deviceSerial);
|
||||
@ -736,5 +830,44 @@ namespace YD_YiLu.Api.Services.Impl
|
||||
};
|
||||
}
|
||||
|
||||
private int CompareVersion(string v1, string v2)
|
||||
{
|
||||
if (string.IsNullOrEmpty(v1) || string.IsNullOrEmpty(v2))
|
||||
return 0;
|
||||
|
||||
var arr1 = v1.Split('.');
|
||||
var arr2 = v2.Split('.');
|
||||
|
||||
int len = Math.Max(arr1.Length, arr2.Length);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
int num1 = i < arr1.Length ? int.Parse(arr1[i]) : 0;
|
||||
int num2 = i < arr2.Length ? int.Parse(arr2[i]) : 0;
|
||||
|
||||
if (num1 > num2) return 1;
|
||||
if (num1 < num2) return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
private int ParseVersionCode(string version)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
return 0;
|
||||
|
||||
var parts = version.Split('.');
|
||||
int code = 0;
|
||||
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
{
|
||||
if (int.TryParse(parts[i], out int num))
|
||||
{
|
||||
code = code * 100 + num;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ namespace YD_YiLu.Api.Services.Interface
|
||||
/// <summary>
|
||||
/// 人员上次成绩(按人员序列号批量查询)
|
||||
/// </summary>
|
||||
Task<List<DeviceSyncLastGradeItemDto>> LastGradeAsync(DeviceSyncLastGradeRequestDto dto);
|
||||
Task<List<DeviceSyncUserDto>> LastGradeAsync(DeviceSyncLastGradeRequestDto dto);
|
||||
|
||||
/// <summary>
|
||||
/// 查询运动记录
|
||||
@ -72,5 +72,10 @@ namespace YD_YiLu.Api.Services.Interface
|
||||
/// 获取人员详情(壹动站协议)
|
||||
/// </summary>
|
||||
Task<DeviceSyncUserDto> GetUserDetailAsync(string deviceSerial, int userId);
|
||||
|
||||
/// <summary>
|
||||
/// 视频回放上传
|
||||
/// </summary>
|
||||
Task<bool> DeviceVideoUploadAsync(DeviceVideoUploadRequestDto dto);
|
||||
}
|
||||
}
|
||||
|
||||
126
YD_YiLu/SmartSportsEntitys/YL_AppVersion.cs
Normal file
126
YD_YiLu/SmartSportsEntitys/YL_AppVersion.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace YD_YiLu.Api.SmartSportsEntitys
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用版本表(设备升级控制)
|
||||
/// </summary>
|
||||
[Table("YL_AppVersion")]
|
||||
public class YL_AppVersion
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键ID
|
||||
/// </summary>
|
||||
[Key]
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用名称(壹动站 / 壹体机等)
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public string ApplyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备类型
|
||||
/// </summary>
|
||||
public int DeviceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 硬件类型(如 LZ)
|
||||
/// </summary>
|
||||
[MaxLength(20)]
|
||||
public string HardwareType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作系统(android)
|
||||
/// </summary>
|
||||
[MaxLength(20)]
|
||||
public string OperatingSystem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 版本号(如 1.0.1)
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(20)]
|
||||
public string VersionNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 版本Code(用于排序,安卓强烈建议使用)
|
||||
/// </summary>
|
||||
public int VersionCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否强制更新 0=否 1=是
|
||||
/// </summary>
|
||||
public int NeedForcedUpdating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新包下载地址
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(500)]
|
||||
public string UpdateUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为最新版本 0=否 1=是
|
||||
/// </summary>
|
||||
public int IsLatest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发布状态 0=未发布 1=已发布
|
||||
/// </summary>
|
||||
public int PublishStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最小支持版本(低于该版本必须升级)
|
||||
/// </summary>
|
||||
[MaxLength(20)]
|
||||
public string? MinSupportVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新说明
|
||||
/// </summary>
|
||||
[MaxLength(500)]
|
||||
public string? UpdateContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载包大小(KB)
|
||||
/// </summary>
|
||||
public long? FileSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载MD5(用于校验)
|
||||
/// </summary>
|
||||
[MaxLength(64)]
|
||||
public string? FileMd5 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 灰度发布比例(0-100)
|
||||
/// </summary>
|
||||
public int GrayPercent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态 0=禁用 1=启用
|
||||
/// </summary>
|
||||
public int Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
public DateTime? UpdateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
[MaxLength(500)]
|
||||
public string? Remark { get; set; }
|
||||
}
|
||||
}
|
||||
81
YD_YiLu/SmartSportsEntitys/YL_DeviceVideo.cs
Normal file
81
YD_YiLu/SmartSportsEntitys/YL_DeviceVideo.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace YD_YiLu.Api.SmartSportsEntitys
|
||||
{
|
||||
/// <summary>
|
||||
/// 视频回放表(训练视频上传)
|
||||
/// </summary>
|
||||
public class YL_DeviceVideo
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键ID
|
||||
/// </summary>
|
||||
[Key]
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备序列号
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(64)]
|
||||
public string DeviceSerial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备类型
|
||||
/// </summary>
|
||||
public int DeviceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 训练序列号(一次训练唯一标识)
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(64)]
|
||||
public string VideoSerial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 视频访问地址(上传后返回)
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(500)]
|
||||
public string VideoUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件名称
|
||||
/// </summary>
|
||||
[MaxLength(200)]
|
||||
public string? FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件大小(KB)
|
||||
/// </summary>
|
||||
public long? FileSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 视频时长(秒)
|
||||
/// </summary>
|
||||
public int? Duration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间戳(客户端上传)
|
||||
/// </summary>
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// </summary>
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态 0=无效 1=有效
|
||||
/// </summary>
|
||||
public int Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
[MaxLength(500)]
|
||||
public string? Remark { get; set; }
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user