Compare commits

..

127 Commits

Author SHA1 Message Date
橙子
eabbd55789 feat: 基础设施搭建 2024-10-14 00:29:55 +08:00
橙子
801e30c1dc !64 修复Ruoyi启动的系列警告信息。
Merge pull request !64 from Po/abp
2024-10-13 10:34:46 +00:00
Po
7049175827 修复ruoyi调试警告信息 2024-10-13 18:29:02 +08:00
橙子
f27a5a135b fix: 修复找回密码问题 2024-10-13 01:04:58 +08:00
橙子
82ad9e249a !63 总算可以管理角色的用户了
Merge pull request !63 from 李大饼/abp
2024-10-12 17:01:41 +00:00
李大饼
fcff711a04 处理冲突 2024-10-12 13:42:48 +00:00
橙子
0c78b8d868 !61 fix:修复部分菜单功能下下拉菜单宽度过窄导致选中内容看不到
Merge pull request !61 from 李大饼/abp
2024-10-12 13:22:33 +00:00
李大饼
b6b54164a8 feat:添加角色用户管理,修复一堆相关问题 2024-10-12 17:16:48 +08:00
李大饼
983daddebc refactor:用户菜单界面取消默认的用户显示(主要是单元格有点宽,对于实际使用意义不大),性别列显示出来(主要是看到字典请求了,但是前端没有这一列) 2024-10-12 02:00:21 +00:00
李大饼
605db9340c refactor:用户菜单界面取消默认的用户显示(主要是单元格有点宽,对于实际使用意义不大),性别列显示出来(主要是看到字典请求了,但是前端没有这一列) 2024-10-12 09:55:39 +08:00
李大饼
36ea04bd70 fix:修复部分菜单功能下下拉菜单宽度过窄导致选中内容看不到 2024-10-12 09:39:00 +08:00
橙子
6f691e45d8 Merge remote-tracking branch 'origin/abp' into abp 2024-10-11 19:58:30 +08:00
chenchun
2c6558874d feat: 恢复备案 2024-10-11 19:58:21 +08:00
chenchun
d60b432f0c style: 恢复备案 2024-10-11 16:48:11 +08:00
橙子
adb9849650 !58 update Yi.Abp.Net8/framework/Yi.Framework.Core/Extensions/HttpContextExtensions.cs.
Merge pull request !58 from 凯明/N/A
2024-10-10 07:09:22 +00:00
橙子
48abbbf83e !60 update Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Entities/LoginLogAggregateRoot.cs.
Merge pull request !60 from 凯明/N/A
2024-10-10 07:09:06 +00:00
橙子
9e5361338c !59 update Yi.Abp.Net8/framework/Yi.Framework.Core/Extensions/HttpContextExtensions.cs.
Merge pull request !59 from 凯明/N/A
2024-10-10 07:08:48 +00:00
凯明
c5ecd71c6e update Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Entities/LoginLogAggregateRoot.cs.
IpTool.Search(ipAddr); 可能搜索失败报错,加入try catch

Signed-off-by: 凯明 <120665461@qq.com>
2024-10-10 06:57:13 +00:00
凯明
b09bbba21b update Yi.Abp.Net8/framework/Yi.Framework.Core/Extensions/HttpContextExtensions.cs.
//Ip规则校验 ,内网使用时,前端传递的IP会包含端口号造成校验失败    

Signed-off-by: 凯明 <120665461@qq.com>
2024-10-10 06:53:43 +00:00
凯明
2db543573c update Yi.Abp.Net8/framework/Yi.Framework.Core/Extensions/HttpContextExtensions.cs.
//Ip规则校验 ,内网使用时,前端传递的IP会包含端口号造成校验失败

Signed-off-by: 凯明 <120665461@qq.com>
2024-10-10 06:48:14 +00:00
橙子
cadd4df5d0 !57 update Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Operlog/OperLogGlobalAttribute.cs.
Merge pull request !57 from 凯明/N/A
2024-10-10 03:54:04 +00:00
凯明
427de4b42f update Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Operlog/OperLogGlobalAttribute.cs.
搜索IP归属地失败时会直接报错,改为搜索失败时保存IP地址。

Signed-off-by: 凯明 <120665461@qq.com>
2024-10-10 03:39:58 +00:00
橙子
79cb82ea24 !56 rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Menu/MenuCreateInputVo.cs.
Merge pull request !56 from 凯明/N/A
2024-10-09 12:15:15 +00:00
橙子
1b472c4ad7 !55 update Yi.Pure.Vue3/src/views/system/menu/utils/hook.tsx.
Merge pull request !55 from 凯明/N/A
2024-10-09 12:14:42 +00:00
橙子
21ab4950c8 !54 fix:修复前端表格状态列tag显示异常问题
Merge pull request !54 from 李大饼/abp
2024-10-09 12:14:16 +00:00
凯明
1b2977d591 rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Menu/MenuCreateInputVo.cs.
根目录时传入空ID会报异常

Signed-off-by: 凯明 <120665461@qq.com>
2024-10-09 09:27:32 +00:00
凯明
c54cf0bca2 update Yi.Pure.Vue3/src/views/system/menu/utils/hook.tsx.
根目录下创建菜单时,parentId为空,应该传入"00000000-0000-0000-0000-000000000000",而不是0. 

Signed-off-by: 凯明 <120665461@qq.com>
2024-10-09 09:07:08 +00:00
李大饼
935c990fe4 fix:修复前端表格状态列tag显示异常问题 2024-10-08 16:13:51 +08:00
橙子
44f94f8398 fix: 修复更新用户密码处理 2024-10-07 22:24:02 +08:00
橙子
8380cb1084 feat: 新增一键备案切换模式功能 2024-10-07 15:10:05 +08:00
橙子
83fc4f46b2 !49 pure补充头像上传逻辑
Merge pull request !49 from tyjctl/abp
2024-10-07 07:02:36 +00:00
橙子
9a97134a37 !52 解决Pure新建用户赋予权限菜单不显示问题Bug
Merge pull request !52 from Po/abp
2024-10-07 06:58:58 +00:00
橙子
912010ed70 !51 添加 PostgreSQL 数据库的配置,并新增驼峰转下划线功能
Merge pull request !51 from 凤凰/abp
2024-10-07 06:54:04 +00:00
Po
09b0bd8b09 修正Pure新建用户赋予权限菜单不显示问题 2024-10-06 22:40:17 +08:00
凤凰
c0dece8936 update Yi.RuoYi.Vue3/src/views/system/tenant/index.vue. 2024-10-05 19:40:41 +00:00
Your Name
571b610417 添加 PostgreSQL 数据库的配置,并新增驼峰转下划线功能 2024-10-06 03:32:34 +08:00
Dev Po
658339047c .gitignore排除日志目录和db文件 2024-10-05 20:19:35 +08:00
橙子
13120712b1 feat: 优化聊天室功能,修复复制等问题 2024-10-04 00:37:36 +08:00
橙子
10e1fad7f3 feat: 新增找回密码功能 2024-10-04 00:00:44 +08:00
橙子
d7629763ef feat: 新增找回密码功能接口 2024-10-03 01:10:32 +08:00
橙子
94ee0fb058 feat: 支持注册带入昵称 2024-10-02 23:25:29 +08:00
橙子
d4e8ce9c89 feat: 优化异步操作 2024-09-30 22:34:12 +08:00
橙子
707e241f25 feat: 主题展示图片 2024-09-30 00:54:28 +08:00
橙子
58fa94e8b8 feat: 新增主题图片功能 2024-09-30 00:40:47 +08:00
橙子
72d307503e style: 修改站点样式 2024-09-29 00:55:30 +08:00
橙子
d9fd9163e4 feat: 优化前端请求加载 2024-09-29 00:47:48 +08:00
chenchun
21807c3a66 Merge remote-tracking branch 'origin/abp' into abp 2024-09-25 12:09:47 +08:00
chenchun
f499d2d8a9 feat: 优化db连接字符串获取 2024-09-25 12:09:34 +08:00
tyjctl
0f9958bb26 update Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs.
Signed-off-by: tyjctl <419999127@qq.com>
2024-09-24 07:13:14 +00:00
tyjctl
8e66a9880c 补充pure用户头像上传功能 2024-09-24 14:44:06 +08:00
橙子
38e112fb06 update README-en.md.
Signed-off-by: 橙子 <454313500@qq.com>
2024-09-24 03:47:34 +00:00
chenchun
bb0e48cd41 style: 新增英文readme 2024-09-24 11:45:51 +08:00
chenchun
fd0edd93ea feat: 优化rbac结构 2024-09-24 11:16:19 +08:00
chenchun
6359696bde chorm: 合并pr:https://gitee.com/ccnetcore/Yi/pulls/44/files 2024-09-24 10:24:38 +08:00
橙子
1fee392989 !45 update Yi.RuoYi.Vue3/src/views/system/user/index.vue.
Merge pull request !45 from ゞ↘絟℡℃ツ/N/A
2024-09-24 02:18:08 +00:00
橙子
dfdced9644 !46 update Yi.RuoYi.Vue3/src/views/code/field/components/TableList.vue.
Merge pull request !46 from ゞ↘絟℡℃ツ/N/A
2024-09-24 02:17:29 +00:00
ゞ↘絟℡℃ツ
e50c1c374a update Yi.RuoYi.Vue3/src/views/code/field/components/TableList.vue.
增加简单分页

Signed-off-by: ゞ↘絟℡℃ツ <137586129@qq.com>
2024-09-19 02:12:46 +00:00
ゞ↘絟℡℃ツ
dbcd051aae update Yi.RuoYi.Vue3/src/views/system/user/index.vue.
修复 用户信息编辑框, 状态单选框, 因缺少 :label="dict.value", 导致的,  无法选择单选框问题

Signed-off-by: ゞ↘絟℡℃ツ <137586129@qq.com>
2024-09-19 02:02:04 +00:00
橙子
96571bb999 !43 update Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Operlog/OperLogGlobalAttribute.cs.
Merge pull request !43 from tyjctl/N/A
2024-09-18 14:21:04 +00:00
橙子
0620f6b6e3 Merge remote-tracking branch 'origin/abp' into abp 2024-09-18 22:17:55 +08:00
橙子
ae163167b6 feat: 兼容nuget 2024-09-18 22:17:45 +08:00
tyjctl
ba0bb32b5f update Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Operlog/OperLogGlobalAttribute.cs.
OperLog保存请求参数

Signed-off-by: tyjctl <419999127@qq.com>
2024-09-18 13:41:38 +00:00
橙子
b15e789b0b !42 使用linux自带命令获取内存信息,解决在docker容器内无法监控内存信息
Merge pull request !42 from Bi8bo/abp
2024-09-18 13:40:15 +00:00
橙子
8c7afa2e7a feat: 补充缺少文件 2024-09-18 21:38:22 +08:00
Bi8bo
87e30b9edf 修复在docker 容器内无法获取内存相关信息的问题 2024-09-18 09:51:02 +08:00
Bi8bo
887ebe6f2f 修复系统监控模块 cpu核心数的bug,调整接口响应模型结构 2024-09-14 14:12:35 +08:00
橙子
099581dddc update README.md.
Signed-off-by: 橙子 <454313500@qq.com>
2024-09-09 06:14:48 +00:00
橙子
3e84890765 style: 修改readme样式 2024-09-07 15:12:05 +08:00
橙子
d2fb0791d9 style: 新增readme 说明 2024-09-07 15:09:36 +08:00
橙子
a2f22007cf chorm: 新增发布文件 2024-09-07 14:41:15 +08:00
橙子
d4dd531ac4 Merge branch 'refs/heads/pure-dev' into abp 2024-09-07 13:49:43 +08:00
橙子
8374c81860 feat: 完成ruoyi、pure、yi、bbs 三位一体 2024-09-07 13:49:25 +08:00
橙子
579f60e789 feat: 菜单种子数据 2024-09-07 13:43:28 +08:00
橙子
40b5f33c4e feat: 完成菜单接入 2024-09-07 13:43:00 +08:00
橙子
978a7fab4c feat: 完成ruoyi、pure菜单兼容 2024-09-07 02:17:07 +08:00
橙子
f7790c46d2 Merge branch 'refs/heads/pure-dev' into abp 2024-09-06 22:06:26 +08:00
chenchun
9fc5b521e5 feat: 新增pure菜单路由 2024-09-06 18:23:11 +08:00
橙子
775e31c5e9 Merge branch 'refs/heads/pure-dev' into abp 2024-09-05 23:13:18 +08:00
橙子
3339e30014 feat: 整体pure,核心功能对接完成 2024-09-05 23:10:40 +08:00
chenchun
4ed44a2a8f feat:新增个人中心api 2024-09-05 21:44:10 +08:00
橙子
6134e76030 feat: 完成在线用户、登录日志、操作日志页面 2024-09-04 23:31:42 +08:00
chenchun
e1ea210fe9 fix: 合并修复构建错误 2024-09-04 16:35:21 +08:00
橙子
d9022a0383 !33 添加前端无感刷新功能,并将token相关改为localstage存储
Merge pull request !33 from daxiongok/master
2024-09-04 08:33:00 +00:00
橙子
0b0c1405ea !40 新增支持abp实体IHasConcurrencyStamp接口,并发更新ConcurrencyStamp
Merge pull request !40 from Bi8bo/Fix-ConcurrencyStampProp-invalid
2024-09-04 08:29:58 +00:00
chenchun
e393e1f525 feat:新增岗位管理 2024-09-04 16:24:26 +08:00
橙子
ad78cb1bcd fix: 修复登录验证码样式问题 2024-09-04 00:17:49 +08:00
橙子
e886614641 feat: 完成用户管理、角色管理、菜单管理、部门管理 2024-09-02 23:26:41 +08:00
chenchun
bc83362b35 feat:新增菜单、部门页面 2024-09-02 18:16:07 +08:00
chenchun
b648f09f16 feat:完善接口定义 2024-09-02 17:16:25 +08:00
Bi8bo
eb8d1626ea 并发修改失败修改为异常抛出 2024-09-02 16:06:20 +08:00
Bi8bo
db94cd32d5 单实体更新支持abp IHasConcurrencyStamp接口,乐观锁更新 2024-09-02 15:01:49 +08:00
橙子
71fd5a13fc feat: 完成pure角色页面对接 2024-09-01 21:34:36 +08:00
橙子
67c7ef37e6 feat: 新增支持furion规范化接口格式 2024-09-01 03:06:03 +08:00
橙子
2e22f4ea67 !38 审计日志报错
Merge pull request !38 from faith/abp
2024-08-31 15:28:30 +00:00
橙子
8be36f088b feat: 完成用户页面查询 2024-08-31 22:01:55 +08:00
Administrator
b985c2c784 修改审计日志bug,表达式里只能有具体值 2024-08-31 20:24:26 +08:00
橙子
e39d381f08 Merge branch 'refs/heads/abp' into pure 2024-08-31 19:08:38 +08:00
橙子
7694c7f97b !36 解决Sqlsugar Select()映射时如果嵌套对象,实体访问修饰符为非public的属性无法绑定值
Merge pull request !36 from Bi8bo/fix-sqlsugar-select-valuebind
2024-08-31 10:51:22 +00:00
橙子
eadb5eb216 !34 修复CPU系统使用率、当前空闲率
Merge pull request !34 from GitHubList/abp
2024-08-31 10:47:28 +00:00
橙子
cf5c46b2ce feat: 完成用户页面查询 2024-08-31 12:55:04 +08:00
chenchun
6e9dd669ba feat:完善用户表单页面对接 2024-08-30 17:36:33 +08:00
chenchun
60ef93b510 feat:完善对应菜单 2024-08-30 16:08:20 +08:00
bi8bo
e3aada0fff 替换Sqlsugar默认序列化器,解决.Select()映射嵌套/匿名类时,实体的非公有访问器 值无法绑定,如Id(protect属性) 2024-08-30 14:41:45 +08:00
橙子
bbe1a44788 feat: 完成登录页面接入pure 2024-08-29 22:59:16 +08:00
chenchun
bfe0f346c8 fix:修复软删除问题 2024-08-29 10:26:07 +08:00
GitHubList
8f10146d39 修复CPU系统使用率、当前空闲率 2024-08-28 00:03:15 +08:00
chenchun
5f402488d4 feat:登录页面改造 2024-08-23 18:26:26 +08:00
chenchun
07c48479af chorm: 搭建前端 2024-08-23 17:00:18 +08:00
chenchun
4bc2cebd60 feat:新增pure-admin前端 2024-08-23 14:31:00 +08:00
daxiongok
dc242420f8 feat:添加列表排序支持和默认排序
Signed-off-by: daxiongok <571115139@qq.com>
2024-08-18 10:48:54 +08:00
daxiongok
0656e3f536 feat:默认时间倒序
Signed-off-by: daxiongok <571115139@qq.com>
2024-08-18 10:20:23 +08:00
daxiongok
cfffcda068 Merge branch 'master' of https://gitee.com/tirisfalcn/Yi.git 2024-08-18 10:06:50 +08:00
daxiongok
187885fdb9 fix:未引用方法问题
Signed-off-by: daxiongok <571115139@qq.com>
2024-08-18 10:05:26 +08:00
daxiongok
f67b60dd82 update Yi.RuoYi.Vue3/src/utils/request.js.
Signed-off-by: daxiongok <12421064+tirisfalcn@user.noreply.gitee.com>
2024-08-17 15:08:17 +00:00
daxiongok
92a2421a9b feat:新增前端token无感刷新功能
fix:前端权限码太多时,cookie太大请求异常问题。改为localstage存储token

Signed-off-by: daxiongok <571115139@qq.com>
2024-08-17 22:53:19 +08:00
chenchun
556d32729a chorm: 构建 2024-08-16 19:07:08 +08:00
chenchun
4a3bd18bac fix: 修复新手任务 2024-08-16 18:57:17 +08:00
chenchun
de8f23bf2f fix: 修复更新昵称任务 2024-08-16 18:42:48 +08:00
chenchun
3691a74d7e feat: 上线新手任务功能 2024-08-16 18:33:10 +08:00
chenchun
2aba4eccee feat: 新增新手任务 2024-08-16 17:57:58 +08:00
chenchun
3e6d02eccc Merge branch 'refs/heads/abp-dev' into abp 2024-08-16 15:32:26 +08:00
橙子
2cf244058b fix:修复点赞主题通知问题 2024-08-15 21:53:28 +08:00
橙子
971f137a21 fix;修复链接跳转 2024-08-15 21:43:15 +08:00
橙子
b1a245c2a2 Merge branch 'refs/heads/abp-dev' into abp 2024-08-15 21:39:29 +08:00
chenchun
0c1ad1f4e5 Merge remote-tracking branch 'origin/abp' into abp 2024-08-12 18:19:53 +08:00
橙子
e2a675054c !29 update Yi.RuoYi.Vue3/src/views/system/dept/index.vue.
Merge pull request !29 from songjianjack/N/A
2024-08-12 10:15:37 +00:00
songjianjack
0ad49e9b9d update Yi.RuoYi.Vue3/src/views/system/dept/index.vue.
解决新增部门无法选择上级部门

Signed-off-by: songjianjack <1400053039@qq.com>
2024-08-12 09:38:04 +00:00
766 changed files with 110233 additions and 1888 deletions

7
.gitignore vendored
View File

@@ -20,7 +20,7 @@ x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[L]og/
# Visual Studio 2015 cache/options directory
.vs/
@@ -267,9 +267,10 @@ dist
.vscode
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Development.json
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Production.json
Logs
logs
/Yi.Abp.Net8/test/Yi.Abp.Test/appsettings.Development.json
/Yi.Abp.Net8/test/Yi.Abp.Test/appsettings.Production.json
database_backup
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Staging.json
/Yi.Abp.Net8/src/Yi.Abp.Web/logs/
/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db

213
README-en.md Normal file
View File

