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;
///
/// 授权服务(客户级授权:对客户授权后,该客户能运行所有项目)
///
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;
}
///
/// 获取授权列表(销售只能查看自己创建的客户的授权)
///
public async Task>> 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>.Success(
PagedResult.Create(items, total, request.PageIndex, request.PageSize));
}
///
/// 获取授权详情
///
public async Task> GetAuthorizationByIdAsync(long id)
{
await MarkExpiredAsync();
var entity = await _db.Authorizations
.Include(a => a.User)
.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);
}
}
var authorizer = await _db.Users.FirstOrDefaultAsync(u => u.Id == entity.AuthorizedBy);
var dto = MapToDto(entity, entity.User, authorizer);
return ApiResponse.Success(dto);
}
///
/// 创建/重新授权(客户级授权):一个客户只能有一个授权记录。
/// 若客户已有授权(任意状态):Active 则续期(EndTime += days),Cancelled/Expired 则重置为 Active 重新开通。
/// 若客户无授权:创建新记录。
///
public async Task> 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.Fail("用户不存在");
}
// 销售数据隔离:销售只能为自己创建的客户授权
if (_currentUser.Role == UserRole.Sales && user.CreatedBy != _currentUser.UserId)
{
return ApiResponse.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.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.Success(MapToDto(entity, user, currentUser));
}
///
/// 批量授权:一个客户一个授权记录,对每个 UserId 调用复用/新建逻辑。
/// Active 则续期,Cancelled/Expired 则重置为 Active 重新开通,无授权则新建。
///
public async Task 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} 条");
}
///
/// 续期授权(按授权 ID 续期)
///
public async Task> RenewAuthorizationAsync(long id, int days)
{
if (days <= 0)
{
return ApiResponse.Fail("续期天数必须大于 0");
}
await MarkExpiredAsync();
var entity = await _db.Authorizations
.Include(a => a.User)
.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 状态的授权可续期;Cancelled/Expired 请使用重新授权功能
if (entity.Status != AuthorizationStatus.Active)
{
var hint = entity.Status == AuthorizationStatus.Cancelled
? "已取消的授权请使用重新授权功能"
: "已过期的授权请使用重新授权功能";
return ApiResponse.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.Success(MapToDto(entity, entity.User, authorizer), "续期成功");
}
///
/// 取消授权(仅 Active 状态可取消)
///
public async Task 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("授权已取消");
}
///
/// 检查用户是否有任何有效授权(客户级授权,不检查项目)
///
public async Task> 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.Success(hasAuth);
}
///
/// 获取当前客户的授权列表
///
public async Task>> 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>.Success(items);
}
///
/// 自动将过期授权标记为 Expired(EndTime < Now 且 Status == Active),并通知用户
///
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();
}
}
///
/// 映射为 DTO(不再映射项目字段)
///
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
};
}
}