using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using NCC.Common.Enum;
using NCC.Common.Core.Manager;
using NCC.Common.Filter;
using NCC.Dependency;
using NCC.DynamicApiController;
using NCC.Extend.Entitys.lq_user_profile_version;
using NCC.Extend.Interfaces.UserProfileVersion;
using NCC.Extend.Models;
using NCC.FriendlyException;
using NCC.System.Entitys.Permission;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using SqlSugar;
namespace NCC.Extend
{
///
/// 用户主档时点全量版本查询与还原(R-004)。
///
[ApiDescriptionSettings(Tag = "绿纤用户主档版本", Name = "LqUserProfileVersion", Order = 201)]
[Route("api/Extend/LqUserProfileVersion")]
public class LqUserProfileVersionQueryService : IDynamicApiController, ITransient
{
private readonly ISqlSugarClient _db;
private readonly IUserManager _userManager;
private readonly IUserProfileVersionAppender _userProfileVersionAppender;
public LqUserProfileVersionQueryService(
ISqlSugarClient db,
IUserManager userManager,
IUserProfileVersionAppender userProfileVersionAppender)
{
_db = db;
_userManager = userManager;
_userProfileVersionAppender = userProfileVersionAppender;
}
///
/// 按用户分页查询全量主档版本(新到旧);权限与字段审计列表一致。
///
[HttpGet("logs")]
public async Task GetVersionLogs([FromQuery] string targetUserId, [FromQuery] PageInputBase page)
{
if (string.IsNullOrWhiteSpace(targetUserId))
{
throw NCCException.Oh("targetUserId 不能为空");
}
page ??= new PageInputBase();
var userInfo = await _userManager.GetUserInfo();
var target = await _db.Queryable()
.Where(x => x.Id == targetUserId && x.DeleteMark == null)
.FirstAsync();
if (target == null)
{
throw NCCException.Oh("目标用户不存在");
}
if (!userInfo.isAdministrator
&& !userInfo.dataScope.Any(it => it.organizeId == target.OrganizeId && it.Edit == true))
{
throw NCCException.Oh(ErrorCode.D1013);
}
var q = _db.Queryable()
.Where(x => x.TargetUserId == targetUserId)
.OrderBy(x => x.VersionNo, OrderByType.Desc);
var data = await q.ToPagedListAsync(page.currentPage, page.pageSize);
return PageResult.SqlSugarPageResult(data);
}
///
/// 将 BASE_USER 还原为指定版本快照(密码/密钥保留当前库内值);成功后追加新版本记录。
///
[HttpPost("restore")]
public async Task RestoreToVersion([FromBody] LqUserProfileVersionRestoreInput input)
{
if (string.IsNullOrWhiteSpace(input?.VersionId))
{
throw NCCException.Oh("versionId 不能为空");
}
var userInfo = await _userManager.GetUserInfo();
var verRows = await _db.Queryable()
.Where(x => x.Id == input.VersionId)
.Take(1)
.ToListAsync();
var ver = verRows.FirstOrDefault();
if (ver == null)
{
throw NCCException.Oh("版本不存在");
}
LqUserProfileFullSnapshotDto dto;
try
{
dto = JsonConvert.DeserializeObject(ver.FullSnapshotJson);
}
catch
{
throw NCCException.Oh("快照 JSON 解析失败");
}
if (dto == null
|| dto.SchemaVersion < LqUserProfileSnapshotConstants.FullUserSchemaVersion
|| dto.User == null)
{
throw NCCException.Oh("该版本快照格式过旧,无法一键还原,请人工对照 JSON 修改主档");
}
var currentRows = await _db.Queryable()
.Where(x => x.Id == ver.TargetUserId)
.Take(1)
.ToListAsync();
var current = currentRows.FirstOrDefault();
if (current == null)
{
throw NCCException.Oh("用户不存在");
}
if (!userInfo.isAdministrator
&& !userInfo.dataScope.Any(it => it.organizeId == current.OrganizeId && it.Edit == true))
{
throw NCCException.Oh(ErrorCode.D1013);
}
CopyUserSnapshotOntoRow(current, dto.User);
try
{
_db.Ado.BeginTran();
var ok = await _db.Updateable(current).ExecuteCommandAsync();
if (ok <= 0)
{
throw NCCException.Oh("还原写入失败");
}
await _userProfileVersionAppender.AfterUserProfilePersistedAsync(ver.TargetUserId, "RESTORE_FROM_VERSION", true);
_db.Ado.CommitTran();
}
catch
{
_db.Ado.RollbackTran();
throw;
}
}
///
/// 将快照中的业务字段覆盖到当前行,保留 Id、Password、Secretkey。
///
internal static void CopyUserSnapshotOntoRow(UserEntity target, UserEntity source)
{
if (target == null || source == null)
{
return;
}
foreach (var prop in typeof(UserEntity).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!prop.CanRead || !prop.CanWrite)
{
continue;
}
var n = prop.Name;
if (string.Equals(n, nameof(UserEntity.Id), StringComparison.OrdinalIgnoreCase)
|| string.Equals(n, nameof(UserEntity.Password), StringComparison.OrdinalIgnoreCase)
|| string.Equals(n, nameof(UserEntity.Secretkey), StringComparison.OrdinalIgnoreCase))
{
continue;
}
prop.SetValue(target, prop.GetValue(source));
}
}
}
}