@@ -0,0 +1,213 @@
<h1 align="center"><img align="left" height="150px" src="https://ccnetcore.com/prod-api/wwwroot/logo.png"> Yi-Framework</h1>
<h4 align="center">A .NET 8 Web open-source Asp.NetCore framework focused on user experience.</h4>
<h5 align="center">Supports Native/Abp.vNext/Furion/Ruoyi/Pure</h5>
<h2 align="center">A comprehensive solution that ultimately becomes a wheel</h2>
[![star](https://gitee.com/ccnetcore/yi/badge/star.svg?theme=dark)](https://gitee.com/ccnetcore/Yi)
[![fork](https://gitee.com/ccnetcore/yi/badge/fork.svg?theme=dark)](https://gitee.com/ccnetcore/Yi)
[![license](https://img.shields.io/badge/license-MIT-yellow)](https://gitee.com/ccnetcore/Yi)
English | [简体中文](README.md)
****
## 🍍 Introduction:
YiFramework is a DDD (Domain-Driven Design) backend open-source framework based on .Net8, Abp.vNext, and SqlSugar.
Who says ABP is complex? Who says DDD is difficult?`Breaking conventions, simplifying complexity.`,Newcomer-friendly and one of the best approaches for project extensions.
Modular design allows for the independent inclusion or exclusion of components based on business needs. It is an all-encompassing framework where you may gain unique insights.
A Comprehensive Solution, Ultimately Just Another Wheel.
(Frequent updates, feel free to watch for continuous updates.)
— This is not just a program; it is also a work of art, focused on artistic development!
> Core Features: Simple and easy to use, the framework is not referenced in a packaged form, but is provided directly with the project alongside the source code. It offers maximum freedom and complies with the MIT license, allowing for unrestricted modifications (please indicate the source).
**Branch Directory**
- Branch **Abp**: Based on the Abp.vNext branch, DDD (Domain-Driven Design) simplifies the essence of development, providing support for multiple frontends from one backend.
- Yi.Abp.Net8Backend
- Yi.Bbs.Vue3Bbs Community - Frontend
- Yi.Doc.Md: Open Source Documentation Tutorial
- Yi.Pure.Vue3Pure TS Backend Frontend
- Yi.RuoYi.Vue3RuoYi JS Backend Frontend
****
## 🍊 Official website and demo link
Let's get straight to the point and provide the link.
YiCommunity official website URL.(Bbs)[ccnetcore.com](https://ccnetcore.com) (Now live, welcome to join!)
Rbachttps://ccnetcore.com:1000 (userName cc\password 123456)
Purehttps://ccnetcore.com:1001 userNamecc\password 123456
## 🍏 Support:
- [x] Fully supports monolithic application architecture
- [x] Fully supports distributed application architecture
- [x] Fully supports microservices architecture
****
## 🍇 Explosive Detail Yi Framework Tutorial Navigation:
1. [Framework Quick Start Guide](https://ccnetcore.com/article/aaa00329-7f35-d3fe-d258-3a0f8380b742)(Completed)
2. [Framework Functionality Module Tutorials](https://ccnetcore.com/article/8c464ab3-8ba5-2761-a4b0-3a0f83a9f312)(Completed)
3. [Practical Development Exercises](https://ccnetcore.com/article/e89c9593-f337-ada7-d108-3a0f83ae48e6)(Completed)
4. [Chengzi Ops CI/CD Tutorial](https://ccnetcore.com/article/6b80ed42-50cd-db15-c073-3a0fa8f7fd77)(Completed)
5. [Version Update Log](https://ccnetcore.com/article/e9e69a38-ce1e-06f5-7944-3a0fdc942ef3)(Completed)
****
## 🍓 Its philosophy:
Who says ABP is complicated? Who says DDD is difficult? Break the norm, simplify complexity, and serve as one of the best ways for newcomers and project second development.
> For every hundred people, there are a hundred different interpretations of DDD. The YiFramework may not strict adherence to DDD principles, but it is built on the shoulders of giants, distilled from numerous projects to craft a best practice.
Effortlessly achieve rapid development; typically, simplicity and elegance are hard to reconcile. The YiFramework does not solely pursue extreme decoupling but considers user experience and ease of use.
A user-oriented rapid development backend framework.
> Once you truly get hands-on, you'll understand this: extreme simplicity is also a form of elegance.
****
## 🍍 Features
- A user-oriented backend framework that is easy to use, suitable for small, medium, and enterprise-level projects.
- The project comes with the source code directly embedded, without packaging, making it ideal for secondary development and modification.
- Includes a large number of reusable modules for common scenarios.
- Elegantly supports distributed and microservices architectures.
- And more…
## 🥭 Core Technologies
#### Backend
C# Asp.NetCore 8.0
- [x] Dynamic API: Abp.vNext
- [x] Authentication and Authorization: Jwt
- [x] Logging: Serilog
- [x] Modularization: Abp.vNext
- [x] Dependency Injection: Autofac
- [x] Object Mapping: Mapster
- [x] ORM: SqlsugarCore
- [x] Multi-tenancy: Abp.vNext
- [x] Background Tasks: Quartz.Net
- [x] Local Caching: Abp.vNext
- [x] Distributed Caching: Abp.vNext
- [x] Event Bus: Abp.vNext
#### Frontend
js Vue3
- [x] Asynchronous Requests: axios
- [x] Charts: echarts
- [x] UI: element-plus
- [x] State Management: pinia
- [x] Routing: vue-router
- [x] Bundling: vite
#### DevOps
- [x] Deployment: nginx
- [x] CICD: gitlab+Jenkins
- [x] Docker: harbor
#### 🍉 Demo
<table>
<tr>
<td><img src="readme/101.png"/></td>
<td><img src="readme/102.png"/></td>
</tr>
<tr>
<td><img src="readme/103.png"/></td>
<td><img src="readme/104.png"/></td>
</tr>
</table>
<table>
<tr>
<td><img src="readme/201.png"/></td>
<td><img src="readme/202.png"/></td>
</tr>
<tr>
<td><img src="readme/203.png"/></td>
<td><img src="readme/204.png"/></td>
</tr>
<tr>
<td><img src="readme/205.png"/></td>
<td><img src="readme/206.png"/></td>
</tr>
</table>
<table>
<tr>
<td><img src="readme/1.png"/></td>
<td><img src="readme/2.png"/></td>
</tr>
<tr>
<td><img src="readme/3.png"/></td>
<td><img src="readme/4.png"/></td>
</tr>
<tr>
<td><img src="readme/3.png"/></td>
<td><img src="readme/4.png"/></td>
</tr>
<tr>
<td><img src="readme/5.png"/></td>
<td><img src="readme/6.png"/></td>
</tr>
<tr>
<td><img src="readme/7.png"/></td>
<td><img src="readme/8.png"/></td>
</tr>
<tr>
<td><img src="readme/9.png"/></td>
<td><img src="readme/10.png"/></td>
</tr>
<tr>
<td><img src="readme/11.png"/></td>
<td><img src="readme/12.png"/></td>
</tr>
</table>
## 🌶 Thank you
[橙子]https://ccnetcore.com
[XWen]https://gitee.com/on-wensil
[朝夕教育]https://www.zhaoxiedu.net
[Sqlsugar老杰哥]https://www.donet5.com/Home/Doc
[车神]微信公众号搜索Dotnet技术进阶
[RuYiAdmin如意老兄]https://gitee.com/pang-mingjun/RuYiAdmin
[ZrAdminNetCore字母老哥]https://gitee.com/izory/ZrAdminNetCore
[Admin.NET]https://gitee.com/zuohuaijun/Admin.NET
[Furion百小僧]https://furion.baiqian.ltd/
****
## 🌽 Contact Us:
Author's QQ`454313500`
QQ group chat官方一群Full、官方二群Full、官方三群`786308927`Full、官方四群:`498310311`Full、官方五群:`981136525`New
WeChat Group Chat官方微信一群Full、官方微信二群
WeChat Community: Add the author's WeChat chengzilaoge520 橙子老哥520,Note: Join the group.
Contact the author, everyone here is a consultant.
Official website message area[ccnetcore.com](https://ccnetcore.com)
****
## 🍄 FQA:
Visit the official website to view the message board.
[the message board](https://ccnetcore.com/discuss/1641030787056930818)

View File

@@ -1,6 +1,6 @@
<h1 align="center"><img align="left" height="150px" src="https://ccnetcore.com/prod-api/wwwroot/logo.png"> Yi框架</h1>
<h4 align="center">一套以用户体验出发的.Net8 Web开源框架</h4>
<h5 align="center">支持Abp.vNext 版本原生版本、Furion版本前端后台接入Ruoyi Vue3.0</h5>
<h5 align="center">支持Abp.vNext 版本原生版本、Furion版本前端接入Ruoyi/Pure Vue</h5>
<h2 align="center">集大成者,终究轮子</h2>
[![star](https://gitee.com/ccnetcore/yi/badge/star.svg?theme=dark)](https://gitee.com/ccnetcore/Yi)
@@ -9,7 +9,7 @@
[English](README-en.md) | 简体中文
****
## :tw-1f34e: 简介:
## 🍍 简介:
YiFramework是一个基于.Net8+Abp.vNext+SqlSugar的DDD领域驱动设计后端开源框架
谁说Abp复杂谁说DDD难`打破常规,化繁为简`,新人入门,项目二开,最佳方式之一
@@ -31,44 +31,45 @@ Yi框架-一套与SqlSugar一样爽的.Net8开源框架。
> 核心特点简单好用框架不以打包形式引用而是直接以项目附带源码给出自由度拉满遵循Mit协议允许随意修改请注明来源即可
**分支:**
**分支目录**
- (推荐) **Abp**: 基于Abp.vNext分支DDD领域驱动设计,回归开发本质,极度简单,用起来贼爽
- 分支**Abp**: 基于Abp.vNext分支DDD领域驱动设计,回归开发本质,极度简单,一个后台支持以下多个前端
- **Furion**: 基于Furion分支
- Yi.Abp.Net8后端
- Yi.Bbs.Vue3Bbs社区 前端
- Yi.Doc.Md: 开源文档教程
- Yi.Pure.Vue3Pure ts后台前端
- Yi.RuoYi.Vue3RuoYi js后台前端
****
## :tw-1f350: 官网及演示地址:
## 🍊 官网及演示地址:
废话少说直接上地址
Yi社区官网网址[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
Yi社区官网网址Bbs社区正式[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
Rbac后台管理系统:已上线,暂不提供演示地址,可本地部署访问
Rbac后台演示地址https://ccnetcore.com:1000 用户cc、密码123456
App移动端系统已上线暂不提供演示地址可本地部署访问
Pure后台演示地址https://ccnetcore.com:1001 用户cc、密码123456
Rbac演示地址https://ccnetcore.com:1000 用户cc、密码123456
## :tw-1f351: 支持:
## 🍏 支持:
- [x] 完全支持单体应用架构
- [x] 完全支持分布式应用架构
- [x] 完全支持微服务架构
****
## :tw-1f352: 详细到爆炸的Yi框架教程导航
## 🍇 详细到爆炸的Yi框架教程导航
1. [框架快速开始教程](https://ccnetcore.com/article/aaa00329-7f35-d3fe-d258-3a0f8380b742)(已完成)
2. [框架功能模块教程](https://ccnetcore.com/article/8c464ab3-8ba5-2761-a4b0-3a0f83a9f312)(已完成)
3. [实战演练开发教程](https://ccnetcore.com/article/e89c9593-f337-ada7-d108-3a0f83ae48e6)
3. [实战演练开发教程](https://ccnetcore.com/article/e89c9593-f337-ada7-d108-3a0f83ae48e6)(已完成)
4. [橙子运维CICD教程](https://ccnetcore.com/article/6b80ed42-50cd-db15-c073-3a0fa8f7fd77)(已完成)
5. [版本更新日志](https://ccnetcore.com/article/e9e69a38-ce1e-06f5-7944-3a0fdc942ef3)(已完成)
****
## :tw-1f353: 它的理念:
## 🍓 它的理念:
谁说Abp复杂谁说DDD难打破常规化繁为简新人入门项目二开最佳方式之一
> 一百个人就有一百种DDDYi框架不一定是极度严格的DDD而是站在巨人的肩膀上经过极多项目的提炼摸索出一种最佳实践
@@ -78,17 +79,17 @@ Rbac演示地址https://ccnetcore.com:1000 用户cc、密码123456
> 一个面向用户的快速开发后端框架
在真正的使用,你会明白这一点,极致的简单,也是优雅的一种体现。
在真正的使用,你会明白这一点,极致的简单,也是优雅的一种体现。
****
## :tw-1f354: 特点
## 🍍 特点
- 面向用户的后端框架,使用简单,适合小型、中型、企业级项目
- 项目直接内置源码,不打包,非常适合进行二开改造
- 内置包含大量通用场景模块
- 优雅支持分布式及微服务架构
- 等等
## :tw-1f340: 基础设施简介
## 🍍 基础设施简介
以下全部功能可直接使用:
@@ -96,14 +97,14 @@ Rbac演示地址https://ccnetcore.com:1000 用户cc、密码123456
- [SqlSugar官网](https://www.donet5.com/home/doc)
## :tw-1f341: 内置模块简介
- Rbac权限管理系统已上线
## 🍅 内置模块简介
- Rbac权限管理系统已上线支持pure、ruoyi前端
- Bbs论坛社区系统已上线
> 重复的东西,无需再写一遍,这也是优雅的体现之一
****
## :tw-1f31e: 核心技术
## 🥭 核心技术
#### 后端
C# Asp.NetCore 8.0
- [x] 动态ApiAbp.vNext
@@ -120,7 +121,7 @@ C# Asp.NetCore 8.0
- [x] 事件总线Abp.vNext
#### 前端
js Vue3.2
js Vue3
- [x] 异步请求axios
- [x] 图表echarts
- [x] uielement-plus
@@ -135,9 +136,9 @@ js Vue3.2
****
## :tw-1f366: 业务支持模块:
## 🍌 业务支持模块:
#### :tw-1f42f: RABC权限管理系统持续更新
#### 🍒 RABC权限管理系统持续更新
采用ruoyi前端
- 用户管理
- 角色管理
@@ -152,9 +153,8 @@ js Vue3.2
- 定时任务
- 缓存列表
- 服务监控
- WebFirst代码生成工具
#### :tw-1f431: BBS社区论坛系统持续更新
#### 🍐 BBS社区论坛系统持续更新
采用vue3前端
- 文章功能
- 板块功能
@@ -163,7 +163,7 @@ js Vue3.2
- 授权中心
- 权限管理
#### :star: 演示截图:
#### 🍉 演示截图:
<table>
<tr>
<td><img src="readme/101.png"/></td>
@@ -174,7 +174,22 @@ js Vue3.2
<td><img src="readme/104.png"/></td>
</tr>
</table>
<table>
<tr>
<td><img src="readme/201.png"/></td>
<td><img src="readme/202.png"/></td>
</tr>
<tr>
<td><img src="readme/203.png"/></td>
<td><img src="readme/204.png"/></td>
</tr>
<tr>
<td><img src="readme/205.png"/></td>
<td><img src="readme/206.png"/></td>
</tr>
</table>
<table>
<tr>
@@ -207,7 +222,7 @@ js Vue3.2
</tr>
</table>
## :tw-1f44f: 感谢:
## 🌶 感谢:
[橙子]https://ccnetcore.com
@@ -228,12 +243,14 @@ js Vue3.2
[Furion百小僧]https://furion.baiqian.ltd/
****
## :tw-1f438: 联系我们:
## 🌽 联系我们:
作者QQ`454313500`2029年之前作者24小时在线时刻保持活跃更新。
QQ交流群官方一群已满、官方二群已满、官方三群`786308927`(已满)、官方四群:`498310311`(基本已满)、官方五群:`981136525`(新群)
微信交流群:官方微信一群(已满)、官方微信二群
微信交流群:加作者微信 chengzilaoge520 橙子老哥520备注拉群
联系作者,这里人人都是顾问
@@ -241,7 +258,7 @@ QQ交流群官方一群已满、官方二群已满、官方三群
官方网址留言区:[ccnetcore.com](https://ccnetcore.com)
****
## :tw-1f41e: FQA:
## 🍄 FQA:
前往官网查看留言区

View File

@@ -35,6 +35,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
usings.props = usings.props
version.props = version.props
publish.bat = publish.bat
publish_Demo.bat = publish_Demo.bat
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SqlSugarCore.Abstractions", "framework\Yi.Framework.SqlSugarCore.Abstractions\Yi.Framework.SqlSugarCore.Abstractions.csproj", "{FD6D6860-3753-4747-8A26-977E4A3001F9}"

View File

@@ -0,0 +1,46 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 异常元数据
/// </summary>
public sealed class ExceptionMetadata
{
/// <summary>
/// 状态码
/// </summary>
public int StatusCode { get; internal set; }
/// <summary>
/// 错误码
/// </summary>
public object ErrorCode { get; internal set; }
/// <summary>
/// 错误码(没被复写过的 ErrorCode
/// </summary>
public object OriginErrorCode { get; internal set; }
/// <summary>
/// 错误对象(信息)
/// </summary>
public object Errors { get; internal set; }
/// <summary>
/// 额外数据
/// </summary>
public object Data { get; internal set; }
}

View File

@@ -0,0 +1,106 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Core.Extensions;
namespace Yi.Framework.AspNetCore.UnifyResult.Fiters;
/// <summary>
/// 友好异常拦截器
/// </summary>
public sealed class FriendlyExceptionFilter : IAsyncExceptionFilter
{
/// <summary>
/// 异常拦截
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnExceptionAsync(ExceptionContext context)
{
// 排除 WebSocket 请求处理
if (context.HttpContext.IsWebSocketRequest()) return;
// 如果异常在其他地方被标记了处理,那么这里不再处理
if (context.ExceptionHandled) return;
// 解析异常信息
var exceptionMetadata = GetExceptionMetadata(context);
IUnifyResultProvider unifyResult = context.GetRequiredService<IUnifyResultProvider>();
// 执行规范化异常处理
context.Result = unifyResult.OnException(context, exceptionMetadata);
// 创建日志记录器
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<FriendlyExceptionFilter>>();
// 记录拦截日常
logger.LogError(context.Exception, context.Exception.Message);
}
/// <summary>
/// 获取异常元数据
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static ExceptionMetadata GetExceptionMetadata(ActionContext context)
{
object errorCode = default;
object originErrorCode = default;
object errors = default;
object data = default;
var statusCode = StatusCodes.Status500InternalServerError;
var isValidationException = false; // 判断是否是验证异常
var isFriendlyException = false;
// 判断是否是 ExceptionContext 或者 ActionExecutedContext
var exception = context is ExceptionContext exContext
? exContext.Exception
: (
context is ActionExecutedContext edContext
? edContext.Exception
: default
);
// 判断是否是友好异常
if (exception is UserFriendlyException friendlyException)
{
int statusCode2 = 500;
int.TryParse(friendlyException.Code, out statusCode2);
isFriendlyException = true;
errorCode = friendlyException.Code;
originErrorCode = friendlyException.Code;
statusCode = statusCode2==0?403:statusCode2;
isValidationException = false;
errors = friendlyException.Message;
data = friendlyException.Data;
}
return new ExceptionMetadata
{
StatusCode = statusCode,
ErrorCode = errorCode,
OriginErrorCode = originErrorCode,
Errors = errors,
Data = data
};
}
}

View File

@@ -0,0 +1,276 @@
using System.Collections;
using System.Reflection;
using System.Text.Encodings.Web;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Core.Extensions;
namespace Yi.Framework.AspNetCore.UnifyResult.Fiters;
/// <summary>
/// 规范化结构(请求成功)过滤器
/// </summary>
public class SucceededUnifyResultFilter : IAsyncActionFilter, IOrderedFilter
{
/// <summary>
/// 过滤器排序
/// </summary>
private const int FilterOrder = 8888;
/// <summary>
/// 排序属性
/// </summary>
public int Order => FilterOrder;
/// <summary>
/// 处理规范化结果
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// 执行 Action 并获取结果
var actionExecutedContext = await next();
// 排除 WebSocket 请求处理
if (actionExecutedContext.HttpContext.IsWebSocketRequest()) return;
// 处理已经含有状态码结果的 Result
if (actionExecutedContext.Result is IStatusCodeActionResult statusCodeResult &&
statusCodeResult.StatusCode != null)
{
// 小于 200 或者 大于 299 都不是成功值,直接跳过
if (statusCodeResult.StatusCode.Value < 200 || statusCodeResult.StatusCode.Value > 299)
{
// 处理规范化结果
if (!CheckStatusCodeNonUnify(context.HttpContext, out var unifyRes))
{
var httpContext = context.HttpContext;
var statusCode = statusCodeResult.StatusCode.Value;
// 解决刷新 Token 时间和 Token 时间相近问题
if (statusCodeResult.StatusCode.Value == StatusCodes.Status401Unauthorized
&& httpContext.Response.Headers.ContainsKey("access-token")
&& httpContext.Response.Headers.ContainsKey("x-access-token"))
{
httpContext.Response.StatusCode = statusCode = StatusCodes.Status403Forbidden;
}
// 如果 Response 已经完成输出,则禁止写入
if (httpContext.Response.HasStarted) return;
await unifyRes.OnResponseStatusCodes(httpContext, statusCode,
httpContext.RequestServices.GetService<IOptions<UnifyResultSettingsOptions>>()?.Value);
}
return;
}
}
// 如果出现异常,则不会进入该过滤器
if (actionExecutedContext.Exception != null) return;
// 获取控制器信息
var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
// 判断是否支持 MVC 规范化处理,检测配置而已
// if (!UnifyContext.CheckSupportMvcController(context.HttpContext, actionDescriptor, out _)) return;
// 判断是否跳过规范化处理检测NonUnifyAttribute而已
if (CheckSucceededNonUnify(actionDescriptor.MethodInfo))
{
return;
}
IUnifyResultProvider unifyResult = context.GetRequiredService<IUnifyResultProvider>();
// 处理 BadRequestObjectResult 类型规范化处理
if (actionExecutedContext.Result is BadRequestObjectResult badRequestObjectResult)
{
// 解析验证消息
var validationMetadata = GetValidationMetadata(badRequestObjectResult.Value);
var result = unifyResult.OnValidateFailed(context, validationMetadata);
if (result != null) actionExecutedContext.Result = result;
}
else
{
IActionResult result = default;
// 检查是否是有效的结果(可进行规范化的结果)
if (CheckVaildResult(actionExecutedContext.Result, out var data))
{
result = unifyResult.OnSucceeded(actionExecutedContext, data);
}
// 如果是不能规范化的结果类型,则跳过
if (result == null) return;
actionExecutedContext.Result = result;
}
}
/// <summary>
/// 获取验证错误信息
/// </summary>
/// <param name="errors"></param>
/// <returns></returns>
private static ValidationMetadata GetValidationMetadata(object errors)
{
ModelStateDictionary _modelState = null;
object validationResults = null;
(string message, string firstErrorMessage, string firstErrorProperty) = (default, default, default);
// 判断是否是集合类型
if (errors is IEnumerable && errors is not string)
{
// 如果是模型验证字典类型
if (errors is ModelStateDictionary modelState)
{
_modelState = modelState;
// 将验证错误信息转换成字典并序列化成 Json
validationResults = modelState.Where(u => modelState[u.Key].ValidationState == ModelValidationState.Invalid)
.ToDictionary(u => u.Key, u => modelState[u.Key].Errors.Select(c => c.ErrorMessage).ToArray());
}
// 如果是 ValidationProblemDetails 特殊类型
else if (errors is ValidationProblemDetails validation)
{
validationResults = validation.Errors
.ToDictionary(u => u.Key, u => u.Value.ToArray());
}
// 如果是字典类型
else if (errors is Dictionary<string, string[]> dicResults)
{
validationResults = dicResults;
}
message = JsonSerializer.Serialize(validationResults, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
});
firstErrorMessage = (validationResults as Dictionary<string, string[]>).First().Value[0];
firstErrorProperty = (validationResults as Dictionary<string, string[]>).First().Key;
}
// 其他类型
else
{
validationResults = firstErrorMessage = message = errors?.ToString();
}
return new ValidationMetadata
{
ValidationResult = validationResults,
Message = message,
ModelState = _modelState,
FirstErrorProperty = firstErrorProperty,
FirstErrorMessage = firstErrorMessage
};
}
/// <summary>
/// 检查是否是有效的结果(可进行规范化的结果)
/// </summary>
/// <param name="result"></param>
/// <param name="data"></param>
/// <returns></returns>
private bool CheckVaildResult(IActionResult result, out object data)
{
data = default;
// 排除以下结果,跳过规范化处理
var isDataResult = result switch
{
ViewResult => false,
PartialViewResult => false,
FileResult => false,
ChallengeResult => false,
SignInResult => false,
SignOutResult => false,
RedirectToPageResult => false,
RedirectToRouteResult => false,
RedirectResult => false,
RedirectToActionResult => false,
LocalRedirectResult => false,
ForbidResult => false,
ViewComponentResult => false,
PageResult => false,
NotFoundResult => false,
NotFoundObjectResult => false,
_ => true,
};
// 目前支持返回值 ActionResult
if (isDataResult) data = result switch
{
// 处理内容结果
ContentResult content => content.Content,
// 处理对象结果
ObjectResult obj => obj.Value,
// 处理 JSON 对象
JsonResult json => json.Value,
_ => null,
};
return isDataResult;
}
/// <summary>
/// 检查短路状态码(>=400是否进行规范化处理
/// </summary>
/// <param name="context"></param>
/// <param name="unifyResult"></param>
/// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
internal static bool CheckStatusCodeNonUnify(HttpContext context, out IUnifyResultProvider unifyResult)
{
// 获取终点路由特性
var endpointFeature = context.Features.Get<IEndpointFeature>();
if (endpointFeature == null) return (unifyResult = null) == null;
// 判断是否跳过规范化处理
var isSkip = context.GetEndpoint()?.Metadata?.GetMetadata<NonUnifyAttribute>()!= null
|| endpointFeature?.Endpoint?.Metadata?.GetMetadata<NonUnifyAttribute>() != null
|| context.Request.Headers["accept"].ToString().Contains("odata.metadata=", StringComparison.OrdinalIgnoreCase)
|| context.Request.Headers["accept"].ToString().Contains("odata.streaming=", StringComparison.OrdinalIgnoreCase);
if (isSkip == true) unifyResult = null;
else
{
unifyResult = context.RequestServices.GetRequiredService<IUnifyResultProvider>();
}
return unifyResult == null || isSkip;
}
/// <summary>
/// 检查请求成功是否进行规范化处理
/// </summary>
/// <param name="method"></param>
/// <param name="isWebRequest"></param>
/// <returns>返回 true 跳过处理,否则进行规范化处理</returns>
private bool CheckSucceededNonUnify(MethodInfo method, bool isWebRequest = true)
{
// 判断是否跳过规范化处理
var isSkip = method.CustomAttributes.Any(x => typeof(NonUnifyAttribute).IsAssignableFrom(x.AttributeType) || typeof(ProducesResponseTypeAttribute).IsAssignableFrom(x.AttributeType) || typeof(IApiResponseMetadataProvider).IsAssignableFrom(x.AttributeType))
|| method.ReflectedType.IsDefined(typeof(NonUnifyAttribute), true)
|| method.DeclaringType.Assembly.GetName().Name.StartsWith("Microsoft.AspNetCore.OData");
if (!isWebRequest)
{
return isSkip;
}
return isSkip;
}
}

View File

@@ -0,0 +1,58 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 规范化结果提供器
/// </summary>
public interface IUnifyResultProvider
{
/// <summary>
/// 异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata);
/// <summary>
/// 成功返回值
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
IActionResult OnSucceeded(ActionExecutedContext context, object data);
/// <summary>
/// 验证失败返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata);
/// <summary>
/// 拦截返回状态码
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
/// <returns></returns>
Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings = default);
}

View File

@@ -0,0 +1,23 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 禁止规范化处理
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class NonUnifyAttribute : Attribute
{
}

View File

@@ -0,0 +1,136 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Volo.Abp.DependencyInjection;
namespace Yi.Framework.AspNetCore.UnifyResult.Providers;
/// <summary>
/// RESTful 风格返回值
/// </summary>
[Dependency(TryRegister = true)]
[ExposeServices(typeof(IUnifyResultProvider))]
public class RESTfulResultProvider : IUnifyResultProvider,ITransientDependency
{
/// <summary>
/// 设置响应状态码
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
public static void SetResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
{
if (unifyResultSettings == null) return;
// 篡改响应状态码
if (unifyResultSettings.AdaptStatusCodes != null && unifyResultSettings.AdaptStatusCodes.Length > 0)
{
var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode);
if (adaptStatusCode != null && adaptStatusCode.Length > 0 && adaptStatusCode[0] > 0)
{
context.Response.StatusCode = adaptStatusCode[1];
return;
}
}
// 如果为 null则所有请求错误的状态码设置为 200
if (unifyResultSettings.Return200StatusCodes == null) context.Response.StatusCode = 200;
// 否则只有里面的才设置为 200
else if (unifyResultSettings.Return200StatusCodes.Contains(statusCode)) context.Response.StatusCode = 200;
else { }
}
/// <summary>
/// 异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors));
}
/// <summary>
/// 成功返回值
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
public IActionResult OnSucceeded(ActionExecutedContext context, object data)
{
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
}
/// <summary>
/// 验证失败/业务异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult));
}
/// <summary>
/// 特定状态码返回值
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
/// <returns></returns>
public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
{
// 设置响应状态码
SetResponseStatusCodes(context, statusCode, unifyResultSettings);
switch (statusCode)
{
// 处理 401 状态码
case StatusCodes.Status401Unauthorized:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 Unauthorized"));
break;
// 处理 403 状态码
case StatusCodes.Status403Forbidden:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 Forbidden"));
break;
default: break;
}
}
/// <summary>
/// 返回 RESTful 风格结果集
/// </summary>
/// <param name="statusCode"></param>
/// <param name="succeeded"></param>
/// <param name="data"></param>
/// <param name="errors"></param>
/// <returns></returns>
public static RESTfulResult<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default)
{
return new RESTfulResult<object>
{
StatusCode = statusCode,
Succeeded = succeeded,
Data = data,
Errors = errors,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
}
}

View File

@@ -0,0 +1,52 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// RESTful 风格结果集
/// </summary>
/// <typeparam name="T"></typeparam>
public class RESTfulResult<T>
{
/// <summary>
/// 状态码
/// </summary>
public int? StatusCode { get; set; }
/// <summary>
/// 数据
/// </summary>
public T Data { get; set; }
/// <summary>
/// 执行成功
/// </summary>
public bool Succeeded { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public object Errors { get; set; }
/// <summary>
/// 附加数据
/// </summary>
public object Extras { get; set; }
/// <summary>
/// 时间戳
/// </summary>
public long Timestamp { get; set; }
}

View File

@@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.SwaggerGen;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Yi.Framework.AspNetCore.UnifyResult.Fiters;
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 规范化接口
/// 由于太多人反应想兼容一套类似furion的返回情况200状态码包一层更符合国内习惯既然如此不如直接搬过来
/// </summary>
public static class UnifyResultExtensions
{
public static IServiceCollection AddFurionUnifyResultApi(this IServiceCollection services)
{
//成功规范接口
services.AddTransient<SucceededUnifyResultFilter>();
//异常规范接口
services.AddTransient<FriendlyExceptionFilter>();
services.AddMvc(options =>
{
options.Filters.AddService<SucceededUnifyResultFilter>(99);
options.Filters.AddService<FriendlyExceptionFilter>(100);
options.Filters.RemoveAll(x => (x as ServiceFilterAttribute)?.ServiceType == typeof(AbpExceptionFilter));
});
return services;
}
}

View File

@@ -0,0 +1,50 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.Extensions.Configuration;
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 规范化配置选项
/// </summary>
public sealed class UnifyResultSettingsOptions
{
/// <summary>
/// 设置返回 200 状态码列表
/// <para>默认401403如果设置为 null则标识所有状态码都返回 200 </para>
/// </summary>
public int[] Return200StatusCodes { get; set; }
/// <summary>
/// 适配篡改Http 状态码(只支持短路状态码,比如 401403500 等)
/// </summary>
public int[][] AdaptStatusCodes { get; set; }
/// <summary>
/// 是否支持 MVC 控制台规范化处理
/// </summary>
public bool? SupportMvcController { get; set; }
/// <summary>
/// 选项后期配置
/// </summary>
/// <param name="options"></param>
/// <param name="configuration"></param>
public void PostConfigure(UnifyResultSettingsOptions options, IConfiguration configuration)
{
options.Return200StatusCodes ??= new[] { 401, 403 };
options.SupportMvcController ??= false;
}
}

View File

@@ -0,0 +1,69 @@
// MIT 许可证
//
// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Yi.Framework.AspNetCore.UnifyResult;
/// <summary>
/// 验证信息元数据
/// </summary>
public sealed class ValidationMetadata
{
/// <summary>
/// 验证结果
/// </summary>
/// <remarks>返回字典或字符串类型</remarks>
public object ValidationResult { get; internal set; }
/// <summary>
/// 异常消息
/// </summary>
public string Message { get; internal set; }
/// <summary>
/// 验证状态
/// </summary>
public ModelStateDictionary ModelState { get; internal set; }
/// <summary>
/// 错误码
/// </summary>
public object ErrorCode { get; internal set; }
/// <summary>
/// 错误码(没被复写过的 ErrorCode
/// </summary>
public object OriginErrorCode { get; internal set; }
/// <summary>
/// 状态码
/// </summary>
public int? StatusCode { get; internal set; }
/// <summary>
/// 首个错误属性
/// </summary>
public string FirstErrorProperty { get; internal set; }
/// <summary>
/// 首个错误消息
/// </summary>
public string FirstErrorMessage { get; internal set; }
/// <summary>
/// 额外数据
/// </summary>
public object Data { get; internal set; }
}

View File

@@ -1,4 +1,4 @@
using System.Text;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
@@ -74,9 +74,12 @@ namespace Yi.Framework.Core.Extensions
result = "127.0.0.1";
result = result.Replace("::ffff:", "127.0.0.1");
//如果有端口号,删除端口号
result = Regex.Replace(result, @":\d{1,5}$", "");
//Ip规则校验
var regResult = Regex.IsMatch(result, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
var regResult =
Regex.IsMatch(result, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$")
|| Regex.IsMatch(result, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?):\d{1,5}$");
result = regResult ? result : "127.0.0.1";
return result;
@@ -96,5 +99,15 @@ namespace Yi.Framework.Core.Extensions
{
return context.User.Claims.Where(x => x.Type == permissionsName).Select(x => x.Value).ToArray();
}
/// <summary>
/// 判断是否是 WebSocket 请求
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static bool IsWebSocketRequest(this HttpContext context)
{
return context.WebSockets.IsWebSocketRequest || context.Request.Path == "/ws";
}
}
}

View File

@@ -78,12 +78,25 @@ namespace Yi.Framework.Core.Helper
return 0;
}
}
/// <summary>
/// CPU使用情况
/// </summary>
/// <returns></returns>
public static CPUMetrics GetCPUMetrics()
{
CPUMetrics cpuMetrics = new CPUMetrics();
var cpudetail = GetCPUDetails();
cpuMetrics.CoreTotal = cpudetail.Cores;
cpuMetrics.LogicalProcessors =cpudetail.LogicalProcessors;
cpuMetrics.CPURate = Math.Ceiling(ParseToDouble(GetCPURate()));
cpuMetrics.FreeRate = 1 - cpuMetrics.CPURate;
return cpuMetrics;
}
/// <summary>
/// 内存使用情况
/// </summary>
/// <returns></returns>
public static MemoryMetrics GetComputerInfo()
public static MemoryMetrics GetMemoryMetrics()
{
try
{
@@ -94,7 +107,7 @@ namespace Yi.Framework.Core.Helper
memoryMetrics.UsedRam = Math.Round(memoryMetrics.Used / 1024, 2) + "GB";
memoryMetrics.TotalRAM = Math.Round(memoryMetrics.Total / 1024, 2) + "GB";
memoryMetrics.RAMRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total).ToString() + "%";
memoryMetrics.CPURate = Math.Ceiling(ParseToDouble(GetCPURate())) + "%";
return memoryMetrics;
}
catch (Exception ex)
@@ -105,7 +118,7 @@ namespace Yi.Framework.Core.Helper
}
/// <summary>
/// 获取内存大小
/// 获取磁盘信息
/// </summary>
/// <returns></returns>
public static List<DiskInfo> GetDiskInfos()
@@ -174,7 +187,7 @@ namespace Yi.Framework.Core.Helper
var isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
return isUnix;
}
public static string GetCPURate()
{
string cpuRate;
@@ -221,8 +234,69 @@ namespace Yi.Framework.Core.Helper
}
return runTime;
}
}
public static CPUInfo GetCPUDetails()
{
int logicalProcessors = 0;
int cores = 0;
if (IsUnix())
{
string logicalOutput = ShellHelper.Bash("lscpu | grep '^CPU(s):' | awk '{print $2}'");
logicalProcessors = int.Parse(logicalOutput.Trim());
string coresOutput = ShellHelper.Bash("lscpu | grep 'Core(s) per socket:' | awk '{print $4}'");
string socketsOutput = ShellHelper.Bash("lscpu | grep 'Socket(s):' | awk '{print $2}'");
cores = int.Parse(coresOutput.Trim()) * int.Parse(socketsOutput.Trim());
}
else
{
string output = ShellHelper.Cmd("wmic", "cpu get NumberOfCores,NumberOfLogicalProcessors /format:csv");
var lines = output.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length > 1)
{
var values = lines[1].Split(',');
cores = int.Parse(values[1].Trim());
logicalProcessors =int.Parse(values[2].Trim());
}
}
return new CPUInfo
{
LogicalProcessors = logicalProcessors,
Cores = cores
};
}
}
public class CPUInfo
{
public int LogicalProcessors { get; set; }
public int Cores { get; set; }
}
public class CPUMetrics
{
/// <summary>
/// 内核数
/// </summary>
public int CoreTotal { get; set; }
/// <summary>
/// 逻辑处理器数
/// </summary>
public int LogicalProcessors { get; set; }
/// <summary>
/// CPU使用率%
/// </summary>
public double CPURate { get; set; }
/// <summary>
/// CPU空闲率%
/// </summary>
public double FreeRate { get; set; }
}
/// <summary>
/// 内存信息
/// </summary>
@@ -236,10 +310,7 @@ namespace Yi.Framework.Core.Helper
public double Free { get; set; }
public string UsedRam { get; set; }
/// <summary>
/// CPU使用率%
/// </summary>
public string CPURate { get; set; }
/// <summary>
/// 总内存 GB
/// </summary>
@@ -306,20 +377,25 @@ namespace Yi.Framework.Core.Helper
/// <returns></returns>
public MemoryMetrics GetUnixMetrics()
{
string output = ShellHelper.Bash("free -m | awk '{print $2,$3,$4,$5,$6}'");
string output = ShellHelper.Bash(@"
# 从 /proc/meminfo 文件中提取总内存
total_mem=$(cat /proc/meminfo | grep -i ""MemTotal"" | awk '{print $2}')
# 从 /proc/meminfo 文件中提取剩余内存
free_mem=$(cat /proc/meminfo | grep -i ""MemFree"" | awk '{print $2}')
# 显示提取的信息
echo $total_mem $used_mem $free_mem
");
var metrics = new MemoryMetrics();
var lines = output.Split('\n', (char)StringSplitOptions.RemoveEmptyEntries);
if (lines.Length <= 0) return metrics;
if (lines != null && lines.Length > 0)
if (!string.IsNullOrWhiteSpace(output))
{
var memory = lines[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
if (memory.Length >= 3)
var memory = output.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
if (memory.Length >= 2)
{
metrics.Total = double.Parse(memory[0]);
metrics.Used = double.Parse(memory[1]);
metrics.Free = double.Parse(memory[2]);//m
metrics.Total = Math.Round(double.Parse(memory[0]) / 1024, 0);
metrics.Free = Math.Round(double.Parse(memory[1])/ 1024, 0);//m
metrics.Used = metrics.Total - metrics.Free;
}
}
return metrics;

View File

@@ -13,5 +13,39 @@ namespace Yi.Framework.Ddd.Application.Contracts
/// 查询结束时间条件
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 排序列名,字段名对应前端
/// </summary>
public string? OrderByColumn { get; set; }
/// <summary>
/// 是否顺序,字段名对应前端
/// </summary>
public string? IsAsc { get; set; }
/// <summary>
/// 是否顺序
/// </summary>
public bool CanAsc => IsAsc?.ToLower() == "ascending" ? true : false;
private string _sorting;
//排序引用
public new string? Sorting
{
get
{
if (!OrderByColumn.IsNullOrWhiteSpace())
{
return $"{OrderByColumn} {(CanAsc ? "ASC" : "DESC")}";
}
else
{
return _sorting;
}
}
set => _sorting = value;
}
}
}
}

View File

@@ -8,9 +8,11 @@ using Volo.Abp.Domain.Repositories;
namespace Yi.Framework.Ddd.Application
{
public abstract class YiCrudAppService<TEntity, TEntityDto, TKey> : YiCrudAppService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
public abstract class
YiCrudAppService<TEntity, TEntityDto, TKey> : YiCrudAppService<TEntity, TEntityDto, TKey,
PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected YiCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
@@ -49,16 +51,53 @@ namespace Yi.Framework.Ddd.Application
}
public abstract class YiCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
public abstract class YiCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput,
TUpdateInput>
: CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
where TEntity : class, IEntity<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
protected YiCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
{
}
public override async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
{
await CheckUpdatePolicyAsync();
var entity = await GetEntityByIdAsync(id);
await CheckUpdateInputDtoAsync(entity,input);
await MapToEntityAsync(input, entity);
await Repository.UpdateAsync(entity, autoSave: true);
return await MapToGetOutputDtoAsync(entity);
}
protected virtual Task CheckUpdateInputDtoAsync(TEntity entity,TUpdateInput input)
{
return Task.CompletedTask;
}
public override async Task<TGetOutputDto> CreateAsync(TCreateInput input)
{
await CheckCreatePolicyAsync();
await CheckCreateInputDtoAsync(input);
var entity = await MapToEntityAsync(input);
TryToSetTenantId(entity);
await Repository.InsertAsync(entity, autoSave: true);
return await MapToGetOutputDtoAsync(entity);
}
protected virtual Task CheckCreateInputDtoAsync(TCreateInput input)
{
return Task.CompletedTask;
}
/// <summary>
/// 多查
/// </summary>
@@ -70,12 +109,14 @@ namespace Yi.Framework.Ddd.Application
//区分多查还是批量查
if (input is IPagedResultRequest pagedInput)
{
entites = await Repository.GetPagedListAsync(pagedInput.SkipCount, pagedInput.MaxResultCount, string.Empty);
entites = await Repository.GetPagedListAsync(pagedInput.SkipCount, pagedInput.MaxResultCount,
string.Empty);
}
else
{
entites = await Repository.GetListAsync();
}
var total = await Repository.GetCountAsync();
var output = await MapToGetListOutputDtosAsync(entites);
return new PagedResultDto<TGetListOutputDto>(total, output);
@@ -146,4 +187,4 @@ namespace Yi.Framework.Ddd.Application
//await Repository.InsertManyAsync(entities);
}
}
}
}

View File

@@ -19,7 +19,10 @@ namespace Yi.Framework.SqlSugarCore.Abstractions
/// </summary>
public bool EnabledDbSeed { get; set; } = false;
/// <summary>
/// 开启驼峰转下划线
/// </summary>
public bool EnableUnderLine { get; set; } = false;
/// <summary>
/// 开启codefirst

View File

@@ -1,8 +1,12 @@
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Microsoft.Extensions.Logging;
using Nito.AsyncEx;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
@@ -13,9 +17,9 @@ namespace Yi.Framework.SqlSugarCore.Repositories
{
public class SqlSugarRepository<TEntity> : ISqlSugarRepository<TEntity>, IRepository<TEntity> where TEntity : class, IEntity, new()
{
public ISqlSugarClient _Db => GetDbContextAsync().Result;
public ISqlSugarClient _Db => AsyncContext.Run(async () => await GetDbContextAsync());
public ISugarQueryable<TEntity> _DbQueryable => GetDbContextAsync().Result.Queryable<TEntity>();
public ISugarQueryable<TEntity> _DbQueryable => _Db.Queryable<TEntity>();
private ISugarDbContextProvider<ISqlSugarDbContext> _sugarDbContextProvider;
public IAsyncQueryableExecuter AsyncExecuter { get; }
@@ -33,9 +37,7 @@ namespace Yi.Framework.SqlSugarCore.Repositories
/// <returns></returns>
public virtual async Task<ISqlSugarClient> GetDbContextAsync()
{
var db = (await _sugarDbContextProvider.GetDbContextAsync()).SqlSugarClient;
//await Console.Out.WriteLineAsync("获取的id" + db.ContextID);
return db;
}
@@ -243,7 +245,7 @@ namespace Yi.Framework.SqlSugarCore.Repositories
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
return await (await GetDbSimpleClientAsync()).AsUpdateable().SetColumns(nameof(ISoftDelete), true).Where(whereExpression).ExecuteCommandAsync() > 0;
return await (await GetDbSimpleClientAsync()).AsUpdateable().SetColumns(nameof(ISoftDelete.IsDeleted), true).Where(whereExpression).ExecuteCommandAsync() > 0;
}
else
{
@@ -372,6 +374,20 @@ namespace Yi.Framework.SqlSugarCore.Repositories
public virtual async Task<bool> UpdateAsync(TEntity updateObj)
{
if (typeof(TEntity).IsAssignableTo<IHasConcurrencyStamp>())//带版本号乐观锁更新
{
try
{
int num = await (await GetDbSimpleClientAsync())
.Context.Updateable(updateObj).ExecuteCommandWithOptLockAsync(true);
return num>0;
}
catch (VersionExceptions ex)
{
throw new AbpDbConcurrencyException($"{ex.Message}[更新失败ConcurrencyStamp不是最新版本],entityInfo{updateObj}", ex);
}
}
return await (await GetDbSimpleClientAsync()).UpdateAsync(updateObj);
}

View File

@@ -64,6 +64,12 @@ namespace Yi.Framework.SqlSugarCore
//设置codefirst非空值判断
ConfigureExternalServices = new ConfigureExternalServices
{
// 处理表
EntityNameService = (type, entity) =>
{
if (dbConnOptions.EnableUnderLine && !entity.DbTableName.Contains('_'))
entity.DbTableName = UtilMethods.ToUnderLine(entity.DbTableName);// 驼峰转下划线
},
EntityService = (c, p) =>
{
if (new NullabilityInfoContext()
@@ -72,6 +78,9 @@ namespace Yi.Framework.SqlSugarCore
p.IsNullable = true;
}
if (dbConnOptions.EnableUnderLine && !p.IsIgnore && !p.DbColumnName.Contains('_'))
p.DbColumnName = UtilMethods.ToUnderLine(p.DbColumnName);// 驼峰转下划线
EntityService(c, p);
}
},

View File

@@ -1,13 +1,10 @@
using System;
using System.Collections;
using System.Collections;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
@@ -17,6 +14,7 @@ using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
using Yi.Framework.SqlSugarCore.Abstractions;
using Check = Volo.Abp.Check;
namespace Yi.Framework.SqlSugarCore
{
@@ -26,22 +24,23 @@ namespace Yi.Framework.SqlSugarCore
/// SqlSugar 客户端
/// </summary>
public ISqlSugarClient SqlSugarClient { get; private set; }
public ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
protected ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
private IAbpLazyServiceProvider LazyServiceProvider { get; }
private IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
protected ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
private ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
private ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
public IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
protected IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
public IEntityChangeEventHelper EntityChangeEventHelper => LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
private IEntityChangeEventHelper EntityChangeEventHelper => LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
public AbpDbConnectionOptions ConnectionOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDbConnectionOptions>>().Value;
private ISqlSugarDbConnectionCreator _dbConnectionCreator;
private ISerializeService SerializeService=> LazyServiceProvider.LazyGetRequiredService<ISerializeService>();
public void SetSqlSugarClient(ISqlSugarClient sqlSugarClient)
{
SqlSugarClient = sqlSugarClient;
@@ -50,7 +49,6 @@ namespace Yi.Framework.SqlSugarCore
{
LazyServiceProvider = lazyServiceProvider;
var connectionCreator = LazyServiceProvider.LazyGetRequiredService<ISqlSugarDbConnectionCreator>();
_dbConnectionCreator = connectionCreator;
connectionCreator.OnSqlSugarClientConfig = OnSqlSugarClientConfig;
connectionCreator.EntityService = EntityService;
connectionCreator.DataExecuting = DataExecuting;
@@ -62,7 +60,10 @@ namespace Yi.Framework.SqlSugarCore
options.ConnectionString = GetCurrentConnectionString();
options.DbType = GetCurrentDbType();
}));
//统一使用aop处理
connectionCreator.SetDbAop(SqlSugarClient);
//替换默认序列化器
SqlSugarClient.CurrentConnectionConfig.ConfigureExternalServices.SerializeService = SerializeService;
}
/// <summary>
@@ -71,23 +72,12 @@ namespace Yi.Framework.SqlSugarCore
/// <returns></returns>
protected virtual string GetCurrentConnectionString()
{
var defautlUrl = Options.Url ?? ConnectionOptions.GetConnectionStringOrNull(ConnectionStrings.DefaultConnectionStringName);
//如果未开启多租户返回db url 或者 默认连接字符串
if (!Options.EnabledSaasMultiTenancy)
{
return defautlUrl;
}
//开启了多租户
var connectionStringResolver = LazyServiceProvider.LazyGetRequiredService<IConnectionStringResolver>();
var connectionString = connectionStringResolver.ResolveAsync().GetAwaiter().GetResult();
var connectionString = connectionStringResolver.ResolveAsync().ConfigureAwait(false).GetAwaiter().GetResult();
//没有检测到使用多租户功能,默认使用默认库即可
if (string.IsNullOrWhiteSpace(connectionString))
{
Volo.Abp.Check.NotNull(Options.Url, "租户默认库Defalut未找到");
connectionString = defautlUrl;
Check.NotNull(Options.Url, "dbUrl未配置");
}
return connectionString!;
}
@@ -102,7 +92,7 @@ namespace Yi.Framework.SqlSugarCore
return dbTypeFromTenantName.Value;
}
}
Volo.Abp.Check.NotNull(Options.DbType, "默认DbType未配置");
Check.NotNull(Options.DbType, "默认DbType未配置");
return Options.DbType!.Value;
}
@@ -150,9 +140,9 @@ namespace Yi.Framework.SqlSugarCore
}
if (IsMultiTenantFilterEnabled)
{
//表达式不能放方法
Guid? tenantId = CurrentTenant?.Id;
sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == tenantId);
//表达式里只能有具体值,不能运算
var expressionCurrentTenant = CurrentTenant.Id ?? null;
sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == expressionCurrentTenant);
}
CustomDataFilter(sqlSugarClient);
}
@@ -205,23 +195,22 @@ namespace Yi.Framework.SqlSugarCore
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreationTime)))
{
//为空或者为默认最小值
if (oldValue is null || DateTime.MinValue.Equals(oldValue))
if (DateTime.MinValue.Equals(oldValue))
{
entityInfo.SetValue(DateTime.Now);
}
}
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreatorId)))
{
if (CurrentUser.Id != null)
if (CurrentUser.Id is not null)
{
entityInfo.SetValue(CurrentUser.Id);
}
}
//插入时需要租户id,先预留
if (entityInfo.PropertyName.Equals(nameof(IMultiTenant.TenantId)))
{
if (CurrentTenant is not null)
if (CurrentTenant.Id is not null)
{
entityInfo.SetValue(CurrentTenant.Id);
}
@@ -315,9 +304,11 @@ namespace Yi.Framework.SqlSugarCore
/// <param name="column"></param>
protected virtual void EntityService(PropertyInfo property, EntityColumnInfo column)
{
if (property.Name == "ConcurrencyStamp")
if (property.Name == nameof(IHasConcurrencyStamp.ConcurrencyStamp)) //带版本号并发更新
{
column.IsIgnore = true;
// column.IsOnlyIgnoreInsert = true;
// column.IsOnlyIgnoreUpdate = true;
column.IsEnableUpdateVersionValidation = true;
}
if (property.PropertyType == typeof(ExtraPropertyDictionary))
{

View File

@@ -0,0 +1,76 @@
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using SqlSugar;
namespace Yi.Framework.SqlSugarCore;
public class NonPublicPropertiesResolver : DefaultContractResolver
{
/// <summary>
/// 重写获取属性存在get set方法就可以写入
/// </summary>
/// <param name="member"></param>
/// <param name="memberSerialization"></param>
/// <returns></returns>
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
if (member is PropertyInfo pi)
{
prop.Readable = (pi.GetMethod != null);
prop.Writable = (pi.SetMethod != null);
}
return prop;
}
}
public class SqlSugarNonPublicSerializer : ISerializeService
{
/// <summary>
/// 默认的序列化服务
/// </summary>
private readonly ISerializeService _serializeService = DefaultServices.Serialize;
public string SerializeObject(object value)
{
//保留原有实现
return _serializeService.SerializeObject(value);
}
public string SugarSerializeObject(object value)
{ //保留原有实现
return _serializeService.SugarSerializeObject(value);
}
/// <summary>
/// 重写对象反序列化支持NoPublic访问器
/// </summary>
/// <param name="value"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T DeserializeObject<T>(string value)
{
if (typeof(T).FullName.StartsWith("System.Text.Json."))
{
// 动态创建一个 JsonSerializer 实例
Type serializerType =typeof(T).Assembly.GetType("System.Text.Json.JsonSerializer");
var methods = serializerType
.GetMethods().Where(it=>it.Name== "Deserialize")
.Where(it=>it.GetParameters().Any(z=>z.ParameterType==typeof(string))).First();
// 调用 SerializeObject 方法序列化对象
T json = (T)methods.MakeGenericMethod(typeof(T))
.Invoke(null, new object[] { value, null });
return json;
}
var jSetting = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver =new NonPublicPropertiesResolver() //替换默认解析器使能支持protect
};
return JsonConvert.DeserializeObject<T>(value, jSetting);
}
}

View File

@@ -14,7 +14,6 @@ namespace Yi.Framework.SqlSugarCore.Uow
public ISqlSugarDbContext GetDbContext()
{
return _sqlsugarDbContext;
}

View File

@@ -48,7 +48,7 @@ namespace Yi.Framework.SqlSugarCore.Uow
//获取当前连接字符串,未多租户时,默认为空
var connectionString = await ResolveConnectionStringAsync(connectionStringName);
var dbContextKey = $"{this.GetType().FullName}_{connectionString}";
var dbContextKey = $"{this.GetType().Name}_{connectionString}";
var unitOfWork = UnitOfWorkManager.Current;

View File

@@ -25,7 +25,8 @@ namespace Yi.Framework.SqlSugarCore
{
var service = context.Services;
var configuration = service.GetConfiguration();
Configure<DbConnOptions>(configuration.GetSection("DbConnOptions"));
var section = configuration.GetSection("DbConnOptions");
Configure<DbConnOptions>(section);
service.TryAddScoped<ISqlSugarDbContext, SqlSugarDbContext>();
@@ -39,7 +40,12 @@ namespace Yi.Framework.SqlSugarCore
service.AddTransient(typeof(ISqlSugarRepository<,>), typeof(SqlSugarRepository<,>));
service.AddTransient(typeof(ISugarDbContextProvider<>), typeof(UnitOfWorkSqlsugarDbContextProvider<>));
//替换Sqlsugar默认序列化器用来解决.Select()不支持嵌套对象/匿名对象的非公有访问器 值无法绑定,如Id属性
context.Services.AddSingleton<ISerializeService, SqlSugarNonPublicSerializer>();
var dbConfig = section.Get<DbConnOptions>();
//将默认db传递给abp连接字符串模块
Configure<AbpDbConnectionOptions>(x => { x.ConnectionStrings.Default = dbConfig.Url; });
return Task.CompletedTask;
}
@@ -50,8 +56,8 @@ namespace Yi.Framework.SqlSugarCore
//进行CodeFirst
var service = context.ServiceProvider;
var options = service.GetRequiredService<IOptions<DbConnOptions>>().Value;
var _logger= service.GetRequiredService<ILogger<YiFrameworkSqlSugarCoreModule>>();
var logger = service.GetRequiredService<ILogger<YiFrameworkSqlSugarCoreModule>>();
StringBuilder sb = new StringBuilder();
@@ -65,13 +71,14 @@ namespace Yi.Framework.SqlSugarCore
sb.AppendLine("===============================");
_logger.LogInformation(sb.ToString());
logger.LogInformation(sb.ToString());
//Todo准备支持多租户种子数据及CodeFirst
if (options.EnabledCodeFirst)
{
CodeFirst(service);
}
if (options.EnabledDbSeed)
{
await DataSeedAsync(service);
@@ -80,7 +87,6 @@ namespace Yi.Framework.SqlSugarCore
private void CodeFirst(IServiceProvider service)
{
var moduleContainer = service.GetRequiredService<IModuleContainer>();
var db = service.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient;
@@ -95,11 +101,11 @@ namespace Yi.Framework.SqlSugarCore
.Where(x => x.GetCustomAttribute<SugarTable>() != null)
.Where(x => x.GetCustomAttribute<SplitTableAttribute>() is null));
}
if (types.Count > 0)
{
db.CopyNew().CodeFirst.InitTables(types.ToArray());
}
}
private async Task DataSeedAsync(IServiceProvider service)
@@ -108,4 +114,4 @@ namespace Yi.Framework.SqlSugarCore
await dataSeeder.SeedAsync();
}
}
}
}

