feat: 钱包冻结解冻

This commit is contained in:
tk
2025-07-15 19:21:42 +08:00
committed by nsnail
parent e4d71a516d
commit fe19289b79
96 changed files with 1826 additions and 806 deletions

View File

@ -13,7 +13,10 @@ USDT
中专
中共党员
为其中之一
交易
人工
人工审核
代码模板导出
以什么开始
以什么结束
作业名称
@ -52,9 +55,12 @@ USDT
小于
小于等于
小学
已冻结
已发送
已婚
已校验
已经冻结
已解冻
已读
并且
归属角色
@ -116,7 +122,6 @@ USDT
登录名
登录日志导出
硕士
代码模板导出
离异
空闲
站内信导出
@ -129,6 +134,7 @@ USDT
系统模块
绑定手机号码
结果非预期
结算解冻
群众
自助充值
自定义
@ -143,6 +149,7 @@ USDT
请求方式
请求日志导出
调试
超时解冻
身份证
运行
追踪
@ -154,6 +161,7 @@ USDT
配置导出
重设密码
钱包交易导出
钱包冻结导出
链接
错误
随机排序

View File

@ -12,6 +12,7 @@ XML注释文件不存在
作业名称不能为空
允许的文件大小
允许的文件格式
冻结状态不正确
区号电话号码分机号
参数格式不正确
唯一编码不能为空
@ -111,7 +112,7 @@ XML注释文件不存在
部门可见
部门名称不能为空
配置文件初始化完毕
钱包余额不足
钱包可用余额不足
键值不能为空
键名称不能为空
随机延时结束时间不正确

View File

@ -106,5 +106,17 @@
{
"ApiId": "api/sys/user.invite/query",
"RoleId": 371729946431493,
}
},
{
"ApiId": "api/sys/wallet.frozen/get",
"RoleId": 371729946431493,
},
{
"ApiId": "api/sys/wallet.frozen/paged.query",
"RoleId": 371729946431493,
},
{
"ApiId": "api/sys/wallet.frozen/count.by",
"RoleId": 371729946431493,
},
]

View File

@ -1,7 +1,7 @@
{
"version": "2.4.0",
"devDependencies": {
"cz-git": "^1.11.2",
"cz-git": "^1.12.0",
"commitizen": "^4.3.1",
"prettier": "^3.6.2",
"standard-version": "^9.5.0"

View File

@ -0,0 +1,23 @@
using NetAdmin.Domain;
using NetAdmin.Domain.Dto.Dependency;
namespace NetAdmin.Application.Extensions;
/// <summary>
/// FreeSql Select 扩展方法
/// </summary>
public static class ISelectExtensions
{
/// <summary>
/// 附加其他过滤条件
/// </summary>
public static ISelect<T> AppendOtherFilters<T, TQuery>(this ISelect<T> me, QueryReq<TQuery> req)
where TQuery : DataAbstraction, new()
{
if (req.IgnoreOwner) {
me = me.DisableGlobalFilter(Chars.FLG_FREE_SQL_GLOBAL_FILTER_DATA);
}
return me;
}
}

View File

@ -42,12 +42,7 @@ public abstract class ServiceBase : IScoped, IService
/// <summary>
/// 获取服务
/// </summary>
#pragma warning disable RCS1036
#pragma warning restore RCS1036
// ReSharper disable once MemberCanBeMadeStatic.Global
#pragma warning disable CA1822, S2325
protected T S<T>()
protected static T S<T>()
#pragma warning restore S2325, CA1822
where T : class
{

View File

@ -1,15 +1,10 @@
using NetAdmin.Domain.Attributes;
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 代码模板表
/// </summary>
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_CodeTemplate))]
public record Sys_CodeTemplate : VersionEntity, IFieldSummary, IFieldOwner, IFieldSort, IFieldEnabled
public record Sys_CodeTemplate : VersionEntity, IFieldSort, IFieldSummary, IFieldEnabled, IFieldOwner
{
/// <summary>
/// 是否启用
@ -20,6 +15,34 @@ public record Sys_CodeTemplate : VersionEntity, IFieldSummary, IFieldOwner, IFie
[JsonIgnore]
public virtual bool Enabled { get; init; }
/// <summary>
/// 性别
/// </summary>
/// <example>Male</example>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual Genders? Gender { get; init; }
/// <summary>
/// 唯一编码
/// </summary>
/// <example>123456</example>
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
[CsvIgnore]
[JsonIgnore]
[Snowflake]
public override long Id { get; init; }
/// <summary>
/// 名称
/// </summary>
/// <example>老王</example>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
[CsvIgnore]
[JsonIgnore]
public virtual string Name { get; init; }
/// <summary>
/// 归属用户
/// </summary>
@ -63,32 +86,4 @@ public record Sys_CodeTemplate : VersionEntity, IFieldSummary, IFieldOwner, IFie
[CsvIgnore]
[JsonIgnore]
public virtual string Summary { get; init; }
/// <summary>
/// 代码模板编号
/// </summary>
/// <example>123456</example>
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
[CsvIgnore]
[JsonIgnore]
[Snowflake]
public override long Id { get; init; }
/// <summary>
/// 名称
/// </summary>
/// <example>老王</example>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
[CsvIgnore]
[JsonIgnore]
public virtual string Name { get; init; }
/// <summary>
/// 性别
/// </summary>
/// <example>Male</example>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual Genders? Gender { get; init; }
}

View File

@ -46,14 +46,6 @@ public record Sys_UserWallet : LiteVersionEntity, IFieldOwner
[JsonIgnore]
public virtual long? OwnerId { get; init; }
/// <summary>
/// 总余额
/// </summary>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual long TotalBalance { get; init; }
/// <summary>
/// 总支出
/// </summary>

View File

@ -0,0 +1,99 @@
using NetAdmin.Domain.Enums.Sys;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 钱包冻结表
/// </summary>
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_WalletFrozen))]
public record Sys_WalletFrozen : LiteVersionEntity, IFieldOwner, IFieldSummary
{
/// <summary>
/// 冻结金额
/// </summary>
/// <example>100</example>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual long Amount { get; init; }
/// <summary>
/// 冻结前数值
/// </summary>
/// <example>100</example>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual long FrozenBalanceBefore { get; init; }
/// <summary>
/// 冻结编号
/// </summary>
/// <example>123456</example>
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
[CsvIgnore]
[JsonIgnore]
[Snowflake]
public override long Id { get; init; }
/// <summary>
/// 归属用户
/// </summary>
[CsvIgnore]
[JsonIgnore]
[Navigate(nameof(OwnerId))]
public Sys_User Owner { get; init; }
/// <summary>
/// 归属部门编号
/// </summary>
/// <example>370942943322181</example>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual long? OwnerDeptId { get; init; }
/// <summary>
/// 归属用户编号
/// </summary>
/// <example>370942943322181</example>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual long? OwnerId { get; init; }
/// <summary>
/// 冻结原因
/// </summary>
/// <example>Trade</example>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual WalletFrozenReasons Reason { get; init; }
/// <summary>
/// 冻结状态
/// </summary>
/// <example>Frozen</example>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual WalletFrozenStatues Status { get; init; }
/// <summary>
/// 备注
/// </summary>
/// <example>备注文字</example>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[CsvIgnore]
[JsonIgnore]
public virtual string Summary { get; init; }
/// <summary>
/// 钱包
/// </summary>
[CsvIgnore]
[JsonIgnore]
[Navigate(nameof(OwnerId))]
public Sys_UserWallet Wallet { get; init; }
}

View File

@ -22,6 +22,12 @@ public record QueryReq<T> : DataAbstraction
/// </summary>
public T Filter { get; init; }
/// <summary>
/// 忽略归属
/// </summary>
[JsonIgnore]
public bool IgnoreOwner { get; init; }
/// <summary>
/// 查询关键字
/// </summary>

View File

