修改User模块
This commit is contained in:
1 parent
d6471f97d3
commit
bb7162aaf6
21 files changed
+278
-336
No files matched your search
@@ -1,3 +1,4 @@
|
||||
using Store.Users.Contracts.Enums;
|
||||
using Store.Users.Entities;
|
||||
|
||||
namespace Store.Users.Services;
|
||||
@@ -5,14 +6,19 @@ namespace Store.Users.Services;
|
||||
/// <summary>统一账户管理范围:管理员管理全部,主账号管理本人及直属子账号。</summary>
|
||||
internal static class UserAccessPolicy
|
||||
{
|
||||
public static bool IsAdmin(UserEntity actor) => actor.SaLevel == 1;
|
||||
public static bool IsAdmin(UserEntity actor) => actor.SaLevel == UserAdminLevel.SuperAdmin;
|
||||
|
||||
public static bool Owns(UserEntity actor, UserEntity target) => actor.IsMainUser
|
||||
&& string.Equals(target.ParentUserName, actor.UserName, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public static bool CanRead(UserEntity actor, UserEntity target) =>
|
||||
IsAdmin(actor) || actor.Id == target.Id || Owns(actor, target);
|
||||
CanUseSelfService(actor, target);
|
||||
|
||||
/// <summary>子账号可查询本人、修改本人密码、提交充值备注和为本人店铺续费。</summary>
|
||||
public static bool CanUseSelfService(UserEntity actor, UserEntity target) =>
|
||||
actor.Id == target.Id || CanManage(actor, target);
|
||||
|
||||
/// <summary>管理员不受归属限制;普通主账号不能接管具有管理员身份的子账号。</summary>
|
||||
public static bool CanManage(UserEntity actor, UserEntity target) =>
|
||||
IsAdmin(actor) || (!IsAdmin(target) && actor.IsMainUser && (actor.Id == target.Id || Owns(actor, target)));
|
||||
}
|
||||
@@ -14,14 +14,6 @@ public sealed class UserFundsService(IFreeSql orm, ICurrentUser currentUser) : I
|
||||
public async Task<UserFundsResponse> Change(UserFundsRequest request, bool points, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
// DTO 只检查非空,其余长度、金额和操作组合在服务中手动判断。
|
||||
UserInputChecks.Length(request.UserName, 20, "目标账户");
|
||||
UserInputChecks.Length(request.RequestId, 64, "请求号");
|
||||
UserInputChecks.Length(request.Remark, 255, "备注");
|
||||
if (request.Amount is < 0.01m or > 1000000m)
|
||||
throw new ValidationException("金额必须为0.01-1000000");
|
||||
if (decimal.Round(request.Amount, 2) != request.Amount)
|
||||
throw new ValidationException("金额最多保留两位小数");
|
||||
if (points ? request.Operation is not ("充值" or "转移") : request.Operation is not ("充值" or "消费"))
|
||||
throw new ValidationException("不支持的资金操作类型");
|
||||
ct.ThrowIfCancellationRequested();
|
||||
@@ -32,15 +24,12 @@ public sealed class UserFundsService(IFreeSql orm, ICurrentUser currentUser) : I
|
||||
var actor = await db.Select<UserEntity>().Where(u => u.UserName == currentUser.UserName).FirstAsync(ct)
|
||||
?? throw new UnauthorizedAccessException();
|
||||
bool transfer = points && request.Operation == "转移";
|
||||
if (!transfer && actor.SaLevel != 1) throw new AppException("充值和余额调整仅允许管理员操作", 403);
|
||||
var name = request.UserName.Trim();
|
||||
var target = await db.Select<UserEntity>().Where(u => u.UserName == name).FirstAsync(ct)
|
||||
?? throw new AppException("目标账户不存在");
|
||||
if (transfer && actor.Id == target.Id) throw new AppException("不能向自己转移积分");
|
||||
if (transfer && !UserAccessPolicy.CanManage(actor, target))
|
||||
throw new AppException("只能向管理范围内的账户转移积分", 403);
|
||||
// 双账户转移按相同主键顺序加锁,避免双方同时转账时产生反向锁顺序。
|
||||
var ids = transfer ? new[] { actor.Id, target.Id }.Order().ToArray() : new[] { target.Id };
|
||||
// 操作者和目标按主键顺序加锁,再按最新账户状态授权,避免权限变更与资金写入交错。
|
||||
var ids = new[] { actor.Id, target.Id }.Distinct().Order().ToArray();
|
||||
foreach (var id in ids)
|
||||
{
|
||||
var locked = await db.Select<UserEntity>().Where(u => u.Id == id).ForUpdate().FirstAsync(ct)
|
||||
@@ -48,6 +37,10 @@ public sealed class UserFundsService(IFreeSql orm, ICurrentUser currentUser) : I
|
||||
if (id == target.Id) target = locked;
|
||||
if (id == actor.Id) actor = locked;
|
||||
}
|
||||
if (transfer && !UserAccessPolicy.CanManage(actor, target))
|
||||
throw new AppException("只能向管理范围内的账户转移积分", 403);
|
||||
if (!transfer && !UserAccessPolicy.IsAdmin(actor))
|
||||
throw new AppException("充值和余额调整仅允许管理员操作", 403);
|
||||
var key = "user:" + request.RequestId.Trim();
|
||||
// 持有目标行锁后检查请求号,相同账户的并发重试只能执行一次。
|
||||
bool duplicate = points
|
||||
@@ -101,7 +94,7 @@ public sealed class UserFundsService(IFreeSql orm, ICurrentUser currentUser) : I
|
||||
if (await db.Insert(new UserPointsRecordEntity
|
||||
{
|
||||
UserName = user.UserName, UserNick = user.NickName, DeltaPoints = amount, NewPoints = value,
|
||||
OptionType = operation, OperatorType = actor.SaLevel == 1 ? "管理员" : "用户", OperatorName = actor.UserName,
|
||||
OptionType = operation, OperatorType = UserAccessPolicy.IsAdmin(actor) ? "管理员" : "用户", OperatorName = actor.UserName,
|
||||
CounterpartyAccountName = counterparty, OutTradeNo = key, Remark = remark, AddTime = DateTime.Now
|
||||
}).ExecuteAffrowsAsync(ct) != 1) throw new AppException("写入积分流水失败");
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Store.Users.Contracts.Dtos;
|
||||
using Store.Users.Dtos;
|
||||
|
||||
namespace Store.Users.Services;
|
||||
|
||||
/// <summary>User 服务共用的手动参数检查。DTO 只保留非空规则,其余检查在服务层执行。</summary>
|
||||
internal static class UserInputChecks
|
||||
{
|
||||
public static void Length(string? value, int max, string name, int min = 0)
|
||||
{
|
||||
if (value != null && (value.Length < min || value.Length > max))
|
||||
throw new ValidationException(min > 0 ? $"{name}长度必须为{min}-{max}个字符" : $"{name}长度不能超过{max}个字符");
|
||||
}
|
||||
|
||||
public static void PositiveId(long id)
|
||||
{
|
||||
if (id <= 0) throw new ValidationException("ID必须大于0");
|
||||
}
|
||||
|
||||
private static void Group(byte? group)
|
||||
{
|
||||
if (group.HasValue && group.Value is not (3 or 4 or 5 or 8 or 9 or 10 or 11))
|
||||
throw new ValidationException("用户分组必须为3、4、5、8、9、10或11");
|
||||
}
|
||||
|
||||
public static void Edit(UserEditRequest request)
|
||||
{
|
||||
Length(request.NickName, 50, "昵称");
|
||||
Length(request.Mobile, 30, "手机号");
|
||||
Length(request.CompanyName, 100, "公司名称");
|
||||
Length(request.Notes, 255, "备注");
|
||||
Length(request.CardId, 50, "卡号");
|
||||
Length(request.Authority, 1000, "权限");
|
||||
Length(request.ForbiddenPage, 1000, "禁止页面");
|
||||
Length(request.OemId, 50, "OEM编号");
|
||||
Group(request.GroupId);
|
||||
if (request.SaLevel.HasValue && request.SaLevel.Value is not (0 or 1))
|
||||
throw new ValidationException("管理员级别只能为0或1");
|
||||
if (request.ShopPointsPerMonth < 30) throw new ValidationException("每月续费积分不能低于30");
|
||||
if (request.PostagePerOrder is < 2.2m or > 999999m) throw new ValidationException("每单费用必须为2.2-999999元");
|
||||
if (request.RefundPostagePerOrder is < 2.5m or > 999999m) throw new ValidationException("退货费用必须为2.5-999999元");
|
||||
if (request.GiftOemAmountPerOrder is < 0m or > 999999m) throw new ValidationException("礼物附加费用必须为0-999999元");
|
||||
if (request.SyncLogisticsPerOrder is < 0m or > 999999m) throw new ValidationException("物流同步费用必须为0-999999元");
|
||||
}
|
||||
|
||||
public static void Search(UserSearchRequest request)
|
||||
{
|
||||
if (request.Page is < 1 or > 1000000) throw new ValidationException("页码必须为1-1000000");
|
||||
if (request.PageSize is < 1 or > 200) throw new ValidationException("每页条数必须为1-200");
|
||||
Length(request.Keyword, 100, "查询关键字");
|
||||
Length(request.ParentUserName, 20, "父账号");
|
||||
Group(request.GroupId);
|
||||
}
|
||||
|
||||
public static void Records(UserRecordSearchRequest request)
|
||||
{
|
||||
Search(request);
|
||||
Length(request.Operation, 20, "操作类型");
|
||||
Length(request.ShopId, 50, "店铺ID");
|
||||
Length(request.OrderId, 100, "订单ID");
|
||||
if (request.StartTime.HasValue && request.EndTime.HasValue && request.StartTime > request.EndTime)
|
||||
throw new ValidationException("开始时间不能晚于结束时间");
|
||||
}
|
||||
|
||||
public static void Renew(ShopRenewRequest request)
|
||||
{
|
||||
Length(request.ShopId.Trim(), 50, "店铺ID");
|
||||
if (request.RenewDuration is < 1 or > 120) throw new ValidationException("续费时长必须为1-120个月");
|
||||
Length(request.Remark?.Trim(), 255, "备注");
|
||||
}
|
||||
|
||||
public static void Qrcode(UserTopUpQrcodeRequest request)
|
||||
{
|
||||
Length(request.AgentCode, 50, "代理编码");
|
||||
Length(request.QrcodeBase64, 2 * 1024 * 1024, "二维码");
|
||||
var content = request.QrcodeBase64;
|
||||
if (content.StartsWith("data:image/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
int comma = content.IndexOf(',');
|
||||
if (comma < 0 || !content[..comma].EndsWith(";base64", StringComparison.OrdinalIgnoreCase))
|
||||
throw new ValidationException("二维码格式错误");
|
||||
content = content[(comma + 1)..];
|
||||
}
|
||||
bool valid;
|
||||
try { valid = Convert.FromBase64String(content).Length > 0; }
|
||||
catch (FormatException) { valid = false; }
|
||||
if (!valid) throw new ValidationException("二维码必须为有效的Base64数据");
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Store.Users.Contracts.Enums;
|
||||
using MAX.Framework.Validation;
|
||||
using MAX.Framework.Commons;
|
||||
using Store.Users.Contracts.Dtos;
|
||||
@@ -10,13 +11,11 @@ public sealed partial class UserManagementService
|
||||
public async Task<long> AddTopUpMessage(UserTopUpMessageRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Length(request.Message, 1000, "充值备注");
|
||||
UserInputChecks.Length(request.UserName, 20, "用户名");
|
||||
var actor = await Actor(orm, ct);
|
||||
var name = string.IsNullOrWhiteSpace(request.UserName) ? actor.UserName : request.UserName.Trim();
|
||||
var target = await orm.Select<UserEntity>().Where(u => u.UserName == name).FirstAsync(ct)
|
||||
?? throw new AppException("用户不存在");
|
||||
if (!UserAccessPolicy.CanManage(actor, target) && actor.Id != target.Id)
|
||||
if (!UserAccessPolicy.CanUseSelfService(actor, target))
|
||||
throw new AppException("无权为该账户提交充值备注", 403);
|
||||
return await orm.Insert(new UserTopUpMessageEntity
|
||||
{
|
||||
@@ -28,8 +27,6 @@ public sealed partial class UserManagementService
|
||||
public async Task UpdateTopUpMessage(UserTopUpResultRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.PositiveId(request.Id);
|
||||
UserInputChecks.Length(request.Result, 1000, "处理结果");
|
||||
var actor = await Actor(orm, ct);
|
||||
var message = await orm.Select<UserTopUpMessageEntity>().Where(m => m.Id == request.Id).FirstAsync(ct)
|
||||
?? throw new AppException("充值备注不存在");
|
||||
@@ -47,14 +44,13 @@ public sealed partial class UserManagementService
|
||||
public async Task<UserPage<UserTopUpMessageEntity>> TopUpMessages(UserSearchRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Search(request);
|
||||
var actor = await Actor(orm, ct);
|
||||
var select = orm.Select<UserTopUpMessageEntity>();
|
||||
if (!IsAdmin(actor))
|
||||
{
|
||||
if (actor.IsMainUser)
|
||||
select = select.Where(m => m.UserName == actor.UserName || orm.Select<UserEntity>()
|
||||
.Where(u => u.UserName == m.UserName && u.ParentUserName == actor.UserName).Any());
|
||||
.Where(u => u.UserName == m.UserName && u.ParentUserName == actor.UserName && (u.SaLevel == null || u.SaLevel != UserAdminLevel.SuperAdmin)).Any());
|
||||
else select = select.Where(m => m.UserName == actor.UserName);
|
||||
}
|
||||
var total = await select.CountAsync(ct);
|
||||
@@ -66,7 +62,6 @@ public sealed partial class UserManagementService
|
||||
public async Task UpdateTopUpQrcode(UserTopUpQrcodeRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Qrcode(request);
|
||||
var actor = await Actor(orm, ct);
|
||||
if (!IsAdmin(actor)) throw new AppException("仅管理员可以维护充值二维码", 403);
|
||||
await orm.InsertOrUpdate<UserTopUpQrcodeEntity>().SetSource(new UserTopUpQrcodeEntity
|
||||
@@ -79,7 +74,6 @@ public sealed partial class UserManagementService
|
||||
public async Task<string?> GetTopUpQrcode(string agentCode, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(new UserTopUpQrcodeQuery { AgentCode = agentCode?.Trim()! });
|
||||
UserInputChecks.Length(agentCode.Trim(), 50, "代理编码");
|
||||
await Actor(orm, ct);
|
||||
var code = agentCode.Trim();
|
||||
var item = await orm.Select<UserTopUpQrcodeEntity>().Where(q => q.AgentCode == code).FirstAsync(ct);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Store.Users.Contracts.Enums;
|
||||
using MAX.Framework.Validation;
|
||||
using FreeSql;
|
||||
using MAX.Core.DependencyInjection;
|
||||
@@ -30,7 +31,6 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
var actor = await Actor(orm, ct);
|
||||
var name = string.IsNullOrWhiteSpace(userName) ? actor.UserName : userName.Trim();
|
||||
RequestValidator.Validate(new UserNameRequest { UserName = name });
|
||||
UserInputChecks.Length(name, 20, "用户名");
|
||||
var target = await orm.Select<UserEntity>().Where(u => u.UserName == name).FirstAsync(ct)
|
||||
?? throw new AppException("用户不存在");
|
||||
if (!UserAccessPolicy.CanRead(actor, target))
|
||||
@@ -38,27 +38,12 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
return await query.GetUser(target.UserName);
|
||||
}
|
||||
|
||||
/// <summary>解析代操作目标,服务端校验管理员或主子账号归属,省略用户名时操作本人。</summary>
|
||||
public async Task<string> GetManagedUserName(string? userName, CancellationToken ct)
|
||||
{
|
||||
var actor = await Actor(orm, ct);
|
||||
var name = string.IsNullOrWhiteSpace(userName) ? actor.UserName : userName.Trim();
|
||||
UserInputChecks.Length(name, 20, "用户名");
|
||||
var target = await orm.Select<UserEntity>().Where(u => u.UserName == name).FirstAsync(ct)
|
||||
?? throw new AppException("用户不存在");
|
||||
if (!UserAccessPolicy.CanManage(actor, target) && actor.Id != target.Id)
|
||||
throw new AppException("无权操作该账户", 403);
|
||||
return target.UserName;
|
||||
}
|
||||
|
||||
/// <summary>管理员查询全部账户;普通主账号的列表范围为本人和直属子账号。</summary>
|
||||
public async Task<UserPage<UserInfo>> Search(UserSearchRequest request, bool subUsersOnly, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Search(request);
|
||||
var actor = await Actor(orm, ct);
|
||||
if (!subUsersOnly && !IsAdmin(actor) && !actor.IsMainUser) throw new AppException("无权查询账户列表", 403);
|
||||
if (subUsersOnly && !IsAdmin(actor) && !actor.IsMainUser) throw new AppException("子账号不能管理其他账户", 403);
|
||||
if (!IsAdmin(actor) && !actor.IsMainUser) throw new AppException("子账号不能管理账户列表", 403);
|
||||
var select = orm.Select<UserEntity>();
|
||||
if (subUsersOnly)
|
||||
{
|
||||
@@ -75,11 +60,16 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
if (!string.IsNullOrWhiteSpace(parentUserName)
|
||||
&& !string.Equals(parentUserName, actor.UserName, StringComparison.OrdinalIgnoreCase))
|
||||
throw new AppException("只能查询自己的子账号", 403);
|
||||
select = select.Where(u => u.ParentUserName == actor.UserName);
|
||||
select = select.Where(u => u.ParentUserName == actor.UserName && (u.SaLevel == null || u.SaLevel != UserAdminLevel.SuperAdmin));
|
||||
}
|
||||
}
|
||||
else if (!IsAdmin(actor))
|
||||
select = select.Where(u => u.Id == actor.Id || u.ParentUserName == actor.UserName);
|
||||
select = select.Where(u => u.Id == actor.Id || (u.ParentUserName == actor.UserName && (u.SaLevel == null || u.SaLevel != UserAdminLevel.SuperAdmin)));
|
||||
if (!subUsersOnly && !string.IsNullOrWhiteSpace(request.ParentUserName))
|
||||
{
|
||||
var parentName = request.ParentUserName.Trim();
|
||||
select = select.Where(u => u.ParentUserName == parentName);
|
||||
}
|
||||
if (request.GroupId.HasValue) select = select.Where(u => u.GroupId == request.GroupId.Value);
|
||||
if (!string.IsNullOrWhiteSpace(request.Keyword))
|
||||
{
|
||||
@@ -96,10 +86,6 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
public async Task<UserInfo> Create(UserCreateRequest request, bool subUser, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Length(request.UserName, 20, "用户名");
|
||||
UserInputChecks.Length(request.Password, 128, "密码", 6);
|
||||
UserInputChecks.Edit(request);
|
||||
UserInputChecks.Length(request.ParentUserName, 20, "父账号");
|
||||
|
||||
using var work = orm.CreateUnitOfWork();
|
||||
work.GetOrBeginTransaction();
|
||||
@@ -124,9 +110,9 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
if (await db.Select<UserEntity>().Where(u => u.UserName == name).AnyAsync(ct)) throw new AppException("用户名已存在");
|
||||
var user = new UserEntity
|
||||
{
|
||||
UserName = name, ParentUserName = parentName, GroupId = subUser ? (byte)4 : (byte)3,
|
||||
UserName = name, ParentUserName = parentName, GroupId = subUser ? UserGroup.CustomerService : UserGroup.MainAccount,
|
||||
NickName = name, Mobile = "", CompanyName = "", Notes = "", CardId = "", Authority = "",
|
||||
ForbiddenPage = "", OemId = "", SaLevel = 0, ShopPointsPerMonth = 30,
|
||||
ForbiddenPage = "", OemId = "", SaLevel = UserAdminLevel.Normal, ShopPointsPerMonth = 30,
|
||||
PostagePerOrder = 2.2m, RefundPostagePerOrder = 2.5m, GiftOemAmountPerOrder = 0,
|
||||
SyncLogisticsPerOrder = 0, Points = 0, Balance = 0, RechargePointsTotal = 0,
|
||||
RechargeAmountTotal = 0, AddTime = DateTime.Now, MdyTime = DateTime.Now
|
||||
@@ -141,11 +127,10 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
}
|
||||
|
||||
/// <summary>修改资料。登录名保持稳定,避免破坏主子账号、商铺和历史流水中的关联。</summary>
|
||||
public async Task<UserInfo> Update(int id, UserEditRequest request, bool subUser, CancellationToken ct)
|
||||
public async Task<UserInfo> Update(UserUpdateRequest request, bool subUser, CancellationToken ct)
|
||||
{
|
||||
UserInputChecks.PositiveId(id);
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Edit(request);
|
||||
var id = request.Id;
|
||||
using var work = orm.CreateUnitOfWork();
|
||||
work.GetOrBeginTransaction();
|
||||
var db = work.Orm;
|
||||
@@ -173,14 +158,13 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
/// <summary>只有无余额、无积分、无子账号且无商铺关联的账户才能删除,保留历史流水。</summary>
|
||||
public async Task Delete(int id, CancellationToken ct)
|
||||
{
|
||||
UserInputChecks.PositiveId(id);
|
||||
RequestValidator.Validate(new UserIdRequest { Id = id });
|
||||
using var work = orm.CreateUnitOfWork();
|
||||
work.GetOrBeginTransaction();
|
||||
var db = work.Orm;
|
||||
var actor = await Actor(db, ct);
|
||||
var target = await db.Select<UserEntity>().Where(u => u.Id == id).ForUpdate().FirstAsync(ct)
|
||||
?? throw new AppException("用户不存在");
|
||||
if (actor.Id == target.Id) throw new AppException("不能删除当前登录账户");
|
||||
if (!UserAccessPolicy.CanManage(actor, target)) throw new AppException("无权删除该账户", 403);
|
||||
if ((target.Balance ?? 0) != 0 || target.Points != 0) throw new AppException("账户仍有余额或积分,不能删除");
|
||||
if (await db.Select<UserEntity>().Where(u => u.ParentUserName == target.UserName).AnyAsync(ct))
|
||||
@@ -194,18 +178,15 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
onlineUsers.SignOut(target.UserName);
|
||||
}
|
||||
|
||||
/// <summary>本人改密校验原密码,管理者可重置授权账户密码;比较并更新后失效目标账户会话。</summary>
|
||||
/// <summary>非管理员本人改密校验原密码,管理员可重置任意账户密码;比较并更新后失效目标账户会话。</summary>
|
||||
public async Task ChangePassword(ChangePasswordRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Length(request.OldPassword, 128, "原密码");
|
||||
UserInputChecks.Length(request.NewPassword, 128, "新密码", 6);
|
||||
var actor = await Actor(orm, ct);
|
||||
var name = string.IsNullOrWhiteSpace(request.UserName) ? actor.UserName : request.UserName.Trim();
|
||||
UserInputChecks.Length(name, 20, "用户名");
|
||||
var target = await orm.Select<UserEntity>().Where(u => u.UserName == name).FirstAsync(ct)
|
||||
?? throw new AppException("用户不存在");
|
||||
if (target.Id == actor.Id)
|
||||
if (target.Id == actor.Id && !IsAdmin(actor))
|
||||
{
|
||||
RequestValidator.Validate(new UserPasswordProofRequest { OldPassword = request.OldPassword! });
|
||||
if (!UserPassword.Verify(target, request.OldPassword!)) throw new AppException("原密码错误");
|
||||
@@ -223,7 +204,6 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
public async Task<UserPage<UserPointsRecordEntity>> Points(string? userName, UserRecordSearchRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Records(request);
|
||||
var user = await GetUser(userName, ct);
|
||||
var select = orm.Select<UserPointsRecordEntity>().Where(r => r.UserName == user.UserName);
|
||||
if (request.StartTime.HasValue) select = select.Where(r => r.AddTime >= request.StartTime.Value);
|
||||
@@ -239,7 +219,6 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
public async Task<UserPage<UserPaymentRecordEntity>> Balance(string? userName, UserRecordSearchRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
UserInputChecks.Records(request);
|
||||
var user = await GetUser(userName, ct);
|
||||
var select = orm.Select<UserPaymentRecordEntity>().Where(r => r.UserName == user.UserName && r.IsDeleted == 0);
|
||||
if (request.StartTime.HasValue) select = select.Where(r => r.CreateTime >= request.StartTime.Value);
|
||||
@@ -259,9 +238,9 @@ public sealed partial class UserManagementService(IFreeSql orm, ICurrentUser cur
|
||||
|
||||
private static void Apply(UserEntity user, UserEditRequest request, UserEntity actor, bool subUser)
|
||||
{
|
||||
// 合法分组值已由服务参数检查保证,这里检查主/子账号身份是否匹配。
|
||||
if (request.GroupId.HasValue && (subUser ? request.GroupId == 3 : request.GroupId != 3))
|
||||
throw new AppException("主账号分组必须为3,子账号分组必须为4、5、8、9、10或11");
|
||||
// 合法分组值已由 DTO 保证,这里检查主/子账号身份是否匹配。
|
||||
if (request.GroupId.HasValue && (subUser ? request.GroupId == UserGroup.MainAccount : request.GroupId != UserGroup.MainAccount))
|
||||
throw new AppException("主账号必须使用主号分组,子账号不能使用主号分组");
|
||||
// 普通主账号只能授予自己已有的权限,不能通过创建子账号提升权限。
|
||||
if (!IsAdmin(actor) && request.Authority != null)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@ internal sealed class UserQueryService(IUserRepository repository) : IUserQueryS
|
||||
public async Task<UserInfo> GetUser(string userName)
|
||||
{
|
||||
RequestValidator.Validate(new UserNameRequest { UserName = userName?.Trim()! });
|
||||
UserInputChecks.Length(userName.Trim(), 20, "用户名");
|
||||
var user = await repository.GetUser(userName.Trim()) ?? throw new AppException("用户不存在");
|
||||
return new UserMapper().ToInfo(user);
|
||||
}
|
||||
@@ -27,7 +26,6 @@ internal sealed class UserAuthenticationService(IFreeSql orm, IUserQueryService
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (!RequestValidator.TryValidate(new UserLoginRequest { UserName = userName?.Trim()!, Password = password }, out _))
|
||||
return null;
|
||||
if (userName.Trim().Length > 20 || password.Length > 128) return null;
|
||||
var user = await orm.Select<UserEntity>().Where(u => u.UserName == userName.Trim()).FirstAsync(cancellationToken);
|
||||
if (user == null || !UserPassword.Verify(user, password)) return null;
|
||||
return await query.GetUser(user.UserName);
|
||||
|
||||
Reference in new issue
Block a user