后端手册
后端手册
配置文件
appsettings.json说明
查看完整配置
{
"dbConfigs": [
{
"Conn": "Data Source=LAPTOP-STKF2M8H\\SQLEXPRESS;User ID=admin;Password=admin123;Initial Catalog=ZrAdmin;",
"DbType": 1, //数据库类型 MySql = 0, SqlServer = 1, Oracle = 3, PgSql = 4
"ConfigId": "0", //多租户唯一标识
"IsAutoCloseConnection": true
},
{
"Conn": "Data Source=LAPTOP-STKF2M8H\\SQLEXPRESS;User ID=admin;Password=admin123;Initial Catalog=ZrAdmin;",
"DbType": 1,
"ConfigId": "1", //多租户唯一标识(商城数据库)
"IsAutoCloseConnection": true
}
//...下面添加更多的数据库源
],
//代码生成数据库配置
"CodeGenDbConfig": {
//代码生成连接字符串,注意{dbName}为固定格式,不要填写数据库名
"Conn": "Data Source=LAPTOP-STKF2M8H\\SQLEXPRESS;User ID=admin;Password=admin123;Initial Catalog={dbName};",
"DbType": 1,
"IsAutoCloseConnection": true,
"DbName": "ZrAdmin" //代码生成默认连接数据库
},
"urls": "http://localhost:8888", //项目启动url
"corsUrls": ["http://localhost:8887", "http://localhost:8886"], //跨域地址,多个用","隔开
//jwt 授权认证配置
"JwtSettings": {
"Issuer": "ZRAdmin.NET",
"Audience": "ZRAdmin.NET",
"SecretKey": "SecretKey-ZRADMIN.NET-202311281883838",
"Expire": 1440, //jwt登录超时时间(分)
"RefreshTokenTime": 30, //token有效期剩多久(分钟)刷新token
"TokenType": "Bearer"
},
// 多租户配置
"MainDb": "0", // 主库 ConfigId
"MallDb": "0", // 商城库 ConfigId
"TenantSettings": {
"UseTenant": 0, //多租户开关 0:关闭, 1/true/on:启用
"PlatformMenuPermPrefixes": ["system:menu:", "system:tenant:", "monitor:job:"]
},
"InjectClass": ["ZR.Repository", "ZR.Service", "ZR.Tasks", "ZR.ServiceCore", "ZR.Mall"], //自动注入类
"ShowDbLog": false, //是否打印db日志
"InitDb": false, //是否初始化db
"InitSeed": true, //建表后是否自动导入种子数据
"DemoMode": false, //是否演示模式
"SingleLogin": false, //是否允许多设备/浏览器登录
"workId": 1, //雪花id唯一数字
"sqlExecutionTime": 5, //Sql执行时间超过多少秒记录日志并警报
//上传配置
"Upload": {
"UploadUrl": "http://localhost:8888/uploads", //默认存储访问域名
"localSavePath": "", //本地存储目录,空=wwwroot
"maxSize": 15, //上传文件大小限制 15M
"requestLimitSize": 50, //请求body大小限制 50M
"notAllowedExt": [".bat", ".exe", ".jar", ".js"]
},
//阿里云存储配置
"ALIYUN_OSS": {
"REGIONID": "cn-hangzhou",
"KEY": "XX",
"SECRET": "XX",
"bucketName": "bucketName",
"domainUrl": "http://xxx.xxx.com"
},
//企业微信通知配置
"WxCorp": {
"AgentID": "",
"CorpID": "",
"CorpSecret": "",
"SendUser": "@all"
},
//微信公众号设置
"WxOpen": {
"AppID": "",
"AppSecret": ""
},
//邮箱配置信息
"MailOptions": [
{
"FromName": "system",
"FromEmail": "", //eg:xxxx@qq.com
"Password": "",
"Smtp": "smtp.qq.com",
"Port": 587,
"Signature": "系统邮件,请勿回复!",
"UseSsl": true
}
],
//redis服务配置
"RedisServer": {
"open": 0, //是否启用redis
"dbCache": false, //数据库是否使用Redis缓存,如果启用open要为1
"Cache": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=cache:",
"Session": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=session:"
},
//代码生成配置
"CodeGen": {
"showApp": false,
"autoPre": true,
"moduleName": "business",
"author": "admin",
"tablePrefix": "sys_",
"vuePath": ""
}
}
codeGen.json代码生成
查看完整配置
{
"CodeGen": {
"csharpTypeArr": {
"string": ["varchar", "nvarchar", "text", "longtext"],
"int": ["int", "integer", "smallint", "int4", "int8", "int2"],
"long": ["bigint", "number"],
"float": ["numeric", "real", "float"],
"decimal": ["money", "decimal", "smallmoney"],
"dateTime": ["date", "datetime", "datetime2", "smalldatetime", "timestamp"],
"byte": ["tinyint"],
"bool": ["bit"]
}
}
}
iprate.jsonIP 限流
查看完整配置
{
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"EndpointWhitelist": ["post:/system/dict/data/types", "*:/msghub/negotiate", "*:/LogOut", "*:/common/uploadfile"],
"QuotaExceededResponse": {
"Content": "{{\"code\":429,\"msg\":\"访问过于频繁,请稍后重试\"}}",
"ContentType": "application/json",
"StatusCode": 429
},
"GeneralRules": [
{
"Endpoint": "*:/captchaImage",
"Period": "3s",
"Limit": 5
},
{
"Endpoint": "((post)|(put)):*",
"Period": "3s",
"Limit": 1
}
],
"IpRateLimitPolicies": {}
}
}
upload文件上传
//本地环境配置
"Upload": {
"UploadUrl": "http://xxx.xx.com/uploads",
"localSavePath": "",
},
//线上环境配置
"Upload": {
"UploadUrl": "http://xxx.xx.com/prod-api/uploads",
"localSavePath": "uploads",
},
环境配置
建议自己项目新建对应环境的配置文件:
- 生产环境:
appsettings.Production.json - 开发环境:
appsettings.Development.json
有关跨域配置
前后端单独部署时需要配置跨域:
//跨域地址,配置前端启动地址多个用","隔开。注意:http://localhost:8887 和 http://localhost:8887/是不同的
"corsUrls": ["http://localhost:8887"]
获取用户信息
在 Controller 或 Service 层中获取:
// Controller 中获取
long userId = HttpContext.GetUId(); // 用户ID
string userName = HttpContext.GetName(); // 用户名
long deptId = HttpContext.GetDeptId(); // 部门ID
// Service 层获取(通过 App.HttpContext,等价于上面)
string userName = App.HttpContext.GetName();
long userId = App.HttpContext.GetUId();
// 获取完整登录信息(含 Permissions,首次从 Redis 补全并回写 Items)
LoginUser info = HttpContext.GetCurrentUser(); // Controller 内
LoginUser info = App.HttpContext.GetCurrentUser(); // Service 内
//获取当前租户ID
string tenantId = App.GetCurrentTenantId();
//获取主库ConfigId
string mainDb = App.MainDbConfigId;
分页实现
- 前端基于 element 封装的分页组件 pagination
- 后端基于 SqlSugar
前端调用实现
// 一般在查询参数中定义分页变量
queryParams: {
pageNum: 1,
pageSize: 10
}
// 页面添加分页组件,传入分页变量
<pagination
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"/>
// 调用后台方法,传入参数 获取结果
listUser(this.queryParams).then(response => {
this.userList = response.data.result;
this.total = response.data.totalNum;
})
vue3 需要注意:
<pagination
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"/>
后台逻辑实现
方式一:扩展实现
// 查询方法
public List<SysLogininfor> GetLoginLog(PagerInfo pager)
{
int totalCount = 0;
var list = Context.Queryable<SysLogininfor>()
.ToPageList(pager.PageNum, pager.PageSize, ref totalCount);
pager.TotalNum = totalCount;
return list;
}
//前端返回
public IActionResult LoginLogList([FromQuery] PagerInfo pagerInfo)
{
var list = sysLoginService.GetLoginLog(pagerInfo);
return SUCCESS(list.ToPage(pagerInfo));
}
方式二:直接返回
public IActionResult List([FromQuery] SysPost post, [FromQuery] PagerInfo pagerInfo)
{
//开始拼装查询条件
var predicate = Expressionable.Create<SysPost>();
var list = PostService.GetPages(predicate.ToExpression(), pagerInfo, s => new { s.PostSort });
return SUCCESS(list);
}
导出 Excel
多数据库源设置
添加数据库配置
修改 appsettings.json,项目已支持多库配置,只需在配置文件中添加对应数据库即可。
"dbConfigs": [
{
"Conn": "Data Source=localhost;Initial Catalog=ZrAdmin;...",
"DbType": 1, //数据库类型 MySql = 0, SqlServer = 1, Oracle = 3, PgSql = 4
"ConfigId": "0",
"IsAutoCloseConnection": true
},
{
"Conn": "Data Source=localhost;Initial Catalog=myAdmin;...",
"DbType": 1,
"ConfigId": "1",
"IsAutoCloseConnection": true
}
],
实体类配置
[Tenant("1")]
public class GenDemo
{
//...属性
}
Tenant("1") 表示连接 ConfigId = "1" 的数据库,配置后自动路由。
更多用法:SqlSugar 多租户文档
多库事务
try
{
itenant.BeginTran();
//库1
itenant.GetConnection("1").Insertable(new SysUser { });
//库2
itenant.GetConnection("2").Insertable(new SysUser { });
itenant.CommitTran();
}
catch (Exception ex)
{
itenant.RollbackTran();
Console.WriteLine(ex.Message);
}
雪花 id
使用雪花 id 作为 long 类型后,前端 JS 精度丢失需要做序列化转换:
[JsonConverter(typeof(ValueToStringConverter))]
[SugarColumn(IsPrimaryKey = true)]
public long Id { get; set; }
- 雪花 ID 需要配置
workId避免 id 重复,program.cs中已配置 - 更多:SqlSugar 雪花 ID 文档
关于自动刷新 token
默认 JWT token 有效时间为 1440 分钟(JwtSettings.Expire)。自动刷新机制在 jwt 有效期结束前 5 分钟有操作时触发(JwtAuthMiddleware),可根据实际情况调整时间:
if (!CacheHelper.Exists(cacheKey) && ts.TotalMinutes < 5 && ts.TotalMinutes > 0)
{
// 自动刷新 token
}
数据脱敏
- 后端使用
1. 概述
项目对手机号、邮箱、身份证、IP、昵称等敏感字段提供统一脱敏能力,由两部分组成:
MaskUtil(命名空间ZR.Infrastructure.Helper):纯函数式脱敏工具,输入原值、返回脱敏值。MaskExtensions(命名空间ZR.Infrastructure.Helper):扩展方法,把「权限判断 + 循环/赋值 + 脱敏写回」收口为一行调用,消除各处重复的foreach + if样板代码。
脱敏是否生效取决于当前登录用户是否拥有对应敏感数据查看权限(见 SensitivePerms):无权限才脱敏,有权限返回明文。
2. 敏感权限常量(SensitivePerms)
位于 Infrastructure/Constant/SensitivePerms.cs,命名空间 ZR.Infrastructure.Constant。
| 常量 | 值 | 含义 |
|---|---|---|
ViewRealPhone | p:vrp | 查看真实手机号 |
ViewRealIdCard | p:vri | 查看真实身份证 |
ViewEmail | p:ve | 查看真实邮箱 |
ViewRealIP | p:vip | 查看真实 IP |
权限判断:HttpContextExtension.HasSensitivePerm(App.HttpContext, SensitivePerms.XXX),返回 true 表示拥有权限(即不脱敏)。
3. MaskUtil 脱敏方法
命名空间 ZR.Infrastructure.Helper。均为 public static string,入参为 null/空时原样返回。
| 方法 | 规则 | 示例 |
|---|---|---|
MaskPhone | 保留前 3 后 4,中间 **** | 13812345678 → 138****5678 |
MaskEmail | 保留首字符与完整域名 | zhangsan@example.com → z****@example.com |
MaskIdCard | 保留前 4 后 4,中间 8 位 * | 110101199001011234 → 1101********1234 |
MaskName | 首尾各 1,中间 * | 张三 → 张*;张三丰 → 张*丰 |
MaskIp | IPv4 保留前 2 段,IPv6 保留前 3 段 | 123.45.67.89 → 123.45.*.* |
4. MaskExtensions 扩展方法(推荐用法)
命名空间 ZR.Infrastructure.Helper。把权限判断与脱敏统一收口,避免重复代码,且权限判断只执行一次。
4.1 列表脱敏
list.Result.MaskField(
HttpContextExtension.HasSensitivePerm(App.HttpContext, SensitivePerms.ViewRealIP), // hasPerm:true=不脱敏
it => it.OperIp, // getter:读取字段
(it, v) => it.OperIp = v, // setter:写回字段
MaskUtil.MaskIp); // 脱敏方法
4.2 单个对象脱敏
user.MaskField(
HttpContextExtension.HasSensitivePerm(App.HttpContext, SensitivePerms.ViewRealPhone),
it => it.Phonenumber, (it, v) => it.Phonenumber = v, MaskUtil.MaskPhone);
参数为
true(拥有权限)或对象为null时直接返回,不做任何处理。
5. 业务示例
| 场景 | 位置 |
|---|---|
| 操作日志 IP | SysOperLogService.SelectOperLogList |
| 登录日志 IP | SysLoginService.GetLoginLog |
| 用户列表 手机/邮箱 | SysUserService 列表查询 |
| 单个用户 手机/邮箱 | SysUserService.SelectUserById |
6. 新增一个脱敏字段的步骤
- 在
MaskUtil增加对应脱敏方法(如尚无匹配规则)。 - 在
SensitivePerms增加权限常量(如尚无)。 - 在返回数据处调用
xxx.MaskField(hasPerm, getter, setter, mask)。
注意:邮箱务必使用
MaskUtil.MaskEmail,不要复用MaskUtil.MaskPhone(二者规则不同)。
- 前端:需给当前角色授权对应的敏感权限。