View File

@@ -9,11 +9,11 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
public class DiscussGetListOutputDto : EntityDto<Guid>
{
/// <summary>
/// 是否禁止评论创建功能
/// 是否禁止评论创建功能
/// </summary>
public bool IsDisableCreateComment { get; set; }
/// <summary>
/// 是否已点赞,默认未登录不点赞
/// 是否已点赞,默认未登录不点赞
/// </summary>
public bool IsAgree { get; set; } = false;
public string Title { get; set; }
@@ -23,26 +23,26 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
public int AgreeNum { get; set; }
public int SeeNum { get; set; }
//批量查询,不给内容,性能考虑
//批量查询,不给内容,性能考虑
//public string Content { get; set; }
public string? Color { get; set; }
public Guid PlateId { get; set; }
//是否置顶默认false
//是否置顶默认false
public bool IsTop { get; set; }
public DiscussPermissionTypeEnum PermissionType { get; set; }
//是否禁止默认false
//是否禁止默认false
public bool IsBan { get; set; }
/// <summary>
/// 封面
/// 封面
/// </summary>
public string? Cover { get; set; }
//私有需要判断code权限
//私有需要判断code权限
public string? PrivateCode { get; set; }
public DateTime CreationTime { get; set; }
@@ -55,7 +55,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
Title = DiscussConst.Privacy;
Introduction = "";
Cover = null;
//被禁止
//被禁止
IsBan = true;
}
}
@@ -73,14 +73,14 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
case DiscussPermissionTypeEnum.Public:
break;
case DiscussPermissionTypeEnum.Oneself:
//当前主题是仅自己可见,同时不是当前登录用户
//当前主题是仅自己可见,同时不是当前登录用户
if (dto.User.Id != userId)
{
dto.SetBan();
}
break;
case DiscussPermissionTypeEnum.User:
//当前主题为部分可见,同时不是当前登录用户 也 不在可见用户列表中
//当前主题为部分可见,同时不是当前登录用户 也 不在可见用户列表中
if (dto.User.Id != userId && !dto.PermissionUserIds.Contains(userId))
{
dto.SetBan();

View File

@@ -17,9 +17,11 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
public class BbsForumAnalyseService : ApplicationService, IApplicationService
{
private ForumManager _forumManager;
public BbsForumAnalyseService(ForumManager forumManager)
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
public BbsForumAnalyseService(ForumManager forumManager, ISqlSugarRepository<AgreeEntity> agreeRepository)
{
_forumManager = forumManager;
_agreeRepository = agreeRepository;
}
/// <summary>
@@ -38,7 +40,7 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
.Select((discuss, user, info) => new DiscussGetListOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
// IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
@@ -52,6 +54,26 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
}, true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount);
var discussId = output.Select(x => x.Id);
//点赞字典key为主题idy为用户ids
var agreeDic =
(await _agreeRepository._DbQueryable.Where(x => discussId.Contains(x.DiscussId)).ToListAsync())
.GroupBy(x => x.DiscussId)
.ToDictionary(x => x.Key, y => y.Select(y => y.CreatorId).ToList());
//等级、是否点赞赋值
output?.ForEach(x =>
{
if (CurrentUser.Id is not null)
{
//默认fasle
if (agreeDic.TryGetValue(x.Id,out var userIds))
{
x.IsAgree = userIds.Contains(CurrentUser.Id);
}
}
});
return output;
}