@ -1,6 +1,3 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.CodeTemplate;
/// <summary>
@ -11,13 +8,9 @@ public record CreateCodeTemplateReq : Sys_CodeTemplate
/// <inheritdoc cref="IFieldEnabled.Enabled" />
public override bool Enabled { get; init; } = true;
/// <inheritdoc cref="IFieldSort.Sort" />
/// <inheritdoc cref="Sys_CodeTemplate.Gender" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Sort { get; init; }
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
public override Genders? Gender { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
@ -27,8 +20,11 @@ public record CreateCodeTemplateReq : Sys_CodeTemplate
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Name { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Gender" />
/// <inheritdoc cref="IFieldSort.Sort" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[EnumDataType(typeof(Genders))]
public override Genders? Gender { get; init; }
public override long Sort { get; init; }
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
}

View File

@ -1,5 +1,3 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.Dto.Sys.CodeTemplate;
/// <summary>

View File

@ -1,6 +1,3 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.CodeTemplate;
/// <summary>
@ -8,7 +5,7 @@ namespace NetAdmin.Domain.Dto.Sys.CodeTemplate;
/// </summary>
public sealed record QueryCodeTemplateReq : Sys_CodeTemplate
{
/// <inheritdoc cref="EntityBase{T}.Id" />
/// <inheritdoc cref="Sys_CodeTemplate.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
}

View File

@ -1,6 +1,3 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Dto.Sys.CodeTemplate;
@ -10,18 +7,10 @@ namespace NetAdmin.Domain.Dto.Sys.CodeTemplate;
/// </summary>
public record QueryCodeTemplateRsp : Sys_CodeTemplate
{
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc cref="IFieldModifiedTime.ModifiedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override DateTime? ModifiedTime { get; init; }
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? CreatedUserId { get; init; }
@ -30,6 +19,22 @@ public record QueryCodeTemplateRsp : Sys_CodeTemplate
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string CreatedUserName { get; init; }
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Gender" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override Genders? Gender { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldModifiedTime.ModifiedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override DateTime? ModifiedTime { get; init; }
/// <inheritdoc cref="IFieldModifiedUser.ModifiedUserId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? ModifiedUserId { get; init; }
@ -38,6 +43,10 @@ public record QueryCodeTemplateRsp : Sys_CodeTemplate
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ModifiedUserName { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Name" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Name { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Owner" />
public new virtual QueryUserRsp Owner { get; init; }
@ -60,16 +69,4 @@ public record QueryCodeTemplateRsp : Sys_CodeTemplate
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Name" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Name { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Gender" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override Genders? Gender { get; init; }
}

View File

@ -1,21 +0,0 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.Dto.Sys.CodeTemplate;
/// <summary>
/// 请求:设置代码模板启用状态
/// </summary>
public sealed record SetCodeTemplateEnabledReq : Sys_CodeTemplate
{
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="Sys_CodeTemplate.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -1,4 +1,4 @@
namespace NetAdmin.Domain.Dto.Sys.Dev;
namespace NetAdmin.Domain.Dto.Sys.Dev;
/// <summary>
/// 信息:字段项信息

View File

@ -1,4 +1,4 @@
namespace NetAdmin.Domain.Dto.Sys.Dev;
namespace NetAdmin.Domain.Dto.Sys.Dev;
/// <summary>
/// 请求:生成后端代码

View File

@ -1,4 +1,4 @@
namespace NetAdmin.Domain.Dto.Sys.Dev;
namespace NetAdmin.Domain.Dto.Sys.Dev;
/// <summary>
/// 请求:获取所有数据类型

View File

@ -38,10 +38,6 @@ public record QueryUserWalletRsp : Sys_UserWallet
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? OwnerId { get; init; }
/// <inheritdoc cref="Sys_UserWallet.TotalBalance" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long TotalBalance { get; init; }
/// <inheritdoc cref="Sys_UserWallet.TotalExpenditure" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long TotalExpenditure { get; init; }

View File

@ -0,0 +1,33 @@
using NetAdmin.Domain.Enums.Sys;
namespace NetAdmin.Domain.Dto.Sys.WalletFrozen;
/// <summary>
/// 请求:创建钱包冻结
/// </summary>
public record CreateWalletFrozenReq : Sys_WalletFrozen
{
/// <inheritdoc cref="Sys_WalletFrozen.Amount" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Amount { get; init; }
/// <inheritdoc cref="Sys_WalletFrozen.OwnerId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? OwnerId { get; init; }
/// <inheritdoc cref="Sys_WalletFrozen.Reason" />
[EnumDataType(typeof(WalletFrozenReasons))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override WalletFrozenReasons Reason { get; init; }
/// <inheritdoc cref="Sys_WalletFrozen.Status" />
public override WalletFrozenStatues Status { get; init; } = WalletFrozenStatues.Frozen;
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,15 @@
namespace NetAdmin.Domain.Dto.Sys.WalletFrozen;
/// <summary>
/// 请求:编辑钱包冻结
/// </summary>
public sealed record EditWalletFrozenReq : CreateWalletFrozenReq
{
/// <inheritdoc cref="Sys_WalletFrozen.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,11 @@
namespace NetAdmin.Domain.Dto.Sys.WalletFrozen;
/// <summary>
/// 请求:查询钱包冻结
/// </summary>
public sealed record QueryWalletFrozenReq : Sys_WalletFrozen
{
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
}

View File

@ -0,0 +1,57 @@
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.Domain.Enums.Sys;
namespace NetAdmin.Domain.Dto.Sys.WalletFrozen;
/// <summary>
/// 响应:查询钱包冻结
/// </summary>
public record QueryWalletFrozenRsp : Sys_WalletFrozen
{
/// <inheritdoc cref="Sys_WalletFrozen.Amount" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Amount { get; init; }
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc cref="Sys_WalletFrozen.FrozenBalanceBefore" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long FrozenBalanceBefore { get; init; }
/// <inheritdoc cref="Sys_WalletFrozen.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldModifiedTime.ModifiedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override DateTime? ModifiedTime { get; init; }
/// <inheritdoc cref="Sys_WalletFrozen.Owner" />
public new virtual QueryUserRsp Owner { get; init; }
/// <inheritdoc cref="IFieldOwner.OwnerDeptId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? OwnerDeptId { get; init; }
/// <inheritdoc cref="IFieldOwner.OwnerId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? OwnerId { get; init; }
/// <inheritdoc cref="Sys_WalletFrozen.Reason" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override WalletFrozenReasons Reason { get; init; }
/// <inheritdoc cref="Sys_WalletFrozen.Status" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override WalletFrozenStatues Status { get; init; }
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,11 @@
namespace NetAdmin.Domain.Dto.Sys.WalletFrozen;
/// <summary>
/// 请求:将状态设置为解冻
/// </summary>
public record SetStatusToThawedReq : Sys_WalletFrozen
{
/// <inheritdoc cref="Sys_WalletFrozen.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
}

View File

@ -5,8 +5,6 @@ namespace NetAdmin.Domain.Dto.Sys.WalletTrade;
/// </summary>
public record CreateWalletTradeReq : Sys_WalletTrade, IValidatableObject
{
private readonly TradeTypes _tradeType;
/// <inheritdoc cref="Sys_WalletTrade.Amount" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Amount { get; init; }
@ -33,9 +31,9 @@ public record CreateWalletTradeReq : Sys_WalletTrade, IValidatableObject
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[EnumDataType(typeof(TradeTypes))]
public override TradeTypes TradeType {
get => _tradeType;
get;
init {
_tradeType = value;
field = value;
TradeDirection = value.Attr<TradeAttribute>().Direction;
}
}

View File

@ -0,0 +1,22 @@
namespace NetAdmin.Domain.Enums.Sys;
/// <summary>
/// 钱包冻结原因
/// </summary>
[Export]
public enum WalletFrozenReasons
{
/// <summary>
/// 交易
/// </summary>
[ResourceDescription<Ln>(nameof(Ln.交易))]
Trade = 1
,
/// <summary>
/// 人工
/// </summary>
[ResourceDescription<Ln>(nameof(Ln.人工))]
Manual = 2
}

View File

@ -0,0 +1,22 @@
namespace NetAdmin.Domain.Enums.Sys;
/// <summary>
/// 钱包冻结状态
/// </summary>
[Export]
public enum WalletFrozenStatues
{
/// <summary>
/// 已冻结
/// </summary>
[ResourceDescription<Ln>(nameof(Ln.已冻结))]
Frozen = 1
,
/// <summary>
/// 已解冻
/// </summary>
[ResourceDescription<Ln>(nameof(Ln.已解冻))]
Thawed = 2
}

View File

@ -24,9 +24,7 @@ public static class StringExtensions
/// <summary>
/// 去掉尾部字符串“Async”
/// </summary>
#pragma warning disable RCS1047, ASA002, VSTHRD200
public static string TrimSuffixAsync(this string me)
#pragma warning restore VSTHRD200, ASA002, RCS1047
public static string TrimAsyncSuffix(this string me)
{
return me.TrimSuffix("Async");
}
@ -34,7 +32,7 @@ public static class StringExtensions
/// <summary>
/// 去掉尾部字符串“Options”
/// </summary>
public static string TrimSuffixOptions(this string me)
public static string TrimOptionsSuffix(this string me)
{
return me.TrimSuffix("Options");
}

View File

@ -27,8 +27,9 @@ public static class PhoneNumberHelper
/// <summary>
/// 电话号码转国家代码
/// </summary>
public static CountryCodes PhoneNumberToCountryCode(string phoneNumber)
public static CountryCodes? PhoneNumberToCountryCode(string phoneNumber)
{
return _countryList.First(x => phoneNumber.Replace("+", string.Empty).Trim().StartsWith(x.CallingCode, StringComparison.Ordinal)).CountryCode;
return _countryList.FirstOrDefault(x => phoneNumber.Replace("+", string.Empty).Trim().StartsWith(x.CallingCode, StringComparison.Ordinal))
.CountryCode;
}
}

View File

@ -17,7 +17,7 @@ public sealed class XmlCommentReader : ISingleton
public XmlCommentReader(IOptions<SpecificationDocumentSettingsOptions> specificationDocumentSettings)
{
var xmlComments = specificationDocumentSettings.Value.XmlComments //
?? App.GetConfig<SpecificationDocumentSettingsOptions>(nameof(SpecificationDocumentSettingsOptions).TrimSuffixOptions())
?? App.GetConfig<SpecificationDocumentSettingsOptions>(nameof(SpecificationDocumentSettingsOptions).TrimOptionsSuffix())
.XmlComments;
foreach (var commentFile in xmlComments) {
var xmlDoc = new XmlDocument();

View File

@ -9,10 +9,4 @@ public interface ICodeTemplateModule : ICrudModule<CreateCodeTemplateReq, QueryC
, EditCodeTemplateReq // 编辑类型
, QueryCodeTemplateReq, QueryCodeTemplateRsp // 查询类型
, DelReq // 删除类型
>
{
/// <summary>
/// 设置代码模板启用状态
/// </summary>
Task<int> SetEnabledAsync(SetCodeTemplateEnabledReq req);
}
>;

View File

@ -0,0 +1,18 @@
using NetAdmin.Domain.Dto.Sys.WalletFrozen;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 钱包冻结模块
/// </summary>
public interface IWalletFrozenModule : ICrudModule<CreateWalletFrozenReq, QueryWalletFrozenRsp // 创建类型
, EditWalletFrozenReq // 编辑类型
, QueryWalletFrozenReq, QueryWalletFrozenRsp // 查询类型
, DelReq // 删除类型
>
{
/// <summary>
/// 将状态设置为解冻
/// </summary>
Task<int> SetStatusToThawedAsync(SetStatusToThawedReq req);
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Api;
using NetAdmin.Domain.Extensions;
@ -176,7 +177,7 @@ public sealed class ApiService(
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -187,6 +188,6 @@ public sealed class ApiService(
ret = ret.OrderByDescending(a => a.CreatedTime);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.CodeTemplate;
using NetAdmin.Domain.Extensions;
@ -113,13 +114,6 @@ public sealed class CodeTemplateService(BasicRepository<Sys_CodeTemplate, long>
return ret.Adapt<IEnumerable<QueryCodeTemplateRsp>>();
}
/// <inheritdoc />
public Task<int> SetEnabledAsync(SetCodeTemplateEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.Enabled)]);
}
private ISelect<Sys_CodeTemplate> QueryInternal(QueryReq<QueryCodeTemplateReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
@ -127,7 +121,7 @@ public sealed class CodeTemplateService(BasicRepository<Sys_CodeTemplate, long>
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -137,6 +131,6 @@ public sealed class CodeTemplateService(BasicRepository<Sys_CodeTemplate, long>
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Config;
using NetAdmin.Domain.Extensions;
@ -142,7 +143,7 @@ public sealed class ConfigService(BasicRepository<Sys_Config, long> rpo) //
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -152,6 +153,6 @@ public sealed class ConfigService(BasicRepository<Sys_Config, long> rpo) //
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -3,4 +3,10 @@ namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 用户邀请服务
/// </summary>
public interface IUserInviteService : IService, IUserInviteModule;
public interface IUserInviteService : IService, IUserInviteModule
{
/// <summary>
/// 获取关联用户Id
/// </summary>
Task<List<long>> GetAssociatedUserIdAsync(long userId);
}

View File

@ -0,0 +1,6 @@
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 钱包冻结服务
/// </summary>
public interface IWalletFrozenService : IService, IWalletFrozenModule;

View File

@ -220,7 +220,7 @@ public sealed class DepositOrderService(BasicRepository<Sys_DepositOrder, long>
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -230,6 +230,6 @@ public sealed class DepositOrderService(BasicRepository<Sys_DepositOrder, long>
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Dept;
using NetAdmin.Domain.Extensions;
@ -143,7 +144,7 @@ public sealed class DeptService(BasicRepository<Sys_Dept, long> rpo) //
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -158,6 +159,6 @@ public sealed class DeptService(BasicRepository<Sys_Dept, long> rpo) //
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -302,6 +302,87 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
await WriteCsCodeAsync(Path.Combine(dir, $"{req.EntityName}Cache.cs"), templateContent).ConfigureAwait(false);
}
private static Task GenerateDomainCreateReqFileAsync(GenerateCsCodeReq req, string project, string prefix)
{
var sb = new StringBuilder();
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"using {project}.DbMaps.{prefix};");
if (!req.Interfaces.NullOrEmpty()) {
_ = sb.AppendLine("using NetAdmin.Domain.DbMaps.Dependency.Fields;");
}
_ = sb.AppendLine();
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"namespace {project}.Dto.{prefix}.{req.EntityName};");
_ = sb.AppendLine();
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"""
/// <summary>
/// 请求:创建{req.Summary}
/// </summary>
""");
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"public record Create{req.EntityName}Req : {prefix}_{req.EntityName}");
_ = sb.Append('{');
if (req.Interfaces?.Contains(nameof(IFieldEnabled)) == true) {
_ = sb.AppendLine("""
/// <inheritdoc cref="IFieldEnabled.Enabled" />
public override bool Enabled { get; init; } = true;
""");
}
if (req.BaseClass != nameof(SimpleEntity) && req.FieldList.Single(x => x.IsPrimary).Type == "string") {
_ = sb.AppendLine("""
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[Required]
public override string Id { get; init; }
""");
}
if (req.Interfaces?.Contains(nameof(IFieldSort)) == true) {
_ = sb.AppendLine("""
/// <inheritdoc cref="IFieldSort.Sort" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Sort { get; init; }
""");
}
if (req.Interfaces?.Contains(nameof(IFieldSummary)) == true) {
_ = sb.AppendLine("""
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
""");
}
foreach (var field in req.FieldList) {
var condition = field.IsStruct ? "Never" : "WhenWritingNull";
var nul = field.IsStruct && field.IsNullable ? "?" : null;
var isEnum = S<IConstantService>().GetEnums().FirstOrDefault(x => x.Key == field.Type);
string enumConstraint = null;
if (!isEnum.Key.NullOrEmpty()) {
enumConstraint = $"""
[EnumDataType(typeof({field.Type}))]
""";
}
_ = sb.AppendLine(CultureInfo.InvariantCulture, $$"""
/// <inheritdoc cref="{{prefix}}_{{req.EntityName}}.{{field.Name}}" />
[JsonIgnore(Condition = JsonIgnoreCondition.{{condition}})]{{enumConstraint}}
public override {{field.Type}}{{nul}} {{field.Name}} { get; init; }
""");
}
_ = sb.Append('}');
var outPath = Path.Combine(Path.GetDirectoryName(req.Project)!, "Dto", prefix, req.EntityName);
_ = Directory.CreateDirectory(outPath);
return WriteCsCodeAsync(Path.Combine(outPath, $"Create{req.EntityName}Req.cs"), sb.ToString());
}
private static Task GenerateDomainEditReqFileAsync(GenerateCsCodeReq req, string project, string prefix)
{
var sb = new StringBuilder();
@ -1002,85 +1083,4 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
content = string.Join('\n', usings) + sb;
return File.WriteAllTextAsync(path, usings.Count == 0 ? content.Trim() : content);
}
private Task GenerateDomainCreateReqFileAsync(GenerateCsCodeReq req, string project, string prefix)
{
var sb = new StringBuilder();
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"using {project}.DbMaps.{prefix};");
if (!req.Interfaces.NullOrEmpty()) {
_ = sb.AppendLine("using NetAdmin.Domain.DbMaps.Dependency.Fields;");
}
_ = sb.AppendLine();
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"namespace {project}.Dto.{prefix}.{req.EntityName};");
_ = sb.AppendLine();
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"""
/// <summary>
/// 请求:创建{req.Summary}
/// </summary>
""");
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"public record Create{req.EntityName}Req : {prefix}_{req.EntityName}");
_ = sb.Append('{');
if (req.Interfaces?.Contains(nameof(IFieldEnabled)) == true) {
_ = sb.AppendLine("""
/// <inheritdoc cref="IFieldEnabled.Enabled" />
public override bool Enabled { get; init; } = true;
""");
}
if (req.BaseClass != nameof(SimpleEntity) && req.FieldList.Single(x => x.IsPrimary).Type == "string") {
_ = sb.AppendLine("""
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[Required]
public override string Id { get; init; }
""");
}
if (req.Interfaces?.Contains(nameof(IFieldSort)) == true) {
_ = sb.AppendLine("""
/// <inheritdoc cref="IFieldSort.Sort" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Sort { get; init; }
""");
}
if (req.Interfaces?.Contains(nameof(IFieldSummary)) == true) {
_ = sb.AppendLine("""
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
""");
}
foreach (var field in req.FieldList) {
var condition = field.IsStruct ? "Never" : "WhenWritingNull";
var nul = field.IsStruct && field.IsNullable ? "?" : null;
var isEnum = S<IConstantService>().GetEnums().FirstOrDefault(x => x.Key == field.Type);
string enumConstraint = null;
if (!isEnum.Key.NullOrEmpty()) {
enumConstraint = $"""
[EnumDataType(typeof({field.Type}))]
""";
}
_ = sb.AppendLine(CultureInfo.InvariantCulture, $$"""
/// <inheritdoc cref="{{prefix}}_{{req.EntityName}}.{{field.Name}}" />
[JsonIgnore(Condition = JsonIgnoreCondition.{{condition}})]{{enumConstraint}}
public override {{field.Type}}{{nul}} {{field.Name}} { get; init; }
""");
}
_ = sb.Append('}');
var outPath = Path.Combine(Path.GetDirectoryName(req.Project)!, "Dto", prefix, req.EntityName);
_ = Directory.CreateDirectory(outPath);
return WriteCsCodeAsync(Path.Combine(outPath, $"Create{req.EntityName}Req.cs"), sb.ToString());
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Dic.Catalog;
using NetAdmin.Domain.Extensions;
@ -126,7 +127,7 @@ public sealed class DicCatalogService(BasicRepository<Sys_DicCatalog, long> rpo)
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -136,6 +137,6 @@ public sealed class DicCatalogService(BasicRepository<Sys_DicCatalog, long> rpo)
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
using NetAdmin.Domain.Extensions;
@ -148,7 +149,7 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -158,6 +159,6 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Doc.Catalog;
using NetAdmin.Domain.Extensions;
@ -127,7 +128,7 @@ public sealed class DocCatalogService(BasicRepository<Sys_DocCatalog, long> rpo)
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -137,6 +138,6 @@ public sealed class DocCatalogService(BasicRepository<Sys_DocCatalog, long> rpo)
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Doc.Content;
using NetAdmin.Domain.Enums.Sys;
@ -171,7 +172,7 @@ public sealed class DocContentService(BasicRepository<Sys_DocContent, long> rpo)
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -181,6 +182,6 @@ public sealed class DocContentService(BasicRepository<Sys_DocContent, long> rpo)
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys;
using NetAdmin.Domain.Dto.Sys.JobRecord;
@ -161,7 +162,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -171,6 +172,6 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,5 +1,6 @@
using Cronos;
using FreeSql.Internal;
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys;
using NetAdmin.Domain.Dto.Sys.Job;
@ -342,7 +343,7 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -352,6 +353,6 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
ret = ret.OrderByDescending(a => a.LastExecTime);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.LoginLog;
using NetAdmin.Domain.Extensions;
@ -129,7 +130,7 @@ public sealed class LoginLogService(BasicRepository<Sys_LoginLog, long> rpo) //
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -139,6 +140,6 @@ public sealed class LoginLogService(BasicRepository<Sys_LoginLog, long> rpo) //
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Menu;
using NetAdmin.Domain.Extensions;
@ -130,7 +131,7 @@ public sealed class MenuService(BasicRepository<Sys_Menu, long> rpo, IUserServic
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
return req.Order switch {
ret = req.Order switch {
Orders.None => ret
, Orders.Random => ret.OrderByRandom()
, _ => ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
@ -138,5 +139,6 @@ public sealed class MenuService(BasicRepository<Sys_Menu, long> rpo, IUserServic
.OrderBy(a => a.Name)
.OrderBy(a => a.Id)
};
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.RequestLogDetail;
using NetAdmin.Domain.Extensions;
@ -113,7 +114,7 @@ public sealed class RequestLogDetailService(BasicRepository<Sys_RequestLogDetail
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -123,6 +124,6 @@ public sealed class RequestLogDetailService(BasicRepository<Sys_RequestLogDetail
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys;
using NetAdmin.Domain.Dto.Sys.LoginLog;
@ -215,7 +216,7 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo,
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -226,6 +227,6 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo,
ret = ret.OrderByDescending(a => a.CreatedTime);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Role;
using NetAdmin.Domain.Dto.Sys.UserRole;
@ -168,7 +169,7 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> rpo, IUserRoleSe
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -183,6 +184,6 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> rpo, IUserRoleSe
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.SiteMsgDept;
using NetAdmin.Domain.Extensions;
@ -110,7 +111,7 @@ public sealed class SiteMsgDeptService(BasicRepository<Sys_SiteMsgDept, long> rp
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -120,6 +121,6 @@ public sealed class SiteMsgDeptService(BasicRepository<Sys_SiteMsgDept, long> rp
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.SiteMsgFlag;
using NetAdmin.Domain.Extensions;
@ -117,7 +118,7 @@ public sealed class SiteMsgFlagService(BasicRepository<Sys_SiteMsgFlag, long> rp
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -127,6 +128,6 @@ public sealed class SiteMsgFlagService(BasicRepository<Sys_SiteMsgFlag, long> rp
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.SiteMsgRole;
using NetAdmin.Domain.Extensions;
@ -110,7 +111,7 @@ public sealed class SiteMsgRoleService(BasicRepository<Sys_SiteMsgRole, long> rp
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -120,6 +121,6 @@ public sealed class SiteMsgRoleService(BasicRepository<Sys_SiteMsgRole, long> rp
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.SiteMsg;
@ -271,7 +272,7 @@ public sealed class SiteMsgService(BasicRepository<Sys_SiteMsg, long> rpo, Conte
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -281,7 +282,7 @@ public sealed class SiteMsgService(BasicRepository<Sys_SiteMsg, long> rpo, Conte
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
private ISelectGrouping //

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.SiteMsgUser;
using NetAdmin.Domain.Extensions;
@ -110,7 +111,7 @@ public sealed class SiteMsgUserService(BasicRepository<Sys_SiteMsgUser, long> rp
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
}
@ -120,6 +121,6 @@ public sealed class SiteMsgUserService(BasicRepository<Sys_SiteMsgUser, long> rp
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.UserInvite;
using NetAdmin.Domain.Extensions;
@ -78,6 +79,16 @@ public sealed class UserInviteService(BasicRepository<Sys_UserInvite, long> rpo)
return ExportAsync<QueryUserInviteReq, QueryUserInviteRsp>(QueryInternal, req, Ln.);
}
/// <inheritdoc />
public Task<List<long>> GetAssociatedUserIdAsync(long userId)
{
return Rpo.Orm.Select<Sys_UserInvite>()
.DisableGlobalFilter(Chars.FLG_FREE_SQL_GLOBAL_FILTER_DATA)
.Where(a => a.Id == userId)
.AsTreeCte(up: true, disableGlobalFilters: [Chars.FLG_FREE_SQL_GLOBAL_FILTER_DATA])
.ToListAsync(a => a.Id);
}
/// <inheritdoc />
public async Task<QueryUserInviteRsp> GetAsync(QueryUserInviteReq req)
{
@ -128,9 +139,9 @@ public sealed class UserInviteService(BasicRepository<Sys_UserInvite, long> rpo)
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
return ret.OrderByRandom().AppendOtherFilters(req);
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
@ -138,6 +149,6 @@ public sealed class UserInviteService(BasicRepository<Sys_UserInvite, long> rpo)
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
@ -188,9 +189,9 @@ public sealed class UserProfileService(BasicRepository<Sys_UserProfile, long> rp
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
return ret.OrderByRandom().AppendOtherFilters(req);
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
@ -198,7 +199,7 @@ public sealed class UserProfileService(BasicRepository<Sys_UserProfile, long> rp
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
private ISelect<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent> QueryInternalComplex(

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.UserRole;
using NetAdmin.Domain.Extensions;
@ -110,9 +111,9 @@ public sealed class UserRoleService(BasicRepository<Sys_UserRole, long> rpo) //
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
return ret.OrderByRandom().AppendOtherFilters(req);
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
@ -120,6 +121,6 @@ public sealed class UserRoleService(BasicRepository<Sys_UserRole, long> rpo) //
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.Attributes.DataValidation;
using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.DbMaps.Sys;
@ -551,9 +552,9 @@ public sealed class UserService(
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
return ret.OrderByRandom().AppendOtherFilters(req);
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
@ -562,7 +563,7 @@ public sealed class UserService(
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
private ISelect<Sys_User> QueryInternal(QueryReq<QueryUserReq> req)

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.UserWallet;
using NetAdmin.Domain.Extensions;
@ -65,7 +66,8 @@ public sealed class UserWalletService(BasicRepository<Sys_UserWallet, long> rpo)
{
req.ThrowIfInvalid();
#if DBTYPE_SQLSERVER
return (await UpdateReturnListAsync(req).ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryUserWalletRsp>();
return (await UpdateReturnListAsync(req, [nameof(req.FrozenBalance), nameof(req.AvailableBalance)]).ConfigureAwait(false)).FirstOrDefault()
?.Adapt<QueryUserWalletRsp>();
#else
return await UpdateAsync(req).ConfigureAwait(false) > 0 ? await GetAsync(new QueryUserWalletReq { Id = req.Id }).ConfigureAwait(false) : null;
#endif
@ -130,9 +132,9 @@ public sealed class UserWalletService(BasicRepository<Sys_UserWallet, long> rpo)
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
return ret.OrderByRandom().AppendOtherFilters(req);
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
@ -140,6 +142,6 @@ public sealed class UserWalletService(BasicRepository<Sys_UserWallet, long> rpo)
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.VerifyCode;
using NetAdmin.Domain.Enums.Sys;
@ -191,9 +192,9 @@ public sealed class VerifyCodeService(BasicRepository<Sys_VerifyCode, long> rpo,
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
return ret.OrderByRandom().AppendOtherFilters(req);
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
@ -201,6 +202,6 @@ public sealed class VerifyCodeService(BasicRepository<Sys_VerifyCode, long> rpo,
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -0,0 +1,195 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.Domain.Dto.Sys.UserWallet;
using NetAdmin.Domain.Dto.Sys.WalletFrozen;
using NetAdmin.Domain.Enums.Sys;
using NetAdmin.Domain.Extensions;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IWalletFrozenService" />
public sealed class WalletFrozenService(BasicRepository<Sys_WalletFrozen, long> rpo) //
: RepositoryService<Sys_WalletFrozen, long, IWalletFrozenService>(rpo), IWalletFrozenService
{
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var ret = 0;
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var item in req.Items) {
ret += await DeleteAsync(item).ConfigureAwait(false);
}
return ret;
}
/// <inheritdoc />
public Task<long> CountAsync(QueryReq<QueryWalletFrozenReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).WithNoLockNoWait().CountAsync();
}
/// <inheritdoc />
public async Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryWalletFrozenReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None })
.WithNoLockNoWait()
.GroupBy(req.GetToListExp<Sys_WalletFrozen>())
.ToDictionaryAsync(a => a.Count())
.ConfigureAwait(false);
return ret.Select(x => new KeyValuePair<IImmutableDictionary<string, string>, int>(
req.RequiredFields.ToImmutableDictionary(
y => y
, y => y.Contains('.')
? typeof(Sys_WalletFrozen).GetRecursiveProperty(y)!.GetValue(
x.Key.GetType().GetRecursiveProperty(y[..y.LastIndexOf('.')]).GetValue(x.Key))!.ToString()
: typeof(Sys_WalletFrozen).GetProperty(y)!.GetValue(x.Key)!.ToString()), x.Value))
.OrderByDescending(x => x.Value);
}
/// <inheritdoc />
public async Task<QueryWalletFrozenRsp> CreateAsync(CreateWalletFrozenReq req)
{
req.ThrowIfInvalid();
var userId = UserToken.Id;
var userDeptId = UserToken.DeptId;
if (req.OwnerId != null) {
userId = req.OwnerId.Value;
var owner = await S<IUserService>().GetAsync(new QueryUserReq { Id = userId }).ConfigureAwait(false);
userDeptId = owner.DeptId;
}
var wallet = await S<IUserWalletService>().GetAsync(new QueryUserWalletReq { Id = userId }).ConfigureAwait(false);
if (wallet.AvailableBalance < req.Amount) {
throw new NetAdminInvalidOperationException(Ln.);
}
var frozenBalanceBefore = wallet.FrozenBalance;
if (await S<IUserWalletService>()
.EditAsync(wallet.Adapt<EditUserWalletReq>() with {
AvailableBalance = wallet.AvailableBalance - req.Amount
, FrozenBalance = wallet.FrozenBalance + req.Amount
})
.ConfigureAwait(false) == null) {
throw new NetAdminUnexpectedException(Ln.);
}
var ret = await Rpo.InsertAsync(req with { OwnerDeptId = userDeptId, FrozenBalanceBefore = frozenBalanceBefore }).ConfigureAwait(false);
return ret.Adapt<QueryWalletFrozenRsp>();
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public async Task<QueryWalletFrozenRsp> EditAsync(EditWalletFrozenReq req)
{
req.ThrowIfInvalid();
#if DBTYPE_SQLSERVER
return (await UpdateReturnListAsync(req).ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryWalletFrozenRsp>();
#else
return await UpdateAsync(req).ConfigureAwait(false) > 0
? await GetAsync(new QueryWalletFrozenReq { Id = req.Id }).ConfigureAwait(false)
: null;
#endif
}
/// <inheritdoc />
public Task<IActionResult> ExportAsync(QueryReq<QueryWalletFrozenReq> req)
{
req.ThrowIfInvalid();
return ExportAsync<QueryWalletFrozenReq, QueryWalletFrozenRsp>(QueryInternal, req, Ln.);
}
/// <inheritdoc />
public async Task<QueryWalletFrozenRsp> GetAsync(QueryWalletFrozenReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryWalletFrozenReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryWalletFrozenRsp>();
}
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryWalletFrozenRsp>> PagedQueryAsync(PagedQueryReq<QueryWalletFrozenReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Include(a => a.Owner)
.Page(req.Page, req.PageSize)
.WithNoLockNoWait()
.Count(out var total)
.ToListAsync(req)
.ConfigureAwait(false);
return new PagedQueryRsp<QueryWalletFrozenRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryWalletFrozenRsp>>());
}
/// <inheritdoc />
public async Task<IEnumerable<QueryWalletFrozenRsp>> QueryAsync(QueryReq<QueryWalletFrozenReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).WithNoLockNoWait().Take(req.Count).ToListAsync(req).ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryWalletFrozenRsp>>();
}
/// <inheritdoc />
public async Task<int> SetStatusToThawedAsync(SetStatusToThawedReq req)
{
req.ThrowIfInvalid();
var frozen = await GetAsync(new QueryWalletFrozenReq { Id = req.Id }).ConfigureAwait(false);
if (frozen?.Status != WalletFrozenStatues.Frozen) {
throw new NetAdminInvalidOperationException(Ln.);
}
var updateCnt = await UpdateAsync(new Sys_WalletFrozen { Status = WalletFrozenStatues.Thawed }, [nameof(req.Status)], null
, a => a.Status == WalletFrozenStatues.Frozen && a.Id == req.Id)
.ConfigureAwait(false);
if (updateCnt != 1) {
throw new NetAdminUnexpectedException(Ln.);
}
var userWalletService = S<IUserWalletService>();
var wallet = await userWalletService.GetAsync(new QueryUserWalletReq { Id = frozen.OwnerId!.Value }).ConfigureAwait(false);
if (wallet.FrozenBalance < req.Amount) {
throw new NetAdminUnexpectedException(Ln.);
}
_ = await userWalletService
.EditAsync(wallet.Adapt<EditUserWalletReq>() with {
AvailableBalance = wallet.AvailableBalance + frozen.Amount
, FrozenBalance = wallet.FrozenBalance - frozen.Amount
})
.ConfigureAwait(false) ?? throw new NetAdminUnexpectedException(Ln.);
return 1;
}
private ISelect<Sys_WalletFrozen> QueryInternal(QueryReq<QueryWalletFrozenReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom().AppendOtherFilters(req);
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.Id);
}
return ret.AppendOtherFilters(req);
}
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Application.Extensions;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.UserWallet;
using NetAdmin.Domain.Dto.Sys.WalletTrade;
@ -53,13 +54,10 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
var userWalletService = S<IUserWalletService>();
var wallet = await userWalletService.GetAsync(new QueryUserWalletReq { Id = req.OwnerId!.Value }).ConfigureAwait(false);
if (wallet.AvailableBalance + req.Amount < 0) {
throw new NetAdminInvalidOperationException(Ln.);
throw new NetAdminInvalidOperationException(Ln.);
}
_ = await userWalletService.EditAsync(wallet.Adapt<EditUserWalletReq>() with {
AvailableBalance = wallet.AvailableBalance + req.Amount
, TotalBalance = wallet.TotalBalance + req.Amount
})
_ = await userWalletService.EditAsync(wallet.Adapt<EditUserWalletReq>() with { AvailableBalance = wallet.AvailableBalance + req.Amount })
.ConfigureAwait(false) ?? throw new NetAdminUnexpectedException(Ln.);
var ret = await Rpo.InsertAsync(req with { BalanceBefore = wallet.AvailableBalance, OwnerDeptId = wallet.OwnerDeptId }).ConfigureAwait(false);
return ret.Adapt<QueryWalletTradeRsp>();
@ -130,9 +128,9 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
return ret.AppendOtherFilters(req);
case Orders.Random:
return ret.OrderByRandom();
return ret.OrderByRandom().AppendOtherFilters(req);
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
@ -140,6 +138,6 @@ public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rp
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
return ret.AppendOtherFilters(req);
}
}

View File

@ -65,10 +65,4 @@ public sealed class CodeTemplateCache(IDistributedCache cache, ICodeTemplateServ
{
return Service.QueryAsync(req);
}
/// <inheritdoc />
public Task<int> SetEnabledAsync(SetCodeTemplateEnabledReq req)
{
return Service.SetEnabledAsync(req);
}
}

View File

@ -0,0 +1,6 @@
namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
/// <summary>
/// 钱包冻结缓存
/// </summary>
public interface IWalletFrozenCache : ICache<IDistributedCache, IWalletFrozenService>, IWalletFrozenModule;

View File

@ -0,0 +1,74 @@
using NetAdmin.Domain.Dto.Sys.WalletFrozen;
namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="IWalletFrozenCache" />
public sealed class WalletFrozenCache(IDistributedCache cache, IWalletFrozenService service)
: DistributedCache<IWalletFrozenService>(cache, service), IScoped, IWalletFrozenCache
{
/// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
return Service.BulkDeleteAsync(req);
}
/// <inheritdoc />
public Task<long> CountAsync(QueryReq<QueryWalletFrozenReq> req)
{
return Service.CountAsync(req);
}
/// <inheritdoc />
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryWalletFrozenReq> req)
{
return Service.CountByAsync(req);
}
/// <inheritdoc />
public Task<QueryWalletFrozenRsp> CreateAsync(CreateWalletFrozenReq req)
{
return Service.CreateAsync(req);
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
return Service.DeleteAsync(req);
}
/// <inheritdoc />
public Task<QueryWalletFrozenRsp> EditAsync(EditWalletFrozenReq req)
{
return Service.EditAsync(req);
}
/// <inheritdoc />
public Task<IActionResult> ExportAsync(QueryReq<QueryWalletFrozenReq> req)
{
return Service.ExportAsync(req);
}
/// <inheritdoc />
public Task<QueryWalletFrozenRsp> GetAsync(QueryWalletFrozenReq req)
{
return Service.GetAsync(req);
}
/// <inheritdoc />
public Task<PagedQueryRsp<QueryWalletFrozenRsp>> PagedQueryAsync(PagedQueryReq<QueryWalletFrozenReq> req)
{
return Service.PagedQueryAsync(req);
}
/// <inheritdoc />
public Task<IEnumerable<QueryWalletFrozenRsp>> QueryAsync(QueryReq<QueryWalletFrozenReq> req)
{
return Service.QueryAsync(req);
}
/// <inheritdoc />
public Task<int> SetStatusToThawedAsync(SetStatusToThawedReq req)
{
return Service.SetStatusToThawedAsync(req);
}
}

View File

@ -95,12 +95,4 @@ public sealed class CodeTemplateController(ICodeTemplateCache cache)
{
return Cache.QueryAsync(req);
}
/// <summary>
/// 设置代码模板启用状态
/// </summary>
public Task<int> SetEnabledAsync(SetCodeTemplateEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}
}

View File

@ -0,0 +1,107 @@
using NetAdmin.Domain.Dto.Sys.WalletFrozen;
namespace NetAdmin.SysComponent.Host.Controllers.Sys;
/// <summary>
/// 钱包冻结服务
/// </summary>
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
[Produces(Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_JSON)]
public sealed class WalletFrozenController(IWalletFrozenCache cache)
: ControllerBase<IWalletFrozenCache, IWalletFrozenService>(cache), IWalletFrozenModule
{
/// <summary>
/// 批量删除钱包冻结
/// </summary>
[Transaction]
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
return Cache.BulkDeleteAsync(req);
}
/// <summary>
/// 钱包冻结计数
/// </summary>
public Task<long> CountAsync(QueryReq<QueryWalletFrozenReq> req)
{
return Cache.CountAsync(req);
}
/// <summary>
/// 钱包冻结分组计数
/// </summary>
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryWalletFrozenReq> req)
{
return Cache.CountByAsync(req);
}
/// <summary>
/// 创建钱包冻结
/// </summary>
[Transaction]
public Task<QueryWalletFrozenRsp> CreateAsync(CreateWalletFrozenReq req)
{
return Cache.CreateAsync(req);
}
/// <summary>
/// 删除钱包冻结
/// </summary>
[Transaction]
public Task<int> DeleteAsync(DelReq req)
{
return Cache.DeleteAsync(req);
}
/// <summary>
/// 编辑钱包冻结
/// </summary>
[Transaction]
public Task<QueryWalletFrozenRsp> EditAsync(EditWalletFrozenReq req)
{
return Cache.EditAsync(req);
}
/// <summary>
/// 导出钱包冻结
/// </summary>
[NonAction]
public Task<IActionResult> ExportAsync(QueryReq<QueryWalletFrozenReq> req)
{
return Cache.ExportAsync(req);
}
/// <summary>
/// 获取单个钱包冻结
/// </summary>
public Task<QueryWalletFrozenRsp> GetAsync(QueryWalletFrozenReq req)
{
return Cache.GetAsync(req);
}
/// <summary>
/// 分页查询钱包冻结
/// </summary>
public Task<PagedQueryRsp<QueryWalletFrozenRsp>> PagedQueryAsync(PagedQueryReq<QueryWalletFrozenReq> req)
{
return Cache.PagedQueryAsync(req);
}
/// <summary>
/// 查询钱包冻结
/// </summary>
[NonAction]
public Task<IEnumerable<QueryWalletFrozenRsp>> QueryAsync(QueryReq<QueryWalletFrozenReq> req)
{
return Cache.QueryAsync(req);
}
/// <summary>
/// 将状态设置为解冻
/// </summary>
[Transaction]
public Task<int> SetStatusToThawedAsync(SetStatusToThawedReq req)
{
return Cache.SetStatusToThawedAsync(req);
}
}

View File

@ -5,7 +5,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.7"/>
<PackageReference Include="xunit" Version="2.9.3"/>
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1">
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

View File

@ -206,17 +206,6 @@ public class UserTests(WebTestApplicationFactory<Startup> factory, ITestOutputHe
return null;
}
/// <inheritdoc />
[InlineData(null)]
[Theory]
[TestPriority(101600)]
public async Task<IEnumerable<QueryUserRsp>> QueryRelationAsync(QueryReq<QueryUserReq> req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return null;
}
/// <inheritdoc />
[InlineData(null)]
[Theory]

View File

@ -1 +1 @@
global using CsvIgnore = CsvHelper.Configuration.Attributes.IgnoreAttribute;
global using CsvIgnore = CsvHelper.Configuration.Attributes.IgnoreAttribute;

View File

@ -10,13 +10,13 @@
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"ace-builds": "1.43.0",
"ace-builds": "1.43.1",
"aieditor": "1.4.0",
"axios": "1.10.0",
"crypto-js": "4.2.0",
"dayjs": "1.11.13",
"echarts": "5.6.0",
"element-plus": "2.10.2",
"element-plus": "2.10.3",
"json-bigint": "1.0.0",
"markdown-it": "14.1.0",
"markdown-it-emoji": "3.0.0",
@ -25,7 +25,7 @@
"sortablejs": "1.15.6",
"vkbeautify": "0.99.3",
"vue": "3.5.17",
"vue-i18n": "11.1.7",
"vue-i18n": "11.1.9",
"vue-router": "4.5.1",
"vue3-ace-editor": "2.2.4",
"vue3-json-viewer": "2.4.1",
@ -38,7 +38,7 @@
"prettier-plugin-organize-attributes": "1.0.0",
"sass": "1.89.2",
"terser": "5.43.1",
"vite": "7.0.0"
"vite": "7.0.4"
},
"browserslist": [
"> 1%",

View File

@ -92,15 +92,4 @@ export default {
return await http.post(this.url, data, config)
},
},
/**
* 设置代码模板启用状态
*/
setEnabled: {
url: `${config.API_URL}/api/sys/code.template/set.enabled`,
name: `设置代码模板启用状态`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
}

View File

@ -0,0 +1,106 @@
/**
* 钱包冻结服务
* @module @/api/sys/wallet.frozen
*/
import config from '@/config'
import http from '@/utils/request'
export default {
/**
* 批量删除钱包冻结
*/
bulkDelete: {
url: `${config.API_URL}/api/sys/wallet.frozen/bulk.delete`,
name: `批量删除钱包冻结`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 钱包冻结计数
*/
count: {
url: `${config.API_URL}/api/sys/wallet.frozen/count`,
name: `钱包冻结计数`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 钱包冻结分组计数
*/
countBy: {
url: `${config.API_URL}/api/sys/wallet.frozen/count.by`,
name: `钱包冻结分组计数`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 创建钱包冻结
*/
create: {
url: `${config.API_URL}/api/sys/wallet.frozen/create`,
name: `创建钱包冻结`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 删除钱包冻结
*/
delete: {
url: `${config.API_URL}/api/sys/wallet.frozen/delete`,
name: `删除钱包冻结`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 编辑钱包冻结
*/
edit: {
url: `${config.API_URL}/api/sys/wallet.frozen/edit`,
name: `编辑钱包冻结`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个钱包冻结
*/
get: {
url: `${config.API_URL}/api/sys/wallet.frozen/get`,
name: `获取单个钱包冻结`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 分页查询钱包冻结
*/
pagedQuery: {
url: `${config.API_URL}/api/sys/wallet.frozen/paged.query`,
name: `分页查询钱包冻结`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 将状态设置为解冻
*/
setStatusToThawed: {
url: `${config.API_URL}/api/sys/wallet.frozen/set.status.to.thawed`,
name: `将状态设置为解冻`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
}

View File

@ -1,6 +1,6 @@
<template>
<svg class="icon" height="256" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
<path d="M256 512h512v85.333333H256z" fill="" />
<path d="M256 512h512v85.333333H256z" />
<path
d="M936.448 370.56l-85.333333-256.042667A42.538667 42.538667 0 0 0 810.666667 85.333333H213.333333a42.581333 42.581333 0 0 0-40.448 29.184l-85.333333 256 0.512 0.170667A39.594667 39.594667 0 0 0 85.333333 384v426.666667a42.666667 42.666667 0 0 0 42.666667 42.666666h768a42.666667 42.666667 0 0 0 42.666667-42.666666V384a41.088 41.088 0 0 0-2.688-13.312l0.469333-0.128zM244.096 170.666667H779.946667l56.874666 170.666666H187.178667l56.917333-170.666666zM853.333333 768H170.666667v-341.333333h682.666666v341.333333z" />
</svg>

View File

@ -0,0 +1,15 @@
<template>
<svg class="icon" height="256" p-id="5898" t="1752220814991" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
<path
d="M512 954.181818c-218.763636 0-465.454545 0-465.454545-279.272727C46.545455 418.909091 256 209.454545 512 209.454545s465.454545 209.454545 465.454545 465.454546c0 279.272727-246.690909 279.272727-465.454545 279.272727z m0-698.181818c-230.4 0-418.909091 188.509091-418.909091 418.909091 0 216.436364 165.236364 232.727273 418.909091 232.727273s418.909091-16.290909 418.909091-232.727273c0-230.4-188.509091-418.909091-418.909091-418.909091z"
fill="#424752"
p-id="5899"></path>
<path
d="M679.563636 283.927273l-44.218181-13.963637 37.236363-128c0-2.327273 0-4.654545-2.327273-4.654545 0 0-2.327273-2.327273-6.981818 0l-41.890909 16.290909c-20.945455 6.981818-41.890909 4.654545-58.181818-11.636364l-58.181818-58.181818c-2.327273-2.327273-6.981818-2.327273-9.309091 0l-58.181818 58.181818c-16.290909 16.290909-37.236364 20.945455-58.181818 11.636364l-41.89091-16.290909c-4.654545-2.327273-6.981818 0-6.981818 0-2.327273 2.327273-2.327273 2.327273-2.327272 4.654545l37.236363 128-44.218182 13.963637-37.236363-128c-4.654545-18.618182 0-39.563636 13.963636-53.527273 16.290909-13.963636 37.236364-18.618182 55.854546-9.309091l41.890909 16.290909c2.327273 2.327273 4.654545 2.327273 6.981818-2.327273l58.181818-58.181818c20.945455-20.945455 53.527273-20.945455 74.472727 0l58.181818 58.181818c2.327273 2.327273 4.654545 2.327273 6.981819 2.327273l41.890909-16.290909c18.618182-6.981818 39.563636-4.654545 55.854545 9.309091 18.618182 16.290909 23.272727 37.236364 18.618182 55.854545l-37.236364 125.672728zM512 581.818182l-155.927273-155.927273 32.581818-32.581818 123.345455 123.345454 123.345455-123.345454 32.581818 32.581818z"
fill="#424752"
p-id="5900"></path>
<path d="M325.818182 535.272727h372.363636v46.545455H325.818182z" fill="#424752" p-id="5901"></path>
<path d="M488.727273 535.272727h46.545454v279.272728h-46.545454z" fill="#424752" p-id="5902"></path>
<path d="M325.818182 674.909091h372.363636v46.545454H325.818182z" fill="#424752" p-id="5903"></path>
</svg>
</template>

View File

@ -77,3 +77,5 @@ export { default as 'nick-name' } from './nick-name'
export { default as telegram } from './telegram'
export { default as country } from './country'
export { default as template } from './template.vue'
export { default as order } from './order.vue'
export { default as income } from './income.vue'

View File

@ -0,0 +1,6 @@
<template>
<svg class="icon" height="256" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
<path
d="M856.006891 49.877991 793.57805 49.877991c-17.263176 0-37.193087 10.472516-37.193087 27.492144s19.929912 34.038234 37.193087 34.038234l62.428841 0c37.190017 0 62.425771 24.877597 62.425771 61.530378l0 708.23636c0 36.652781-25.235754 61.530378-62.425771 61.530378L183.931103 942.705486c-37.185924 0-78.360695-24.877597-78.360695-61.530378L105.570407 172.937724c0-36.652781 25.232684-61.530378 62.425771-61.530378l78.363765 0c17.270339 0 23.907503-17.019629 23.907503-34.038234s-6.638187-27.492144-23.907503-27.492144l-78.363765 0c-87.661525 0-124.853589 53.674457-124.853589 124.365472l0 700.384531c0 75.928297 46.486754 129.603777 131.494846 129.603777l674.722058 0c85.011162 0 131.497916-44.51382 131.497916-129.603777L980.857411 174.243463C980.857411 103.552447 937.03023 49.877991 856.006891 49.877991L856.006891 49.877991 856.006891 49.877991zM246.359944 327.412958c0 17.022699 14.609743 31.420617 31.873941 31.420617l468.857411 0c17.266245 0 31.880081-14.398942 31.880081-31.420617 0-17.019629-14.613836-31.420617-31.880081-31.420617L278.233885 295.992341C260.969687 295.991318 246.359944 310.393329 246.359944 327.412958L246.359944 327.412958 246.359944 327.412958zM745.766115 466.180442 278.233885 466.180442c-17.263176 0-31.873941 14.402011-31.873941 31.42471 0 17.015535 14.609743 31.414477 31.873941 31.414477l468.857411 0c17.266245 0 31.880081-14.398942 31.880081-31.414477C777.646196 479.273644 763.02929 466.180442 745.766115 466.180442L745.766115 466.180442 745.766115 466.180442zM745.766115 650.767484 278.233885 650.767484c-17.263176 0-31.873941 14.402011-31.873941 31.417547 0 17.019629 14.609743 31.417547 31.873941 31.417547l468.857411 0c17.266245 0 31.880081-14.398942 31.880081-31.417547C777.646196 663.860686 763.02929 650.767484 745.766115 650.767484L745.766115 650.767484 745.766115 650.767484zM404.415726 127.118166l217.828121 0c25.235754 0 46.486754-24.871457 46.486754-53.674457 0-28.79686-21.251-53.674457-47.818075-53.674457L404.415726 19.769252c-26.564006 0-47.811936 24.874527-47.811936 53.674457C356.603791 102.246709 377.851721 127.118166 404.415726 127.118166L404.415726 127.118166 404.415726 127.118166zM404.415726 127.118166"></path>
</svg>
</template>

View File

@ -1,6 +1,8 @@
<template>
<sc-dialog v-model="visible" :full-screen="dialogFullScreen" :title="titleMap[mode]" @closed="$emit(`closed`)" destroy-on-close>
<div v-loading="loading">
<el-tabs v-model="tabId" :class="{ 'hide-tabs': !tabs || !tabs[mode] || tabs[mode].length === 0 }">
<el-tab-pane :label="$t(`基本信息`)" name="basic">
<el-form
:disabled="![`edit`, `add`].includes(mode)"
:model="form"
@ -10,7 +12,13 @@
ref="dialogForm">
<template v-for="(item, i) in columns" :key="i">
<el-form-item v-if="item.show?.includes(mode)" :label="item.label" :prop="i">
<el-date-picker v-if="i.endsWith(`Time`)" v-model="form[i]" :disabled="item.disabled?.includes(mode)" type="datetime" />
<el-date-picker
v-bind="item.detail?.props"
v-if="i.endsWith(`Time`)"
v-model="form[i]"
:disabled="item.disabled?.includes(mode)"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss" />
<el-select v-else-if="item.enum" v-model="form[i]" :disabled="item.disabled?.includes(mode)" clearable filterable>
<el-option
v-for="e in Object.entries(this.$GLOBAL.enums[item.enum]).map((x) => {
@ -27,6 +35,12 @@
v-else-if="typeof form[i] === `boolean` || item.isBoolean"
v-model="form[i]"
:disabled="item.disabled?.includes(mode)" />
<component
v-bind="item.detail?.props"
v-else-if="item.detail?.vModelValue"
v-model:value="form[i]"
:disabled="item.disabled?.includes(mode)"
:is="item.detail?.is ?? `el-input`" />
<component
v-bind="item.detail?.props"
v-else
@ -36,6 +50,11 @@
</el-form-item>
</template>
</el-form>
</el-tab-pane>
<el-tab-pane v-bind="item" v-for="(item, i) in tabs[mode]" v-if="tabs" :key="i" :name="item.name">
<component v-if="item.name === tabId" :is="item.component" :ref="item.ref" @closed="paneClosed" />
</el-tab-pane>
</el-tabs>
</div>
<template #footer>
<el-button @click="visible = false">{{ $t(`取消`) }}</el-button>
@ -49,6 +68,7 @@ export default {
components: {},
data() {
return {
tabId: `basic`,
rules: {},
visible: false,
mode: `add`,
@ -64,6 +84,9 @@ export default {
created() {},
emits: [`success`, `closed`, `mounted`],
methods: {
async paneClosed() {
this.visible = false
},
//显示
async open(data) {
this.visible = true
@ -102,11 +125,13 @@ export default {
//表单提交方法
async submit() {
this.loading = true
if (this.tabId === `basic`) {
const valid = await this.$refs.dialogForm.validate().catch(() => {})
if (!valid) {
this.loading = false
return false
}
this.loading = true
const method = this.mode === `add` ? this.$API[this.entityName].create : this.$API[this.entityName].edit
try {
const res = await method.post(this.form)
@ -114,6 +139,12 @@ export default {
this.visible = false
this.$message.success(this.$t(`操作成功`))
} catch {}
} else {
if (await this.tabs.submit(this.$refs, this.tabId)) {
this.visible = false
}
}
this.loading = false
},
},
@ -125,8 +156,13 @@ export default {
summary: { type: String },
columns: { type: Array },
dialogFullScreen: { type: Boolean },
tabs: { type: Array },
},
}
</script>
<style scoped />
<style scoped>
.hide-tabs :deep(.el-tabs__nav-scroll) {
display: none;
}
</style>

View File

@ -1,4 +1,4 @@
<template>
<template>
<el-container>
<!-- 仪表板-->
<el-header v-loading="statistics.total === `...`" class="el-header-statistics">
@ -30,6 +30,7 @@
ref="search" />
</div>
<div class="right-panel">
<el-button v-bind="item" v-for="(item, i) in rightButtons" :key="i">{{ item.title }}</el-button>
<el-button v-if="operations.includes(`add`)" @click="onAddClick" icon="el-icon-plus" type="primary" />
<el-button
v-if="operations.includes(`del`)"
@ -64,7 +65,7 @@
<component
v-bind="item"
v-if="item.show.includes(`list`)"
:formatter="item.thousands ? (row) => $TOOL.groupSeparator(row[i]) : undefined"
:formatter="item.thousands ? (row) => $TOOL.groupSeparator($TOOL.getNestedProperty(row, i)) : undefined"
:is="item.is ?? `el-table-column`"
:options="
item.options ??
@ -96,11 +97,25 @@
<el-table-column :label="$t(`操作`)" align="right" fixed="right" width="150">
<template #default="{ row }">
<div class="flex justify-content-right">
<el-button-group size="small">
<el-button v-if="operations.includes(`view`)" @click="onViewClick(row)" icon="el-icon-view" />
<el-button v-if="operations.includes(`edit`)" @click="onEditClick(row)" icon="el-icon-edit" />
<el-button v-if="operations.includes(`del`)" @click="onDeleteClick(row)" icon="el-icon-delete" type="danger" />
</el-button-group>
<el-dropdown>
<el-button size="small">...</el-button>
<template #dropdown>
<el-dropdown-menu>
<template v-for="(button, j) in rowButtons?.filter((x) => !x.condition || x.condition(row))" :key="j">
<el-dropdown-item v-bind="button.props" @click="onRowButtonClick(row, button)" size="small">{{
button.title
}}</el-dropdown-item>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-table-column>
</sc-table>
@ -122,6 +137,7 @@ import { defineAsyncComponent } from 'vue'
import tableConfig from '@/config/table'
import naColOperation from '@/config/na-col-operation'
import config from '@/config'
import naColTags from '@/components/na-col-tags'
const naColAvatar = defineAsyncComponent(() => import('@/components/na-col-avatar'))
const detailDialog = defineAsyncComponent(() => import('./detail'))
const naColUser = defineAsyncComponent(() => import('@/components/na-col-user'))
@ -130,6 +146,7 @@ export default {
detailDialog,
naColAvatar,
naColUser,
naColTags,
},
computed: {
config() {
@ -287,6 +304,12 @@ export default {
// ---------------------------- 搜索栏事件 ↑ ----------------------------
// ---------------------------- ↓ 表格事件 ----------------------------
async onRowButtonClick(row, button) {
if (await button.click(row)) {
this.$message.success(this.$t(`操作成功`))
await this.$refs.table.upData()
}
},
async onViewClick(row) {
this.dialog.detail = { mode: `view`, row }
},
@ -352,6 +375,7 @@ export default {
calls.push(
this.$API[this.entityName].countBy.post({
filter: this.query.filter,
dynamicFilter: { filters: this.query.dynamicFilter.filters },
requiredFields: [col.replace(/(?:^|\.)[a-z]/g, (m) => m.toUpperCase())],
}),
@ -370,7 +394,16 @@ export default {
}
}
for (const item of this.selectFilterData) {
item.options.sort((x, y) => (y.label === this.$t(`全部`) ? 999999999 : (y.badge ?? 0 - x.badge ?? 0)))
while (!this.$t) {
await new Promise((x) => setTimeout(x, 100))
}
item.options.sort((x, y) => {
if (y.label === this.$t(`全部`)) {
return 999999999
} else {
return (y.badge ?? 0) - (x.badge ?? 0)
}
})
}
},
onSelectionChange(data) {
@ -396,7 +429,10 @@ export default {
customSearchControls: { type: Array, default: [] },
columns: { type: Object },
operations: { type: Array, default: [`view`, `add`, `edit`, `del`] },
rowButtons: { type: Array, default: [] },
rightButtons: { type: Array, default: [] },
dialogFullScreen: { type: Boolean },
tabs: { type: Array },
},
watch: {},
}

View File

@ -1,8 +1,8 @@
<template>
<slot :open="open">
<el-button @click="open" plain type="primary">{{ $t('导入') }}</el-button>
<el-button @click="open" plain type="primary">{{ $t(`导入`) }}</el-button>
</slot>
<el-dialog v-model="dialog" :close-on-click-modal="false" :title="$t('导入')" :width="550" append-to-body destroy-on-close>
<el-dialog v-model="dialog" :close-on-click-modal="false" :title="$t(`导入`)" :width="550" append-to-body destroy-on-close>
<el-progress v-if="loading" :percentage="percentage" :stroke-width="20" :text-inside="true" style="margin-bottom: 1rem" />
<div v-loading="loading">
<el-upload
@ -23,15 +23,15 @@
<el-icon-upload-filled />
</el-icon>
<div class="el-upload__text">
{{ $t('将文件拖到此处或 ') }}<em>{{ $t('点击选择文件上传') }}</em>
{{ $t(`将文件拖到此处或 `) }}<em>{{ $t(`点击选择文件上传`) }}</em>
</div>
</slot>
<template #tip>
<div class="el-upload__tip">
<template v-if="tip">{{ tip }}</template>
<template v-else>{{ $t('请上传小于或等于 {{ maxSize }}M {{ accept }} 格式文件') }}</template>
<template v-else>{{ $t(`请上传小于或等于 {maxSize}M {accept} 格式文件`) }}</template>
<p v-if="templateUrl" style="margin-top: 7px">
<el-link :href="templateUrl" :underline="false" target="_blank" type="primary">{{ $t('下载导入模板') }}</el-link>
<el-link :href="templateUrl" :underline="false" target="_blank" type="primary">{{ $t(`下载导入模板`) }}</el-link>
</p>
</div>
</template>
@ -45,7 +45,7 @@
<script>
export default {
emits: ['success'],
emits: [`success`],
props: {
apiObj: {
type: Object,
@ -55,10 +55,10 @@ export default {
type: Object,
default: () => {},
},
accept: { type: String, default: '.xls, .xlsx' },
accept: { type: String, default: `.xls, .xlsx` },
maxSize: { type: Number, default: 10 },
tip: { type: String, default: '' },
templateUrl: { type: String, default: '' },
tip: { type: String, default: `` },
templateUrl: { type: String, default: `` },
},
data() {
return {
@ -80,7 +80,7 @@ export default {
before(file) {
const maxSize = file.size / 1024 / 1024 < this.maxSize
if (!maxSize) {
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`)
this.$message.warning(this.$t(`上传文件大小不能超过 {maxSize}MB`, { maxSize: this.maxSize }))
return false
}
this.loading = true
@ -93,13 +93,13 @@ export default {
this.$refs.uploader.clearFiles()
this.loading = false
this.percentage = 0
this.$emit('success', res, this.close)
this.$emit(`success`, res, this.close)
},
error(err) {
this.loading = false
this.percentage = 0
this.$notify.error({
title: '上传文件未成功',
title: `上传文件未成功`,
message: err,
})
},

View File

@ -3,7 +3,7 @@
<div v-loading="menuLoading" class="sc-file-select__side">
<div class="sc-file-select__side-menu">
<el-tree
:current-node-key="menu.length > 0 ? menu[0][treeProps.key] : ''"
:current-node-key="menu.length > 0 ? menu[0][treeProps.key] : ``"
:data="menu"
:node-key="treeProps.key"
:props="treeProps"
@ -37,16 +37,16 @@
action=""
class="sc-file-select__upload"
multiple>
<el-button icon="el-icon-upload" type="primary">{{ $t('本地上传') }}</el-button>
<el-button icon="el-icon-upload" type="primary">{{ $t(`本地上传`) }}</el-button>
</el-upload>
<span class="tips"
><el-icon><el-icon-warning />{{ $t('大小不超过{{ maxSize }}MB') }} </el-icon></span
><el-icon><el-icon-warning />{{ $t(`大小不超过 {maxSize} MB`) }} </el-icon></span
>
</div>
<div class="keyword">
<el-input
v-model="keyword"
:placeholder="$t('文件名搜索')"
:placeholder="$t(`文件名搜索`)"
@clear="search"
@keyup.enter="search"
clearable
@ -55,7 +55,7 @@
</div>
<div class="sc-file-select__list">
<el-scrollbar ref="scrollbar">
<el-empty v-if="fileList.length === 0 && data.length === 0" :description="$t('无数据')" :image-size="80" />
<el-empty v-if="fileList.length === 0 && data.length === 0" :description="$t(`无数据`)" :image-size="80" />
<div v-for="(file, index) in fileList" :key="index" class="sc-file-select__item">
<div class="sc-file-select__item__file">
<div class="sc-file-select__item__upload">
@ -108,7 +108,7 @@
</div>
<div class="sc-file-select__do">
<slot name="do" />
<el-button :disabled="value.length <= 0" @click="submit" type="primary">{{ $t('确定') }}</el-button>
<el-button :disabled="value.length <= 0" @click="submit" type="primary">{{ $t(`确定`) }}</el-button>
</div>
</div>
</div>
@ -134,10 +134,10 @@ export default {
currentPage: 1,
data: [],
menu: [],
menuId: '',
value: this.multiple ? [] : '',
menuId: ``,
value: this.multiple ? [] : ``,
fileList: [],
accept: this.onlyImage ? 'image/gif, image/jpeg, image/png' : '',
accept: this.onlyImage ? `image/gif, image/jpeg, image/png` : ``,
listLoading: false,
menuLoading: false,
treeProps: config.menuProps,
@ -147,8 +147,8 @@ export default {
},
watch: {
multiple() {
this.value = this.multiple ? [] : ''
this.$emit('update:modelValue', JSON.parse(JSON.stringify(this.value)))
this.value = this.multiple ? [] : ``
this.$emit(`update:modelValue`, JSON.parse(JSON.stringify(this.value)))
},
},
mounted() {
@ -173,7 +173,7 @@ export default {
[config.request.keyword]: this.keyword,
}
if (this.onlyImage) {
reqData.type = 'image'
reqData.type = `image`
}
const res = await config.listApiObj.get(reqData)
const parseData = config.listParseData(res)
@ -210,7 +210,7 @@ export default {
}
} else {
if (this.value.includes(itemUrl)) {
this.value = ''
this.value = ``
} else {
this.value = itemUrl
}
@ -218,8 +218,8 @@ export default {
},
submit() {
const value = JSON.parse(JSON.stringify(this.value))
this.$emit('update:modelValue', value)
this.$emit('submit', value)
this.$emit(`update:modelValue`, value)
this.$emit(`submit`, value)
},
//上传处理
uploadChange(file, fileList) {
@ -229,14 +229,14 @@ export default {
uploadBefore(file) {
const maxSize = file.size / 1024 / 1024 < this.maxSize
if (!maxSize) {
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`)
this.$message.warning(this.$t(`上传文件大小不能超过 {maxSize}MB`, { maxSize: this.maxSize }))
return false
}
},
uploadRequest(param) {
const apiObj = config.apiObj
const data = new FormData()
data.append('file', param.file)
data.append(`file`, param.file)
data.append([config.request.menuKey], this.menuId)
apiObj
.post(data, {
@ -271,18 +271,18 @@ export default {
},
uploadError(err) {
this.$notify.error({
title: '上传文件错误',
title: this.$t(`上传文件错误`),
message: err,
})
},
//内置函数
_isImg(fileUrl) {
const imgExt = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
const fileExt = fileUrl.substring(fileUrl.lastIndexOf('.'))
const imgExt = [`.jpg`, `.jpeg`, `.png`, `.gif`, `.bmp`]
const fileExt = fileUrl.substring(fileUrl.lastIndexOf(`.`))
return imgExt.indexOf(fileExt) !== -1
},
_getExt(fileUrl) {
return fileUrl.substring(fileUrl.lastIndexOf('.') + 1)
return fileUrl.substring(fileUrl.lastIndexOf(`.`) + 1)
},
},
}

View File

@ -26,7 +26,7 @@
<el-scrollbar>
<ul @click="selectIcon">
<el-empty v-if="item.icons.length === 0" :description="$t('未查询到相关图标')" :image-size="100" />
<li v-for="icon in item.icons" :key="icon">
<li v-copy="icon" v-for="icon in item.icons" :key="icon">
<span :data-icon="icon"></span>
<el-icon>
<component :is="icon" />

View File

@ -19,7 +19,7 @@
:on-success="success"
:show-file-list="showFileList">
<slot>
<el-button :disabled="disabled" type="primary">点击上传</el-button>
<el-button :disabled="disabled" type="primary">{{ $t(`点击上传`) }}</el-button>
</slot>
<template #tip>
<div v-if="tip" class="el-upload__tip">{{ tip }}</div>
@ -34,9 +34,9 @@ import config from '@/config/upload'
export default {
props: {
modelValue: { type: [String, Array], default: '' },
tip: { type: String, default: '' },
action: { type: String, default: '' },
modelValue: { type: [String, Array], default: `` },
tip: { type: String, default: `` },
action: { type: String, default: `` },
apiObj: {
type: Object,
default: () => {},
@ -46,7 +46,7 @@ export default {
type: Object,
default: () => {},
},
accept: { type: String, default: '' },
accept: { type: String, default: `` },
maxSize: { type: Number, default: config.maxSizeFile },
limit: { type: Number, default: 0 },
autoUpload: { type: Boolean, default: true },
@ -63,7 +63,7 @@ export default {
},
data() {
return {
value: '',
value: ``,
defaultFileList: [],
}
},
@ -83,7 +83,7 @@ export default {
},
defaultFileList: {
handler(val) {
this.$emit('update:modelValue', Array.isArray(this.modelValue) ? this.formatArr(val) : this.toStr(val))
this.$emit(`update:modelValue`, Array.isArray(this.modelValue) ? this.formatArr(val) : this.toStr(val))
this.value = this.toStr(val)
},
deep: true,
@ -97,10 +97,10 @@ export default {
//默认值转换为数组
toArr(str) {
const _arr = []
const arr = str.split(',')
const arr = str.split(`,`)
arr.forEach((item) => {
if (item) {
const urlArr = item.split('/')
const urlArr = item.split(`/`)
const fileName = urlArr[urlArr.length - 1]
_arr.push({
name: fileName,
@ -112,7 +112,7 @@ export default {
},
//数组转换为原始值
toStr(arr) {
return arr.map((v) => v.url).join(',')
return arr.map((v) => v.url).join(`,`)
},
//格式化数组值
formatArr(arr) {
@ -130,7 +130,7 @@ export default {
before(file) {
const maxSize = file.size / 1024 / 1024 < this.maxSize
if (!maxSize) {
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`)
this.$message.warning(this.$t(`上传文件大小不能超过 {maxSize}MB`, { maxSize: this.maxSize }))
return false
}
},
@ -145,13 +145,13 @@ export default {
},
error(err) {
this.$notify.error({
title: '上传文件未成功',
title: this.$t(`上传文件未成功`),
message: err,
})
},
beforeRemove(uploadFile) {
return this.$confirm(`是否移除 ${uploadFile.name} ?`, '提示', {
type: 'warning',
return this.$confirm(this.$t(`是否移除 {name} ?`, { name: uploadFile.name }), `提示`, {
type: `warning`,
})
.then(() => {
return true
@ -161,7 +161,7 @@ export default {
})
},
handleExceed() {
this.$message.warning(`当前设置最多上传 ${this.limit} 个文件,请移除后上传!`)
this.$message.warning(this.$t(`当前设置最多上传 {limit} 个文件,请移除后上传!`, { limit: this.limit }))
},
handlePreview(uploadFile) {
window.open(uploadFile.url)
@ -188,7 +188,7 @@ export default {
if (response.code === config.successCode) {
param.onSuccess(res)
} else {
param.onError(response.msg || '未知错误')
param.onError(response.msg || this.$t(`未知错误`))
}
})
.catch((err) => {

View File

@ -199,7 +199,7 @@ export default {
}
const maxSize = file.size / 1024 / 1024 < this.maxSize
if (!maxSize) {
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`)
this.$message.warning(this.$t(`上传文件大小不能超过 {maxSize}MB`, { maxSize: this.maxSize }))
this.clearFiles()
return false
}

View File

@ -188,7 +188,7 @@ export default {
}
const maxSize = file.size / 1024 / 1024 < this.maxSize
if (!maxSize) {
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`)
this.$message.warning(this.$t(`上传文件大小不能超过 {maxSize}MB`, { maxSize: this.maxSize }))
return false
}
},

File diff suppressed because one or more lines are too long

View File

@ -628,4 +628,5 @@ export default {
'编辑{summary}: {id}': '编辑{summary}: {id}',
'{field} 不能为空': '{field} 不能为空',
'{field} 不正确': '{field} 不正确',
'上传文件大小不能超过 {maxSize}MB': '上传文件大小不能超过 {maxSize}MB',
}

View File

@ -7,33 +7,29 @@
extra: [`createdTime`],
width: 170,
show: [`list`, `view`],
searchable: true,
searchable: `eq`,
},
name: {
label: $t(`名`),
label: $t(`名`),
width: 150,
show: [`list`, `view`, `add`, `edit`],
rule: {
required: true,
},
show: [`list`, `view`, `add`, `edit`],
searchable: true,
operator: `contains`,
searchable: `eq`,
},
gender: {
label: $t(`性别`),
is: `na-col-indicator`,
enum: `genders`,
width: 200,
width: 100,
align: `center`,
rule: {
required: true,
},
countBy: true,
show: [`list`, `view`, `add`, `edit`],
},
sort: {
label: $t(`排序`),
align: `right`,
thousands: true,
width: 100,
show: [`list`, `view`, `add`, `edit`],
rule: {
@ -47,8 +43,7 @@
summary: {
label: $t(`备注`),
show: [`list`, `view`, `add`, `edit`],
searchable: true,
operator: `contains`,
searchable: `contains`,
},
enabled: {
label: $t(`启用`),
@ -67,31 +62,47 @@
show: [`view`],
},
}"
:operations="operations"
:summary="$t(`页面模板`)"
:operations="[`add`, `del`, `edit`]"
:search-controls="[
{
type: `input`,
field: [`root`, `keywords`],
placeholder: $t(`消息编号 / 消息主题 / 消息内容`),
style: `width:25rem`,
},
]"
:select-filters="[
{
title: $t(`是否启用`),
key: `Enabled`,
enumName: `Enabled`,
isBoolean: [
{ label: $t(`启用`), value: true },
{ label: $t(`禁用`), value: false },
],
},
{
title: $t(`性别`),
key: `Gender`,
enumName: `genders`,
},
]"
:summary="$t(`代码模板`)"
entity-name="sys_codetemplate" />
</template>
<script>
export default {
created() {
if (this.$GLOBAL.hasApiPermission(`api/sys/code.template/get`)) {
this.operations.push(`view`)
}
if (this.$GLOBAL.hasApiPermission(`api/sys/code.template/edit`)) {
this.operations.push(`edit`)
}
if (this.$GLOBAL.hasApiPermission(`api/sys/code.template/create`)) {
this.operations.push(`add`)
}
if (this.$GLOBAL.hasApiPermission(`api/sys/code.template/delete`)) {
this.operations.push(`del`)
}
},
data() {
return {
operations: [],
}
},
components: {},
computed: {},
created() {},
data() {},
inject: [`reload`],
methods: {},
mounted() {},
props: [`keywords`],
watch: {},
}
</script>
<style scoped />

View File

@ -0,0 +1,121 @@
<template>
<na-table-page
:buttons="[
{
title: $t(`解冻`),
condition: (row) => {
return row.status === `frozen` && this.$GLOBAL.hasApiPermission(`api/sys/wallet.frozen/set.status.to.thawed`)
},
click: async (row) => {
try {
await this.$confirm(`确定解冻该记录?`, '提示', {
type: 'warning',
confirmButtonText: '是',
cancelButtonText: '否',
})
} catch {
return
}
await this.$API.sys_walletfrozen.setStatusToThawed.post({ id: row.id })
return true
},
},
]"
:columns="{
id: {
label: $t(`日志编号`),
is: `na-col-id`,
extra: [`createdTime`],
width: 170,
show: [`list`, `view`],
searchable: true,
},
ownerId: {
rule: {
type: `number`,
required: true,
},
headerAlign: `center`,
is: `na-col-user`,
clickOpenDialog: this.$GLOBAL.hasApiPermission(`api/sys/user/get`),
nestProp: `owner.userName`,
nestProp2: `ownerId`,
width: 170,
label: $t(`归属用户`),
show: [`list`, `view`, `add`],
detail: {
is: `sc-select`,
props: {
queryApi: $API.sys_user.query,
clearable: true,
filterable: true,
config: {
props: {
label: `userName`,
value: `id`,
},
},
},
},
},
amount: {
label: $t(`冻结金额`),
align: `right`,
thousands: true,
width: 100,
show: [`list`, `view`, `add`],
rule: {
required: true,
validator: (rule, value, callback) => {
if (/^[1-9]\d*$/.test(value)) callback()
else callback(new Error())
},
},
},
status: {
is: `na-col-indicator`,
enum: `walletFrozenStatues`,
label: $t(`状态`),
countBy: true,
align: `center`,
width: 100,
show: [`list`, `view`],
},
reason: {
rule: {
required: true,
},
countBy: true,
is: `na-col-indicator`,
enum: `walletFrozenReasons`,
label: $t(`冻结原因`),
align: `center`,
width: 100,
show: [`list`, `view`, `add`],
},
modifiedTime: {
label: $t(`更新时间`),
show: [`list`, `view`],
width: 170,
},
summary: {
label: $t(`备注`),
show: [`list`, `view`, `add`],
searchable: true,
operator: `contains`,
},
createdTime: {
label: $t(`创建时间`),
show: [`view`],
},
version: {
label: $t(`数据版本`),
show: [`view`],
},
}"
:operations="[`view`, `add`]"
:summary="$t(`冻结记录`)"
entity-name="sys_walletfrozen" />
</template>
<style scoped />

View File

@ -1,323 +1,35 @@
<template>
<el-container>
<el-header v-loading="statistics.total === '...'" class="el-header-statistics">
<el-row :gutter="15">
<el-col :lg="24">
<el-card shadow="never">
<sc-statistic :title="$t('总数')" :value="statistics.total" group-separator />
</el-card>
</el-col>
</el-row>
</el-header>
<el-header class="el-header-select-filter">
<sc-select-filter
:data="[
{
title: $t('交易方向'),
key: 'tradeDirection',
options: [
{ label: '全部', value: '' },
...Object.entries(this.$GLOBAL.enums.tradeDirections).map((x) => {
return {
value: x[0],
label: x[1][1],
badge: this.statistics.tradeDirection?.find((y) => y.key.tradeDirection.toLowerCase() === x[0].toLowerCase())
?.value,
}
}),
],
},
{
title: $t('交易类型'),
key: 'tradeType',
options: [
{ label: '全部', value: '' },
...Object.entries(this.$GLOBAL.enums.tradeTypes).map((x) => {
return {
value: x[0],
label: x[1][1],
badge: this.statistics.tradeType?.find((y) => y.key.tradeType.toLowerCase() === x[0].toLowerCase())?.value,
}
}),
],
},
]"
:label-width="15"
@on-change="filterChange"
ref="selectFilter" />
</el-header>
<el-header>
<div class="left-panel">
<na-search
:controls="[
{
type: 'select-input',
field: [
'dy',
[
{ label: $t('交易编号'), key: 'id' },
{ label: $t('用户名'), key: 'owner.userName' },
{ label: $t('用户编号'), key: 'ownerId' },
],
],
placeholder: $t('匹配内容'),
style: 'width:25rem',
selectStyle: 'width:8rem',
},
]"
:vue="this"
@reset="onReset"
@search="onSearch"
dateFormat="YYYY-MM-DD HH:mm:ss"
dateType="datetimerange"
dateValueFormat="YYYY-MM-DD HH:mm:ss"
ref="search" />
</div>
<div class="right-panel"></div>
<el-header style="border: none; padding-bottom: 0">
<el-tabs v-model="tabId" class="w100p">
<el-tab-pane :label="$t(`交易记录`)" name="list"></el-tab-pane>
<el-tab-pane :label="$t(`冻结记录`)" name="frozen"></el-tab-pane>
</el-tabs>
</el-header>
<el-main class="nopadding">
<sc-table
:context-extra="{ id: ['createdTime'], ownerId: ['owner.userName'] }"
:context-menus="[
'id',
'ownerId',
'createdTime',
'tradeType',
'amount',
'balanceBefore',
'summary',
'owner.userName',
'tradeDirection',
]"
:context-opers="['view']"
:default-sort="{ prop: 'id', order: 'descending' }"
:export-api="$API.sys_wallettrade.export"
:params="query"
:query-api="$API.sys_wallettrade.pagedQuery"
:vue="this"
@data-change="getStatistics"
@selection-change="
(items) => {
selection = items
}
"
ref="table"
remote-filter
remote-sort
row-key="id"
stripe>
<na-col-id :label="$t('交易编号')" prop="id" sortable="custom" width="170" />
<na-col-user
:clickOpenDialog="$GLOBAL.hasApiPermission('api/sys/user/get')"
:label="$t('归属用户')"
header-align="center"
nestProp="owner.userName"
nestProp2="ownerId"
prop="ownerId"
sortable="custom"
width="170" />
<na-col-indicator
:label="$t('交易方向')"
:options="
Object.entries(this.$GLOBAL.enums.tradeDirections).map((x) => {
return { value: x[0], text: `${x[1][1]}`, type: x[1][2], pulse: x[1][3] === 'true' }
})
"
align="center"
prop="tradeDirection"
sortable="custom" />
<na-col-indicator
:label="$t('交易类型')"
:options="
Object.entries(this.$GLOBAL.enums.tradeTypes).map((x) => {
return { value: x[0], text: `${x[1][1]}`, type: x[1][2], pulse: x[1][3] === 'true' }
})
"
align="center"
prop="tradeType"
sortable="custom" />
<el-table-column
:formatter="(row) => $TOOL.groupSeparator(row.balanceBefore)"
:label="$t('交易前余额')"
align="right"
prop="balanceBefore"
sortable="custom" />
<el-table-column
:formatter="(row) => $TOOL.groupSeparator(row.amount)"
:label="$t('发生金额')"
align="right"
prop="amount"
sortable="custom" />
<el-table-column :label="$t('交易后余额')" align="right">
<template #default="{ row }">
{{ $TOOL.groupSeparator(row.balanceBefore + row.amount) }}
</template>
</el-table-column>
<el-table-column :label="$t('备注')" min-width="100" prop="summary" show-overflow-tooltip sortable="custom" />
<na-col-operation :buttons="[naColOperation.buttons[0]]" :vue="this" width="50" />
</sc-table>
<component :is="tabId" />
</el-main>
</el-container>
<save-dialog
v-if="dialog.save"
@closed="dialog.save = null"
@mounted="$refs.saveDialog.open(dialog.save)"
@success="(data, mode) => $refs.table.refresh()"
ref="saveDialog" />
</template>
<script>
import { defineAsyncComponent } from 'vue'
import table from '@/config/table'
import naColOperation from '@/config/na-col-operation'
const naColUser = defineAsyncComponent(() => import('@/components/na-col-user'))
const saveDialog = defineAsyncComponent(() => import('./save'))
const list = defineAsyncComponent(() => import('./list'))
const frozen = defineAsyncComponent(() => import('./frozen'))
export default {
components: {
naColUser,
saveDialog,
},
computed: {
naColOperation() {
return naColOperation
},
table() {
return table
},
},
async created() {
if (this.ownerId) {
this.query.dynamicFilter.filters.push({ field: 'ownerId', operator: 'eq', value: this.ownerId })
}
},
components: { list, frozen },
computed: {},
created() {},
data() {
return {
statistics: {
total: '...',
},
dialog: {},
loading: false,
query: {
dynamicFilter: {
filters: [],
},
filter: {},
keywords: this.keywords,
},
selection: [],
tabId: 'list',
}
},
inject: ['reload'],
methods: {
filterChange(data) {
Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
})
this.$refs.search.search()
},
async getStatistics() {
this.statistics.total = this.$refs.table?.total
const res = await Promise.all([
this.$API.sys_wallettrade.countBy.post({
dynamicFilter: {
filters: this.query.dynamicFilter.filters,
},
requiredFields: ['TradeDirection'],
}),
this.$API.sys_wallettrade.countBy.post({
dynamicFilter: {
filters: this.query.dynamicFilter.filters,
},
requiredFields: ['TradeType'],
}),
])
this.statistics.tradeDirection = res[0].data
this.statistics.tradeType = res[1].data
},
//重置
onReset() {
Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))
if (this.ownerId) {
this.$refs.search.selectInputKey = 'ownerId'
}
},
//搜索
async onSearch(form) {
if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({
field: 'createdTime',
operator: 'dateRange',
value: form.dy.createdTime.map((x) => x.replace(/ 00:00:00$/, '')),
})
}
if (typeof form.dy['owner.userName'] === 'string' && form.dy['owner.userName'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'owner.userName',
operator: 'eq',
value: form.dy['owner.userName'],
})
}
if (typeof form.dy['ownerId'] === 'string' && form.dy['ownerId'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'ownerId',
operator: 'eq',
value: form.dy['ownerId'],
})
}
if (typeof form.dy['id'] === 'string' && form.dy['id'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'id',
operator: 'eq',
value: form.dy['id'],
})
}
if (typeof form.dy['tradeType'] === 'string' && form.dy['tradeType'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'tradeType',
operator: 'eq',
value: form.dy['tradeType'],
})
}
if (typeof form.dy['tradeDirection'] === 'string' && form.dy['tradeDirection'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'tradeDirection',
operator: 'eq',
value: form.dy['tradeDirection'],
})
}
await this.$refs.table.upData()
},
},
async mounted() {
if (this.ownerId) {
this.$refs.search.selectInputKey = 'ownerId'
this.$refs.search.form.dy.ownerId = this.ownerId
this.$refs.search.keeps.push({
field: 'ownerId',
value: this.ownerId,
type: 'dy',
})
}
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
this.onReset()
},
props: ['keywords', 'ownerId'],
methods: {},
mounted() {},
watch: {},
}
</script>
<style scoped />
<style scoped></style>

View File

@ -0,0 +1,323 @@
<template>
<el-container>
<el-header v-loading="statistics.total === '...'" class="el-header-statistics">
<el-row :gutter="15">
<el-col :lg="24">
<el-card shadow="never">
<sc-statistic :title="$t('总数')" :value="statistics.total" group-separator />
</el-card>
</el-col>
</el-row>
</el-header>
<el-header class="el-header-select-filter">
<sc-select-filter
:data="[
{
title: $t('交易方向'),
key: 'tradeDirection',
options: [
{ label: '全部', value: '' },
...Object.entries(this.$GLOBAL.enums.tradeDirections).map((x) => {
return {
value: x[0],
label: x[1][1],
badge: this.statistics.tradeDirection?.find((y) => y.key.tradeDirection.toLowerCase() === x[0].toLowerCase())
?.value,
}
}),
],
},
{
title: $t('交易类型'),
key: 'tradeType',
options: [
{ label: '全部', value: '' },
...Object.entries(this.$GLOBAL.enums.tradeTypes).map((x) => {
return {
value: x[0],
label: x[1][1],
badge: this.statistics.tradeType?.find((y) => y.key.tradeType.toLowerCase() === x[0].toLowerCase())?.value,
}
}),
],
},
]"
:label-width="15"
@on-change="filterChange"
ref="selectFilter" />
</el-header>
<el-header>
<div class="left-panel">
<na-search
:controls="[
{
type: 'select-input',
field: [
'dy',
[
{ label: $t('交易编号'), key: 'id' },
{ label: $t('用户名'), key: 'owner.userName' },
{ label: $t('用户编号'), key: 'ownerId' },
],
],
placeholder: $t('匹配内容'),
style: 'width:25rem',
selectStyle: 'width:8rem',
},
]"
:vue="this"
@reset="onReset"
@search="onSearch"
dateFormat="YYYY-MM-DD HH:mm:ss"
dateType="datetimerange"
dateValueFormat="YYYY-MM-DD HH:mm:ss"
ref="search" />
</div>
<div class="right-panel"></div>
</el-header>
<el-main class="nopadding">
<sc-table
:context-extra="{ id: ['createdTime'], ownerId: ['owner.userName'] }"
:context-menus="[
'id',
'ownerId',
'createdTime',
'tradeType',
'amount',
'balanceBefore',
'summary',
'owner.userName',
'tradeDirection',
]"
:context-opers="['view']"
:default-sort="{ prop: 'id', order: 'descending' }"
:export-api="$API.sys_wallettrade.export"
:params="query"
:query-api="$API.sys_wallettrade.pagedQuery"
:vue="this"
@data-change="getStatistics"
@selection-change="
(items) => {
selection = items
}
"
ref="table"
remote-filter
remote-sort
row-key="id"
stripe>
<na-col-id :label="$t('交易编号')" prop="id" sortable="custom" width="170" />
<na-col-user
:clickOpenDialog="$GLOBAL.hasApiPermission('api/sys/user/get')"
:label="$t('归属用户')"
header-align="center"
nestProp="owner.userName"
nestProp2="ownerId"
prop="ownerId"
sortable="custom"
width="170" />
<na-col-indicator
:label="$t('交易方向')"
:options="
Object.entries(this.$GLOBAL.enums.tradeDirections).map((x) => {
return { value: x[0], text: `${x[1][1]}`, type: x[1][2], pulse: x[1][3] === 'true' }
})
"
align="center"
prop="tradeDirection"
sortable="custom" />
<na-col-indicator
:label="$t('交易类型')"
:options="
Object.entries(this.$GLOBAL.enums.tradeTypes).map((x) => {
return { value: x[0], text: `${x[1][1]}`, type: x[1][2], pulse: x[1][3] === 'true' }
})
"
align="center"
prop="tradeType"
sortable="custom" />
<el-table-column
:formatter="(row) => $TOOL.groupSeparator(row.balanceBefore)"
:label="$t('交易前余额')"
align="right"
prop="balanceBefore"
sortable="custom" />
<el-table-column
:formatter="(row) => $TOOL.groupSeparator(row.amount)"
:label="$t('发生金额')"
align="right"
prop="amount"
sortable="custom" />
<el-table-column :label="$t('交易后余额')" align="right">
<template #default="{ row }">
{{ $TOOL.groupSeparator(row.balanceBefore + row.amount) }}
</template>
</el-table-column>
<el-table-column :label="$t('备注')" min-width="100" prop="summary" show-overflow-tooltip sortable="custom" />
<na-col-operation :buttons="[naColOperation.buttons[0]]" :vue="this" width="50" />
</sc-table>
</el-main>
</el-container>
<save-dialog
v-if="dialog.save"
@closed="dialog.save = null"
@mounted="$refs.saveDialog.open(dialog.save)"
@success="(data, mode) => $refs.table.refresh()"
ref="saveDialog" />
</template>
<script>
import { defineAsyncComponent } from 'vue'
import table from '@/config/table'
import naColOperation from '@/config/na-col-operation'
const naColUser = defineAsyncComponent(() => import('@/components/na-col-user'))
const saveDialog = defineAsyncComponent(() => import('./save'))
export default {
components: {
naColUser,
saveDialog,
},
computed: {
naColOperation() {
return naColOperation
},
table() {
return table
},
},
async created() {
if (this.ownerId) {
this.query.dynamicFilter.filters.push({ field: 'ownerId', operator: 'eq', value: this.ownerId })
}
},
data() {
return {
statistics: {
total: '...',
},
dialog: {},
loading: false,
query: {
dynamicFilter: {
filters: [],
},
filter: {},
keywords: this.keywords,
},
selection: [],
}
},
inject: ['reload'],
methods: {
filterChange(data) {
Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
})
this.$refs.search.search()
},
async getStatistics() {
this.statistics.total = this.$refs.table?.total
const res = await Promise.all([
this.$API.sys_wallettrade.countBy.post({
dynamicFilter: {
filters: this.query.dynamicFilter.filters,
},
requiredFields: ['TradeDirection'],
}),
this.$API.sys_wallettrade.countBy.post({
dynamicFilter: {
filters: this.query.dynamicFilter.filters,
},
requiredFields: ['TradeType'],
}),
])
this.statistics.tradeDirection = res[0].data
this.statistics.tradeType = res[1].data
},
//重置
onReset() {
Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))
if (this.ownerId) {
this.$refs.search.selectInputKey = 'ownerId'
}
},
//搜索
async onSearch(form) {
if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({
field: 'createdTime',
operator: 'dateRange',
value: form.dy.createdTime.map((x) => x.replace(/ 00:00:00$/, '')),
})
}
if (typeof form.dy['owner.userName'] === 'string' && form.dy['owner.userName'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'owner.userName',
operator: 'eq',
value: form.dy['owner.userName'],
})
}
if (typeof form.dy['ownerId'] === 'string' && form.dy['ownerId'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'ownerId',
operator: 'eq',
value: form.dy['ownerId'],
})
}
if (typeof form.dy['id'] === 'string' && form.dy['id'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'id',
operator: 'eq',
value: form.dy['id'],
})
}
if (typeof form.dy['tradeType'] === 'string' && form.dy['tradeType'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'tradeType',
operator: 'eq',
value: form.dy['tradeType'],
})
}
if (typeof form.dy['tradeDirection'] === 'string' && form.dy['tradeDirection'].trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'tradeDirection',
operator: 'eq',
value: form.dy['tradeDirection'],
})
}
await this.$refs.table.upData()
},
},
async mounted() {
if (this.ownerId) {
this.$refs.search.selectInputKey = 'ownerId'
this.$refs.search.form.dy.ownerId = this.ownerId
this.$refs.search.keeps.push({
field: 'ownerId',
value: this.ownerId,
type: 'dy',
})
}
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
this.onReset()
},
props: ['keywords', 'ownerId'],
watch: {},
}
</script>
<style scoped />

View File

@ -65,7 +65,6 @@
'ownerId',
'owner.userName',
'createdTime',
'totalBalance',
'availableBalance',
'frozenBalance',
'totalIncome',
@ -98,12 +97,6 @@
prop="ownerId"
sortable="custom"
width="170" />
<el-table-column
:formatter="(row) => $TOOL.groupSeparator(row.totalBalance)"
:label="$t('总余额')"
align="right"
prop="totalBalance"
sortable="custom" />
<el-table-column
:formatter="(row) => $TOOL.groupSeparator(row.availableBalance)"
:label="$t('可用余额')"

View File

@ -7,9 +7,6 @@
<el-form-item :label="$t('唯一编码')" prop="id">
<el-input v-model="form.id" clearable />
</el-form-item>
<el-form-item :label="$t('总余额')" prop="totalBalance">
<el-input v-model="form.totalBalance" clearable />
</el-form-item>
<el-form-item :label="$t('可用余额')" prop="availableBalance">
<el-input v-model="form.availableBalance" clearable />
</el-form-item>