diff --git a/assets/res/NetAdmin.Fields.ln b/assets/res/NetAdmin.Fields.ln index 76111069..a5e83057 100644 --- a/assets/res/NetAdmin.Fields.ln +++ b/assets/res/NetAdmin.Fields.ln @@ -57,11 +57,11 @@ USDT 已校验 已读 并且 +归属角色 +归属部门 微信支付 成功 或者 -归属角色 -归属部门 手机 手机号 执行耗时 @@ -108,6 +108,7 @@ USDT 用户代理 用户名 用户导出 +用户邀请导出 用户钱包导出 电子邮箱 男 diff --git a/assets/seed-data/Sys_Menu.json b/assets/seed-data/Sys_Menu.json index a1ed29a5..00647d8d 100644 --- a/assets/seed-data/Sys_Menu.json +++ b/assets/seed-data/Sys_Menu.json @@ -107,6 +107,27 @@ "Title": "充值订单", "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", diff --git a/assets/seed-data/Sys_RoleApi.json b/assets/seed-data/Sys_RoleApi.json index f6f25acf..c5584175 100644 --- a/assets/seed-data/Sys_RoleApi.json +++ b/assets/seed-data/Sys_RoleApi.json @@ -102,5 +102,9 @@ { "ApiId": "api/sys/deposit.order/pay.confirm", "RoleId": 371729946431493, + }, + { + "ApiId": "api/sys/user.invite/query", + "RoleId": 371729946431493, } ] \ No newline at end of file diff --git a/assets/seed-data/Sys_RoleMenu.json b/assets/seed-data/Sys_RoleMenu.json index 46b55e0f..d34d5c0d 100644 --- a/assets/seed-data/Sys_RoleMenu.json +++ b/assets/seed-data/Sys_RoleMenu.json @@ -30,5 +30,13 @@ { "MenuId": 690907673255944, "RoleId": 371729946431493 + }, + { + "MenuId": 692575802241032, + "RoleId": 371729946431493 + }, + { + "MenuId": 692575802245126, + "RoleId": 371729946431493 } ] \ No newline at end of file diff --git a/assets/seed-data/Sys_User.json b/assets/seed-data/Sys_User.json index 65706084..4083b204 100644 --- a/assets/seed-data/Sys_User.json +++ b/assets/seed-data/Sys_User.json @@ -3,24 +3,27 @@ "DeptId": 372119301627909, "Enabled": true, "Id": 370942943322181, + "InviteCode": "Q09Y8O", "Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15", "Token": "A9AFD92E-A33D-4152-9A6C-A9C141D24887", - "UserName": "root" + "UserName": "root", }, { "DeptId": 372119301627909, "Enabled": true, "Id": 560217289236492, + "InviteCode": "7ZH5PB", "Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15", "Token": "4208EA97-B32F-4E39-A290-4C0D27B61EBF", - "UserName": "user" + "UserName": "user", }, { "DeptId": 372119301627909, "Enabled": true, "Id": 664362432344581, + "InviteCode": "47Q56H", "Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15", "Token": "751D599B-2B8C-417C-9565-CCF2363F5F6F", - "UserName": "jobs" + "UserName": "jobs", } ] \ No newline at end of file diff --git a/assets/seed-data/Sys_UserInvite.json b/assets/seed-data/Sys_UserInvite.json new file mode 100644 index 00000000..ffca242a --- /dev/null +++ b/assets/seed-data/Sys_UserInvite.json @@ -0,0 +1,15 @@ +[ + { + "Id": 370942943322181, + }, + { + "Id": 560217289236492, + "OwnerDeptId": 372119301627909, + "OwnerId": 370942943322181, + }, + { + "Id": 664362432344581, + "OwnerDeptId": 372119301627909, + "OwnerId": 370942943322181, + } +] \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.Application/Services/RepositoryService.cs b/src/backend/NetAdmin/NetAdmin.Application/Services/RepositoryService.cs index 3cd0731c..9ecdb515 100644 --- a/src/backend/NetAdmin/NetAdmin.Application/Services/RepositoryService.cs +++ b/src/backend/NetAdmin/NetAdmin.Application/Services/RepositoryService.cs @@ -71,6 +71,7 @@ public abstract class RepositoryService(BasicReposit /// 查询表达式 /// 查询sql /// 是否忽略版本锁 + /// 是否忽略全局数据权限过滤 /// 更新行数 protected Task UpdateAsync( // TEntity newValue // @@ -78,11 +79,15 @@ public abstract class RepositoryService(BasicReposit , List excludeFields = null // , Expression> whereExp = null // , string whereSql = null // - , bool ignoreVersion = false) + , bool ignoreVersion = false, bool disableGlobalDataFilter = false) { // 默认匹配主键 whereExp ??= a => a.Id.Equals(newValue.Id); 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(); } @@ -101,8 +106,8 @@ public abstract class RepositoryService(BasicReposit TEntity newValue // , List includeFields = null // , List excludeFields = null // - , Expression> whereExp = null // - , string whereSql = null // + , Expression> whereExp = null // + , string whereSql = null // , bool ignoreVersion = false) { // 默认匹配主键 diff --git a/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_Config.cs b/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_Config.cs index fae8534d..948b11c6 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_Config.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_Config.cs @@ -22,6 +22,22 @@ public record Sys_Config : VersionEntity, IFieldEnabled [JsonIgnore] public virtual bool Enabled { get; init; } + /// + /// 必须邀请注册 + /// + [Column] + [CsvIgnore] + [JsonIgnore] + public virtual bool RegisterInviteRequired { get; init; } + + /// + /// 必须手机号注册 + /// + [Column] + [CsvIgnore] + [JsonIgnore] + public virtual bool RegisterMobileRequired { get; init; } + /// /// Trc20收款地址 /// diff --git a/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_User.cs b/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_User.cs index fb6d3450..f49ffebd 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_User.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_User.cs @@ -5,9 +5,10 @@ namespace NetAdmin.Domain.DbMaps.Sys; /// /// 用户基本信息表 /// -[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(UserName), nameof(UserName), 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(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))] public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister { @@ -51,6 +52,21 @@ public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister [JsonIgnore] public virtual bool Enabled { get; init; } + /// + /// 用户邀请 + /// + [CsvIgnore] + [JsonIgnore] + public Sys_UserInvite Invite { get; init; } + + /// + /// 邀请码 + /// + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_CHAR_6)] + [CsvIgnore] + [JsonIgnore] + public virtual string InviteCode { get; init; } + /// /// 最后登录时间 /// diff --git a/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_UserInvite.cs b/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_UserInvite.cs new file mode 100644 index 00000000..21142f85 --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.Domain/DbMaps/Sys/Sys_UserInvite.cs @@ -0,0 +1,48 @@ +namespace NetAdmin.Domain.DbMaps.Sys; + +/// +/// 用户邀请表 +/// +[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_UserInvite))] +public record Sys_UserInvite : VersionEntity, IFieldOwner +{ + /// + /// 子节点 + /// + [CsvIgnore] + [JsonIgnore] + [Navigate(nameof(OwnerId))] + public IEnumerable Children { get; init; } + + /// + /// 归属 + /// + [CsvIgnore] + [JsonIgnore] + [Navigate(nameof(OwnerId))] + public Sys_User Owner { get; init; } + + /// + /// 归属部门编号 + /// + [Column] + [CsvIgnore] + [JsonIgnore] + public virtual long? OwnerDeptId { get; init; } + + /// + /// 归属用户编号 + /// + [Column] + [CsvIgnore] + [JsonIgnore] + public virtual long? OwnerId { get; init; } + + /// + /// 用户 + /// + [CsvIgnore] + [JsonIgnore] + [Navigate(nameof(Id))] + public Sys_User User { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/Config/CreateConfigReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/Config/CreateConfigReq.cs index 2b89683d..d9b79ad0 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/Config/CreateConfigReq.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/Config/CreateConfigReq.cs @@ -14,6 +14,14 @@ public record CreateConfigReq : Sys_Config [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override bool Enabled { get; init; } + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override bool RegisterInviteRequired { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override bool RegisterMobileRequired { get; init; } + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [Length(34, 34)] diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/Config/QueryConfigRsp.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/Config/QueryConfigRsp.cs index 2ba1d9ad..0a430fc3 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/Config/QueryConfigRsp.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/Config/QueryConfigRsp.cs @@ -24,6 +24,14 @@ public record QueryConfigRsp : Sys_Config [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override long Id { get; init; } + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override bool RegisterInviteRequired { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override bool RegisterMobileRequired { get; init; } + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override string Trc20ReceiptAddress { get; init; } diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/CheckInviterAvailableReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/CheckInviterAvailableReq.cs new file mode 100644 index 00000000..7ea649b1 --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/CheckInviterAvailableReq.cs @@ -0,0 +1,13 @@ +namespace NetAdmin.Domain.Dto.Sys.User; + +/// +/// 请求:检查邀请码是否正确 +/// +public sealed record CheckInviterAvailableReq : DataAbstraction +{ + /// + /// 邀请码 + /// + [Required] + public string Code { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/CreateUserReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/CreateUserReq.cs index 67a940ec..e11a16c8 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/CreateUserReq.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/CreateUserReq.cs @@ -1,3 +1,4 @@ +using NetAdmin.Domain.Dto.Sys.UserInvite; using NetAdmin.Domain.Dto.Sys.UserProfile; namespace NetAdmin.Domain.Dto.Sys.User; @@ -7,6 +8,15 @@ namespace NetAdmin.Domain.Dto.Sys.User; /// public sealed record CreateUserReq : CreateEditUserReq { + /// + public override bool Enabled { get; init; } = true; + + /// + public new CreateUserInviteReq Invite { get; init; } + + /// + public override string InviteCode { get; init; } = ((long)new[] { 60466176, int.MaxValue }.Rand()).Sys36().ToUpperInvariant(); + /// [Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.密码不能为空))] public override string PasswordText { get; init; } @@ -18,6 +28,7 @@ public sealed record CreateUserReq : CreateEditUserReq public override void Register(TypeAdapterConfig config) { _ = config.ForType() // + .Ignore(a => a.InviteCode) .Map(d => d.Mobile, s => s.VerifySmsCodeReq.DestDevice) // diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/QueryUserRsp.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/QueryUserRsp.cs index 165825a9..c5a03cf7 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/QueryUserRsp.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/QueryUserRsp.cs @@ -39,6 +39,10 @@ public record QueryUserRsp : Sys_User [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override long Id { get; init; } + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string InviteCode { get; init; } + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override DateTime? LastLoginTime { get; init; } diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/RegisterUserReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/RegisterUserReq.cs index 5f755aef..cd28ca61 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/RegisterUserReq.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/User/RegisterUserReq.cs @@ -7,6 +7,11 @@ namespace NetAdmin.Domain.Dto.Sys.User; /// public sealed record RegisterUserReq : Sys_User { + /// + /// 邀请者的邀请码 + /// + public string Inviter { get; init; } + /// /// 密码 /// diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/CreateUserInviteReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/CreateUserInviteReq.cs new file mode 100644 index 00000000..6199dd1b --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/CreateUserInviteReq.cs @@ -0,0 +1,6 @@ +namespace NetAdmin.Domain.Dto.Sys.UserInvite; + +/// +/// 请求:创建用户邀请 +/// +public record CreateUserInviteReq : Sys_UserInvite; \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/EditUserInviteReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/EditUserInviteReq.cs new file mode 100644 index 00000000..3b5476f1 --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/EditUserInviteReq.cs @@ -0,0 +1,15 @@ +namespace NetAdmin.Domain.Dto.Sys.UserInvite; + +/// +/// 请求:编辑用户邀请 +/// +public record EditUserInviteReq : CreateUserInviteReq +{ + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override long Id { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override long Version { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/QueryUserInviteReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/QueryUserInviteReq.cs new file mode 100644 index 00000000..e0a8243c --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/QueryUserInviteReq.cs @@ -0,0 +1,11 @@ +namespace NetAdmin.Domain.Dto.Sys.UserInvite; + +/// +/// 请求:查询用户邀请 +/// +public sealed record QueryUserInviteReq : Sys_UserInvite +{ + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override long Id { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/QueryUserInviteRsp.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/QueryUserInviteRsp.cs new file mode 100644 index 00000000..2f42250e --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserInvite/QueryUserInviteRsp.cs @@ -0,0 +1,60 @@ +using NetAdmin.Domain.Dto.Sys.User; + +namespace NetAdmin.Domain.Dto.Sys.UserInvite; + +/// +/// 响应:查询用户邀请 +/// +public record QueryUserInviteRsp : Sys_UserInvite +{ + /// + public new virtual IEnumerable Children { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override DateTime CreatedTime { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override long? CreatedUserId { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string CreatedUserName { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override long Id { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override DateTime? ModifiedTime { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override long? ModifiedUserId { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string ModifiedUserName { get; init; } + + /// + [CsvIgnore] + public new virtual QueryUserRsp Owner { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override long? OwnerDeptId { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override long? OwnerId { get; init; } + + /// + [CsvIgnore] + public new virtual QueryUserRsp User { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override long Version { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserWallet/CreateUserWalletReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserWallet/CreateUserWalletReq.cs index 902113b8..51d2c494 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserWallet/CreateUserWalletReq.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/UserWallet/CreateUserWalletReq.cs @@ -1,5 +1,3 @@ -using NetAdmin.Domain.DbMaps.Sys; - namespace NetAdmin.Domain.Dto.Sys.UserWallet; /// diff --git a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/WalletTrade/QueryWalletTradeReq.cs b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/WalletTrade/QueryWalletTradeReq.cs index 913d7151..c1320958 100644 --- a/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/WalletTrade/QueryWalletTradeReq.cs +++ b/src/backend/NetAdmin/NetAdmin.Domain/Dto/Sys/WalletTrade/QueryWalletTradeReq.cs @@ -1,5 +1,3 @@ -using NetAdmin.Domain.DbMaps.Sys; - namespace NetAdmin.Domain.Dto.Sys.WalletTrade; /// diff --git a/src/backend/NetAdmin/NetAdmin.Host/Utils/CollectionJsonTypeInfoResolver.cs b/src/backend/NetAdmin/NetAdmin.Host/Utils/CollectionJsonTypeInfoResolver.cs index ed6b0199..8637f557 100644 --- a/src/backend/NetAdmin/NetAdmin.Host/Utils/CollectionJsonTypeInfoResolver.cs +++ b/src/backend/NetAdmin/NetAdmin.Host/Utils/CollectionJsonTypeInfoResolver.cs @@ -46,7 +46,7 @@ public sealed class CollectionJsonTypeInfoResolver : DefaultJsonTypeInfoResolver /// 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); } /// @@ -61,7 +61,7 @@ public sealed class CollectionJsonTypeInfoResolver : DefaultJsonTypeInfoResolver } catch (AmbiguousMatchException) { // 这里处理子类new隐藏父类同名属性, 取得多个同名属性的问题 - prop = GetNewProperty(memberName, obj).GetValue(obj); + prop = GetNewProperty(memberName, obj)?.GetValue(obj); } 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); } catch (AmbiguousMatchException) { - GetNewProperty(memberName, obj).SetValue(obj, val); + GetNewProperty(memberName, obj)?.SetValue(obj, val); } }; } diff --git a/src/backend/NetAdmin/NetAdmin.Infrastructure/Constant/Chars.cs b/src/backend/NetAdmin/NetAdmin.Infrastructure/Constant/Chars.cs index 23206685..f1ef13da 100644 --- a/src/backend/NetAdmin/NetAdmin.Infrastructure/Constant/Chars.cs +++ b/src/backend/NetAdmin/NetAdmin.Infrastructure/Constant/Chars.cs @@ -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_UNIQUE_CONSTRAINT_CONFLICT = "UNIQUE constraint"; 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_1022 = "nvarchar(1022)"; public const string FLG_DB_FIELD_TYPE_NVARCHAR_127 = "nvarchar(127)"; diff --git a/src/backend/NetAdmin/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj b/src/backend/NetAdmin/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj index 8445307e..88a9270c 100644 --- a/src/backend/NetAdmin/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj +++ b/src/backend/NetAdmin/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IConfigModule.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IConfigModule.cs index 7d6a0ad1..125d7495 100644 --- a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IConfigModule.cs +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IConfigModule.cs @@ -16,6 +16,11 @@ public interface IConfigModule : ICrudModule Task GetLatestConfigAsync(); + /// + /// 获取注册配置 + /// + Task GetRegisterConfigAsync(); + /// /// 设置配置启用状态 /// diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IUserInviteModule.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IUserInviteModule.cs new file mode 100644 index 00000000..c7159792 --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IUserInviteModule.cs @@ -0,0 +1,12 @@ +using NetAdmin.Domain.Dto.Sys.UserInvite; + +namespace NetAdmin.SysComponent.Application.Modules.Sys; + +/// +/// 用户邀请模块 +/// +public interface IUserInviteModule : ICrudModule; \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IUserModule.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IUserModule.cs index 554005ba..5255b7a3 100644 --- a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IUserModule.cs +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Modules/Sys/IUserModule.cs @@ -12,6 +12,11 @@ public partial interface IUserModule : ICrudModule { + /// + /// 检查邀请码是否正确 + /// + Task CheckInviterAvailableAsync(CheckInviterAvailableReq req); + /// /// 检查手机号码是否可用 /// diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/ConfigService.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/ConfigService.cs index e71b64f2..012c38ed 100644 --- a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/ConfigService.cs +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/ConfigService.cs @@ -92,6 +92,16 @@ public sealed class ConfigService(BasicRepository rpo) // return ret.FirstOrDefault(); } + /// + public async Task GetRegisterConfigAsync() + { + var latestConfigAsync = await GetLatestConfigAsync().ConfigureAwait(false); + return new QueryConfigRsp { + RegisterInviteRequired = latestConfigAsync.RegisterInviteRequired + , RegisterMobileRequired = latestConfigAsync.RegisterMobileRequired + }; + } + /// public async Task> PagedQueryAsync(PagedQueryReq req) { diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IUserInviteService.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IUserInviteService.cs new file mode 100644 index 00000000..7ac01dfd --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IUserInviteService.cs @@ -0,0 +1,6 @@ +namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency; + +/// +/// 用户邀请服务 +/// +public interface IUserInviteService : IService, IUserInviteModule; \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/UserInviteService.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/UserInviteService.cs new file mode 100644 index 00000000..eeab803b --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/UserInviteService.cs @@ -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; + +/// +public sealed class UserInviteService(BasicRepository rpo) // + : RepositoryService(rpo), IUserInviteService +{ + /// + public async Task BulkDeleteAsync(BulkReq req) + { + req.ThrowIfInvalid(); + var ret = 0; + + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var item in req.Items) { + ret += await DeleteAsync(item).ConfigureAwait(false); + } + + return ret; + } + + /// + public Task CountAsync(QueryReq req) + { + req.ThrowIfInvalid(); + return QueryInternal(req).WithNoLockNoWait().CountAsync(); + } + + /// + public async Task, int>>> CountByAsync(QueryReq req) + { + req.ThrowIfInvalid(); + var ret = await QueryInternal(req with { Order = Orders.None }) + .WithNoLockNoWait() + .GroupBy(req.GetToListExp()) + .ToDictionaryAsync(a => a.Count()) + .ConfigureAwait(false); + return ret.Select(x => new KeyValuePair, 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); + } + + /// + public async Task CreateAsync(CreateUserInviteReq req) + { + req.ThrowIfInvalid(); + var ret = await Rpo.InsertAsync(req).ConfigureAwait(false); + return ret.Adapt(); + } + + /// + public Task DeleteAsync(DelReq req) + { + req.ThrowIfInvalid(); + return Rpo.DeleteAsync(a => a.Id == req.Id); + } + + /// + public async Task EditAsync(EditUserInviteReq req) + { + req.ThrowIfInvalid(); + #if DBTYPE_SQLSERVER + return (await UpdateReturnListAsync(req).ConfigureAwait(false)).FirstOrDefault()?.Adapt(); + #else + return await UpdateAsync(req).ConfigureAwait(false) > 0 ? await GetAsync(new QueryUserInviteReq { Id = req.Id }).ConfigureAwait(false) : null; + #endif + } + + /// + public Task ExportAsync(QueryReq req) + { + req.ThrowIfInvalid(); + return ExportAsync(QueryInternal, req, Ln.用户邀请导出); + } + + /// + public async Task GetAsync(QueryUserInviteReq req) + { + req.ThrowIfInvalid(); + var ret = await QueryInternal(new QueryReq { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false); + return ret.Adapt(); + } + + /// + public async Task> PagedQueryAsync(PagedQueryReq 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(req.Page, req.PageSize, total, list.Adapt>()); + } + + /// + public async Task> QueryAsync(QueryReq req) + { + req.ThrowIfInvalid(); + var ret = await QueryInternal(req).Include(a => a.Owner).Include(a => a.User).WithNoLockNoWait().ToTreeListAsync().ConfigureAwait(false); + return ret.Adapt>(); + } + + private ISelect QueryInternal(QueryReq 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; + } +} \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/UserService.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/UserService.cs index 23563260..d0e5226f 100644 --- a/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/UserService.cs +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Application/Services/Sys/UserService.cs @@ -2,6 +2,7 @@ using NetAdmin.Domain.Attributes.DataValidation; using NetAdmin.Domain.Contexts; using NetAdmin.Domain.DbMaps.Sys; using NetAdmin.Domain.Dto.Sys.User; +using NetAdmin.Domain.Dto.Sys.UserInvite; using NetAdmin.Domain.Dto.Sys.UserProfile; using NetAdmin.Domain.Dto.Sys.UserWallet; using NetAdmin.Domain.Dto.Sys.VerifyCode; @@ -14,6 +15,7 @@ namespace NetAdmin.SysComponent.Application.Services.Sys; public sealed class UserService( BasicRepository rpo // , IUserProfileService userProfileService // + , IUserInviteService userInviteService // , IUserWalletService userWalletService // , IVerifyCodeService verifyCodeService // , IEventPublisher eventPublisher) // @@ -52,6 +54,13 @@ public sealed class UserService( return ret; } + /// + public Task CheckInviterAvailableAsync(CheckInviterAvailableReq req) + { + req.ThrowIfInvalid(); + return Rpo.Select.Where(a => a.InviteCode == req.Code && a.Enabled).AnyAsync(); + } + /// public async Task CheckMobileAvailableAsync(CheckMobileAvailableReq req) { @@ -116,14 +125,14 @@ public sealed class UserService( .ConfigureAwait(false); // 钱包表 - _ = await userWalletService.CreateAsync(new CreateUserWalletReq() with // - { - Id = dbUser.Id, OwnerId = dbUser.Id, OwnerDeptId = dbUser.DeptId - }) + _ = await userWalletService.CreateAsync(new CreateUserWalletReq { Id = dbUser.Id, OwnerId = dbUser.Id, OwnerDeptId = dbUser.DeptId }) .ConfigureAwait(false); var userList = await QueryAsync(new QueryReq { 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(); await eventPublisher.PublishAsync(new UserCreatedEvent(ret.Adapt())).ConfigureAwait(false); @@ -295,12 +304,34 @@ public sealed class UserService( public async Task RegisterAsync(RegisterUserReq req) { req.ThrowIfInvalid(); - if (!await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) { + var config = await S().GetLatestConfigAsync().ConfigureAwait(false); + + if (config.RegisterMobileRequired && !await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) { throw new NetAdminInvalidOperationException(Ln.验证码不正确); } - var createReq = req.Adapt() with { Profile = new CreateUserProfileReq() }; - return (await CreateAsync(createReq).ConfigureAwait(false)).Adapt(); + long? inviterId = null; + 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() 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(); } /// @@ -505,6 +536,8 @@ public sealed class UserService( .WhereIf(deptIds != null, a => deptIds.Contains(a.DeptId)) .WhereIf( // req.Filter?.Id > 0, a => a.Id == req.Filter.Id) + .WhereIf( // + req.Filter?.InviteCode?.Length > 0, a => a.InviteCode == req.Filter.InviteCode) .WhereIf( // req.Filter?.RoleId > 0, a => Enumerable.Any(a.Roles, b => b.Id == req.Filter.RoleId)) .WhereIf( // diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/ConfigCache.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/ConfigCache.cs index 3a876cde..6a02da6c 100644 --- a/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/ConfigCache.cs +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/ConfigCache.cs @@ -60,6 +60,12 @@ public sealed class ConfigCache(IDistributedCache cache, IConfigService service) return Service.GetLatestConfigAsync(); } + /// + public Task GetRegisterConfigAsync() + { + return Service.GetRegisterConfigAsync(); + } + /// public Task> PagedQueryAsync(PagedQueryReq req) { diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/Dependency/IUserInviteCache.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/Dependency/IUserInviteCache.cs new file mode 100644 index 00000000..1df8f285 --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/Dependency/IUserInviteCache.cs @@ -0,0 +1,6 @@ +namespace NetAdmin.SysComponent.Cache.Sys.Dependency; + +/// +/// 用户邀请缓存 +/// +public interface IUserInviteCache : ICache, IUserInviteModule; \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/UserCache.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/UserCache.cs index 970bf0d4..c5ee0417 100644 --- a/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/UserCache.cs +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/UserCache.cs @@ -13,6 +13,12 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe return Service.BulkDeleteAsync(req); } + /// + public Task CheckInviterAvailableAsync(CheckInviterAvailableReq req) + { + return Service.CheckInviterAvailableAsync(req); + } + /// public Task CheckMobileAvailableAsync(CheckMobileAvailableReq req) { diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/UserInviteCache.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/UserInviteCache.cs new file mode 100644 index 00000000..c3ed7982 --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Cache/Sys/UserInviteCache.cs @@ -0,0 +1,68 @@ +using NetAdmin.Domain.Dto.Sys.UserInvite; + +namespace NetAdmin.SysComponent.Cache.Sys; + +/// +public sealed class UserInviteCache(IDistributedCache cache, IUserInviteService service) + : DistributedCache(cache, service), IScoped, IUserInviteCache +{ + /// + public Task BulkDeleteAsync(BulkReq req) + { + return Service.BulkDeleteAsync(req); + } + + /// + public Task CountAsync(QueryReq req) + { + return Service.CountAsync(req); + } + + /// + public Task, int>>> CountByAsync(QueryReq req) + { + return Service.CountByAsync(req); + } + + /// + public Task CreateAsync(CreateUserInviteReq req) + { + return Service.CreateAsync(req); + } + + /// + public Task DeleteAsync(DelReq req) + { + return Service.DeleteAsync(req); + } + + /// + public Task EditAsync(EditUserInviteReq req) + { + return Service.EditAsync(req); + } + + /// + public Task ExportAsync(QueryReq req) + { + return Service.ExportAsync(req); + } + + /// + public Task GetAsync(QueryUserInviteReq req) + { + return Service.GetAsync(req); + } + + /// + public Task> PagedQueryAsync(PagedQueryReq req) + { + return Service.PagedQueryAsync(req); + } + + /// + public Task> QueryAsync(QueryReq req) + { + return Service.QueryAsync(req); + } +} \ No newline at end of file diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/ConfigController.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/ConfigController.cs index d08648cd..cd248545 100644 --- a/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/ConfigController.cs +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/ConfigController.cs @@ -85,6 +85,15 @@ public sealed class ConfigController(IConfigCache cache) : ControllerBase + /// 获取注册配置 + /// + [AllowAnonymous] + public Task GetRegisterConfigAsync() + { + return Cache.GetRegisterConfigAsync(); + } + /// /// 分页查询配置 /// diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/UserController.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/UserController.cs index 694567b2..00fc0cd3 100644 --- a/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/UserController.cs +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/UserController.cs @@ -20,6 +20,15 @@ public sealed class UserController(IUserCache cache, IConfigCache configCache) : return Cache.BulkDeleteAsync(req); } + /// + /// 检查邀请码是否正确 + /// + [AllowAnonymous] + public Task CheckInviterAvailableAsync(CheckInviterAvailableReq req) + { + return Cache.CheckInviterAvailableAsync(req); + } + /// /// 检查手机号码是否可用 /// diff --git a/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/UserInviteController.cs b/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/UserInviteController.cs new file mode 100644 index 00000000..5e6eb350 --- /dev/null +++ b/src/backend/NetAdmin/NetAdmin.SysComponent.Host/Controllers/Sys/UserInviteController.cs @@ -0,0 +1,96 @@ +using NetAdmin.Domain.Dto.Sys.UserInvite; + +namespace NetAdmin.SysComponent.Host.Controllers.Sys; + +/// +/// 用户邀请服务 +/// +[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))] +[Produces(Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_JSON)] +public sealed class UserInviteController(IUserInviteCache cache) : ControllerBase(cache), IUserInviteModule +{ + /// + /// 批量删除用户邀请 + /// + [Transaction] + public Task BulkDeleteAsync(BulkReq req) + { + return Cache.BulkDeleteAsync(req); + } + + /// + /// 用户邀请计数 + /// + public Task CountAsync(QueryReq req) + { + return Cache.CountAsync(req); + } + + /// + /// 用户邀请分组计数 + /// + public Task, int>>> CountByAsync(QueryReq req) + { + return Cache.CountByAsync(req); + } + + /// + /// 创建用户邀请 + /// + [Transaction] + public Task CreateAsync(CreateUserInviteReq req) + { + return Cache.CreateAsync(req); + } + + /// + /// 删除用户邀请 + /// + [Transaction] + public Task DeleteAsync(DelReq req) + { + return Cache.DeleteAsync(req); + } + + /// + /// 编辑用户邀请 + /// + [Transaction] + public Task EditAsync(EditUserInviteReq req) + { + return Cache.EditAsync(req); + } + + /// + /// 导出用户邀请 + /// + [NonAction] + public Task ExportAsync(QueryReq req) + { + return Cache.ExportAsync(req); + } + + /// + /// 获取单个用户邀请 + /// + public Task GetAsync(QueryUserInviteReq req) + { + return Cache.GetAsync(req); + } + + /// + /// 分页查询用户邀请 + /// + public Task> PagedQueryAsync(PagedQueryReq req) + { + return Cache.PagedQueryAsync(req); + } + + /// + /// 查询用户邀请 + /// + public Task> QueryAsync(QueryReq req) + { + return Cache.QueryAsync(req); + } +} \ No newline at end of file diff --git a/src/backend/UnitTests/Sys/ConfigTests.cs b/src/backend/UnitTests/Sys/ConfigTests.cs index b836c8be..c6579387 100644 --- a/src/backend/UnitTests/Sys/ConfigTests.cs +++ b/src/backend/UnitTests/Sys/ConfigTests.cs @@ -99,6 +99,14 @@ public class ConfigTests(WebTestApplicationFactory factory, ITestOutput return null; } + /// + public async Task GetRegisterConfigAsync() + { + var rsp = await PostJsonAsync(typeof(ConfigController)); + Assert.True(rsp.IsSuccessStatusCode); + return null; + } + /// [InlineData(null)] [Theory] diff --git a/src/backend/UnitTests/Sys/UserTests.cs b/src/backend/UnitTests/Sys/UserTests.cs index 81927287..82723189 100644 --- a/src/backend/UnitTests/Sys/UserTests.cs +++ b/src/backend/UnitTests/Sys/UserTests.cs @@ -24,6 +24,17 @@ public class UserTests(WebTestApplicationFactory factory, ITestOutputHe return 0; } + /// + [InlineData(null)] + [Theory] + [TestPriority(100200)] + public async Task CheckInviterAvailableAsync(CheckInviterAvailableReq req) + { + var rsp = await PostJsonAsync(typeof(UserController), new CheckInviterAvailableReq { Code = "111111" }); + Assert.True(rsp.IsSuccessStatusCode); + return false; + } + /// [InlineData(null)] [Theory] @@ -195,6 +206,17 @@ public class UserTests(WebTestApplicationFactory factory, ITestOutputHe return null; } + /// + [InlineData(null)] + [Theory] + [TestPriority(101600)] + public async Task> QueryRelationAsync(QueryReq req) + { + var rsp = await PostJsonAsync(typeof(UserController), req); + Assert.True(rsp.IsSuccessStatusCode); + return null; + } + /// [InlineData(null)] [Theory] diff --git a/src/frontend/admin/src/api/sys/config.js b/src/frontend/admin/src/api/sys/config.js index 6bcbbf18..b425b7af 100644 --- a/src/frontend/admin/src/api/sys/config.js +++ b/src/frontend/admin/src/api/sys/config.js @@ -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) + }, + }, + /** * 分页查询配置 */ diff --git a/src/frontend/admin/src/api/sys/depositorder.js b/src/frontend/admin/src/api/sys/depositorder.js index 7fc897e1..5361bf51 100644 --- a/src/frontend/admin/src/api/sys/depositorder.js +++ b/src/frontend/admin/src/api/sys/depositorder.js @@ -114,4 +114,15 @@ export default { 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) + }, + }, } \ No newline at end of file diff --git a/src/frontend/admin/src/api/sys/user.js b/src/frontend/admin/src/api/sys/user.js index 929ebb52..36f6ba58 100644 --- a/src/frontend/admin/src/api/sys/user.js +++ b/src/frontend/admin/src/api/sys/user.js @@ -5,6 +5,17 @@ import config from '@/config' import http from '@/utils/request' 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) + }, + }, + /** * 检查手机号码是否可用 */ diff --git a/src/frontend/admin/src/api/sys/userinvite.js b/src/frontend/admin/src/api/sys/userinvite.js new file mode 100644 index 00000000..3b08b899 --- /dev/null +++ b/src/frontend/admin/src/api/sys/userinvite.js @@ -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) + }, + }, +} \ No newline at end of file diff --git a/src/frontend/admin/src/locales/lang/en.js b/src/frontend/admin/src/locales/lang/en.js index 3905f5b1..afbd1b6b 100644 --- a/src/frontend/admin/src/locales/lang/en.js +++ b/src/frontend/admin/src/locales/lang/en.js @@ -193,7 +193,7 @@ export default { 用于登录系统: 'Used to log in to the system', 用户代理: 'User agent', 用户名: 'Username', - 用户名称: 'User name', + 用户名: 'User name', 用户注册: 'User registration', 用户注册设置: 'User registration settings', 用户编号: 'User ID', diff --git a/src/frontend/admin/src/locales/lang/zh-cn.js b/src/frontend/admin/src/locales/lang/zh-cn.js index 79580683..00cdfe47 100644 --- a/src/frontend/admin/src/locales/lang/zh-cn.js +++ b/src/frontend/admin/src/locales/lang/zh-cn.js @@ -193,7 +193,7 @@ export default { 用于登录系统: '用于登录系统', 用户代理: '用户代理', 用户名: '用户名', - 用户名称: '用户名称', + 用户名: '用户名', 用户注册: '用户注册', 用户注册设置: '用户注册设置', 用户编号: '用户编号', diff --git a/src/frontend/admin/src/views/guest/register.vue b/src/frontend/admin/src/views/guest/register.vue index c2306e6c..913257db 100644 --- a/src/frontend/admin/src/views/guest/register.vue +++ b/src/frontend/admin/src/views/guest/register.vue @@ -2,7 +2,7 @@ - + @@ -29,6 +29,9 @@ show-password type="password"> + + + {{ $t('我已阅读并同意') }} 《{{ $t('平台服务协议') }}》 @@ -52,8 +55,17 @@ {{ $t('上一步') }} - {{ $t('下一步') }} - {{ $t('提交') }} + {{ + $t('下一步') + }} + {{ $t('提交') }}