View File

@@ -29,19 +29,27 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// <summary>
/// Discuss应用服务实现,用于参数校验、领域服务业务组合、日志记录、事务处理、账户信息
/// </summary>
public class DiscussService : YiCrudAppService<DiscussAggregateRoot, DiscussGetOutputDto, DiscussGetListOutputDto, Guid, DiscussGetListInputVo, DiscussCreateInputVo, DiscussUpdateInputVo>,
IDiscussService
public class DiscussService : YiCrudAppService<DiscussAggregateRoot, DiscussGetOutputDto, DiscussGetListOutputDto,
Guid, DiscussGetListInputVo, DiscussCreateInputVo, DiscussUpdateInputVo>,
IDiscussService
{
private ISqlSugarRepository<DiscussTopEntity> _discussTopEntityRepository;
private ISqlSugarRepository<DiscussTopEntity> _discussTopRepository;
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
private BbsUserManager _bbsUserManager;
public DiscussService(BbsUserManager bbsUserManager, ForumManager forumManager, ISqlSugarRepository<DiscussTopEntity> discussTopEntityRepository, ISqlSugarRepository<PlateAggregateRoot> plateEntityRepository, ILocalEventBus localEventBus) : base(forumManager._discussRepository)
public DiscussService(BbsUserManager bbsUserManager, ForumManager forumManager,
ISqlSugarRepository<DiscussTopEntity> discussTopRepository,
ISqlSugarRepository<PlateAggregateRoot> plateEntityRepository, ILocalEventBus localEventBus,
ISqlSugarRepository<AgreeEntity> agreeRepository) : base(forumManager._discussRepository)
{
_forumManager = forumManager;
_plateEntityRepository = plateEntityRepository;
_localEventBus = localEventBus;
_discussTopEntityRepository = discussTopEntityRepository;
_bbsUserManager=bbsUserManager;
_agreeRepository = agreeRepository;
_discussTopRepository = discussTopRepository;
_bbsUserManager = bbsUserManager;
}
private readonly ILocalEventBus _localEventBus;
private ForumManager _forumManager { get; set; }
@@ -49,8 +57,6 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
private ISqlSugarRepository<PlateAggregateRoot> _plateEntityRepository { get; set; }
/// <summary>
/// 单查
/// </summary>
@@ -58,42 +64,43 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// <returns></returns>
public async override Task<DiscussGetOutputDto> GetAsync(Guid id)
{
//查询主题发布 浏览主题 事件,浏览数+1
var item = await _forumManager._discussRepository._DbQueryable.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
var item = await _forumManager._discussRepository._DbQueryable
.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
.LeftJoin<PlateAggregateRoot>((discuss, user, info, plate) => plate.Id == discuss.PlateId)
.Select((discuss, user, info, plate) => new DiscussGetOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
UserName = user.UserName,
Nick = user.Nick,
Icon = user.Icon,
Id = user.Id,
Level = info.Level,
UserLimit = info.UserLimit,
Money=info.Money,
Experience=info.Experience
},
Plate = new Contracts.Dtos.Plate.PlateGetOutputDto()
{
Name = plate.Name,
Id = plate.Id,
Code = plate.Code,
Introduction = plate.Introduction,
Logo = plate.Logo
}
}, true)
.SingleAsync(discuss => discuss.Id == id);
.Select((discuss, user, info, plate) => new DiscussGetOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null,
x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
UserName = user.UserName,
Nick = user.Nick,
Icon = user.Icon,
Id = user.Id,
Level = info.Level,
UserLimit = info.UserLimit,
Money = info.Money,
Experience = info.Experience
},
Plate = new Contracts.Dtos.Plate.PlateGetOutputDto()
{
Name = plate.Name,
Id = plate.Id,
Code = plate.Code,
Introduction = plate.Introduction,
Logo = plate.Logo
}
}, true)
.SingleAsync(discuss => discuss.Id == id);
if (item is not null)
{
await VerifyDiscussPermissionAsync(item.Id);
await _localEventBus.PublishAsync(new SeeDiscussEventArgs { DiscussId = item.Id, OldSeeNum = item.SeeNum });
await _localEventBus.PublishAsync(new SeeDiscussEventArgs
{ DiscussId = item.Id, OldSeeNum = item.SeeNum });
}
return item;
@@ -105,49 +112,65 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<PagedResultDto<DiscussGetListOutputDto>> GetListAsync([FromQuery] DiscussGetListInputVo input)
public override async Task<PagedResultDto<DiscussGetListOutputDto>> GetListAsync(
[FromQuery] DiscussGetListInputVo input)
{
//需要关联创建者用户
RefAsync<int> total = 0;
var items = await _forumManager._discussRepository._DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.Title), x => x.Title.Contains(input.Title))
.WhereIF(input.PlateId is not null, x => x.PlateId == input.PlateId)
.WhereIF(input.IsTop is not null, x => x.IsTop == input.IsTop)
.WhereIF(input.UserId is not null,x=>x.CreatorId==input.UserId)
.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
.WhereIF(input.UserName is not null, (discuss, user)=>user.UserName==input.UserName!)
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
.OrderByDescending(discuss => discuss.OrderNum)
.OrderByIF(input.Type == QueryDiscussTypeEnum.New, discuss => discuss.CreationTime, OrderByType.Desc)
.OrderByIF(input.Type == QueryDiscussTypeEnum.Host, discuss => discuss.SeeNum, OrderByType.Desc)
.OrderByIF(input.Type == QueryDiscussTypeEnum.Suggest, discuss => discuss.AgreeNum, OrderByType.Desc)
.Select((discuss, user, info) => new DiscussGetListOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
Id = user.Id,
UserName = user.UserName,
Nick = user.Nick,
Icon = user.Icon,
Level = info.Level,
UserLimit = info.UserLimit,
Money = info.Money,
Experience = info.Experience
}
}, true)
.WhereIF(!string.IsNullOrEmpty(input.Title), x => x.Title.Contains(input.Title))
.WhereIF(input.PlateId is not null, x => x.PlateId == input.PlateId)
.WhereIF(input.IsTop is not null, x => x.IsTop == input.IsTop)
.WhereIF(input.UserId is not null, x => x.CreatorId == input.UserId)
.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
.WhereIF(input.UserName is not null, (discuss, user) => user.UserName == input.UserName!)
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
.OrderByDescending(discuss => discuss.OrderNum)
.OrderByIF(input.Type == QueryDiscussTypeEnum.New, discuss => discuss.CreationTime, OrderByType.Desc)
.OrderByIF(input.Type == QueryDiscussTypeEnum.Host, discuss => discuss.SeeNum, OrderByType.Desc)
.OrderByIF(input.Type == QueryDiscussTypeEnum.Suggest, discuss => discuss.AgreeNum, OrderByType.Desc)
.Select((discuss, user, info) => new DiscussGetListOutputDto
{
Id = discuss.Id,
// 优化查询,不使用子查询
// IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
Id = user.Id,
UserName = user.UserName,
Nick = user.Nick,
Icon = user.Icon,
Level = info.Level,
UserLimit = info.UserLimit,
Money = info.Money,
Experience = info.Experience
}
}, true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var discussId = items.Select(x => x.Id);
//点赞字典key为主题idy为用户ids
var agreeDic =
(await _agreeRepository._DbQueryable.Where(x => discussId.Contains(x.DiscussId)).ToListAsync())
.GroupBy(x => x.DiscussId)
.ToDictionary(x => x.Key, y => y.Select(y => y.CreatorId).ToList());
//查询完主题之后,要过滤一下私有的主题信息
items.ApplyPermissionTypeFilter(CurrentUser.Id ?? Guid.Empty);
items?.ForEach(x => x.User.LevelName = _bbsUserManager._levelCacheDic[x.User.Level].Name);
//等级、是否点赞赋值
items?.ForEach(x =>
{
x.User.LevelName = _bbsUserManager._levelCacheDic[x.User.Level].Name;
if (CurrentUser.Id is not null)
{
//默认fasle
if (agreeDic.TryGetValue(x.Id,out var userIds))
{
x.IsAgree = userIds.Contains(CurrentUser.Id);
}
}
});
return new PagedResultDto<DiscussGetListOutputDto>(total, items);
}
@@ -157,14 +180,16 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// <returns></returns>
public async Task<List<DiscussGetListOutputDto>> GetListTopAsync()
{
var output = await _discussTopEntityRepository._DbQueryable.LeftJoin<DiscussAggregateRoot>((top, discuss) => top.DiscussId == discuss.Id)
var output = await _discussTopRepository._DbQueryable
.LeftJoin<DiscussAggregateRoot>((top, discuss) => top.DiscussId == discuss.Id)
.LeftJoin<UserAggregateRoot>((top, discuss, user) => discuss.CreatorId == user.Id)
.LeftJoin<BbsUserExtraInfoEntity>((top, discuss, user, info) => user.Id == info.UserId)
.OrderByDescending(top => top.OrderNum)
.Select((top, discuss, user, info) => new DiscussGetListOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null,
x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto
{
Id = user.Id,
@@ -206,6 +231,11 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
throw new UserFriendlyException(PlateConst.No_Exist);
}
if (await _forumManager._discussRepository.IsAnyAsync(x => x.Title == input.Title))
{
throw new UserFriendlyException(DiscussConst.Repeat);
}
//如果开启了禁用创建主题
if (plate.IsDisableCreateDiscuss == true)
{
@@ -233,6 +263,7 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
{
throw new UserFriendlyException(DiscussConst.No_Exist);
}
if (discuss.PermissionType == DiscussPermissionTypeEnum.Oneself)
{
if (discuss.CreatorId != CurrentUser.Id)
@@ -240,13 +271,15 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
throw new UserFriendlyException(DiscussConst.Privacy);
}
}
if (discuss.PermissionType == DiscussPermissionTypeEnum.User)
{
if (discuss.CreatorId != CurrentUser.Id && !discuss.PermissionUserIds.Contains(CurrentUser.Id ?? Guid.Empty))
if (discuss.CreatorId != CurrentUser.Id &&
!discuss.PermissionUserIds.Contains(CurrentUser.Id ?? Guid.Empty))
{
throw new UserFriendlyException(DiscussConst.Privacy);
}
}
}
}
}
}

View File

@@ -11,6 +11,7 @@ namespace Yi.Framework.Bbs.Domain.Shared.Consts
/// </summary>
public class DiscussConst
{
public const string Repeat = "创建主题重复";
public const string No_Exist = "传入的主题id不存在";
public const string Privacy = "【私密】您无该主题权限,可联系作者申请开放";

View File

@@ -5,24 +5,25 @@ public enum AssignmentRequirementTypeEnum
/// <summary>
/// 主题
/// </summary>
Discuss=2,
Discuss = 2,
/// <summary>
/// 评论
/// </summary>
Comment=4,
Comment = 4,
/// <summary>
/// 点赞
/// </summary>
Agree=8,
/// <summary>
/// 更新个人中心
/// </summary>
UpdateProfile=16
Agree = 8,
/// <summary>
/// 更新昵称
/// </summary>
UpdateNick = 16,
/// <summary>
/// 更新头像
/// </summary>
UpdateIcon = 32,
}

View File

@@ -9,6 +9,7 @@ namespace Yi.Framework.Bbs.Domain.Entities.Forum
[SugarTable("Discuss")]
[SugarIndex($"index_{nameof(Title)}", nameof(Title), OrderByType.Asc)]
[SugarIndex($"index_{nameof(PlateId)}", nameof(PlateId), OrderByType.Asc)]
[SugarIndex($"index_{nameof(CreatorId)}", nameof(CreatorId), OrderByType.Asc)]
[SugarIndex($"index_{nameof(CreationTime)}", nameof(CreationTime), OrderByType.Desc)]
public class DiscussAggregateRoot : AggregateRoot<Guid>, ISoftDelete, IAuditedObject
{

View File

@@ -39,6 +39,7 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
//查询主题的信息
var discussAndAgreeDto = await _agreeRepository._DbQueryable
.Where(agree=>agree.Id==agreeEntity.Id)
.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) =>
new

View File

@@ -33,21 +33,27 @@ public class AssignmentEventHandler : ILocalEventHandler<AssignmentEventArgs>, I
case AssignmentRequirementTypeEnum.Discuss:
SetCurrentStepNumber(AssignmentRequirementTypeEnum.Discuss, currentAssignmentList);
break;
//发表评论
case AssignmentRequirementTypeEnum.Comment:
SetCurrentStepNumber(AssignmentRequirementTypeEnum.Comment, currentAssignmentList);
break;
//点赞
case AssignmentRequirementTypeEnum.Agree:
SetCurrentStepNumber(AssignmentRequirementTypeEnum.Agree, currentAssignmentList);
break;
//更新个人信息
case AssignmentRequirementTypeEnum.UpdateProfile:
//这里还需判断是否更新了
//更新昵称
case AssignmentRequirementTypeEnum.UpdateNick:
SetCurrentStepNumber(AssignmentRequirementTypeEnum.UpdateNick, currentAssignmentList);
break;
//更新头像
case AssignmentRequirementTypeEnum.UpdateIcon:
SetCurrentStepNumber(AssignmentRequirementTypeEnum.UpdateIcon, currentAssignmentList);
break;
default:
throw new ArgumentOutOfRangeException();
}
@@ -72,7 +78,7 @@ public class AssignmentEventHandler : ILocalEventHandler<AssignmentEventArgs>, I
x.CurrentStepNumber < x.TotalStepNumber)
{
x.CurrentStepNumber += 1;
if (x.CurrentStepNumber==x.TotalStepNumber)
if (x.CurrentStepNumber == x.TotalStepNumber)
{
x.AssignmentState = AssignmentStateEnum.Completed;
}

View File

@@ -1,16 +1,70 @@
using Yi.Framework.Bbs.Domain.Entities.Assignment;
using SqlSugar;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Bbs.Domain.Entities.Assignment;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders;
/// <summary>
/// 新手任务提供者
/// </summary>
public class NoviceProvider : IAssignmentProvider
[ExposeServices(typeof(IAssignmentProvider))]
public class NoviceProvider : IAssignmentProvider
{
public async Task<List<AssignmentDefineAggregateRoot>> GetCanReceiveListAsync(AssignmentContext context)
{
List<AssignmentDefineAggregateRoot> output = new List<AssignmentDefineAggregateRoot>();
//新手任务是要有前置依赖关系的,链表类型依赖
throw new NotImplementedException();
//先获取到对应任务定义列表,新手任务
var assignmentDefines = context.AllAssignmentDefine.Where(x => x.AssignmentType == AssignmentTypeEnum.Novice)
.ToList();
var assignmentDefineIds = assignmentDefines.Select(x => x.Id).ToList();
//根路径
var rootAssignmentDefine = assignmentDefines.Where(x => x.PreAssignmentId == null).OrderBy(x=>x.OrderNum).FirstOrDefault();
//代表没有定义新手任务
if (rootAssignmentDefine is null)
{
return output;
}
//1查询该用户有正在进行的新手任务如果有跳过
if (context.CurrentUserAssignments
.Where(x => assignmentDefineIds.Contains(x.AssignmentDefineId))
.Any(x => x.AssignmentState == AssignmentStateEnum.Progress))
{
return output;
}
//2: 查询该用户是否有完成的新手任务,如果没有,直接返回根节点,如果有,则根据链表选择最后的节点
var assignmentFilterIds = context.CurrentUserAssignments
.Where(x => assignmentDefineIds.Contains(x.AssignmentDefineId))
.Where(x =>
//已经完成的
x.AssignmentState == AssignmentStateEnum.End||
x.AssignmentState==AssignmentStateEnum.Completed
)
.Select(x => x.AssignmentDefineId)
.ToList();
if (assignmentFilterIds.Count == 0)
{
output.Add(rootAssignmentDefine);
return output;
}
//该用户接受的最后一个新手任务
var lastAssignment = assignmentDefines.Where(x => assignmentFilterIds.Contains(x.Id))
.OrderByDescending(x => x.OrderNum).First();
//包含比该用户还要大的任务
if (assignmentDefines.Any(x => x.OrderNum > lastAssignment.OrderNum))
{
output.Add(assignmentDefines.FirstOrDefault(x => x.OrderNum == lastAssignment.OrderNum + 1));
return output;
}
return output;
}
}

View File

@@ -8,12 +8,12 @@
/// <summary>
/// 账号
/// </summary>
public string UserName { get; set; } = string.Empty;
public string UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; } = string.Empty;
public string Password { get; set; }
/// <summary>
/// 唯一标识码
@@ -29,5 +29,11 @@
/// 验证码
/// </summary>
public string? Code { get; set; }
/// <summary>
/// 昵称
/// </summary>
public string? Nick{ get; set; }
}
}

View File

@@ -0,0 +1,24 @@
namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Account;
public class RetrievePasswordDto
{
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
/// <summary>
/// 唯一标识码
/// </summary>
public string? Uuid { get; set; }
/// <summary>
/// 电话
/// </summary>
public long Phone { get; set; }
/// <summary>
/// 验证码
/// </summary>
public string? Code { get; set; }
}

View File

@@ -3,5 +3,6 @@
public class UpdateIconDto
{
public string? Icon { get; set; }
public Guid? UserId { get; set; }
}
}

View File

@@ -4,17 +4,17 @@ using Yi.Framework.Ddd.Application.Contracts;
namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Config
{
/// <summary>
/// 配置查询参数
/// 配置查询参数
/// </summary>
public class ConfigGetListInputVo : PagedAllResultRequestDto
{
/// <summary>
/// 配置名称
/// 配置名称
/// </summary>
public string? ConfigName { get; set; }
/// <summary>
/// 配置键
/// 配置键
/// </summary>
public string? ConfigKey { get; set; }

View File

@@ -5,14 +5,11 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Dept
/// </summary>
public class DeptCreateInputVo
{
public Guid Id { get; set; }
public DateTime CreationTime { get; set; } = DateTime.Now;
public Guid? CreatorId { get; set; }
public bool State { get; set; }
public string DeptName { get; set; } = string.Empty;
public string DeptCode { get; set; } = string.Empty;
public string DeptName { get; set; }
public string DeptCode { get; set; }
public string? Leader { get; set; }
public Guid ParentId { get; set; }
public Guid? ParentId { get; set; }=Guid.Empty;
public string? Remark { get; set; }
}
}

View File

@@ -2,14 +2,11 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Dept
{
public class DeptUpdateInputVo
{
public Guid Id { get; set; }
public DateTime CreationTime { get; set; } = DateTime.Now;
public Guid? CreatorId { get; set; }
public bool State { get; set; }
public string DeptName { get; set; } = string.Empty;
public string DeptCode { get; set; } = string.Empty;
public string? Leader { get; set; }
public Guid ParentId { get; set; }
public Guid? ParentId { get; set; }=Guid.Empty;
public string? Remark { get; set; }
}
}

View File

@@ -7,7 +7,7 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Menu
/// </summary>
public class MenuCreateInputVo
{
public Guid Id { get; set; }
public Guid? Id { get; set; }
public DateTime CreationTime { get; set; } = DateTime.Now;
public Guid? CreatorId { get; set; }
public bool State { get; set; }
@@ -24,5 +24,7 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Menu
public string? Component { get; set; }
public string? Query { get; set; }
public int OrderNum { get; set; }
public MenuSourceEnum MenuSource { get; set; } = MenuSourceEnum.Ruoyi;
public string? RouterName { get; set; }
}
}

View File

@@ -1,4 +1,5 @@
using Volo.Abp.Application.Dtos;
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Menu
{
@@ -7,6 +8,6 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Menu
public bool? State { get; set; }
public string? MenuName { get; set; }
public MenuSourceEnum MenuSource { get; set; } = MenuSourceEnum.Ruoyi;
}
}

View File

@@ -20,7 +20,6 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Menu
public string? Component { get; set; }
public string? Query { get; set; }
public int OrderNum { get; set; }
//public List<MenuEntity>? Children { get; set; }
public string? RouterName { get; set; }
}
}

View File

@@ -5,12 +5,9 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Post
/// </summary>
public class PostCreateInputVo
{
public Guid Id { get; set; }
public DateTime CreationTime { get; set; } = DateTime.Now;
public long? CreatorId { get; set; }
public bool? State { get; set; }
public string PostCode { get; set; } = string.Empty;
public string PostName { get; set; } = string.Empty;
public string PostCode { get; set; }
public string PostName { get; set; }
public string? Remark { get; set; }
}
}

View File

@@ -2,12 +2,9 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Post
{
public class PostUpdateInputVo
{
public Guid Id { get; set; }
public DateTime CreationTime { get; set; } = DateTime.Now;
public Guid? CreatorId { get; set; }
public bool? State { get; set; }
public string PostCode { get; set; } = string.Empty;
public string PostName { get; set; } = string.Empty;
public string PostCode { get; set; }
public string PostName { get; set; }
public string? Remark { get; set; }
}
}

View File

@@ -3,6 +3,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\framework\Yi.Framework.Ddd.Application.Contracts\Yi.Framework.Ddd.Application.Contracts.csproj" />
<ProjectReference Include="..\..\bbs\Yi.Framework.Bbs.Domain.Shared\Yi.Framework.Bbs.Domain.Shared.csproj" />
<ProjectReference Include="..\Yi.Framework.Rbac.Domain.Shared\Yi.Framework.Rbac.Domain.Shared.csproj" />
</ItemGroup>

View File

