mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-07-04 09:38:15 +08:00
feat: ✨ 财务管理
This commit is contained in:
@ -17,7 +17,6 @@
|
||||
以什么结束
|
||||
作业名称
|
||||
作业状态
|
||||
密
|
||||
信息
|
||||
倒序排序
|
||||
全部数据
|
||||
@ -45,6 +44,7 @@
|
||||
字典内容导出
|
||||
宕机
|
||||
客户端IP
|
||||
密
|
||||
小于
|
||||
小于等于
|
||||
小学
|
||||
@ -71,6 +71,8 @@
|
||||
接口路径
|
||||
插入种子数据
|
||||
操作系统
|
||||
支出
|
||||
收入
|
||||
数据范围
|
||||
文档内容
|
||||
文档内容导出
|
||||
@ -99,6 +101,7 @@
|
||||
用户代理
|
||||
用户名
|
||||
用户导出
|
||||
用户钱包导出
|
||||
电子邮箱
|
||||
男
|
||||
登录
|
||||
@ -111,11 +114,14 @@
|
||||
站内信导出
|
||||
等于
|
||||
等待发送
|
||||
管理员充值
|
||||
管理员扣费
|
||||
管理模块
|
||||
系统模块
|
||||
绑定手机号码
|
||||
结果非预期
|
||||
群众
|
||||
自助充值
|
||||
自定义
|
||||
范围
|
||||
菜单
|
||||
@ -128,16 +134,17 @@
|
||||
请求方式
|
||||
请求日志导出
|
||||
调试
|
||||
追踪
|
||||
追踪标识
|
||||
身份证
|
||||
运行
|
||||
追踪
|
||||
追踪标识
|
||||
通知
|
||||
邮箱号
|
||||
部门名称
|
||||
部门导出
|
||||
配置导出
|
||||
重设密码
|
||||
钱包交易导出
|
||||
链接
|
||||
错误
|
||||
随机排序
|
||||
|
@ -5,6 +5,8 @@ XML注释文件不存在
|
||||
中文姓名
|
||||
事务已回滚
|
||||
事务已提交
|
||||
交易失败
|
||||
交易金额不正确
|
||||
人机校验请求不能为空
|
||||
人机验证未通过
|
||||
作业名称不能为空
|
||||
@ -108,6 +110,7 @@ XML注释文件不存在
|
||||
部门可见
|
||||
部门名称不能为空
|
||||
配置文件初始化完毕
|
||||
钱包余额不足
|
||||
键值不能为空
|
||||
键名称不能为空
|
||||
随机延时结束时间不正确
|
||||
|
@ -26,7 +26,7 @@
|
||||
"Id": 373837957840901,
|
||||
"Name": "sys/user",
|
||||
"ParentId": 373837917724677,
|
||||
"Path": "/sys/user",
|
||||
"Path": "/power/user",
|
||||
"Sort": 100,
|
||||
"Title": "用户管理",
|
||||
"Type": 1
|
||||
@ -37,7 +37,7 @@
|
||||
"Id": 373838018527237,
|
||||
"Name": "sys/role",
|
||||
"ParentId": 373837917724677,
|
||||
"Path": "/sys/role",
|
||||
"Path": "/power/role",
|
||||
"Sort": 99,
|
||||
"Title": "角色管理",
|
||||
"Type": 1
|
||||
@ -48,7 +48,7 @@
|
||||
"Id": 373838045605893,
|
||||
"Name": "sys/dept",
|
||||
"ParentId": 373837917724677,
|
||||
"Path": "/sys/dept",
|
||||
"Path": "/power/dept",
|
||||
"Sort": 98,
|
||||
"Title": "部门管理",
|
||||
"Type": 1
|
||||
@ -59,18 +59,50 @@
|
||||
"Id": 373838070898693,
|
||||
"Name": "sys/menu",
|
||||
"ParentId": 373837917724677,
|
||||
"Path": "/sys/menu",
|
||||
"Path": "/power/menu",
|
||||
"Sort": 97,
|
||||
"Title": "菜单管理",
|
||||
"Type": 1
|
||||
},
|
||||
// ------------------------------ 财务管理 ------------------------------
|
||||
{
|
||||
"Icon": "el-icon-money",
|
||||
"Id": 690906994118665,
|
||||
"Name": "finance",
|
||||
"Path": "/finance",
|
||||
"Sort": 99,
|
||||
"Title": "财务管理",
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Component": "sys/wallet",
|
||||
"Icon": "el-icon-wallet",
|
||||
"Id": 690907673255942,
|
||||
"Name": "sys/wallet",
|
||||
"ParentId": 690906994118665,
|
||||
"Path": "/finance/wallet",
|
||||
"Sort": 100,
|
||||
"Title": "钱包管理",
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Component": "sys/trade",
|
||||
"Icon": "el-icon-calendar",
|
||||
"Id": 690907673255943,
|
||||
"Name": "sys/trade",
|
||||
"ParentId": 690906994118665,
|
||||
"Path": "/finance/trade",
|
||||
"Sort": 99,
|
||||
"Title": "交易流水",
|
||||
"Type": 1
|
||||
},
|
||||
// ------------------------------ 系统管理 ------------------------------
|
||||
{
|
||||
"Icon": "sc-icon-App",
|
||||
"Id": 485278637670422,
|
||||
"Name": "sys",
|
||||
"Path": "/sys",
|
||||
"Sort": 99,
|
||||
"Sort": 98,
|
||||
"Title": "系统管理",
|
||||
"Type": 1
|
||||
},
|
||||
@ -80,7 +112,7 @@
|
||||
"Id": 380415005847557,
|
||||
"Name": "sys/config",
|
||||
"ParentId": 485278637670422,
|
||||
"Path": "/sys/config",
|
||||
"Path": "/system/config",
|
||||
"Sort": 100,
|
||||
"Title": "系统设置",
|
||||
"Type": 1
|
||||
@ -91,7 +123,7 @@
|
||||
"Id": 510067557638158,
|
||||
"Name": "sys/job",
|
||||
"ParentId": 485278637670422,
|
||||
"Path": "/sys/job",
|
||||
"Path": "/system/job",
|
||||
"Sort": 99,
|
||||
"Title": "计划作业",
|
||||
"Type": 1
|
||||
@ -102,7 +134,7 @@
|
||||
"Id": 375315654221829,
|
||||
"Name": "sys/dic",
|
||||
"ParentId": 485278637670422,
|
||||
"Path": "/sys/dic",
|
||||
"Path": "/system/dic",
|
||||
"Sort": 98,
|
||||
"Title": "字典管理",
|
||||
"Type": 1
|
||||
@ -113,7 +145,7 @@
|
||||
"Id": 482779610341392,
|
||||
"Name": "sys/msg",
|
||||
"ParentId": 485278637670422,
|
||||
"Path": "/sys/msg",
|
||||
"Path": "/system/msg",
|
||||
"Sort": 97,
|
||||
"Title": "消息管理",
|
||||
"Type": 1,
|
||||
@ -124,7 +156,7 @@
|
||||
"Id": 397880678895621,
|
||||
"Name": "sys/api",
|
||||
"ParentId": 485278637670422,
|
||||
"Path": "/sys/api",
|
||||
"Path": "/system/api",
|
||||
"Sort": 96,
|
||||
"Title": "接口管理",
|
||||
"Type": 1
|
||||
@ -135,7 +167,7 @@
|
||||
"Id": 374911555702789,
|
||||
"Name": "sys/cache",
|
||||
"ParentId": 485278637670422,
|
||||
"Path": "/sys/cache",
|
||||
"Path": "/system/cache",
|
||||
"Sort": 95,
|
||||
"Title": "缓存管理",
|
||||
"Type": 1
|
||||
@ -146,7 +178,7 @@
|
||||
"Id": 616214756757512,
|
||||
"Name": "archive",
|
||||
"Path": "/archive",
|
||||
"Sort": 98,
|
||||
"Sort": 97,
|
||||
"Title": "档案管理",
|
||||
"Type": 1
|
||||
},
|
||||
@ -167,7 +199,7 @@
|
||||
"Id": 374792687640581,
|
||||
"Name": "log",
|
||||
"Path": "/log",
|
||||
"Sort": 97,
|
||||
"Sort": 96,
|
||||
"Title": "日志管理",
|
||||
"Type": 1
|
||||
},
|
||||
@ -177,7 +209,7 @@
|
||||
"Id": 485285246504976,
|
||||
"Name": "sys/log/operation",
|
||||
"ParentId": 374792687640581,
|
||||
"Path": "/sys/log/operation",
|
||||
"Path": "/log/operation",
|
||||
"Sort": 100,
|
||||
"Title": "操作日志",
|
||||
"Type": 1,
|
||||
@ -188,7 +220,7 @@
|
||||
"Id": 485285246504970,
|
||||
"Name": "sys/log/login",
|
||||
"ParentId": 374792687640581,
|
||||
"Path": "/sys/log/login",
|
||||
"Path": "/log/login",
|
||||
"Sort": 99,
|
||||
"Title": "登录日志",
|
||||
"Type": 1,
|
||||
@ -199,7 +231,7 @@
|
||||
"Id": 373838105399301,
|
||||
"Name": "dev",
|
||||
"Path": "/dev",
|
||||
"Sort": 96,
|
||||
"Sort": 95,
|
||||
"Title": "开发管理",
|
||||
"Type": 1
|
||||
},
|
||||
|
@ -58,5 +58,25 @@
|
||||
{
|
||||
"ApiId": "api/sys/login.log/export",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/user.wallet/paged.query",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/user.wallet/get",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/wallet.trade/paged.query",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/wallet.trade/count.by",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/wallet.trade/get",
|
||||
"RoleId": 371729946431493,
|
||||
}
|
||||
]
|
@ -6,5 +6,25 @@
|
||||
{
|
||||
"MenuId": 374967228141573,
|
||||
"RoleId": 371729946431493
|
||||
},
|
||||
{
|
||||
"MenuId": 690906994118665,
|
||||
"RoleId": 371729946431493
|
||||
},
|
||||
{
|
||||
"MenuId": 690907673255942,
|
||||
"RoleId": 371729946431493
|
||||
},
|
||||
{
|
||||
"MenuId": 374792687640581,
|
||||
"RoleId": 371729946431493
|
||||
},
|
||||
{
|
||||
"MenuId": 485285246504970,
|
||||
"RoleId": 371729946431493
|
||||
},
|
||||
{
|
||||
"MenuId": 690907673255943,
|
||||
"RoleId": 371729946431493
|
||||
}
|
||||
]
|
12
assets/seed-data/Sys_UserWallet.json
Normal file
12
assets/seed-data/Sys_UserWallet.json
Normal file
@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"Id": 370942943322181,
|
||||
"OwnerDeptId": 372119301627909,
|
||||
"OwnerId": 370942943322181,
|
||||
},
|
||||
{
|
||||
"Id": 560217289236492,
|
||||
"OwnerDeptId": 372119301627909,
|
||||
"OwnerId": 560217289236492,
|
||||
}
|
||||
]
|
@ -3,7 +3,7 @@
|
||||
"devDependencies": {
|
||||
"cz-git": "^1.11.2",
|
||||
"commitizen": "^4.3.1",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier": "^3.6.1",
|
||||
"standard-version": "^9.5.0"
|
||||
},
|
||||
"config": {
|
||||
|
@ -10,7 +10,7 @@ public abstract class ServiceBase<TLogger> : ServiceBase
|
||||
/// </summary>
|
||||
protected ServiceBase() //
|
||||
{
|
||||
Logger = App.GetService<ILogger<TLogger>>();
|
||||
Logger = S<ILogger<TLogger>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -29,7 +29,7 @@ public abstract class ServiceBase : IScoped, IService
|
||||
/// </summary>
|
||||
protected ServiceBase()
|
||||
{
|
||||
UserToken = App.GetService<ContextUserToken>();
|
||||
UserToken = S<ContextUserToken>();
|
||||
ServiceId = Guid.NewGuid();
|
||||
}
|
||||
|
||||
@ -38,4 +38,19 @@ public abstract class ServiceBase : IScoped, IService
|
||||
|
||||
/// <inheritdoc />
|
||||
public ContextUserToken UserToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务
|
||||
/// </summary>
|
||||
#pragma warning disable RCS1036
|
||||
#pragma warning restore RCS1036
|
||||
|
||||
// ReSharper disable once MemberCanBeMadeStatic.Global
|
||||
#pragma warning disable CA1822, S2325
|
||||
protected T S<T>()
|
||||
#pragma warning restore S2325, CA1822
|
||||
where T : class
|
||||
{
|
||||
return App.GetService<T>();
|
||||
}
|
||||
}
|
@ -42,8 +42,9 @@ public sealed class ExampleService(BasicRepository<Tpl_Example, long> rpo) //
|
||||
.ToDictionaryAsync(a => a.Count())
|
||||
.ConfigureAwait(false);
|
||||
return ret.Select(x => new KeyValuePair<IImmutableDictionary<string, string>, int>(
|
||||
req.RequiredFields.ToImmutableDictionary(y => y, y => typeof(Tpl_Example).GetProperty(y)!.GetValue(x.Key)!.ToString())
|
||||
req.RequiredFields.ToImmutableDictionary(y => y, y => typeof(Tpl_Example).GetProperty(y)!.GetValue(x.Key)?.ToString())
|
||||
, x.Value))
|
||||
.Where(x => x.Key.Any(y => !y.Value.NullOrEmpty()))
|
||||
.OrderByDescending(x => x.Value);
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,17 @@
|
||||
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者字段接口
|
||||
/// 所有者字段接口
|
||||
/// </summary>
|
||||
public interface IFieldOwner
|
||||
{
|
||||
/// <summary>
|
||||
/// 拥有者部门编号
|
||||
/// 所有者部门编号
|
||||
/// </summary>
|
||||
long? OwnerDeptId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者用户编号
|
||||
/// 所有者用户编号
|
||||
/// </summary>
|
||||
long? OwnerId { get; init; }
|
||||
}
|
@ -40,7 +40,7 @@ public record Sys_DocCatalog : VersionEntity, IFieldOwner
|
||||
public virtual string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者
|
||||
/// 所有者
|
||||
/// </summary>
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
@ -48,7 +48,7 @@ public record Sys_DocCatalog : VersionEntity, IFieldOwner
|
||||
public Sys_User Owner { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者部门编号
|
||||
/// 所有者部门编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
@ -56,7 +56,7 @@ public record Sys_DocCatalog : VersionEntity, IFieldOwner
|
||||
public virtual long? OwnerDeptId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者用户编号
|
||||
/// 所有者用户编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
|
@ -41,7 +41,7 @@ public record Sys_DocContent : VersionEntity, IFieldEnabled, IFieldOwner
|
||||
public virtual bool Enabled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者
|
||||
/// 所有者
|
||||
/// </summary>
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
@ -49,7 +49,7 @@ public record Sys_DocContent : VersionEntity, IFieldEnabled, IFieldOwner
|
||||
public Sys_User Owner { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者部门编号
|
||||
/// 所有者部门编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
@ -57,7 +57,7 @@ public record Sys_DocContent : VersionEntity, IFieldEnabled, IFieldOwner
|
||||
public virtual long? OwnerDeptId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者用户编号
|
||||
/// 所有者用户编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
|
@ -35,7 +35,7 @@ public record Sys_JobRecord : LiteImmutableEntity
|
||||
public int HttpStatusCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者信息
|
||||
/// 所有者信息
|
||||
/// </summary>
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
|
@ -66,7 +66,7 @@ public record Sys_LoginLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFiel
|
||||
public virtual string LoginUserName { get; protected init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者
|
||||
/// 所有者
|
||||
/// </summary>
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
@ -74,7 +74,7 @@ public record Sys_LoginLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFiel
|
||||
public Sys_User Owner { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者部门编号
|
||||
/// 所有者部门编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
@ -82,7 +82,7 @@ public record Sys_LoginLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFiel
|
||||
public virtual long? OwnerDeptId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者用户编号
|
||||
/// 所有者用户编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
|
@ -76,7 +76,7 @@ public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFi
|
||||
public virtual int HttpStatusCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者
|
||||
/// 所有者
|
||||
/// </summary>
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
@ -84,7 +84,7 @@ public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFi
|
||||
public Sys_User Owner { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者部门编号
|
||||
/// 所有者部门编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
@ -92,7 +92,7 @@ public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFi
|
||||
public virtual long? OwnerDeptId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者用户编号
|
||||
/// 所有者用户编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
|
@ -0,0 +1,72 @@
|
||||
namespace NetAdmin.Domain.DbMaps.Sys;
|
||||
|
||||
/// <summary>
|
||||
/// 用户钱包表
|
||||
/// </summary>
|
||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_UserWallet))]
|
||||
public record Sys_UserWallet : LiteVersionEntity, IFieldOwner
|
||||
{
|
||||
/// <summary>
|
||||
/// 可用余额
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long AvailableBalance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 冻结余额
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long FrozenBalance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有者
|
||||
/// </summary>
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
[Navigate(nameof(OwnerId))]
|
||||
public Sys_User Owner { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有者部门编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long? OwnerDeptId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有者用户编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long? OwnerId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 总余额
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long TotalBalance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 总支出
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long TotalExpenditure { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 总收入
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long TotalIncome { get; init; }
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
namespace NetAdmin.Domain.DbMaps.Sys;
|
||||
|
||||
/// <summary>
|
||||
/// 钱包交易表
|
||||
/// </summary>
|
||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_WalletTrade))]
|
||||
public record Sys_WalletTrade : ImmutableEntity, IFieldOwner, IFieldSummary
|
||||
{
|
||||
/// <summary>
|
||||
/// 交易金额
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long Amount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易前余额
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long BalanceBefore { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 业务订单号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long? BusinessOrderNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有者
|
||||
/// </summary>
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
[Navigate(nameof(OwnerId))]
|
||||
public Sys_User Owner { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有者部门编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long? OwnerDeptId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有者用户编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual long? OwnerId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual string Summary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易方向
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual TradeDirections TradeDirection { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易类型
|
||||
/// </summary>
|
||||
[Column]
|
||||
[CsvIgnore]
|
||||
[JsonIgnore]
|
||||
public virtual TradeTypes TradeType { get; init; }
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
using NetAdmin.Domain.DbMaps.Sys;
|
||||
|
||||
namespace NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
|
||||
/// <summary>
|
||||
/// 请求:创建用户钱包
|
||||
/// </summary>
|
||||
public record CreateUserWalletReq : Sys_UserWallet;
|
@ -0,0 +1,15 @@
|
||||
namespace NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
|
||||
/// <summary>
|
||||
/// 请求:编辑用户钱包
|
||||
/// </summary>
|
||||
public record EditUserWalletReq : CreateUserWalletReq
|
||||
{
|
||||
/// <inheritdoc cref="EntityBase{T}.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; }
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
namespace NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
|
||||
/// <summary>
|
||||
/// 请求:查询用户钱包
|
||||
/// </summary>
|
||||
public sealed record QueryUserWalletReq : Sys_UserWallet
|
||||
{
|
||||
/// <summary>
|
||||
/// 部门编号
|
||||
/// </summary>
|
||||
public long DeptId { get; init; }
|
||||
|
||||
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 角色编号
|
||||
/// </summary>
|
||||
public long RoleId { get; init; }
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
using NetAdmin.Domain.Dto.Sys.User;
|
||||
|
||||
namespace NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
|
||||
/// <summary>
|
||||
/// 响应:查询用户钱包
|
||||
/// </summary>
|
||||
public record QueryUserWalletRsp : Sys_UserWallet
|
||||
{
|
||||
/// <inheritdoc cref="Sys_UserWallet.AvailableBalance" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long AvailableBalance { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override DateTime CreatedTime { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_UserWallet.FrozenBalance" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long FrozenBalance { get; init; }
|
||||
|
||||
/// <inheritdoc cref="EntityBase{T}.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_UserWallet.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_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; }
|
||||
|
||||
/// <inheritdoc cref="Sys_UserWallet.TotalIncome" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long TotalIncome { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldVersion.Version" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Version { get; init; }
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
namespace NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
/// <summary>
|
||||
/// 请求:创建钱包交易
|
||||
/// </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; }
|
||||
|
||||
/// <inheritdoc cref="Sys_WalletTrade.BusinessOrderNumber" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override long? BusinessOrderNumber { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldOwner.OwnerId" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[Required]
|
||||
[UserId]
|
||||
[Range(1, long.MaxValue)]
|
||||
public override long? OwnerId { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldSummary.Summary" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string Summary { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_WalletTrade.TradeDirection" />
|
||||
public override TradeDirections TradeDirection { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_WalletTrade.TradeType" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
[EnumDataType(typeof(TradeTypes))]
|
||||
public override TradeTypes TradeType {
|
||||
get => _tradeType;
|
||||
init {
|
||||
_tradeType = value;
|
||||
TradeDirection = value.Attr<TradeAttribute>().Direction;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
var tradeDirection = TradeType.Attr<TradeAttribute>().Direction;
|
||||
if (Amount == 0 || (tradeDirection == TradeDirections.Income && Amount < 0) || (tradeDirection == TradeDirections.Expense && Amount > 0)) {
|
||||
yield return new ValidationResult(Ln.交易金额不正确, [nameof(Amount)]);
|
||||
}
|
||||
|
||||
yield return ValidationResult.Success;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
namespace NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
/// <summary>
|
||||
/// 请求:编辑钱包交易
|
||||
/// </summary>
|
||||
public record EditWalletTradeReq : CreateWalletTradeReq
|
||||
{
|
||||
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Id { get; init; }
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using NetAdmin.Domain.DbMaps.Sys;
|
||||
|
||||
namespace NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
/// <summary>
|
||||
/// 请求:查询钱包交易
|
||||
/// </summary>
|
||||
public sealed record QueryWalletTradeReq : Sys_WalletTrade
|
||||
{
|
||||
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Id { get; init; }
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
using NetAdmin.Domain.Dto.Sys.User;
|
||||
|
||||
namespace NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
/// <summary>
|
||||
/// 响应:查询钱包交易
|
||||
/// </summary>
|
||||
public record QueryWalletTradeRsp : Sys_WalletTrade
|
||||
{
|
||||
/// <inheritdoc cref="Sys_WalletTrade.Amount" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Amount { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_WalletTrade.BalanceBefore" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long BalanceBefore { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_WalletTrade.BusinessOrderNumber" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override long? BusinessOrderNumber { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override DateTime CreatedTime { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserId" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override long? CreatedUserId { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserName" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string CreatedUserName { get; init; }
|
||||
|
||||
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Id { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_WalletTrade.Owner" />
|
||||
[CsvIgnore]
|
||||
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="IFieldSummary.Summary" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string Summary { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_WalletTrade.TradeDirection" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override TradeDirections TradeDirection { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_WalletTrade.TradeType" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override TradeTypes TradeType { get; init; }
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
namespace NetAdmin.Infrastructure.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// 交易方向特性
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Enum)]
|
||||
public sealed class TradeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 交易方向
|
||||
/// </summary>
|
||||
public TradeDirections Direction { get; init; }
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
namespace NetAdmin.Infrastructure.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 交易方向
|
||||
/// </summary>
|
||||
[Export]
|
||||
public enum TradeDirections
|
||||
{
|
||||
/// <summary>
|
||||
/// 收入
|
||||
/// </summary>
|
||||
[ResourceDescription<Ln>(nameof(Ln.收入))]
|
||||
Income = 4
|
||||
|
||||
,
|
||||
|
||||
/// <summary>
|
||||
/// 支出
|
||||
/// </summary>
|
||||
[ResourceDescription<Ln>(nameof(Ln.支出))]
|
||||
Expense = 5
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
namespace NetAdmin.Infrastructure.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 交易类型
|
||||
/// </summary>
|
||||
[Export]
|
||||
public enum TradeTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// 管理员充值
|
||||
/// </summary>
|
||||
[ResourceDescription<Ln>(nameof(Ln.管理员充值))]
|
||||
[Trade(Direction = TradeDirections.Income)]
|
||||
AdminDeposit = 1
|
||||
|
||||
,
|
||||
|
||||
/// <summary>
|
||||
/// 管理员扣费
|
||||
/// </summary>
|
||||
[ResourceDescription<Ln>(nameof(Ln.管理员扣费))]
|
||||
[Trade(Direction = TradeDirections.Expense)]
|
||||
AdminDeduct = 2
|
||||
|
||||
,
|
||||
|
||||
/// <summary>
|
||||
/// 自助充值
|
||||
/// </summary>
|
||||
[ResourceDescription<Ln>(nameof(Ln.自助充值))]
|
||||
[Trade(Direction = TradeDirections.Income)]
|
||||
SelfDeposit = 3
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
|
||||
namespace NetAdmin.SysComponent.Application.Modules.Sys;
|
||||
|
||||
/// <summary>
|
||||
/// 用户钱包模块
|
||||
/// </summary>
|
||||
public interface IUserWalletModule : ICrudModule<CreateUserWalletReq, QueryUserWalletRsp // 创建类型
|
||||
, EditUserWalletReq // 编辑类型
|
||||
, QueryUserWalletReq, QueryUserWalletRsp // 查询类型
|
||||
, DelReq // 删除类型
|
||||
>;
|
@ -0,0 +1,12 @@
|
||||
using NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
namespace NetAdmin.SysComponent.Application.Modules.Sys;
|
||||
|
||||
/// <summary>
|
||||
/// 钱包交易模块
|
||||
/// </summary>
|
||||
public interface IWalletTradeModule : ICrudModule<CreateWalletTradeReq, QueryWalletTradeRsp // 创建类型
|
||||
, EditWalletTradeReq // 编辑类型
|
||||
, QueryWalletTradeReq, QueryWalletTradeRsp // 查询类型
|
||||
, DelReq // 删除类型
|
||||
>;
|
@ -0,0 +1,6 @@
|
||||
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
|
||||
|
||||
/// <summary>
|
||||
/// 用户钱包服务
|
||||
/// </summary>
|
||||
public interface IUserWalletService : IService, IUserWalletModule;
|
@ -0,0 +1,6 @@
|
||||
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
|
||||
|
||||
/// <summary>
|
||||
/// 钱包交易服务
|
||||
/// </summary>
|
||||
public interface IWalletTradeService : IService, IWalletTradeModule;
|
@ -16,7 +16,7 @@ public sealed class ToolsService : ServiceBase<IToolsService>, IToolsService
|
||||
public async Task<object[][]> ExecuteSqlAsync(ExecuteSqlReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var cmd = App.GetService<IFreeSql>().Ado.CommandFluent(req.Sql).CommandTimeout(req.TimeoutSecs).ExecuteArrayAsync();
|
||||
var cmd = S<IFreeSql>().Ado.CommandFluent(req.Sql).CommandTimeout(req.TimeoutSecs).ExecuteArrayAsync();
|
||||
return req.WaitResult ? await cmd.ConfigureAwait(false) : null;
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ public sealed class UserProfileService(BasicRepository<Sys_UserProfile, long> rp
|
||||
|
||||
// 默认仪表版
|
||||
if (req.AppConfig == "[]") {
|
||||
req.AppConfig = BuildAppConfig(App.GetService<ContextUserInfo>().Roles.ToDictionary(x => x.Id, x => x.DashboardLayout));
|
||||
req.AppConfig = BuildAppConfig(S<ContextUserInfo>().Roles.ToDictionary(x => x.Id, x => x.DashboardLayout));
|
||||
}
|
||||
|
||||
return UpdateAsync(req, [nameof(req.AppConfig)], null, a => a.Id == UserToken.Id, null, true);
|
||||
|
@ -3,6 +3,7 @@ using NetAdmin.Domain.Contexts;
|
||||
using NetAdmin.Domain.DbMaps.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys.User;
|
||||
using NetAdmin.Domain.Dto.Sys.UserProfile;
|
||||
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
using NetAdmin.Domain.Dto.Sys.VerifyCode;
|
||||
using NetAdmin.Domain.Events.Sys;
|
||||
using NetAdmin.Domain.Extensions;
|
||||
@ -13,6 +14,7 @@ namespace NetAdmin.SysComponent.Application.Services.Sys;
|
||||
public sealed class UserService(
|
||||
BasicRepository<Sys_User, long> rpo //
|
||||
, IUserProfileService userProfileService //
|
||||
, IUserWalletService userWalletService //
|
||||
, IVerifyCodeService verifyCodeService //
|
||||
, IEventPublisher eventPublisher) //
|
||||
: RepositoryService<Sys_User, long, IUserService>(rpo), IUserService
|
||||
@ -112,6 +114,14 @@ public sealed class UserService(
|
||||
, AppConfig = appConfig
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// 钱包表
|
||||
_ = await userWalletService.CreateAsync(new CreateUserWalletReq() with //
|
||||
{
|
||||
Id = dbUser.Id, OwnerId = dbUser.Id, OwnerDeptId = dbUser.DeptId
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var userList = await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = dbUser.Id } }).ConfigureAwait(false);
|
||||
|
||||
// 发布用户创建事件
|
||||
|
@ -0,0 +1,145 @@
|
||||
using NetAdmin.Domain.DbMaps.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
using NetAdmin.Domain.Extensions;
|
||||
|
||||
namespace NetAdmin.SysComponent.Application.Services.Sys;
|
||||
|
||||
/// <inheritdoc cref="IUserWalletService" />
|
||||
public sealed class UserWalletService(BasicRepository<Sys_UserWallet, long> rpo) //
|
||||
: RepositoryService<Sys_UserWallet, long, IUserWalletService>(rpo), IUserWalletService
|
||||
{
|
||||
/// <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<QueryUserWalletReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
return QueryInternal(req).WithNoLockNoWait().CountAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await QueryInternal(req with { Order = Orders.None })
|
||||
.WithNoLockNoWait()
|
||||
.GroupBy(req.GetToListExp<Sys_UserWallet>())
|
||||
.ToDictionaryAsync(a => a.Count())
|
||||
.ConfigureAwait(false);
|
||||
return ret.Select(x => new KeyValuePair<IImmutableDictionary<string, string>, int>(
|
||||
req.RequiredFields.ToImmutableDictionary(
|
||||
y => y, y => typeof(Sys_UserWallet).GetProperty(y)!.GetValue(x.Key)?.ToString()), x.Value))
|
||||
.Where(x => x.Key.Any(y => !y.Value.NullOrEmpty()))
|
||||
.OrderByDescending(x => x.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<QueryUserWalletRsp> CreateAsync(CreateUserWalletReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
|
||||
return ret.Adapt<QueryUserWalletRsp>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> DeleteAsync(DelReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
return Rpo.DeleteAsync(a => a.Id == req.Id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<QueryUserWalletRsp> EditAsync(EditUserWalletReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
#if DBTYPE_SQLSERVER
|
||||
return (await UpdateReturnListAsync(req).ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryUserWalletRsp>();
|
||||
#else
|
||||
return await UpdateAsync(req).ConfigureAwait(false) > 0 ? await GetAsync(new QueryUserWalletReq { Id = req.Id }).ConfigureAwait(false) : null;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IActionResult> ExportAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
return ExportAsync<QueryUserWalletReq, QueryUserWalletRsp>(QueryInternal, req, Ln.用户钱包导出);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<QueryUserWalletRsp> GetAsync(QueryUserWalletReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await QueryInternal(new QueryReq<QueryUserWalletReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
|
||||
return ret.Adapt<QueryUserWalletRsp>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedQueryRsp<QueryUserWalletRsp>> PagedQueryAsync(PagedQueryReq<QueryUserWalletReq> 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<QueryUserWalletRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryUserWalletRsp>>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<QueryUserWalletRsp>> QueryAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await QueryInternal(req).WithNoLockNoWait().Take(req.Count).ToListAsync(req).ConfigureAwait(false);
|
||||
return ret.Adapt<IEnumerable<QueryUserWalletRsp>>();
|
||||
}
|
||||
|
||||
private ISelect<Sys_UserWallet> QueryInternal(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
IEnumerable<long> deptIds = null;
|
||||
if (req.Filter?.DeptId > 0) {
|
||||
deptIds = Rpo.Orm.Select<Sys_Dept>().Where(a => a.Id == req.Filter.DeptId).AsTreeCte().ToList(a => a.Id);
|
||||
}
|
||||
|
||||
return QueryInternal(req, deptIds);
|
||||
}
|
||||
|
||||
private ISelect<Sys_UserWallet> QueryInternal(QueryReq<QueryUserWalletReq> req, IEnumerable<long> deptIds)
|
||||
{
|
||||
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
|
||||
.WhereIf(req.Filter?.Id > 0, a => a.Id == req.Filter.Id)
|
||||
.WhereIf(deptIds != null, a => deptIds.Contains(a.Owner.DeptId))
|
||||
.WhereIf( //
|
||||
req.Filter?.RoleId > 0, a => a.Owner.Roles.Any(b => b.Id == req.Filter.RoleId));
|
||||
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (req.Order) {
|
||||
case Orders.None:
|
||||
return ret;
|
||||
case Orders.Random:
|
||||
return ret.OrderByRandom();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
using NetAdmin.Domain.DbMaps.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
using NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
using NetAdmin.Domain.Extensions;
|
||||
|
||||
namespace NetAdmin.SysComponent.Application.Services.Sys;
|
||||
|
||||
/// <inheritdoc cref="IWalletTradeService" />
|
||||
public sealed class WalletTradeService(BasicRepository<Sys_WalletTrade, long> rpo) //
|
||||
: RepositoryService<Sys_WalletTrade, long, IWalletTradeService>(rpo), IWalletTradeService
|
||||
{
|
||||
/// <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<QueryWalletTradeReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
return QueryInternal(req).WithNoLockNoWait().CountAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await QueryInternal(req with { Order = Orders.None })
|
||||
.WithNoLockNoWait()
|
||||
.GroupBy(req.GetToListExp<Sys_WalletTrade>())
|
||||
.ToDictionaryAsync(a => a.Count())
|
||||
.ConfigureAwait(false);
|
||||
return ret.Select(x => new KeyValuePair<IImmutableDictionary<string, string>, int>(
|
||||
req.RequiredFields.ToImmutableDictionary(
|
||||
y => y, y => typeof(Sys_WalletTrade).GetProperty(y)!.GetValue(x.Key)?.ToString()), x.Value))
|
||||
.Where(x => x.Key.Any(y => !y.Value.NullOrEmpty()))
|
||||
.OrderByDescending(x => x.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<QueryWalletTradeRsp> CreateAsync(CreateWalletTradeReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
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.钱包余额不足);
|
||||
}
|
||||
|
||||
_ = await userWalletService.EditAsync(wallet.Adapt<EditUserWalletReq>() with {
|
||||
AvailableBalance = wallet.AvailableBalance + req.Amount
|
||||
, TotalBalance = wallet.TotalBalance + 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>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> DeleteAsync(DelReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
return Rpo.DeleteAsync(a => a.Id == req.Id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<QueryWalletTradeRsp> EditAsync(EditWalletTradeReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
#if DBTYPE_SQLSERVER
|
||||
return (await UpdateReturnListAsync(req).ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryWalletTradeRsp>();
|
||||
#else
|
||||
return await UpdateAsync(req).ConfigureAwait(false) > 0
|
||||
? await GetAsync(new QueryWalletTradeReq { Id = req.Id }).ConfigureAwait(false)
|
||||
: null;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IActionResult> ExportAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
return ExportAsync<QueryWalletTradeReq, QueryWalletTradeRsp>(QueryInternal, req, Ln.钱包交易导出);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<QueryWalletTradeRsp> GetAsync(QueryWalletTradeReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await QueryInternal(new QueryReq<QueryWalletTradeReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
|
||||
return ret.Adapt<QueryWalletTradeRsp>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedQueryRsp<QueryWalletTradeRsp>> PagedQueryAsync(PagedQueryReq<QueryWalletTradeReq> 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<QueryWalletTradeRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryWalletTradeRsp>>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<QueryWalletTradeRsp>> QueryAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await QueryInternal(req).WithNoLockNoWait().Take(req.Count).ToListAsync(req).ConfigureAwait(false);
|
||||
return ret.Adapt<IEnumerable<QueryWalletTradeRsp>>();
|
||||
}
|
||||
|
||||
private ISelect<Sys_WalletTrade> QueryInternal(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
|
||||
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (req.Order) {
|
||||
case Orders.None:
|
||||
return ret;
|
||||
case Orders.Random:
|
||||
return ret.OrderByRandom();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
|
||||
|
||||
/// <summary>
|
||||
/// 用户钱包缓存
|
||||
/// </summary>
|
||||
public interface IUserWalletCache : ICache<IDistributedCache, IUserWalletService>, IUserWalletModule;
|
@ -0,0 +1,6 @@
|
||||
namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
|
||||
|
||||
/// <summary>
|
||||
/// 钱包交易缓存
|
||||
/// </summary>
|
||||
public interface IWalletTradeCache : ICache<IDistributedCache, IWalletTradeService>, IWalletTradeModule;
|
@ -0,0 +1,68 @@
|
||||
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
|
||||
namespace NetAdmin.SysComponent.Cache.Sys;
|
||||
|
||||
/// <inheritdoc cref="IUserWalletCache" />
|
||||
public sealed class UserWalletCache(IDistributedCache cache, IUserWalletService service)
|
||||
: DistributedCache<IUserWalletService>(cache, service), IScoped, IUserWalletCache
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||
{
|
||||
return Service.BulkDeleteAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<long> CountAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Service.CountAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Service.CountByAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryUserWalletRsp> CreateAsync(CreateUserWalletReq req)
|
||||
{
|
||||
return Service.CreateAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> DeleteAsync(DelReq req)
|
||||
{
|
||||
return Service.DeleteAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryUserWalletRsp> EditAsync(EditUserWalletReq req)
|
||||
{
|
||||
return Service.EditAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IActionResult> ExportAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Service.ExportAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryUserWalletRsp> GetAsync(QueryUserWalletReq req)
|
||||
{
|
||||
return Service.GetAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<PagedQueryRsp<QueryUserWalletRsp>> PagedQueryAsync(PagedQueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Service.PagedQueryAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<QueryUserWalletRsp>> QueryAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Service.QueryAsync(req);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
using NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
namespace NetAdmin.SysComponent.Cache.Sys;
|
||||
|
||||
/// <inheritdoc cref="IWalletTradeCache" />
|
||||
public sealed class WalletTradeCache(IDistributedCache cache, IWalletTradeService service)
|
||||
: DistributedCache<IWalletTradeService>(cache, service), IScoped, IWalletTradeCache
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||
{
|
||||
return Service.BulkDeleteAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<long> CountAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Service.CountAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Service.CountByAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryWalletTradeRsp> CreateAsync(CreateWalletTradeReq req)
|
||||
{
|
||||
return Service.CreateAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> DeleteAsync(DelReq req)
|
||||
{
|
||||
return Service.DeleteAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryWalletTradeRsp> EditAsync(EditWalletTradeReq req)
|
||||
{
|
||||
return Service.EditAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IActionResult> ExportAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Service.ExportAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryWalletTradeRsp> GetAsync(QueryWalletTradeReq req)
|
||||
{
|
||||
return Service.GetAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<PagedQueryRsp<QueryWalletTradeRsp>> PagedQueryAsync(PagedQueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Service.PagedQueryAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<QueryWalletTradeRsp>> QueryAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Service.QueryAsync(req);
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||
|
||||
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 UserWalletController(IUserWalletCache cache) : ControllerBase<IUserWalletCache, IUserWalletService>(cache), IUserWalletModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 批量删除用户钱包
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||
{
|
||||
return Cache.BulkDeleteAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户钱包计数
|
||||
/// </summary>
|
||||
public Task<long> CountAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Cache.CountAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户钱包分组计数
|
||||
/// </summary>
|
||||
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Cache.CountByAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建用户钱包
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<QueryUserWalletRsp> CreateAsync(CreateUserWalletReq req)
|
||||
{
|
||||
return Cache.CreateAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除用户钱包
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<int> DeleteAsync(DelReq req)
|
||||
{
|
||||
return Cache.DeleteAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑用户钱包
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<QueryUserWalletRsp> EditAsync(EditUserWalletReq req)
|
||||
{
|
||||
return Cache.EditAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出用户钱包
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public Task<IActionResult> ExportAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Cache.ExportAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取单个用户钱包
|
||||
/// </summary>
|
||||
public Task<QueryUserWalletRsp> GetAsync(QueryUserWalletReq req)
|
||||
{
|
||||
return Cache.GetAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询用户钱包
|
||||
/// </summary>
|
||||
public Task<PagedQueryRsp<QueryUserWalletRsp>> PagedQueryAsync(PagedQueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Cache.PagedQueryAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询用户钱包
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public Task<IEnumerable<QueryUserWalletRsp>> QueryAsync(QueryReq<QueryUserWalletReq> req)
|
||||
{
|
||||
return Cache.QueryAsync(req);
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
using NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||
|
||||
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 WalletTradeController(IWalletTradeCache cache) : ControllerBase<IWalletTradeCache, IWalletTradeService>(cache), IWalletTradeModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 批量删除钱包交易
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||
{
|
||||
return Cache.BulkDeleteAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 钱包交易计数
|
||||
/// </summary>
|
||||
public Task<long> CountAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Cache.CountAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 钱包交易分组计数
|
||||
/// </summary>
|
||||
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Cache.CountByAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建钱包交易
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<QueryWalletTradeRsp> CreateAsync(CreateWalletTradeReq req)
|
||||
{
|
||||
return Cache.CreateAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除钱包交易
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<int> DeleteAsync(DelReq req)
|
||||
{
|
||||
return Cache.DeleteAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑钱包交易
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<QueryWalletTradeRsp> EditAsync(EditWalletTradeReq req)
|
||||
{
|
||||
return Cache.EditAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出钱包交易
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public Task<IActionResult> ExportAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Cache.ExportAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取单个钱包交易
|
||||
/// </summary>
|
||||
public Task<QueryWalletTradeRsp> GetAsync(QueryWalletTradeReq req)
|
||||
{
|
||||
return Cache.GetAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询钱包交易
|
||||
/// </summary>
|
||||
public Task<PagedQueryRsp<QueryWalletTradeRsp>> PagedQueryAsync(PagedQueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Cache.PagedQueryAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询钱包交易
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public Task<IEnumerable<QueryWalletTradeRsp>> QueryAsync(QueryReq<QueryWalletTradeReq> req)
|
||||
{
|
||||
return Cache.QueryAsync(req);
|
||||
}
|
||||
}
|
@ -45,21 +45,21 @@ public sealed class OperationLogger : IEventSubscriber
|
||||
|
||||
// 插入登录日志
|
||||
if (log.ApiPathCrc32 == Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Crc32()) {
|
||||
_ = await App.GetService<ILoginLogCache>().CreateAsync(log.Adapt<CreateLoginLogReq>()).ConfigureAwait(false);
|
||||
_ = await S<ILoginLogCache>().CreateAsync(log.Adapt<CreateLoginLogReq>()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果首尾日期不一致,要分别插入不同的日期分表
|
||||
if (inserts[0].CreatedTime.Date != inserts[^1].CreatedTime.Date) {
|
||||
foreach (var dayInserts in inserts.GroupBy(x => x.CreatedTime.Date)) {
|
||||
await App.GetService<IFreeSql>()
|
||||
await S<IFreeSql>()
|
||||
.Insert<Sys_RequestLog>(dayInserts.Select(x => x))
|
||||
.ExecuteSqlBulkCopyAsync(tableName: $"{nameof(Sys_RequestLog)}_{dayInserts.Key:yyyyMMdd}")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
await App.GetService<IFreeSql>()
|
||||
await S<IFreeSql>()
|
||||
.Insert<Sys_RequestLog>(inserts)
|
||||
.ExecuteSqlBulkCopyAsync(tableName: $"{nameof(Sys_RequestLog)}_{inserts[0].CreatedTime:yyyyMMdd}")
|
||||
.ConfigureAwait(false);
|
||||
|
@ -149,7 +149,7 @@ public sealed class SqlAuditor : ISingleton
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置拥有者
|
||||
/// 设置所有者
|
||||
/// </summary>
|
||||
private static void SetOwner(AuditValueEventArgs e, ContextUserInfo userInfo)
|
||||
{
|
||||
|
@ -10,8 +10,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.1",
|
||||
"ace-builds": "1.42.0",
|
||||
"aieditor": "1.3.9",
|
||||
"ace-builds": "1.43.0",
|
||||
"aieditor": "1.4.0",
|
||||
"axios": "1.10.0",
|
||||
"crypto-js": "4.2.0",
|
||||
"dayjs": "1.11.13",
|
||||
@ -23,8 +23,8 @@
|
||||
"nprogress": "0.2.0",
|
||||
"sortablejs": "1.15.6",
|
||||
"vkbeautify": "0.99.3",
|
||||
"vue": "3.5.16",
|
||||
"vue-i18n": "11.1.6",
|
||||
"vue": "3.5.17",
|
||||
"vue-i18n": "11.1.7",
|
||||
"vue-router": "4.5.1",
|
||||
"vue3-ace-editor": "2.2.4",
|
||||
"vue3-json-viewer": "2.4.0",
|
||||
@ -32,12 +32,12 @@
|
||||
"vuex": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "5.2.4",
|
||||
"prettier": "3.5.3",
|
||||
"@vitejs/plugin-vue": "6.0.0",
|
||||
"prettier": "3.6.1",
|
||||
"prettier-plugin-organize-attributes": "1.0.0",
|
||||
"sass": "1.89.2",
|
||||
"terser": "5.43.0",
|
||||
"vite": "6.3.5"
|
||||
"terser": "5.43.1",
|
||||
"vite": "7.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
95
src/frontend/admin/src/api/sys/userwallet.js
Normal file
95
src/frontend/admin/src/api/sys/userwallet.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 用户钱包服务
|
||||
* @module @/api/sys/user.wallet
|
||||
*/
|
||||
import config from '@/config'
|
||||
import http from '@/utils/request'
|
||||
export default {
|
||||
/**
|
||||
* 批量删除用户钱包
|
||||
*/
|
||||
bulkDelete: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/bulk.delete`,
|
||||
name: `批量删除用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户钱包计数
|
||||
*/
|
||||
count: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/count`,
|
||||
name: `用户钱包计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户钱包分组计数
|
||||
*/
|
||||
countBy: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/count.by`,
|
||||
name: `用户钱包分组计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建用户钱包
|
||||
*/
|
||||
create: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/create`,
|
||||
name: `创建用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除用户钱包
|
||||
*/
|
||||
delete: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/delete`,
|
||||
name: `删除用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 编辑用户钱包
|
||||
*/
|
||||
edit: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/edit`,
|
||||
name: `编辑用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取单个用户钱包
|
||||
*/
|
||||
get: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/get`,
|
||||
name: `获取单个用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询用户钱包
|
||||
*/
|
||||
pagedQuery: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/paged.query`,
|
||||
name: `分页查询用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
}
|
95
src/frontend/admin/src/api/sys/wallettrade.js
Normal file
95
src/frontend/admin/src/api/sys/wallettrade.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 钱包交易服务
|
||||
* @module @/api/sys/wallet.trade
|
||||
*/
|
||||
import config from '@/config'
|
||||
import http from '@/utils/request'
|
||||
export default {
|
||||
/**
|
||||
* 批量删除钱包交易
|
||||
*/
|
||||
bulkDelete: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/bulk.delete`,
|
||||
name: `批量删除钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 钱包交易计数
|
||||
*/
|
||||
count: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/count`,
|
||||
name: `钱包交易计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 钱包交易分组计数
|
||||
*/
|
||||
countBy: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/count.by`,
|
||||
name: `钱包交易分组计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建钱包交易
|
||||
*/
|
||||
create: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/create`,
|
||||
name: `创建钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除钱包交易
|
||||
*/
|
||||
delete: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/delete`,
|
||||
name: `删除钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 编辑钱包交易
|
||||
*/
|
||||
edit: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/edit`,
|
||||
name: `编辑钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取单个钱包交易
|
||||
*/
|
||||
get: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/get`,
|
||||
name: `获取单个钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询钱包交易
|
||||
*/
|
||||
pagedQuery: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/paged.query`,
|
||||
name: `分页查询钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
}
|
@ -60,6 +60,9 @@ export default {
|
||||
hasPermission: function (p) {
|
||||
return this.permissions.includes('*/*/*') || this.permissions.some((a) => a === p)
|
||||
},
|
||||
hasApiPermission: function (p) {
|
||||
return this.apiPermissions.includes('*/*/*') || this.apiPermissions.some((a) => a === p)
|
||||
},
|
||||
}
|
||||
|
||||
app.use(JsonViewer)
|
||||
|
@ -6,7 +6,7 @@
|
||||
<el-button
|
||||
@click="
|
||||
() => {
|
||||
this.$router.push({ path: '/sys/job', query: { view: 'fail' } })
|
||||
this.$router.push({ path: '/system/job', query: { view: 'fail' } })
|
||||
this.$emit('closed')
|
||||
}
|
||||
"
|
||||
@ -22,7 +22,7 @@
|
||||
<el-button
|
||||
@click="
|
||||
() => {
|
||||
this.$router.push({ path: '/sys/job' })
|
||||
this.$router.push({ path: '/system/job' })
|
||||
this.$emit('closed')
|
||||
}
|
||||
"
|
||||
|
@ -624,6 +624,6 @@ export default {
|
||||
链接: 'Link',
|
||||
框架: 'IFrame',
|
||||
按钮: 'Button',
|
||||
倒序排序:'Sort-Descending',
|
||||
顺序排序:'Sort-Ascending',
|
||||
倒序排序: 'Sort-Descending',
|
||||
顺序排序: 'Sort-Ascending',
|
||||
}
|
@ -622,6 +622,6 @@ export default {
|
||||
链接: '链接',
|
||||
框架: '框架',
|
||||
按钮: '按钮',
|
||||
倒序排序:'倒序排序',
|
||||
顺序排序:'顺序排序',
|
||||
倒序排序: '倒序排序',
|
||||
顺序排序: '顺序排序',
|
||||
}
|
@ -51,5 +51,12 @@ export default {
|
||||
global.user?.roles.findIndex((x) => x.ignorePermissionControl) >= 0
|
||||
? ['*/*/*']
|
||||
: tool.recursiveFindProperty(preloads[0]?.data, 'type', 'button').map((x) => x.tag)
|
||||
global.apiPermissions =
|
||||
global.user?.roles.findIndex((x) => x.ignorePermissionControl) >= 0
|
||||
? ['*/*/*']
|
||||
: preloads[1]?.data?.roles
|
||||
?.map((x) => x.apiIds.join(','))
|
||||
?.join(',')
|
||||
.split(',')
|
||||
},
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-card :header="$t('登录日志')" shadow="never">
|
||||
<login-log :keywords="$GLOBAL.user.id" :show-filter="false"></login-log>
|
||||
<login-log :ownerId="$GLOBAL.user.id.toString()" :show-filter="false"></login-log>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
|
@ -74,10 +74,19 @@
|
||||
<na-search
|
||||
:controls="[
|
||||
{
|
||||
type: 'input',
|
||||
field: ['root', 'keywords'],
|
||||
placeholder: $t('日志编号 / 登录名 / 客户端IP'),
|
||||
type: 'select-input',
|
||||
field: [
|
||||
'dy',
|
||||
[
|
||||
{ label: $t('日志编号'), key: 'id' },
|
||||
{ label: $t('用户编号'), key: 'owner.id' },
|
||||
{ label: $t('登录名'), key: 'loginUserName' },
|
||||
{ label: $t('客户端IP'), key: 'createdClientIp' },
|
||||
],
|
||||
],
|
||||
placeholder: $t('匹配内容'),
|
||||
style: 'width:25rem',
|
||||
selectStyle: 'width:8rem',
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
@ -150,7 +159,15 @@ export default {
|
||||
naInfo,
|
||||
},
|
||||
computed: {},
|
||||
created() {},
|
||||
created() {
|
||||
if (this.ownerId) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.id',
|
||||
operator: 'eq',
|
||||
value: this.ownerId,
|
||||
})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statistics: {
|
||||
@ -227,8 +244,11 @@ export default {
|
||||
this.$refs.search.search()
|
||||
},
|
||||
onReset() {
|
||||
if (!this.showFilter) return
|
||||
Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))
|
||||
if (this.showFilter) {
|
||||
Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))
|
||||
}
|
||||
this.$refs.search.selectInputKey = 'id'
|
||||
if (this.ownerId) this.$refs.search.selectInputKey = 'owner.id'
|
||||
},
|
||||
//搜索
|
||||
async onSearch(form) {
|
||||
@ -255,6 +275,13 @@ export default {
|
||||
)
|
||||
}
|
||||
|
||||
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.errorCode === 'string' && form.dy.errorCode.trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'errorCode',
|
||||
@ -279,6 +306,15 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof form.dy['owner.id'] === 'string' && form.dy['owner.id'].trim() !== '') {
|
||||
this.$refs.search.selectInputKey = 'owner.id'
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.id',
|
||||
operator: 'eq',
|
||||
value: form.dy['owner.id'],
|
||||
})
|
||||
}
|
||||
|
||||
await this.$refs.table.upData()
|
||||
},
|
||||
|
||||
@ -295,6 +331,15 @@ export default {
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
if (this.ownerId) {
|
||||
this.$refs.search.selectInputKey = 'owner.id'
|
||||
this.$refs.search.form.dy['owner.id'] = this.ownerId
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'owner.id',
|
||||
value: this.ownerId,
|
||||
type: 'dy',
|
||||
})
|
||||
}
|
||||
if (this.keywords) {
|
||||
this.$refs.search.form.root.keywords = this.keywords
|
||||
this.$refs.search.keeps.push({
|
||||
@ -303,8 +348,9 @@ export default {
|
||||
type: 'root',
|
||||
})
|
||||
}
|
||||
this.onReset()
|
||||
},
|
||||
props: { keywords: { type: String }, showFilter: { type: Boolean, default: true } },
|
||||
props: { keywords: { type: String }, showFilter: { type: Boolean, default: true }, ownerId: { type: String } },
|
||||
watch: {},
|
||||
}
|
||||
</script>
|
||||
|
@ -67,7 +67,7 @@
|
||||
config: { props: { label: 'userName', value: 'id' } },
|
||||
placeholder: '用户',
|
||||
style: 'width:15rem',
|
||||
condition: () => $GLOBAL.hasPermission('sys/log/operation/user'),
|
||||
condition: () => $GLOBAL.hasApiPermission('api/sys/user/query'),
|
||||
},
|
||||
{
|
||||
multiple: true,
|
||||
|
323
src/frontend/admin/src/views/sys/trade/index.vue
Normal file
323
src/frontend/admin/src/views/sys/trade/index.vue
Normal 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">
|
||||
<scStatistic :title="$t('总数')" :value="statistics.total" group-separator></scStatistic>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-header>
|
||||
<el-header class="el-header-select-filter">
|
||||
<scSelectFilter
|
||||
: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"></scSelectFilter>
|
||||
</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">
|
||||
<scTable
|
||||
:context-menus="[
|
||||
'id',
|
||||
'ownerId',
|
||||
'createdTime',
|
||||
'tradeType',
|
||||
'amount',
|
||||
'balanceBefore',
|
||||
'summary',
|
||||
'owner.userName',
|
||||
'tradeDirection',
|
||||
]"
|
||||
:context-multi="{ id: ['createdTime'], ownerId: ['owner.userName'] }"
|
||||
: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>
|
||||
<naColId :label="$t('交易编号')" prop="id" sortable="custom" width="170" />
|
||||
<naColUser
|
||||
:clickOpenDialog="$GLOBAL.hasApiPermission('api/sys/user/get')"
|
||||
:label="$t('所属用户')"
|
||||
header-align="center"
|
||||
nestProp="owner.userName"
|
||||
nestProp2="ownerId"
|
||||
prop="ownerId"
|
||||
sortable="custom"
|
||||
width="170"></naColUser>
|
||||
<naColIndicator
|
||||
: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" />
|
||||
<naColIndicator
|
||||
: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" />
|
||||
<naColOperation :buttons="[naColOperation.buttons[0]]" :vue="this" width="50" />
|
||||
</scTable>
|
||||
</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"></save-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import table from '@/config/table'
|
||||
import naColOperation from '@/config/naColOperation'
|
||||
const naColUser = defineAsyncComponent(() => import('@/components/naColUser'))
|
||||
const saveDialog = defineAsyncComponent(() => import('./save.vue'))
|
||||
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></style>
|
119
src/frontend/admin/src/views/sys/trade/save.vue
Normal file
119
src/frontend/admin/src/views/sys/trade/save.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<scDialog v-model="visible" :title="`${titleMap[mode]}:${form?.id ?? '...'}`" @closed="$emit('closed')" destroy-on-close full-screen>
|
||||
<div v-loading="loading">
|
||||
<el-tabs v-model="tabId" @tab-change="tabChange" tab-position="top">
|
||||
<el-tab-pane :label="$t('基本信息')" name="basic">
|
||||
<el-form :disabled="mode === 'view'" :model="form" :rules="rules" label-width="15rem" ref="dialogForm">
|
||||
<el-form-item :label="$t('唯一编码')" prop="id">
|
||||
<el-input v-model="form.id" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易方向')" prop="tradeDirection">
|
||||
<el-input v-model="form.tradeDirection" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易类型')" prop="tradeType">
|
||||
<el-input v-model="form.tradeType" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易前余额')" prop="balanceBefore">
|
||||
<el-input v-model="form.balanceBefore" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易金额')" prop="amount">
|
||||
<el-input v-model="form.amount" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易后余额')">
|
||||
<el-input :value="form.balanceBefore + form.amount" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者部门编号')" prop="ownerDeptId">
|
||||
<el-input v-model="form.ownerDeptId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者用户编号')" prop="ownerId">
|
||||
<el-input v-model="form.ownerId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('创建者用户编号')" prop="createdUserId">
|
||||
<el-input v-model="form.createdUserId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者用户名')" prop="createdUserName">
|
||||
<el-input v-model="form.createdUserName" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('创建时间')" prop="createdTime">
|
||||
<el-input v-model="form.createdTime" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
|
||||
<JsonViewer
|
||||
:expand-depth="5"
|
||||
:theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'dark' : 'light'"
|
||||
:value="form"
|
||||
copyable
|
||||
expanded
|
||||
sort></JsonViewer>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
|
||||
<el-button v-if="mode !== 'view'" :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
|
||||
</template>
|
||||
</scDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
//表单数据
|
||||
form: {},
|
||||
loading: true,
|
||||
mode: 'add',
|
||||
//验证规则
|
||||
rules: {},
|
||||
tabId: 'basic',
|
||||
titleMap: {
|
||||
add: this.$t('新增交易'),
|
||||
edit: this.$t('编辑交易'),
|
||||
view: this.$t('查看交易'),
|
||||
},
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
emits: ['success', 'closed', 'mounted'],
|
||||
methods: {
|
||||
//显示
|
||||
async open(data) {
|
||||
this.visible = true
|
||||
if (data.mode === 'add') {
|
||||
this.loading = false
|
||||
return this
|
||||
}
|
||||
this.loading = true
|
||||
this.mode = data.mode
|
||||
if (data.row?.id) {
|
||||
const res = await this.$API.sys_wallettrade.get.post({ id: data.row.id })
|
||||
if (res.data) {
|
||||
Object.assign(this.form, res.data)
|
||||
this.loading = false
|
||||
return this
|
||||
}
|
||||
}
|
||||
this.$message.error(`未找到该数据`)
|
||||
return this
|
||||
},
|
||||
//表单提交方法
|
||||
async submit() {
|
||||
const valid = await this.$refs.dialogForm.validate().catch(() => {})
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.loading = true
|
||||
//
|
||||
this.loading = false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$emit('mounted')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -116,8 +116,8 @@
|
||||
<naColAvatar :label="$t('用户名')" prop="userName" width="170" />
|
||||
<el-table-column :label="$t('手机号 / 邮箱')" align="right" prop="mobile" sortable="custom" width="250">
|
||||
<template #default="{ row }">
|
||||
<p>{{ row.mobile }}</p>
|
||||
<p>{{ row.email }}</p>
|
||||
<p>{{ row.mobile ?? '-' }}</p>
|
||||
<p>{{ row.email ?? '-' }}</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<naColTags
|
||||
|
@ -7,7 +7,7 @@
|
||||
destroy-on-close
|
||||
full-screen>
|
||||
<el-form
|
||||
:disabled="mode === 'view' && tabId !== 'log'"
|
||||
:disabled="mode === 'view' && tabId !== 'log' && tabId !== 'wallet' && tabId !== 'trade'"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="right"
|
||||
@ -234,6 +234,12 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('钱包信息')" name="wallet">
|
||||
<wallet v-if="tabId === 'wallet'" :id="form.id.toString()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('交易流水')" name="trade">
|
||||
<trade v-if="tabId === 'trade'" :ownerId="form.id.toString()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('操作日志')" name="log">
|
||||
<log v-if="tabId === 'log'" :owner-id="form.id"></log>
|
||||
</el-tab-pane>
|
||||
@ -259,12 +265,14 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const log = defineAsyncComponent(() => import('@/views/sys/log/operation'))
|
||||
const trade = defineAsyncComponent(() => import('@/views/sys/trade'))
|
||||
const wallet = defineAsyncComponent(() => import('@/views/sys/wallet'))
|
||||
const naArea = defineAsyncComponent(() => import('@/components/naArea'))
|
||||
const naDept = defineAsyncComponent(() => import('@/components/naDept'))
|
||||
const scUpload = defineAsyncComponent(() => import('@/components/scUpload'))
|
||||
const scSelect = defineAsyncComponent(() => import('@/components/scSelect'))
|
||||
export default {
|
||||
components: { log, naArea, naDept, scUpload, scSelect },
|
||||
components: { log, naArea, naDept, scUpload, scSelect, trade, wallet },
|
||||
data() {
|
||||
return {
|
||||
//表单数据
|
||||
|
315
src/frontend/admin/src/views/sys/wallet/index.vue
Normal file
315
src/frontend/admin/src/views/sys/wallet/index.vue
Normal file
@ -0,0 +1,315 @@
|
||||
<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">
|
||||
<scStatistic :title="$t('总数')" :value="statistics.total" group-separator></scStatistic>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</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: 'owner.email' },
|
||||
{ label: $t('手机号'), key: 'owner.mobile' },
|
||||
],
|
||||
],
|
||||
placeholder: $t('匹配内容'),
|
||||
style: 'width:25rem',
|
||||
selectStyle: 'width:8rem',
|
||||
},
|
||||
{
|
||||
type: 'remote-select',
|
||||
field: ['filter', 'deptId'],
|
||||
api: $API.sys_dept.query,
|
||||
config: { props: { label: 'name', value: 'id' } },
|
||||
placeholder: $t('所属部门'),
|
||||
style: 'width:15rem',
|
||||
condition: () => $GLOBAL.hasApiPermission('api/sys/dept/query'),
|
||||
},
|
||||
{
|
||||
type: 'remote-select',
|
||||
field: ['filter', 'roleId'],
|
||||
api: $API.sys_role.query,
|
||||
config: { props: { label: 'name', value: 'id' } },
|
||||
placeholder: $t('所属角色'),
|
||||
style: 'width:15rem',
|
||||
condition: () => $GLOBAL.hasApiPermission('api/sys/dept/query'),
|
||||
},
|
||||
]"
|
||||
: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">
|
||||
<scTable
|
||||
:context-menus="[
|
||||
'id',
|
||||
'ownerId',
|
||||
'owner.userName',
|
||||
'createdTime',
|
||||
'totalBalance',
|
||||
'availableBalance',
|
||||
'frozenBalance',
|
||||
'totalIncome',
|
||||
'totalExpenditure',
|
||||
'modifiedTime',
|
||||
]"
|
||||
:context-multi="{ id: ['createdTime'], ownerId: ['owner.userName'] }"
|
||||
:context-opers="['view']"
|
||||
:default-sort="{ prop: 'id', order: 'descending' }"
|
||||
:export-api="$API.sys_userwallet.export"
|
||||
:params="query"
|
||||
:query-api="$API.sys_userwallet.pagedQuery"
|
||||
:vue="this"
|
||||
@data-change="getStatistics"
|
||||
@selection-change="
|
||||
(items) => {
|
||||
selection = items
|
||||
}
|
||||
"
|
||||
ref="table"
|
||||
remote-filter
|
||||
remote-sort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<naColId :label="$t('钱包编号')" prop="id" sortable="custom" width="170" />
|
||||
<naColUser
|
||||
:clickOpenDialog="$GLOBAL.hasApiPermission('api/sys/user/get')"
|
||||
:label="$t('所属用户')"
|
||||
nestProp="owner.userName"
|
||||
nestProp2="ownerId"
|
||||
prop="ownerId"
|
||||
sortable="custom"
|
||||
width="170"></naColUser>
|
||||
<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('可用余额')"
|
||||
align="right"
|
||||
prop="availableBalance"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.frozenBalance)"
|
||||
:label="$t('冻结余额')"
|
||||
align="right"
|
||||
prop="frozenBalance"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.totalIncome)"
|
||||
:label="$t('总收入')"
|
||||
align="right"
|
||||
prop="totalIncome"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.totalExpenditure)"
|
||||
:label="$t('总支出')"
|
||||
align="right"
|
||||
prop="totalExpenditure"
|
||||
sortable="custom" />
|
||||
<el-table-column v-tim :label="$t('最后交易时间')" align="right" prop="modifiedTime" sortable="custom" width="150">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.modifiedTime" v-time.tip="row.modifiedTime" :title="row.modifiedTime"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<naColOperation
|
||||
:buttons="[
|
||||
naColOperation.buttons[0],
|
||||
{
|
||||
icon: 'el-icon-plus',
|
||||
title: $t('新建交易'),
|
||||
click: async (row, vue) => {
|
||||
vue.dialog.trade = { row }
|
||||
},
|
||||
condition: () => {
|
||||
return $GLOBAL.hasApiPermission('api/sys/wallet.trade/create')
|
||||
},
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
width="120" />
|
||||
</scTable>
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
<trade-dialog
|
||||
v-if="dialog.trade"
|
||||
@closed="dialog.trade = null"
|
||||
@mounted="$refs.tradeDialog.open(dialog.trade)"
|
||||
@success="(data, mode) => $refs.table.refresh()"
|
||||
ref="tradeDialog"></trade-dialog>
|
||||
|
||||
<save-dialog
|
||||
v-if="dialog.save"
|
||||
@closed="dialog.save = null"
|
||||
@mounted="$refs.saveDialog.open(dialog.save)"
|
||||
@success="(data, mode) => $refs.table.refresh()"
|
||||
ref="saveDialog"></save-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import table from '@/config/table'
|
||||
import naColOperation from '@/config/naColOperation'
|
||||
const tradeDialog = defineAsyncComponent(() => import('./trade.vue'))
|
||||
const saveDialog = defineAsyncComponent(() => import('./save.vue'))
|
||||
const naColUser = defineAsyncComponent(() => import('@/components/naColUser'))
|
||||
export default {
|
||||
components: {
|
||||
tradeDialog,
|
||||
saveDialog,
|
||||
naColUser,
|
||||
},
|
||||
computed: {
|
||||
naColOperation() {
|
||||
return naColOperation
|
||||
},
|
||||
table() {
|
||||
return table
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
if (this.roleId) {
|
||||
this.query.filter.roleId = this.roleId
|
||||
}
|
||||
if (this.deptId) {
|
||||
this.query.filter.deptId = this.deptId
|
||||
}
|
||||
if (this.id) {
|
||||
this.query.dynamicFilter.filters.push({ field: 'id', operator: 'eq', value: this.id })
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statistics: {
|
||||
total: '...',
|
||||
},
|
||||
dialog: {},
|
||||
loading: false,
|
||||
query: {
|
||||
dynamicFilter: {
|
||||
filters: [],
|
||||
},
|
||||
filter: {},
|
||||
keywords: this.keywords,
|
||||
},
|
||||
selection: [],
|
||||
}
|
||||
},
|
||||
inject: ['reload'],
|
||||
methods: {
|
||||
async getStatistics() {
|
||||
this.statistics.total = this.$refs.table?.total
|
||||
},
|
||||
//重置
|
||||
onReset() {
|
||||
if (this.id) {
|
||||
this.$refs.search.selectInputKey = 'id'
|
||||
}
|
||||
},
|
||||
//搜索
|
||||
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['owner.email'] === 'string' && form.dy['owner.email'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.email',
|
||||
operator: 'eq',
|
||||
value: form.dy['owner.email'],
|
||||
})
|
||||
}
|
||||
if (typeof form.dy['owner.mobile'] === 'string' && form.dy['owner.mobile'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.mobile',
|
||||
operator: 'eq',
|
||||
value: form.dy['owner.mobile'],
|
||||
})
|
||||
}
|
||||
if (typeof form.dy['id'] === 'string' && form.dy['id'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'id',
|
||||
operator: 'eq',
|
||||
value: form.dy['id'],
|
||||
})
|
||||
}
|
||||
await this.$refs.table.upData()
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
if (this.keywords) {
|
||||
this.$refs.search.form.root.keywords = this.keywords
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'keywords',
|
||||
value: this.keywords,
|
||||
type: 'root',
|
||||
})
|
||||
}
|
||||
|
||||
if (this.id) {
|
||||
this.$refs.search.selectInputKey = 'id'
|
||||
this.$refs.search.form.dy.id = this.id
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'id',
|
||||
value: this.id,
|
||||
type: 'dy',
|
||||
})
|
||||
}
|
||||
|
||||
if (this.roleId) {
|
||||
this.$refs.search.form.filter.roleId = this.roleId
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'roleId',
|
||||
value: this.roleId,
|
||||
type: 'filter',
|
||||
})
|
||||
}
|
||||
if (this.deptId) {
|
||||
this.$refs.search.form.filter.deptId = this.deptId
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'deptId',
|
||||
value: this.deptId,
|
||||
type: 'filter',
|
||||
})
|
||||
}
|
||||
|
||||
this.onReset()
|
||||
},
|
||||
props: ['keywords', 'roleId', 'deptId', 'id'],
|
||||
watch: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
124
src/frontend/admin/src/views/sys/wallet/save.vue
Normal file
124
src/frontend/admin/src/views/sys/wallet/save.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<scDialog v-model="visible" :title="`${titleMap[mode]}:${form?.id ?? '...'}`" @closed="$emit('closed')" destroy-on-close full-screen>
|
||||
<div v-loading="loading">
|
||||
<el-tabs v-model="tabId" @tab-change="tabChange" tab-position="top">
|
||||
<el-tab-pane :label="$t('基本信息')" name="basic">
|
||||
<el-form :disabled="mode === 'view'" :model="form" :rules="rules" label-width="15rem" ref="dialogForm">
|
||||
<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>
|
||||
<el-form-item :label="$t('冻结余额')" prop="frozenBalance">
|
||||
<el-input v-model="form.frozenBalance" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('总收入')" prop="totalIncome">
|
||||
<el-input v-model="form.totalIncome" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('总支出')" prop="totalExpenditure">
|
||||
<el-input v-model="form.totalExpenditure" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者部门编号')" prop="ownerDeptId">
|
||||
<el-input v-model="form.ownerDeptId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者用户编号')" prop="ownerId">
|
||||
<el-input v-model="form.ownerId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('创建时间')" prop="createdTime">
|
||||
<el-input v-model="form.createdTime" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('修改时间')" prop="modifiedTime">
|
||||
<el-input v-model="form.modifiedTime" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('数据版本')" prop="version">
|
||||
<el-input v-model="form.version" :disabled="mode === 'edit'" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('交易流水')" name="trade">
|
||||
<trade v-if="tabId === 'trade'" :ownerId="form.ownerId.toString()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
|
||||
<JsonViewer
|
||||
:expand-depth="5"
|
||||
:theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'dark' : 'light'"
|
||||
:value="form"
|
||||
copyable
|
||||
expanded
|
||||
sort></JsonViewer>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
|
||||
<el-button v-if="mode !== 'view'" :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
|
||||
</template>
|
||||
</scDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
const trade = defineAsyncComponent(() => import('@/views/sys/trade'))
|
||||
export default {
|
||||
components: { trade },
|
||||
data() {
|
||||
return {
|
||||
//表单数据
|
||||
form: {},
|
||||
loading: true,
|
||||
mode: 'add',
|
||||
//验证规则
|
||||
rules: {},
|
||||
tabId: 'basic',
|
||||
titleMap: {
|
||||
add: this.$t('新增钱包'),
|
||||
edit: this.$t('编辑钱包'),
|
||||
view: this.$t('查看钱包'),
|
||||
},
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
emits: ['success', 'closed', 'mounted'],
|
||||
methods: {
|
||||
//显示
|
||||
async open(data) {
|
||||
this.visible = true
|
||||
if (data.mode === 'add') {
|
||||
this.loading = false
|
||||
return this
|
||||
}
|
||||
this.loading = true
|
||||
this.mode = data.mode
|
||||
if (data.row?.id) {
|
||||
const res = await this.$API.sys_userwallet.get.post({ id: data.row.id })
|
||||
if (res.data) {
|
||||
Object.assign(this.form, res.data)
|
||||
this.loading = false
|
||||
return this
|
||||
}
|
||||
}
|
||||
this.$message.error(`未找到该数据`)
|
||||
return this
|
||||
},
|
||||
//表单提交方法
|
||||
async submit() {
|
||||
const valid = await this.$refs.dialogForm.validate().catch(() => {})
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.loading = true
|
||||
//
|
||||
this.loading = false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$emit('mounted')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
91
src/frontend/admin/src/views/sys/wallet/trade.vue
Normal file
91
src/frontend/admin/src/views/sys/wallet/trade.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<scDialog v-model="visible" :title="$t('新建交易')" @closed="$emit('closed')" append-to-body destroy-on-close>
|
||||
<el-form :model="form" :rules="rules" label-position="right" label-width="12rem" ref="dialogForm" style="height: 100%">
|
||||
<el-form-item>
|
||||
<el-descriptions border column="1">
|
||||
<el-descriptions-item :label="$t('用户名')">
|
||||
<b>{{ row.owner.userName }}</b>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('用户编号')">{{ row.owner.id }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('可用余额')">{{ $TOOL.groupSeparator(row.availableBalance) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易类型')" prop="tradeType">
|
||||
<el-select v-model="form.tradeType" clearable filterable>
|
||||
<el-option v-for="(item, i) in $GLOBAL.enums.tradeTypes" :key="i" :label="item[1]" :value="i" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易金额')" prop="amount">
|
||||
<el-input-number v-model="form.amount" :max="999999999" :min="-999999999" precision="0" style="width: 15rem"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('备注')" prop="summary">
|
||||
<el-input v-model="form.summary" rows="3" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
|
||||
<el-button v-if="mode !== 'view'" :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
|
||||
</template>
|
||||
</scDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
//表单数据
|
||||
form: {},
|
||||
row: {},
|
||||
loading: true,
|
||||
|
||||
//验证规则
|
||||
rules: {
|
||||
tradeType: [{ required: true, message: this.$t('请选择交易类型') }],
|
||||
amount: [{ required: true, message: this.$t('请输入交易金额') }],
|
||||
},
|
||||
tabId: '0',
|
||||
titleMap: {
|
||||
add: this.$t('新增用户'),
|
||||
edit: this.$t('编辑用户'),
|
||||
view: this.$t('查看用户'),
|
||||
},
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
emits: ['success', 'closed', 'mounted'],
|
||||
methods: {
|
||||
//显示
|
||||
async open(data) {
|
||||
this.row = data.row
|
||||
this.form.ownerId = data.row.id
|
||||
this.visible = true
|
||||
this.loading = false
|
||||
return this
|
||||
},
|
||||
|
||||
//表单提交方法
|
||||
async submit() {
|
||||
const valid = await this.$refs.dialogForm.validate().catch(() => {})
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await this.$API.sys_wallettrade.create.post(this.form)
|
||||
this.$emit('success', res.data, this.mode)
|
||||
this.visible = false
|
||||
this.$message.success(this.$t('操作成功'))
|
||||
} catch {}
|
||||
this.loading = false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$emit('mounted')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
Reference in New Issue
Block a user