mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-07-04 09:38:15 +08:00
feat: ✨ 营销管理
This commit is contained in:
@ -57,11 +57,11 @@ USDT
|
|||||||
已校验
|
已校验
|
||||||
已读
|
已读
|
||||||
并且
|
并且
|
||||||
|
归属角色
|
||||||
|
归属部门
|
||||||
微信支付
|
微信支付
|
||||||
成功
|
成功
|
||||||
或者
|
或者
|
||||||
归属角色
|
|
||||||
归属部门
|
|
||||||
手机
|
手机
|
||||||
手机号
|
手机号
|
||||||
执行耗时
|
执行耗时
|
||||||
@ -108,6 +108,7 @@ USDT
|
|||||||
用户代理
|
用户代理
|
||||||
用户名
|
用户名
|
||||||
用户导出
|
用户导出
|
||||||
|
用户邀请导出
|
||||||
用户钱包导出
|
用户钱包导出
|
||||||
电子邮箱
|
电子邮箱
|
||||||
男
|
男
|
||||||
|
@ -107,6 +107,27 @@
|
|||||||
"Title": "充值订单",
|
"Title": "充值订单",
|
||||||
"Type": 1
|
"Type": 1
|
||||||
},
|
},
|
||||||
|
// ------------------------------ 营销管理 ------------------------------
|
||||||
|
{
|
||||||
|
"Icon": "el-icon-share",
|
||||||
|
"Id": 692575802241032,
|
||||||
|
"Name": "market",
|
||||||
|
"Path": "/market",
|
||||||
|
"Sort": 98,
|
||||||
|
"Title": "营销管理",
|
||||||
|
"Type": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Component": "sys/invite",
|
||||||
|
"Icon": "el-icon-connection",
|
||||||
|
"Id": 692575802245126,
|
||||||
|
"Name": "sys/invite",
|
||||||
|
"ParentId": 692575802241032,
|
||||||
|
"Path": "/market/invite",
|
||||||
|
"Sort": 100,
|
||||||
|
"Title": "邀请管理",
|
||||||
|
"Type": 1
|
||||||
|
},
|
||||||
// ------------------------------ 系统管理 ------------------------------
|
// ------------------------------ 系统管理 ------------------------------
|
||||||
{
|
{
|
||||||
"Icon": "sc-icon-App",
|
"Icon": "sc-icon-App",
|
||||||
|
@ -102,5 +102,9 @@
|
|||||||
{
|
{
|
||||||
"ApiId": "api/sys/deposit.order/pay.confirm",
|
"ApiId": "api/sys/deposit.order/pay.confirm",
|
||||||
"RoleId": 371729946431493,
|
"RoleId": 371729946431493,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ApiId": "api/sys/user.invite/query",
|
||||||
|
"RoleId": 371729946431493,
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -30,5 +30,13 @@
|
|||||||
{
|
{
|
||||||
"MenuId": 690907673255944,
|
"MenuId": 690907673255944,
|
||||||
"RoleId": 371729946431493
|
"RoleId": 371729946431493
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"MenuId": 692575802241032,
|
||||||
|
"RoleId": 371729946431493
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"MenuId": 692575802245126,
|
||||||
|
"RoleId": 371729946431493
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -3,24 +3,27 @@
|
|||||||
"DeptId": 372119301627909,
|
"DeptId": 372119301627909,
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Id": 370942943322181,
|
"Id": 370942943322181,
|
||||||
|
"InviteCode": "Q09Y8O",
|
||||||
"Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15",
|
"Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15",
|
||||||
"Token": "A9AFD92E-A33D-4152-9A6C-A9C141D24887",
|
"Token": "A9AFD92E-A33D-4152-9A6C-A9C141D24887",
|
||||||
"UserName": "root"
|
"UserName": "root",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DeptId": 372119301627909,
|
"DeptId": 372119301627909,
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Id": 560217289236492,
|
"Id": 560217289236492,
|
||||||
|
"InviteCode": "7ZH5PB",
|
||||||
"Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15",
|
"Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15",
|
||||||
"Token": "4208EA97-B32F-4E39-A290-4C0D27B61EBF",
|
"Token": "4208EA97-B32F-4E39-A290-4C0D27B61EBF",
|
||||||
"UserName": "user"
|
"UserName": "user",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DeptId": 372119301627909,
|
"DeptId": 372119301627909,
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Id": 664362432344581,
|
"Id": 664362432344581,
|
||||||
|
"InviteCode": "47Q56H",
|
||||||
"Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15",
|
"Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15",
|
||||||
"Token": "751D599B-2B8C-417C-9565-CCF2363F5F6F",
|
"Token": "751D599B-2B8C-417C-9565-CCF2363F5F6F",
|
||||||
"UserName": "jobs"
|
"UserName": "jobs",
|
||||||
}
|
}
|
||||||
]
|
]
|
15
assets/seed-data/Sys_UserInvite.json
Normal file
15
assets/seed-data/Sys_UserInvite.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"Id": 370942943322181,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 560217289236492,
|
||||||
|
"OwnerDeptId": 372119301627909,
|
||||||
|
"OwnerId": 370942943322181,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 664362432344581,
|
||||||
|
"OwnerDeptId": 372119301627909,
|
||||||
|
"OwnerId": 370942943322181,
|
||||||
|
}
|
||||||
|
]
|
@ -71,6 +71,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
|
|||||||
/// <param name="whereExp">查询表达式</param>
|
/// <param name="whereExp">查询表达式</param>
|
||||||
/// <param name="whereSql">查询sql</param>
|
/// <param name="whereSql">查询sql</param>
|
||||||
/// <param name="ignoreVersion">是否忽略版本锁</param>
|
/// <param name="ignoreVersion">是否忽略版本锁</param>
|
||||||
|
/// <param name="disableGlobalDataFilter">是否忽略全局数据权限过滤</param>
|
||||||
/// <returns>更新行数</returns>
|
/// <returns>更新行数</returns>
|
||||||
protected Task<int> UpdateAsync( //
|
protected Task<int> UpdateAsync( //
|
||||||
TEntity newValue //
|
TEntity newValue //
|
||||||
@ -78,11 +79,15 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
|
|||||||
, List<string> excludeFields = null //
|
, List<string> excludeFields = null //
|
||||||
, Expression<Func<TEntity, bool>> whereExp = null //
|
, Expression<Func<TEntity, bool>> whereExp = null //
|
||||||
, string whereSql = null //
|
, string whereSql = null //
|
||||||
, bool ignoreVersion = false)
|
, bool ignoreVersion = false, bool disableGlobalDataFilter = false)
|
||||||
{
|
{
|
||||||
// 默认匹配主键
|
// 默认匹配主键
|
||||||
whereExp ??= a => a.Id.Equals(newValue.Id);
|
whereExp ??= a => a.Id.Equals(newValue.Id);
|
||||||
var update = BuildUpdate(newValue, includeFields, excludeFields, ignoreVersion).Where(whereExp).Where(whereSql);
|
var update = BuildUpdate(newValue, includeFields, excludeFields, ignoreVersion).Where(whereExp).Where(whereSql);
|
||||||
|
if (disableGlobalDataFilter) {
|
||||||
|
update = update.DisableGlobalFilter(nameof(Chars.FLG_FREE_SQL_GLOBAL_FILTER_DATA));
|
||||||
|
}
|
||||||
|
|
||||||
return update.ExecuteEffectsAsync();
|
return update.ExecuteEffectsAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,8 +106,8 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
|
|||||||
TEntity newValue //
|
TEntity newValue //
|
||||||
, List<string> includeFields = null //
|
, List<string> includeFields = null //
|
||||||
, List<string> excludeFields = null //
|
, List<string> excludeFields = null //
|
||||||
, Expression<Func<TEntity, bool>> whereExp = null //
|
, Expression<Func<TEntity, bool>> whereExp = null //
|
||||||
, string whereSql = null //
|
, string whereSql = null //
|
||||||
, bool ignoreVersion = false)
|
, bool ignoreVersion = false)
|
||||||
{
|
{
|
||||||
// 默认匹配主键
|
// 默认匹配主键
|
||||||
|
@ -22,6 +22,22 @@ public record Sys_Config : VersionEntity, IFieldEnabled
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public virtual bool Enabled { get; init; }
|
public virtual bool Enabled { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 必须邀请注册
|
||||||
|
/// </summary>
|
||||||
|
[Column]
|
||||||
|
[CsvIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
public virtual bool RegisterInviteRequired { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 必须手机号注册
|
||||||
|
/// </summary>
|
||||||
|
[Column]
|
||||||
|
[CsvIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
public virtual bool RegisterMobileRequired { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Trc20收款地址
|
/// Trc20收款地址
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -5,9 +5,10 @@ namespace NetAdmin.Domain.DbMaps.Sys;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户基本信息表
|
/// 用户基本信息表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(Email), nameof(Email), true)]
|
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(Email), nameof(Email), true)]
|
||||||
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(Mobile), nameof(Mobile), true)]
|
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(Mobile), nameof(Mobile), true)]
|
||||||
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(UserName), nameof(UserName), true)]
|
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(InviteCode), nameof(InviteCode), true)]
|
||||||
|
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(UserName), nameof(UserName), true)]
|
||||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_User))]
|
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_User))]
|
||||||
public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister
|
public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister
|
||||||
{
|
{
|
||||||
@ -51,6 +52,21 @@ public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public virtual bool Enabled { get; init; }
|
public virtual bool Enabled { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户邀请
|
||||||
|
/// </summary>
|
||||||
|
[CsvIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
public Sys_UserInvite Invite { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邀请码
|
||||||
|
/// </summary>
|
||||||
|
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_CHAR_6)]
|
||||||
|
[CsvIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
public virtual string InviteCode { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 最后登录时间
|
/// 最后登录时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
namespace NetAdmin.Domain.DbMaps.Sys;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户邀请表
|
||||||
|
/// </summary>
|
||||||
|
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_UserInvite))]
|
||||||
|
public record Sys_UserInvite : VersionEntity, IFieldOwner
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 子节点
|
||||||
|
/// </summary>
|
||||||
|
[CsvIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
[Navigate(nameof(OwnerId))]
|
||||||
|
public IEnumerable<Sys_UserInvite> Children { 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>
|
||||||
|
[CsvIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
[Navigate(nameof(Id))]
|
||||||
|
public Sys_User User { get; init; }
|
||||||
|
}
|
@ -14,6 +14,14 @@ public record CreateConfigReq : Sys_Config
|
|||||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
public override bool Enabled { get; init; }
|
public override bool Enabled { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sys_Config.RegisterInviteRequired" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
|
public override bool RegisterInviteRequired { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sys_Config.RegisterMobileRequired" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
|
public override bool RegisterMobileRequired { get; init; }
|
||||||
|
|
||||||
/// <inheritdoc cref="Sys_Config.Trc20ReceiptAddress" />
|
/// <inheritdoc cref="Sys_Config.Trc20ReceiptAddress" />
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
[Length(34, 34)]
|
[Length(34, 34)]
|
||||||
|
@ -24,6 +24,14 @@ public record QueryConfigRsp : Sys_Config
|
|||||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
public override long Id { get; init; }
|
public override long Id { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sys_Config.RegisterInviteRequired" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
|
public override bool RegisterInviteRequired { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sys_Config.RegisterMobileRequired" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
|
public override bool RegisterMobileRequired { get; init; }
|
||||||
|
|
||||||
/// <inheritdoc cref="Sys_Config.Trc20ReceiptAddress" />
|
/// <inheritdoc cref="Sys_Config.Trc20ReceiptAddress" />
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public override string Trc20ReceiptAddress { get; init; }
|
public override string Trc20ReceiptAddress { get; init; }
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
namespace NetAdmin.Domain.Dto.Sys.User;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求:检查邀请码是否正确
|
||||||
|
/// </summary>
|
||||||
|
public sealed record CheckInviterAvailableReq : DataAbstraction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 邀请码
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public string Code { get; init; }
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
using NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
using NetAdmin.Domain.Dto.Sys.UserProfile;
|
using NetAdmin.Domain.Dto.Sys.UserProfile;
|
||||||
|
|
||||||
namespace NetAdmin.Domain.Dto.Sys.User;
|
namespace NetAdmin.Domain.Dto.Sys.User;
|
||||||
@ -7,6 +8,15 @@ namespace NetAdmin.Domain.Dto.Sys.User;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record CreateUserReq : CreateEditUserReq
|
public sealed record CreateUserReq : CreateEditUserReq
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Enabled { get; init; } = true;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sys_User.Invite" />
|
||||||
|
public new CreateUserInviteReq Invite { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string InviteCode { get; init; } = ((long)new[] { 60466176, int.MaxValue }.Rand()).Sys36().ToUpperInvariant();
|
||||||
|
|
||||||
/// <inheritdoc cref="CreateEditUserReq.PasswordText" />
|
/// <inheritdoc cref="CreateEditUserReq.PasswordText" />
|
||||||
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.密码不能为空))]
|
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.密码不能为空))]
|
||||||
public override string PasswordText { get; init; }
|
public override string PasswordText { get; init; }
|
||||||
@ -18,6 +28,7 @@ public sealed record CreateUserReq : CreateEditUserReq
|
|||||||
public override void Register(TypeAdapterConfig config)
|
public override void Register(TypeAdapterConfig config)
|
||||||
{
|
{
|
||||||
_ = config.ForType<RegisterUserReq, CreateUserReq>() //
|
_ = config.ForType<RegisterUserReq, CreateUserReq>() //
|
||||||
|
.Ignore(a => a.InviteCode)
|
||||||
.Map(d => d.Mobile, s => s.VerifySmsCodeReq.DestDevice)
|
.Map(d => d.Mobile, s => s.VerifySmsCodeReq.DestDevice)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -39,6 +39,10 @@ public record QueryUserRsp : Sys_User
|
|||||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
public override long Id { get; init; }
|
public override long Id { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sys_User.InviteCode" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public override string InviteCode { get; init; }
|
||||||
|
|
||||||
/// <inheritdoc cref="Sys_User.LastLoginTime" />
|
/// <inheritdoc cref="Sys_User.LastLoginTime" />
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public override DateTime? LastLoginTime { get; init; }
|
public override DateTime? LastLoginTime { get; init; }
|
||||||
|
@ -7,6 +7,11 @@ namespace NetAdmin.Domain.Dto.Sys.User;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record RegisterUserReq : Sys_User
|
public sealed record RegisterUserReq : Sys_User
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 邀请者的邀请码
|
||||||
|
/// </summary>
|
||||||
|
public string Inviter { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 密码
|
/// 密码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求:创建用户邀请
|
||||||
|
/// </summary>
|
||||||
|
public record CreateUserInviteReq : Sys_UserInvite;
|
@ -0,0 +1,15 @@
|
|||||||
|
namespace NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求:编辑用户邀请
|
||||||
|
/// </summary>
|
||||||
|
public record EditUserInviteReq : CreateUserInviteReq
|
||||||
|
{
|
||||||
|
/// <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,11 @@
|
|||||||
|
namespace NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求:查询用户邀请
|
||||||
|
/// </summary>
|
||||||
|
public sealed record QueryUserInviteReq : Sys_UserInvite
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
|
public override long Id { get; init; }
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
using NetAdmin.Domain.Dto.Sys.User;
|
||||||
|
|
||||||
|
namespace NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 响应:查询用户邀请
|
||||||
|
/// </summary>
|
||||||
|
public record QueryUserInviteRsp : Sys_UserInvite
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="Sys_UserInvite.Children" />
|
||||||
|
public new virtual IEnumerable<QueryUserInviteRsp> Children { 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="IFieldModifiedTime.ModifiedTime" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public override DateTime? ModifiedTime { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFieldModifiedUser.ModifiedUserId" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public override long? ModifiedUserId { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFieldModifiedUser.ModifiedUserName" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public override string ModifiedUserName { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sys_UserInvite.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="Sys_UserInvite.User" />
|
||||||
|
[CsvIgnore]
|
||||||
|
public new virtual QueryUserRsp User { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFieldVersion.Version" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
|
public override long Version { get; init; }
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
using NetAdmin.Domain.DbMaps.Sys;
|
|
||||||
|
|
||||||
namespace NetAdmin.Domain.Dto.Sys.UserWallet;
|
namespace NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using NetAdmin.Domain.DbMaps.Sys;
|
|
||||||
|
|
||||||
namespace NetAdmin.Domain.Dto.Sys.WalletTrade;
|
namespace NetAdmin.Domain.Dto.Sys.WalletTrade;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -46,7 +46,7 @@ public sealed class CollectionJsonTypeInfoResolver : DefaultJsonTypeInfoResolver
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static PropertyInfo GetNewProperty(string memberName, object obj)
|
private static PropertyInfo GetNewProperty(string memberName, object obj)
|
||||||
{
|
{
|
||||||
return obj.GetType().GetProperties().Where(x => x.Name == memberName).First(x => x.DeclaringType == x.ReflectedType);
|
return obj.GetType().GetProperties().Where(x => x.Name == memberName).FirstOrDefault(x => x.DeclaringType == x.ReflectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -61,7 +61,7 @@ public sealed class CollectionJsonTypeInfoResolver : DefaultJsonTypeInfoResolver
|
|||||||
}
|
}
|
||||||
catch (AmbiguousMatchException) {
|
catch (AmbiguousMatchException) {
|
||||||
// 这里处理子类new隐藏父类同名属性, 取得多个同名属性的问题
|
// 这里处理子类new隐藏父类同名属性, 取得多个同名属性的问题
|
||||||
prop = GetNewProperty(memberName, obj).GetValue(obj);
|
prop = GetNewProperty(memberName, obj)?.GetValue(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
return prop switch { string => prop, ICollection { Count: > 0 } => prop, _ => null };
|
return prop switch { string => prop, ICollection { Count: > 0 } => prop, _ => null };
|
||||||
@ -79,7 +79,7 @@ public sealed class CollectionJsonTypeInfoResolver : DefaultJsonTypeInfoResolver
|
|||||||
obj.GetType().GetProperty(memberName!)?.SetValue(obj, val);
|
obj.GetType().GetProperty(memberName!)?.SetValue(obj, val);
|
||||||
}
|
}
|
||||||
catch (AmbiguousMatchException) {
|
catch (AmbiguousMatchException) {
|
||||||
GetNewProperty(memberName, obj).SetValue(obj, val);
|
GetNewProperty(memberName, obj)?.SetValue(obj, val);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ public static class Chars
|
|||||||
public const string FLG_DB_EXCEPTION_PRIMARY_KEY_CONFLICT = "PRIMARY KEY";
|
public const string FLG_DB_EXCEPTION_PRIMARY_KEY_CONFLICT = "PRIMARY KEY";
|
||||||
public const string FLG_DB_EXCEPTION_UNIQUE_CONSTRAINT_CONFLICT = "UNIQUE constraint";
|
public const string FLG_DB_EXCEPTION_UNIQUE_CONSTRAINT_CONFLICT = "UNIQUE constraint";
|
||||||
public const string FLG_DB_FIELD_TYPE_CHAR_34 = "char(34)";
|
public const string FLG_DB_FIELD_TYPE_CHAR_34 = "char(34)";
|
||||||
|
public const string FLG_DB_FIELD_TYPE_CHAR_6 = "char(6)";
|
||||||
public const string FLG_DB_FIELD_TYPE_NVARCHAR = "nvarchar";
|
public const string FLG_DB_FIELD_TYPE_NVARCHAR = "nvarchar";
|
||||||
public const string FLG_DB_FIELD_TYPE_NVARCHAR_1022 = "nvarchar(1022)";
|
public const string FLG_DB_FIELD_TYPE_NVARCHAR_1022 = "nvarchar(1022)";
|
||||||
public const string FLG_DB_FIELD_TYPE_NVARCHAR_127 = "nvarchar(127)";
|
public const string FLG_DB_FIELD_TYPE_NVARCHAR_127 = "nvarchar(127)";
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<PackageReference Include="Gurion" Version="1.2.15" Label="refs"/>
|
<PackageReference Include="Gurion" Version="1.2.15" Label="refs"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.6"/>
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.6"/>
|
||||||
<PackageReference Include="Minio" Version="6.0.5"/>
|
<PackageReference Include="Minio" Version="6.0.5"/>
|
||||||
<PackageReference Include="NSExt" Version="2.3.6"/>
|
<PackageReference Include="NSExt" Version="2.3.7"/>
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.6"/>
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.6"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -16,6 +16,11 @@ public interface IConfigModule : ICrudModule<CreateConfigReq, QueryConfigRsp //
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Task<QueryConfigRsp> GetLatestConfigAsync();
|
Task<QueryConfigRsp> GetLatestConfigAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取注册配置
|
||||||
|
/// </summary>
|
||||||
|
Task<QueryConfigRsp> GetRegisterConfigAsync();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置配置启用状态
|
/// 设置配置启用状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
using NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
|
|
||||||
|
namespace NetAdmin.SysComponent.Application.Modules.Sys;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户邀请模块
|
||||||
|
/// </summary>
|
||||||
|
public interface IUserInviteModule : ICrudModule<CreateUserInviteReq, QueryUserInviteRsp // 创建类型
|
||||||
|
, EditUserInviteReq // 编辑类型
|
||||||
|
, QueryUserInviteReq, QueryUserInviteRsp // 查询类型
|
||||||
|
, DelReq // 删除类型
|
||||||
|
>;
|
@ -12,6 +12,11 @@ public partial interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp /
|
|||||||
, DelReq // 删除类型
|
, DelReq // 删除类型
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 检查邀请码是否正确
|
||||||
|
/// </summary>
|
||||||
|
Task<bool> CheckInviterAvailableAsync(CheckInviterAvailableReq req);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查手机号码是否可用
|
/// 检查手机号码是否可用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -92,6 +92,16 @@ public sealed class ConfigService(BasicRepository<Sys_Config, long> rpo) //
|
|||||||
return ret.FirstOrDefault();
|
return ret.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<QueryConfigRsp> GetRegisterConfigAsync()
|
||||||
|
{
|
||||||
|
var latestConfigAsync = await GetLatestConfigAsync().ConfigureAwait(false);
|
||||||
|
return new QueryConfigRsp {
|
||||||
|
RegisterInviteRequired = latestConfigAsync.RegisterInviteRequired
|
||||||
|
, RegisterMobileRequired = latestConfigAsync.RegisterMobileRequired
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<PagedQueryRsp<QueryConfigRsp>> PagedQueryAsync(PagedQueryReq<QueryConfigReq> req)
|
public async Task<PagedQueryRsp<QueryConfigRsp>> PagedQueryAsync(PagedQueryReq<QueryConfigReq> req)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户邀请服务
|
||||||
|
/// </summary>
|
||||||
|
public interface IUserInviteService : IService, IUserInviteModule;
|
@ -0,0 +1,130 @@
|
|||||||
|
using NetAdmin.Domain.DbMaps.Sys;
|
||||||
|
using NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
|
using NetAdmin.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace NetAdmin.SysComponent.Application.Services.Sys;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IUserInviteService" />
|
||||||
|
public sealed class UserInviteService(BasicRepository<Sys_UserInvite, long> rpo) //
|
||||||
|
: RepositoryService<Sys_UserInvite, long, IUserInviteService>(rpo), IUserInviteService
|
||||||
|
{
|
||||||
|
/// <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<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
return QueryInternal(req).WithNoLockNoWait().CountAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = await QueryInternal(req with { Order = Orders.None })
|
||||||
|
.WithNoLockNoWait()
|
||||||
|
.GroupBy(req.GetToListExp<Sys_UserInvite>())
|
||||||
|
.ToDictionaryAsync(a => a.Count())
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
return ret.Select(x => new KeyValuePair<IImmutableDictionary<string, string>, int>(
|
||||||
|
req.RequiredFields.ToImmutableDictionary(
|
||||||
|
y => y, y => typeof(Sys_UserInvite).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<QueryUserInviteRsp> CreateAsync(CreateUserInviteReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
|
||||||
|
return ret.Adapt<QueryUserInviteRsp>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<int> DeleteAsync(DelReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
return Rpo.DeleteAsync(a => a.Id == req.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<QueryUserInviteRsp> EditAsync(EditUserInviteReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
#if DBTYPE_SQLSERVER
|
||||||
|
return (await UpdateReturnListAsync(req).ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryUserInviteRsp>();
|
||||||
|
#else
|
||||||
|
return await UpdateAsync(req).ConfigureAwait(false) > 0 ? await GetAsync(new QueryUserInviteReq { Id = req.Id }).ConfigureAwait(false) : null;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<IActionResult> ExportAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
return ExportAsync<QueryUserInviteReq, QueryUserInviteRsp>(QueryInternal, req, Ln.用户邀请导出);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<QueryUserInviteRsp> GetAsync(QueryUserInviteReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = await QueryInternal(new QueryReq<QueryUserInviteReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
|
||||||
|
return ret.Adapt<QueryUserInviteRsp>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<PagedQueryRsp<QueryUserInviteRsp>> PagedQueryAsync(PagedQueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var list = await QueryInternal(req)
|
||||||
|
.Page(req.Page, req.PageSize)
|
||||||
|
.WithNoLockNoWait()
|
||||||
|
.Count(out var total)
|
||||||
|
.ToListAsync(req)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return new PagedQueryRsp<QueryUserInviteRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryUserInviteRsp>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IEnumerable<QueryUserInviteRsp>> QueryAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = await QueryInternal(req).Include(a => a.Owner).Include(a => a.User).WithNoLockNoWait().ToTreeListAsync().ConfigureAwait(false);
|
||||||
|
return ret.Adapt<IEnumerable<QueryUserInviteRsp>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISelect<Sys_UserInvite> QueryInternal(QueryReq<QueryUserInviteReq> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ using NetAdmin.Domain.Attributes.DataValidation;
|
|||||||
using NetAdmin.Domain.Contexts;
|
using NetAdmin.Domain.Contexts;
|
||||||
using NetAdmin.Domain.DbMaps.Sys;
|
using NetAdmin.Domain.DbMaps.Sys;
|
||||||
using NetAdmin.Domain.Dto.Sys.User;
|
using NetAdmin.Domain.Dto.Sys.User;
|
||||||
|
using NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
using NetAdmin.Domain.Dto.Sys.UserProfile;
|
using NetAdmin.Domain.Dto.Sys.UserProfile;
|
||||||
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
using NetAdmin.Domain.Dto.Sys.UserWallet;
|
||||||
using NetAdmin.Domain.Dto.Sys.VerifyCode;
|
using NetAdmin.Domain.Dto.Sys.VerifyCode;
|
||||||
@ -14,6 +15,7 @@ namespace NetAdmin.SysComponent.Application.Services.Sys;
|
|||||||
public sealed class UserService(
|
public sealed class UserService(
|
||||||
BasicRepository<Sys_User, long> rpo //
|
BasicRepository<Sys_User, long> rpo //
|
||||||
, IUserProfileService userProfileService //
|
, IUserProfileService userProfileService //
|
||||||
|
, IUserInviteService userInviteService //
|
||||||
, IUserWalletService userWalletService //
|
, IUserWalletService userWalletService //
|
||||||
, IVerifyCodeService verifyCodeService //
|
, IVerifyCodeService verifyCodeService //
|
||||||
, IEventPublisher eventPublisher) //
|
, IEventPublisher eventPublisher) //
|
||||||
@ -52,6 +54,13 @@ public sealed class UserService(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<bool> CheckInviterAvailableAsync(CheckInviterAvailableReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
return Rpo.Select.Where(a => a.InviteCode == req.Code && a.Enabled).AnyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req)
|
public async Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req)
|
||||||
{
|
{
|
||||||
@ -116,14 +125,14 @@ public sealed class UserService(
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
// 钱包表
|
// 钱包表
|
||||||
_ = await userWalletService.CreateAsync(new CreateUserWalletReq() with //
|
_ = await userWalletService.CreateAsync(new CreateUserWalletReq { Id = dbUser.Id, OwnerId = dbUser.Id, OwnerDeptId = dbUser.DeptId })
|
||||||
{
|
|
||||||
Id = dbUser.Id, OwnerId = dbUser.Id, OwnerDeptId = dbUser.DeptId
|
|
||||||
})
|
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
var userList = await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = dbUser.Id } }).ConfigureAwait(false);
|
var userList = await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = dbUser.Id } }).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// 邀请表
|
||||||
|
_ = await userInviteService.CreateAsync((req.Invite ?? new CreateUserInviteReq()) with { Id = dbUser.Id }).ConfigureAwait(false);
|
||||||
|
|
||||||
// 发布用户创建事件
|
// 发布用户创建事件
|
||||||
var ret = userList.First();
|
var ret = userList.First();
|
||||||
await eventPublisher.PublishAsync(new UserCreatedEvent(ret.Adapt<UserInfoRsp>())).ConfigureAwait(false);
|
await eventPublisher.PublishAsync(new UserCreatedEvent(ret.Adapt<UserInfoRsp>())).ConfigureAwait(false);
|
||||||
@ -295,12 +304,34 @@ public sealed class UserService(
|
|||||||
public async Task<UserInfoRsp> RegisterAsync(RegisterUserReq req)
|
public async Task<UserInfoRsp> RegisterAsync(RegisterUserReq req)
|
||||||
{
|
{
|
||||||
req.ThrowIfInvalid();
|
req.ThrowIfInvalid();
|
||||||
if (!await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) {
|
var config = await S<IConfigService>().GetLatestConfigAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (config.RegisterMobileRequired && !await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) {
|
||||||
throw new NetAdminInvalidOperationException(Ln.验证码不正确);
|
throw new NetAdminInvalidOperationException(Ln.验证码不正确);
|
||||||
}
|
}
|
||||||
|
|
||||||
var createReq = req.Adapt<CreateUserReq>() with { Profile = new CreateUserProfileReq() };
|
long? inviterId = null;
|
||||||
return (await CreateAsync(createReq).ConfigureAwait(false)).Adapt<UserInfoRsp>();
|
long? inviterDeptId = null;
|
||||||
|
if (!req.Inviter.NullOrEmpty() || config.RegisterInviteRequired) {
|
||||||
|
if (req.Inviter.NullOrEmpty()) {
|
||||||
|
throw new NetAdminInvalidOperationException(Ln.邀请码不正确);
|
||||||
|
}
|
||||||
|
|
||||||
|
var inviter = await GetAsync(new QueryUserReq { InviteCode = req.Inviter }).ConfigureAwait(false);
|
||||||
|
if (inviter?.Enabled != true) {
|
||||||
|
throw new NetAdminInvalidOperationException(Ln.邀请码不正确);
|
||||||
|
}
|
||||||
|
|
||||||
|
inviterId = inviter.Id;
|
||||||
|
inviterDeptId = inviter.DeptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var createReq = req.Adapt<CreateUserReq>() with {
|
||||||
|
Profile = new CreateUserProfileReq()
|
||||||
|
, Invite = new CreateUserInviteReq { OwnerId = inviterId, OwnerDeptId = inviterDeptId }
|
||||||
|
};
|
||||||
|
return (await CreateAsync(createReq with { Mobile = config.RegisterMobileRequired ? createReq.Mobile : null }).ConfigureAwait(false))
|
||||||
|
.Adapt<UserInfoRsp>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -505,6 +536,8 @@ public sealed class UserService(
|
|||||||
.WhereIf(deptIds != null, a => deptIds.Contains(a.DeptId))
|
.WhereIf(deptIds != null, a => deptIds.Contains(a.DeptId))
|
||||||
.WhereIf( //
|
.WhereIf( //
|
||||||
req.Filter?.Id > 0, a => a.Id == req.Filter.Id)
|
req.Filter?.Id > 0, a => a.Id == req.Filter.Id)
|
||||||
|
.WhereIf( //
|
||||||
|
req.Filter?.InviteCode?.Length > 0, a => a.InviteCode == req.Filter.InviteCode)
|
||||||
.WhereIf( //
|
.WhereIf( //
|
||||||
req.Filter?.RoleId > 0, a => Enumerable.Any(a.Roles, b => b.Id == req.Filter.RoleId))
|
req.Filter?.RoleId > 0, a => Enumerable.Any(a.Roles, b => b.Id == req.Filter.RoleId))
|
||||||
.WhereIf( //
|
.WhereIf( //
|
||||||
|
@ -60,6 +60,12 @@ public sealed class ConfigCache(IDistributedCache cache, IConfigService service)
|
|||||||
return Service.GetLatestConfigAsync();
|
return Service.GetLatestConfigAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<QueryConfigRsp> GetRegisterConfigAsync()
|
||||||
|
{
|
||||||
|
return Service.GetRegisterConfigAsync();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<PagedQueryRsp<QueryConfigRsp>> PagedQueryAsync(PagedQueryReq<QueryConfigReq> req)
|
public Task<PagedQueryRsp<QueryConfigRsp>> PagedQueryAsync(PagedQueryReq<QueryConfigReq> req)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户邀请缓存
|
||||||
|
/// </summary>
|
||||||
|
public interface IUserInviteCache : ICache<IDistributedCache, IUserInviteService>, IUserInviteModule;
|
@ -13,6 +13,12 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
|
|||||||
return Service.BulkDeleteAsync(req);
|
return Service.BulkDeleteAsync(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<bool> CheckInviterAvailableAsync(CheckInviterAvailableReq req)
|
||||||
|
{
|
||||||
|
return Service.CheckInviterAvailableAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req)
|
public Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
using NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
|
|
||||||
|
namespace NetAdmin.SysComponent.Cache.Sys;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IUserInviteCache" />
|
||||||
|
public sealed class UserInviteCache(IDistributedCache cache, IUserInviteService service)
|
||||||
|
: DistributedCache<IUserInviteService>(cache, service), IScoped, IUserInviteCache
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||||
|
{
|
||||||
|
return Service.BulkDeleteAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<long> CountAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Service.CountAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Service.CountByAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<QueryUserInviteRsp> CreateAsync(CreateUserInviteReq req)
|
||||||
|
{
|
||||||
|
return Service.CreateAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<int> DeleteAsync(DelReq req)
|
||||||
|
{
|
||||||
|
return Service.DeleteAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<QueryUserInviteRsp> EditAsync(EditUserInviteReq req)
|
||||||
|
{
|
||||||
|
return Service.EditAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<IActionResult> ExportAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Service.ExportAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<QueryUserInviteRsp> GetAsync(QueryUserInviteReq req)
|
||||||
|
{
|
||||||
|
return Service.GetAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<PagedQueryRsp<QueryUserInviteRsp>> PagedQueryAsync(PagedQueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Service.PagedQueryAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<IEnumerable<QueryUserInviteRsp>> QueryAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Service.QueryAsync(req);
|
||||||
|
}
|
||||||
|
}
|
@ -85,6 +85,15 @@ public sealed class ConfigController(IConfigCache cache) : ControllerBase<IConfi
|
|||||||
return Cache.GetLatestConfigAsync();
|
return Cache.GetLatestConfigAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取注册配置
|
||||||
|
/// </summary>
|
||||||
|
[AllowAnonymous]
|
||||||
|
public Task<QueryConfigRsp> GetRegisterConfigAsync()
|
||||||
|
{
|
||||||
|
return Cache.GetRegisterConfigAsync();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分页查询配置
|
/// 分页查询配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -20,6 +20,15 @@ public sealed class UserController(IUserCache cache, IConfigCache configCache) :
|
|||||||
return Cache.BulkDeleteAsync(req);
|
return Cache.BulkDeleteAsync(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查邀请码是否正确
|
||||||
|
/// </summary>
|
||||||
|
[AllowAnonymous]
|
||||||
|
public Task<bool> CheckInviterAvailableAsync(CheckInviterAvailableReq req)
|
||||||
|
{
|
||||||
|
return Cache.CheckInviterAvailableAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查手机号码是否可用
|
/// 检查手机号码是否可用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
using NetAdmin.Domain.Dto.Sys.UserInvite;
|
||||||
|
|
||||||
|
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 UserInviteController(IUserInviteCache cache) : ControllerBase<IUserInviteCache, IUserInviteService>(cache), IUserInviteModule
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 批量删除用户邀请
|
||||||
|
/// </summary>
|
||||||
|
[Transaction]
|
||||||
|
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||||
|
{
|
||||||
|
return Cache.BulkDeleteAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户邀请计数
|
||||||
|
/// </summary>
|
||||||
|
public Task<long> CountAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Cache.CountAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户邀请分组计数
|
||||||
|
/// </summary>
|
||||||
|
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Cache.CountByAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建用户邀请
|
||||||
|
/// </summary>
|
||||||
|
[Transaction]
|
||||||
|
public Task<QueryUserInviteRsp> CreateAsync(CreateUserInviteReq req)
|
||||||
|
{
|
||||||
|
return Cache.CreateAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除用户邀请
|
||||||
|
/// </summary>
|
||||||
|
[Transaction]
|
||||||
|
public Task<int> DeleteAsync(DelReq req)
|
||||||
|
{
|
||||||
|
return Cache.DeleteAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑用户邀请
|
||||||
|
/// </summary>
|
||||||
|
[Transaction]
|
||||||
|
public Task<QueryUserInviteRsp> EditAsync(EditUserInviteReq req)
|
||||||
|
{
|
||||||
|
return Cache.EditAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导出用户邀请
|
||||||
|
/// </summary>
|
||||||
|
[NonAction]
|
||||||
|
public Task<IActionResult> ExportAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Cache.ExportAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取单个用户邀请
|
||||||
|
/// </summary>
|
||||||
|
public Task<QueryUserInviteRsp> GetAsync(QueryUserInviteReq req)
|
||||||
|
{
|
||||||
|
return Cache.GetAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分页查询用户邀请
|
||||||
|
/// </summary>
|
||||||
|
public Task<PagedQueryRsp<QueryUserInviteRsp>> PagedQueryAsync(PagedQueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Cache.PagedQueryAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询用户邀请
|
||||||
|
/// </summary>
|
||||||
|
public Task<IEnumerable<QueryUserInviteRsp>> QueryAsync(QueryReq<QueryUserInviteReq> req)
|
||||||
|
{
|
||||||
|
return Cache.QueryAsync(req);
|
||||||
|
}
|
||||||
|
}
|
@ -99,6 +99,14 @@ public class ConfigTests(WebTestApplicationFactory<Startup> factory, ITestOutput
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<QueryConfigRsp> GetRegisterConfigAsync()
|
||||||
|
{
|
||||||
|
var rsp = await PostJsonAsync(typeof(ConfigController));
|
||||||
|
Assert.True(rsp.IsSuccessStatusCode);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[InlineData(null)]
|
[InlineData(null)]
|
||||||
[Theory]
|
[Theory]
|
||||||
|
@ -24,6 +24,17 @@ public class UserTests(WebTestApplicationFactory<Startup> factory, ITestOutputHe
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[InlineData(null)]
|
||||||
|
[Theory]
|
||||||
|
[TestPriority(100200)]
|
||||||
|
public async Task<bool> CheckInviterAvailableAsync(CheckInviterAvailableReq req)
|
||||||
|
{
|
||||||
|
var rsp = await PostJsonAsync(typeof(UserController), new CheckInviterAvailableReq { Code = "111111" });
|
||||||
|
Assert.True(rsp.IsSuccessStatusCode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[InlineData(null)]
|
[InlineData(null)]
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -195,6 +206,17 @@ public class UserTests(WebTestApplicationFactory<Startup> factory, ITestOutputHe
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[InlineData(null)]
|
||||||
|
[Theory]
|
||||||
|
[TestPriority(101600)]
|
||||||
|
public async Task<IEnumerable<QueryUserRsp>> QueryRelationAsync(QueryReq<QueryUserReq> req)
|
||||||
|
{
|
||||||
|
var rsp = await PostJsonAsync(typeof(UserController), req);
|
||||||
|
Assert.True(rsp.IsSuccessStatusCode);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[InlineData(null)]
|
[InlineData(null)]
|
||||||
[Theory]
|
[Theory]
|
||||||
|
@ -104,6 +104,17 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注册配置
|
||||||
|
*/
|
||||||
|
getRegisterConfig: {
|
||||||
|
url: `${config.API_URL}/api/sys/config/get.register.config`,
|
||||||
|
name: `获取注册配置`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询配置
|
* 分页查询配置
|
||||||
*/
|
*/
|
||||||
|
@ -114,4 +114,15 @@ export default {
|
|||||||
return await http.post(this.url, data, config)
|
return await http.post(this.url, data, config)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 到账确认
|
||||||
|
*/
|
||||||
|
receivedConfirmation: {
|
||||||
|
url: `${config.API_URL}/api/sys/deposit.order/received.confirmation`,
|
||||||
|
name: `到账确认`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
@ -5,6 +5,17 @@
|
|||||||
import config from '@/config'
|
import config from '@/config'
|
||||||
import http from '@/utils/request'
|
import http from '@/utils/request'
|
||||||
export default {
|
export default {
|
||||||
|
/**
|
||||||
|
* 检查邀请码是否正确
|
||||||
|
*/
|
||||||
|
checkInviterAvailable: {
|
||||||
|
url: `${config.API_URL}/api/sys/user/check.inviter.available`,
|
||||||
|
name: `检查邀请码是否正确`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查手机号码是否可用
|
* 检查手机号码是否可用
|
||||||
*/
|
*/
|
||||||
|
106
src/frontend/admin/src/api/sys/userinvite.js
Normal file
106
src/frontend/admin/src/api/sys/userinvite.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* 用户邀请服务
|
||||||
|
* @module @/api/sys/user.invite
|
||||||
|
*/
|
||||||
|
import config from '@/config'
|
||||||
|
import http from '@/utils/request'
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 批量删除用户邀请
|
||||||
|
*/
|
||||||
|
bulkDelete: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/bulk.delete`,
|
||||||
|
name: `批量删除用户邀请`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户邀请计数
|
||||||
|
*/
|
||||||
|
count: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/count`,
|
||||||
|
name: `用户邀请计数`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户邀请分组计数
|
||||||
|
*/
|
||||||
|
countBy: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/count.by`,
|
||||||
|
name: `用户邀请分组计数`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户邀请
|
||||||
|
*/
|
||||||
|
create: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/create`,
|
||||||
|
name: `创建用户邀请`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除用户邀请
|
||||||
|
*/
|
||||||
|
delete: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/delete`,
|
||||||
|
name: `删除用户邀请`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑用户邀请
|
||||||
|
*/
|
||||||
|
edit: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/edit`,
|
||||||
|
name: `编辑用户邀请`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单个用户邀请
|
||||||
|
*/
|
||||||
|
get: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/get`,
|
||||||
|
name: `获取单个用户邀请`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询用户邀请
|
||||||
|
*/
|
||||||
|
pagedQuery: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/paged.query`,
|
||||||
|
name: `分页查询用户邀请`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用户邀请
|
||||||
|
*/
|
||||||
|
query: {
|
||||||
|
url: `${config.API_URL}/api/sys/user.invite/query`,
|
||||||
|
name: `查询用户邀请`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
@ -193,7 +193,7 @@ export default {
|
|||||||
用于登录系统: 'Used to log in to the system',
|
用于登录系统: 'Used to log in to the system',
|
||||||
用户代理: 'User agent',
|
用户代理: 'User agent',
|
||||||
用户名: 'Username',
|
用户名: 'Username',
|
||||||
用户名称: 'User name',
|
用户名: 'User name',
|
||||||
用户注册: 'User registration',
|
用户注册: 'User registration',
|
||||||
用户注册设置: 'User registration settings',
|
用户注册设置: 'User registration settings',
|
||||||
用户编号: 'User ID',
|
用户编号: 'User ID',
|
||||||
|
@ -193,7 +193,7 @@ export default {
|
|||||||
用于登录系统: '用于登录系统',
|
用于登录系统: '用于登录系统',
|
||||||
用户代理: '用户代理',
|
用户代理: '用户代理',
|
||||||
用户名: '用户名',
|
用户名: '用户名',
|
||||||
用户名称: '用户名称',
|
用户名: '用户名',
|
||||||
用户注册: '用户注册',
|
用户注册: '用户注册',
|
||||||
用户注册设置: '用户注册设置',
|
用户注册设置: '用户注册设置',
|
||||||
用户编号: '用户编号',
|
用户编号: '用户编号',
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<common-page :title="$t('注册新账号')">
|
<common-page :title="$t('注册新账号')">
|
||||||
<el-steps :active="stepActive" finish-status="success" simple>
|
<el-steps :active="stepActive" finish-status="success" simple>
|
||||||
<el-step :title="$t('填写账号')" />
|
<el-step :title="$t('填写账号')" />
|
||||||
<el-step :title="$t('验证手机')" />
|
<el-step v-if="config.registerInviteRequired" :title="$t('验证手机')" />
|
||||||
<el-step :title="$t('注册成功')" />
|
<el-step :title="$t('注册成功')" />
|
||||||
</el-steps>
|
</el-steps>
|
||||||
<el-form v-if="stepActive === 0" :model="form" :rules="rules" @keyup.enter="next" label-width="15rem" ref="stepForm_0" size="large">
|
<el-form v-if="stepActive === 0" :model="form" :rules="rules" @keyup.enter="next" label-width="15rem" ref="stepForm_0" size="large">
|
||||||
@ -29,6 +29,9 @@
|
|||||||
show-password
|
show-password
|
||||||
type="password"></el-input>
|
type="password"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('邀请码')" prop="inviter">
|
||||||
|
<el-input v-model="form.inviter" :placeholder="$t('请输入邀请码')" clearable maxlength="6"></el-input>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="" prop="agree">
|
<el-form-item label="" prop="agree">
|
||||||
<el-checkbox v-model="form.agree" label="">{{ $t('我已阅读并同意') }}</el-checkbox>
|
<el-checkbox v-model="form.agree" label="">{{ $t('我已阅读并同意') }}</el-checkbox>
|
||||||
<span @click="showAgree = true" class="link">《{{ $t('平台服务协议') }}》</span>
|
<span @click="showAgree = true" class="link">《{{ $t('平台服务协议') }}》</span>
|
||||||
@ -52,8 +55,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-form size="large" style="text-align: center">
|
<el-form size="large" style="text-align: center">
|
||||||
<el-button v-if="stepActive > 0 && stepActive < 2" @click="pre" size="large">{{ $t('上一步') }}</el-button>
|
<el-button v-if="stepActive > 0 && stepActive < 2" @click="pre" size="large">{{ $t('上一步') }}</el-button>
|
||||||
<el-button v-if="stepActive < 1" @click="next" size="large" type="primary">{{ $t('下一步') }}</el-button>
|
<el-button v-if="stepActive < (this.config.registerInviteRequired ? 1 : 0)" @click="next" size="large" type="primary">{{
|
||||||
<el-button v-if="stepActive === 1" :loading="loading" @click="save" size="large" type="primary">{{ $t('提交') }}</el-button>
|
$t('下一步')
|
||||||
|
}}</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="stepActive === (this.config.registerInviteRequired ? 1 : 0)"
|
||||||
|
:loading="loading"
|
||||||
|
@click="save"
|
||||||
|
size="large"
|
||||||
|
type="primary"
|
||||||
|
>{{ $t('提交') }}</el-button
|
||||||
|
>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-dialog v-model="showAgree" :title="$t('平台服务协议')" destroy-on-close>
|
<el-dialog v-model="showAgree" :title="$t('平台服务协议')" destroy-on-close>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -87,6 +99,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
config: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
stepActive: 0,
|
stepActive: 0,
|
||||||
showAgree: false,
|
showAgree: false,
|
||||||
@ -128,6 +141,22 @@ export default {
|
|||||||
trigger: 'blur',
|
trigger: 'blur',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
inviter: [
|
||||||
|
{
|
||||||
|
validator: async (rule, valueEquals, callback) => {
|
||||||
|
if (!valueEquals) return callback()
|
||||||
|
try {
|
||||||
|
const res = await this.$API.sys_user.checkInviterAvailable.post({
|
||||||
|
code: valueEquals,
|
||||||
|
})
|
||||||
|
return res.data ? callback() : callback(new Error(this.$t('邀请码不正确')))
|
||||||
|
} catch (ex) {
|
||||||
|
return callback(new Error(ex.data.msg.code[0].children))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
verifySmsCodeReq: {
|
verifySmsCodeReq: {
|
||||||
destDevice: [phoneConfig.mobile(this), phoneConfig.mobileNoUsed(this, () => this.form.id)],
|
destDevice: [phoneConfig.mobile(this), phoneConfig.mobileNoUsed(this, () => this.form.id)],
|
||||||
code: phoneConfig.code(this),
|
code: phoneConfig.code(this),
|
||||||
@ -135,6 +164,17 @@ export default {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async created() {
|
||||||
|
const res = await this.$API.sys_config.getRegisterConfig.post({})
|
||||||
|
Object.assign(this.config, res.data)
|
||||||
|
if (this.config.registerInviteRequired) {
|
||||||
|
this.rules.inviter.push({
|
||||||
|
required: true,
|
||||||
|
message: this.$t('请输入邀请码'),
|
||||||
|
trigger: 'blur',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
async mounted() {},
|
async mounted() {},
|
||||||
methods: {
|
methods: {
|
||||||
pre() {
|
pre() {
|
||||||
@ -153,7 +193,13 @@ export default {
|
|||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
await this.$API.sys_user.register.post(this.form)
|
const postData = Object.assign({}, this.form)
|
||||||
|
if (!postData.verifySmsCodeReq.destDevice) {
|
||||||
|
postData.verifySmsCodeReq.destDevice = '13838381438'
|
||||||
|
postData.verifySmsCodeReq.deviceType = 1
|
||||||
|
postData.verifySmsCodeReq.code = '1234'
|
||||||
|
}
|
||||||
|
await this.$API.sys_user.register.post(postData)
|
||||||
this.stepActive += 2
|
this.stepActive += 2
|
||||||
} catch {
|
} catch {
|
||||||
//
|
//
|
||||||
|
@ -5,9 +5,18 @@
|
|||||||
<scUpload v-model="form.avatar" :onSuccess="updateUser" :title="$t('上传头像')"></scUpload>
|
<scUpload v-model="form.avatar" :onSuccess="updateUser" :title="$t('上传头像')"></scUpload>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('用户编号')">
|
<el-form-item :label="$t('用户编号')">
|
||||||
<el-input v-model="form.id" readonly></el-input>
|
<div class="flex w100p gap05">
|
||||||
|
<el-input v-model="form.id" readonly></el-input>
|
||||||
|
<el-button v-copy="form.id">{{ $t('复制') }}</el-button>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('用户名称')">
|
<el-form-item :label="$t('邀请码')">
|
||||||
|
<div class="flex w100p gap05">
|
||||||
|
<el-input v-model="form.inviteCode" readonly></el-input>
|
||||||
|
<el-button v-copy="form.inviteCode">{{ $t('复制') }}</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('用户名')">
|
||||||
<el-input v-model="form.userName" readonly></el-input>
|
<el-input v-model="form.userName" readonly></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('密码')">
|
<el-form-item :label="$t('密码')">
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card :header="$t('授权信息')" shadow="never">
|
<el-card :header="$t('授权信息')" shadow="never">
|
||||||
<el-form :model="form" :rules="rules" label-width="10rem" ref="form">
|
<el-form :model="form" :rules="rules" label-width="10rem" ref="form">
|
||||||
<el-form-item :label="$t('用户标识')">
|
|
||||||
<el-input v-model="form.id" class="font-monospace" readonly></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('授权令牌')">
|
<el-form-item :label="$t('授权令牌')">
|
||||||
<el-input v-model="form.token" class="font-monospace" readonly rows="10" type="textarea"></el-input>
|
<el-input v-model="form.token" class="font-monospace" readonly rows="10" type="textarea"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -79,11 +79,21 @@
|
|||||||
<el-table-column :label="$t('用户注册')" align="center">
|
<el-table-column :label="$t('用户注册')" align="center">
|
||||||
<el-table-column :label="$t('默认部门')" align="center" prop="userRegisterDept.name" width="150" />
|
<el-table-column :label="$t('默认部门')" align="center" prop="userRegisterDept.name" width="150" />
|
||||||
<el-table-column :label="$t('默认角色')" align="center" prop="userRegisterRole.name" width="150" />
|
<el-table-column :label="$t('默认角色')" align="center" prop="userRegisterRole.name" width="150" />
|
||||||
<el-table-column :label="$t('人工审核')" align="center" prop="userRegisterConfirm" width="120">
|
<el-table-column :label="$t('人工审核')" align="center" prop="userRegisterConfirm" width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-switch v-model="row.userRegisterConfirm" @change="changeSwitch($event, row)"></el-switch>
|
<el-switch v-model="row.userRegisterConfirm" @change="changeSwitch($event, row)"></el-switch>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('邀请注册')" align="center" prop="registerInviteRequired" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-switch v-model="row.registerInviteRequired" @change="changeSwitch($event, row)"></el-switch>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('手机注册')" align="center" prop="registerMobileRequired" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-switch v-model="row.registerMobileRequired" @change="changeSwitch($event, row)"></el-switch>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('财务配置')" align="center">
|
<el-table-column :label="$t('财务配置')" align="center">
|
||||||
<el-table-column :label="$t('人民币兑点数比率')" align="center" prop="cnyToPointRate" width="150" />
|
<el-table-column :label="$t('人民币兑点数比率')" align="center" prop="cnyToPointRate" width="150" />
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
<el-form-item :label="$t('开启人工审核')" prop="userRegisterConfirm">
|
<el-form-item :label="$t('开启人工审核')" prop="userRegisterConfirm">
|
||||||
<el-switch v-model="form.userRegisterConfirm"></el-switch>
|
<el-switch v-model="form.userRegisterConfirm"></el-switch>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('邀请注册')" prop="registerInviteRequired">
|
||||||
|
<el-switch v-model="form.registerInviteRequired"></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('手机注册')" prop="registerMobileRequired">
|
||||||
|
<el-switch v-model="form.registerMobileRequired"></el-switch>
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<el-collapse-item :title="$t('财务配置')" name="2">
|
<el-collapse-item :title="$t('财务配置')" name="2">
|
||||||
|
171
src/frontend/admin/src/views/sys/invite/index.vue
Normal file
171
src/frontend/admin/src/views/sys/invite/index.vue
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<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: 'user.userName' },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
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">
|
||||||
|
<el-dropdown v-show="this.selection.length > 0">
|
||||||
|
<el-button type="primary">
|
||||||
|
{{ $t('批量操作') }}
|
||||||
|
<el-icon>
|
||||||
|
<el-icon-arrow-down />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu> </el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
<el-main class="nopadding">
|
||||||
|
<scTable
|
||||||
|
:context-menus="['id', 'user.userName', 'createdTime']"
|
||||||
|
:context-opers="[]"
|
||||||
|
:default-sort="{ prop: 'sort', order: 'descending' }"
|
||||||
|
:params="query"
|
||||||
|
:query-api="$API.sys_userinvite.query"
|
||||||
|
:vue="this"
|
||||||
|
@data-change="getStatistics"
|
||||||
|
@selection-change="
|
||||||
|
(items) => {
|
||||||
|
selection = items
|
||||||
|
}
|
||||||
|
"
|
||||||
|
default-expand-all
|
||||||
|
hidePagination
|
||||||
|
ref="table"
|
||||||
|
remote-filter
|
||||||
|
remote-sort
|
||||||
|
row-key="id"
|
||||||
|
stripe>
|
||||||
|
<el-table-column type="selection" width="50" />
|
||||||
|
<el-table-column :label="$t('用户编号')" prop="id" sortable="custom" />
|
||||||
|
<naColAvatar :label="$t('用户名')" prop="user.userName" />
|
||||||
|
<el-table-column :label="$t('注册时间')" prop="createdTime" sortable="custom" />
|
||||||
|
</scTable>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
import table from '@/config/table'
|
||||||
|
import naColOperation from '@/config/naColOperation'
|
||||||
|
|
||||||
|
const naColAvatar = defineAsyncComponent(() => import('@/components/naColAvatar'))
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
naColAvatar,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
naColOperation() {
|
||||||
|
return naColOperation
|
||||||
|
},
|
||||||
|
table() {
|
||||||
|
return table
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {},
|
||||||
|
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?.tableData?.length
|
||||||
|
},
|
||||||
|
//重置
|
||||||
|
onReset() {},
|
||||||
|
//搜索
|
||||||
|
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['id'] === 'string' && form.dy['id'].trim() !== '') {
|
||||||
|
this.query.dynamicFilter.filters.push({
|
||||||
|
field: 'id',
|
||||||
|
operator: 'eq',
|
||||||
|
value: form.dy['id'],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof form.dy['user.userName'] === 'string' && form.dy['user.userName'].trim() !== '') {
|
||||||
|
this.query.dynamicFilter.filters.push({
|
||||||
|
field: 'user.userName',
|
||||||
|
operator: 'eq',
|
||||||
|
value: form.dy['user.userName'],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onReset()
|
||||||
|
},
|
||||||
|
props: ['keywords'],
|
||||||
|
watch: {},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
@ -25,6 +25,9 @@
|
|||||||
<el-form-item :label="$t('登录账号')" prop="userName">
|
<el-form-item :label="$t('登录账号')" prop="userName">
|
||||||
<el-input v-model="form.userName" :placeholder="$t('用于登录系统')" clearable></el-input>
|
<el-input v-model="form.userName" :placeholder="$t('用于登录系统')" clearable></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item v-if="mode === 'view'" :label="$t('邀请码')" prop="inviteCode">
|
||||||
|
<el-input v-model="form.inviteCode" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :lg="12">
|
<el-col :lg="12">
|
||||||
<el-form-item :label="$t('手机号')" prop="mobile">
|
<el-form-item :label="$t('手机号')" prop="mobile">
|
||||||
|
Reference in New Issue
Block a user