@@ -1,18 +1,24 @@
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
using Lazy.Captcha.Core;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization;
using Volo.Abp.Caching;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Guids;
using Volo.Abp.Uow;
using Volo.Abp.Users;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Account;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Entities;
@@ -21,14 +27,16 @@ using Yi.Framework.Rbac.Domain.Repositories;
using Yi.Framework.Rbac.Domain.Shared.Caches;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.Rbac.Domain.Shared.Dtos;
using Yi.Framework.Rbac.Domain.Shared.Enums;
using Yi.Framework.Rbac.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Shared.Options;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services
{
public class AccountService : ApplicationService, IAccountService
{
protected ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService<ILocalEventBus>();
private IDistributedCache<CaptchaPhoneCacheItem, CaptchaPhoneCacheKey> _phoneCache;
private readonly ICaptcha _captcha;
private readonly IGuidGenerator _guidGenerator;
@@ -36,6 +44,8 @@ namespace Yi.Framework.Rbac.Application.Services
private readonly IAliyunManger _aliyunManger;
private IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> _userCache;
private UserManager _userManager;
private IHttpContextAccessor _httpContextAccessor;
public AccountService(IUserRepository userRepository,
ICurrentUser currentUser,
IAccountManager accountManager,
@@ -46,7 +56,7 @@ namespace Yi.Framework.Rbac.Application.Services
IGuidGenerator guidGenerator,
IOptions<RbacOptions> options,
IAliyunManger aliyunManger,
UserManager userManager)
UserManager userManager, IHttpContextAccessor httpContextAccessor)
{
_userRepository = userRepository;
_currentUser = currentUser;
@@ -59,6 +69,7 @@ namespace Yi.Framework.Rbac.Application.Services
_aliyunManger = aliyunManger;
_userCache = userCache;
_userManager = userManager;
_httpContextAccessor = httpContextAccessor;
}
@@ -66,6 +77,7 @@ namespace Yi.Framework.Rbac.Application.Services
private ICurrentUser _currentUser;
private IAccountManager _accountManager;
private ISqlSugarRepository<MenuAggregateRoot> _menuRepository;
/// <summary>
/// 校验图片登录验证码,无需和账号绑定
/// </summary>
@@ -83,7 +95,6 @@ namespace Yi.Framework.Rbac.Application.Services
}
/// <summary>
/// 登录
/// </summary>
@@ -104,10 +115,21 @@ namespace Yi.Framework.Rbac.Application.Services
//校验
await _accountManager.LoginValidationAsync(input.UserName, input.Password, x => user = x);
var userInfo = new UserRoleMenuDto();
//获取token
var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id);
var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id, (info) => userInfo = info);
var refreshToken = _accountManager.CreateRefreshToken(user.Id);
//这里抛出一个登录的事件,也可以在全部流程走完,在应用层组装
if (_httpContextAccessor.HttpContext is not null)
{
var loginEntity = new LoginLogAggregateRoot().GetInfoByHttpContext(_httpContextAccessor.HttpContext);
var loginEto = loginEntity.Adapt<LoginEventArgs>();
loginEto.UserName = userInfo.User.UserName;
loginEto.UserId = userInfo.User.Id;
await LocalEventBus.PublishAsync(loginEto);
}
return new { Token = accessToken, RefreshToken = refreshToken };
}
@@ -130,7 +152,6 @@ namespace Yi.Framework.Rbac.Application.Services
/// 生成验证码
/// </summary>
/// <returns></returns>
[AllowAnonymous]
public async Task<CaptchaImageDto> GetCaptchaImageAsync()
{
@@ -143,37 +164,62 @@ namespace Yi.Framework.Rbac.Application.Services
/// 验证电话号码
/// </summary>
/// <param name="str_handset"></param>
private async Task ValidationPhone(string str_handset)
private async Task ValidationPhone(string phone)
{
var res = Regex.IsMatch(str_handset, @"^\d{11}$");
var res = Regex.IsMatch(phone, @"^\d{11}$");
if (res == false)
{
throw new UserFriendlyException("手机号码格式错误!请检查");
}
if (await _userRepository.IsAnyAsync(x => x.Phone.ToString() == str_handset))
{
throw new UserFriendlyException("该手机号已被注册!");
}
}
/// <summary>
/// 注册 手机验证码
/// 手机验证码-注册
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("account/captcha-phone")]
[AllowAnonymous]
public async Task<object> PostCaptchaPhoneForRegisterAsync(PhoneCaptchaImageDto input)
{
return await PostCaptchaPhoneAsync(ValidationPhoneTypeEnum.Register, input);
}
/// <summary>
/// 手机验证码-找回密码
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("account/captcha-phone/repassword")]
public async Task<object> PostCaptchaPhoneForRetrievePasswordAsync(PhoneCaptchaImageDto input)
{
return await PostCaptchaPhoneAsync(ValidationPhoneTypeEnum.RetrievePassword, input);
}
/// <summary>
/// 手机验证码
/// </summary>
/// <returns></returns>
[AllowAnonymous]
public async Task<object> PostCaptchaPhone(PhoneCaptchaImageDto input)
private async Task<object> PostCaptchaPhoneAsync(ValidationPhoneTypeEnum validationPhoneType,
PhoneCaptchaImageDto input)
{
await ValidationPhone(input.Phone);
var value = await _phoneCache.GetAsync(new CaptchaPhoneCacheKey(input.Phone));
//注册的手机号验证,是不能已经注册过的
if (validationPhoneType == ValidationPhoneTypeEnum.Register&& await _userRepository.IsAnyAsync(x => x.Phone.ToString() == input.Phone))
{
throw new UserFriendlyException("该手机号已被注册!");
}
var value = await _phoneCache.GetAsync(new CaptchaPhoneCacheKey(validationPhoneType, input.Phone));
//防止暴刷
if (value is not null)
{
throw new UserFriendlyException($"{input.Phone}已发送过验证码10分钟后可重试");
}
//生成一个4位数的验证码
//发送短信同时生成uuid
////key 电话号码 value:验证码+uuid
@@ -181,7 +227,9 @@ namespace Yi.Framework.Rbac.Application.Services
var uuid = Guid.NewGuid();
await _aliyunManger.SendSmsAsync(input.Phone, code);
await _phoneCache.SetAsync(new CaptchaPhoneCacheKey(input.Phone), new CaptchaPhoneCacheItem(code), new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(10) });
await _phoneCache.SetAsync(new CaptchaPhoneCacheKey(validationPhoneType, input.Phone),
new CaptchaPhoneCacheItem(code),
new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(10) });
return new
{
Uuid = uuid
@@ -191,18 +239,43 @@ namespace Yi.Framework.Rbac.Application.Services
/// <summary>
/// 校验电话验证码,需要与电话号码绑定
/// </summary>
private async Task ValidationPhoneCaptchaAsync(RegisterDto input)
private async Task ValidationPhoneCaptchaAsync(ValidationPhoneTypeEnum validationPhoneType, long phone,
string code)
{
var item = await _phoneCache.GetAsync(new CaptchaPhoneCacheKey(input.Phone.ToString()));
if (item is not null && item.Code.Equals($"{input.Code}"))
var item = await _phoneCache.GetAsync(new CaptchaPhoneCacheKey(validationPhoneType, phone.ToString()));
if (item is not null && item.Code.Equals($"{code}"))
{
//成功,需要清空
await _phoneCache.RemoveAsync(new CaptchaPhoneCacheKey(input.Phone.ToString()));
await _phoneCache.RemoveAsync(new CaptchaPhoneCacheKey(validationPhoneType, code.ToString()));
return;
}
throw new UserFriendlyException("验证码错误");
}
/// <summary>
/// 找回密码
/// </summary>
/// <param name="input"></param>
[AllowAnonymous]
[UnitOfWork]
public async Task<string> PostRetrievePasswordAsync(RetrievePasswordDto input)
{
//校验验证码,根据电话号码获取 value比对验证码已经uuid
await ValidationPhoneCaptchaAsync(ValidationPhoneTypeEnum.RetrievePassword, input.Phone, input.Code);
var entity = await _userRepository.GetFirstAsync(x => x.Phone == input.Phone);
if (entity is null)
{
throw new UserFriendlyException("该手机号码未注册");
}
await _accountManager.RestPasswordAsync(entity.Id, input.Password);
return entity.UserName;
}
/// <summary>
/// 注册,需要验证码通过
/// </summary>
@@ -216,23 +289,24 @@ namespace Yi.Framework.Rbac.Application.Services
{
throw new UserFriendlyException("该系统暂未开放注册功能");
}
if (_rbacOptions.EnableCaptcha)
{
//校验验证码,根据电话号码获取 value比对验证码已经uuid
await ValidationPhoneCaptchaAsync(input);
await ValidationPhoneCaptchaAsync(ValidationPhoneTypeEnum.Register, input.Phone, input.Code);
}
//注册领域逻辑
await _accountManager.RegisterAsync(input.UserName, input.Password, input.Phone);
await _accountManager.RegisterAsync(input.UserName, input.Password, input.Phone, input.Nick);
}
/// <summary>
/// 查询已登录的账户信息,已缓存
/// 查询已登录的账户信息
/// </summary>
/// <returns></returns>
[Route("account")]
[Authorize]
public async Task<UserRoleMenuDto> GetAsync()
{
//通过鉴权jwt获取到用户的id
@@ -241,6 +315,7 @@ namespace Yi.Framework.Rbac.Application.Services
{
throw new UserFriendlyException("用户未登录");
}
//此处优先从缓存中获取
var output = await _userManager.GetInfoAsync(userId.Value);
return output;
@@ -249,18 +324,19 @@ namespace Yi.Framework.Rbac.Application.Services
/// <summary>
/// 获取当前登录用户的前端路由
/// 支持ruoyi/pure
/// </summary>
/// <returns></returns>
[Authorize]
[Route("account/Vue3Router")]
public async Task<List<Vue3RouterDto>> GetVue3Router()
[Route("account/Vue3Router/{routerType?}")]
public async Task<object> GetVue3Router([FromRoute] string? routerType)
{
var userId = _currentUser.Id;
if (_currentUser.Id is null)
{
throw new AbpAuthorizationException("用户未登录");
}
var data = await _userManager.GetInfoAsync(userId!.Value);
var menus = data.Menus.ToList();
@@ -269,9 +345,22 @@ namespace Yi.Framework.Rbac.Application.Services
{
menus = ObjectMapper.Map<List<MenuAggregateRoot>, List<MenuDto>>(await _menuRepository.GetListAsync());
}
//将后端菜单转换成前端路由,组件级别需要过滤
List<Vue3RouterDto> routers = ObjectMapper.Map<List<MenuDto>, List<MenuAggregateRoot>>(menus).Vue3RouterBuild();
return routers;
object output = null;
if (routerType is null || routerType == "ruoyi")
{
//将后端菜单转换成前端路由,组件级别需要过滤
output =
ObjectMapper.Map<List<MenuDto>, List<MenuAggregateRoot>>(menus).Vue3RuoYiRouterBuild();
}
else if (routerType == "pure")
{
//将后端菜单转换成前端路由,组件级别需要过滤
output =
ObjectMapper.Map<List<MenuDto>, List<MenuAggregateRoot>>(menus).Vue3PureRouterBuild();
}
return output;
}
/// <summary>
@@ -287,6 +376,7 @@ namespace Yi.Framework.Rbac.Application.Services
return false;
// throw new UserFriendlyException("用户已退出");
}
await _userCache.RemoveAsync(new UserInfoCacheKey(userId.Value));
//Jwt去中心化登出只需用记录日志即可
return true;
@@ -303,11 +393,14 @@ namespace Yi.Framework.Rbac.Application.Services
{
throw new UserFriendlyException("无效更新!输入的数据,新密码不能与老密码相同");
}
if (_currentUser.Id is null)
{
throw new UserFriendlyException("用户未登录");
}
await _accountManager.UpdatePasswordAsync(_currentUser.Id ?? Guid.Empty, input.NewPassword, input.OldPassword);
await _accountManager.UpdatePasswordAsync(_currentUser.Id ?? Guid.Empty, input.NewPassword,
input.OldPassword);
return true;
}
@@ -324,6 +417,7 @@ namespace Yi.Framework.Rbac.Application.Services
{
throw new UserFriendlyException("重置密码不能为空!");
}
await _accountManager.RestPasswordAsync(userId, input.Password);
return true;
}
@@ -335,11 +429,22 @@ namespace Yi.Framework.Rbac.Application.Services
/// <returns></returns>
public async Task<bool> UpdateIconAsync(UpdateIconDto input)
{
var entity = await _userRepository.GetByIdAsync(_currentUser.Id);
Guid userId=input.UserId == null?_currentUser.GetId():input.UserId.Value;
var entity = await _userRepository.GetByIdAsync(userId);
if (entity.Icon == input.Icon)
{
return false;
}
entity.Icon = input.Icon;
await _userRepository.UpdateAsync(entity);
//发布更新头像任务事件
await this.LocalEventBus.PublishAsync(
new AssignmentEventArgs(AssignmentRequirementTypeEnum.UpdateIcon, userId), false);
return true;
}
}
}
}

View File

@@ -5,6 +5,7 @@ using Yi.Framework.Ddd.Application;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Config;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services
@@ -12,10 +13,12 @@ namespace Yi.Framework.Rbac.Application.Services
/// <summary>
/// Config服务实现
/// </summary>
public class ConfigService : YiCrudAppService<ConfigAggregateRoot, ConfigGetOutputDto, ConfigGetListOutputDto, Guid, ConfigGetListInputVo, ConfigCreateInputVo, ConfigUpdateInputVo>,
IConfigService
public class ConfigService : YiCrudAppService<ConfigAggregateRoot, ConfigGetOutputDto, ConfigGetListOutputDto, Guid,
ConfigGetListInputVo, ConfigCreateInputVo, ConfigUpdateInputVo>,
IConfigService
{
private ISqlSugarRepository<ConfigAggregateRoot, Guid> _repository;
public ConfigService(ISqlSugarRepository<ConfigAggregateRoot, Guid> repository) : base(repository)
{
_repository = repository;
@@ -30,11 +33,33 @@ namespace Yi.Framework.Rbac.Application.Services
{
RefAsync<int> total = 0;
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.ConfigKey), x => x.ConfigKey.Contains(input.ConfigKey!))
.WhereIF(!string.IsNullOrEmpty(input.ConfigName), x => x.ConfigName!.Contains(input.ConfigName!))
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.ConfigKey),
x => x.ConfigKey.Contains(input.ConfigKey!))
.WhereIF(!string.IsNullOrEmpty(input.ConfigName), x => x.ConfigName!.Contains(input.ConfigName!))
.WhereIF(input.StartTime is not null && input.EndTime is not null,
x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<ConfigGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}
protected override async Task CheckCreateInputDtoAsync(ConfigCreateInputVo input)
{
var isExist =
await _repository.IsAnyAsync(x => x.ConfigKey == input.ConfigKey);
if (isExist)
{
throw new UserFriendlyException(ConfigConst.Exist);
}
}
protected override async Task CheckUpdateInputDtoAsync(ConfigAggregateRoot entity, ConfigUpdateInputVo input)
{
var isExist = await _repository._DbQueryable.Where(x => x.Id != entity.Id)
.AnyAsync(x => x.ConfigKey == input.ConfigKey);
if (isExist)
{
throw new UserFriendlyException(ConfigConst.Exist);
}
}
}
}
}

View File

@@ -6,6 +6,7 @@ using Yi.Framework.Ddd.Application;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Dictionary;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.SqlSugarCore.Abstractions;
@@ -14,26 +15,30 @@ namespace Yi.Framework.Rbac.Application.Services
/// <summary>
/// Dictionary服务实现
/// </summary>
public class DictionaryService : YiCrudAppService<DictionaryEntity, DictionaryGetOutputDto, DictionaryGetListOutputDto, Guid, DictionaryGetListInputVo, DictionaryCreateInputVo, DictionaryUpdateInputVo>,
IDictionaryService
public class DictionaryService : YiCrudAppService<DictionaryEntity, DictionaryGetOutputDto,
DictionaryGetListOutputDto, Guid, DictionaryGetListInputVo, DictionaryCreateInputVo,
DictionaryUpdateInputVo>,
IDictionaryService
{
private ISqlSugarRepository<DictionaryEntity, Guid> _repository;
public DictionaryService(ISqlSugarRepository<DictionaryEntity, Guid> repository) : base(repository)
{
_repository= repository;
_repository = repository;
}
/// <summary>
/// 查询
/// </summary>
public override async Task<PagedResultDto<DictionaryGetListOutputDto>> GetListAsync(DictionaryGetListInputVo input)
public override async Task<PagedResultDto<DictionaryGetListOutputDto>> GetListAsync(
DictionaryGetListInputVo input)
{
RefAsync<int> total = 0;
var entities = await _repository._DbQueryable.WhereIF(input.DictType is not null, x => x.DictType == input.DictType)
.WhereIF(input.DictLabel is not null, x => x.DictLabel!.Contains(input.DictLabel!))
.WhereIF(input.State is not null, x => x.State == input.State)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var entities = await _repository._DbQueryable
.WhereIF(input.DictType is not null, x => x.DictType == input.DictType)
.WhereIF(input.DictLabel is not null, x => x.DictLabel!.Contains(input.DictLabel!))
.WhereIF(input.State is not null, x => x.State == input.State)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<DictionaryGetListOutputDto>
{
TotalCount = total,
@@ -55,4 +60,4 @@ namespace Yi.Framework.Rbac.Application.Services
return result;
}
}
}
}

View File

@@ -6,6 +6,7 @@ using Yi.Framework.Ddd.Application;
using Yi.Framework.Rbac.Application.Contracts.Dtos.DictionaryType;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services
@@ -22,7 +23,7 @@ namespace Yi.Framework.Rbac.Application.Services
_repository = repository;
}
public async override Task<PagedResultDto<DictionaryTypeGetListOutputDto>> GetListAsync(DictionaryTypeGetListInputVo input)
public override async Task<PagedResultDto<DictionaryTypeGetListOutputDto>> GetListAsync(DictionaryTypeGetListInputVo input)
{
RefAsync<int> total = 0;
@@ -38,5 +39,25 @@ namespace Yi.Framework.Rbac.Application.Services
Items = await MapToGetListOutputDtosAsync(entities)
};
}
protected override async Task CheckCreateInputDtoAsync(DictionaryTypeCreateInputVo input)
{
var isExist =
await _repository.IsAnyAsync(x => x.DictType == input.DictType);
if (isExist)
{
throw new UserFriendlyException(DictionaryConst.Exist);
}
}
protected override async Task CheckUpdateInputDtoAsync(DictionaryTypeAggregateRoot entity, DictionaryTypeUpdateInputVo input)
{
var isExist = await _repository._DbQueryable.Where(x => x.Id != entity.Id)
.AnyAsync(x => x.DictType == input.DictType);
if (isExist)
{
throw new UserFriendlyException(DictionaryConst.Exist);
}
}
}
}

View File

@@ -39,7 +39,8 @@ namespace Yi.Framework.Rbac.Application.Services
if (!File.Exists(path))
{
throw new UserFriendlyException("文件不存在",code:"404");
return new NotFoundResult();
// throw new UserFriendlyException("文件不存在",code:"404");
}
@@ -66,12 +67,6 @@ namespace Yi.Framework.Rbac.Application.Services
// path = $"wwwroot/{FileTypeEnum.Thumbnail}/{file.Id}{Path.GetExtension(file.FileName)}";
//}
//路径为: 文件路径/文件id+文件扩展名
if (!File.Exists(path))
{
throw new UserFriendlyException("本地文件不存在", "404");
}
return path;
}

View File

@@ -21,7 +21,7 @@ namespace Yi.Framework.Rbac.Application.Services.Monitor
[HttpGet("monitor-server/info")]
public object GetInfo()
{
int cpuNum = Environment.ProcessorCount;
string computerName = Environment.MachineName;
string osName = RuntimeInformation.OSDescription;
string osArch = RuntimeInformation.OSArchitecture.ToString();
@@ -35,9 +35,10 @@ namespace Yi.Framework.Rbac.Application.Services.Monitor
string programRunTime = DateTimeHelper.FormatTime(long.Parse((DateTime.Now - programStartTime).TotalMilliseconds.ToString().Split('.')[0]));
var data = new
{
cpu = ComputerHelper.GetComputerInfo(),
memory = ComputerHelper.GetMemoryMetrics(),
cpu = ComputerHelper.GetCPUMetrics(),
disk = ComputerHelper.GetDiskInfos(),
sys = new { cpuNum, computerName, osName, osArch, serverIP, runTime = sysRunTime },
sys = new {computerName, osName, osArch, serverIP, runTime = sysRunTime },
app = new
{
name = _hostEnvironment.EnvironmentName,

View File

@@ -41,8 +41,6 @@ namespace Yi.Framework.Rbac.Application.Services
return new PagedResultDto<NoticeGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}
/// <summary>
/// 发送在线消息
/// </summary>

View File

@@ -20,10 +20,12 @@ namespace Yi.Framework.Rbac.Application.Services.RecordLog
public override async Task<PagedResultDto<LoginLogGetListOutputDto>> GetListAsync(LoginLogGetListInputVo input)
{
RefAsync<int> total = 0;
if (input.Sorting.IsNullOrWhiteSpace())
input.Sorting = $"{nameof(LoginLogAggregateRoot.CreationTime)} Desc";
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.LoginIp), x => x.LoginIp.Contains(input.LoginIp!))
.WhereIF(!string.IsNullOrEmpty(input.LoginUser), x => x.LoginUser!.Contains(input.LoginUser!))
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.OrderBy(input.Sorting)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<LoginLogGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}

View File

@@ -24,9 +24,12 @@ namespace Yi.Framework.Rbac.Application.Services.RecordLog
public override async Task<PagedResultDto<OperationLogGetListOutputDto>> GetListAsync(OperationLogGetListInputVo input)
{
RefAsync<int> total = 0;
if (input.Sorting.IsNullOrWhiteSpace())
input.Sorting = $"{nameof(OperationLogEntity.CreationTime)} Desc";
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.OperUser), x => x.OperUser.Contains(input.OperUser!))
.WhereIF(input.OperType is not null, x => x.OperType == input.OperType)
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.OrderBy(input.Sorting)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<OperationLogGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}

View File

@@ -1,28 +1,31 @@
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Yi.Framework.Ddd.Application;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Dept;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Repositories;
using Yi.Framework.Rbac.Domain.Shared.Consts;
namespace Yi.Framework.Rbac.Application.Services.System
{
/// <summary>
/// Dept服务实现
/// </summary>
public class DeptService : YiCrudAppService<DeptAggregateRoot, DeptGetOutputDto, DeptGetListOutputDto, Guid, DeptGetListInputVo, DeptCreateInputVo, DeptUpdateInputVo>, IDeptService
public class DeptService : YiCrudAppService<DeptAggregateRoot, DeptGetOutputDto, DeptGetListOutputDto, Guid,
DeptGetListInputVo, DeptCreateInputVo, DeptUpdateInputVo>, IDeptService
{
private IDeptRepository _deptRepository;
public DeptService(IDeptRepository deptRepository) : base(deptRepository)
{ _deptRepository = deptRepository; }
private IDeptRepository _repository;
public DeptService(IDeptRepository repository) : base(repository)
{
_repository = repository;
}
[RemoteService(false)]
public async Task<List<Guid>> GetChildListAsync(Guid deptId)
{
return await _deptRepository.GetChildListAsync(deptId);
return await _repository.GetChildListAsync(deptId);
}
/// <summary>
@@ -32,7 +35,7 @@ namespace Yi.Framework.Rbac.Application.Services.System
//[Route("{roleId}")]
public async Task<List<DeptGetListOutputDto>> GetRoleIdAsync(Guid roleId)
{
var entities = await _deptRepository.GetListRoleIdAsync(roleId);
var entities = await _repository.GetListRoleIdAsync(roleId);
return await MapToGetListOutputDtosAsync(entities);
}
@@ -44,16 +47,36 @@ namespace Yi.Framework.Rbac.Application.Services.System
public override async Task<PagedResultDto<DeptGetListOutputDto>> GetListAsync(DeptGetListInputVo input)
{
RefAsync<int> total = 0;
var entities = await _deptRepository._DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.DeptName), u => u.DeptName.Contains(input.DeptName!))
.WhereIF(input.State is not null, u => u.State == input.State)
.OrderBy(u => u.OrderNum, OrderByType.Asc)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var entities = await _repository._DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.DeptName), u => u.DeptName.Contains(input.DeptName!))
.WhereIF(input.State is not null, u => u.State == input.State)
.OrderBy(u => u.OrderNum, OrderByType.Asc)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<DeptGetListOutputDto>
{
Items = await MapToGetListOutputDtosAsync(entities),
TotalCount = total
};
}
protected override async Task CheckCreateInputDtoAsync(DeptCreateInputVo input)
{
var isExist =
await _repository.IsAnyAsync(x => x.DeptCode == input.DeptCode);
if (isExist)
{
throw new UserFriendlyException(DeptConst.Exist);
}
}
protected override async Task CheckUpdateInputDtoAsync(DeptAggregateRoot entity, DeptUpdateInputVo input)
{
var isExist = await _repository._DbQueryable.Where(x => x.Id != entity.Id)
.AnyAsync(x => x.DeptCode == input.DeptCode);
if (isExist)
{
throw new UserFriendlyException(DeptConst.Exist);
}
}
}
}
}

View File

@@ -1,10 +1,10 @@
using SqlSugar;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Yi.Framework.Ddd.Application;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Menu;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services.System
@@ -23,14 +23,12 @@ namespace Yi.Framework.Rbac.Application.Services.System
public override async Task<PagedResultDto<MenuGetListOutputDto>> GetListAsync(MenuGetListInputVo input)
{
RefAsync<int> total = 0;
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.MenuName), x => x.MenuName.Contains(input.MenuName!))
.WhereIF(input.State is not null, x => x.State == input.State)
.Where(x=>x.MenuSource==input.MenuSource)
.OrderByDescending(x => x.OrderNum)
.ToListAsync();
//.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<MenuGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}

View File

@@ -1,10 +1,10 @@
using SqlSugar;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Yi.Framework.Ddd.Application;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Post;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services.System
@@ -12,10 +12,12 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// <summary>
/// Post服务实现
/// </summary>
public class PostService : YiCrudAppService<PostAggregateRoot, PostGetOutputDto, PostGetListOutputDto, Guid, PostGetListInputVo, PostCreateInputVo, PostUpdateInputVo>,
IPostService
public class PostService : YiCrudAppService<PostAggregateRoot, PostGetOutputDto, PostGetListOutputDto, Guid,
PostGetListInputVo, PostCreateInputVo, PostUpdateInputVo>,
IPostService
{
private readonly ISqlSugarRepository<PostAggregateRoot, Guid> _repository;
public PostService(ISqlSugarRepository<PostAggregateRoot, Guid> repository) : base(repository)
{
_repository = repository;
@@ -25,10 +27,31 @@ namespace Yi.Framework.Rbac.Application.Services.System
{
RefAsync<int> total = 0;
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.PostName), x => x.PostName.Contains(input.PostName!))
.WhereIF(input.State is not null, x => x.State == input.State)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.PostName),
x => x.PostName.Contains(input.PostName!))
.WhereIF(input.State is not null, x => x.State == input.State)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<PostGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}
protected override async Task CheckCreateInputDtoAsync(PostCreateInputVo input)
{
var isExist =
await _repository.IsAnyAsync(x => x.PostCode == input.PostCode);
if (isExist)
{
throw new UserFriendlyException(PostConst.Exist);
}
}
protected override async Task CheckUpdateInputDtoAsync(PostAggregateRoot entity, PostUpdateInputVo input)
{
var isExist = await _repository._DbQueryable.Where(x => x.Id != entity.Id)
.AnyAsync(x => x.PostCode == input.PostCode);
if (isExist)
{
throw new UserFriendlyException(RoleConst.Exist);
}
}
}
}
}

View File

