LqUserProfileVersionQueryService.cs
6.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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
{
/// <summary>
/// 用户主档时点全量版本查询与还原(R-004)。
/// </summary>
[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;
}
/// <summary>
/// 按用户分页查询全量主档版本(新到旧);权限与字段审计列表一致。
/// </summary>
[HttpGet("logs")]
public async Task<dynamic> 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<UserEntity>()
.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<LqUserProfileVersionEntity>()
.Where(x => x.TargetUserId == targetUserId)
.OrderBy(x => x.VersionNo, OrderByType.Desc);
var data = await q.ToPagedListAsync(page.currentPage, page.pageSize);
return PageResult<LqUserProfileVersionEntity>.SqlSugarPageResult(data);
}
/// <summary>
/// 将 BASE_USER 还原为指定版本快照(密码/密钥保留当前库内值);成功后追加新版本记录。
/// </summary>
[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<LqUserProfileVersionEntity>()
.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<LqUserProfileFullSnapshotDto>(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<UserEntity>()
.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;
}
}
/// <summary>
/// 将快照中的业务字段覆盖到当前行,保留 Id、Password、Secretkey。
/// </summary>
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));
}
}
}
}