修改User模块

This commit is contained in:
chfei committed 2026-10-23 19:01:28 +08:00
1 parent d6471f97d3
commit bb7162aaf6
21 files changed
+278 -336

No files matched your search

@@ -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("写入积分流水失败");
}