@@ -11,6 +11,7 @@ using Yi.Framework.Rbac.Application.Contracts.Dtos.User;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Managers;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.Rbac.Domain.Shared.Enums;
using Yi.Framework.SqlSugarCore.Abstractions;
@@ -19,13 +20,16 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// <summary>
/// Role服务实现
/// </summary>
public class RoleService : YiCrudAppService<RoleAggregateRoot, RoleGetOutputDto, RoleGetListOutputDto, Guid, RoleGetListInputVo, RoleCreateInputVo, RoleUpdateInputVo>,
IRoleService
public class RoleService : YiCrudAppService<RoleAggregateRoot, RoleGetOutputDto, RoleGetListOutputDto, Guid,
RoleGetListInputVo, RoleCreateInputVo, RoleUpdateInputVo>,
IRoleService
{
public RoleService(RoleManager roleManager, ISqlSugarRepository<RoleDeptEntity> roleDeptRepository, ISqlSugarRepository<UserRoleEntity> userRoleRepository, ISqlSugarRepository<RoleAggregateRoot, Guid> repository) : base(repository)
public RoleService(RoleManager roleManager, ISqlSugarRepository<RoleDeptEntity> roleDeptRepository,
ISqlSugarRepository<UserRoleEntity> userRoleRepository,
ISqlSugarRepository<RoleAggregateRoot, Guid> repository) : base(repository)
{
(_roleManager, _roleDeptRepository, _userRoleRepository, _repository) =
(roleManager, roleDeptRepository, userRoleRepository, repository);
(roleManager, roleDeptRepository, userRoleRepository, repository);
}
private ISqlSugarRepository<RoleAggregateRoot, Guid> _repository;
@@ -41,23 +45,25 @@ namespace Yi.Framework.Rbac.Application.Services.System
if (input.DataScope == DataScopeEnum.CUSTOM)
{
await _roleDeptRepository.DeleteAsync(x => x.RoleId == input.RoleId);
var insertEntities = input.DeptIds.Select(x => new RoleDeptEntity { DeptId = x, RoleId = input.RoleId }).ToList();
var insertEntities = input.DeptIds.Select(x => new RoleDeptEntity { DeptId = x, RoleId = input.RoleId })
.ToList();
await _roleDeptRepository.InsertRangeAsync(insertEntities);
}
var entity = new RoleAggregateRoot() { DataScope = input.DataScope };
EntityHelper.TrySetId(entity, () => input.RoleId);
await _repository._Db.Updateable(entity).UpdateColumns(x => x.DataScope).ExecuteCommandAsync();
}
public override async Task<PagedResultDto<RoleGetListOutputDto>> GetListAsync(RoleGetListInputVo input)
{
RefAsync<int> total = 0;
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.RoleCode), x => x.RoleCode.Contains(input.RoleCode!))
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.RoleCode),
x => x.RoleCode.Contains(input.RoleCode!))
.WhereIF(!string.IsNullOrEmpty(input.RoleName), x => x.RoleName.Contains(input.RoleName!))
.WhereIF(input.State is not null, x => x.State == input.State)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
.WhereIF(input.State is not null, x => x.State == input.State)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<RoleGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}
@@ -68,15 +74,16 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// <returns></returns>
public override async Task<RoleGetOutputDto> CreateAsync(RoleCreateInputVo input)
{
RoleGetOutputDto outputDto;
//using (var uow = _unitOfWorkManager.CreateContext())
//{
var isExist =
await _repository.IsAnyAsync(x => x.RoleCode == input.RoleCode || x.RoleName == input.RoleName);
if (isExist)
{
throw new UserFriendlyException(RoleConst.Exist);
}
var entity = await MapToEntityAsync(input);
await _repository.InsertAsync(entity);
outputDto = await MapToGetOutputDtoAsync(entity);
await _roleManager.GiveRoleSetMenuAsync(new List<Guid> { entity.Id }, input.MenuIds);
// uow.Commit();
//}
var outputDto = await MapToGetOutputDtoAsync(entity);
return outputDto;
}
@@ -89,18 +96,20 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// <returns></returns>
public override async Task<RoleGetOutputDto> UpdateAsync(Guid id, RoleUpdateInputVo input)
{
var dto = new RoleGetOutputDto();
//using (var uow = _unitOfWorkManager.CreateContext())
//{
var entity = await _repository.GetByIdAsync(id);
var isExist = await _repository._DbQueryable.Where(x => x.Id != entity.Id).AnyAsync(x => x.RoleCode == input.RoleCode || x.RoleName == input.RoleName);
if (isExist)
{
throw new UserFriendlyException(RoleConst.Exist);
}
await MapToEntityAsync(input, entity);
await _repository.UpdateAsync(entity);
await _roleManager.GiveRoleSetMenuAsync(new List<Guid> { id }, input.MenuIds);
dto = await MapToGetOutputDtoAsync(entity);
// uow.Commit();
//}
var dto = await MapToGetOutputDtoAsync(entity);
return dto;
}
@@ -134,7 +143,8 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// <param name="isAllocated">是否在该角色下</param>
/// <returns></returns>
[Route("role/auth-user/{roleId}/{isAllocated}")]
public async Task<PagedResultDto<UserGetListOutputDto>> GetAuthUserByRoleIdAsync([FromRoute] Guid roleId, [FromRoute] bool isAllocated, [FromQuery] RoleAuthUserGetListInput input)
public async Task<PagedResultDto<UserGetListOutputDto>> GetAuthUserByRoleIdAsync([FromRoute] Guid roleId,
[FromRoute] bool isAllocated, [FromQuery] RoleAuthUserGetListInput input)
{
PagedResultDto<UserGetListOutputDto> output;
//角色下已授权用户
@@ -147,30 +157,34 @@ namespace Yi.Framework.Rbac.Application.Services.System
{
output = await GetNotAllocatedAuthUserByRoleIdAsync(roleId, input);
}
return output;
}
private async Task<PagedResultDto<UserGetListOutputDto>> GetAllocatedAuthUserByRoleIdAsync(Guid roleId, RoleAuthUserGetListInput input)
private async Task<PagedResultDto<UserGetListOutputDto>> GetAllocatedAuthUserByRoleIdAsync(Guid roleId,
RoleAuthUserGetListInput input)
{
RefAsync<int> total = 0;
var output = await _userRoleRepository._DbQueryable
.LeftJoin<UserAggregateRoot>((ur, u) => ur.UserId == u.Id && ur.RoleId == roleId)
.Where((ur, u) => ur.RoleId == roleId)
.WhereIF(!string.IsNullOrEmpty(input.UserName), (ur, u) => u.UserName.Contains(input.UserName))
.WhereIF(input.Phone is not null, (ur, u) => u.Phone.ToString().Contains(input.Phone.ToString()))
.Select((ur, u) => new UserGetListOutputDto { Id = u.Id }, true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
.LeftJoin<UserAggregateRoot>((ur, u) => ur.UserId == u.Id && ur.RoleId == roleId)
.Where((ur, u) => ur.RoleId == roleId)
.WhereIF(!string.IsNullOrEmpty(input.UserName), (ur, u) => u.UserName.Contains(input.UserName))
.WhereIF(input.Phone is not null, (ur, u) => u.Phone.ToString().Contains(input.Phone.ToString()))
.Select((ur, u) => new UserGetListOutputDto { Id = u.Id }, true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<UserGetListOutputDto>(total, output);
}
private async Task<PagedResultDto<UserGetListOutputDto>> GetNotAllocatedAuthUserByRoleIdAsync(Guid roleId, RoleAuthUserGetListInput input)
private async Task<PagedResultDto<UserGetListOutputDto>> GetNotAllocatedAuthUserByRoleIdAsync(Guid roleId,
RoleAuthUserGetListInput input)
{
RefAsync<int> total = 0;
var entities = await _userRoleRepository._Db.Queryable<UserAggregateRoot>()
.Where(u => SqlFunc.Subqueryable<UserRoleEntity>().Where(x => x.RoleId == roleId).Where(x => x.UserId == u.Id).NotAny())
.WhereIF(!string.IsNullOrEmpty(input.UserName), u => u.UserName.Contains(input.UserName))
.WhereIF(input.Phone is not null, u => u.Phone.ToString().Contains(input.Phone.ToString()))
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
.Where(u => SqlFunc.Subqueryable<UserRoleEntity>().Where(x => x.RoleId == roleId)
.Where(x => x.UserId == u.Id).NotAny())
.WhereIF(!string.IsNullOrEmpty(input.UserName), u => u.UserName.Contains(input.UserName))
.WhereIF(input.Phone is not null, u => u.Phone.ToString().Contains(input.Phone.ToString()))
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var output = entities.Adapt<List<UserGetListOutputDto>>();
return new PagedResultDto<UserGetListOutputDto>(total, output);
}
@@ -181,9 +195,10 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task CreateAuthUserAsync(RoleAuthUserCreateOrDeleteInput input)
public async Task CreateAuthUserAsync([FromBody] RoleAuthUserCreateOrDeleteInput input)
{
var userRoleEntities = input.UserIds.Select(u => new UserRoleEntity { RoleId = input.RoleId, UserId = u }).ToList();
var userRoleEntities = input.UserIds.Select(u => new UserRoleEntity { RoleId = input.RoleId, UserId = u })
.ToList();
await _userRoleRepository.InsertRangeAsync(userRoleEntities);
}
@@ -193,11 +208,12 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task DeleteAuthUserAsync(RoleAuthUserCreateOrDeleteInput input)
public async Task DeleteAuthUserAsync([FromBody] RoleAuthUserCreateOrDeleteInput input)
{
await _userRoleRepository._Db.Deleteable<UserRoleEntity>().Where(x => x.RoleId == input.RoleId)
.Where(x => input.UserIds.Contains(x.UserId))
.ExecuteCommandAsync(); ;
.ExecuteCommandAsync();
;
}
}
}
}

View File

@@ -6,6 +6,8 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Caching;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Users;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Ddd.Application;
using Yi.Framework.Rbac.Application.Contracts.Dtos.User;
using Yi.Framework.Rbac.Application.Contracts.IServices;
@@ -24,13 +26,20 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// <summary>
/// User服务实现
/// </summary>
public class UserService : YiCrudAppService<UserAggregateRoot, UserGetOutputDto, UserGetListOutputDto, Guid, UserGetListInputVo, UserCreateInputVo, UserUpdateInputVo>, IUserService
public class UserService : YiCrudAppService<UserAggregateRoot, UserGetOutputDto, UserGetListOutputDto, Guid,
UserGetListInputVo, UserCreateInputVo, UserUpdateInputVo>, IUserService
//IUserService
{
public UserService(ISqlSugarRepository<UserAggregateRoot, Guid> repository, UserManager userManager, IUserRepository userRepository, ICurrentUser currentUser, IDeptService deptService, ILocalEventBus localEventBus, IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> userCache) : base(repository)
protected ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService<ILocalEventBus>();
public UserService(ISqlSugarRepository<UserAggregateRoot, Guid> repository, UserManager userManager,
IUserRepository userRepository, ICurrentUser currentUser, IDeptService deptService,
ILocalEventBus localEventBus,
IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> userCache) : base(repository)
=>
(_userManager, _userRepository, _currentUser, _deptService, _repository, _localEventBus) =
(userManager, userRepository, currentUser, deptService, repository, localEventBus);
(_userManager, _userRepository, _currentUser, _deptService, _repository, _localEventBus) =
(userManager, userRepository, currentUser, deptService, repository, localEventBus);
private UserManager _userManager { get; set; }
private ISqlSugarRepository<UserAggregateRoot, Guid> _repository;
private IUserRepository _userRepository { get; set; }
@@ -39,6 +48,7 @@ namespace Yi.Framework.Rbac.Application.Services.System
private ICurrentUser _currentUser { get; set; }
private ILocalEventBus _localEventBus;
/// <summary>
/// 查询用户
/// </summary>
@@ -56,22 +66,21 @@ namespace Yi.Framework.Rbac.Application.Services.System
List<Guid> ids = input.Ids?.Split(",").Select(x => Guid.Parse(x)).ToList();
var outPut = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.UserName), x => x.UserName.Contains(input.UserName!))
.WhereIF(input.Phone is not null, x => x.Phone.ToString()!.Contains(input.Phone.ToString()!))
.WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name!.Contains(input.Name!))
.WhereIF(input.State is not null, x => x.State == input.State)
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
var outPut = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.UserName),
x => x.UserName.Contains(input.UserName!))
.WhereIF(input.Phone is not null, x => x.Phone.ToString()!.Contains(input.Phone.ToString()!))
.WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name!.Contains(input.Name!))
.WhereIF(input.State is not null, x => x.State == input.State)
.WhereIF(input.StartTime is not null && input.EndTime is not null,
x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
//这个为过滤当前部门,加入数据权限后,将由数据权限控制
.WhereIF(input.DeptId is not null, x => deptIds.Contains(x.DeptId ?? Guid.Empty))
.WhereIF(ids is not null, x => ids.Contains(x.Id))
.LeftJoin<DeptAggregateRoot>((user, dept) => user.DeptId == dept.Id)
.OrderByDescending(user => user.CreationTime)
.Select((user, dept) => new UserGetListOutputDto(), true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
//这个为过滤当前部门,加入数据权限后,将由数据权限控制
.WhereIF(input.DeptId is not null, x => deptIds.Contains(x.DeptId ?? Guid.Empty))
.WhereIF(ids is not null, x => ids.Contains(x.Id))
.LeftJoin<DeptAggregateRoot>((user, dept) => user.DeptId == dept.Id)
.OrderByDescending(user => user.CreationTime)
.Select((user, dept) => new UserGetListOutputDto(), true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var result = new PagedResultDto<UserGetListOutputDto>();
result.Items = outPut;
@@ -96,7 +105,6 @@ namespace Yi.Framework.Rbac.Application.Services.System
[Permission("system:user:add")]
public async override Task<UserGetOutputDto> CreateAsync(UserCreateInputVo input)
{
var entitiy = await MapToEntityAsync(input);
await _userManager.CreateAsync(entitiy);
@@ -119,10 +127,12 @@ namespace Yi.Framework.Rbac.Application.Services.System
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[Permission("system:user:list")]
public override async Task<UserGetOutputDto> GetAsync(Guid id)
{
//使用导航树形查询
var entity = await _repository._DbQueryable.Includes(u => u.Roles).Includes(u => u.Posts).Includes(u => u.Dept).InSingleAsync(id);
var entity = await _repository._DbQueryable.Includes(u => u.Roles).Includes(u => u.Posts)
.Includes(u => u.Dept).InSingleAsync(id);
return await MapToGetOutputDtoAsync(entity);
}
@@ -141,17 +151,20 @@ namespace Yi.Framework.Rbac.Application.Services.System
{
throw new UserFriendlyException(UserConst.Name_Not_Allowed);
}
if (await _repository.IsAnyAsync(u => input.UserName!.Equals(u.UserName) && !id.Equals(u.Id)))
{
throw new UserFriendlyException("用户已经存在,更新失败");
throw new UserFriendlyException(UserConst.Exist);
}
var entity = await _repository.GetByIdAsync(id);
//更新密码,特殊处理
if (input.Password is not null)
if (!string.IsNullOrWhiteSpace(input.Password))
{
entity.EncryPassword.Password = input.Password;
entity.BuildPassword();
}
await MapToEntityAsync(input, entity);
var res1 = await _repository.UpdateAsync(entity);
@@ -173,6 +186,14 @@ namespace Yi.Framework.Rbac.Application.Services.System
await _repository.UpdateAsync(entity);
var dto = await MapToGetOutputDtoAsync(entity);
//发布更新昵称任务事件
if (input.Nick != entity.Icon)
{
await this.LocalEventBus.PublishAsync(
new AssignmentEventArgs(AssignmentRequirementTypeEnum.UpdateNick, _currentUser.GetId(), input.Nick),
false);
}
return dto;
}
@@ -192,15 +213,16 @@ namespace Yi.Framework.Rbac.Application.Services.System
{
throw new ApplicationException("用户未存在");
}
entity.State = state;
await _repository.UpdateAsync(entity);
return await MapToGetOutputDtoAsync(entity);
}
[OperLog("删除用户", OperEnum.Delete)]
[Permission("system:user:delete")]
public override async Task DeleteAsync(Guid id)
{
await base.DeleteAsync(id);
}
@@ -216,4 +238,4 @@ namespace Yi.Framework.Rbac.Application.Services.System
return base.PostImportExcelAsync(input);
}
}
}
}

View File

@@ -1,120 +0,0 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Uow;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Options;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services
{
/// <summary>
/// 测试文档控制器
/// </summary>
public class TestServcie : ApplicationService
{
private IRepository<StudentEntity> _repository;
private IUnitOfWorkManager _unitOfWork;
private ISqlSugarRepository<StudentEntity> _sqlsugarRepository;
public IOptions<JwtOptions> options { get; set; }
public TestServcie(IRepository<StudentEntity> repository, IUnitOfWorkManager unitOfWork, ISqlSugarRepository<StudentEntity> sqlsugarRepository, IRepository<StudentEntity, Guid> repository2)
{
_unitOfWork = unitOfWork;
_repository = repository;
_sqlsugarRepository = sqlsugarRepository;
}
/// <summary>
/// 你好,多线程
/// </summary>
/// <returns></returns>
public async Task<string> GetTaskTest()
{
var tasks = Enumerable.Range(0, 2).Select(x =>
{
return Task.Run(async () =>
{
using (var uow = _unitOfWork.Begin(true))
{
// await _repository.GetListAsync();
await _sqlsugarRepository._DbQueryable.ToListAsync();
await uow.CompleteAsync();
}
});
}).ToList();
await Task.WhenAll(tasks);
return "你哈";
}
[Authorize]
public async Task<List<StudentEntity>> GetTest()
{
//using (var uow = _unitOfWork.Begin(true))
//{
var data = await _repository.GetListAsync();
var data2 = await _repository.GetListAsync();
//await uow.CompleteAsync();
return data;
//}
}
//[UnitOfWork]
public async Task<StudentEntity> PostTest()
{
//using (var uow = _unitOfWork.Begin())
//{
var stu = new StudentEntity() { Name = $"{DateTime.Now.ToString()}你好" };
var data = await _repository.InsertAsync(stu);
//await uow.CompleteAsync();
return data;
//}
}
public async Task<StudentEntity> PostError()
{
throw new ApplicationException();
}
public async Task<StudentEntity> PostUserError()
{
throw new UserFriendlyException("直接爆炸");
}
public string Login()
{
var data = options.Value;
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(data.SecurityKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new List<Claim>()
{
new Claim("name","admin")
};
var token = new JwtSecurityToken(
issuer: data.Issuer,
audience: data.Audience,
claims: claims,
expires: DateTime.Now.AddSeconds(60 * 60 * 2),
notBefore: DateTime.Now,
signingCredentials: creds);
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
return returnToken;
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Domain.Shared.Caches
{
@@ -14,13 +15,15 @@ namespace Yi.Framework.Rbac.Domain.Shared.Caches
public class CaptchaPhoneCacheKey
{
public CaptchaPhoneCacheKey(string phone) { Phone = phone; }
public CaptchaPhoneCacheKey(ValidationPhoneTypeEnum validationPhoneType,string phone) { Phone = phone;
ValidationPhoneType = validationPhoneType;
}
public ValidationPhoneTypeEnum ValidationPhoneType { get; set; }
public string Phone { get; set; }
public override string ToString()
{
return $"Phone:{Phone}";
return $"Phone:{ValidationPhoneType.ToString()}:{Phone}";
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Yi.Framework.Rbac.Domain.Shared.Consts;
public class ConfigConst
{
public const string Exist = "该配置已经存在";
}

View File

@@ -12,5 +12,6 @@ namespace Yi.Framework.Rbac.Domain.Shared.Consts
public class DeptConst
{
public const string Exist = "该部门已经存在";
}
}

View File

@@ -0,0 +1,6 @@
namespace Yi.Framework.Rbac.Domain.Shared.Consts;
public class DictionaryConst
{
public const string Exist = "该字典已经存在";
}

View File

@@ -12,5 +12,6 @@ namespace Yi.Framework.Rbac.Domain.Shared.Consts
public class MenuConst
{
public const string Exist = "该菜单已经存在";
}
}

View File

@@ -12,5 +12,6 @@ namespace Yi.Framework.Rbac.Domain.Shared.Consts
public class PostConst
{
public const string Exist = "该岗位已经存在";
}
}

View File

@@ -12,5 +12,6 @@ namespace Yi.Framework.Rbac.Domain.Shared.Consts
public class RoleConst
{
public const string Exist = "该角色已经存在";
}
}

View File

@@ -16,7 +16,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Consts
public const string Login_User_No_Exist = "登录失败!用户名不存在!";
public const string Login_Passworld_Error = "密码为空,添加失败!";
public const string Create_Passworld_Error = "密码格式错误长度需大于等于6位";
public const string User_Exist = "用户已经存在,创建失败!";
public const string Exist = "用户已经存在,创建失败!";
public const string State_Is_State = "该用户已被禁用,请联系管理员进行恢复";
public const string No_Permission = "登录禁用!该用户分配无任何权限,无意义登录!";
public const string No_Role = "登录禁用!该用户分配无任何角色,无意义登录!";

View File

@@ -195,6 +195,11 @@ namespace Yi.Framework.Rbac.Domain.Shared.Dtos
public class MenuDto
{
public Guid Id { get; set; }
/// <summary>
/// 菜单来源
/// </summary>
public MenuSourceEnum MenuSource { get; set; }
public string? RouterName { get; set; }
/// <summary>
/// 逻辑删除

View File

@@ -0,0 +1,31 @@
namespace Yi.Framework.Rbac.Domain.Shared.Dtos;
public class Vue3PureRouterDto
{
public Guid Id { get; set; }
public Guid ParentId { get; set; }
public string Path { get; set; }
public string Name { get; set; }
public MetaPureRouterDto Meta { get; set; } = new MetaPureRouterDto();
public string? component { get; set; }
public List<Vue3PureRouterDto>? Children { get; set; }
}
public class MetaPureRouterDto
{
public string Icon { get; set; }
public string Title { get; set; }
public List<string>? Roles { get; set; }
public List<string>? Auths { get; set; }
public string? FrameSrc { get; set; }
public string? FrameLoading { get; set; }
public bool? KeepAlive { get; set; }
public bool? showLink { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace Yi.Framework.Rbac.Domain.Shared.Enums;
public enum MenuSourceEnum
{
Ruoyi=0,
Pure=1
}

View File

@@ -0,0 +1,13 @@
namespace Yi.Framework.Rbac.Domain.Shared.Enums;
public enum ValidationPhoneTypeEnum
{
/// <summary>
/// 注册
/// </summary>
Register,
/// <summary>
/// 忘记密码
/// </summary>
RetrievePassword
}

View File

@@ -1,4 +1,4 @@
using IPTools.Core;
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using SqlSugar;
using UAParser;
@@ -75,7 +75,14 @@ namespace Yi.Framework.Rbac.Domain.Entities
}
else
{
location = IpTool.Search(ipAddr);
try
{
location = IpTool.Search(ipAddr);
}
catch
{
location = new IpInfo() { Province = ipAddr, City = "未知地区" };
}
}
ClientInfo clientInfo = GetClientInfo(context);
LoginLogAggregateRoot entity = new()

View File

@@ -1,4 +1,6 @@
using SqlSugar;
using System.Web;
using NUglify.Helpers;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
@@ -15,10 +17,22 @@ namespace Yi.Framework.Rbac.Domain.Entities
[SugarTable("Menu")]
public partial class MenuAggregateRoot : AggregateRoot<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IState
{
public MenuAggregateRoot() { }
public MenuAggregateRoot()
{
}
public MenuAggregateRoot(Guid id)
{
Id = id;
ParentId = Guid.Empty;
}
public MenuAggregateRoot(Guid id, Guid parentId)
{
Id = id;
ParentId = parentId;
}
public MenuAggregateRoot(Guid id) { Id = id; ParentId = Guid.Empty; }
public MenuAggregateRoot(Guid id, Guid parentId) { Id = id; ParentId = parentId; }
/// <summary>
/// 主键
/// </summary>
@@ -63,17 +77,25 @@ namespace Yi.Framework.Rbac.Domain.Entities
/// <summary>
/// 菜单名
/// </summary>
public string MenuName { get; set; } = string.Empty;
public string MenuName { get; set; }
/// <summary>
/// 路由名称
/// </summary>
public string? RouterName { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MenuType")]
public MenuTypeEnum MenuType { get; set; } = MenuTypeEnum.Menu;
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "PermissionCode")]
public string? PermissionCode { get; set; }
/// <summary>
///
///</summary>
@@ -85,21 +107,25 @@ namespace Yi.Framework.Rbac.Domain.Entities
///</summary>
[SugarColumn(ColumnName = "MenuIcon")]
public string? MenuIcon { get; set; }
/// <summary>
/// 菜单组件路由
///</summary>
[SugarColumn(ColumnName = "Router")]
public string? Router { get; set; }
/// <summary>
/// 是否为外部链接
///</summary>
[SugarColumn(ColumnName = "IsLink")]
public bool IsLink { get; set; }
/// <summary>
/// 是否缓存
///</summary>
[SugarColumn(ColumnName = "IsCache")]
public bool IsCache { get; set; }
/// <summary>
/// 是否显示
///</summary>
@@ -111,20 +137,25 @@ namespace Yi.Framework.Rbac.Domain.Entities
///</summary>
[SugarColumn(ColumnName = "Remark")]
public string? Remark { get; set; }
/// <summary>
/// 组件路径
///</summary>
[SugarColumn(ColumnName = "Component")]
public string? Component { get; set; }
/// <summary>
/// 菜单来源
/// </summary>
public MenuSourceEnum MenuSource { get; set; } = MenuSourceEnum.Ruoyi;
/// <summary>
/// 路由参数
///</summary>
[SugarColumn(ColumnName = "Query")]
public string? Query { get; set; }
[SugarColumn(IsIgnore = true)]
public List<MenuAggregateRoot>? Children { get; set; }
[SugarColumn(IsIgnore = true)] public List<MenuAggregateRoot>? Children { get; set; }
}
/// <summary>
@@ -137,13 +168,16 @@ namespace Yi.Framework.Rbac.Domain.Entities
/// </summary>
/// <param name="menus"></param>
/// <returns></returns>
public static List<Vue3RouterDto> Vue3RouterBuild(this List<MenuAggregateRoot> menus)
public static List<Vue3RouterDto> Vue3RuoYiRouterBuild(this List<MenuAggregateRoot> menus)
{
menus = menus.Where(m => m.MenuType != MenuTypeEnum.Component).ToList();
menus = menus
.Where(m => m.State == true)
.Where(m => m.MenuType != MenuTypeEnum.Component)
.Where(m => m.MenuSource == MenuSourceEnum.Ruoyi)
.ToList();
List<Vue3RouterDto> routers = new();
foreach (var m in menus)
{
var r = new Vue3RouterDto();
r.OrderNum = m.OrderNum;
var routerName = m.Router?.Split("/").LastOrDefault();
@@ -171,6 +205,7 @@ namespace Yi.Framework.Rbac.Domain.Entities
r.Component = "ParentView";
}
}
if (m.MenuType == MenuTypeEnum.Menu)
{
r.Redirect = "noRedirect";
@@ -178,6 +213,7 @@ namespace Yi.Framework.Rbac.Domain.Entities
r.Component = m.Component!;
r.AlwaysShow = false;
}
r.Meta = new Meta
{
Title = m.MenuName!,
@@ -192,8 +228,62 @@ namespace Yi.Framework.Rbac.Domain.Entities
routers.Add(r);
}
return TreeHelper.SetTree(routers);
return TreeHelper.SetTree(routers);
}
/// <summary>
/// 构建vue3 pure路由
/// </summary>
/// <param name="menus"></param>
/// <returns></returns>
public static List<Vue3PureRouterDto> Vue3PureRouterBuild(this List<MenuAggregateRoot> menus)
{
//pure的菜单为树形
var allRouters = menus
.Where(m => m.State == true)
.Where(m => m.MenuType != MenuTypeEnum.Component)
.Where(m => m.MenuSource == MenuSourceEnum.Pure)
.Select(m => new Vue3PureRouterDto
{
Path =m.Router.StartsWith("/")?m.Router:"/"+m.Router,
Name =m.IsLink==true?"Link": m.RouterName,
component = m.Component,
Meta = new MetaPureRouterDto()
{
showLink = m.IsShow,
FrameSrc = m.IsLink == true ? m.Router : null,
Auths = new List<string>() { m.PermissionCode },
Icon = m.MenuIcon,
Title = m.MenuName,
},
Children =null,
Id = m.Id,
ParentId = m.ParentId
})
.ToList();
var routerDic = allRouters.GroupBy(x => x.ParentId).ToDictionary(x => x.Key,y=>y.ToList());
//根路由
if (!routerDic.TryGetValue(Guid.Empty, out var rootRouters))
{
return new List<Vue3PureRouterDto>();
}
Stack<Vue3PureRouterDto> stack = new Stack<Vue3PureRouterDto>(rootRouters);
while (stack.Count > 0)
{
var currentRouter = stack.Pop();
if (routerDic.TryGetValue(currentRouter.Id, out var items))
{
currentRouter.Children = items;
items?.ForEach(x => stack.Push(x));
}
}
return rootRouters;
}
}
}
}

