feat: ✨ 基础模块
注册登录 用户管理 角色管理 部门管理 消息管理 接口管理 菜单管理 字典管理 缓存管理 请求日志 系统设置 版本信息 代码生成
72
.commitlintrc.js
Normal file
@ -0,0 +1,72 @@
|
||||
// .commitlintrc.js
|
||||
/** @type {import('cz-git').UserConfig} */
|
||||
module.exports = {
|
||||
rules: {
|
||||
// @see: https://commitlint.js.org/#/reference-rules
|
||||
},
|
||||
prompt: {
|
||||
alias: { fd: 'docs: fix typos' },
|
||||
messages: {
|
||||
type: '选择你要提交的类型 :',
|
||||
scope: '选择一个提交范围(可选):',
|
||||
customScope: '请输入自定义的提交范围 :',
|
||||
subject: '填写简短精炼的变更描述 :\n',
|
||||
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
|
||||
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
|
||||
footerPrefixesSelect: '选择关联issue前缀(可选):',
|
||||
customFooterPrefix: '输入自定义issue前缀 :',
|
||||
footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
|
||||
confirmCommit: '是否提交或修改commit ?'
|
||||
},
|
||||
types: [
|
||||
{ value: 'wip', name: 'wip: 开发之中 | In development ', emoji:'🧠' },
|
||||
{ value: 'feat', name: 'feat: 新增功能 | A new feature', emoji:'✨' },
|
||||
{ value: 'fix', name: 'fix: 修复缺陷 | A bug fix', emoji:'🐛' },
|
||||
{ value: 'docs', name: 'docs: 文档更新 | Documentation only changes', emoji:'📝' },
|
||||
{ value: 'style', name: 'style: 代码格式 | Changes that do not affect the meaning of the code', emoji:'💄' },
|
||||
{ value: 'refactor', name: 'refactor: 代码重构 | A code change that neither fixes a bug nor adds a feature', emoji:'♻️' },
|
||||
{ value: 'perf', name: 'perf: 性能提升 | A code change that improves performance', emoji:'⚡' },
|
||||
{ value: 'test', name: 'test: 测试相关 | Adding missing tests or correcting existing tests', emoji:'✅' },
|
||||
{ value: 'build', name: 'build: 构建相关 | Changes that affect the build system or external dependencies', emoji:'📦' },
|
||||
{ value: 'ci', name: 'ci: 持续集成 | Changes to our CI configuration files and scripts', emoji:'🎡' },
|
||||
{ value: 'revert', name: 'revert: 回退代码 | Revert to a commit', emoji:'⏪' },
|
||||
{ value: 'chore', name: 'chore: 其他修改 | Other changes that do not modify src or test files', emoji:'🔨' },
|
||||
],
|
||||
useEmoji: true,
|
||||
emojiAlign: 'center',
|
||||
useAI: false,
|
||||
aiNumber: 1,
|
||||
themeColorCode: '',
|
||||
scopes: [],
|
||||
allowCustomScopes: true,
|
||||
allowEmptyScopes: true,
|
||||
customScopesAlign: 'bottom',
|
||||
customScopesAlias: 'custom',
|
||||
emptyScopesAlias: 'empty',
|
||||
upperCaseSubject: false,
|
||||
markBreakingChangeMode: false,
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
breaklineNumber: 100,
|
||||
breaklineChar: '|',
|
||||
skipQuestions: [],
|
||||
issuePrefixes: [
|
||||
// 如果使用 gitee 作为开发管理
|
||||
{ value: 'link', name: 'link: 链接 ISSUES 进行中' },
|
||||
{ value: 'closed', name: 'closed: 标记 ISSUES 已完成' }
|
||||
],
|
||||
customIssuePrefixAlign: 'top',
|
||||
emptyIssuePrefixAlias: 'skip',
|
||||
customIssuePrefixAlias: 'custom',
|
||||
allowCustomIssuePrefix: true,
|
||||
allowEmptyIssuePrefix: true,
|
||||
confirmColorize: true,
|
||||
maxHeaderLength: Infinity,
|
||||
maxSubjectLength: Infinity,
|
||||
minSubjectLength: 0,
|
||||
scopeOverrides: undefined,
|
||||
defaultBody: '',
|
||||
defaultIssues: '',
|
||||
defaultScope: '',
|
||||
defaultSubject: ''
|
||||
}
|
||||
}
|
@ -15,25 +15,24 @@ indent_size = 2
|
||||
|
||||
[*.cs]
|
||||
dotnet_analyzer_diagnostic.severity = warning
|
||||
dotnet_diagnostic.CA1200.severity = none
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
dotnet_diagnostic.CA1716.severity = none
|
||||
dotnet_diagnostic.IDE0005.severity = none
|
||||
dotnet_diagnostic.IDE0008.severity = none
|
||||
dotnet_diagnostic.IDE0010.severity = none
|
||||
dotnet_diagnostic.IDE0028.severity = none
|
||||
dotnet_diagnostic.IDE0055.severity = none
|
||||
dotnet_diagnostic.IDE0160.severity = none
|
||||
dotnet_diagnostic.IDE0270.severity = none
|
||||
dotnet_diagnostic.IDE0300.severity = none
|
||||
dotnet_diagnostic.IDE0305.severity = none
|
||||
dotnet_diagnostic.RCS1141.severity = none
|
||||
dotnet_diagnostic.RCS1142.severity = none
|
||||
dotnet_diagnostic.RCS1181.severity = none
|
||||
dotnet_diagnostic.RCS1186.severity = none
|
||||
dotnet_diagnostic.S101.severity = none
|
||||
dotnet_diagnostic.S1121.severity = none
|
||||
dotnet_diagnostic.S1199.severity = none
|
||||
dotnet_diagnostic.S1135.severity = none
|
||||
dotnet_diagnostic.S125.severity = none
|
||||
dotnet_diagnostic.S2094.severity = none
|
||||
dotnet_diagnostic.S3925.severity = none
|
||||
dotnet_diagnostic.S3604.severity = none
|
||||
dotnet_diagnostic.S4663.severity = none
|
||||
dotnet_diagnostic.SYSLIB1045.severity = none
|
||||
|
||||
|
52
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
# This workflow will build a .NET project
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [ 20.x ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
# https://docs.github.com/zh/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('./src/frontend/admin/package.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm
|
||||
- name: Publish frontend
|
||||
working-directory: ./src/frontend/admin
|
||||
run:
|
||||
npm install && npm run build
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: ${{ runner.os }}-nuget
|
||||
- name: Publish backend
|
||||
working-directory: ./src/backend/NetAdmin.BizServer.Host
|
||||
run: dotnet publish NetAdmin.BizServer.Host.csproj -c Release
|
||||
- name: Build docker images
|
||||
run: docker build -t nsnail/netadmin .
|
||||
- name: Docker login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: "nsnail"
|
||||
password: "${{secrets.DOCKER_PASSWORD}}"
|
||||
- name: Push docker images
|
||||
run: docker push nsnail/netadmin
|
4
.tgitconfig
Normal file
@ -0,0 +1,4 @@
|
||||
[hook "startcommit"]
|
||||
cmdline = dot.clean.cmd
|
||||
wait = true
|
||||
show = true
|
10
1.git.pr.ps1
Normal file
@ -0,0 +1,10 @@
|
||||
$branch = $( git branch --show-current )
|
||||
./dot.clean.cmd
|
||||
git add .
|
||||
./node_modules/.bin/git-cz.ps1
|
||||
git pull
|
||||
git push --set-upstream origin $branch
|
||||
Start-Process -FilePath "https://github.com/nsnail/NetAdmin/compare/main...$branch"
|
||||
Write-Host "按『Enter』重建分支,『Ctrl+C』退出"
|
||||
Pause
|
||||
./git.rc.ps1
|
198
Build.cake
@ -1,198 +0,0 @@
|
||||
var target = Argument("target", "Default");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
var outputDirectory = Argument("output-directory", "./dist/Server/publish");
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Tasks
|
||||
|
||||
Task("Clean")
|
||||
.Does(context =>
|
||||
{
|
||||
context.CleanDirectory("./dist");
|
||||
});
|
||||
|
||||
Task("Build")
|
||||
.IsDependentOn("Clean")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetBuild("./NetAdmin.sln", new DotNetBuildSettings {
|
||||
Configuration = configuration
|
||||
});
|
||||
});
|
||||
|
||||
Task("Publish-BizServer")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetPublish("./src/Server/NetAdmin.BizServer.Host/NetAdmin.BizServer.Host.csproj", new DotNetPublishSettings {
|
||||
NoBuild = true,
|
||||
Configuration = configuration,
|
||||
OutputDirectory = new DirectoryPath(outputDirectory)
|
||||
});
|
||||
});
|
||||
|
||||
Task("Publish-SdkServer")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetPublish("./src/Server/NetAdmin.SdkServer.Host/NetAdmin.SdkServer.Host.csproj", new DotNetPublishSettings {
|
||||
NoBuild = true,
|
||||
Configuration = configuration,
|
||||
OutputDirectory = new DirectoryPath(outputDirectory)
|
||||
});
|
||||
});
|
||||
|
||||
Task("Publish-ManServer")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetPublish("./src/Server/NetAdmin.ManServer.Host/NetAdmin.ManServer.Host.csproj", new DotNetPublishSettings {
|
||||
NoBuild = true,
|
||||
Configuration = configuration,
|
||||
OutputDirectory = new DirectoryPath(outputDirectory)
|
||||
});
|
||||
});
|
||||
|
||||
Task("Publish-SdkService")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetPublish("./src/Server/NetAdmin.SdkService/NetAdmin.SdkService.csproj", new DotNetPublishSettings {
|
||||
NoBuild = true,
|
||||
Configuration = configuration,
|
||||
OutputDirectory = new DirectoryPath(outputDirectory)
|
||||
});
|
||||
});
|
||||
|
||||
Task("Publish-ManService")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetPublish("./src/Server/NetAdmin.ManService/NetAdmin.ManService.csproj", new DotNetPublishSettings {
|
||||
NoBuild = true,
|
||||
Configuration = configuration,
|
||||
OutputDirectory = new DirectoryPath(outputDirectory)
|
||||
});
|
||||
});
|
||||
|
||||
Task("Publish-CallbackService")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetPublish("./src/Server/NetAdmin.CallbackService/NetAdmin.CallbackService.csproj", new DotNetPublishSettings {
|
||||
NoBuild = true,
|
||||
Configuration = configuration,
|
||||
OutputDirectory = new DirectoryPath(outputDirectory)
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Task("Publish-ScheduledService")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetPublish("./src/Server/NetAdmin.ScheduledService/NetAdmin.ScheduledService.csproj", new DotNetPublishSettings {
|
||||
NoBuild = true,
|
||||
Configuration = configuration,
|
||||
OutputDirectory = new DirectoryPath(outputDirectory)
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Task("Publish-PushService")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetPublish("./src/Server/NetAdmin.PushService/NetAdmin.PushService.csproj", new DotNetPublishSettings {
|
||||
NoBuild = true,
|
||||
Configuration = configuration,
|
||||
OutputDirectory = new DirectoryPath(outputDirectory)
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Task("Test")
|
||||
// .IsDependentOn("Build")
|
||||
// .Does(context =>
|
||||
// {
|
||||
// DotNetTest("./test/Spectre.Console.Tests/Spectre.Console.Tests.csproj", new DotNetTestSettings {
|
||||
// Configuration = configuration,
|
||||
// NoRestore = true,
|
||||
// NoBuild = true,
|
||||
// });
|
||||
//
|
||||
// DotNetTest("./test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj", new DotNetTestSettings {
|
||||
// Configuration = configuration,
|
||||
// NoRestore = true,
|
||||
// NoBuild = true,
|
||||
// });
|
||||
//
|
||||
// DotNetTest("./test/Spectre.Console.Analyzer.Tests/Spectre.Console.Analyzer.Tests.csproj", new DotNetTestSettings {
|
||||
// Configuration = configuration,
|
||||
// NoRestore = true,
|
||||
// NoBuild = true,
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// Task("Publish-GitHub")
|
||||
// .WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
|
||||
// //.IsDependentOn("Package")
|
||||
// .Does(context =>
|
||||
// {
|
||||
// var apiKey = Argument<string>("github-key", null);
|
||||
// if(string.IsNullOrWhiteSpace(apiKey)) {
|
||||
// throw new CakeException("No GitHub API key was provided.");
|
||||
// }
|
||||
//
|
||||
// // Publish to GitHub Packages
|
||||
// var exitCode = 0;
|
||||
// foreach(var file in context.GetFiles("./.artifacts/*.nupkg"))
|
||||
// {
|
||||
// context.Information("Publishing {0}...", file.GetFilename().FullPath);
|
||||
// exitCode += StartProcess("dotnet",
|
||||
// new ProcessSettings {
|
||||
// Arguments = new ProcessArgumentBuilder()
|
||||
// .Append("gpr")
|
||||
// .Append("push")
|
||||
// .AppendQuoted(file.FullPath)
|
||||
// .AppendSwitchSecret("-k", " ", apiKey)
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// if(exitCode != 0)
|
||||
// {
|
||||
// throw new CakeException("Could not push GitHub packages.");
|
||||
// }
|
||||
// });
|
||||
|
||||
// Task("Publish-NuGet")
|
||||
// //.WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
|
||||
// //.IsDependentOn("Package")
|
||||
// .Does(context =>
|
||||
// {
|
||||
// var apiKey = Argument<string>("nuget-key", null);
|
||||
// if(string.IsNullOrWhiteSpace(apiKey)) {
|
||||
// throw new CakeException("No NuGet API key was provided.");
|
||||
// }
|
||||
//
|
||||
// // Publish to GitHub Packages
|
||||
// foreach(var file in context.GetFiles("./.artifacts/*.nupkg"))
|
||||
// {
|
||||
// context.Information("Publishing {0}...", file.GetFilename().FullPath);
|
||||
// DotNetNuGetPush(file.FullPath, new DotNetNuGetPushSettings
|
||||
// {
|
||||
// Source = "https://api.nuget.org/v3/index.json",
|
||||
// ApiKey = apiKey,
|
||||
// SkipDuplicate = true
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Targets
|
||||
|
||||
// Task("Publish")
|
||||
// .IsDependentOn("Publish-GitHub")
|
||||
// .IsDependentOn("Publish-NuGet");
|
||||
|
||||
Task("Default")
|
||||
.IsDependentOn("Build");
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Execution
|
||||
|
||||
RunTarget(target)
|
10
CHANGELOG.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## 1.0.0 (2023-11-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ 基础模块 ([cd2a241](https://github.com/nsnail/NetAdmin/commit/cd2a2412b91bd07346b1d95b19102d48883479a5))
|
@ -1,26 +1,34 @@
|
||||
<!-- 注意此文件名大小写不可变更 -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- $(XXX) 定义有顺序 排序请注意-->
|
||||
<SolutionDir>$(MSBuildThisFileDirectory)</SolutionDir>
|
||||
<BaseIntermediateOutputPath>$(SolutionDir)/dist/Server/$(MSBuildProjectName)/obj</BaseIntermediateOutputPath>
|
||||
<BaseOutputPath>$(SolutionDir)/dist/Server/$(MSBuildProjectName)/bin</BaseOutputPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)/minver.targets"/>
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Authors>nsnail</Authors>
|
||||
<BaseIntermediateOutputPath>$(SolutionDir)/dist/backend/$(MSBuildProjectName)/obj</BaseIntermediateOutputPath>
|
||||
<BaseOutputPath>$(SolutionDir)/dist/backend/$(MSBuildProjectName)/bin</BaseOutputPath>
|
||||
<Copyright>© 2006-2023 nsnail</Copyright>
|
||||
<Description>一个基于.Net8/Vue3,极致优雅的RBAC通用权限管理模板</Description>
|
||||
<EnableBaseIntermediateOutputPathMismatchWarning>false</EnableBaseIntermediateOutputPathMismatchWarning>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<MinVerDefaultPreReleaseIdentifiers>beta</MinVerDefaultPreReleaseIdentifiers>
|
||||
<Product>NetAdmin</Product>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>http://git.shequnpay.com/lingyun/NetAdmin.git</RepositoryUrl>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MinVer" Version="5.0.0-alpha.1">
|
||||
<PackageReference Include="MinVer" Version="5.0.0-beta.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="../GlobalUsings.cs" Link="GlobalUsings.cs" />
|
||||
<Compile Include="../GlobalUsings.cs" Link="GlobalUsings.cs"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
7
Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
RUN apt update
|
||||
RUN apt install -y redis
|
||||
COPY ./dist/backend/NetAdmin.BizServer.Host/bin/Release/net8.0/publish .
|
||||
ENTRYPOINT redis-server --daemonize yes && dotnet NetAdmin.BizServer.Host.dll
|
76
NetAdmin.sln
@ -9,23 +9,46 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "backend", "backend", "{4DAF
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{5198A03D-0CAC-4828-A807-34A693F73859}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.commitlintrc.js = .commitlintrc.js
|
||||
.editorconfig = .editorconfig
|
||||
.gitattributes = .gitattributes
|
||||
.gitignore = .gitignore
|
||||
Build.cake = Build.cake
|
||||
CodeQuality.props = CodeQuality.props
|
||||
CopyPackageXmlCommentFiles.targets = CopyPackageXmlCommentFiles.targets
|
||||
.tgitconfig = .tgitconfig
|
||||
1.git.pr.ps1 = 1.git.pr.ps1
|
||||
build.cake = build.cake
|
||||
clone.project.refs.ps1 = clone.project.refs.ps1
|
||||
code.clean.csx = code.clean.csx
|
||||
code.clean.ps1 = code.clean.ps1
|
||||
code.cleanup.full.ps1 = code.cleanup.full.ps1
|
||||
code.quality.props = code.quality.props
|
||||
copy.pkg.xml.comment.files.targets = copy.pkg.xml.comment.files.targets
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Dockerfile = Dockerfile
|
||||
dot.clean.cmd = dot.clean.cmd
|
||||
dotnet-tools.json = dotnet-tools.json
|
||||
gen.cs.tt = gen.cs.tt
|
||||
gen.id.linq = gen.id.linq
|
||||
gen.ln.cmd = gen.ln.cmd
|
||||
gen.resx.tt = gen.resx.tt
|
||||
git.rc.ps1 = git.rc.ps1
|
||||
global.json = global.json
|
||||
image.optimize.csx = image.optimize.csx
|
||||
install.as.tpl.ps1 = install.as.tpl.ps1
|
||||
LICENSE = LICENSE
|
||||
minver.targets = minver.targets
|
||||
NetAdmin.sln.DotSettings = NetAdmin.sln.DotSettings
|
||||
NuGet.Config = NuGet.Config
|
||||
nuget.config = nuget.config
|
||||
package.json = package.json
|
||||
PreBuild.targets = PreBuild.targets
|
||||
prebuild.targets = prebuild.targets
|
||||
README.md = README.md
|
||||
rename.csx = rename.csx
|
||||
stylecop.analyzers.ruleset = stylecop.analyzers.ruleset
|
||||
StyleCop.json = StyleCop.json
|
||||
StyleCopAnalyzers.ruleset = StyleCopAnalyzers.ruleset
|
||||
switcher.freesql.json = switcher.freesql.json
|
||||
switcher.furion.json = switcher.furion.json
|
||||
switcher.nsext.json = switcher.nsext.json
|
||||
switcher.ps1 = switcher.ps1
|
||||
sync.sln.files.csx = sync.sln.files.csx
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetAdmin.Infrastructure", "src\backend\NetAdmin.Infrastructure\NetAdmin.Infrastructure.csproj", "{1E62C322-EE42-4699-A6F1-791C53EFA62D}"
|
||||
@ -54,8 +77,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetAdmin.SysComponent.Appli
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02.Components", "02.Components", "{3F23258D-8299-4992-9F51-2EE9B52CF9D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "21.System", "21.System", "{E146D707-4D44-47B3-A8F9-D51EC5E1D047}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04.BackgroundServices", "04.BackgroundServices", "{CBFBF29B-27E8-4DB1-ADD6-4B750897ACD3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01.Frameworks", "01.Frameworks", "{D9C3EF66-2757-473D-A26B-54FD08DA203F}"
|
||||
@ -66,28 +87,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "06.Tests", "06.Tests", "{89
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetAdmin.BizServer.Tests", "src\backend\NetAdmin.BizServer.Tests\NetAdmin.BizServer.Tests.csproj", "{C7F27698-DA05-4ACD-B0D7-4791B3972002}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{2D81D62C-1B2E-4758-84C6-728343CB734F}"
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1129FE25-466B-4F4F-85FC-3752664245E1}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{3C6F049E-3EE8-4D66-9AFF-E8A369032487}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
CloneProjectRefs.ps1 = tools/CloneProjectRefs.ps1
|
||||
CodeCleanup.csx = tools/CodeCleanup.csx
|
||||
CodeCleanup.ps1 = tools/CodeCleanup.ps1
|
||||
CodeCleanupFull.ps1 = tools/CodeCleanupFull.ps1
|
||||
DotClean.cmd = tools/DotClean.cmd
|
||||
GenerateLn.cmd = tools/GenerateLn.cmd
|
||||
GenerateLnCs.tt = tools/GenerateLnCs.tt
|
||||
GenerateLnResx.tt = tools/GenerateLnResx.tt
|
||||
GitPR.ps1 = tools/GitPR.ps1
|
||||
GitRecreate.ps1 = tools/GitRecreate.ps1
|
||||
IdGenerator.linq = tools/IdGenerator.linq
|
||||
ImageOptimize.csx = tools/ImageOptimize.csx
|
||||
InstallAsTpl.ps1 = tools/InstallAsTpl.ps1
|
||||
Switcher.FreeSql.json = tools/Switcher.FreeSql.json
|
||||
Switcher.Furion.json = tools/Switcher.Furion.json
|
||||
Switcher.NSExt.json = tools/Switcher.NSExt.json
|
||||
Switcher.ps1 = tools/Switcher.ps1
|
||||
SyncMetaFiles.csx = tools/SyncMetaFiles.csx
|
||||
ci.yml = .github/workflows/ci.yml
|
||||
EndProjectSection
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetAdmin.Tests", "src\backend\NetAdmin.Tests\NetAdmin.Tests.csproj", "{00604162-C444-478B-B773-3AB23C856CA7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -150,15 +157,15 @@ Global
|
||||
{C7F27698-DA05-4ACD-B0D7-4791B3972002}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C7F27698-DA05-4ACD-B0D7-4791B3972002}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C7F27698-DA05-4ACD-B0D7-4791B3972002}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00604162-C444-478B-B773-3AB23C856CA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00604162-C444-478B-B773-3AB23C856CA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00604162-C444-478B-B773-3AB23C856CA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00604162-C444-478B-B773-3AB23C856CA7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{4DAF9366-855F-46BB-AE4C-660C92FA0697} = {C84EB5A0-37AD-4B17-A51E-E36888C4441E}
|
||||
{12AE5B4B-CB1A-498E-83B8-04E201E31D86} = {4DAF9366-855F-46BB-AE4C-660C92FA0697}
|
||||
{3F23258D-8299-4992-9F51-2EE9B52CF9D2} = {4DAF9366-855F-46BB-AE4C-660C92FA0697}
|
||||
{E146D707-4D44-47B3-A8F9-D51EC5E1D047} = {3F23258D-8299-4992-9F51-2EE9B52CF9D2}
|
||||
{34650E82-D257-46DA-BD6B-DE307113347B} = {E146D707-4D44-47B3-A8F9-D51EC5E1D047}
|
||||
{19872A4C-3C9A-4C62-A33B-74F5B8D6F77C} = {E146D707-4D44-47B3-A8F9-D51EC5E1D047}
|
||||
{C2CC1596-3BEE-43EA-A9BE-4EDE5716296C} = {E146D707-4D44-47B3-A8F9-D51EC5E1D047}
|
||||
{CBFBF29B-27E8-4DB1-ADD6-4B750897ACD3} = {4DAF9366-855F-46BB-AE4C-660C92FA0697}
|
||||
{D9C3EF66-2757-473D-A26B-54FD08DA203F} = {4DAF9366-855F-46BB-AE4C-660C92FA0697}
|
||||
{70C54E1B-2083-4196-AB68-34CAF0075D82} = {D9C3EF66-2757-473D-A26B-54FD08DA203F}
|
||||
@ -172,5 +179,10 @@ Global
|
||||
{CE895E44-EEC3-4ECE-A56A-8A82E7D863E3} = {12AE5B4B-CB1A-498E-83B8-04E201E31D86}
|
||||
{89260294-80FC-49F1-8D73-AECD39AFF2B7} = {4DAF9366-855F-46BB-AE4C-660C92FA0697}
|
||||
{C7F27698-DA05-4ACD-B0D7-4791B3972002} = {89260294-80FC-49F1-8D73-AECD39AFF2B7}
|
||||
{3C6F049E-3EE8-4D66-9AFF-E8A369032487} = {1129FE25-466B-4F4F-85FC-3752664245E1}
|
||||
{00604162-C444-478B-B773-3AB23C856CA7} = {D9C3EF66-2757-473D-A26B-54FD08DA203F}
|
||||
{34650E82-D257-46DA-BD6B-DE307113347B} = {3F23258D-8299-4992-9F51-2EE9B52CF9D2}
|
||||
{19872A4C-3C9A-4C62-A33B-74F5B8D6F77C} = {3F23258D-8299-4992-9F51-2EE9B52CF9D2}
|
||||
{C2CC1596-3BEE-43EA-A9BE-4EDE5716296C} = {3F23258D-8299-4992-9F51-2EE9B52CF9D2}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.cdn.azure.cn" value="https://nuget.cdn.azure.cn/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
@ -1,24 +0,0 @@
|
||||
<Project>
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Command="dotnet tool restore" StdOutEncoding="utf-8"/>
|
||||
<Exec Condition="!Exists('$(SolutionDir)/assets/resx/Ln.resx')" WorkingDirectory="$(SolutionDir)/tools"
|
||||
Command="dotnet t4 $(SolutionDir)/tools/GenerateLnResx.tt -o $(SolutionDir)/assets/resx/Ln.resx"
|
||||
StdOutEncoding="utf-8"/>
|
||||
<Exec Condition="!Exists('$(SolutionDir)/dist/Server/$(ProjectName)/Ln.cs')"
|
||||
WorkingDirectory="$(SolutionDir)/tools"
|
||||
Command="dotnet t4 $(SolutionDir)/tools/GenerateLnCs.tt -o $(SolutionDir)/dist/Server/$(ProjectName)/Ln.cs"
|
||||
StdOutEncoding="utf-8"/>
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<None Include="$(SolutionDir)/assets/resx/Ln.txt">
|
||||
<Link>Languages/Ln.txt</Link>
|
||||
</None>
|
||||
<EmbeddedResource Include="$(SolutionDir)/assets/resx/Ln.resx">
|
||||
<Link>Languages/Ln.resx</Link>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="$(SolutionDir)/dist/Server/$(ProjectName)/Ln.Designer.cs">
|
||||
<Link>Languages/Ln.Designer.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
121
README.md
@ -1,73 +1,87 @@
|
||||
# NetAdmin
|
||||
|
||||
## Git Commits 语义
|
||||
|
||||
- `FEA` 新增特性
|
||||
- `REF` 项目重构
|
||||
- `FIX` 缺陷修复
|
||||
- `PER` 性能优化
|
||||
- `RVT` 还原变更
|
||||
- `FMT` 格式整理
|
||||
- `DOC` 文档变更
|
||||
- `TST` 单元测试
|
||||
- `BLD` 工程构建
|
||||
通用后台权限管理系统、快速开发框架(基于C#12/.NET8、Vue3/Vite、Element Plus等现代技术构建,具有十分整洁、优雅的编码规范)
|
||||
|
||||
|
||||
## 构建指南
|
||||
1. 后端
|
||||
1. 检查dotnet-sdk版本>=7.0.0
|
||||
```
|
||||
[](https://github.com/nsnail/NetAdmin/actions/workflows/ci.yml)
|
||||
[](https://github.com/nsnail/NetAdmin/blob/main/LICENSE)
|
||||
[](https://github.com/nsnail/NetAdmin/blob/main/LICENSE)
|
||||
[](https://github.com/nsnail/NetAdmin/blob/main/LICENSE)
|
||||
|
||||
## 在线预览
|
||||
|
||||
http://na.yaopy.com
|
||||
|
||||
## 一键运行
|
||||
|
||||
```shell
|
||||
docker run -p 8080:8080 nsnail/netadmin
|
||||
```
|
||||
|
||||
## 构建步骤
|
||||
|
||||
- 后端
|
||||
1. 检查dotnet-sdk版本>=8.0.0
|
||||
``` shell
|
||||
dotnet --list-sdks
|
||||
|
||||
# 下载 dotnet https://dotnet.microsoft.com/zh-cn/download/dotnet
|
||||
```
|
||||
2. 克隆代码仓库
|
||||
```
|
||||
``` shell
|
||||
git clone https://github.com/nsnail/NetAdmin.git
|
||||
cd ./NetAdmin
|
||||
|
||||
# 下载 git https://git-scm.com/downloads
|
||||
```
|
||||
3. 确保本机redis处于运行状态
|
||||
```
|
||||
``` shell
|
||||
redis-cli
|
||||
|
||||
# 下载 redis for windows https://github.com/redis-windows/redis-windows/releases
|
||||
# 下载 redis for linux/mac https://redis.io/download
|
||||
```
|
||||
4. 运行后端WebApi
|
||||
```
|
||||
dotnet run --project ./src/backend/NetAdmin.BizServer.Host/NetAdmin.BizServer.Host.csproj --urls http://[::]:65010
|
||||
``` shell
|
||||
dotnet run --project ./src/backend/NetAdmin.BizServer.Host/NetAdmin.BizServer.Host.csproj --urls http://[::]:5010
|
||||
```
|
||||
5. 体验WebApi程序
|
||||
```
|
||||
浏览器打开 http://localhost:65010 ,将看到Swagger(Knife4jUI)界面
|
||||
```
|
||||
2. 前端
|
||||
- 浏览器打开 http://localhost:5010 ,将看到Swagger(Knife4jUI)界面
|
||||
|
||||
---
|
||||
|
||||
- 前端
|
||||
1. 检查nodejs版本>=20
|
||||
```
|
||||
``` shell
|
||||
node -v
|
||||
|
||||
# 下载 nodejs https://nodejs.org/en/download
|
||||
```
|
||||
2. 安装npm依赖包
|
||||
```
|
||||
``` shell
|
||||
cd ./src/frontend/admin
|
||||
npm install
|
||||
```
|
||||
3. 运行前端项目
|
||||
```
|
||||
``` shell
|
||||
npm run dev
|
||||
```
|
||||
4. 体验前端程序
|
||||
```
|
||||
浏览器打开 http://localhost:65020 ,将看到管理界面(默认用户名:root,密码:1234qwer)
|
||||
```
|
||||
- 浏览器打开 http://localhost:5020 ,将看到管理界面(默认用户名:root,密码:1234qwer)
|
||||
|
||||
## 文件目录树
|
||||
|
||||
## 项目文件目录树描述
|
||||
```
|
||||
+---.template.config dotnet 项目模板配置目录
|
||||
+---assets 程序运行需要的资源文件目录
|
||||
+---dist 项目编译与分发的二进制文件目录
|
||||
+---refs 引用的第三方项目源文件目录
|
||||
+---src 项目源文件目录
|
||||
| +---backend 后端程序源文件目录
|
||||
| \---frontend 前端程序源文件目录
|
||||
\---tools 构建相关的工具目录
|
||||
+---.template.config # dotnet 项目模板配置目录
|
||||
+---assets # 程序运行需要的资源文件目录
|
||||
+---dist # 程序编译与分发的二进制文件目录
|
||||
+---docs # 项目文档目录
|
||||
+---refs # 引用的第三方项目仓库目录
|
||||
+---src # 项目源文件目录
|
||||
```
|
||||
|
||||
## 后端项目架构
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
H["NetAdmin.Host\n公共主机层\n(.Net自托管主机程序)\n(输入输出格式化)\n(数据校验、鉴权)\n(...所有HTTP管道过滤器中间件)"] --> C["NetAdmin.Cache\n公共缓存层\n(基于Redis或MemoryCache的缓存策略实现)"]
|
||||
@ -84,3 +98,34 @@ XA["NetAdmin.XXX.Application\n(业务逻辑层实例)"]-->A
|
||||
XH-->XC
|
||||
XC-->XA
|
||||
```
|
||||
|
||||
## 引用的开源代码 / 特别鸣谢
|
||||
|
||||
| 语言 | 集成领域 | 开源库 |
|
||||
|------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| C# | Web基础框架 | [ASP.NET Core](https://github.com/dotnet/aspnetcore) |
|
||||
| C# | 快速开发脚手架 | [Furion](https://gitee.com/dotnetchina/Furion) |
|
||||
| C# | 数据库关系映射 | [FreeSql](https://github.com/dotnetcore/FreeSql) |
|
||||
| C# | 代码质量检查 | [Roslynator.Analyzers](https://github.com/josefpihrt/roslynator) \| [SonarAnalyzer.CSharp](https://github.com/SonarSource/sonar-dotnet) \| [StyleCop.Analyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) |
|
||||
| C# | 单元测试框架 | [xunit](https://github.com/xunit/xunit) \| [coverlet.collector](https://github.com/coverlet-coverage/coverlet) |
|
||||
| C# | 分布式锁 | [RedLock.net](https://github.com/samcook/RedLock.net) |
|
||||
| C# | 控制台终端界面库 | [Spectre.Console](https://github.com/spectreconsole/spectre.console) |
|
||||
| C# | 扩展函数库 | [NSExt](https://github.com/nsnail/ns-ext.git) |
|
||||
| C# | 图形处理库 | [SixLabors.ImageSharp](https://github.com/SixLabors/ImageSharp) |
|
||||
| C# | 文件对象存储 | [Minio](https://github.com/minio/minio-dotnet) |
|
||||
| C# | 性能监控采集 | [prometheus-net.AspNetCore](https://github.com/prometheus-net/prometheus-net) |
|
||||
| C# | 雪花ID生成器 | [Yitter.IdGenerator](https://github.com/yitter/idgenerator) |
|
||||
| C# | 自动化版本管理 | [MinVer](https://github.com/adamralph/minver) |
|
||||
| C# | JavaScript引擎 | [MsieJavaScriptEngine](https://github.com/Taritsyn/MsieJavaScriptEngine) |
|
||||
| C# | WebApi图形界面 | [IGeekFan.AspNetCore.Knife4jUI](https://github.com/luoyunchong/IGeekFan.AspNetCore.Knife4jUI) |
|
||||
| TypeScript | SPA基础框架 | [Vue](https://github.com/vuejs/core) |
|
||||
| TypeScript | 前端构建工具 | [Vite](https://github.com/vitejs/vite) |
|
||||
| TypeScript | UI控件库 | [Element Plus](https://github.com/element-plus/element-plus) |
|
||||
| TypeScript | CSS解析器 | [Sass](https://github.com/sass/sass) |
|
||||
| TypeScript | 图表和数据可视化 | [ECharts](https://github.com/apache/echarts) |
|
||||
| JavaScript | 后台管理界面 | [SCUI](https://gitee.com/lolicode/scui) |
|
||||
| JavaScript | HTTP请求库 | [Axios](https://github.com/axios/axios) |
|
||||
| JavaScript | JavaScript解析器 | [Terser](https://github.com/terser/terser) |
|
||||
| JavaScript | 代码质量检查 | [ESLint](https://github.com/eslint/eslint) |
|
||||
| JavaScript | 代码格式化工具 | [Prettier](https://github.com/prettier/prettier) |
|
||||
| JavaScript | 标准加密库 | [crypto-js](https://github.com/brix/crypto-js) |
|
Before Width: | Height: | Size: 3.9 KiB |
@ -1,15 +0,0 @@
|
||||
version: "3"
|
||||
services:
|
||||
net-admin-minio:
|
||||
image: minio/minio:latest
|
||||
command: server /data --console-address ":9001"
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
restart: always
|
||||
container_name: net-admin-minio
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime
|
||||
- /etc/timezone:/etc/timezone
|
@ -22,52 +22,63 @@
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
// ReSharper disable DuplicateResource
|
||||
<data name="_1分钟内只能发送1次" xml:space="preserve"><value>1分钟内只能发送1次</value></data>
|
||||
<data name="_6位数字" xml:space="preserve"><value>6位数字</value></data>
|
||||
<data name="_8位以上数字字母组合" xml:space="preserve"><value>8位以上数字字母组合</value></data>
|
||||
<data name="Xml注释文件不存在" xml:space="preserve"><value>Xml注释文件不存在</value></data>
|
||||
<data name="不正确" xml:space="preserve"><value>不正确</value></data>
|
||||
<data name="不能为空" xml:space="preserve"><value>不能为空</value></data>
|
||||
<data name="XML注释文件不存在" xml:space="preserve"><value>XML注释文件不存在</value></data>
|
||||
<data name="不为其中之一" xml:space="preserve"><value>不为其中之一</value></data>
|
||||
<data name="不以什么开始" xml:space="preserve"><value>不以什么开始</value></data>
|
||||
<data name="不以什么结束" xml:space="preserve"><value>不以什么结束</value></data>
|
||||
<data name="不包含" xml:space="preserve"><value>不包含</value></data>
|
||||
<data name="不等于" xml:space="preserve"><value>不等于</value></data>
|
||||
<data name="业务模块" xml:space="preserve"><value>业务模块</value></data>
|
||||
<data name="东乡族" xml:space="preserve"><value>东乡族</value></data>
|
||||
<data name="丧偶" xml:space="preserve"><value>丧偶</value></data>
|
||||
<data name="中专" xml:space="preserve"><value>中专</value></data>
|
||||
<data name="中共党员" xml:space="preserve"><value>中共党员</value></data>
|
||||
<data name="中文姓名" xml:space="preserve"><value>中文姓名</value></data>
|
||||
<data name="为其中之一" xml:space="preserve"><value>为其中之一</value></data>
|
||||
<data name="乌孜别克族" xml:space="preserve"><value>乌孜别克族</value></data>
|
||||
<data name="事务已回滚" xml:space="preserve"><value>事务已回滚</value></data>
|
||||
<data name="事务已提交" xml:space="preserve"><value>事务已提交</value></data>
|
||||
<data name="京族" xml:space="preserve"><value>京族</value></data>
|
||||
<data name="人机校验请求" xml:space="preserve"><value>人机校验请求</value></data>
|
||||
<data name="人机校验请求不能为空" xml:space="preserve"><value>人机校验请求不能为空</value></data>
|
||||
<data name="人机验证未通过" xml:space="preserve"><value>人机验证未通过</value></data>
|
||||
<data name="仡佬族" xml:space="preserve"><value>仡佬族</value></data>
|
||||
<data name="以什么开始" xml:space="preserve"><value>以什么开始</value></data>
|
||||
<data name="以什么结束" xml:space="preserve"><value>以什么结束</value></data>
|
||||
<data name="仫佬族" xml:space="preserve"><value>仫佬族</value></data>
|
||||
<data name="佤族" xml:space="preserve"><value>佤族</value></data>
|
||||
<data name="侗族" xml:space="preserve"><value>侗族</value></data>
|
||||
<data name="俄罗斯族" xml:space="preserve"><value>俄罗斯族</value></data>
|
||||
<data name="保安族" xml:space="preserve"><value>保安族</value></data>
|
||||
<data name="保密" xml:space="preserve"><value>保密</value></data>
|
||||
<data name="信息" xml:space="preserve"><value>信息</value></data>
|
||||
<data name="倒序排序" xml:space="preserve"><value>倒序排序</value></data>
|
||||
<data name="傈僳族" xml:space="preserve"><value>傈僳族</value></data>
|
||||
<data name="傣族" xml:space="preserve"><value>傣族</value></data>
|
||||
<data name="允许的文件大小" xml:space="preserve"><value>允许的文件大小</value></data>
|
||||
<data name="允许的文件格式" xml:space="preserve"><value>允许的文件格式</value></data>
|
||||
<data name="全部数据" xml:space="preserve"><value>全部数据</value></data>
|
||||
<data name="公告" xml:space="preserve"><value>公告</value></data>
|
||||
<data name="共青团员" xml:space="preserve"><value>共青团员</value></data>
|
||||
<data name="出生证" xml:space="preserve"><value>出生证</value></data>
|
||||
<data name="初中" xml:space="preserve"><value>初中</value></data>
|
||||
<data name="初始化完毕" xml:space="preserve"><value>初始化完毕</value></data>
|
||||
<data name="删除" xml:space="preserve"><value>删除</value></data>
|
||||
<data name="包含" xml:space="preserve"><value>包含</value></data>
|
||||
<data name="区号电话号码分机号" xml:space="preserve"><value>区号电话号码分机号</value></data>
|
||||
<data name="博士" xml:space="preserve"><value>博士</value></data>
|
||||
<data name="博士后" xml:space="preserve"><value>博士后</value></data>
|
||||
<data name="参数格式不正确" xml:space="preserve"><value>参数格式不正确</value></data>
|
||||
<data name="发送失败" xml:space="preserve"><value>发送失败</value></data>
|
||||
<data name="同步数据库结构" xml:space="preserve"><value>同步数据库结构</value></data>
|
||||
<data name="哈尼族" xml:space="preserve"><value>哈尼族</value></data>
|
||||
<data name="哈萨克族" xml:space="preserve"><value>哈萨克族</value></data>
|
||||
<data name="唯一编码" xml:space="preserve"><value>唯一编码</value></data>
|
||||
<data name="唯一编码不能为空" xml:space="preserve"><value>唯一编码不能为空</value></data>
|
||||
<data name="回族" xml:space="preserve"><value>回族</value></data>
|
||||
<data name="图标代码" xml:space="preserve"><value>图标代码</value></data>
|
||||
<data name="图标名称" xml:space="preserve"><value>图标名称</value></data>
|
||||
<data name="图标代码不能为空" xml:space="preserve"><value>图标代码不能为空</value></data>
|
||||
<data name="图标名称不能为空" xml:space="preserve"><value>图标名称不能为空</value></data>
|
||||
<data name="土家族" xml:space="preserve"><value>土家族</value></data>
|
||||
<data name="土族" xml:space="preserve"><value>土族</value></data>
|
||||
<data name="基诺族" xml:space="preserve"><value>基诺族</value></data>
|
||||
@ -76,65 +87,84 @@
|
||||
<data name="壮族" xml:space="preserve"><value>壮族</value></data>
|
||||
<data name="外国人居留证" xml:space="preserve"><value>外国人居留证</value></data>
|
||||
<data name="大专" xml:space="preserve"><value>大专</value></data>
|
||||
<data name="大于" xml:space="preserve"><value>大于</value></data>
|
||||
<data name="大于等于" xml:space="preserve"><value>大于等于</value></data>
|
||||
<data name="女" xml:space="preserve"><value>女</value></data>
|
||||
<data name="字典名称" xml:space="preserve"><value>字典名称</value></data>
|
||||
<data name="字典名称不能为空" xml:space="preserve"><value>字典名称不能为空</value></data>
|
||||
<data name="字典目录不存在" xml:space="preserve"><value>字典目录不存在</value></data>
|
||||
<data name="字典目录编号" xml:space="preserve"><value>字典目录编号</value></data>
|
||||
<data name="字典编码" xml:space="preserve"><value>字典编码</value></data>
|
||||
<data name="密码" xml:space="preserve"><value>密码</value></data>
|
||||
<data name="字典目录编号不能为空" xml:space="preserve"><value>字典目录编号不能为空</value></data>
|
||||
<data name="字典编码不能为空" xml:space="preserve"><value>字典编码不能为空</value></data>
|
||||
<data name="宕机" xml:space="preserve"><value>宕机</value></data>
|
||||
<data name="密码不能为空" xml:space="preserve"><value>密码不能为空</value></data>
|
||||
<data name="小于" xml:space="preserve"><value>小于</value></data>
|
||||
<data name="小于等于" xml:space="preserve"><value>小于等于</value></data>
|
||||
<data name="小学" xml:space="preserve"><value>小学</value></data>
|
||||
<data name="已发送" xml:space="preserve"><value>已发送</value></data>
|
||||
<data name="已婚" xml:space="preserve"><value>已婚</value></data>
|
||||
<data name="已完成" xml:space="preserve"><value>已完成</value></data>
|
||||
<data name="已校验" xml:space="preserve"><value>已校验</value></data>
|
||||
<data name="已读" xml:space="preserve"><value>已读</value></data>
|
||||
<data name="布依族" xml:space="preserve"><value>布依族</value></data>
|
||||
<data name="布朗族" xml:space="preserve"><value>布朗族</value></data>
|
||||
<data name="并且" xml:space="preserve"><value>并且</value></data>
|
||||
<data name="开始事务" xml:space="preserve"><value>开始事务</value></data>
|
||||
<data name="彝族" xml:space="preserve"><value>彝族</value></data>
|
||||
<data name="德昂族" xml:space="preserve"><value>德昂族</value></data>
|
||||
<data name="必须介于" xml:space="preserve"><value>必须介于</value></data>
|
||||
<data name="怒族" xml:space="preserve"><value>怒族</value></data>
|
||||
<data name="意外错误" xml:space="preserve"><value>意外错误</value></data>
|
||||
<data name="成功" xml:space="preserve"><value>成功</value></data>
|
||||
<data name="或者" xml:space="preserve"><value>或者</value></data>
|
||||
<data name="手机" xml:space="preserve"><value>手机</value></data>
|
||||
<data name="手机号码" xml:space="preserve"><value>手机号码</value></data>
|
||||
<data name="手机号码或座机号码" xml:space="preserve"><value>手机号码或座机号码</value></data>
|
||||
<data name="手机号码不正确" xml:space="preserve"><value>手机号码不正确</value></data>
|
||||
<data name="护照" xml:space="preserve"><value>护照</value></data>
|
||||
<data name="拉祜族族" xml:space="preserve"><value>拉祜族族</value></data>
|
||||
<data name="指定部门数据" xml:space="preserve"><value>指定部门数据</value></data>
|
||||
<data name="按钮" xml:space="preserve"><value>按钮</value></data>
|
||||
<data name="插入种子数据" xml:space="preserve"><value>插入种子数据</value></data>
|
||||
<data name="撒拉族" xml:space="preserve"><value>撒拉族</value></data>
|
||||
<data name="支付宝账号" xml:space="preserve"><value>支付宝账号</value></data>
|
||||
<data name="数据库同步开始" xml:space="preserve"><value>数据库同步开始</value></data>
|
||||
<data name="数据库服务器时钟偏移" xml:space="preserve"><value>数据库服务器时钟偏移</value></data>
|
||||
<data name="数据库结构同步完成" xml:space="preserve"><value>数据库结构同步完成</value></data>
|
||||
<data name="数据版本" xml:space="preserve"><value>数据版本</value></data>
|
||||
<data name="数据版本不能为空" xml:space="preserve"><value>数据版本不能为空</value></data>
|
||||
<data name="文件不能为空" xml:space="preserve"><value>文件不能为空</value></data>
|
||||
<data name="新密码" xml:space="preserve"><value>新密码</value></data>
|
||||
<data name="新密码不能为空" xml:space="preserve"><value>新密码不能为空</value></data>
|
||||
<data name="新手机号码" xml:space="preserve"><value>新手机号码</value></data>
|
||||
<data name="新手机号码验证码不正确" xml:space="preserve"><value>新手机号码验证码不正确</value></data>
|
||||
<data name="无效操作" xml:space="preserve"><value>无效操作</value></data>
|
||||
<data name="无效网络地址" xml:space="preserve"><value>无效网络地址</value></data>
|
||||
<data name="无效端口号" xml:space="preserve"><value>无效端口号</value></data>
|
||||
<data name="无效证件号码" xml:space="preserve"><value>无效证件号码</value></data>
|
||||
<data name="无效输入" xml:space="preserve"><value>无效输入</value></data>
|
||||
<data name="日志长度超过限制" xml:space="preserve"><value>日志长度超过限制</value></data>
|
||||
<data name="旧密码" xml:space="preserve"><value>旧密码</value></data>
|
||||
<data name="日期范围" xml:space="preserve"><value>日期范围</value></data>
|
||||
<data name="旧密码不正确" xml:space="preserve"><value>旧密码不正确</value></data>
|
||||
<data name="旧密码不能为空" xml:space="preserve"><value>旧密码不能为空</value></data>
|
||||
<data name="旧手机号码" xml:space="preserve"><value>旧手机号码</value></data>
|
||||
<data name="旧手机号码不正确" xml:space="preserve"><value>旧手机号码不正确</value></data>
|
||||
<data name="旧手机号码验证码不正确" xml:space="preserve"><value>旧手机号码验证码不正确</value></data>
|
||||
<data name="时间戳缺失或误差过大" xml:space="preserve"><value>时间戳缺失或误差过大</value></data>
|
||||
<data name="普米族" xml:space="preserve"><value>普米族</value></data>
|
||||
<data name="景颇族" xml:space="preserve"><value>景颇族</value></data>
|
||||
<data name="朝鲜族" xml:space="preserve"><value>朝鲜族</value></data>
|
||||
<data name="未婚" xml:space="preserve"><value>未婚</value></data>
|
||||
<data name="未指定部门" xml:space="preserve"><value>未指定部门</value></data>
|
||||
<data name="未读" xml:space="preserve"><value>未读</value></data>
|
||||
<data name="本人数据" xml:space="preserve"><value>本人数据</value></data>
|
||||
<data name="本科" xml:space="preserve"><value>本科</value></data>
|
||||
<data name="本部门和下级部门数据" xml:space="preserve"><value>本部门和下级部门数据</value></data>
|
||||
<data name="本部门数据" xml:space="preserve"><value>本部门数据</value></data>
|
||||
<data name="柯尔克孜族" xml:space="preserve"><value>柯尔克孜族</value></data>
|
||||
<data name="框架" xml:space="preserve"><value>框架</value></data>
|
||||
<data name="模块名称不能为空" xml:space="preserve"><value>模块名称不能为空</value></data>
|
||||
<data name="模块说明不能为空" xml:space="preserve"><value>模块说明不能为空</value></data>
|
||||
<data name="比较数据库结构" xml:space="preserve"><value>比较数据库结构</value></data>
|
||||
<data name="毛南族" xml:space="preserve"><value>毛南族</value></data>
|
||||
<data name="水族" xml:space="preserve"><value>水族</value></data>
|
||||
<data name="汉族" xml:space="preserve"><value>汉族</value></data>
|
||||
<data name="注册" xml:space="preserve"><value>注册</value></data>
|
||||
<data name="消息主题不能为空" xml:space="preserve"><value>消息主题不能为空</value></data>
|
||||
<data name="消息内容不能为空" xml:space="preserve"><value>消息内容不能为空</value></data>
|
||||
<data name="港澳台通行证" xml:space="preserve"><value>港澳台通行证</value></data>
|
||||
<data name="满族" xml:space="preserve"><value>满族</value></data>
|
||||
<data name="父节点不存在" xml:space="preserve"><value>父节点不存在</value></data>
|
||||
@ -142,22 +172,24 @@
|
||||
<data name="珞巴族" xml:space="preserve"><value>珞巴族</value></data>
|
||||
<data name="瑶族" xml:space="preserve"><value>瑶族</value></data>
|
||||
<data name="用户不存在" xml:space="preserve"><value>用户不存在</value></data>
|
||||
<data name="用户名" xml:space="preserve"><value>用户名</value></data>
|
||||
<data name="用户名不能为空" xml:space="preserve"><value>用户名不能为空</value></data>
|
||||
<data name="用户名不能是手机号" xml:space="preserve"><value>用户名不能是手机号</value></data>
|
||||
<data name="用户名或密码错误" xml:space="preserve"><value>用户名或密码错误</value></data>
|
||||
<data name="用户名长度4位以上" xml:space="preserve"><value>用户名长度4位以上</value></data>
|
||||
<data name="用户头像" xml:space="preserve"><value>用户头像</value></data>
|
||||
<data name="用户档案" xml:space="preserve"><value>用户档案</value></data>
|
||||
<data name="用户头像不能为空" xml:space="preserve"><value>用户头像不能为空</value></data>
|
||||
<data name="用户档案不能为空" xml:space="preserve"><value>用户档案不能为空</value></data>
|
||||
<data name="电子邮箱" xml:space="preserve"><value>电子邮箱</value></data>
|
||||
<data name="男" xml:space="preserve"><value>男</value></data>
|
||||
<data name="畲族" xml:space="preserve"><value>畲族</value></data>
|
||||
<data name="登录" xml:space="preserve"><value>登录</value></data>
|
||||
<data name="白族" xml:space="preserve"><value>白族</value></data>
|
||||
<data name="目标手机" xml:space="preserve"><value>目标手机</value></data>
|
||||
<data name="目标设备" xml:space="preserve"><value>目标设备</value></data>
|
||||
<data name="短信验证请求" xml:space="preserve"><value>短信验证请求</value></data>
|
||||
<data name="目标设备不能为空" xml:space="preserve"><value>目标设备不能为空</value></data>
|
||||
<data name="短信验证请求不能为空" xml:space="preserve"><value>短信验证请求不能为空</value></data>
|
||||
<data name="硕士" xml:space="preserve"><value>硕士</value></data>
|
||||
<data name="离异" xml:space="preserve"><value>离异</value></data>
|
||||
<data name="私信" xml:space="preserve"><value>私信</value></data>
|
||||
<data name="站内信不存在" xml:space="preserve"><value>站内信不存在</value></data>
|
||||
<data name="等于" xml:space="preserve"><value>等于</value></data>
|
||||
<data name="等待发送" xml:space="preserve"><value>等待发送</value></data>
|
||||
<data name="签名缺失" xml:space="preserve"><value>签名缺失</value></data>
|
||||
<data name="系统模块" xml:space="preserve"><value>系统模块</value></data>
|
||||
@ -167,49 +199,56 @@
|
||||
<data name="维吾尔族" xml:space="preserve"><value>维吾尔族</value></data>
|
||||
<data name="羌族" xml:space="preserve"><value>羌族</value></data>
|
||||
<data name="群众" xml:space="preserve"><value>群众</value></data>
|
||||
<data name="自定义" xml:space="preserve"><value>自定义</value></data>
|
||||
<data name="苗族" xml:space="preserve"><value>苗族</value></data>
|
||||
<data name="范围" xml:space="preserve"><value>范围</value></data>
|
||||
<data name="菜单" xml:space="preserve"><value>菜单</value></data>
|
||||
<data name="菜单名称" xml:space="preserve"><value>菜单名称</value></data>
|
||||
<data name="菜单标题" xml:space="preserve"><value>菜单标题</value></data>
|
||||
<data name="菜单编号" xml:space="preserve"><value>菜单编号</value></data>
|
||||
<data name="菜单名称不能为空" xml:space="preserve"><value>菜单名称不能为空</value></data>
|
||||
<data name="菜单标题不能为空" xml:space="preserve"><value>菜单标题不能为空</value></data>
|
||||
<data name="菜单编号不能为空" xml:space="preserve"><value>菜单编号不能为空</value></data>
|
||||
<data name="蒙古族" xml:space="preserve"><value>蒙古族</value></data>
|
||||
<data name="藏族" xml:space="preserve"><value>藏族</value></data>
|
||||
<data name="裕固族" xml:space="preserve"><value>裕固族</value></data>
|
||||
<data name="角色不存在" xml:space="preserve"><value>角色不存在</value></data>
|
||||
<data name="角色名称" xml:space="preserve"><value>角色名称</value></data>
|
||||
<data name="角色编号" xml:space="preserve"><value>角色编号</value></data>
|
||||
<data name="角色编号列表" xml:space="preserve"><value>角色编号列表</value></data>
|
||||
<data name="角色名称不能为空" xml:space="preserve"><value>角色名称不能为空</value></data>
|
||||
<data name="角色编号不能为空" xml:space="preserve"><value>角色编号不能为空</value></data>
|
||||
<data name="角色编号列表不能为空" xml:space="preserve"><value>角色编号列表不能为空</value></data>
|
||||
<data name="解绑手机号" xml:space="preserve"><value>解绑手机号</value></data>
|
||||
<data name="设备类型" xml:space="preserve"><value>设备类型</value></data>
|
||||
<data name="警告" xml:space="preserve"><value>警告</value></data>
|
||||
<data name="设备类型不能为空" xml:space="preserve"><value>设备类型不能为空</value></data>
|
||||
<data name="该角色下存在用户" xml:space="preserve"><value>该角色下存在用户</value></data>
|
||||
<data name="该部门下存在子部门" xml:space="preserve"><value>该部门下存在子部门</value></data>
|
||||
<data name="该部门下存在用户" xml:space="preserve"><value>该部门下存在用户</value></data>
|
||||
<data name="请求" xml:space="preserve"><value>请求</value></data>
|
||||
<data name="请求对象" xml:space="preserve"><value>请求对象</value></data>
|
||||
<data name="请求对象不能为空" xml:space="preserve"><value>请求对象不能为空</value></data>
|
||||
<data name="请联系管理员激活账号" xml:space="preserve"><value>请联系管理员激活账号</value></data>
|
||||
<data name="读取用户令牌出错" xml:space="preserve"><value>读取用户令牌出错</value></data>
|
||||
<data name="账号" xml:space="preserve"><value>账号</value></data>
|
||||
<data name="调试" xml:space="preserve"><value>调试</value></data>
|
||||
<data name="账号不能为空" xml:space="preserve"><value>账号不能为空</value></data>
|
||||
<data name="赫哲族" xml:space="preserve"><value>赫哲族</value></data>
|
||||
<data name="跟踪" xml:space="preserve"><value>跟踪</value></data>
|
||||
<data name="身份证" xml:space="preserve"><value>身份证</value></data>
|
||||
<data name="达斡尔族" xml:space="preserve"><value>达斡尔族</value></data>
|
||||
<data name="邀请码不正确" xml:space="preserve"><value>邀请码不正确</value></data>
|
||||
<data name="邮箱验证码不正确" xml:space="preserve"><value>邮箱验证码不正确</value></data>
|
||||
<data name="部门不存在" xml:space="preserve"><value>部门不存在</value></data>
|
||||
<data name="部门名称" xml:space="preserve"><value>部门名称</value></data>
|
||||
<data name="部门名称不能为空" xml:space="preserve"><value>部门名称不能为空</value></data>
|
||||
<data name="鄂伦春族" xml:space="preserve"><value>鄂伦春族</value></data>
|
||||
<data name="鄂温克族" xml:space="preserve"><value>鄂温克族</value></data>
|
||||
<data name="配置文件初始化完毕" xml:space="preserve"><value>配置文件初始化完毕</value></data>
|
||||
<data name="重设密码" xml:space="preserve"><value>重设密码</value></data>
|
||||
<data name="链接" xml:space="preserve"><value>链接</value></data>
|
||||
<data name="错误" xml:space="preserve"><value>错误</value></data>
|
||||
<data name="锡伯族" xml:space="preserve"><value>锡伯族</value></data>
|
||||
<data name="键值" xml:space="preserve"><value>键值</value></data>
|
||||
<data name="键名称" xml:space="preserve"><value>键名称</value></data>
|
||||
<data name="键值不能为空" xml:space="preserve"><value>键值不能为空</value></data>
|
||||
<data name="键名称不能为空" xml:space="preserve"><value>键名称不能为空</value></data>
|
||||
<data name="门巴族" xml:space="preserve"><value>门巴族</value></data>
|
||||
<data name="阿昌族" xml:space="preserve"><value>阿昌族</value></data>
|
||||
<data name="顺序排序" xml:space="preserve"><value>顺序排序</value></data>
|
||||
<data name="验证数据" xml:space="preserve"><value>验证数据</value></data>
|
||||
<data name="验证码" xml:space="preserve"><value>验证码</value></data>
|
||||
<data name="验证数据不能为空" xml:space="preserve"><value>验证数据不能为空</value></data>
|
||||
<data name="验证码不正确" xml:space="preserve"><value>验证码不正确</value></data>
|
||||
<data name="验证码类型" xml:space="preserve"><value>验证码类型</value></data>
|
||||
<data name="验证码不能为空" xml:space="preserve"><value>验证码不能为空</value></data>
|
||||
<data name="验证码类型不能为空" xml:space="preserve"><value>验证码类型不能为空</value></data>
|
||||
<data name="高中" xml:space="preserve"><value>高中</value></data>
|
||||
<data name="高山族" xml:space="preserve"><value>高山族</value></data>
|
||||
<data name="黎族" xml:space="preserve"><value>黎族</value></data>
|
@ -1,49 +1,59 @@
|
||||
1分钟内只能发送1次
|
||||
6位数字
|
||||
8位以上数字字母组合
|
||||
Xml注释文件不存在
|
||||
不正确
|
||||
不能为空
|
||||
XML注释文件不存在
|
||||
不为其中之一
|
||||
不以什么开始
|
||||
不以什么结束
|
||||
不包含
|
||||
不等于
|
||||
业务模块
|
||||
东乡族
|
||||
丧偶
|
||||
中专
|
||||
中共党员
|
||||
中文姓名
|
||||
为其中之一
|
||||
乌孜别克族
|
||||
事务已回滚
|
||||
事务已提交
|
||||
京族
|
||||
人机校验请求
|
||||
人机校验请求不能为空
|
||||
人机验证未通过
|
||||
仡佬族
|
||||
以什么开始
|
||||
以什么结束
|
||||
仫佬族
|
||||
佤族
|
||||
侗族
|
||||
俄罗斯族
|
||||
保安族
|
||||
保密
|
||||
信息
|
||||
倒序排序
|
||||
傈僳族
|
||||
傣族
|
||||
允许的文件大小
|
||||
允许的文件格式
|
||||
全部数据
|
||||
公告
|
||||
共青团员
|
||||
出生证
|
||||
初中
|
||||
初始化完毕
|
||||
删除
|
||||
包含
|
||||
区号电话号码分机号
|
||||
博士
|
||||
博士后
|
||||
参数格式不正确
|
||||
发送失败
|
||||
同步数据库结构
|
||||
哈尼族
|
||||
哈萨克族
|
||||
唯一编码
|
||||
唯一编码不能为空
|
||||
回族
|
||||
图标代码
|
||||
图标名称
|
||||
图标代码不能为空
|
||||
图标名称不能为空
|
||||
土家族
|
||||
土族
|
||||
基诺族
|
||||
@ -52,65 +62,84 @@ Xml注释文件不存在
|
||||
壮族
|
||||
外国人居留证
|
||||
大专
|
||||
大于
|
||||
大于等于
|
||||
女
|
||||
字典名称
|
||||
字典名称不能为空
|
||||
字典目录不存在
|
||||
字典目录编号
|
||||
字典编码
|
||||
密码
|
||||
字典目录编号不能为空
|
||||
字典编码不能为空
|
||||
宕机
|
||||
密码不能为空
|
||||
小于
|
||||
小于等于
|
||||
小学
|
||||
已发送
|
||||
已婚
|
||||
已完成
|
||||
已校验
|
||||
已读
|
||||
布依族
|
||||
布朗族
|
||||
并且
|
||||
开始事务
|
||||
彝族
|
||||
德昂族
|
||||
必须介于
|
||||
怒族
|
||||
意外错误
|
||||
成功
|
||||
或者
|
||||
手机
|
||||
手机号码
|
||||
手机号码或座机号码
|
||||
手机号码不正确
|
||||
护照
|
||||
拉祜族族
|
||||
指定部门数据
|
||||
按钮
|
||||
插入种子数据
|
||||
撒拉族
|
||||
支付宝账号
|
||||
数据库同步开始
|
||||
数据库服务器时钟偏移
|
||||
数据库结构同步完成
|
||||
数据版本
|
||||
数据版本不能为空
|
||||
文件不能为空
|
||||
新密码
|
||||
新密码不能为空
|
||||
新手机号码
|
||||
新手机号码验证码不正确
|
||||
无效操作
|
||||
无效网络地址
|
||||
无效端口号
|
||||
无效证件号码
|
||||
无效输入
|
||||
日志长度超过限制
|
||||
旧密码
|
||||
日期范围
|
||||
旧密码不正确
|
||||
旧密码不能为空
|
||||
旧手机号码
|
||||
旧手机号码不正确
|
||||
旧手机号码验证码不正确
|
||||
时间戳缺失或误差过大
|
||||
普米族
|
||||
景颇族
|
||||
朝鲜族
|
||||
未婚
|
||||
未指定部门
|
||||
未读
|
||||
本人数据
|
||||
本科
|
||||
本部门和下级部门数据
|
||||
本部门数据
|
||||
柯尔克孜族
|
||||
框架
|
||||
模块名称不能为空
|
||||
模块说明不能为空
|
||||
比较数据库结构
|
||||
毛南族
|
||||
水族
|
||||
汉族
|
||||
注册
|
||||
消息主题不能为空
|
||||
消息内容不能为空
|
||||
港澳台通行证
|
||||
满族
|
||||
父节点不存在
|
||||
@ -118,22 +147,24 @@ Xml注释文件不存在
|
||||
珞巴族
|
||||
瑶族
|
||||
用户不存在
|
||||
用户名
|
||||
用户名不能为空
|
||||
用户名不能是手机号
|
||||
用户名或密码错误
|
||||
用户名长度4位以上
|
||||
用户头像
|
||||
用户档案
|
||||
用户头像不能为空
|
||||
用户档案不能为空
|
||||
电子邮箱
|
||||
男
|
||||
畲族
|
||||
登录
|
||||
白族
|
||||
目标手机
|
||||
目标设备
|
||||
短信验证请求
|
||||
目标设备不能为空
|
||||
短信验证请求不能为空
|
||||
硕士
|
||||
离异
|
||||
私信
|
||||
站内信不存在
|
||||
等于
|
||||
等待发送
|
||||
签名缺失
|
||||
系统模块
|
||||
@ -143,49 +174,56 @@ Xml注释文件不存在
|
||||
维吾尔族
|
||||
羌族
|
||||
群众
|
||||
自定义
|
||||
苗族
|
||||
范围
|
||||
菜单
|
||||
菜单名称
|
||||
菜单标题
|
||||
菜单编号
|
||||
菜单名称不能为空
|
||||
菜单标题不能为空
|
||||
菜单编号不能为空
|
||||
蒙古族
|
||||
藏族
|
||||
裕固族
|
||||
角色不存在
|
||||
角色名称
|
||||
角色编号
|
||||
角色编号列表
|
||||
角色名称不能为空
|
||||
角色编号不能为空
|
||||
角色编号列表不能为空
|
||||
解绑手机号
|
||||
设备类型
|
||||
警告
|
||||
设备类型不能为空
|
||||
该角色下存在用户
|
||||
该部门下存在子部门
|
||||
该部门下存在用户
|
||||
请求
|
||||
请求对象
|
||||
请求对象不能为空
|
||||
请联系管理员激活账号
|
||||
读取用户令牌出错
|
||||
账号
|
||||
调试
|
||||
账号不能为空
|
||||
赫哲族
|
||||
跟踪
|
||||
身份证
|
||||
达斡尔族
|
||||
邀请码不正确
|
||||
邮箱验证码不正确
|
||||
部门不存在
|
||||
部门名称
|
||||
部门名称不能为空
|
||||
鄂伦春族
|
||||
鄂温克族
|
||||
配置文件初始化完毕
|
||||
重设密码
|
||||
链接
|
||||
错误
|
||||
锡伯族
|
||||
键值
|
||||
键名称
|
||||
键值不能为空
|
||||
键名称不能为空
|
||||
门巴族
|
||||
阿昌族
|
||||
顺序排序
|
||||
验证数据
|
||||
验证码
|
||||
验证数据不能为空
|
||||
验证码不正确
|
||||
验证码类型
|
||||
验证码不能为空
|
||||
验证码类型不能为空
|
||||
高中
|
||||
高山族
|
||||
黎族
|
@ -9,16 +9,6 @@
|
||||
"Title": "控制面板",
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Component": "profile",
|
||||
"Id": 374967228141573,
|
||||
"Name": "profile",
|
||||
"Path": "/profile",
|
||||
"Sort": 100,
|
||||
"Title": "账号信息",
|
||||
"Hidden": true,
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Icon": "el-icon-setting",
|
||||
"Id": 373837917724677,
|
||||
@ -116,6 +106,17 @@
|
||||
"Title": "系统设置",
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Component": "home/widgets/components/ver",
|
||||
"Icon": "el-icon-pointer",
|
||||
"Id": 480998862188554,
|
||||
"Name": "sys-about",
|
||||
"ParentId": 373837917724677,
|
||||
"Path": "/sys/about",
|
||||
"Sort": 91,
|
||||
"Title": "版本信息",
|
||||
"Type": 1,
|
||||
},
|
||||
{
|
||||
"Component": "sys/api",
|
||||
"Icon": "sc-icon-api",
|
||||
@ -128,46 +129,15 @@
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Icon": "sc-icon-business",
|
||||
"Id": 420881941635077,
|
||||
"Name": "biz",
|
||||
"Path": "/biz",
|
||||
"Sort": 99,
|
||||
"Title": "业务管理",
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Component": "biz/product-category",
|
||||
"Icon": "sc-icon-product-category",
|
||||
"Id": 420881941635087,
|
||||
"Name": "biz-product-category",
|
||||
"ParentId": 420881941635077,
|
||||
"Path": "/biz/product-category",
|
||||
"Sort": 100,
|
||||
"Title": "商品分类",
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Component": "biz/member",
|
||||
"Icon": "el-icon-user",
|
||||
"Id": 422658027704337,
|
||||
"Name": "biz-member",
|
||||
"ParentId": 420881941635077,
|
||||
"Path": "/biz/member",
|
||||
"Sort": 100,
|
||||
"Title": "会员管理",
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Component": "biz/product",
|
||||
"Icon": "sc-icon-product",
|
||||
"Id": 421506245349391,
|
||||
"Name": "biz-product",
|
||||
"ParentId": 420881941635077,
|
||||
"Path": "/biz/product",
|
||||
"Sort": 100,
|
||||
"Title": "商品管理",
|
||||
"Type": 1
|
||||
"Component": "sys/msg",
|
||||
"Icon": "el-icon-message",
|
||||
"Id": 482779610341392,
|
||||
"Name": "sys-msg",
|
||||
"ParentId": 373837917724677,
|
||||
"Path": "/sys/msg",
|
||||
"Sort": 98,
|
||||
"Title": "消息管理",
|
||||
"Type": 1,
|
||||
},
|
||||
{
|
||||
"Icon": "sc-icon-code",
|
||||
@ -189,4 +159,14 @@
|
||||
"Title": "代码生成",
|
||||
"Type": 1
|
||||
},
|
||||
{
|
||||
"Id": 482777529417739,
|
||||
"ParentId": 373838105399301,
|
||||
"Icon": "el-icon-eleme-filled",
|
||||
"Name": "dev-element",
|
||||
"Path": "https://element-plus.gitee.io/zh-CN/component/button.html",
|
||||
"Sort": 100,
|
||||
"Title": "Element",
|
||||
"Type": 3,
|
||||
}
|
||||
]
|
10
assets/seed-data/Sys_SiteMsg.json
Normal file
@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"Content": "<p>尊敬的用户:</p>\n<p style=\"padding-left: 40px;\">欢迎您使用 NetAdmin 后台管理系统!NetAdmin 是一款通用后台权限管理系统和快速开发框架,它基于 C#12/.NET8、Vue3/Vite、Element Plus 等现代技术构建,具有十分整洁、优雅的编码规范。</p>\n<p style=\"padding-left: 40px;\">NetAdmin 致力于为企业提供高效、安全、易用的解决方案,帮助您快速构建出符合业务需求的应用程序。系统提供了丰富的功能模块,包括用户管理、权限管理、日志管理、文件上传等,可以满足您日常管理的需求。</p>\n<p style=\"padding-left: 40px;\">在使用 NetAdmin 的过程中,我们真诚地希望您能够遵守以下规定:</p>\n<p style=\"padding-left: 80px;\">1. 不得利用 NetAdmin 进行非法活动或者侵犯他人权益;</p>\n<p style=\"padding-left: 80px;\">2. 不得对 NetAdmin 系统进行恶意攻击或者破坏;</p>\n<p style=\"padding-left: 80px;\">3. 不得将 NetAdmin 系统的任何部分用于商业目的或者未经授权的访问。</p>\n<p style=\"padding-left: 80px;\">4. 为了更好地为您提供服务,NetAdmin 将不断进行优化和升级,同时也欢迎您提出宝贵的意见和建议。如果您在使用过程中遇到任何问题,可以通过官方网站或者技术支持团队进行咨询和解决。</p>\n<p style=\"padding-left: 40px;\">再次感谢您对 NetAdmin 的信任和支持!我们相信,在您的使用过程中,NetAdmin 一定会成为您的得力助手,为您的事业发展提供强有力的支持!</p>\n<p style=\"text-align: right;\">NetAdmin 开发团队</p>",
|
||||
"MsgType": 2,
|
||||
"Summary": "尊敬的用户:\n欢迎您使用 NetAdmin 后台管理系统!NetAdmin 是一款通用后台权限管理系统和快速开发框架,它基于 C#12/.NET8、Vue3/Vite、Element Plus 等现代",
|
||||
"Title": "欢迎使用 NetAdmin 后台管理系统",
|
||||
"CreatedUserId": 370942943322181,
|
||||
"CreatedUserName": "root",
|
||||
}
|
||||
]
|
@ -2,5 +2,5 @@ $refs = ('https://github.com/nsnail/ns-ext.git', 'https://github.com/nsnail/Furi
|
||||
|
||||
foreach ($item in $refs)
|
||||
{
|
||||
git clone --depth 1 --config "http.proxy=http://127.0.0.1:1081" $item "../refs/$( [regex]::Match($item, '/([^/]+)\.git$').Groups[1] )"
|
||||
git clone --depth 1 --config "http.proxy=http://127.0.0.1:1081" $item "./refs/$( [regex]::Match($item, '/([^/]+)\.git$').Groups[1] )"
|
||||
}
|
@ -18,7 +18,7 @@ using System.Net.Http.Json;
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
FileName = "dotnet",
|
||||
Arguments = $"jb cleanupcode --include=\"{files}\" --no-build ../NetAdmin.sln",
|
||||
Arguments = $"jb cleanupcode --include=\"{files}\" --no-build ./NetAdmin.sln",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true
|
||||
}
|
@ -3,5 +3,5 @@ $files = $( foreach ($line in $( git diff head origin/dev --stat-width 200 ) | f
|
||||
$line.split('\|')[0].trim()
|
||||
} ) -join ';'
|
||||
echo $files
|
||||
dotnet jb cleanupcode --no-build --include = "$files" ../NetAdmin.sln
|
||||
dotnet script ../PushSign.csx
|
||||
dotnet jb cleanupcode --no-build --include = "$files" ./NetAdmin.sln
|
||||
dotnet script ./PushSign.csx
|
1
code.cleanup.full.ps1
Normal file
@ -0,0 +1 @@
|
||||
dotnet jb cleanupcode --no-build ./NetAdmin.sln
|
@ -1,6 +1,8 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<CodeAnalysisRuleSet>$(SolutionDir)/StyleCopAnalyzers.ruleset</CodeAnalysisRuleSet>
|
||||
<CodeAnalysisRuleSet>$(SolutionDir)/stylecop.analyzers.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
|
||||
@ -13,11 +15,11 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.4.0">
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.6.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.7.0.75501">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.12.0.78982">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
@ -3,8 +3,8 @@
|
||||
<ItemGroup>
|
||||
<PackageReferenceFiles
|
||||
Condition="%(PackageReference.CopyToOutputDirectory) != ''"
|
||||
Include="$(NugetPackageRoot)\%(PackageReference.Identity)\%(PackageReference.Version)\%(PackageReference.CopyToOutputDirectory)" />
|
||||
Include="$(NugetPackageRoot)\%(PackageReference.Identity)\%(PackageReference.Version)\%(PackageReference.CopyToOutputDirectory)"/>
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(OutDir)" />
|
||||
<Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(OutDir)"/>
|
||||
</Target>
|
||||
</Project>
|
182
docs/CONTRIBUTING.md
Normal file
@ -0,0 +1,182 @@
|
||||
# 约定式提交 1.0.0
|
||||
|
||||
## [](#概述)概述
|
||||
|
||||
约定式提交规范是一种基于提交信息的轻量级约定。 它提供了一组简单规则来创建清晰的提交历史; 这更有利于编写自动化工具。 通过在提交信息中描述功能、修复和破坏性变更, 使这种惯例与 [SemVer](http://semver.org/lang/zh-CN) 相互对应。
|
||||
|
||||
提交说明的结构如下所示:
|
||||
|
||||
* * *
|
||||
|
||||
原文:
|
||||
|
||||
<type>[optional scope]: <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer(s)]
|
||||
|
||||
|
||||
译文:
|
||||
|
||||
<类型>[可选 范围]: <描述>
|
||||
|
||||
[可选 正文]
|
||||
|
||||
[可选 脚注]
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
提交说明包含了下面的结构化元素,以向类库使用者表明其意图:
|
||||
|
||||
1. **fix:** _类型_ 为 `fix` 的提交表示在代码库中修复了一个 bug(这和语义化版本中的 [`PATCH`](https://semver.org/lang/zh-CN/#%E6%91%98%E8%A6%81) 相对应)。
|
||||
2. **feat:** _类型_ 为 `feat` 的提交表示在代码库中新增了一个功能(这和语义化版本中的 [`MINOR`](https://semver.org/lang/zh-CN/#%E6%91%98%E8%A6%81) 相对应)。
|
||||
3. **BREAKING CHANGE:** 在脚注中包含 `BREAKING CHANGE:` 或 <类型>(范围) 后面有一个 `!` 的提交,表示引入了破坏性 API 变更(这和语义化版本中的 [`MAJOR`](https://semver.org/lang/zh-CN/#%E6%91%98%E8%A6%81) 相对应)。 破坏性变更可以是任意 _类型_ 提交的一部分。
|
||||
4. 除 `fix:` 和 `feat:` 之外,也可以使用其它提交 _类型_ ,例如 [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional)(基于 [Angular 约定](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines))中推荐的 `build:`、`chore:`、 `ci:`、`docs:`、`style:`、`refactor:`、`perf:`、`test:`,等等。
|
||||
* build: 用于修改项目构建系统,例如修改依赖库、外部接口或者升级 Node 版本等;
|
||||
* chore: 用于对非业务性代码进行修改,例如修改构建流程或者工具配置等;
|
||||
* ci: 用于修改持续集成流程,例如修改 Travis、Jenkins 等工作流配置;
|
||||
* docs: 用于修改文档,例如修改 README 文件、API 文档等;
|
||||
* style: 用于修改代码的样式,例如调整缩进、空格、空行等;
|
||||
* refactor: 用于重构代码,例如修改代码结构、变量名、函数名等但不修改功能逻辑;
|
||||
* perf: 用于优化性能,例如提升代码的性能、减少内存占用等;
|
||||
* test: 用于修改测试用例,例如添加、删除、修改代码的测试用例等。
|
||||
5. 脚注中除了 `BREAKING CHANGE: <description>` ,其它条目应该采用类似 [git trailer format](https://git-scm.com/docs/git-interpret-trailers) 这样的惯例。
|
||||
|
||||
其它提交类型在约定式提交规范中并没有强制限制,并且在语义化版本中没有隐式影响(除非它们包含 BREAKING CHANGE)。 可以为提交类型添加一个围在圆括号内的范围,以为其提供额外的上下文信息。例如 `feat(parser): adds ability to parse arrays.`。
|
||||
|
||||
## [](#示例)示例
|
||||
|
||||
### [](#包含了描述并且脚注中有破坏性变更的提交说明)包含了描述并且脚注中有破坏性变更的提交说明
|
||||
|
||||
feat: allow provided config object to extend other configs
|
||||
|
||||
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
|
||||
|
||||
|
||||
### [](#包含了--字符以提醒注意破坏性变更的提交说明)包含了 `!` 字符以提醒注意破坏性变更的提交说明
|
||||
|
||||
feat!: send an email to the customer when a product is shipped
|
||||
|
||||
|
||||
### [](#包含了范围和破坏性变更--的提交說明)包含了范围和破坏性变更 `!` 的提交說明
|
||||
|
||||
feat(api)!: send an email to the customer when a product is shipped
|
||||
|
||||
|
||||
### [](#包含了--和-breaking-change-脚注的提交说明)包含了 `!` 和 BREAKING CHANGE 脚注的提交说明
|
||||
|
||||
chore!: drop support for Node 6
|
||||
|
||||
BREAKING CHANGE: use JavaScript features not available in Node 6.
|
||||
|
||||
|
||||
### [](#不包含正文的提交说明)不包含正文的提交说明
|
||||
|
||||
docs: correct spelling of CHANGELOG
|
||||
|
||||
|
||||
### [](#包含范围的提交说明)包含范围的提交说明
|
||||
|
||||
feat(lang): add polish language
|
||||
|
||||
|
||||
### [](#包含多行正文和多行脚注的提交说明)包含多行正文和多行脚注的提交说明
|
||||
|
||||
fix: prevent racing of requests
|
||||
|
||||
Introduce a request id and a reference to latest request. Dismiss
|
||||
incoming responses other than from latest request.
|
||||
|
||||
Remove timeouts which were used to mitigate the racing issue but are
|
||||
obsolete now.
|
||||
|
||||
Reviewed-by: Z
|
||||
Refs: #123
|
||||
|
||||
|
||||
## [](#约定式提交规范)约定式提交规范
|
||||
|
||||
本文中的关键词 “必须(MUST)”、“禁止(MUST NOT)”、“必要(REQUIRED)”、“应当(SHALL)”、“不应当(SHALL NOT)”、“应该(SHOULD)”、“不应该(SHOULD NOT)”、“推荐(RECOMMENDED)”、“可以(MAY)” 和 “可选(OPTIONAL)” ,其相关解释参考 [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt) 。
|
||||
|
||||
1. 每个提交都**必须**使用类型字段前缀,它由一个名词构成,诸如 `feat` 或 `fix` , 其后接**可选的**范围字段,**可选的** `!`,以及**必要的**冒号(英文半角)和空格。
|
||||
2. 当一个提交为应用或类库实现了新功能时,**必须**使用 `feat` 类型。
|
||||
3. 当一个提交为应用修复了 bug 时,**必须**使用 `fix` 类型。
|
||||
4. 范围字段**可以**跟随在类型字段后面。范围**必须**是一个描述某部分代码的名词,并用圆括号包围,例如: `fix(parser):`
|
||||
5. 描述字段**必须**直接跟在 <类型>(范围) 前缀的冒号和空格之后。 描述指的是对代码变更的简短总结,例如: _fix: array parsing issue when multiple spaces were contained in string_ 。
|
||||
6. 在简短描述之后,**可以**编写较长的提交正文,为代码变更提供额外的上下文信息。正文**必须**起始于描述字段结束的一个空行后。
|
||||
7. 提交的正文内容自由编写,并**可以**使用空行分隔不同段落。
|
||||
8. 在正文结束的一个空行之后,**可以**编写一行或多行脚注。每行脚注都**必须**包含 一个令牌(token),后面紧跟 `:<space>` 或 `<space>#` 作为分隔符,后面再紧跟令牌的值(受 [git trailer convention](https://git-scm.com/docs/git-interpret-trailers) 启发)。
|
||||
9. 脚注的令牌**必须**使用 `-` 作为连字符,比如 `Acked-by` (这样有助于 区分脚注和多行正文)。有一种例外情况就是 `BREAKING CHANGE`,它**可以**被认为是一个令牌。
|
||||
10. 脚注的值**可以**包含空格和换行,值的解析过程**必须**直到下一个脚注的令牌/分隔符出现为止。
|
||||
11. 破坏性变更**必须**在提交信息中标记出来,要么在 <类型>(范围) 前缀中标记,要么作为脚注的一项。
|
||||
12. 包含在脚注中时,破坏性变更**必须**包含大写的文本 `BREAKING CHANGE`,后面紧跟着冒号、空格,然后是描述,例如: _BREAKING CHANGE: environment variables now take precedence over config files_ 。
|
||||
13. 包含在 <类型>(范围) 前缀时,破坏性变更**必须**通过把 `!` 直接放在 `:` 前面标记出来。 如果使用了 `!`,那么脚注中**可以**不写 `BREAKING CHANGE:`, 同时提交信息的描述中**应该**用来描述破坏性变更。
|
||||
14. 在提交说明中,**可以**使用 `feat` 和 `fix` 之外的类型,比如:_docs: updated ref docs._ 。
|
||||
15. 工具的实现必须**不区分**大小写地解析构成约定式提交的信息单元,只有 `BREAKING CHANGE` **必须**是大写的。
|
||||
16. BREAKING-CHANGE 作为脚注的令牌时**必须**是 BREAKING CHANGE 的同义词。
|
||||
|
||||
## [](#为什么使用约定式提交)为什么使用约定式提交
|
||||
|
||||
* 自动化生成 CHANGELOG。
|
||||
* 基于提交的类型,自动决定语义化的版本变更。
|
||||
* 向同事、公众与其他利益关系者传达变化的性质。
|
||||
* 触发构建和部署流程。
|
||||
* 让人们探索一个更加结构化的提交历史,以便降低对你的项目做出贡献的难度。
|
||||
|
||||
## [](#faq)FAQ
|
||||
|
||||
### [](#在初始开发阶段我该如何处理提交说明)在初始开发阶段我该如何处理提交说明?
|
||||
|
||||
我们建议你按照假设你已发布了产品那样来处理。因为通常总 _有人_ 使用你的软件,即便那是你软件开发的同事们。他们会希望知道诸如修复了什么、哪里不兼容等信息。
|
||||
|
||||
### [](#提交标题中的类型是大写还是小写)提交标题中的类型是大写还是小写?
|
||||
|
||||
大小写都可以,但最好是一致的。
|
||||
|
||||
### [](#如果提交符合多种类型我该如何操作)如果提交符合多种类型我该如何操作?
|
||||
|
||||
回退并尽可能创建多次提交。约定式提交的好处之一是能够促使我们做出更有组织的提交和 PR。
|
||||
|
||||
### [](#这不会阻碍快速开发和迭代吗)这不会阻碍快速开发和迭代吗?
|
||||
|
||||
它阻碍的是以杂乱无章的方式快速前进。它助你能在横跨多个项目以及和多个贡献者协作时长期地快速演进。
|
||||
|
||||
### [](#约定式提交会让开发者受限于提交的类型吗因为他们会想着已提供的类型)约定式提交会让开发者受限于提交的类型吗(因为他们会想着已提供的类型)?
|
||||
|
||||
约定式提交鼓励我们更多地使用某些类型的提交,比如 `fixes`。除此之外,约定式提交的灵活性也允许你的团队使用自己的类型,并随着时间的推移更改这些类型。
|
||||
|
||||
### [](#这和-semver-有什么关联呢)这和 SemVer 有什么关联呢?
|
||||
|
||||
`fix` 类型提交应当对应到 `PATCH` 版本。`feat` 类型提交应该对应到 `MINOR` 版本。带有 `BREAKING CHANGE` 的提交不管类型如何,都应该对应到 `MAJOR` 版本。
|
||||
|
||||
### [](#我对约定式提交做了形如-jameswomackconventional-commit-spec-的扩展该如何版本化管理这些扩展呢)我对约定式提交做了形如 `@jameswomack/conventional-commit-spec` 的扩展,该如何版本化管理这些扩展呢?
|
||||
|
||||
我们推荐使用 SemVer 来发布你对于这个规范的扩展(并鼓励你创建这些扩展!)
|
||||
|
||||
### [](#如果我不小心使用了错误的提交类型该怎么办呢)如果我不小心使用了错误的提交类型,该怎么办呢?
|
||||
|
||||
#### [](#当你使用了在规范中但错误的类型时例如将-feat-写成了-fix)当你使用了在规范中但错误的类型时,例如将 `feat` 写成了 `fix`
|
||||
|
||||
在合并或发布这个错误之前,我们建议使用 `git rebase -i` 来编辑提交历史。而在发布之后,根据你使用的工具和流程不同,会有不同的清理方案。
|
||||
|
||||
#### [](#当使用了-不在-规范中的类型时例如将-feat-写成了-feet)当使用了 _不在_ 规范中的类型时,例如将 `feat` 写成了 `feet`
|
||||
|
||||
在最坏的场景下,即便提交没有满足约定式提交的规范,也不会是世界末日。这只意味着这个提交会被基于规范的工具错过而已。
|
||||
|
||||
### [](#所有的贡献者都需要使用约定式提交规范吗)所有的贡献者都需要使用约定式提交规范吗?
|
||||
|
||||
并不!如果你使用基于 squash 的 Git 工作流,主管维护者可以在合并时清理提交信息——这不会对普通提交者产生额外的负担。 有种常见的工作流是让 git 系统自动从 pull request 中 squash 出提交,并向主管维护者提供一份表单,用以在合并时输入合适的 git 提交信息。
|
||||
|
||||
### [](#约定式提交规范中如何处理还原revert提交)约定式提交规范中如何处理还原(revert)提交?
|
||||
|
||||
还原提交(Reverting)会比较复杂:你还原的是多个提交吗?如果你还原了一个功能模块,下次发布的应该是补丁吗?
|
||||
|
||||
约定式提交不能明确的定义还原行为。所以我们把这个问题留给工具开发者, 基于 _类型_ 和 _脚注_ 的灵活性来开发他们自己的还原处理逻辑。
|
||||
|
||||
一种建议是使用 `revert` 类型,和一个指向被还原提交摘要的脚注:
|
||||
|
||||
revert: let us never again speak of the noodle incident
|
||||
|
||||
Refs: 676104e, a215868
|
226
docs/SEMVER.md
Normal file
@ -0,0 +1,226 @@
|
||||
语义化版本 2.0.0
|
||||
===
|
||||
|
||||
摘要
|
||||
---
|
||||
|
||||
版本格式:主版本号.次版本号.修订号,版本号递增规则如下:
|
||||
|
||||
1. 主版本号:当你做了不兼容的 API 修改,
|
||||
2. 次版本号:当你做了向下兼容的功能性新增,
|
||||
3. 修订号:当你做了向下兼容的问题修正。
|
||||
|
||||
先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。
|
||||
|
||||
简介
|
||||
---
|
||||
|
||||
在软件管理的领域里存在着被称作“依赖地狱”的死亡之谷,系统规模越大,加入的包越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。
|
||||
|
||||
在依赖高的系统中发布新版本包可能很快会成为噩梦。如果依赖关系过高,可能面临版本控制被锁死的风险(必须对每一个依赖包改版才能完成某次升级)。而如果依赖关系过于松散,又将无法避免版本的混乱(假设兼容于未来的多个版本已超出了合理数量)。当你项目的进展因为版本依赖被锁死或版本混乱变得不够简便和可靠,就意味着你正处于依赖地狱之中。
|
||||
|
||||
作为这个问题的解决方案之一,我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不局限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作,你必须先有定义好的公共 API。这可能包括文档或代码的强制要求。无论如何,这套 API 的清楚明了是十分重要的。一旦你定义了公共 API,你就可以透过修改相应的版本号来向大家说明你的修改。考虑使用这样的版本号格式:X.Y.Z(主版本号.次版本号.修订号)修复问题但不影响 API 时,递增修订号;API 保持向下兼容的新增及修改时,递增次版本号;进行不向下兼容的修改时,递增主版本号。
|
||||
|
||||
我称这套系统为“语义化的版本控制”,在这套约定下,版本号及其更新方式包含了相邻版本间的底层代码和修改内容的信息。
|
||||
|
||||
语义化版本控制规范(SemVer)
|
||||
---
|
||||
|
||||
以下关键词 MUST、MUST NOT、REQUIRED、SHALL、SHALL NOT、SHOULD、SHOULD NOT、 RECOMMENDED、MAY、OPTIONAL 依照 RFC 2119 的叙述解读。
|
||||
|
||||
1. 使用语义化版本控制的软件必须(MUST)定义公共 API。该 API 可以在代码中被定义或出现于严谨的文档内。无论何种形式都应该力求精确且完整。
|
||||
|
||||
2. 标准的版本号必须(MUST)采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止(MUST NOT)在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须(MUST)以数值来递增。例如:1.9.1 -> 1.10.0 -> 1.11.0。
|
||||
|
||||
3. 标记版本号的软件发行后,禁止(MUST NOT)改变该版本软件的内容。任何修改都必须(MUST)以新版本发行。
|
||||
|
||||
4. 主版本号为零(0.y.z)的软件处于开发初始阶段,一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。
|
||||
|
||||
5. 1.0.0 的版本号用于界定公共 API 的形成。这一版本之后所有的版本号更新都基于公共 API 及其修改内容。
|
||||
|
||||
6. 修订号 Z(x.y.Z `|` x > 0)必须(MUST)在只做了向下兼容的修正时才递增。这里的修正指的是针对不正确结果而进行的内部修改。
|
||||
|
||||
7. 次版本号 Y(x.Y.z `|` x > 0)必须(MUST)在有向下兼容的新功能出现时递增。在任何公共 API 的功能被标记为弃用时也必须(MUST)递增。也可以(MAY)在内部程序有大量新功能或改进被加入时递增,其中可以(MAY)包括修订级别的改变。每当次版本号递增时,修订号必须(MUST)归零。
|
||||
|
||||
8. 主版本号 X(X.y.z `|` X > 0)必须(MUST)在有任何不兼容的修改被加入公共 API 时递增。其中可以(MAY)包括次版本号及修订级别的改变。每当主版本号递增时,次版本号和修订号必须(MUST)归零。
|
||||
|
||||
9. 先行版本号可以(MAY)被标注在修订版之后,先加上一个连接号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。数字型的标识符禁止(MUST NOT)在前方补零。先行版的优先级低于相关联的标准版本。被标上先行版本号则表示这个版本并非稳定而且可能无法满足预期的兼容性需求。范例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。
|
||||
|
||||
10. 版本编译信息可以(MAY)被标注在修订版或先行版本号之后,先加上一个加号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。当判断版本的优先层级时,版本编译信息可(SHOULD)被忽略。因此当两个版本只有在版本编译信息有差别时,属于相同的优先层级。范例:1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。
|
||||
|
||||
11. 版本的优先层级指的是不同版本在排序时如何比较。
|
||||
|
||||
1. 判断优先层级时,必须(MUST)把版本依序拆分为主版本号、次版本号、修订号及先行版本号后进行比较(版本编译信息不在这份比较的列表中)。
|
||||
|
||||
2. 由左到右依序比较每个标识符,第一个差异值用来决定优先层级:主版本号、次版本号及修订号以数值比较。
|
||||
|
||||
例如:1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。
|
||||
|
||||
3. 当主版本号、次版本号及修订号都相同时,改以优先层级比较低的先行版本号决定。
|
||||
|
||||
例如:1.0.0-alpha < 1.0.0。
|
||||
|
||||
4. 有相同主版本号、次版本号及修订号的两个先行版本号,其优先层级必须(MUST)透过由左到右的每个被句点分隔的标识符来比较,直到找到一个差异值后决定:
|
||||
|
||||
1. 只有数字的标识符以数值高低比较。
|
||||
|
||||
2. 有字母或连接号时则逐字以 ASCII 的排序来比较。
|
||||
|
||||
3. 数字的标识符比非数字的标识符优先层级低。
|
||||
|
||||
4. 若开头的标识符都相同时,栏位比较多的先行版本号优先层级比较高。
|
||||
|
||||
例如:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0。
|
||||
|
||||
合法语义化版本的巴科斯范式语法
|
||||
--------------------------------------------------
|
||||
```
|
||||
<valid semver> ::= <version core>
|
||||
| <version core> "-" <pre-release>
|
||||
| <version core> "+" <build>
|
||||
| <version core> "-" <pre-release> "+" <build>
|
||||
|
||||
<version core> ::= <major> "." <minor> "." <patch>
|
||||
|
||||
<major> ::= <numeric identifier>
|
||||
|
||||
<minor> ::= <numeric identifier>
|
||||
|
||||
<patch> ::= <numeric identifier>
|
||||
|
||||
<pre-release> ::= <dot-separated pre-release identifiers>
|
||||
|
||||
<dot-separated pre-release identifiers> ::= <pre-release identifier>
|
||||
| <pre-release identifier> "." <dot-separated pre-release identifiers>
|
||||
|
||||
<build> ::= <dot-separated build identifiers>
|
||||
|
||||
<dot-separated build identifiers> ::= <build identifier>
|
||||
| <build identifier> "." <dot-separated build identifiers>
|
||||
|
||||
<pre-release identifier> ::= <alphanumeric identifier>
|
||||
| <numeric identifier>
|
||||
|
||||
<build identifier> ::= <alphanumeric identifier>
|
||||
| <digits>
|
||||
|
||||
<alphanumeric identifier> ::= <non-digit>
|
||||
| <non-digit> <identifier characters>
|
||||
| <identifier characters> <non-digit>
|
||||
| <identifier characters> <non-digit> <identifier characters>
|
||||
|
||||
<numeric identifier> ::= "0"
|
||||
| <positive digit>
|
||||
| <positive digit> <digits>
|
||||
|
||||
<identifier characters> ::= <identifier character>
|
||||
| <identifier character> <identifier characters>
|
||||
|
||||
<identifier character> ::= <digit>
|
||||
| <non-digit>
|
||||
|
||||
<non-digit> ::= <letter>
|
||||
| "-"
|
||||
|
||||
<digits> ::= <digit>
|
||||
| <digit> <digits>
|
||||
|
||||
<digit> ::= "0"
|
||||
| <positive digit>
|
||||
|
||||
<positive digit> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||
|
||||
<letter> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J"
|
||||
| "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T"
|
||||
| "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d"
|
||||
| "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n"
|
||||
| "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
|
||||
| "y" | "z"
|
||||
```
|
||||
|
||||
为什么要使用语义化的版本控制?
|
||||
---
|
||||
|
||||
这并不是一个新的或者革命性的想法。实际上,你可能已经在做一些近似的事情了。问题在于只是“近似”还不够。如果没有某个正式的规范可循,版本号对于依赖的管理并无实质意义。将上述的想法命名并给予清楚的定义,让你对软件使用者传达意向变得容易。一旦这些意向变得清楚,弹性(但又不会太弹性)的依赖规范就能达成。
|
||||
|
||||
举个简单的例子就可以展示语义化的版本控制如何让依赖地狱成为过去。假设有个名为“救火车”的函数库,它需要另一个名为“梯子”并已经有使用语义化版本控制的包。当救火车创建时,梯子的版本号为 3.1.0。因为救火车使用了一些版本 3.1.0 所新增的功能,你可以放心地指定依赖于梯子的版本号大于等于 3.1.0 但小于 4.0.0。这样,当梯子版本 3.1.1 和 3.2.0 发布时,你可以将直接它们纳入你的包管理系统,因为它们能与原有依赖的软件兼容。
|
||||
|
||||
作为一位负责任的开发者,你理当确保每次包升级的运作与版本号的表述一致。现实世界是复杂的,我们除了提高警觉外能做的不多。你所能做的就是让语义化的版本控制为你提供一个健全的方式来发行以及升级包,而无需推出新的依赖包,节省你的时间及烦恼。
|
||||
|
||||
如果你对此认同,希望立即开始使用语义化版本控制,你只需声明你的函数库正在使用它并遵循这些规则就可以了。请在你的 README 文件中保留此页链接,让别人也知道这些规则并从中受益。
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
### 在 0.y.z 初始开发阶段,我该如何进行版本控制?
|
||||
|
||||
最简单的做法是以 0.1.0 作为你的初始化开发版本,并在后续的每次发行时递增次版本号。
|
||||
|
||||
### 如何判断发布 1.0.0 版本的时机?
|
||||
|
||||
当你的软件被用于正式环境,它应该已经达到了 1.0.0 版。如果你已经有个稳定的 API 被使用者依赖,也会是 1.0.0 版。如果你很担心向下兼容的问题,也应该算是 1.0.0 版了。
|
||||
|
||||
### 这不会阻碍快速开发和迭代吗?
|
||||
|
||||
主版本号为零的时候就是为了做快速开发。如果你每天都在改变 API,那么你应该仍在主版本号为零的阶段(0.y.z),或是正在下个主版本的独立开发分支中。
|
||||
|
||||
### 对于公共 API,若即使是最小但不向下兼容的改变都需要产生新的主版本号,岂不是很快就达到 42.0.0 版?
|
||||
|
||||
这是开发的责任感和前瞻性的问题。不兼容的改变不应该轻易被加入到有许多依赖代码的软件中。升级所付出的代价可能是巨大的。要递增主版本号来发行不兼容的改版,意味着你必须为这些改变所带来的影响深思熟虑,并且评估所涉及的成本及效益比。
|
||||
|
||||
### 为整个公共 API 写文档太费事了!
|
||||
|
||||
为供他人使用的软件编写适当的文档,是你作为一名专业开发者应尽的职责。保持项目高效的一个非常重要的部分是掌控软件的复杂度,如果没有人知道如何使用你的软件或不知道哪些函数的调用是可靠的,要掌控复杂度会是困难的。长远来看,使用语义化版本控制以及对于公共 API 有良好规范的坚持,可以让每个人及每件事都运行顺畅。
|
||||
|
||||
### 万一不小心把一个不兼容的改版当成了次版本号发行了该怎么办?
|
||||
|
||||
一旦发现自己破坏了语义化版本控制的规范,就要修正这个问题,并发行一个新的次版本号来更正这个问题并且恢复向下兼容。即使是这种情况,也不能去修改已发行的版本。可以的话,将有问题的版本号记录到文档中,告诉使用者问题所在,让他们能够意识到这是有问题的版本。
|
||||
|
||||
### 如果我更新了自己的依赖但没有改变公共 API 该怎么办?
|
||||
|
||||
由于没有影响到公共 API,这可以被认定是兼容的。若某个软件和你的包有共同依赖,则它会有自己的依赖规范,作者也会告知可能的冲突。要判断改版是属于修订等级或是次版等级,是依据你更新的依赖关系是为了修复问题或是加入新功能。对于后者,我经常会预期伴随着更多的代码,这显然会是一个次版本号级别的递增。
|
||||
|
||||
### 如果我变更了公共 API 但无意中未遵循版本号的改动怎么办呢?(意即在修订等级的发布中,误将重大且不兼容的改变加到代码之中)
|
||||
|
||||
自行做最佳的判断。如果你有庞大的使用者群在依照公共 API 的意图而变更行为后会大受影响,那么最好做一次主版本的发布,即使严格来说这个修复仅是修订等级的发布。记住, 语义化的版本控制就是透过版本号的改变来传达意义。若这些改变对你的使用者是重要的,那就透过版本号来向他们说明。
|
||||
|
||||
### 我该如何处理即将弃用的功能?
|
||||
|
||||
弃用现存的功能是软件开发中的家常便饭,也通常是向前发展所必须的。当你弃用部分公共 API 时,你应该做两件事:(1)更新你的文档让使用者知道这个改变,(2)在适当的时机将弃用的功能透过新的次版本号发布。在新的主版本完全移除弃用功能前,至少要有一个次版本包含这个弃用信息,这样使用者才能平顺地转移到新版 API。
|
||||
|
||||
### 语义化版本对于版本的字符串长度是否有限制呢?
|
||||
|
||||
没有,请自行做适当的判断。举例来说,长到 255 个字符的版本已过度夸张。再者,特定的系统对于字符串长度可能会有他们自己的限制。
|
||||
|
||||
### “v1.2.3” 是一个语义化版本号吗?
|
||||
|
||||
“v1.2.3” 并不是一个语义化的版本号。但是,在语义化版本号之前增加前缀 “v” 是用来表示版本号的常用做法。在版本控制系统中,将 “version” 缩写为 “v” 是很常见的。比如:`git tag v1.2.3 -m "Release version 1.2.3"` 中,“v1.2.3” 表示标签名称,而 “1.2.3” 是语义化版本号。
|
||||
|
||||
### 是否有推荐的正则表达式用以检查语义化版本号的正确性?
|
||||
|
||||
有两个推荐的正则表达式。第一个用于支持按组名称提取的语言(PCRE[Perl 兼容正则表达式,比如 Perl、PHP 和 R]、Python 和 Go)。
|
||||
|
||||
参见:<https://regex101.com/r/Ly7O1x/3/>
|
||||
|
||||
```
|
||||
^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
|
||||
```
|
||||
|
||||
第二个用于支持按编号提取的语言(与第一个对应的提取项按顺序分别为:major、minor、patch、prerelease、buildmetadata)。主要包括 ECMA Script(JavaScript)、PCRE(Perl 兼容正则表达式,比如 Perl、PHP 和 R)、Python 和 Go。
|
||||
参见:<https://regex101.com/r/vkijKf/1/>
|
||||
|
||||
```
|
||||
^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
|
||||
```
|
||||
|
||||
关于
|
||||
---
|
||||
|
||||
语义化版本控制的规范是由 Gravatars 创办者兼 GitHub 共同创办者 [Tom Preston-Werner](http://tom.preston-werner.com) 所建立。
|
||||
|
||||
如果您有任何建议,请到 [GitHub 上提出您的问题](https://github.com/semver/semver/issues)。
|
||||
|
||||
许可证
|
||||
---
|
||||
|
||||
[知识共享 署名 3.0 (CC BY 3.0)](http://creativecommons.org/licenses/by/3.0/)
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
BIN
docs/screenshot/2023-10-26_172812.png
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
docs/screenshot/2023-10-26_172955.png
Normal file
After Width: | Height: | Size: 207 KiB |
BIN
docs/screenshot/2023-10-26_174707.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
docs/screenshot/2023-10-26_174949.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
docs/screenshot/2023-10-26_175308.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/screenshot/2023-10-26_175352.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
docs/screenshot/2023-10-26_175407.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
docs/screenshot/2023-10-26_175419.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
docs/screenshot/2023-10-26_175430.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
docs/screenshot/2023-10-26_175441.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
docs/screenshot/2023-10-26_175447.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
docs/screenshot/2023-10-26_175501.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/screenshot/2023-10-26_175922.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
docs/screenshot/thumb/2023-10-26_172812.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
docs/screenshot/thumb/2023-10-26_172955.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
docs/screenshot/thumb/2023-10-26_174707.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/screenshot/thumb/2023-10-26_174949.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175308.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175352.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175407.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175419.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175430.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175441.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175447.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175501.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/screenshot/thumb/2023-10-26_175922.png
Normal file
After Width: | Height: | Size: 28 KiB |
4
dot.clean.cmd
Normal file
@ -0,0 +1,4 @@
|
||||
call npm --prefix ./src/frontend/admin run prettier
|
||||
dot rbom -w -e refs -e .git -e node_modules
|
||||
dot trim -w -e refs -e .git -e node_modules
|
||||
dot tolf -w -e refs -e .git -e node_modules
|
@ -9,7 +9,7 @@
|
||||
]
|
||||
},
|
||||
"cake.tool": {
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"commands": [
|
||||
"dotnet-cake"
|
||||
]
|
||||
@ -21,7 +21,7 @@
|
||||
]
|
||||
},
|
||||
"jetbrains.resharper.globaltools": {
|
||||
"version": "2023.2.0",
|
||||
"version": "2023.2.3",
|
||||
"commands": [
|
||||
"jb"
|
||||
]
|
||||
|
@ -63,7 +63,7 @@ public sealed class Ln
|
||||
public static CultureInfo Culture { get; set; }
|
||||
<#
|
||||
var xml = new XmlDocument();
|
||||
xml.Load("../assets/resx/Ln.resx");
|
||||
xml.Load("./assets/res/Ln.resx");
|
||||
foreach (XmlNode data in xml.SelectNodes("//root/data")!)
|
||||
{
|
||||
#>
|
2
gen.ln.cmd
Normal file
@ -0,0 +1,2 @@
|
||||
dotnet t4 ./gen.resx.tt -o ./assets/res/Ln.resx
|
||||
dotnet t4 ./gen.cs.tt -o ./dist/backend/NetAdmin.Infrastructure/Ln.cs
|
@ -1,5 +1,7 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ output encoding="utf-8" extension="resx" #>
|
||||
<#@ import namespace="System.IO" #>
|
||||
<#@ import namespace="System.Text.RegularExpressions" #>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||
@ -24,9 +26,10 @@
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
// ReSharper disable DuplicateResource
|
||||
<#
|
||||
var regex = new System.Text.RegularExpressions.Regex(@"^\d", System.Text.RegularExpressions.RegexOptions.Compiled);
|
||||
foreach (var line in System.IO.File.ReadLines("../assets/resx/ln.txt"))
|
||||
var regex = new Regex(@"^\d", RegexOptions.Compiled);
|
||||
foreach (var line in File.ReadLines("./assets/res/ln.txt"))
|
||||
{
|
||||
#>
|
||||
<data name="<#= regex.IsMatch(line) ? "_" : "" #><#= line #>" xml:space="preserve"><value><#= line #></value></data>
|
@ -1,5 +1,5 @@
|
||||
$branch = $(git branch --show-current)
|
||||
git checkout dev
|
||||
$branch = $( git branch --show-current )
|
||||
git checkout main
|
||||
git pull
|
||||
git branch -D $branch
|
||||
git branch $branch
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "7.0.0",
|
||||
"version": "8.0.0",
|
||||
"rollForward": "latestMajor",
|
||||
"allowPrerelease": true
|
||||
},
|
||||
"tools": {
|
||||
"dotnet": "7.0.0"
|
||||
"dotnet": "8.0.0"
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
var files = Directory
|
||||
.EnumerateFiles(
|
||||
".",
|
||||
"./",
|
||||
"*.png",
|
||||
new EnumerationOptions
|
||||
{
|
||||
@ -37,7 +37,7 @@ files = new[] { "*.jpg", "*.jpeg" }
|
||||
.SelectMany(
|
||||
x =>
|
||||
Directory.EnumerateFiles(
|
||||
".",
|
||||
"./",
|
||||
x,
|
||||
new EnumerationOptions
|
||||
{
|
2
install.as.tpl.ps1
Normal file
@ -0,0 +1,2 @@
|
||||
dotnet new uninstall .
|
||||
dotnet new --install .
|
11
minver.targets
Normal file
@ -0,0 +1,11 @@
|
||||
<Project>
|
||||
<Target Name="MyTarget" AfterTargets="MinVer">
|
||||
<PropertyGroup>
|
||||
<AssemblyVersion>$(MinVerMajor).$(MinVerMinor).$(MinVerPatch)</AssemblyVersion>
|
||||
<FileVersion>$(MinVerMajor).$(MinVerMinor).$(MinVerPatch)</FileVersion>
|
||||
<PackageVersion>$(MinVerVersion)</PackageVersion>
|
||||
<ProductVersion>$(MinVerVersion)</ProductVersion>
|
||||
<Version>$(MinVerVersion)</Version>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
</Project>
|
10
package.json
@ -1,12 +1,14 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"prettier": "3.0.2",
|
||||
"prettier-plugin-csharp": "0.6.0-development"
|
||||
"cz-git": "^1.7.1",
|
||||
"commitizen": "^4.3.0",
|
||||
"prettier": "^3.1.0",
|
||||
"standard-version": "9.5.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
}
|
27
rename.csx
Normal file
@ -0,0 +1,27 @@
|
||||
#r "nuget: NSExt, 1.1.0"
|
||||
using NSExt.Extensions;
|
||||
|
||||
Console.WriteLine("请输入原始名称(NetAdmin):");
|
||||
var oldName = Console.ReadLine().NullOrEmpty("NetAdmin");
|
||||
Console.WriteLine("请输入替换名称:");
|
||||
var newName = Console.ReadLine();
|
||||
foreach (var path in Directory.EnumerateDirectories("./", $"*{oldName}*",
|
||||
SearchOption.AllDirectories))
|
||||
{
|
||||
Console.Write($"{path} --> ");
|
||||
var newPath = path.Replace(oldName, newName);
|
||||
Directory.Move(path, newPath);
|
||||
Console.WriteLine(newPath);
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine();
|
||||
foreach (var path in Directory.EnumerateFiles("./", $"*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
File.WriteAllText(path, File.ReadAllText(path).Replace(oldName, newName));
|
||||
var newPath = path.Replace(oldName, newName);
|
||||
if (newPath == path) continue;
|
||||
Console.Write($"{path} --> ");
|
||||
Directory.Move(path, newPath);
|
||||
Console.WriteLine(newPath);
|
||||
}
|
@ -59,11 +59,12 @@ global using NetAdmin.Infrastructure.Configuration.Options.SubNodes.Upload;
|
||||
global using NetAdmin.Infrastructure.Constant;
|
||||
global using NetAdmin.Infrastructure.Enums;
|
||||
global using NetAdmin.Infrastructure.Exceptions;
|
||||
global using NetAdmin.Infrastructure.Exceptions.InvalidInput;
|
||||
global using NetAdmin.Infrastructure.Exceptions.InvalidOperation;
|
||||
global using NetAdmin.Infrastructure.Exceptions.Unexpected;
|
||||
global using NetAdmin.Infrastructure.Extensions;
|
||||
global using NetAdmin.Infrastructure.Languages;
|
||||
global using NetAdmin.Infrastructure.Utils;
|
||||
global using NSExt.Attributes;
|
||||
global using NSExt.Extensions;
|
||||
#if !INFRAS
|
||||
global using DynamicFilterInfo = NetAdmin.Domain.Dto.Dependency.DynamicFilterInfo;
|
||||
global using DynamicFilterOperators = NetAdmin.Domain.Enums.DynamicFilterOperators;
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="$(SolutionDir)/CodeQuality.props"/>
|
||||
<Import Project="$(SolutionDir)/code.quality.props"/>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../NetAdmin.Domain/NetAdmin.Domain.csproj"/>
|
||||
</ItemGroup>
|
||||
|
@ -0,0 +1,19 @@
|
||||
using NetAdmin.Domain.Contexts;
|
||||
using NetAdmin.Domain.DbMaps.Dependency;
|
||||
|
||||
namespace NetAdmin.Application.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 默认仓储
|
||||
/// </summary>
|
||||
public sealed class DefaultRepository<TEntity>(IFreeSql fSql //
|
||||
, UnitOfWorkManager uowManger //
|
||||
, ContextUserToken userToken) //
|
||||
: DefaultRepository<TEntity, long>(fSql, uowManger)
|
||||
where TEntity : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前上下文关联的用户
|
||||
/// </summary>
|
||||
public ContextUserToken UserToken => userToken;
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
using NetAdmin.Domain.Contexts;
|
||||
using NetAdmin.Domain.DbMaps.Dependency;
|
||||
|
||||
namespace NetAdmin.Application.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 基础仓储接口
|
||||
/// </summary>
|
||||
public interface IRepository<TEntity> : IBaseRepository<TEntity>
|
||||
where TEntity : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前上下文关联的用户
|
||||
/// </summary>
|
||||
ContextUserToken UserToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 递归删除
|
||||
/// </summary>
|
||||
/// <param name="exp">exp</param>
|
||||
/// <param name="disableGlobalFilterNames">禁用全局过滤器名</param>
|
||||
Task<bool> DeleteRecursiveAsync(Expression<Func<TEntity, bool>> exp, params string[] disableGlobalFilterNames);
|
||||
|
||||
/// <summary>
|
||||
/// 获得Dto
|
||||
/// </summary>
|
||||
/// <param name="id">主键</param>
|
||||
Task<TDto> GetAsync<TDto>(long id);
|
||||
|
||||
/// <summary>
|
||||
/// 根据条件获取Dto
|
||||
/// </summary>
|
||||
Task<TDto> GetAsync<TDto>(Expression<Func<TEntity, bool>> exp);
|
||||
|
||||
/// <summary>
|
||||
/// 根据条件获取实体
|
||||
/// </summary>
|
||||
Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> exp);
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
using NetAdmin.Domain.Contexts;
|
||||
using NetAdmin.Domain.DbMaps.Dependency;
|
||||
|
||||
namespace NetAdmin.Application.Repositories;
|
||||
|
||||
/// <inheritdoc cref="IRepository{TEntity}" />
|
||||
public sealed class Repository<TEntity> : DefaultRepository<TEntity, long>, IRepository<TEntity>
|
||||
where TEntity : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Repository{TEntity}" /> class.
|
||||
/// </summary>
|
||||
public Repository(IFreeSql fSql, UnitOfWorkManager uowManger, ContextUserToken userToken) //
|
||||
: base(fSql, uowManger)
|
||||
{
|
||||
UserToken = userToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前上下文关联的用户
|
||||
/// </summary>
|
||||
public ContextUserToken UserToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 递归删除
|
||||
/// </summary>
|
||||
/// <param name="exp">exp</param>
|
||||
/// <param name="disableGlobalFilterNames">禁用全局过滤器名</param>
|
||||
public async Task<bool> DeleteRecursiveAsync( //
|
||||
Expression<Func<TEntity, bool>> exp, params string[] disableGlobalFilterNames)
|
||||
{
|
||||
_ = await Select.Where(exp)
|
||||
.DisableGlobalFilter(disableGlobalFilterNames)
|
||||
.AsTreeCte()
|
||||
.ToDelete()
|
||||
.ExecuteAffrowsAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得Dto
|
||||
/// </summary>
|
||||
/// <param name="id">主键</param>
|
||||
public Task<TDto> GetAsync<TDto>(long id)
|
||||
{
|
||||
return Select.WhereDynamic(id).ToOneAsync<TDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据条件获取Dto
|
||||
/// </summary>
|
||||
public Task<TDto> GetAsync<TDto>(Expression<Func<TEntity, bool>> exp)
|
||||
{
|
||||
return Select.Where(exp).ToOneAsync<TDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据条件获取实体
|
||||
/// </summary>
|
||||
public Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> exp)
|
||||
{
|
||||
return Select.Where(exp).ToOneAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取分页列表
|
||||
/// </summary>
|
||||
/// <param name="dynamicFilterInfo">动态过滤器</param>
|
||||
/// <param name="page">页码</param>
|
||||
/// <param name="pageSize">页容量</param>
|
||||
/// <returns>分页列表和总条数</returns>
|
||||
public async Task<(IEnumerable<TEntity> List, long Total)> GetPagedListAsync(
|
||||
DynamicFilterInfo dynamicFilterInfo, int page, int pageSize)
|
||||
{
|
||||
var list = await Select.WhereDynamicFilter(dynamicFilterInfo)
|
||||
.Count(out var total)
|
||||
.Page(page, pageSize)
|
||||
.ToListAsync();
|
||||
return (list, total);
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ public abstract class RepositoryService<TEntity, TLogger> : ServiceBase<TLogger>
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RepositoryService{TEntity, TLogger}" /> class.
|
||||
/// </summary>
|
||||
protected RepositoryService(Repository<TEntity> rpo) //
|
||||
protected RepositoryService(DefaultRepository<TEntity> rpo) //
|
||||
{
|
||||
Rpo = rpo;
|
||||
}
|
||||
@ -22,7 +22,7 @@ public abstract class RepositoryService<TEntity, TLogger> : ServiceBase<TLogger>
|
||||
/// <summary>
|
||||
/// 默认仓储
|
||||
/// </summary>
|
||||
protected Repository<TEntity> Rpo { get; }
|
||||
protected DefaultRepository<TEntity> Rpo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 启用级联保存
|
||||
@ -31,4 +31,12 @@ public abstract class RepositoryService<TEntity, TLogger> : ServiceBase<TLogger>
|
||||
get => Rpo.DbContextOptions.EnableCascadeSave;
|
||||
set => Rpo.DbContextOptions.EnableCascadeSave = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 针对 Sqlite 数据的更新操作
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 非 Sqlite 数据库请删除
|
||||
/// </returns>
|
||||
protected abstract Task<TEntity> UpdateForSqliteAsync(TEntity req);
|
||||
}
|
@ -33,13 +33,9 @@ public abstract class ServiceBase : IScoped, IService
|
||||
ServiceId = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务编号
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public Guid ServiceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上下文用户
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public ContextUserToken UserToken { get; set; }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="$(SolutionDir)/CodeQuality.props"/>
|
||||
<Import Project="$(SolutionDir)/code.quality.props"/>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../NetAdmin.SysComponent.Application/NetAdmin.SysComponent.Application.csproj"/>
|
||||
</ItemGroup>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="$(SolutionDir)/CodeQuality.props"/>
|
||||
<Import Project="$(SolutionDir)/code.quality.props"/>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../NetAdmin.SysComponent.Cache/NetAdmin.SysComponent.Cache.csproj"/>
|
||||
<ProjectReference Include="../NetAdmin.BizServer.Application/NetAdmin.BizServer.Application.csproj"/>
|
||||
|
@ -0,0 +1,66 @@
|
||||
namespace NetAdmin.BizServer.Host.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// ApplicationBuilder对象 扩展方法
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static class IApplicationBuilderExtensions
|
||||
{
|
||||
private const string _INDEX_HTML_PATH = ".index.html";
|
||||
private const string _RES_PFX = $"{nameof(NetAdmin)}.{nameof(BizServer)}.{nameof(Host)}.UI";
|
||||
private static readonly Regex _regex = new(@"\.(\w+)$", RegexOptions.Compiled);
|
||||
private static IEnumerable<string> _allResNames;
|
||||
|
||||
/// <summary>
|
||||
/// 托管管理后台
|
||||
/// </summary>
|
||||
public static IApplicationBuilder UseVueAdmin(this IApplicationBuilder me)
|
||||
{
|
||||
if (Assembly.GetExecutingAssembly().GetManifestResourceInfo(_RES_PFX + _INDEX_HTML_PATH) == null) {
|
||||
return me;
|
||||
}
|
||||
|
||||
_allResNames = Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceNames()
|
||||
.Where(x => x.StartsWith(_RES_PFX, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(x => x[_RES_PFX.Length..]);
|
||||
|
||||
return me.Use(UseVueAdminAsync);
|
||||
}
|
||||
|
||||
private static Stream GetManifestResourceStream(string path)
|
||||
{
|
||||
return Assembly.GetExecutingAssembly().GetManifestResourceStream(_RES_PFX + path);
|
||||
}
|
||||
|
||||
private static async Task UseVueAdminAsync(HttpContext context, Func<Task> next)
|
||||
{
|
||||
if (!context.Request.Path.StartsWithSegments(new PathString("/api"))) {
|
||||
var path = context.Request.Path.Value;
|
||||
if (path == "/" || path?.Length == 0) {
|
||||
path = _INDEX_HTML_PATH;
|
||||
}
|
||||
|
||||
path = path!.Replace('/', '.');
|
||||
|
||||
var output = GetManifestResourceStream(path);
|
||||
if (output == null) {
|
||||
var resName = _allResNames.FirstOrDefault(x => path.EndsWith(x, StringComparison.OrdinalIgnoreCase));
|
||||
output = resName == null
|
||||
? GetManifestResourceStream(_INDEX_HTML_PATH)
|
||||
: GetManifestResourceStream(resName);
|
||||
}
|
||||
|
||||
if (output != null) {
|
||||
context.Response.ContentLength = output.Length;
|
||||
context.Response.ContentType = MimeTypeHelper.GetMimeTypeByExtName(_regex.Match(path!).Groups[1].Value);
|
||||
await output.CopyToAsync(context.Response.Body);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await next.Invoke();
|
||||
}
|
||||
}
|
@ -13,11 +13,11 @@ namespace NetAdmin.BizServer.Host.Extensions;
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册FreeSql
|
||||
/// 添加 FreeSql
|
||||
/// </summary>
|
||||
public static IServiceCollection AddFreeSql(this IServiceCollection me)
|
||||
{
|
||||
_ = me.AddFreeSql( //
|
||||
return me.AddFreeSql( //
|
||||
FreeSqlInitOptions.SyncStructure | FreeSqlInitOptions.InsertSeedData, freeSql => {
|
||||
// 数据权限过滤器
|
||||
_ = freeSql.GlobalFilter.ApplyOnlyIf<IFieldOwner>( //
|
||||
@ -25,11 +25,10 @@ public static class ServiceCollectionExtensions
|
||||
, () => ContextUserInfo.Create()?.Roles.All(x => x.DataScope == DataScopes.Self) ?? false
|
||||
, a => a.OwnerId == ContextUserInfo.Create().Id);
|
||||
});
|
||||
return me;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// jwt授权处理器
|
||||
/// 添加 jwt 授权处理器
|
||||
/// </summary>
|
||||
public static IServiceCollection AddJwt(this IServiceCollection me)
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="$(SolutionDir)/CodeQuality.props"/>
|
||||
<Import Project="$(SolutionDir)/code.quality.props"/>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="$(SolutionDir)/assets/captcha/**" LinkBase="Assets/Captcha"/>
|
||||
</ItemGroup>
|
||||
@ -12,4 +12,7 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="../../../dist/frontend/admin/**/*" LinkBase="UI"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -5,8 +5,8 @@
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "http://localhost:65010",
|
||||
"applicationUrl": "http://[::]:65010",
|
||||
"launchUrl": "http://localhost:5010",
|
||||
"applicationUrl": "http://[::]:5010",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
using NetAdmin.BizServer.Host.Extensions;
|
||||
using NetAdmin.Host.Extensions;
|
||||
using NetAdmin.Host.Middlewares;
|
||||
#if !DEBUG
|
||||
using Prometheus;
|
||||
#endif
|
||||
|
||||
namespace NetAdmin.BizServer.Host;
|
||||
|
||||
@ -25,21 +27,23 @@ public sealed class Startup : NetAdmin.Host.Startup
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
_ = app //
|
||||
.UseRealIp() // 获取真实IP
|
||||
.EnableBuffering() // 启用 Body 重读
|
||||
.UseMiddleware<RequestAuditMiddleware>() // 请求审计
|
||||
.UseRealIp() // 使用RealIp中间件,用于获取真实客户端IP地址
|
||||
.EnableBuffering() // 启用请求体缓冲,允许多次读取请求体
|
||||
.UseMiddleware<RequestAuditMiddleware>() // 使用RequestAuditMiddleware中间件,执行请求审计
|
||||
#if DEBUG
|
||||
.UseOpenApiSkin() // Swagger皮肤
|
||||
.UseOpenApiSkin() // 使用OpenApiSkin中间件(仅在调试模式下),提供Swagger UI皮肤
|
||||
#else
|
||||
.UseVueAdmin() // 托管管理后台,仅在非调试模式下
|
||||
.UseHttpMetrics() // 使用HttpMetrics中间件,启用HTTP性能监控
|
||||
#endif
|
||||
.UseInject(string.Empty) // / Furion脚手架
|
||||
.UseUnifyResultStatusCodes() // 状态码拦截
|
||||
.UseCorsAccessor() // 跨域访问
|
||||
.UseRouting() // 路由映射
|
||||
.UseHttpMetrics() // 性能监控
|
||||
.UseAuthentication() // / 认证
|
||||
.UseAuthorization() // 授权
|
||||
.UseMiddleware<RemoveNullNodeMiddleware>() // 删除json空节点
|
||||
.UseEndpoints(); // 执行匹配的端点
|
||||
.UseInject(string.Empty) // 使用Inject中间件,Furion脚手架的依赖注入支持
|
||||
.UseUnifyResultStatusCodes() // 使用UnifyResultStatusCodes中间件,用于统一处理结果状态码
|
||||
.UseCorsAccessor() // 使用CorsAccessor中间件,启用跨域资源共享(CORS)支持
|
||||
.UseRouting() // 使用Routing中间件,配置路由映射
|
||||
.UseAuthentication() // 使用Authentication中间件,启用身份验证
|
||||
.UseAuthorization() // 使用Authorization中间件,启用授权
|
||||
.UseMiddleware<RemoveNullNodeMiddleware>() // 使用RemoveNullNodeMiddleware中间件,删除JSON中的空节点
|
||||
.UseEndpoints(); // 配置端点以处理请求
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -47,21 +51,21 @@ public sealed class Startup : NetAdmin.Host.Startup
|
||||
/// </summary>
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
_ = services.AddConsoleFormatter() // 控制台日志模板
|
||||
.AddAllOptions() // 注册配置项
|
||||
.AddJwt() // Jwt 授权处理器
|
||||
.AddSnowflake() // 雪花编号生成器
|
||||
.AddEventBus() // 事件总线
|
||||
.AddFreeSql() // freeSql
|
||||
.AddRemoteRequest() // 注册远程请求
|
||||
.AddCorsAccessor() // 支持跨域访问
|
||||
.AddContextUser() // 上下文用户
|
||||
.AddRedisCache() // Redis缓存
|
||||
_ = services.AddConsoleFormatter() // 添加控制台日志模板
|
||||
.AddAllOptions() // 添加配置项
|
||||
.AddJwt() // 添加 Jwt 授权处理器
|
||||
.AddSnowflake() // 添加雪花编号生成器
|
||||
.AddEventBus() // 添加事件总线
|
||||
.AddFreeSql() // 添加 freeSql
|
||||
.AddRemoteRequest() // 添加远程请求
|
||||
.AddCorsAccessor() // 添加支持跨域访问
|
||||
.AddContextUser() // 添加上下文用户
|
||||
.AddRedisCache() // 添加 Redis 缓存
|
||||
|
||||
// IMvcBuilder
|
||||
.AddControllers() // 注册控制器
|
||||
.AddJsonSerializer(true) // json序列化配置
|
||||
.AddDefaultApiResultHandler() // Api结果处理器
|
||||
.AddControllers() // 添加控制器
|
||||
.AddJsonSerializer(true) // 添加JSON序列化器,并设置显示枚举名而非数字枚举值
|
||||
.AddDefaultApiResultHandler() // 添加默认的API结果处理程序
|
||||
;
|
||||
}
|
||||
}
|
172
src/backend/NetAdmin.BizServer.Tests/AllTests.cs
Normal file
@ -0,0 +1,172 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using NetAdmin.BizServer.Host;
|
||||
using NetAdmin.Domain.Dto.Dependency;
|
||||
using NetAdmin.Domain.Dto.Sys.Api;
|
||||
using NetAdmin.Domain.Dto.Sys.Cache;
|
||||
using NetAdmin.Domain.Dto.Sys.Config;
|
||||
using NetAdmin.Domain.Dto.Sys.Tool;
|
||||
using NetAdmin.SysComponent.Application.Modules.Sys;
|
||||
using NetAdmin.Tests;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace NetAdmin.BizServer.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// 所有测试
|
||||
/// </summary>
|
||||
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
|
||||
public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper) :
|
||||
WebApiTestBase<Startup>(factory, testOutputHelper), IToolsModule, ICacheModule, IApiModule, IConfigModule
|
||||
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Fact]
|
||||
public async Task<CacheStatisticsRsp> CacheStatisticsAsync()
|
||||
{
|
||||
var rsp = await PostAsync("/api/sys/cache/cache.statistics", null);
|
||||
Assert.Equal(HttpStatusCode.OK, rsp.StatusCode);
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryConfigRsp> CreateAsync(CreateConfigReq req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryApiRsp> CreateAsync(CreateApiReq req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> DeleteAsync(DelReq req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> ExistAsync(QueryReq<QueryConfigReq> req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> ExistAsync(QueryReq<QueryApiReq> req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Theory]
|
||||
[InlineData(default)]
|
||||
public async Task<PagedQueryRsp<GetAllEntriesRsp>> GetAllEntriesAsync(PagedQueryReq<GetAllEntriesReq> req)
|
||||
{
|
||||
var rsp = await PostAsync("/api/sys/cache/get.all.entries"
|
||||
, JsonContent.Create(new PagedQueryReq<GetAllEntriesReq>()));
|
||||
Assert.Equal(HttpStatusCode.OK, rsp.StatusCode);
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryConfigRsp> GetAsync(QueryConfigReq req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryApiRsp> GetAsync(QueryApiReq req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryConfigRsp> GetLatestConfigAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<GetModulesRsp>> GetModulesAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Fact]
|
||||
public async Task<DateTime> GetServerUtcTimeAsync()
|
||||
{
|
||||
var response = await PostAsync("/api/sys/tools/get.server.utc.time", null);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Fact]
|
||||
public async Task<string> GetVersionAsync()
|
||||
{
|
||||
var response = await PostAsync("/api/sys/tools/version", null);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<(string Name, string Version)>> ModulesAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<PagedQueryRsp<QueryConfigRsp>> PagedQueryAsync(PagedQueryReq<QueryConfigReq> req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<PagedQueryRsp<QueryApiRsp>> PagedQueryAsync(PagedQueryReq<QueryApiReq> req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<QueryConfigRsp>> QueryAsync(QueryReq<QueryConfigReq> req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<QueryApiRsp>> QueryAsync(QueryReq<QueryApiReq> req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Fact]
|
||||
public async Task SyncAsync()
|
||||
{
|
||||
var response = await PostAsync("/api/sys/api/sync", null);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<QueryConfigRsp> UpdateAsync(UpdateConfigReq req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<NopReq> UpdateAsync(NopReq req)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -1,21 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../NetAdmin.Host/NetAdmin.Host.csproj"/>
|
||||
<ProjectReference Include="..\NetAdmin.BizServer.Host\NetAdmin.BizServer.Host.csproj"/>
|
||||
<ProjectReference Include="../NetAdmin.Tests/NetAdmin.Tests.csproj"/>
|
||||
<ProjectReference Include="../NetAdmin.BizServer.Host/NetAdmin.BizServer.Host.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0-preview-23371-04"/>
|
||||
<PackageReference Include="xunit" Version="2.5.1-pre.20"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1-pre.10">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0-preview-23531-01"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,18 +0,0 @@
|
||||
using Xunit;
|
||||
|
||||
namespace NetAdmin.BizServer.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// UsedNumberTests
|
||||
/// </summary>
|
||||
public class UsedNumberTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test1
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
// Assert.True("1".Int32() == 2);
|
||||
}
|
||||
}
|
@ -17,13 +17,9 @@ public abstract class CacheBase<TCacheContainer, TService> : ICache<TCacheContai
|
||||
Service = service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存对象
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public TCacheContainer Cache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联的服务
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public TService Service { get; }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="$(SolutionDir)/CodeQuality.props"/>
|
||||
<Import Project="$(SolutionDir)/code.quality.props"/>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../NetAdmin.Application/NetAdmin.Application.csproj"/>
|
||||
</ItemGroup>
|
||||
|
@ -1,7 +1,7 @@
|
||||
namespace NetAdmin.Domain.Attributes.DataValidation;
|
||||
|
||||
/// <summary>
|
||||
/// 证件号码
|
||||
/// 证件号码验证器
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||
public sealed class CertificateAttribute : RegexAttribute
|
||||
|
@ -1,7 +1,7 @@
|
||||
namespace NetAdmin.Domain.Attributes.DataValidation;
|
||||
|
||||
/// <summary>
|
||||
/// 中文姓名
|
||||
/// 中文姓名验证器
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||
public sealed class ChineseNameAttribute : RegexAttribute
|
||||
|
@ -1,23 +0,0 @@
|
||||
namespace NetAdmin.Domain.Attributes.DataValidation;
|
||||
|
||||
/// <summary>
|
||||
/// 电话验证器(手机或固话)
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||
public sealed class CulturePhoneAttribute : ValidationAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CulturePhoneAttribute" /> class.
|
||||
/// </summary>
|
||||
public CulturePhoneAttribute()
|
||||
{
|
||||
ErrorMessageResourceName = nameof(Ln.手机号码或座机号码);
|
||||
ErrorMessageResourceType = typeof(Ln);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsValid(object value)
|
||||
{
|
||||
return new MobileAttribute().IsValid(value) || new TelephoneAttribute().IsValid(value);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
namespace NetAdmin.Domain.Attributes.DataValidation;
|
||||
|
||||
/// <summary>
|
||||
/// 区间验证器
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||
public sealed class CultureRangeAttribute : RangeAttribute
|
||||
{
|
||||
/// <inheritdoc cref="RangeAttribute" />
|
||||
public CultureRangeAttribute(double minimum, double maximum) //
|
||||
: base(minimum, maximum) { }
|
||||
|
||||
/// <inheritdoc cref="RangeAttribute" />
|
||||
public CultureRangeAttribute(int minimum, int maximum) //
|
||||
: base(minimum, maximum) { }
|
||||
|
||||
/// <inheritdoc cref="RangeAttribute" />
|
||||
public CultureRangeAttribute(Type type, string minimum, string maximum) //
|
||||
: base(type, minimum, maximum) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FormatErrorMessage(string name)
|
||||
{
|
||||
return $"{ErrorMessageString} {Ln.必须介于} {Minimum} - {Maximum}";
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
namespace NetAdmin.Domain.Attributes.DataValidation;
|
||||
|
||||
/// <summary>
|
||||
/// 非空验证器
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||
public sealed class CultureRequiredAttribute : RequiredAttribute
|
||||
{
|
||||
/// <summary>Applies formatting to an error message, based on the data field where the error occurred.</summary>
|
||||
/// <param name="name">The name to include in the formatted message.</param>
|
||||
/// <exception cref="T:System.InvalidOperationException">The current attribute is malformed.</exception>
|
||||
/// <returns>An instance of the formatted error message.</returns>
|
||||
public override string FormatErrorMessage(string name)
|
||||
{
|
||||
return $"{ErrorMessageString.NullOrEmpty(name)} {Ln.不能为空}";
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
namespace NetAdmin.Domain.Attributes.DataValidation;
|
||||
|
||||
/// <summary>
|
||||
/// Url验证器
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||
public sealed class CultureUrlAttribute : RegexAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CultureUrlAttribute" /> class.
|
||||
/// </summary>
|
||||
public CultureUrlAttribute() //
|
||||
: base(Chars.RGX_URL)
|
||||
{
|
||||
ErrorMessageResourceName = nameof(Ln.无效网络地址);
|
||||
ErrorMessageResourceType = typeof(Ln);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
namespace NetAdmin.Domain.Attributes.DataValidation;
|
||||
|
||||
/// <summary>
|
||||
/// 端口号验证器
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||
public sealed class PortAttribute : RangeAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PortAttribute" /> class.
|
||||
/// </summary>
|
||||
public PortAttribute() //
|
||||
: base(1, 65535)
|
||||
{
|
||||
ErrorMessageResourceName = nameof(Ln.无效端口号);
|
||||
ErrorMessageResourceType = typeof(Ln);
|
||||
}
|
||||
}
|
@ -6,7 +6,4 @@ namespace NetAdmin.Domain.Attributes;
|
||||
/// 标记一个字段启用雪花编号生成
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class SnowflakeAttribute : Attribute
|
||||
{
|
||||
//
|
||||
}
|
||||
public sealed class SnowflakeAttribute : Attribute { }
|
@ -10,16 +10,19 @@ public sealed record ContextUserToken : DataAbstraction
|
||||
/// <summary>
|
||||
/// 用户编号
|
||||
/// </summary>
|
||||
public long Id { get; set; }
|
||||
/// ReSharper disable once MemberCanBePrivate.Global
|
||||
public long Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 做授权验证的Token,全局唯一,可以随时重置(强制下线)
|
||||
/// </summary>
|
||||
/// ReSharper disable once MemberCanBePrivate.Global
|
||||
public Guid Token { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
/// ReSharper disable once MemberCanBePrivate.Global
|
||||
public string UserName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -3,14 +3,10 @@ using NetAdmin.Domain.DbMaps.Dependency.Fields;
|
||||
|
||||
namespace NetAdmin.Domain.DbMaps.Dependency;
|
||||
|
||||
/// <summary>
|
||||
/// 不可变实体
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public abstract record ImmutableEntity : ImmutableEntity<long>
|
||||
{
|
||||
/// <summary>
|
||||
/// 唯一编码
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
|
||||
[Snowflake]
|
||||
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
|
||||
public override long Id { get; init; }
|
||||
@ -19,26 +15,20 @@ public abstract record ImmutableEntity : ImmutableEntity<long>
|
||||
/// <summary>
|
||||
/// 不可变实体
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="T">主键类型</typeparam>
|
||||
public abstract record ImmutableEntity<T> : LiteImmutableEntity<T>, IFieldCreatedUser
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建者编号
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserId" />
|
||||
[JsonIgnore]
|
||||
[Column(CanUpdate = false, Position = -1)]
|
||||
public long? CreatedUserId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建者用户名
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserName" />
|
||||
[JsonIgnore]
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31, CanUpdate = false, Position = -1)]
|
||||
public virtual string CreatedUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 唯一编码
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
|
||||
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
|
||||
public override T Id { get; init; }
|
||||
}
|
@ -3,14 +3,10 @@ using NetAdmin.Domain.DbMaps.Dependency.Fields;
|
||||
|
||||
namespace NetAdmin.Domain.DbMaps.Dependency;
|
||||
|
||||
/// <summary>
|
||||
/// 轻型不可变实体
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public abstract record LiteImmutableEntity : LiteImmutableEntity<long>
|
||||
{
|
||||
/// <summary>
|
||||
/// 唯一编码
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
|
||||
[Snowflake]
|
||||
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
|
||||
public override long Id { get; init; }
|
||||
@ -19,19 +15,15 @@ public abstract record LiteImmutableEntity : LiteImmutableEntity<long>
|
||||
/// <summary>
|
||||
/// 轻型不可变实体
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="T">主键类型</typeparam>
|
||||
public abstract record LiteImmutableEntity<T> : EntityBase, IFieldPrimary<T>, IFieldCreatedTime
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
|
||||
[JsonIgnore]
|
||||
[Column(ServerTime = DateTimeKind.Utc, CanUpdate = false, Position = -1)]
|
||||
public virtual DateTime CreatedTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 唯一编码
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
|
||||
[JsonIgnore]
|
||||
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
|
||||
public virtual T Id { get; init; }
|
||||
|