mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-10-14 05:32:27 +08:00
feat: ✨ 框架代码同步 (#346)
[skip ci] Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0.8 AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0.9 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
RUN apt update
|
||||
|
@@ -106,6 +106,7 @@ USDT
|
||||
本部门和下一级部门数据
|
||||
本部门和所有子部门数据
|
||||
本部门数据
|
||||
来自
|
||||
框架
|
||||
比较数据库结构
|
||||
注册
|
||||
@@ -153,6 +154,7 @@ USDT
|
||||
请求日志导出
|
||||
调试
|
||||
身份证
|
||||
转给
|
||||
转账支出
|
||||
转账收入
|
||||
运行
|
||||
|
@@ -144,10 +144,14 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
|
||||
#endif
|
||||
|
||||
#pragma warning disable S2326
|
||||
// ReSharper disable UnusedTypeParameter
|
||||
// ReSharper disable UnusedParameter.Local
|
||||
private static Task<IActionResult> GetExportFileStreamAsync<TExport>(
|
||||
string fileName
|
||||
, object list
|
||||
) {
|
||||
// ReSharper restore UnusedParameter.Local
|
||||
// ReSharper restore UnusedTypeParameter
|
||||
#pragma warning restore S2326
|
||||
throw new NotImplementedException();
|
||||
|
||||
|
@@ -7,9 +7,6 @@ namespace NetAdmin.Domain.Dto.Sys.DepositOrder;
|
||||
/// </summary>
|
||||
public record CreateDepositOrderReq : Sys_DepositOrder
|
||||
{
|
||||
/// <inheritdoc cref="Sys_DepositOrder.ActualPayAmount" />
|
||||
public override long ActualPayAmount { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_DepositOrder.DepositOrderStatus" />
|
||||
public override DepositOrderStatues DepositOrderStatus { get; init; } = DepositOrderStatues.WaitingForPayment;
|
||||
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CronExpressionDescriptor" Version="2.44.0"/>
|
||||
<PackageReference Include="Cronos" Version="0.11.1"/>
|
||||
<PackageReference Include="ExcelMapper" Version="6.0.611"/>
|
||||
<PackageReference Include="ExcelMapper" Version="6.0.612"/>
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@@ -110,7 +110,7 @@ public static class Chars
|
||||
public const string RGX_PAY_PASSWORD = """^\d{6}$""";
|
||||
public const string RGX_TELEPHONE = """^((\d{3,4}\-)|)\d{7,8}(|([-\u8f6c]{1}\d{1,5}))$""";
|
||||
public const string RGX_UP_AND_LOWER_NUMBER = """^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$""";
|
||||
public const string RGX_URL = """^https?://[\x00-\x1F\x21-\x7F]+$""";
|
||||
public const string RGX_URL = """^https?://[\x21-\x7E]+$""";
|
||||
public const string RGX_USERNAME = """^[\u4e00-\u9fa5a-zA-Z0-9_-]{2,16}$""";
|
||||
public const string RGX_VERIFY_CODE = """^\d{4}$""";
|
||||
|
||||
|
@@ -1,11 +1,10 @@
|
||||
#pragma warning disable RCS1194
|
||||
namespace NetAdmin.Infrastructure.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// NetAdmin异常基类
|
||||
/// </summary>
|
||||
#pragma warning disable RCS1194
|
||||
public abstract class NetAdminException(string message, Exception innerException) : Exception(message, innerException)
|
||||
#pragma warning restore RCS1194
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetAdminException" /> class.
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#pragma warning disable RCS1194
|
||||
namespace NetAdmin.Infrastructure.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
@@ -6,9 +7,5 @@ namespace NetAdmin.Infrastructure.Exceptions;
|
||||
/// <remarks>
|
||||
/// 外部接口调用未得到预期的结果
|
||||
/// </remarks>
|
||||
#pragma warning disable RCS1194
|
||||
public sealed class NetAdminExternalErrorException(string message, Exception innerException = null)
|
||||
#pragma warning restore RCS1194
|
||||
: NetAdminException(ErrorCodes.ExternalError, message, innerException)
|
||||
{
|
||||
}
|
||||
: NetAdminException(ErrorCodes.ExternalError, message, innerException);
|
@@ -1,3 +1,4 @@
|
||||
#pragma warning disable RCS1194
|
||||
namespace NetAdmin.Infrastructure.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
@@ -6,8 +7,4 @@ namespace NetAdmin.Infrastructure.Exceptions;
|
||||
/// <remarks>
|
||||
/// 并发执行时锁竞争失败
|
||||
/// </remarks>
|
||||
#pragma warning disable RCS1194
|
||||
public sealed class NetAdminGetLockerException(string message = null) : NetAdminInvalidOperationException(message)
|
||||
{
|
||||
}
|
||||
#pragma warning restore RCS1194
|
||||
public sealed class NetAdminGetLockerException(string message = null) : NetAdminInvalidOperationException(message);
|
@@ -1,3 +1,4 @@
|
||||
#pragma warning disable RCS1194, DesignedForInheritance
|
||||
namespace NetAdmin.Infrastructure.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
@@ -6,9 +7,5 @@ namespace NetAdmin.Infrastructure.Exceptions;
|
||||
/// <remarks>
|
||||
/// 参数格式错误、内容校验错误等
|
||||
/// </remarks>
|
||||
#pragma warning disable DesignedForInheritance, RCS1194
|
||||
public class NetAdminInvalidInputException(string message = null, Exception innerException = null)
|
||||
#pragma warning restore RCS1194, DesignedForInheritance
|
||||
: NetAdminException(ErrorCodes.InvalidInput, message, innerException)
|
||||
{
|
||||
}
|
||||
: NetAdminException(ErrorCodes.InvalidInput, message, innerException);
|
@@ -1,3 +1,4 @@
|
||||
#pragma warning disable RCS1194, DesignedForInheritance
|
||||
namespace NetAdmin.Infrastructure.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
@@ -6,9 +7,5 @@ namespace NetAdmin.Infrastructure.Exceptions;
|
||||
/// <remarks>
|
||||
/// 非正常的业务流程或逻辑
|
||||
/// </remarks>
|
||||
#pragma warning disable DesignedForInheritance, RCS1194
|
||||
public class NetAdminInvalidOperationException(string message, Exception innerException = null)
|
||||
#pragma warning restore RCS1194, DesignedForInheritance
|
||||
: NetAdminException(ErrorCodes.InvalidOperation, message, innerException)
|
||||
{
|
||||
}
|
||||
: NetAdminException(ErrorCodes.InvalidOperation, message, innerException);
|
@@ -1,3 +1,4 @@
|
||||
#pragma warning disable RCS1194
|
||||
namespace NetAdmin.Infrastructure.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
@@ -6,7 +7,5 @@ namespace NetAdmin.Infrastructure.Exceptions;
|
||||
/// <remarks>
|
||||
/// 运行结果是非预期的,例如事务失败回滚
|
||||
/// </remarks>
|
||||
#pragma warning disable RCS1194
|
||||
public sealed class NetAdminUnexpectedException(string message, Exception innerException = null)
|
||||
#pragma warning restore RCS1194
|
||||
: NetAdminException(ErrorCodes.Unexpected, message, innerException);
|
@@ -1,3 +1,4 @@
|
||||
#pragma warning disable RCS1194
|
||||
namespace NetAdmin.Infrastructure.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
@@ -6,10 +7,7 @@ namespace NetAdmin.Infrastructure.Exceptions;
|
||||
/// <remarks>
|
||||
/// 手动调用模型验证方法抛出
|
||||
/// </remarks>
|
||||
#pragma warning disable RCS1194
|
||||
public sealed class NetAdminValidateException(Dictionary<string, string[]> validateResults)
|
||||
#pragma warning restore RCS1194
|
||||
: NetAdminInvalidInputException
|
||||
public sealed class NetAdminValidateException(Dictionary<string, string[]> validateResults) : NetAdminInvalidInputException
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证结果
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<PackageReference Include="NetAdmin.FreeSql.DbContext" Version="1.1.8" Label="refs"/>
|
||||
<PackageReference Include="NetAdmin.FreeSql.Provider.Sqlite" Version="1.1.8" Label="refs"/>
|
||||
<PackageReference Include="Gurion" Version="1.2.17" Label="refs"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.8"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.9"/>
|
||||
<PackageReference Include="Minio" Version="6.0.5"/>
|
||||
<PackageReference Include="NSExt" Version="2.3.8"/>
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.7"/>
|
||||
|
@@ -19,8 +19,8 @@ public sealed class FreeSqlBuilder(DatabaseOptions databaseOptions)
|
||||
) {
|
||||
var freeSql = new FreeSql.FreeSqlBuilder()
|
||||
#if DBTYPE_SQLSERVER
|
||||
.UseConnectionFactory(databaseOptions.DbType, () => new SqlConnection(databaseOptions.ConnStr))
|
||||
.UseAdoConnectionPool(true)
|
||||
.UseConnectionFactory(databaseOptions.DbType, () => new SqlConnection(databaseOptions.ConnStr))
|
||||
.UseAdoConnectionPool(true)
|
||||
#else
|
||||
.UseConnectionString(databaseOptions.DbType, databaseOptions.ConnStr)
|
||||
#endif
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using NetAdmin.Domain.Dto.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
namespace NetAdmin.SysComponent.Application.Modules.Sys;
|
||||
@@ -11,6 +12,16 @@ public interface IWalletTradeModule : ICrudModule<CreateWalletTradeReq, QueryWal
|
||||
, DelReq // 删除类型
|
||||
>
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取自助充值条形图数据
|
||||
/// </summary>
|
||||
Task<IEnumerable<GetBarChartRsp>> GetSelfDepositBarChartAsync(QueryReq<QueryWalletTradeReq> req);
|
||||
|
||||
/// <summary>
|
||||
/// 消费总数计数
|
||||
/// </summary>
|
||||
Task<long> TotalAmountAsync(QueryReq<QueryWalletTradeReq> req);
|
||||
|
||||
/// <summary>
|
||||
/// 从他人账户转账给自己
|
||||
/// </summary>
|
||||
|
@@ -10,6 +10,6 @@ public interface IUserInviteService : IService, IUserInviteModule
|
||||
/// </summary>
|
||||
Task<List<long>> GetAssociatedUserIdAsync(
|
||||
long userId
|
||||
, bool up = true
|
||||
, bool up = true
|
||||
);
|
||||
}
|
@@ -94,8 +94,7 @@ public sealed class DepositOrderService(BasicRepository<Sys_DepositOrder, long>
|
||||
var toPointRate = req.PaymentMode switch
|
||||
{
|
||||
PaymentModes.USDT => config.UsdToPointRate
|
||||
, PaymentModes.Alipay => config.CnyToPointRate
|
||||
, PaymentModes.WeChat => config.CnyToPointRate
|
||||
, PaymentModes.Alipay or PaymentModes.WeChat => config.CnyToPointRate
|
||||
, _ => throw new ArgumentOutOfRangeException(nameof(req))
|
||||
};
|
||||
|
||||
@@ -221,8 +220,14 @@ public sealed class DepositOrderService(BasicRepository<Sys_DepositOrder, long>
|
||||
/// <inheritdoc />
|
||||
public async Task<int> ReceivedConfirmationAsync(JobReq req) {
|
||||
req.ThrowIfInvalid();
|
||||
|
||||
// 初始化返回值
|
||||
var ret = 0;
|
||||
|
||||
// 获取最新配置信息
|
||||
var config = await S<IConfigService>().GetLatestConfigAsync().ConfigureAwait(false);
|
||||
|
||||
// 查询所有状态为“待确认”的充值订单
|
||||
var waitConfirmList = (await QueryAsync(
|
||||
new QueryReq<QueryDepositOrderReq>
|
||||
{
|
||||
@@ -239,59 +244,36 @@ public sealed class DepositOrderService(BasicRepository<Sys_DepositOrder, long>
|
||||
}
|
||||
)
|
||||
.ConfigureAwait(false)).ToList();
|
||||
|
||||
// 调用 TronScan API 获取指定地址的代币转账记录
|
||||
var apiResult = await S<ITronScanClient>()
|
||||
.TransfersAsync(S<IOptions<TronScanOptions>>().Value.Token, req.Count!.Value, config.Trc20ReceiptAddress)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// 如果没有获取到交易数据,则直接返回 0
|
||||
if (apiResult.TokenTransfers == null) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 遍历所有已确认且成功的 USDT 交易
|
||||
foreach (var apiItem in apiResult.TokenTransfers.Where(x =>
|
||||
x.TokenInfo.TokenAbbr == "USDT" && x.Confirmed && x.ContractRet == "SUCCESS" && x.FinalResult == "SUCCESS"
|
||||
)) {
|
||||
// 匹配本地订单中金额一致、创建时间早于交易时间的订单
|
||||
var order = waitConfirmList.SingleOrDefault(x => x.ActualPayAmount == apiItem.Quant.Int64() / 1000);
|
||||
if (order == null || order.CreatedTime > apiItem.BlockTs.Time()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用工作单元模式原子性地更新订单状态并创建钱包交易记录
|
||||
await S<UnitOfWorkManager>()
|
||||
.AtomicOperateAsync(async () =>
|
||||
{
|
||||
var updated = await UpdateAsync(
|
||||
new Sys_DepositOrder
|
||||
{
|
||||
DepositOrderStatus = DepositOrderStatues.Succeeded
|
||||
, Version = order.Version
|
||||
, FinishTimestamp = DateTime.Now.TimeUnixUtcMs()
|
||||
, PaidAccount = apiItem.FromAddress
|
||||
, PaidTime = apiItem.BlockTs.Time()
|
||||
, PaymentFinger = apiItem.TransactionId
|
||||
}, [nameof(Sys_DepositOrder.DepositOrderStatus), nameof(Sys_DepositOrder.FinishTimestamp), nameof(Sys_DepositOrder.PaidAccount), nameof(Sys_DepositOrder.PaidTime), nameof(Sys_DepositOrder.PaymentFinger)]
|
||||
, null, a => a.Id == order.Id && a.DepositOrderStatus == DepositOrderStatues.PaymentConfirming
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
if (updated != 1) {
|
||||
throw new NetAdminUnexpectedException(Ln.结果非预期);
|
||||
}
|
||||
|
||||
_ = await S<IWalletTradeService>()
|
||||
.CreateAsync(
|
||||
new CreateWalletTradeReq
|
||||
{
|
||||
Amount = order.DepositPoint
|
||||
, BusinessOrderNumber = order.Id
|
||||
, TradeDirection = TradeDirections.Income
|
||||
, TradeType = TradeTypes.SelfDeposit
|
||||
, OwnerId = order.OwnerId
|
||||
, OwnerDeptId = order.OwnerDeptId
|
||||
}
|
||||
)
|
||||
.ConfigureAwait(false)
|
||||
?? throw new NetAdminUnexpectedException(Ln.结果非预期);
|
||||
}
|
||||
)
|
||||
.AtomicOperateAsync(async () => await OrderSuccessAsync(order, apiItem).ConfigureAwait(false))
|
||||
.ConfigureAwait(false);
|
||||
ret++;
|
||||
}
|
||||
catch {
|
||||
// ignore
|
||||
// 忽略异常,防止中断整个流程
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,6 +286,53 @@ public sealed class DepositOrderService(BasicRepository<Sys_DepositOrder, long>
|
||||
return QueryInternal(req with { Order = Orders.None }).WithNoLockNoWait().SumAsync(req.GetSumExp<Sys_DepositOrder>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定订单标记为成功,并创建对应的钱包交易记录。
|
||||
/// </summary>
|
||||
/// <param name="order">本地待处理的订单信息。</param>
|
||||
/// <param name="apiItem">来自链上的交易信息。</param>
|
||||
/// <exception cref="NetAdminUnexpectedException">当发生未预期的网络管理异常时抛出此异常</exception>
|
||||
private async Task OrderSuccessAsync(
|
||||
QueryDepositOrderRsp order
|
||||
, TokenTransferInfo apiItem
|
||||
) {
|
||||
// 更新订单状态为成功,并填充支付相关信息
|
||||
var updated = await UpdateAsync(
|
||||
new Sys_DepositOrder
|
||||
{
|
||||
DepositOrderStatus = DepositOrderStatues.Succeeded
|
||||
, Version = order.Version
|
||||
, FinishTimestamp = DateTime.Now.TimeUnixUtcMs()
|
||||
, PaidAccount = apiItem.FromAddress
|
||||
, PaidTime = apiItem.BlockTs.Time()
|
||||
, PaymentFinger = apiItem.TransactionId
|
||||
}, [nameof(Sys_DepositOrder.DepositOrderStatus), nameof(Sys_DepositOrder.FinishTimestamp), nameof(Sys_DepositOrder.PaidAccount), nameof(Sys_DepositOrder.PaidTime), nameof(Sys_DepositOrder.PaymentFinger)]
|
||||
, null, a => a.Id == order.Id && a.DepositOrderStatus == DepositOrderStatues.PaymentConfirming
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// 如果更新失败(影响行数不为1),抛出异常
|
||||
if (updated != 1) {
|
||||
throw new NetAdminUnexpectedException(Ln.结果非预期);
|
||||
}
|
||||
|
||||
// 创建钱包交易记录
|
||||
_ = await S<IWalletTradeService>()
|
||||
.CreateAsync(
|
||||
new CreateWalletTradeReq
|
||||
{
|
||||
Amount = order.DepositPoint
|
||||
, BusinessOrderNumber = order.Id
|
||||
, TradeDirection = TradeDirections.Income
|
||||
, TradeType = TradeTypes.SelfDeposit
|
||||
, OwnerId = order.OwnerId
|
||||
, OwnerDeptId = order.OwnerDeptId
|
||||
}
|
||||
)
|
||||
.ConfigureAwait(false)
|
||||
?? throw new NetAdminUnexpectedException(Ln.结果非预期);
|
||||
}
|
||||
|
||||
private ISelect<Sys_DepositOrder> QueryInternal(QueryReq<QueryDepositOrderReq> req) {
|
||||
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereIf(req.Filter?.Id > 0, a => a.Id == req.Filter.Id);
|
||||
|
||||
|
@@ -20,6 +20,7 @@ public sealed class UserWalletService(BasicRepository<Sys_UserWallet, long> rpo)
|
||||
, Owner = new Sys_User
|
||||
{
|
||||
Id = a.Owner.Id
|
||||
, DeptId = a.Owner.DeptId
|
||||
, UserName = a.Owner.UserName
|
||||
, Avatar = a.Owner.Avatar
|
||||
, Roles = a.Owner.Roles
|
||||
|
@@ -2,6 +2,7 @@ using System.Net.Http.Headers;
|
||||
using Ganss.Excel;
|
||||
using NetAdmin.Application.Extensions;
|
||||
using NetAdmin.Domain.DbMaps.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys.User;
|
||||
using NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
@@ -99,7 +100,7 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
|
||||
.ConfigureAwait(false)
|
||||
?? throw new NetAdminUnexpectedException(Ln.交易失败);
|
||||
var ret = await Rpo
|
||||
.InsertAsync(req with { BalanceBefore = wallet.AvailableBalance + wallet.FrozenBalance, OwnerDeptId = wallet.OwnerDeptId })
|
||||
.InsertAsync(req with { BalanceBefore = wallet.AvailableBalance + wallet.FrozenBalance, OwnerDeptId = wallet.Owner.DeptId })
|
||||
.ConfigureAwait(false);
|
||||
return ret.Adapt<QueryWalletTradeRsp>();
|
||||
}
|
||||
@@ -172,6 +173,27 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
|
||||
return ret.Adapt<QueryWalletTradeRsp>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<GetBarChartRsp>> GetSelfDepositBarChartAsync(QueryReq<QueryWalletTradeReq> req) {
|
||||
req.ThrowIfInvalid();
|
||||
var df = new DynamicFilterInfo
|
||||
{
|
||||
Field = nameof(Sys_WalletTrade.TradeType), Operator = DynamicFilterOperators.Eq, Value = TradeTypes.SelfDeposit
|
||||
};
|
||||
var newdf = req.DynamicFilter.Add(df);
|
||||
var ret = await QueryInternal(req with { Order = Orders.None, DynamicFilter = newdf })
|
||||
.WithNoLockNoWait()
|
||||
.GroupBy(a => new { a.CreatedTime.Year, a.CreatedTime.Month, a.CreatedTime.Day, a.CreatedTime.Hour })
|
||||
.ToListAsync(a => new GetBarChartRsp
|
||||
{
|
||||
Timestamp = new DateTime(a.Key.Year, a.Key.Month, a.Key.Day, a.Key.Hour, 0, 0, DateTimeKind.Unspecified)
|
||||
, Value = (int)Math.Floor(a.Sum(a.Value.Amount) / 100)
|
||||
}
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
return ret.OrderBy(x => x.Timestamp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedQueryRsp<QueryWalletTradeRsp>> PagedQueryAsync(PagedQueryReq<QueryWalletTradeReq> req) {
|
||||
req.ThrowIfInvalid();
|
||||
@@ -208,6 +230,13 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<long> TotalAmountAsync(QueryReq<QueryWalletTradeReq> req) {
|
||||
req.ThrowIfInvalid();
|
||||
var total = QueryInternal(req with { Order = Orders.None }).WithNoLockNoWait().Sum(a => a.Amount) / 100;
|
||||
return Task.FromResult((long)Math.Abs(Math.Floor(total)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> TransferFromAnotherAccountAsync(TransferReq req) {
|
||||
// 检查源账户是不是自己的下级
|
||||
@@ -230,7 +259,7 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
|
||||
OwnerDeptId = fromUser.DeptId
|
||||
, Amount = -req.Amount
|
||||
, OwnerId = fromUser.Id
|
||||
, Summary = req.Summary
|
||||
, Summary = $"{req.Summary} ({Ln.转给}: {UserToken.UserName}/{UserToken.Id})"
|
||||
, TradeDirection = TradeDirections.Expense
|
||||
, TradeType = TradeTypes.TransferExpense
|
||||
}
|
||||
@@ -242,7 +271,7 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
|
||||
new CreateWalletTradeReq
|
||||
{
|
||||
Amount = req.Amount
|
||||
, Summary = req.Summary
|
||||
, Summary = $"{req.Summary} ({Ln.来自}: {fromUser.UserName}/{fromUser.Id})"
|
||||
, TradeDirection = TradeDirections.Income
|
||||
, TradeType = TradeTypes.TransferIncome
|
||||
, OwnerId = UserToken.Id
|
||||
@@ -267,7 +296,7 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
|
||||
new CreateWalletTradeReq
|
||||
{
|
||||
Amount = -req.Amount
|
||||
, Summary = req.Summary
|
||||
, Summary = $"{req.Summary} ({Ln.转给}: {toUser.UserName}/{toUser.Id})"
|
||||
, TradeDirection = TradeDirections.Expense
|
||||
, TradeType = TradeTypes.TransferExpense
|
||||
, OwnerId = UserToken.Id
|
||||
@@ -283,7 +312,7 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
|
||||
OwnerDeptId = toUser.DeptId
|
||||
, Amount = req.Amount
|
||||
, OwnerId = toUser.Id
|
||||
, Summary = req.Summary
|
||||
, Summary = $"{req.Summary} ({Ln.来自}: {UserToken.UserName}/{UserToken.Id})"
|
||||
, TradeDirection = TradeDirections.Income
|
||||
, TradeType = TradeTypes.TransferIncome
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
|
||||
/// <inheritdoc />
|
||||
#if !DEBUG
|
||||
public async Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
|
||||
#else
|
||||
#else
|
||||
public Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
|
||||
#endif
|
||||
{
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using NetAdmin.Domain.Dto.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
namespace NetAdmin.SysComponent.Cache.Sys;
|
||||
@@ -46,6 +47,11 @@ public sealed class WalletTradeCache(IDistributedCache cache, IWalletTradeServic
|
||||
return Service.GetAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<GetBarChartRsp>> GetSelfDepositBarChartAsync(QueryReq<QueryWalletTradeReq> req) {
|
||||
return Service.GetSelfDepositBarChartAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<PagedQueryRsp<QueryWalletTradeRsp>> PagedQueryAsync(PagedQueryReq<QueryWalletTradeReq> req) {
|
||||
return Service.PagedQueryAsync(req);
|
||||
@@ -61,6 +67,11 @@ public sealed class WalletTradeCache(IDistributedCache cache, IWalletTradeServic
|
||||
return Service.SumAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<long> TotalAmountAsync(QueryReq<QueryWalletTradeReq> req) {
|
||||
return Service.TotalAmountAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> TransferFromAnotherAccountAsync(TransferReq req) {
|
||||
return Service.TransferFromAnotherAccountAsync(req);
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using NetAdmin.Domain.Dto.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
namespace NetAdmin.SysComponent.Host.Controllers.Sys;
|
||||
@@ -70,6 +71,13 @@ public sealed class WalletTradeController(IWalletTradeCache cache) : ControllerB
|
||||
return Cache.GetAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取自助充值条形图数据
|
||||
/// </summary>
|
||||
public Task<IEnumerable<GetBarChartRsp>> GetSelfDepositBarChartAsync(QueryReq<QueryWalletTradeReq> req) {
|
||||
return Cache.GetSelfDepositBarChartAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询钱包交易
|
||||
/// </summary>
|
||||
@@ -92,6 +100,13 @@ public sealed class WalletTradeController(IWalletTradeCache cache) : ControllerB
|
||||
return Cache.SumAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消费总数计数
|
||||
/// </summary>
|
||||
public Task<long> TotalAmountAsync(QueryReq<QueryWalletTradeReq> req) {
|
||||
return Cache.TotalAmountAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从他人账户转账给自己
|
||||
/// </summary>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<ProjectReference Include="../NetAdmin.Host/NetAdmin.Host.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.8"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.9"/>
|
||||
<PackageReference Include="xunit" Version="2.9.3"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
@@ -10,13 +10,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.2",
|
||||
"ace-builds": "1.43.2",
|
||||
"aieditor": "1.4.0",
|
||||
"axios": "1.11.0",
|
||||
"ace-builds": "1.43.3",
|
||||
"aieditor": "1.4.1",
|
||||
"axios": "1.12.2",
|
||||
"crypto-js": "4.2.0",
|
||||
"dayjs": "1.11.14",
|
||||
"dayjs": "1.11.18",
|
||||
"echarts": "6.0.0",
|
||||
"element-plus": "2.11.1",
|
||||
"element-plus": "2.11.4",
|
||||
"json-bigint": "1.0.0",
|
||||
"markdown-it": "14.1.0",
|
||||
"markdown-it-emoji": "3.0.0",
|
||||
@@ -24,8 +24,8 @@
|
||||
"qrcode-svg": "1.1.0",
|
||||
"sortablejs": "1.15.6",
|
||||
"vkbeautify": "0.99.3",
|
||||
"vue": "3.5.20",
|
||||
"vue-i18n": "11.1.11",
|
||||
"vue": "3.5.22",
|
||||
"vue-i18n": "11.1.12",
|
||||
"vue-router": "4.5.1",
|
||||
"vue3-ace-editor": "2.2.4",
|
||||
"vue3-json-viewer": "2.4.1",
|
||||
@@ -36,9 +36,9 @@
|
||||
"@vitejs/plugin-vue": "6.0.1",
|
||||
"prettier": "3.6.2",
|
||||
"prettier-plugin-organize-attributes": "1.0.0",
|
||||
"sass": "1.91.0",
|
||||
"terser": "5.43.1",
|
||||
"vite": "7.1.3"
|
||||
"sass": "1.93.2",
|
||||
"terser": "5.44.0",
|
||||
"vite": "7.1.7"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
9
src/frontend/admin/src/assets/icon/android.vue
Normal file
9
src/frontend/admin/src/assets/icon/android.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<svg height="256" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M436.8 212.8m-22.4 0a22.4 22.4 0 1 0 44.8 0 22.4 22.4 0 1 0-44.8 0Z" />
|
||||
<path d="M572.8 212.8m-22.4 0a22.4 22.4 0 1 0 44.8 0 22.4 22.4 0 1 0-44.8 0Z" />
|
||||
<path
|
||||
d="M872 304c-11.2 0-22.4 1.6-32 4.8l-8-25.6c-19.2-57.6-51.2-107.2-94.4-145.6l57.6-62.4c14.4-16 14.4-41.6-1.6-56-16-14.4-41.6-14.4-56 1.6L672 91.2c-46.4-24-99.2-38.4-153.6-38.4-57.6 0-112 14.4-160 41.6L288 20.8c-14.4-16-40-17.6-56-1.6-16 14.4-17.6 40-1.6 56l62.4 67.2c-40 38.4-72 86.4-89.6 142.4l-9.6 28.8c-12.8-4.8-27.2-8-41.6-8C94.4 304 48 350.4 48 408v161.6C48 625.6 94.4 672 152 672h1.6c27.2 0 52.8-11.2 72-28.8v110.4c0 20.8 12.8 38.4 32 43.2v107.2c0 57.6 46.4 104 104 104h1.6c57.6 0 104-46.4 104-104V800h96v104c0 57.6 46.4 104 104 104h1.6c57.6 0 104-46.4 104-104V800h1.6c25.6 0 46.4-20.8 46.4-46.4V656c16 9.6 35.2 16 56 16h1.6c57.6 0 104-46.4 104-104v-160c-6.4-57.6-52.8-104-110.4-104z m-140.8-48H302.4c43.2-75.2 124.8-121.6 214.4-121.6 89.6 0 169.6 46.4 214.4 121.6zM176 568c0 12.8-11.2 24-24 24S128 580.8 128 568v-160c0-12.8 11.2-24 24-24s24 11.2 24 24v160z m208 336c0 12.8-11.2 24-24 24h-1.6c-12.8 0-24-11.2-24-24V800h48v104zM464 720H304V336h432v384H464z m224 184c0 12.8-11.2 24-24 24h-1.6c-12.8 0-24-11.2-24-24V800h48v104z m208-336c0 12.8-11.2 24-24 24h-1.6c-12.8 0-24-11.2-24-24v-160c0-12.8 11.2-24 24-24s24 11.2 24 24v160z" />
|
||||
</svg>
|
||||
</template>
|
||||
<script lang="ts" setup></script>
|
6
src/frontend/admin/src/assets/icon/apple.vue
Normal file
6
src/frontend/admin/src/assets/icon/apple.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<svg height="256" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M704 106.666667a210.346667 210.346667 0 0 1-59.733333 132.266666A209.706667 209.706667 0 0 1 512 298.666667a204.8 204.8 0 0 1 192-192z m-45.226667 277.333333a131.626667 131.626667 0 0 0-69.333333 20.053333 138.88 138.88 0 0 1-146.56 0 130.986667 130.986667 0 0 0-69.333333-20.053333h-7.04c-69.12 3.626667-131.84 85.333333-131.84 170.666667 0 90.026667 30.08 160 100.693333 234.666666 40.96 42.666667 46.506667 42.666667 58.453333 42.666667a50.133333 50.133333 0 0 0 18.56-3.413333 290.773333 290.773333 0 0 1 207.786667 0 48.426667 48.426667 0 0 0 18.346667 3.413333c11.946667 0 17.493333 0 58.88-42.666667A395.946667 395.946667 0 0 0 768 689.493333 215.466667 215.466667 0 0 1 675.413333 512a217.6 217.6 0 0 1 33.493334-115.626667 106.666667 106.666667 0 0 0-42.666667-12.16h-7.253333m0-85.333333h11.733333A214.613333 214.613333 0 0 1 832 395.733333 128 128 0 0 0 760.746667 512 125.653333 125.653333 0 0 0 874.666667 640a422.613333 422.613333 0 0 1-115.626667 208.213333c-42.666667 44.586667-69.76 69.546667-120.533333 69.546667a139.093333 139.093333 0 0 1-49.066667-8.96 203.52 203.52 0 0 0-146.56 0 138.24 138.24 0 0 1-49.066667 8.96c-50.56 0-77.866667-24.96-120.32-69.546667C196.906667 768 149.333333 677.546667 149.333333 554.666667s92.373333-249.386667 213.333334-256h11.52a215.68 215.68 0 0 1 114.56 33.066666 53.76 53.76 0 0 0 56.106666 0A215.68 215.68 0 0 1 658.773333 298.666667z" />
|
||||
</svg>
|
||||
</template>
|
@@ -80,4 +80,6 @@ export { default as template } from './template.vue'
|
||||
export { default as order } from './order.vue'
|
||||
export { default as income } from './income.vue'
|
||||
export { default as transfer } from './transfer.vue'
|
||||
export { default as deposit } from './deposit.vue'
|
||||
export { default as deposit } from './deposit.vue'
|
||||
export { default as apple } from './apple.vue'
|
||||
export { default as android } from './android.vue'
|
@@ -3,6 +3,7 @@
|
||||
v-model="visible"
|
||||
:full-screen="dialogFullScreen.includes(mode)"
|
||||
:title="titleMap[mode]"
|
||||
:width="dialogWidth"
|
||||
@closed="$emit(`closed`)"
|
||||
destroy-on-close
|
||||
ref="dialog">
|
||||
@@ -25,7 +26,7 @@
|
||||
:prop="i">
|
||||
<el-date-picker
|
||||
v-bind="item.detail?.props"
|
||||
v-if="i.endsWith(`Time`)"
|
||||
v-if="i.endsWith(`Time`) && !item.detail?.is"
|
||||
v-model="form[i]"
|
||||
:disabled="item.disabled?.includes(mode)"
|
||||
type="datetime"
|
||||
@@ -76,6 +77,7 @@
|
||||
v-model:value3="form[item.detail.vModel[2]]"
|
||||
v-model:value4="form[item.detail.vModel[3]]"
|
||||
v-model:value5="form[item.detail.vModel[4]]"
|
||||
v-model:value6="form[item.detail.vModel[5]]"
|
||||
:disabled="item.disabled?.includes(mode)"
|
||||
:is="item.detail.is" />
|
||||
</template>
|
||||
@@ -229,6 +231,7 @@ export default {
|
||||
dialogFullScreen: { type: Array },
|
||||
tabs: { type: Array },
|
||||
formInline: { type: Boolean },
|
||||
dialogWidth: { type: String, default: '50%' },
|
||||
formLabelWidth: { type: Number, default: 12 },
|
||||
},
|
||||
}
|
||||
|
@@ -598,6 +598,7 @@ export default {
|
||||
showSelection: { type: Boolean, default: true },
|
||||
showSearchMine: { type: Boolean, default: false },
|
||||
totalCountLabel: { type: String, default: `总数` },
|
||||
dialogWidth: { type: String, default: '50%' },
|
||||
},
|
||||
watch: {},
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -493,6 +493,14 @@ textarea {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.ml-4 {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
@@ -504,9 +512,11 @@ textarea {
|
||||
.mt-8 {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mb-8 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
@@ -646,7 +656,13 @@ textarea {
|
||||
margin-right: 0 !important;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.el-form-item-w33p {
|
||||
margin-right: 0 !important;
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.el-form-item-w25p {
|
||||
margin-right: 0 !important;
|
||||
width: 25%;
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
<div class="widgets-content">
|
||||
<div class="widgets" ref="widgets">
|
||||
<div class="widgets-wrapper">
|
||||
<div v-if="nowCompsList.length <= 0" class="no-widgets">
|
||||
<div v-if="nowCompsList?.length <= 0" class="no-widgets">
|
||||
<el-empty :description="$t('没有部件啦')" :image-size="280" />
|
||||
</div>
|
||||
<el-row :gutter="15">
|
||||
@@ -181,7 +181,7 @@ export default {
|
||||
return this.allCompsList.filter((item) => !item.disabled && !myGrid?.includes(item.key))
|
||||
},
|
||||
nowCompsList() {
|
||||
return this.grid.compsList.reduce(function (a, b) {
|
||||
return this.grid.compsList?.reduce(function (a, b) {
|
||||
return a.concat(b)
|
||||
})
|
||||
},
|
||||
|
@@ -31,7 +31,7 @@
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
const scPasswordStrength = defineAsyncComponent(() => import('@/components/sc-password-strength'))
|
||||
const naFormPassword = defineAsyncComponent(() => import('@/config/na-form-password'))
|
||||
import naFormPassword from '@/config/na-form-password'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@@ -65,7 +65,7 @@
|
||||
isSwitch: { onChange: `setSelfDepositAllowed` },
|
||||
},
|
||||
'user.enabled': {
|
||||
label: $t(`启用`),
|
||||
label: $t(`账号已启用`),
|
||||
width: 120,
|
||||
align: `center`,
|
||||
show: [`list`],
|
||||
|
Reference in New Issue
Block a user