View File

@@ -1,13 +0,0 @@
using Volo.Abp.Domain.Entities;
namespace Yi.Framework.Rbac.Domain.Entities
{
public class StudentEntity : Entity<Guid>
{
[SqlSugar.SugarColumn(IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public string Name { get; set; }
}
}

View File

@@ -19,12 +19,12 @@ namespace Yi.Framework.Rbac.Domain.Entities
{
}
public UserAggregateRoot(string userName, string password, long phone, string nick = "萌新")
public UserAggregateRoot(string userName, string password, long phone, string? nick = null)
{
UserName = userName;
EncryPassword.Password = password;
Phone = phone;
Nick = nick+"-"+userName;
Nick =string.IsNullOrWhiteSpace(nick)?"萌新-"+userName:nick.Trim();
BuildPassword();
}

View File

@@ -1,16 +0,0 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Yi.Framework.Rbac.Domain.Entities;
namespace Yi.Framework.Rbac.Domain.EventHandlers
{
public class StudentEventHandler : ILocalEventHandler<EntityCreatedEventData<StudentEntity>>, ITransientDependency
{
public Task HandleEventAsync(EntityCreatedEventData<StudentEntity> eventData)
{
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(eventData.Entity));
return Task.CompletedTask;
}
}
}

View File

@@ -34,13 +34,11 @@ namespace Yi.Framework.Rbac.Domain.Managers
private readonly ILocalEventBus _localEventBus;
private readonly JwtOptions _jwtOptions;
private readonly RbacOptions _options;
private IHttpContextAccessor _httpContextAccessor;
private UserManager _userManager;
private ISqlSugarRepository<RoleAggregateRoot> _roleRepository;
private RefreshJwtOptions _refreshJwtOptions;
public AccountManager(IUserRepository repository
, IHttpContextAccessor httpContextAccessor
, IOptions<JwtOptions> jwtOptions
, ILocalEventBus localEventBus
, UserManager userManager
@@ -49,7 +47,6 @@ namespace Yi.Framework.Rbac.Domain.Managers
, IOptions<RbacOptions> options)
{
_repository = repository;
_httpContextAccessor = httpContextAccessor;
_jwtOptions = jwtOptions.Value;
_localEventBus = localEventBus;
_userManager = userManager;
@@ -62,9 +59,10 @@ namespace Yi.Framework.Rbac.Domain.Managers
/// 根据用户id获取token
/// </summary>
/// <param name="userId"></param>
/// <param name="getUserInfo"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task<string> GetTokenByUserIdAsync(Guid userId)
public async Task<string> GetTokenByUserIdAsync(Guid userId,Action<UserRoleMenuDto>? getUserInfo=null)
{
//获取用户信息
var userInfo = await _userManager.GetInfoAsync(userId);
@@ -79,23 +77,18 @@ namespace Yi.Framework.Rbac.Domain.Managers
{
throw new UserFriendlyException(UserConst.No_Role);
}
if (userInfo.PermissionCodes.Count() == 0)
if (!userInfo.PermissionCodes.Any())
{
throw new UserFriendlyException(UserConst.No_Permission);
}
//这里抛出一个登录的事件,也可以在全部流程走完,在应用层组装
if (_httpContextAccessor.HttpContext is not null)
if (getUserInfo is not null)
{
var loginEntity = new LoginLogAggregateRoot().GetInfoByHttpContext(_httpContextAccessor.HttpContext);
var loginEto = loginEntity.Adapt<LoginEventArgs>();
loginEto.UserName = userInfo.User.UserName;
loginEto.UserId = userInfo.User.Id;
await _localEventBus.PublishAsync(loginEto);
getUserInfo(userInfo);
}
var accessToken = CreateToken(this.UserInfoToClaim(userInfo));
//将用户信息添加到缓存中,需要考虑的是更改了用户、角色、菜单等整个体系都需要将缓存进行刷新,看具体业务进行选择
return accessToken;
}
@@ -187,6 +180,8 @@ namespace Yi.Framework.Rbac.Domain.Managers
}
return false;
}
/// <summary>
/// 令牌转换
@@ -257,7 +252,7 @@ namespace Yi.Framework.Rbac.Domain.Managers
}
/// <summary>
/// 重置密码
/// 重置密码,也可以是找回密码
/// </summary>
/// <param name="userId"></param>
/// <param name="password"></param>
@@ -265,7 +260,6 @@ namespace Yi.Framework.Rbac.Domain.Managers
public async Task<bool> RestPasswordAsync(Guid userId, string password)
{
var user = await _repository.GetByIdAsync(userId);
// EntityHelper.TrySetId(user, () => GuidGenerator.Create(), true);
user.EncryPassword.Password = password;
user.BuildPassword();
return await _repository.UpdateAsync(user);
@@ -278,12 +272,11 @@ namespace Yi.Framework.Rbac.Domain.Managers
/// <param name="password"></param>
/// <param name="phone"></param>
/// <returns></returns>
public async Task RegisterAsync(string userName, string password, long phone)
public async Task RegisterAsync(string userName, string password, long phone,string? nick)
{
var user = new UserAggregateRoot(userName, password, phone);
var user = new UserAggregateRoot(userName, password, phone,nick);
await _userManager.CreateAsync(user);
await _userManager.SetDefautRoleAsync(user.Id);
}
}

View File

@@ -5,15 +5,16 @@ using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Dtos;
namespace Yi.Framework.Rbac.Domain.Managers
{
public interface IAccountManager : IDomainService
{
string CreateRefreshToken(Guid userId);
Task<string> GetTokenByUserIdAsync(Guid userId);
Task LoginValidationAsync(string userName, string password, Action<UserAggregateRoot> userAction = null);
Task RegisterAsync(string userName, string password, long phone);
Task<string> GetTokenByUserIdAsync(Guid userId,Action<UserRoleMenuDto>? getUserInfo=null);
Task LoginValidationAsync(string userName, string password, Action<UserAggregateRoot>? userAction = null);
Task RegisterAsync(string userName, string password, long phone,string? nick);
Task<bool> RestPasswordAsync(Guid userId, string password);
Task UpdatePasswordAsync(Guid userId, string newPassword, string oldPassword);
}

View File

@@ -118,7 +118,7 @@ namespace Yi.Framework.Rbac.Domain.Managers
var isExist = await _repository.IsAnyAsync(x => x.UserName == userEntity.UserName);
if (isExist)
{
throw new UserFriendlyException(UserConst.User_Exist);
throw new UserFriendlyException(UserConst.Exist);
}
var entity = await _repository.InsertReturnEntityAsync(userEntity);
@@ -162,14 +162,23 @@ namespace Yi.Framework.Rbac.Domain.Managers
}
/// <summary>
/// 查询用户信息,缓存
/// 查询用户信息,取消缓存
/// </summary>
/// <returns></returns>
public async Task<UserRoleMenuDto> GetInfoAsync(Guid userId)
{
var output = await GetInfoByCacheAsync(userId);
return output;
var user = await _userRepository.GetUserAllInfoAsync(userId);
var data = EntityMapToDto(user);
//系统用户数据被重置,老前端访问重新授权
if (data is null)
{
throw new AbpAuthorizationException();
}
//data.Menus.Clear();
// output = data;
return data;
// var output = await GetInfoByCacheAsync(userId);
// return output;
}
private async Task<UserRoleMenuDto> GetInfoByCacheAsync(Guid userId)
{
@@ -222,10 +231,11 @@ namespace Yi.Framework.Rbac.Domain.Managers
var userRoleMenu = new UserRoleMenuDto();
//首先获取到该用户全部信息,导航到角色、菜单,(菜单需要去重,完全交给Set来处理即可)
//if (user is null)
//{
// throw new UserFriendlyException($"数据错误用户id{nameof(userId)} 不存在,请重新登录");
//}
if (user is null)
{
//为了解决token前端缓存后端数据库重新dbseed
throw new UserFriendlyException($"数据错误,查询用户不存在,请重新登录");
}
user.EncryPassword.Password = string.Empty;
user.EncryPassword.Salt = string.Empty;

View File

@@ -1,8 +1,9 @@
using IPTools.Core;
using IPTools.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Users;
@@ -25,9 +26,9 @@ namespace Yi.Framework.Rbac.Domain.Operlog
_currentUser = currentUser;
}
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next.Invoke();
var resultContext = await next();
//执行后
//判断标签是在方法上
@@ -47,10 +48,18 @@ namespace Yi.Framework.Rbac.Domain.Operlog
//获取Ip
string ip = resultContext.HttpContext.GetClientIp();
//根据ip获取地址
//根据ip获取地址
string location = "";
try
{
var ipTool = IpTool.Search(ip);
location = ipTool.Province + " " + ipTool.City;
}
catch
{
location = "搜索地址失败,可能是内网地址:" + ip;
}
var ipTool = IpTool.Search(ip);
string location = ipTool.Province + " " + ipTool.City;
//日志服务插入一条操作记录即可
@@ -84,7 +93,8 @@ namespace Yi.Framework.Rbac.Domain.Operlog
if (operLogAttribute.IsSaveRequestData)
{
//不建议保存,吃性能
//logEntity.RequestParam = context.HttpContext.GetRequestValue(logEntity.RequestMethod);
//保存请求参数
logEntity.RequestParam = JsonConvert.SerializeObject(context.ActionArguments);
}
await _repository.InsertAsync(logEntity);

View File

@@ -51,12 +51,12 @@ namespace Yi.Framework.Rbac.Domain.Operlog
/// <summary>
/// 请求参数
///</summary>
[SugarColumn(ColumnName = "RequestParam")]
[SugarColumn(ColumnName = "RequestParam",ColumnDataType = StaticConfig.CodeFirst_BigString)]
public string? RequestParam { get; set; }
/// <summary>
/// 请求结果
///</summary>
[SugarColumn(ColumnName = "RequestResult", Length = 9999)]
[SugarColumn(ColumnName = "RequestResult",ColumnDataType = StaticConfig.CodeFirst_BigString)]
public string? RequestResult { get; set; }
public DateTime CreationTime { get; set; }

View File

@@ -0,0 +1,519 @@
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Enums;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
{
public class MenuPureDataSeed : IDataSeedContributor, ITransientDependency
{
private ISqlSugarRepository<MenuAggregateRoot> _repository;
private IGuidGenerator _guidGenerator;
public MenuPureDataSeed(ISqlSugarRepository<MenuAggregateRoot> repository, IGuidGenerator guidGenerator)
{
_repository = repository;
_guidGenerator = guidGenerator;
}
public async Task SeedAsync(DataSeedContext context)
{
if (!await _repository.IsAnyAsync(x => x.MenuName == "系统管理"&&x.MenuSource==MenuSourceEnum.Pure))
{
await _repository.InsertManyAsync(GetSeedData());
}
}
public List<MenuAggregateRoot> GetSeedData()
{
List<MenuAggregateRoot> entities = new List<MenuAggregateRoot>();
//系统管理
MenuAggregateRoot system = new MenuAggregateRoot(_guidGenerator.Create(), Guid.Empty)
{
MenuName = "系统管理",
MenuType = MenuTypeEnum.Catalogue,
Router = "/system",
MenuIcon = "ri:settings-3-line",
OrderNum = 100
};
entities.Add(system);
//系统监控
MenuAggregateRoot monitoring = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "系统监控",
MenuType = MenuTypeEnum.Catalogue,
Router = "/monitor",
MenuIcon = "ep:monitor",
OrderNum = 99,
};
entities.Add(monitoring);
//在线用户
MenuAggregateRoot online = new MenuAggregateRoot(_guidGenerator.Create(), monitoring.Id)
{
MenuName = "在线用户",
PermissionCode = "monitor:online:list",
MenuType = MenuTypeEnum.Menu,
Router = "/monitor/online-user",
MenuIcon = "ri:user-voice-line",
OrderNum = 100,
RouterName = "OnlineUser",
Component = "monitor/online/index"
};
entities.Add(online);
//Yi框架
MenuAggregateRoot guide = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "Yi框架",
MenuType = MenuTypeEnum.Catalogue,
Router = "https://ccnetcore.com",
IsLink = true,
MenuIcon = "ri:at-line",
OrderNum = 90,
Component = null
};
entities.Add(guide);
//用户管理
MenuAggregateRoot user = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "用户管理",
PermissionCode = "system:user:list",
MenuType = MenuTypeEnum.Menu,
Router = "/system/user/index",
MenuIcon = "ri:admin-line",
OrderNum = 100,
ParentId = system.Id,
RouterName = "SystemUser"
};
entities.Add(user);
MenuAggregateRoot userQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "用户查询",
PermissionCode = "system:user:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = user.Id,
IsDeleted = false
};
entities.Add(userQuery);
MenuAggregateRoot userAdd = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "用户新增",
PermissionCode = "system:user:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = user.Id,
IsDeleted = false
};
entities.Add(userAdd);
MenuAggregateRoot userEdit = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "用户修改",
PermissionCode = "system:user:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = user.Id,
IsDeleted = false
};
entities.Add(userEdit);
MenuAggregateRoot userRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "用户删除",
PermissionCode = "system:user:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = user.Id,
IsDeleted = false
};
entities.Add(userRemove);
MenuAggregateRoot userResetPwd = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "重置密码",
PermissionCode = "system:user:resetPwd",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = user.Id,
IsDeleted = false
};
entities.Add(userResetPwd);
//角色管理
MenuAggregateRoot role = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "角色管理",
PermissionCode = "system:role:list",
MenuType = MenuTypeEnum.Menu,
Router = "/system/role/index",
MenuIcon = "ri:admin-fill",
OrderNum = 99,
ParentId = system.Id,
RouterName = "SystemRole"
};
entities.Add(role);
MenuAggregateRoot roleQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "角色查询",
PermissionCode = "system:role:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = role.Id,
IsDeleted = false
};
entities.Add(roleQuery);
MenuAggregateRoot roleAdd = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "角色新增",
PermissionCode = "system:role:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = role.Id,
IsDeleted = false
};
entities.Add(roleAdd);
MenuAggregateRoot roleEdit = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "角色修改",
PermissionCode = "system:role:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = role.Id,
IsDeleted = false
};
entities.Add(roleEdit);
MenuAggregateRoot roleRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "角色删除",
PermissionCode = "system:role:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = role.Id,
IsDeleted = false
};
entities.Add(roleRemove);
//菜单管理
MenuAggregateRoot menu = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "菜单管理",
PermissionCode = "system:menu:list",
MenuType = MenuTypeEnum.Menu,
Router = "/system/menu/index",
MenuIcon = "ep:menu",
OrderNum = 98,
ParentId = system.Id,
RouterName = "SystemMenu"
};
entities.Add(menu);
MenuAggregateRoot menuQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "菜单查询",
PermissionCode = "system:menu:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = menu.Id,
IsDeleted = false
};
entities.Add(menuQuery);
MenuAggregateRoot menuAdd = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "菜单新增",
PermissionCode = "system:menu:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = menu.Id,
IsDeleted = false
};
entities.Add(menuAdd);
MenuAggregateRoot menuEdit = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "菜单修改",
PermissionCode = "system:menu:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = menu.Id,
IsDeleted = false
};
entities.Add(menuEdit);
MenuAggregateRoot menuRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "菜单删除",
PermissionCode = "system:menu:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = menu.Id,
IsDeleted = false
};
entities.Add(menuRemove);
//部门管理
MenuAggregateRoot dept = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "部门管理",
PermissionCode = "system:dept:list",
MenuType = MenuTypeEnum.Menu,
Router = "/system/dept/index",
MenuIcon = "ri:git-branch-line",
OrderNum = 97,
ParentId = system.Id,
RouterName = "SystemDept"
};
entities.Add(dept);
MenuAggregateRoot deptQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "部门查询",
PermissionCode = "system:dept:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = dept.Id,
IsDeleted = false
};
entities.Add(deptQuery);
MenuAggregateRoot deptAdd = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "部门新增",
PermissionCode = "system:dept:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = dept.Id,
IsDeleted = false
};
entities.Add(deptAdd);
MenuAggregateRoot deptEdit = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "部门修改",
PermissionCode = "system:dept:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = dept.Id,
IsDeleted = false
};
entities.Add(deptEdit);
MenuAggregateRoot deptRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "部门删除",
PermissionCode = "system:dept:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = dept.Id,
IsDeleted = false
};
entities.Add(deptRemove);
//岗位管理
MenuAggregateRoot post = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "岗位管理",
PermissionCode = "system:post:list",
MenuType = MenuTypeEnum.Menu,
Router = "/system/post/index",
MenuIcon = "ant-design:deployment-unit-outlined",
OrderNum = 96,
ParentId = system.Id,
RouterName = "SystemPost"
};
entities.Add(post);
MenuAggregateRoot postQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "岗位查询",
PermissionCode = "system:post:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = post.Id,
IsDeleted = false
};
entities.Add(postQuery);
MenuAggregateRoot postAdd = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "岗位新增",
PermissionCode = "system:post:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = post.Id,
IsDeleted = false
};
entities.Add(postAdd);
MenuAggregateRoot postEdit = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "岗位修改",
PermissionCode = "system:post:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = post.Id,
IsDeleted = false
};
entities.Add(postEdit);
MenuAggregateRoot postRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "岗位删除",
PermissionCode = "system:post:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = post.Id,
IsDeleted = false
};
entities.Add(postRemove);
//操作日志
MenuAggregateRoot operationLog = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "操作日志",
PermissionCode = "monitor:operlog:list",
MenuType = MenuTypeEnum.Menu,
Router = "/monitor/operation-logs",
MenuIcon = "ri:history-fill",
OrderNum = 100,
ParentId = monitoring.Id,
RouterName = "OperationLog",
Component = "monitor/logs/operation/index"
};
entities.Add(operationLog);
MenuAggregateRoot operationLogQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "操作查询",
PermissionCode = "monitor:operlog:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = operationLog.Id,
IsDeleted = false
};
entities.Add(operationLogQuery);
MenuAggregateRoot operationLogRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "操作删除",
PermissionCode = "monitor:operlog:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = operationLog.Id,
IsDeleted = false
};
entities.Add(operationLogRemove);
//登录日志
MenuAggregateRoot loginLog = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "登录日志",
PermissionCode = "monitor:logininfor:list",
MenuType = MenuTypeEnum.Menu,
Router = "/monitor/login-logs",
IsShow = true,
IsLink = false,
IsCache = true,
Component = "monitor/logs/login/index",
MenuIcon = "ri:window-line",
OrderNum = 100,
ParentId = monitoring.Id,
RouterName = "LoginLog",
};
entities.Add(loginLog);
MenuAggregateRoot loginLogQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "登录查询",
PermissionCode = "monitor:logininfor:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = loginLog.Id,
IsDeleted = false
};
entities.Add(loginLogQuery);
MenuAggregateRoot loginLogRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "登录删除",
PermissionCode = "monitor:logininfor:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = loginLog.Id,
IsDeleted = false,
};
entities.Add(loginLogRemove);
//默认值
entities.ForEach(m =>
{
m.IsDeleted = false;
m.State = true;
m.MenuSource = MenuSourceEnum.Pure;
m.IsShow = true;
});
var p = entities.GroupBy(x => x.Id);
foreach (var k in p)
{
if (k.ToList().Count > 1)
{
Console.WriteLine("菜单id重复");
}
}
return entities;
}
}
}

View File