JnTemplate 模板引擎使用
可用于邮件模板等场景,引用命名空间 using JinianNet.JNTemplate;
读取模板
var tpl = JnHelper.ReadTemplate("wwwroot下面的文件夹", "文件名");
比如读取启动 logo:
var contentTpl = JnHelper.ReadTemplate("", "logo.txt");
var content = contentTpl?.Render();
Console.WriteLine(content);
设置变量 & 输出
tpl.Set("user", new { Name = "Lisa", Id = 1, Sex = 0 });
var result = tpl.Render();
Console.WriteLine(result);
调用接口(非注入方式)
在无法使用 DI 注入的特殊场景中获取服务:
public void Test()
{
ITaskSchedulerServer _schedulerServer = App.GetRequiredService<ITaskSchedulerServer>();
}
网络请求
系统内置了网络请求模块,位置:Infrastructure.Helper.HttpHelper.cs
根据 IP 获取地理位置
var ip_info = IpTool.Search(ip);
提示
ip2region.xdb 是其数据库文件,开源库传送门
缓存监控
加密解密工具使用
本项目内置了常用的加密工具类:
// MD5
NETCore.Encrypt.EncryptProvider.Md5("123456");
// Base64
NETCore.Encrypt.EncryptProvider.Base64Encrypt("123456");
其他加密方式通过 EncryptProvider 类库查看。
接口限流
系统内置 IpRateLimiting 组件,默认对 (post)|(put) 请求方法进行 3 秒内只能访问一次的限流,超过提示访问过于频繁,请稍后重试。
详见上方 👆 配置文件 iprate.json 部分。
