610 lines
22 KiB
C#
610 lines
22 KiB
C#
using Microsoft.EntityFrameworkCore;
|
||
using MilliSim.Common.Dtos;
|
||
using MilliSim.Common.Entities;
|
||
using MilliSim.Common.Models;
|
||
using MilliSim.Data;
|
||
using MilliSim.Services.Infrastructure;
|
||
|
||
namespace MilliSim.Services;
|
||
|
||
/// <summary>
|
||
/// 授权服务(客户级授权:对客户授权后,该客户能运行所有项目)
|
||
/// </summary>
|
||
public class AuthorizationService : IAuthorizationService
|
||
{
|
||
private readonly MilliSimDbContext _db;
|
||
private readonly ICurrentUserService _currentUser;
|
||
private readonly IOperationLogService _operationLog;
|
||
private readonly INotificationService _notification;
|
||
|
||
public AuthorizationService(
|
||
MilliSimDbContext db,
|
||
ICurrentUserService currentUser,
|
||
IOperationLogService operationLog,
|
||
INotificationService notification)
|
||
{
|
||
_db = db;
|
||
_currentUser = currentUser;
|
||
_operationLog = operationLog;
|
||
_notification = notification;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取授权列表(销售只能查看自己创建的客户的授权)
|
||
/// </summary>
|
||
public async Task<ApiResponse<PagedResult<AuthorizationDto>>> GetAuthorizationsAsync(AuthorizationQueryRequest request)
|
||
{
|
||
// 自动将过期授权标记为 Expired
|
||
await MarkExpiredAsync();
|
||
|
||
var query = from a in _db.Authorizations
|
||
join u in _db.Users on a.UserId equals u.Id into ug
|
||
from u in ug.DefaultIfEmpty()
|
||
join au in _db.Users on a.AuthorizedBy equals au.Id into aug
|
||
from au in aug.DefaultIfEmpty()
|
||
select new { a, u, au };
|
||
|
||
// 销售数据隔离:销售只能查看自己创建的客户的授权
|
||
if (_currentUser.Role == UserRole.Sales)
|
||
{
|
||
query = query.Where(x => x.u.CreatedBy == _currentUser.UserId);
|
||
}
|
||
|
||
if (request.UserId.HasValue)
|
||
{
|
||
query = query.Where(x => x.a.UserId == request.UserId.Value);
|
||
}
|
||
|
||
if (request.Status.HasValue)
|
||
{
|
||
query = query.Where(x => x.a.Status == request.Status.Value);
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(request.Keyword))
|
||
{
|
||
var keyword = request.Keyword.Trim();
|
||
query = query.Where(x =>
|
||
(x.u != null && x.u.Username.Contains(keyword)) ||
|
||
(x.u != null && x.u.Nickname.Contains(keyword)));
|
||
}
|
||
|
||
var total = await query.LongCountAsync();
|
||
|
||
var items = await query
|
||
.OrderByDescending(x => x.a.CreatedAt)
|
||
.Skip((request.PageIndex - 1) * request.PageSize)
|
||
.Take(request.PageSize)
|
||
.Select(x => new AuthorizationDto
|
||
{
|
||
Id = x.a.Id,
|
||
UserId = x.a.UserId,
|
||
Username = x.u != null ? x.u.Username : string.Empty,
|
||
UserNickname = x.u != null ? x.u.Nickname : string.Empty,
|
||
AuthorizedBy = x.a.AuthorizedBy,
|
||
AuthorizedByName = x.au != null ? x.au.Nickname : string.Empty,
|
||
StartTime = x.a.StartTime,
|
||
EndTime = x.a.EndTime,
|
||
Status = x.a.Status,
|
||
StatusName = x.a.Status.ToString(),
|
||
CreatedAt = x.a.CreatedAt
|
||
})
|
||
.ToListAsync();
|
||
|
||
return ApiResponse<PagedResult<AuthorizationDto>>.Success(
|
||
PagedResult<AuthorizationDto>.Create(items, total, request.PageIndex, request.PageSize));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取授权详情
|
||
/// </summary>
|
||
public async Task<ApiResponse<AuthorizationDto>> GetAuthorizationByIdAsync(long id)
|
||
{
|
||
await MarkExpiredAsync();
|
||
|
||
var entity = await _db.Authorizations
|
||
.Include(a => a.User)
|
||
.FirstOrDefaultAsync(a => a.Id == id);
|
||
|
||
if (entity == null)
|
||
{
|
||
return ApiResponse<AuthorizationDto>.Fail("授权不存在", 404);
|
||
}
|
||
|
||
// 销售数据隔离
|
||
if (_currentUser.Role == UserRole.Sales)
|
||
{
|
||
var customer = await _db.Users.FirstOrDefaultAsync(u => u.Id == entity.UserId);
|
||
if (customer == null || customer.CreatedBy != _currentUser.UserId)
|
||
{
|
||
return ApiResponse<AuthorizationDto>.Fail("无权查看该授权", 403);
|
||
}
|
||
}
|
||
|
||
var authorizer = await _db.Users.FirstOrDefaultAsync(u => u.Id == entity.AuthorizedBy);
|
||
|
||
var dto = MapToDto(entity, entity.User, authorizer);
|
||
return ApiResponse<AuthorizationDto>.Success(dto);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建/重新授权(客户级授权):一个客户只能有一个授权记录。
|
||
/// 若客户已有授权(任意状态):Active 则续期(EndTime += days),Cancelled/Expired 则重置为 Active 重新开通。
|
||
/// 若客户无授权:创建新记录。
|
||
/// </summary>
|
||
public async Task<ApiResponse<AuthorizationDto>> CreateAuthorizationAsync(CreateAuthorizationRequest request)
|
||
{
|
||
if (request.Days <= 0)
|
||
{
|
||
request.Days = 365;
|
||
}
|
||
|
||
// 校验用户存在
|
||
var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == request.UserId);
|
||
if (user == null)
|
||
{
|
||
return ApiResponse<AuthorizationDto>.Fail("用户不存在");
|
||
}
|
||
|
||
// 销售数据隔离:销售只能为自己创建的客户授权
|
||
if (_currentUser.Role == UserRole.Sales && user.CreatedBy != _currentUser.UserId)
|
||
{
|
||
return ApiResponse<AuthorizationDto>.Fail("无权为该用户授权", 403);
|
||
}
|
||
|
||
await MarkExpiredAsync();
|
||
|
||
// 一个客户只能有一个授权记录:查询任意状态的现有授权
|
||
var existing = await _db.Authorizations
|
||
.FirstOrDefaultAsync(a => a.UserId == request.UserId);
|
||
|
||
var now = DateTime.Now;
|
||
|
||
if (existing != null)
|
||
{
|
||
// 捕获原始状态,用于区分续期/重新授权的通知文案
|
||
var wasActive = existing.Status == AuthorizationStatus.Active;
|
||
|
||
if (wasActive)
|
||
{
|
||
// 续期:在原 EndTime 基础上加天数(如果已过期则从当前时间开始)
|
||
var baseTime = existing.EndTime > now ? existing.EndTime : now;
|
||
existing.EndTime = baseTime.AddDays(request.Days);
|
||
}
|
||
else
|
||
{
|
||
// 重新授权:重置起止时间和状态(Cancelled / Expired → Active)
|
||
existing.StartTime = now;
|
||
existing.EndTime = now.AddDays(request.Days);
|
||
existing.Status = AuthorizationStatus.Active;
|
||
existing.ProjectId = null; // 客户级授权
|
||
}
|
||
existing.AuthorizedBy = _currentUser.UserId;
|
||
existing.UpdatedAt = now;
|
||
await _db.SaveChangesAsync();
|
||
|
||
var actionLabel = wasActive ? "续期" : "重新授权";
|
||
await _operationLog.LogAsync("授权", actionLabel,
|
||
$"用户 {user.Username} {actionLabel} {request.Days} 天,授权 ID: {existing.Id}");
|
||
|
||
// 通知被授权用户
|
||
try
|
||
{
|
||
var title = wasActive ? "授权已续期" : "授权已重新开通";
|
||
var content = wasActive
|
||
? $"您的客户级授权已续期 {request.Days} 天,新到期时间:{existing.EndTime:yyyy-MM-dd}。"
|
||
: $"您的客户级授权已重新开通 {request.Days} 天,到期时间:{existing.EndTime:yyyy-MM-dd},可体验全部项目。";
|
||
await _notification.CreateNotificationAsync(
|
||
NotificationType.Authorization,
|
||
title,
|
||
content,
|
||
userId: user.Id,
|
||
eventId: $"{(wasActive ? "auth-renewed" : "auth-reactivated")}-{existing.Id}-{existing.UpdatedAt:yyyyMMddHHmmss}");
|
||
}
|
||
catch
|
||
{
|
||
// 通知发送失败不影响业务流程
|
||
}
|
||
|
||
var authorizer = await _db.Users.FirstOrDefaultAsync(u => u.Id == existing.AuthorizedBy);
|
||
var msg = wasActive ? "授权续期成功" : "授权重新开通成功";
|
||
return ApiResponse<AuthorizationDto>.Success(MapToDto(existing, user, authorizer), msg);
|
||
}
|
||
|
||
// 创建新授权(客户级授权,不关联项目)
|
||
var entity = new Authorization
|
||
{
|
||
UserId = request.UserId,
|
||
AuthorizedBy = _currentUser.UserId,
|
||
StartTime = now,
|
||
EndTime = now.AddDays(request.Days),
|
||
Status = AuthorizationStatus.Active,
|
||
ProjectId = null
|
||
};
|
||
|
||
_db.Authorizations.Add(entity);
|
||
await _db.SaveChangesAsync();
|
||
|
||
await _operationLog.LogAsync("授权", "创建",
|
||
$"为用户 {user.Username} 创建客户级授权,{request.Days} 天,授权 ID: {entity.Id}");
|
||
|
||
// 通知被授权用户:授权开通
|
||
try
|
||
{
|
||
await _notification.CreateNotificationAsync(
|
||
NotificationType.Authorization,
|
||
"授权已开通",
|
||
$"您已获得 {request.Days} 天客户级授权,可体验全部项目,到期时间:{entity.EndTime:yyyy-MM-dd}。",
|
||
userId: user.Id,
|
||
eventId: $"auth-created-{entity.Id}");
|
||
}
|
||
catch
|
||
{
|
||
// 通知发送失败不影响业务流程
|
||
}
|
||
|
||
var currentUser = await _db.Users.FirstOrDefaultAsync(u => u.Id == _currentUser.UserId);
|
||
return ApiResponse<AuthorizationDto>.Success(MapToDto(entity, user, currentUser));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量授权:一个客户一个授权记录,对每个 UserId 调用复用/新建逻辑。
|
||
/// Active 则续期,Cancelled/Expired 则重置为 Active 重新开通,无授权则新建。
|
||
/// </summary>
|
||
public async Task<ApiResponse> BatchAuthorizeAsync(BatchAuthorizationRequest request)
|
||
{
|
||
if (request.UserIds == null || request.UserIds.Count == 0)
|
||
{
|
||
return ApiResponse.Fail("用户列表不能为空");
|
||
}
|
||
|
||
if (request.Days <= 0)
|
||
{
|
||
request.Days = 365;
|
||
}
|
||
|
||
await MarkExpiredAsync();
|
||
|
||
var now = DateTime.Now;
|
||
var successCount = 0;
|
||
var skipCount = 0;
|
||
|
||
foreach (var userId in request.UserIds.Distinct())
|
||
{
|
||
var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == userId);
|
||
if (user == null)
|
||
{
|
||
skipCount++;
|
||
continue;
|
||
}
|
||
|
||
// 销售数据隔离
|
||
if (_currentUser.Role == UserRole.Sales && user.CreatedBy != _currentUser.UserId)
|
||
{
|
||
skipCount++;
|
||
continue;
|
||
}
|
||
|
||
// 一个客户一个授权记录:查询任意状态的现有授权
|
||
var existing = await _db.Authorizations
|
||
.FirstOrDefaultAsync(a => a.UserId == userId);
|
||
|
||
if (existing != null)
|
||
{
|
||
var wasActive = existing.Status == AuthorizationStatus.Active;
|
||
|
||
if (wasActive)
|
||
{
|
||
// 续期
|
||
var baseTime = existing.EndTime > now ? existing.EndTime : now;
|
||
existing.EndTime = baseTime.AddDays(request.Days);
|
||
}
|
||
else
|
||
{
|
||
// 重新授权(Cancelled / Expired → Active)
|
||
existing.StartTime = now;
|
||
existing.EndTime = now.AddDays(request.Days);
|
||
existing.Status = AuthorizationStatus.Active;
|
||
existing.ProjectId = null;
|
||
}
|
||
existing.AuthorizedBy = _currentUser.UserId;
|
||
existing.UpdatedAt = now;
|
||
successCount++;
|
||
|
||
// 通知用户
|
||
try
|
||
{
|
||
var title = wasActive ? "授权已续期" : "授权已重新开通";
|
||
var content = wasActive
|
||
? $"您的客户级授权已续期 {request.Days} 天,新到期时间:{existing.EndTime:yyyy-MM-dd}。"
|
||
: $"您的客户级授权已重新开通 {request.Days} 天,到期时间:{existing.EndTime:yyyy-MM-dd},可体验全部项目。";
|
||
await _notification.CreateNotificationAsync(
|
||
NotificationType.Authorization,
|
||
title,
|
||
content,
|
||
userId: user.Id,
|
||
eventId: $"{(wasActive ? "auth-renewed" : "auth-reactivated")}-{existing.Id}-{existing.UpdatedAt:yyyyMMddHHmmss}");
|
||
}
|
||
catch
|
||
{
|
||
// 通知失败不影响业务
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 创建新授权(客户级授权,不关联项目)
|
||
var entity = new Authorization
|
||
{
|
||
UserId = userId,
|
||
AuthorizedBy = _currentUser.UserId,
|
||
StartTime = now,
|
||
EndTime = now.AddDays(request.Days),
|
||
Status = AuthorizationStatus.Active,
|
||
ProjectId = null
|
||
};
|
||
_db.Authorizations.Add(entity);
|
||
successCount++;
|
||
|
||
// 先保存以获取 entity.Id
|
||
try
|
||
{
|
||
await _db.SaveChangesAsync();
|
||
await _notification.CreateNotificationAsync(
|
||
NotificationType.Authorization,
|
||
"授权已开通",
|
||
$"您已获得 {request.Days} 天客户级授权,可体验全部项目,到期时间:{entity.EndTime:yyyy-MM-dd}。",
|
||
userId: user.Id,
|
||
eventId: $"auth-created-{entity.Id}");
|
||
}
|
||
catch
|
||
{
|
||
// 通知失败不影响业务
|
||
}
|
||
}
|
||
}
|
||
|
||
await _db.SaveChangesAsync();
|
||
|
||
await _operationLog.LogAsync("授权", "批量授权",
|
||
$"批量授权:成功 {successCount} 条,跳过 {skipCount} 条,{request.Days} 天");
|
||
|
||
return ApiResponse.Success($"批量授权完成,成功 {successCount} 条,跳过 {skipCount} 条");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 续期授权(按授权 ID 续期)
|
||
/// </summary>
|
||
public async Task<ApiResponse<AuthorizationDto>> RenewAuthorizationAsync(long id, int days)
|
||
{
|
||
if (days <= 0)
|
||
{
|
||
return ApiResponse<AuthorizationDto>.Fail("续期天数必须大于 0");
|
||
}
|
||
|
||
await MarkExpiredAsync();
|
||
|
||
var entity = await _db.Authorizations
|
||
.Include(a => a.User)
|
||
.FirstOrDefaultAsync(a => a.Id == id);
|
||
|
||
if (entity == null)
|
||
{
|
||
return ApiResponse<AuthorizationDto>.Fail("授权不存在", 404);
|
||
}
|
||
|
||
// 销售数据隔离
|
||
if (_currentUser.Role == UserRole.Sales)
|
||
{
|
||
var customer = await _db.Users.FirstOrDefaultAsync(u => u.Id == entity.UserId);
|
||
if (customer == null || customer.CreatedBy != _currentUser.UserId)
|
||
{
|
||
return ApiResponse<AuthorizationDto>.Fail("无权操作该授权", 403);
|
||
}
|
||
}
|
||
|
||
// 仅 Active 状态的授权可续期;Cancelled/Expired 请使用重新授权功能
|
||
if (entity.Status != AuthorizationStatus.Active)
|
||
{
|
||
var hint = entity.Status == AuthorizationStatus.Cancelled
|
||
? "已取消的授权请使用重新授权功能"
|
||
: "已过期的授权请使用重新授权功能";
|
||
return ApiResponse<AuthorizationDto>.Fail(hint);
|
||
}
|
||
|
||
// 续期:在原 EndTime 基础上加天数(如果已过期则从当前时间开始)
|
||
var baseTime = entity.EndTime > DateTime.Now ? entity.EndTime : DateTime.Now;
|
||
entity.EndTime = baseTime.AddDays(days);
|
||
entity.Status = AuthorizationStatus.Active;
|
||
entity.UpdatedAt = DateTime.Now;
|
||
|
||
await _db.SaveChangesAsync();
|
||
|
||
var authorizer = await _db.Users.FirstOrDefaultAsync(u => u.Id == entity.AuthorizedBy);
|
||
|
||
await _operationLog.LogAsync("授权", "续期",
|
||
$"授权 ID: {id} 续期 {days} 天");
|
||
|
||
// 通知被授权用户:续期
|
||
try
|
||
{
|
||
await _notification.CreateNotificationAsync(
|
||
NotificationType.Authorization,
|
||
"授权已续期",
|
||
$"您的客户级授权已续期 {days} 天,新到期时间:{entity.EndTime:yyyy-MM-dd}。",
|
||
userId: entity.UserId,
|
||
eventId: $"auth-renewed-{entity.Id}-{entity.UpdatedAt:yyyyMMddHHmmss}");
|
||
}
|
||
catch
|
||
{
|
||
// 通知失败不影响业务
|
||
}
|
||
|
||
return ApiResponse<AuthorizationDto>.Success(MapToDto(entity, entity.User, authorizer), "续期成功");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消授权(仅 Active 状态可取消)
|
||
/// </summary>
|
||
public async Task<ApiResponse> CancelAuthorizationAsync(long id)
|
||
{
|
||
var entity = await _db.Authorizations.FirstOrDefaultAsync(a => a.Id == id);
|
||
if (entity == null)
|
||
{
|
||
return ApiResponse.Fail("授权不存在", 404);
|
||
}
|
||
|
||
// 销售数据隔离
|
||
if (_currentUser.Role == UserRole.Sales)
|
||
{
|
||
var customer = await _db.Users.FirstOrDefaultAsync(u => u.Id == entity.UserId);
|
||
if (customer == null || customer.CreatedBy != _currentUser.UserId)
|
||
{
|
||
return ApiResponse.Fail("无权操作该授权", 403);
|
||
}
|
||
}
|
||
|
||
// 仅 Active 状态可取消;已取消/已过期的不可重复取消
|
||
if (entity.Status == AuthorizationStatus.Cancelled)
|
||
{
|
||
return ApiResponse.Fail("该授权已取消,无需重复操作");
|
||
}
|
||
if (entity.Status == AuthorizationStatus.Expired)
|
||
{
|
||
return ApiResponse.Fail("该授权已过期,请使用重新授权功能");
|
||
}
|
||
|
||
entity.Status = AuthorizationStatus.Cancelled;
|
||
entity.UpdatedAt = DateTime.Now;
|
||
await _db.SaveChangesAsync();
|
||
|
||
await _operationLog.LogAsync("授权", "取消",
|
||
$"取消授权 ID: {id}");
|
||
|
||
// 通知被授权用户:授权已取消
|
||
try
|
||
{
|
||
await _notification.CreateNotificationAsync(
|
||
NotificationType.Authorization,
|
||
"授权已取消",
|
||
"您的客户级授权已被取消。如有疑问请联系销售或客服。",
|
||
userId: entity.UserId,
|
||
eventId: $"auth-cancelled-{entity.Id}");
|
||
}
|
||
catch
|
||
{
|
||
// 通知失败不影响业务
|
||
}
|
||
|
||
return ApiResponse.Success("授权已取消");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查用户是否有任何有效授权(客户级授权,不检查项目)
|
||
/// </summary>
|
||
public async Task<ApiResponse<bool>> CheckAuthorizationAsync(long userId)
|
||
{
|
||
await MarkExpiredAsync();
|
||
|
||
var hasAuth = await _db.Authorizations
|
||
.AnyAsync(a => a.UserId == userId
|
||
&& a.Status == AuthorizationStatus.Active
|
||
&& a.EndTime > DateTime.Now);
|
||
|
||
return ApiResponse<bool>.Success(hasAuth);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前客户的授权列表
|
||
/// </summary>
|
||
public async Task<ApiResponse<List<AuthorizationDto>>> GetMyAuthorizationsAsync()
|
||
{
|
||
await MarkExpiredAsync();
|
||
|
||
var userId = _currentUser.UserId;
|
||
|
||
var query = from a in _db.Authorizations
|
||
join u in _db.Users on a.UserId equals u.Id into ug
|
||
from u in ug.DefaultIfEmpty()
|
||
join au in _db.Users on a.AuthorizedBy equals au.Id into aug
|
||
from au in aug.DefaultIfEmpty()
|
||
where a.UserId == userId
|
||
select new { a, u, au };
|
||
|
||
var items = await query
|
||
.OrderByDescending(x => x.a.CreatedAt)
|
||
.Select(x => new AuthorizationDto
|
||
{
|
||
Id = x.a.Id,
|
||
UserId = x.a.UserId,
|
||
Username = x.u != null ? x.u.Username : string.Empty,
|
||
UserNickname = x.u != null ? x.u.Nickname : string.Empty,
|
||
AuthorizedBy = x.a.AuthorizedBy,
|
||
AuthorizedByName = x.au != null ? x.au.Nickname : string.Empty,
|
||
StartTime = x.a.StartTime,
|
||
EndTime = x.a.EndTime,
|
||
Status = x.a.Status,
|
||
StatusName = x.a.Status.ToString(),
|
||
CreatedAt = x.a.CreatedAt
|
||
})
|
||
.ToListAsync();
|
||
|
||
return ApiResponse<List<AuthorizationDto>>.Success(items);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动将过期授权标记为 Expired(EndTime < Now 且 Status == Active),并通知用户
|
||
/// </summary>
|
||
private async Task MarkExpiredAsync()
|
||
{
|
||
var now = DateTime.Now;
|
||
var expired = await _db.Authorizations
|
||
.Where(a => a.Status == AuthorizationStatus.Active && a.EndTime < now)
|
||
.ToListAsync();
|
||
|
||
if (expired.Count > 0)
|
||
{
|
||
foreach (var item in expired)
|
||
{
|
||
item.Status = AuthorizationStatus.Expired;
|
||
item.UpdatedAt = now;
|
||
|
||
// 通知用户:授权已过期
|
||
try
|
||
{
|
||
await _notification.CreateNotificationAsync(
|
||
NotificationType.Authorization,
|
||
"授权已过期",
|
||
"您的客户级授权已过期,请联系销售续期后继续体验项目。",
|
||
userId: item.UserId,
|
||
eventId: $"auth-expired-{item.Id}");
|
||
}
|
||
catch
|
||
{
|
||
// 通知失败不影响业务
|
||
}
|
||
}
|
||
await _db.SaveChangesAsync();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 映射为 DTO(不再映射项目字段)
|
||
/// </summary>
|
||
private static AuthorizationDto MapToDto(Authorization entity, User? user, User? authorizer)
|
||
{
|
||
return new AuthorizationDto
|
||
{
|
||
Id = entity.Id,
|
||
UserId = entity.UserId,
|
||
Username = user?.Username ?? string.Empty,
|
||
UserNickname = user?.Nickname ?? string.Empty,
|
||
AuthorizedBy = entity.AuthorizedBy,
|
||
AuthorizedByName = authorizer?.Nickname ?? string.Empty,
|
||
StartTime = entity.StartTime,
|
||
EndTime = entity.EndTime,
|
||
Status = entity.Status,
|
||
StatusName = entity.Status.ToString(),
|
||
CreatedAt = entity.CreatedAt
|
||
};
|
||
}
|
||
}
|