@@ -7,11 +7,11 @@ using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
{
public class MenuDataSeed : IDataSeedContributor, ITransientDependency
public class MenuRuoYiDataSeed : IDataSeedContributor, ITransientDependency
{
private ISqlSugarRepository<MenuAggregateRoot> _repository;
private IGuidGenerator _guidGenerator;
public MenuDataSeed(ISqlSugarRepository<MenuAggregateRoot> repository, IGuidGenerator guidGenerator)
public MenuRuoYiDataSeed(ISqlSugarRepository<MenuAggregateRoot> repository, IGuidGenerator guidGenerator)
{
_repository = repository;
_guidGenerator = guidGenerator;
@@ -19,7 +19,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
public async Task SeedAsync(DataSeedContext context)
{
if (!await _repository.IsAnyAsync(x => x.MenuName == "系统管理"))
if (!await _repository.IsAnyAsync(x => x.MenuName == "系统管理"&&x.MenuSource==MenuSourceEnum.Ruoyi))
{
await _repository.InsertManyAsync(GetSeedData());
}
@@ -54,7 +54,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
IsLink = false,
MenuIcon = "build",
OrderNum = 91,
IsDeleted = false
IsDeleted = false,
};
entities.Add(code);
@@ -229,332 +229,332 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
entities.Add(swagger);
//ERP
MenuAggregateRoot erp = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "ERP(待更新)",
MenuType = MenuTypeEnum.Catalogue,
Router = "/erp",
IsShow = true,
IsLink = false,
MenuIcon = "international",
OrderNum = 96,
IsDeleted = false
};
entities.Add(erp);
//供应商定义
MenuAggregateRoot supplier = new MenuAggregateRoot(_guidGenerator.Create(), erp.Id)
{
MenuName = "供应商定义",
PermissionCode = "erp:supplier:list",
MenuType = MenuTypeEnum.Menu,
Router = "supplier",
IsShow = true,
IsLink = false,
IsCache = true,
Component = "erp/supplier/index",
MenuIcon = "education",
OrderNum = 100,
IsDeleted = false
};
entities.Add(supplier);
MenuAggregateRoot supplierQuery = new MenuAggregateRoot(_guidGenerator.Create(), supplier.Id)
{
MenuName = "供应商查询",
PermissionCode = "erp:supplier:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(supplierQuery);
MenuAggregateRoot supplierAdd = new MenuAggregateRoot(_guidGenerator.Create(), supplier.Id)
{
MenuName = "供应商新增",
PermissionCode = "erp:supplier:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(supplierAdd);
MenuAggregateRoot supplierEdit = new MenuAggregateRoot(_guidGenerator.Create(), supplier.Id)
{
MenuName = "供应商修改",
PermissionCode = "erp:supplier:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(supplierEdit);
MenuAggregateRoot supplierRemove = new MenuAggregateRoot(_guidGenerator.Create(), supplier.Id)
{
MenuName = "供应商删除",
PermissionCode = "erp:supplier:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(supplierRemove);
//仓库定义
MenuAggregateRoot warehouse = new MenuAggregateRoot(_guidGenerator.Create(), erp.Id)
{
MenuName = "仓库定义",
PermissionCode = "erp:warehouse:list",
MenuType = MenuTypeEnum.Menu,
Router = "warehouse",
IsShow = true,
IsLink = false,
IsCache = true,
Component = "erp/warehouse/index",
MenuIcon = "education",
OrderNum = 100,
IsDeleted = false
};
entities.Add(warehouse);
MenuAggregateRoot warehouseQuery = new MenuAggregateRoot(_guidGenerator.Create(), warehouse.Id)
{
MenuName = "仓库查询",
PermissionCode = "erp:warehouse:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = warehouse.Id,
IsDeleted = false
};
entities.Add(warehouseQuery);
MenuAggregateRoot warehouseAdd = new MenuAggregateRoot(_guidGenerator.Create(), warehouse.Id)
{
MenuName = "仓库新增",
PermissionCode = "erp:warehouse:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(warehouseAdd);
MenuAggregateRoot warehouseEdit = new MenuAggregateRoot(_guidGenerator.Create(), warehouse.Id)
{
MenuName = "仓库修改",
PermissionCode = "erp:warehouse:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(warehouseEdit);
MenuAggregateRoot warehouseRemove = new MenuAggregateRoot(_guidGenerator.Create(), warehouse.Id)
{
MenuName = "仓库删除",
PermissionCode = "erp:warehouse:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(warehouseRemove);
//单位定义
MenuAggregateRoot unit = new MenuAggregateRoot(_guidGenerator.Create(), erp.Id)
{
MenuName = "单位定义",
PermissionCode = "erp:unit:list",
MenuType = MenuTypeEnum.Menu,
Router = "unit",
IsShow = true,
IsLink = false,
IsCache = true,
Component = "erp/unit/index",
MenuIcon = "education",
OrderNum = 100,
IsDeleted = false
};
entities.Add(unit);
MenuAggregateRoot unitQuery = new MenuAggregateRoot(_guidGenerator.Create(), unit.Id)
{
MenuName = "单位查询",
PermissionCode = "erp:unit:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(unitQuery);
MenuAggregateRoot unitAdd = new MenuAggregateRoot(_guidGenerator.Create(), unit.Id)
{
MenuName = "单位新增",
PermissionCode = "erp:unit:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(unitAdd);
MenuAggregateRoot unitEdit = new MenuAggregateRoot(_guidGenerator.Create(), unit.Id)
{
MenuName = "单位修改",
PermissionCode = "erp:unit:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(unitEdit);
MenuAggregateRoot unitRemove = new MenuAggregateRoot(_guidGenerator.Create(), unit.Id)
{
MenuName = "单位删除",
PermissionCode = "erp:unit:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
IsDeleted = false
};
entities.Add(unitRemove);
//物料定义
MenuAggregateRoot material = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "物料定义",
PermissionCode = "erp:material:list",
MenuType = MenuTypeEnum.Menu,
Router = "material",
IsShow = true,
IsLink = false,
IsCache = true,
Component = "erp/material/index",
MenuIcon = "education",
OrderNum = 100,
ParentId = erp.Id,
IsDeleted = false
};
entities.Add(material);
MenuAggregateRoot materialQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "物料查询",
PermissionCode = "erp:material:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = material.Id,
IsDeleted = false
};
entities.Add(materialQuery);
MenuAggregateRoot materialAdd = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "物料新增",
PermissionCode = "erp:material:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = material.Id,
IsDeleted = false
};
entities.Add(materialAdd);
MenuAggregateRoot materialEdit = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "物料修改",
PermissionCode = "erp:material:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = material.Id,
IsDeleted = false
};
entities.Add(materialEdit);
MenuAggregateRoot materialRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "物料删除",
PermissionCode = "erp:material:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = material.Id,
IsDeleted = false
};
entities.Add(materialRemove);
//采购订单
MenuAggregateRoot purchase = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "采购订单",
PermissionCode = "erp:purchase:list",
MenuType = MenuTypeEnum.Menu,
Router = "purchase",
IsShow = true,
IsLink = false,
IsCache = true,
Component = "erp/purchase/index",
MenuIcon = "education",
OrderNum = 100,
ParentId = erp.Id,
IsDeleted = false
};
entities.Add(purchase);
MenuAggregateRoot purchaseQuery = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "采购订单查询",
PermissionCode = "erp:purchase:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = purchase.Id,
IsDeleted = false
};
entities.Add(purchaseQuery);
MenuAggregateRoot purchaseAdd = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "采购订单新增",
PermissionCode = "erp:purchase:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = purchase.Id,
IsDeleted = false
};
entities.Add(purchaseAdd);
MenuAggregateRoot purchaseEdit = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "采购订单修改",
PermissionCode = "erp:purchase:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = purchase.Id,
IsDeleted = false
};
entities.Add(purchaseEdit);
MenuAggregateRoot purchaseRemove = new MenuAggregateRoot(_guidGenerator.Create())
{
MenuName = "采购订单删除",
PermissionCode = "erp:purchase:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = purchase.Id,
IsDeleted = false
};
entities.Add(purchaseRemove);
// //ERP
// MenuAggregateRoot erp = new MenuAggregateRoot(_guidGenerator.Create())
// {
// MenuName = "ERP(待更新)",
// MenuType = MenuTypeEnum.Catalogue,
// Router = "/erp",
// IsShow = true,
// IsLink = false,
// MenuIcon = "international",
// OrderNum = 96,
// IsDeleted = false
// };
// entities.Add(erp);
//
//
//
// //供应商定义
// MenuAggregateRoot supplier = new MenuAggregateRoot(_guidGenerator.Create(), erp.Id)
// {
// MenuName = "供应商定义",
// PermissionCode = "erp:supplier:list",
// MenuType = MenuTypeEnum.Menu,
// Router = "supplier",
// IsShow = true,
// IsLink = false,
// IsCache = true,
// Component = "erp/supplier/index",
// MenuIcon = "education",
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(supplier);
//
// MenuAggregateRoot supplierQuery = new MenuAggregateRoot(_guidGenerator.Create(), supplier.Id)
// {
// MenuName = "供应商查询",
// PermissionCode = "erp:supplier:query",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(supplierQuery);
//
// MenuAggregateRoot supplierAdd = new MenuAggregateRoot(_guidGenerator.Create(), supplier.Id)
// {
// MenuName = "供应商新增",
// PermissionCode = "erp:supplier:add",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
//
// IsDeleted = false
// };
// entities.Add(supplierAdd);
//
// MenuAggregateRoot supplierEdit = new MenuAggregateRoot(_guidGenerator.Create(), supplier.Id)
// {
// MenuName = "供应商修改",
// PermissionCode = "erp:supplier:edit",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(supplierEdit);
//
// MenuAggregateRoot supplierRemove = new MenuAggregateRoot(_guidGenerator.Create(), supplier.Id)
// {
// MenuName = "供应商删除",
// PermissionCode = "erp:supplier:remove",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(supplierRemove);
//
//
// //仓库定义
// MenuAggregateRoot warehouse = new MenuAggregateRoot(_guidGenerator.Create(), erp.Id)
// {
// MenuName = "仓库定义",
// PermissionCode = "erp:warehouse:list",
// MenuType = MenuTypeEnum.Menu,
// Router = "warehouse",
// IsShow = true,
// IsLink = false,
// IsCache = true,
// Component = "erp/warehouse/index",
// MenuIcon = "education",
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(warehouse);
//
// MenuAggregateRoot warehouseQuery = new MenuAggregateRoot(_guidGenerator.Create(), warehouse.Id)
// {
// MenuName = "仓库查询",
// PermissionCode = "erp:warehouse:query",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = warehouse.Id,
// IsDeleted = false
// };
// entities.Add(warehouseQuery);
//
// MenuAggregateRoot warehouseAdd = new MenuAggregateRoot(_guidGenerator.Create(), warehouse.Id)
// {
// MenuName = "仓库新增",
// PermissionCode = "erp:warehouse:add",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(warehouseAdd);
//
// MenuAggregateRoot warehouseEdit = new MenuAggregateRoot(_guidGenerator.Create(), warehouse.Id)
// {
// MenuName = "仓库修改",
// PermissionCode = "erp:warehouse:edit",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(warehouseEdit);
//
// MenuAggregateRoot warehouseRemove = new MenuAggregateRoot(_guidGenerator.Create(), warehouse.Id)
// {
// MenuName = "仓库删除",
// PermissionCode = "erp:warehouse:remove",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(warehouseRemove);
//
//
// //单位定义
// MenuAggregateRoot unit = new MenuAggregateRoot(_guidGenerator.Create(), erp.Id)
// {
// MenuName = "单位定义",
// PermissionCode = "erp:unit:list",
// MenuType = MenuTypeEnum.Menu,
// Router = "unit",
// IsShow = true,
// IsLink = false,
// IsCache = true,
// Component = "erp/unit/index",
// MenuIcon = "education",
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(unit);
//
// MenuAggregateRoot unitQuery = new MenuAggregateRoot(_guidGenerator.Create(), unit.Id)
// {
// MenuName = "单位查询",
// PermissionCode = "erp:unit:query",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(unitQuery);
//
// MenuAggregateRoot unitAdd = new MenuAggregateRoot(_guidGenerator.Create(), unit.Id)
// {
// MenuName = "单位新增",
// PermissionCode = "erp:unit:add",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(unitAdd);
//
// MenuAggregateRoot unitEdit = new MenuAggregateRoot(_guidGenerator.Create(), unit.Id)
// {
// MenuName = "单位修改",
// PermissionCode = "erp:unit:edit",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(unitEdit);
//
// MenuAggregateRoot unitRemove = new MenuAggregateRoot(_guidGenerator.Create(), unit.Id)
// {
// MenuName = "单位删除",
// PermissionCode = "erp:unit:remove",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// IsDeleted = false
// };
// entities.Add(unitRemove);
//
//
// //物料定义
// MenuAggregateRoot material = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "物料定义",
// PermissionCode = "erp:material:list",
// MenuType = MenuTypeEnum.Menu,
// Router = "material",
// IsShow = true,
// IsLink = false,
// IsCache = true,
// Component = "erp/material/index",
// MenuIcon = "education",
// OrderNum = 100,
// ParentId = erp.Id,
// IsDeleted = false
// };
// entities.Add(material);
//
// MenuAggregateRoot materialQuery = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "物料查询",
// PermissionCode = "erp:material:query",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = material.Id,
// IsDeleted = false
// };
// entities.Add(materialQuery);
//
// MenuAggregateRoot materialAdd = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "物料新增",
// PermissionCode = "erp:material:add",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = material.Id,
// IsDeleted = false
// };
// entities.Add(materialAdd);
//
// MenuAggregateRoot materialEdit = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "物料修改",
// PermissionCode = "erp:material:edit",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = material.Id,
// IsDeleted = false
// };
// entities.Add(materialEdit);
//
// MenuAggregateRoot materialRemove = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "物料删除",
// PermissionCode = "erp:material:remove",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = material.Id,
// IsDeleted = false
// };
// entities.Add(materialRemove);
//
//
// //采购订单
// MenuAggregateRoot purchase = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "采购订单",
// PermissionCode = "erp:purchase:list",
// MenuType = MenuTypeEnum.Menu,
// Router = "purchase",
// IsShow = true,
// IsLink = false,
// IsCache = true,
// Component = "erp/purchase/index",
// MenuIcon = "education",
// OrderNum = 100,
// ParentId = erp.Id,
// IsDeleted = false
// };
// entities.Add(purchase);
//
// MenuAggregateRoot purchaseQuery = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "采购订单查询",
// PermissionCode = "erp:purchase:query",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = purchase.Id,
// IsDeleted = false
// };
// entities.Add(purchaseQuery);
//
// MenuAggregateRoot purchaseAdd = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "采购订单新增",
// PermissionCode = "erp:purchase:add",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = purchase.Id,
// IsDeleted = false
// };
// entities.Add(purchaseAdd);
//
// MenuAggregateRoot purchaseEdit = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "采购订单修改",
// PermissionCode = "erp:purchase:edit",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = purchase.Id,
// IsDeleted = false
// };
// entities.Add(purchaseEdit);
//
// MenuAggregateRoot purchaseRemove = new MenuAggregateRoot(_guidGenerator.Create())
// {
//
// MenuName = "采购订单删除",
// PermissionCode = "erp:purchase:remove",
// MenuType = MenuTypeEnum.Component,
// OrderNum = 100,
// ParentId = purchase.Id,
// IsDeleted = false
// };
// entities.Add(purchaseRemove);
@@ -564,7 +564,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
MenuName = "Yi框架",
MenuType = MenuTypeEnum.Catalogue,
Router = "https://gitee.com/ccnetcore/yi",
Router = "https://ccnetcore.com",
IsShow = true,
IsLink = true,
MenuIcon = "guide",
@@ -1317,6 +1317,8 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
{
m.IsDeleted = false;
m.State = true;
m.MenuSource = MenuSourceEnum.Ruoyi;
m.IsShow = true;
});
var p = entities.GroupBy(x => x.Id);

View File

@@ -0,0 +1,24 @@
@echo on
set SERVER_USER=root
set SERVER_IP=ccnetcore.com
set FILE_PATH=publish_demo_02.zip
set REMOTE_PATH=/home/yi/build/publish_demo_02.zip
set REMOTE_COMMAND="cd /home/yi/demo-net8&&pwd&&unzip -o /home/yi/build/publish_demo_02.zip -d ./&&./start.sh"
set sevenzip_Path="D:\Program Files\7-Zip\7z.exe"
echo start
echo 1-build-start
:: dotnet publish
echo 1-build-end
echo 2-zip-start
%sevenzip_Path% a ./publish_demo_02.zip ./src/Yi.Abp.Web/bin/Release/net8.0/linux-x64/publish/*
:: tar -cvf publish_demo_02.zip -C "dist" "*"
echo 2-zip-end
echo 3-publish-start
scp %FILE_PATH% %SERVER_USER%@%SERVER_IP%:%REMOTE_PATH%
ssh %SERVER_USER%@%SERVER_IP% %REMOTE_COMMAND%
echo 3-publish-end
echo end
pause

View File

@@ -13,22 +13,24 @@
},
//数据库类型列表
"DbList": [ "Sqlite", "Mysql", "Sqlserver", "Oracle" ],
"DbList": [ "Sqlite", "Mysql", "Sqlserver", "Oracle", "PostgreSQL" ],
"DbConnOptions": {
"Url": "DataSource=yi-abp-dev.db",
"DbType": "Sqlite",
"EnabledReadWrite": false,
"EnabledCodeFirst": true,
"EnabledSqlLog": true,
"EnabledDbSeed": true
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
// "server=[xxxx];port=3306;database=[xxxx];user id=[xxxx];password=[xxxx]", //Mysql
// "Data Source=[xxxx];Initial Catalog=[xxxx];User ID=[xxxx];password=[xxxx]" //Sqlserver
//]
},
"DbConnOptions": {
"Url": "DataSource=yi-abp-dev.db",
"DbType": "Sqlite",
"EnabledReadWrite": false,
"EnabledCodeFirst": true,
"EnabledSqlLog": true,
"EnabledDbSeed": true,
"EnableUnderLine": false // 启用驼峰转下划线
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
// "server=[xxxx];port=3306;database=[xxxx];user id=[xxxx];password=[xxxx]", //Mysql
// "Data Source=[xxxx];Initial Catalog=[xxxx];User ID=[xxxx];password=[xxxx]" //Sqlserver
// "HOST=[xxxx];PORT=5432;DATABASE=[xxxx];USERID=[xxxx];PASSWORD=[xxxx]" //PostgreSQL
//]
},
//鉴权
"JwtOptions": {

View File

@@ -39,6 +39,17 @@ namespace Yi.Abp.Application.Services
return name ?? "HelloWord";
}
/// <summary>
/// 异常处理
/// </summary>
/// <returns></returns>
[HttpGet("error")]
public string GetError()
{
throw new UserFriendlyException("业务异常");
throw new Exception("系统异常");
}
/// <summary>
/// SqlSugar
/// </summary>

View File

@@ -3,6 +3,8 @@ using System.Text;
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Converters;
@@ -11,6 +13,7 @@ using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Auditing;
using Volo.Abp.Autofac;
@@ -25,6 +28,7 @@ using Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
using Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
using Yi.Framework.AspNetCore.Microsoft.AspNetCore.Builder;
using Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection;
using Yi.Framework.AspNetCore.UnifyResult;
using Yi.Framework.Bbs.Application;
using Yi.Framework.Bbs.Application.Extensions;
using Yi.Framework.ChatHub.Application;
@@ -68,6 +72,10 @@ namespace Yi.Abp.Web
optios.AlwaysLogSelectors.Add(x => Task.FromResult(true));
});
//采用furion格式的规范化api默认不开启使用abp优雅的方式
//你没看错。。。
//service.AddFurionUnifyResultApi();
//配置错误处理显示详情
Configure<AbpExceptionHandlingOptions>(options => { options.SendExceptionsDetailsToClients = true; });
@@ -299,6 +307,8 @@ namespace Yi.Abp.Web
app.UseDefaultFiles();
app.UseDirectoryBrowser("/api/app/wwwroot");
// app.Properties.Add("_AbpExceptionHandlingMiddleware_Added",false);
//工作单元
app.UseUnitOfWork();

View File

@@ -17,24 +17,26 @@
},
//数据库类型列表
"DbList": [ "Sqlite", "Mysql", "Sqlserver", "Oracle" ],
"DbList": [ "Sqlite", "Mysql", "Sqlserver", "Oracle", "PostgreSQL" ],
"DbConnOptions": {
"Url": "DataSource=yi-abp-dev.db",
"DbType": "Sqlite",
"EnabledReadWrite": false,
"EnabledCodeFirst": true,
"EnabledSqlLog": true,
"EnabledDbSeed": true,
//SAAS多租户
"EnabledSaasMultiTenancy": false
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
// "server=[xxxx];port=3306;database=[xxxx];user id=[xxxx];password=[xxxx]", //Mysql
// "Data Source=[xxxx];Initial Catalog=[xxxx];User ID=[xxxx];password=[xxxx]" //Sqlserver
//]
},
"DbConnOptions": {
"Url": "DataSource=yi-abp-dev.db",
"DbType": "Sqlite",
"EnabledReadWrite": false,
"EnabledCodeFirst": true,
"EnabledSqlLog": true,
"EnabledDbSeed": true,
"EnableUnderLine": false, // 启用驼峰转下划线
//SAAS多租户
"EnabledSaasMultiTenancy": true
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
// "server=[xxxx];port=3306;database=[xxxx];user id=[xxxx];password=[xxxx]", //Mysql
// "Data Source=[xxxx];Initial Catalog=[xxxx];User ID=[xxxx];password=[xxxx]" //Sqlserver
// "HOST=[xxxx];PORT=5432;DATABASE=[xxxx];USERID=[xxxx];PASSWORD=[xxxx]" //PostgreSQL
//]
},
//redis使用freeesql参数在“FreeSqlOptions的ConnectionStringBuilder中”
"Redis": {

View File

@@ -13,22 +13,24 @@
},
//数据库类型列表
"DbList": [ "Sqlite", "Mysql", "Sqlserver", "Oracle" ],
"DbList": [ "Sqlite", "Mysql", "Sqlserver", "Oracle", "PostgreSQL" ],
"DbConnOptions": {
"Url": "DataSource=yi-abp-dev.db",
"DbType": "Sqlite",
"EnabledReadWrite": false,
"EnabledCodeFirst": true,
"EnabledSqlLog": true,
"EnabledDbSeed": true
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
// "server=[xxxx];port=3306;database=[xxxx];user id=[xxxx];password=[xxxx]", //Mysql
// "Data Source=[xxxx];Initial Catalog=[xxxx];User ID=[xxxx];password=[xxxx]" //Sqlserver
//]
},
"DbConnOptions": {
"Url": "DataSource=yi-abp-dev.db",
"DbType": "Sqlite",
"EnabledReadWrite": false,
"EnabledCodeFirst": true,
"EnabledSqlLog": true,
"EnabledDbSeed": true,
"EnableUnderLine": false // 启用驼峰转下划线
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
// "server=[xxxx];port=3306;database=[xxxx];user id=[xxxx];password=[xxxx]", //Mysql
// "Data Source=[xxxx];Initial Catalog=[xxxx];User ID=[xxxx];password=[xxxx]" //Sqlserver
// "HOST=[xxxx];PORT=5432;DATABASE=[xxxx];USERID=[xxxx];PASSWORD=[xxxx]" //PostgreSQL
//]
},
//鉴权
"JwtOptions": {

View File

@@ -55,7 +55,7 @@ namespace Yi.Framework.Rbac.Test.System
}
catch (UserFriendlyException ex)
{
ex.Message.ShouldBe(UserConst.User_Exist);
ex.Message.ShouldBe(UserConst.Exist);
}
}

View File

@@ -13,22 +13,24 @@
},
//数据库类型列表
"DbList": [ "Sqlite", "Mysql", "Sqlserver", "Oracle" ],
"DbList": [ "Sqlite", "Mysql", "Sqlserver", "Oracle", "PostgreSQL" ],
"DbConnOptions": {
"Url": "DataSource=yi-rbac-test.db",
"DbType": "Sqlite",
"EnabledReadWrite": false,
"EnabledCodeFirst": true,
"EnabledSqlLog": true,
"EnabledDbSeed": true
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
// "server=[xxxx];port=3306;database=[xxxx];user id=[xxxx];password=[xxxx]", //Mysql
// "Data Source=[xxxx];Initial Catalog=[xxxx];User ID=[xxxx];password=[xxxx]" //Sqlserver
//]
},
"DbConnOptions": {
"Url": "DataSource=yi-rbac-test.db",
"DbType": "Sqlite",
"EnabledReadWrite": false,
"EnabledCodeFirst": true,
"EnabledSqlLog": true,
"EnabledDbSeed": true,
"EnableUnderLine": false // 启用驼峰转下划线
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
// "server=[xxxx];port=3306;database=[xxxx];user id=[xxxx];password=[xxxx]", //Mysql
// "Data Source=[xxxx];Initial Catalog=[xxxx];User ID=[xxxx];password=[xxxx]" //Sqlserver
// "HOST=[xxxx];PORT=5432;DATABASE=[xxxx];USERID=[xxxx];PASSWORD=[xxxx]" //PostgreSQL
//]
},
//鉴权
"JwtOptions": {

View File

@@ -8,20 +8,18 @@ namespace Yi.Abp.Tool.Domain
public class NugetCrawlerManager : ITransientDependency
{
private const string NugetVersionUrl = "https://www.nuget.org/packages/Yi.Abp.Tool#versions-body-tab";
public NugetCrawlerManager(IDistributedCache<NugetResult> cache)
{
//缓存设置1分钟获取一次结果
this.NugetResult = cache.GetOrAdd("NugetResult", () =>
{
return InitData();
}, () =>
{
var options = new DistributedCacheEntryOptions();
options.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(1);
return options;
})!;
this.NugetResult = cache.GetOrAdd("NugetResult", () => { return InitData(); }, () =>
{
var options = new DistributedCacheEntryOptions();
options.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(1);
return options;
})!;
}
private HtmlDocument HtmlDoc { get; set; }
private NugetResult NugetResult { get; set; } = new NugetResult();
@@ -56,11 +54,12 @@ namespace Yi.Abp.Tool.Domain
foreach (var tr in trDoc)
{
var version = tr.ChildNodes.Where(x => x.Name == "td").First().ChildNodes.Where(x => x.Name == "a").First().GetAttributes("title").First().Value;
var version = tr.ChildNodes.Where(x => x.Name == "td").First().ChildNodes.Where(x => x.Name == "a")
.First().GetAttributes("title").First().Value;
versions.Add(version);
}
return versions;
}
@@ -70,8 +69,17 @@ namespace Yi.Abp.Tool.Domain
/// <returns></returns>
private long GetDownloadNumber()
{
var spanDoc = HtmlDoc.DocumentNode.SelectNodes("//*[@id=\"skippedToContent\"]/section/div/aside/div[1]/div[2]/div[1]/span[2]");
var spanDoc =
HtmlDoc.DocumentNode.SelectNodes(
"//*[@id=\"skippedToContent\"]/section/div/aside/div[1]/div[2]/div[1]/span[2]");
var downLoadNumber = spanDoc.First().InnerText;
if (downLoadNumber.Contains("K"))
{
downLoadNumber = downLoadNumber.TrimEnd('K');
return (long)Math.Round(decimal.Parse(downLoadNumber) * 1000);
}
return long.Parse(downLoadNumber);
}
}
@@ -82,4 +90,4 @@ namespace Yi.Abp.Tool.Domain
public long DownloadNumber { get; set; }
public List<string> Versions { get; set; }
}
}
}

View File

@@ -7,4 +7,7 @@ VITE_APP_URL="http://localhost:19001/api/app"
# ws/开发环境
VITE_APP_BASE_WS = '/dev-ws'
VITE_APP_BASE_URL_WS="http://localhost:19001/hub"
VITE_APP_BASE_URL_WS="http://localhost:19001/hub"
# 是否开启ICP备案模式
VITE_APP_ICP = false

View File

@@ -6,4 +6,7 @@ VITE_APP_URL="http://ccnetcore.com:19001/api/app"
# ws
VITE_APP_BASE_WS = '/prod-ws'
VITE_APP_BASE_URL_WS="http://ccnetcore.com:19001/hub"
VITE_APP_BASE_URL_WS="http://ccnetcore.com:19001/hub"
# 是否开启ICP备案模式
VITE_APP_ICP = false

View File

@@ -4,20 +4,22 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>意社区</title>
<link rel="stylesheet" href="/src/assets/loading.css" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5453339688995325"
crossorigin="anonymous"></script>
</head>
<body>
<div id="Loading">
<div class="loader JS_on">
<span class="binary"></span>
<span class="binary"></span>
<span class="getting-there">意社区很大,你要等一下...</span>
</div>
</div>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
<title>.Net意社区</title>
<!-- 为了icp备案-->
<!-- <title>个人成果展示</title>-->
<link rel="stylesheet" href="/src/assets/loading.css" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5453339688995325"
crossorigin="anonymous"></script>
</head>
<body>
<div id="Loading">
<div class="loader JS_on">
<span class="binary"></span>
<span class="binary"></span>
<span class="getting-there">意社区很大,你要等一下...</span>
</div>
</div>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@@ -18,6 +18,24 @@ export function login(username, password, code, uuid) {
});
}
//找回密码
export function retrievePassword(password, phone, code, uuid) {
const data = {
password,
phone,
code,
uuid,
};
return request({
url: "/account/retrieve-password",
headers: {
isToken: false,
},
method: "post",
data: data,
});
}
// 注册方法
export function register(userName, password, phone, code, uuid) {
const data = {
@@ -76,3 +94,15 @@ export function getCodePhone(phone) {
data: { phone },
});
}
// 获取短信验证码-为了重置密码
export function getCodePhoneForRetrievePassword(phone) {
return request({
url: "/account/captcha-phone/repassword",
headers: {
isToken: false,
},
method: "post",
timeout: 20000,
data: { phone },
});
}

Some files were not shown because too many files have changed in this diff Show More