mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-03-03 00:00:58 +08:00
Compare commits
222 Commits
AbpUnitOfW
...
digital-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f203c53e19 | ||
|
|
8a9a0c8396 | ||
|
|
813b08bf1c | ||
|
|
bce9b58265 | ||
|
|
85223629c1 | ||
|
|
9a73789788 | ||
|
|
25929483c3 | ||
|
|
0e90c54dbb | ||
|
|
5dea4ab18c | ||
|
|
7187397687 | ||
|
|
bf8454a963 | ||
|
|
2b2ac9b924 | ||
|
|
78b874936c | ||
|
|
b9866af6cd | ||
|
|
350e4a5753 | ||
|
|
2544d03434 | ||
|
|
d1c1eed52e | ||
|
|
2329cfd1da | ||
|
|
9960c63f59 | ||
|
|
680f9ae246 | ||
|
|
7994e66283 | ||
|
|
2c9d344b76 | ||
|
|
6a9e28700b | ||
|
|
811e5c1a8c | ||
|
|
545cef34a9 | ||
|
|
950c89a8bc | ||
|
|
7b5bc0fe3e | ||
|
|
e05514bc41 | ||
|
|
438abf6cea | ||
|
|
5d5ebb559b | ||
|
|
3be5675828 | ||
|
|
6482218a68 | ||
|
|
8d6824b03d | ||
|
|
69ab65dbc6 | ||
|
|
4b0b0e4451 | ||
|
|
5d21fd0f7c | ||
|
|
0184015ba8 | ||
|
|
ffb329a0d9 | ||
|
|
03a712fcfe | ||
|
|
652c2b6fd0 | ||
|
|
f0093499d1 | ||
|
|
c00ada5aee | ||
|
|
6c409bfa00 | ||
|
|
1c7637d28b | ||
|
|
48dc89c43d | ||
|
|
b60cc50508 | ||
|
|
4e66762036 | ||
|
|
ff4e1dd182 | ||
|
|
36442072ba | ||
|
|
ea134f52be | ||
|
|
fe97ba1c19 | ||
|
|
2cd8b73aa3 | ||
|
|
07a5b69511 | ||
|
|
da0e207b44 | ||
|
|
fb0edc0ee0 | ||
|
|
8a26a4aeec | ||
|
|
723ce1bd5d | ||
|
|
8408b39fbb | ||
|
|
5dfaf75440 | ||
|
|
bb3b3702e1 | ||
|
|
81138fcaef | ||
|
|
6be5398114 | ||
|
|
365ae8ef0a | ||
|
|
3932b24fda | ||
|
|
f44737216f | ||
|
|
e4180a0c1a | ||
|
|
356938d6d3 | ||
|
|
1090907178 | ||
|
|
da2f7073f9 | ||
|
|
57fe0e702c | ||
|
|
90d4a98e1e | ||
|
|
d047f8aa32 | ||
|
|
f656ec32c1 | ||
|
|
3415543175 | ||
|
|
4191bc86ef | ||
|
|
1cc0ef916f | ||
|
|
fdbee4562a | ||
|
|
5d793344cd | ||
|
|
559a45c917 | ||
|
|
cadbc09846 | ||
|
|
bcaca0b782 | ||
|
|
5cee7319c6 | ||
|
|
e960db0d3e | ||
|
|
eb2c05e9df | ||
|
|
353a6b9d0c | ||
|
|
5d2d269f11 | ||
|
|
9acb157fae | ||
|
|
4198b53996 | ||
|
|
d8286fb005 | ||
|
|
a7bf5e8873 | ||
|
|
fdec9ed6b8 | ||
|
|
a2e2072634 | ||
|
|
84cd83894b | ||
|
|
32e4145927 | ||
|
|
18dd177961 | ||
|
|
e94c65d24a | ||
|
|
e72c0d4480 | ||
|
|
1a7f1c3d15 | ||
|
|
ace5374813 | ||
|
|
41f91ea12d | ||
|
|
91bf5f93cd | ||
|
|
9445fa8005 | ||
|
|
6b491d1246 | ||
|
|
644045b307 | ||
|
|
0bf53a1c0d | ||
|
|
6b47ae232d | ||
|
|
536c3cc56b | ||
|
|
b75a8cb60d | ||
|
|
05ca1aa224 | ||
|
|
02d45bb6a7 | ||
|
|
8e805a4cf8 | ||
|
|
43c4c03832 | ||
|
|
bf2bcd1395 | ||
|
|
f9217dc066 | ||
|
|
dad4ca4ab4 | ||
|
|
b282ee8273 | ||
|
|
3dcb7d0a39 | ||
|
|
6703897fb1 | ||
|
|
eef2ed0d64 | ||
|
|
17bc4ade84 | ||
|
|
aea0896356 | ||
|
|
7c13ed6497 | ||
|
|
93dea4fa46 | ||
|
|
83fb93da11 | ||
|
|
3fbaffe9a2 | ||
|
|
3b93e8b8ec | ||
|
|
24cf087320 | ||
|
|
ae82a2d1cf | ||
|
|
2412bc1da4 | ||
|
|
42b00515eb | ||
|
|
f3c5d0862b | ||
|
|
e832921edf | ||
|
|
0c0ead26c0 | ||
|
|
f9a018638b | ||
|
|
d5ca8ddf1e | ||
|
|
650c29e75a | ||
|
|
ed5c20c612 | ||
|
|
49f1d1a8fa | ||
|
|
a87d6345c2 | ||
|
|
d83db53acb | ||
|
|
c944bd3b0e | ||
|
|
751cc3cadb | ||
|
|
80fe1116a8 | ||
|
|
81f9fd7473 | ||
|
|
9aaa88ef51 | ||
|
|
e2091eb986 | ||
|
|
22a8703978 | ||
|
|
1468a7b878 | ||
|
|
fe7211860f | ||
|
|
fddf80e74a | ||
|
|
5054391f6b | ||
|
|
5e096a277c | ||
|
|
ef2d00a254 | ||
|
|
d38159f68b | ||
|
|
dd29c9a2fa | ||
|
|
ca1b8a728d | ||
|
|
6b647cf4ea | ||
|
|
a21f2342d8 | ||
|
|
0e6f79c28e | ||
|
|
ab6563899c | ||
|
|
894d4eb051 | ||
|
|
f82122edf0 | ||
|
|
8d9c5bb762 | ||
|
|
76d94c0bc9 | ||
|
|
7a916fc78e | ||
|
|
73db2a202a | ||
|
|
31dceec787 | ||
|
|
a2106bba3e | ||
|
|
f9890bdc7f | ||
|
|
b321283f99 | ||
|
|
505e4b6586 | ||
|
|
b2efd065be | ||
|
|
050af30acb | ||
|
|
8a0c0de8a1 | ||
|
|
677dca2231 | ||
|
|
47ca08e432 | ||
|
|
8c940126b5 | ||
|
|
1cb396aa14 | ||
|
|
a47d271a33 | ||
|
|
363be13d12 | ||
|
|
3bc044e148 | ||
|
|
b8e6dfcd99 | ||
|
|
4b320c2af2 | ||
|
|
dc28d701ba | ||
|
|
71b7b7cc79 | ||
|
|
77c423f421 | ||
|
|
f6be4ad7ac | ||
|
|
8a157ba472 | ||
|
|
f6cbe899c6 | ||
|
|
7598c8319f | ||
|
|
a798f36529 | ||
|
|
779e84213e | ||
|
|
254975fcd3 | ||
|
|
e5773df1ab | ||
|
|
c2290f95cf | ||
|
|
6eb72c0303 | ||
|
|
c4e79e46cf | ||
|
|
4fadba27dc | ||
|
|
7eab4dd5b1 | ||
|
|
0e6d380b7e | ||
|
|
1a73f7bef3 | ||
|
|
a518e7b210 | ||
|
|
e2dae1c4ab | ||
|
|
0d00f91b31 | ||
|
|
d3b87e8984 | ||
|
|
fcaad5c6cc | ||
|
|
8ca741792a | ||
|
|
0f687a7e34 | ||
|
|
736995c35b | ||
|
|
4ae548cc5b | ||
|
|
942868f17f | ||
|
|
8a57bf52f9 | ||
|
|
260d87c97e | ||
|
|
cb1bac25a3 | ||
|
|
ef20ef7014 | ||
|
|
b88cad7b80 | ||
|
|
3383e86064 | ||
|
|
a0ef3af155 | ||
|
|
e8fcab4c6b | ||
|
|
1c6a795061 | ||
|
|
36246c2945 | ||
|
|
7af54f600f |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -269,8 +269,10 @@ dist
|
||||
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Production.json
|
||||
/Yi.Abp.Net8/test/Yi.Abp.Test/appsettings.Development.json
|
||||
/Yi.Abp.Net8/test/Yi.Abp.Test/appsettings.Production.json
|
||||
/Yi.Abp.Net8/tool/Yi.Abp.Tool.Web/appsettings.Development.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
|
||||
|
||||
package-lock.json
|
||||
|
||||
@@ -80,20 +80,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.AuditLogging.S
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.AspNetCore.Authentication.OAuth", "framework\Yi.Framework.AspNetCore.Authentication.OAuth\Yi.Framework.AspNetCore.Authentication.OAuth.csproj", "{791AC2FA-50D3-4408-8D68-31DA72F608BE}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{01300F0F-686E-47B3-821D-12424177867B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Web", "sample\Acme.BookStore.Web\Acme.BookStore.Web.csproj", "{576DBC97-4E5D-4444-B65C-F41649A5F8E0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Domain.Shared", "sample\Acme.BookStore.Domain.Shared\Acme.BookStore.Domain.Shared.csproj", "{D7F8BD42-F6A2-4F0A-9212-391B5185A99D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Domain", "sample\Acme.BookStore.Domain\Acme.BookStore.Domain.csproj", "{B615847F-8568-41D1-8B7E-63D61AE69F3D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Application.Contracts", "sample\Acme.BookStore.Application.Contracts\Acme.BookStore.Application.Contracts.csproj", "{20827DB5-5CDE-491A-82E8-3CAB82618C1E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Application", "sample\Acme.BookStore.Application\Acme.BookStore.Application.csproj", "{320273B6-7AE3-42DA-9675-D9AD4928A289}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.SqlSugarCore", "sample\Acme.BookStore.SqlSugarCore\Acme.BookStore.SqlSugarCore.csproj", "{70CCBD89-C0A1-4AC8-9AFA-C86C356DFDD7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Test", "test\Yi.Abp.Test\Yi.Abp.Test.csproj", "{68627BC2-F049-4C69-AD17-81DF9478E8CE}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tenant-management", "tenant-management", "{499A8C71-7892-42D0-A77E-48756E1EFF16}"
|
||||
@@ -170,6 +156,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Abp.Tool.HttpApi.Client"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.SettingManagement.Application", "module\setting-management\Yi.Framework.SettingManagement.Application\Yi.Framework.SettingManagement.Application.csproj", "{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "digital-collectibles", "digital-collectibles", "{B8F76A6B-2EEB-4E64-9F26-D84584E16B9C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.DigitalCollectibles.Application", "module\digital-collectibles\Yi.Framework.DigitalCollectibles.Application\Yi.Framework.DigitalCollectibles.Application.csproj", "{236B88D4-F018-4A5F-A506-7458F2308C70}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.DigitalCollectibles.Application.Contracts", "module\digital-collectibles\Yi.Framework.DigitalCollectibles.Application.Contracts\Yi.Framework.DigitalCollectibles.Application.Contracts.csproj", "{4FE7AC0E-91CC-4DF1-ACA7-ED83483C3F3B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.DigitalCollectibles.Domain", "module\digital-collectibles\Yi.Framework.DigitalCollectibles.Domain\Yi.Framework.DigitalCollectibles.Domain.csproj", "{9B5CAE1A-E062-4C9B-8121-E58FBF69309C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.DigitalCollectibles.Domain.Shared", "module\digital-collectibles\Yi.Framework.DigitalCollectibles.Domain.Shared\Yi.Framework.DigitalCollectibles.Domain.Shared.csproj", "{FFEC9DA6-1A13-480A-AE9E-2BF8763D3061}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.DigitalCollectibles.SqlSugarCore", "module\digital-collectibles\Yi.Framework.DigitalCollectibles.SqlSugarCore\Yi.Framework.DigitalCollectibles.SqlSugarCore.csproj", "{4CE6E4AE-0BA4-4984-A4F1-A9A414B1BB8F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.WeChat.MiniProgram", "framework\Yi.Framework.WeChat.MiniProgram\Yi.Framework.WeChat.MiniProgram.csproj", "{81CEA2ED-917B-41D8-BE0D-39A785B050C0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.BackgroundWorkers.Hangfire", "framework\Yi.Framework.BackgroundWorkers.Hangfire\Yi.Framework.BackgroundWorkers.Hangfire.csproj", "{862CA181-BEE6-4870-82D2-B662E527ED8C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -276,30 +278,6 @@ Global
|
||||
{791AC2FA-50D3-4408-8D68-31DA72F608BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{791AC2FA-50D3-4408-8D68-31DA72F608BE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{576DBC97-4E5D-4444-B65C-F41649A5F8E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{576DBC97-4E5D-4444-B65C-F41649A5F8E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{576DBC97-4E5D-4444-B65C-F41649A5F8E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{576DBC97-4E5D-4444-B65C-F41649A5F8E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D7F8BD42-F6A2-4F0A-9212-391B5185A99D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D7F8BD42-F6A2-4F0A-9212-391B5185A99D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D7F8BD42-F6A2-4F0A-9212-391B5185A99D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D7F8BD42-F6A2-4F0A-9212-391B5185A99D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B615847F-8568-41D1-8B7E-63D61AE69F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B615847F-8568-41D1-8B7E-63D61AE69F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B615847F-8568-41D1-8B7E-63D61AE69F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B615847F-8568-41D1-8B7E-63D61AE69F3D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{20827DB5-5CDE-491A-82E8-3CAB82618C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20827DB5-5CDE-491A-82E8-3CAB82618C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{20827DB5-5CDE-491A-82E8-3CAB82618C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{20827DB5-5CDE-491A-82E8-3CAB82618C1E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{320273B6-7AE3-42DA-9675-D9AD4928A289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{320273B6-7AE3-42DA-9675-D9AD4928A289}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{320273B6-7AE3-42DA-9675-D9AD4928A289}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{320273B6-7AE3-42DA-9675-D9AD4928A289}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{70CCBD89-C0A1-4AC8-9AFA-C86C356DFDD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70CCBD89-C0A1-4AC8-9AFA-C86C356DFDD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70CCBD89-C0A1-4AC8-9AFA-C86C356DFDD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{70CCBD89-C0A1-4AC8-9AFA-C86C356DFDD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{68627BC2-F049-4C69-AD17-81DF9478E8CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{68627BC2-F049-4C69-AD17-81DF9478E8CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{68627BC2-F049-4C69-AD17-81DF9478E8CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -428,6 +406,34 @@ Global
|
||||
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{236B88D4-F018-4A5F-A506-7458F2308C70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{236B88D4-F018-4A5F-A506-7458F2308C70}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{236B88D4-F018-4A5F-A506-7458F2308C70}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{236B88D4-F018-4A5F-A506-7458F2308C70}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4FE7AC0E-91CC-4DF1-ACA7-ED83483C3F3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4FE7AC0E-91CC-4DF1-ACA7-ED83483C3F3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4FE7AC0E-91CC-4DF1-ACA7-ED83483C3F3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4FE7AC0E-91CC-4DF1-ACA7-ED83483C3F3B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9B5CAE1A-E062-4C9B-8121-E58FBF69309C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B5CAE1A-E062-4C9B-8121-E58FBF69309C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B5CAE1A-E062-4C9B-8121-E58FBF69309C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B5CAE1A-E062-4C9B-8121-E58FBF69309C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FFEC9DA6-1A13-480A-AE9E-2BF8763D3061}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FFEC9DA6-1A13-480A-AE9E-2BF8763D3061}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FFEC9DA6-1A13-480A-AE9E-2BF8763D3061}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FFEC9DA6-1A13-480A-AE9E-2BF8763D3061}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4CE6E4AE-0BA4-4984-A4F1-A9A414B1BB8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4CE6E4AE-0BA4-4984-A4F1-A9A414B1BB8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4CE6E4AE-0BA4-4984-A4F1-A9A414B1BB8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4CE6E4AE-0BA4-4984-A4F1-A9A414B1BB8F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81CEA2ED-917B-41D8-BE0D-39A785B050C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{862CA181-BEE6-4870-82D2-B662E527ED8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{862CA181-BEE6-4870-82D2-B662E527ED8C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -461,12 +467,6 @@ Global
|
||||
{73CCF2C4-B9FD-44AB-8D4B-0A421805B094} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
|
||||
{48806510-8E18-4E1E-9BAF-5B97E88C5FC3} = {73CCF2C4-B9FD-44AB-8D4B-0A421805B094}
|
||||
{791AC2FA-50D3-4408-8D68-31DA72F608BE} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
|
||||
{576DBC97-4E5D-4444-B65C-F41649A5F8E0} = {01300F0F-686E-47B3-821D-12424177867B}
|
||||
{D7F8BD42-F6A2-4F0A-9212-391B5185A99D} = {01300F0F-686E-47B3-821D-12424177867B}
|
||||
{B615847F-8568-41D1-8B7E-63D61AE69F3D} = {01300F0F-686E-47B3-821D-12424177867B}
|
||||
{20827DB5-5CDE-491A-82E8-3CAB82618C1E} = {01300F0F-686E-47B3-821D-12424177867B}
|
||||
{320273B6-7AE3-42DA-9675-D9AD4928A289} = {01300F0F-686E-47B3-821D-12424177867B}
|
||||
{70CCBD89-C0A1-4AC8-9AFA-C86C356DFDD7} = {01300F0F-686E-47B3-821D-12424177867B}
|
||||
{68627BC2-F049-4C69-AD17-81DF9478E8CE} = {0D10EEF2-FBAE-4C72-B816-A52823FC299B}
|
||||
{499A8C71-7892-42D0-A77E-48756E1EFF16} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
|
||||
{FA5BBAA1-08DC-472F-BB2C-5314E59D1556} = {499A8C71-7892-42D0-A77E-48756E1EFF16}
|
||||
@@ -503,6 +503,14 @@ Global
|
||||
{4AE84CDE-2A47-4D68-8E93-86193F72E4E8} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
|
||||
{C8F97775-D903-4365-A4FF-3DA97E318CD2} = {084CBEEC-5D37-4716-B9C7-D80D6960DFF4}
|
||||
{2A31D7CB-BDCC-4253-BA73-273B6B5E1956} = {8C68059E-F3B1-4D28-A1C9-A5830F53E5D3}
|
||||
{B8F76A6B-2EEB-4E64-9F26-D84584E16B9C} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
|
||||
{236B88D4-F018-4A5F-A506-7458F2308C70} = {B8F76A6B-2EEB-4E64-9F26-D84584E16B9C}
|
||||
{4FE7AC0E-91CC-4DF1-ACA7-ED83483C3F3B} = {B8F76A6B-2EEB-4E64-9F26-D84584E16B9C}
|
||||
{9B5CAE1A-E062-4C9B-8121-E58FBF69309C} = {B8F76A6B-2EEB-4E64-9F26-D84584E16B9C}
|
||||
{FFEC9DA6-1A13-480A-AE9E-2BF8763D3061} = {B8F76A6B-2EEB-4E64-9F26-D84584E16B9C}
|
||||
{4CE6E4AE-0BA4-4984-A4F1-A9A414B1BB8F} = {B8F76A6B-2EEB-4E64-9F26-D84584E16B9C}
|
||||
{81CEA2ED-917B-41D8-BE0D-39A785B050C0} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
|
||||
{862CA181-BEE6-4870-82D2-B662E527ED8C} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {23D6FBC9-C970-4641-BC1E-2AEA59F51C18}
|
||||
|
||||
@@ -9,84 +9,89 @@ using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Volo.Abp.AspNetCore.Mvc;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Options;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
public static class SwaggerAddExtensions
|
||||
{
|
||||
public static IServiceCollection AddYiSwaggerGen<Program>(this IServiceCollection services, Action<SwaggerGenOptions>? action=null)
|
||||
public static IServiceCollection AddYiSwaggerGen<Program>(this IServiceCollection services,
|
||||
Action<SwaggerGenOptions>? action = null)
|
||||
{
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
var mvcOptions = serviceProvider.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>();
|
||||
|
||||
var mvcSettings = mvcOptions.Value.ConventionalControllers.ConventionalControllerSettings.DistinctBy(x => x.RemoteServiceName);
|
||||
var mvcOptions = services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>().Configure();
|
||||
|
||||
var mvcSettings =
|
||||
mvcOptions.ConventionalControllers.ConventionalControllerSettings.DistinctBy(x => x.RemoteServiceName);
|
||||
|
||||
|
||||
services.AddAbpSwaggerGen(
|
||||
options =>
|
||||
{
|
||||
if (action is not null)
|
||||
options =>
|
||||
{
|
||||
action.Invoke(options);
|
||||
}
|
||||
|
||||
// 配置分组,还需要去重,支持重写,如果外部传入后,将以外部为准
|
||||
foreach (var setting in mvcSettings.OrderBy(x => x.RemoteServiceName))
|
||||
{
|
||||
if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName))
|
||||
if (action is not null)
|
||||
{
|
||||
options.SwaggerDoc(setting.RemoteServiceName, new OpenApiInfo { Title = setting.RemoteServiceName, Version = "v1" });
|
||||
action.Invoke(options);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据分组名称过滤 API 文档
|
||||
options.DocInclusionPredicate((docName, apiDesc) =>
|
||||
{
|
||||
if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
|
||||
// 配置分组,还需要去重,支持重写,如果外部传入后,将以外部为准
|
||||
foreach (var setting in mvcSettings.OrderBy(x => x.RemoteServiceName))
|
||||
{
|
||||
var settingOrNull = mvcSettings.Where(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly).FirstOrDefault();
|
||||
if (settingOrNull is not null)
|
||||
if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName))
|
||||
{
|
||||
return docName == settingOrNull.RemoteServiceName;
|
||||
options.SwaggerDoc(setting.RemoteServiceName,
|
||||
new OpenApiInfo { Title = setting.RemoteServiceName, Version = "v1" });
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
options.CustomSchemaIds(type => type.FullName);
|
||||
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||
if (basePath is not null)
|
||||
{
|
||||
foreach (var item in Directory.GetFiles(basePath, "*.xml"))
|
||||
// 根据分组名称过滤 API 文档
|
||||
options.DocInclusionPredicate((docName, apiDesc) =>
|
||||
{
|
||||
options.IncludeXmlComments(item, true);
|
||||
if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
|
||||
{
|
||||
var settingOrNull = mvcSettings
|
||||
.Where(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly)
|
||||
.FirstOrDefault();
|
||||
if (settingOrNull is not null)
|
||||
{
|
||||
return docName == settingOrNull.RemoteServiceName;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
options.CustomSchemaIds(type => type.FullName);
|
||||
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||
if (basePath is not null)
|
||||
{
|
||||
foreach (var item in Directory.GetFiles(basePath, "*.xml"))
|
||||
{
|
||||
options.IncludeXmlComments(item, true);
|
||||
}
|
||||
}
|
||||
|
||||
options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
|
||||
{
|
||||
Description = "直接输入Token即可",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.Http,
|
||||
Scheme = "bearer"
|
||||
});
|
||||
var scheme = new OpenApiSecurityScheme()
|
||||
{
|
||||
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
|
||||
};
|
||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
||||
{
|
||||
[scheme] = new string[0]
|
||||
});
|
||||
|
||||
options.OperationFilter<AddRequiredHeaderParameter>();
|
||||
options.SchemaFilter<EnumSchemaFilter>();
|
||||
}
|
||||
);
|
||||
|
||||
options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
|
||||
{
|
||||
Description = "直接输入Token即可",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.Http,
|
||||
Scheme = "bearer"
|
||||
});
|
||||
var scheme = new OpenApiSecurityScheme()
|
||||
{
|
||||
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
|
||||
};
|
||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
||||
{
|
||||
[scheme] = new string[0]
|
||||
});
|
||||
|
||||
options.OperationFilter<AddRequiredHeaderParameter>();
|
||||
options.SchemaFilter<EnumSchemaFilter>();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
return services;
|
||||
}
|
||||
@@ -103,7 +108,6 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="context"></param>
|
||||
|
||||
public void Apply(OpenApiSchema model, SchemaFilterContext context)
|
||||
{
|
||||
if (context.Type.IsEnum)
|
||||
@@ -112,7 +116,7 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
|
||||
model.Type = "string";
|
||||
model.Format = null;
|
||||
|
||||
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Enum.GetNames(context.Type)
|
||||
.ToList()
|
||||
@@ -121,9 +125,10 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
|
||||
Enum e = (Enum)Enum.Parse(context.Type, name);
|
||||
var descrptionOrNull = GetEnumDescription(e);
|
||||
model.Enum.Add(new OpenApiString(name));
|
||||
stringBuilder.Append($"【枚举:{name}{(descrptionOrNull is null ? string.Empty : $"({descrptionOrNull})")}={Convert.ToInt64(Enum.Parse(context.Type, name))}】<br />");
|
||||
stringBuilder.Append(
|
||||
$"【枚举:{name}{(descrptionOrNull is null ? string.Empty : $"({descrptionOrNull})")}={Convert.ToInt64(Enum.Parse(context.Type, name))}】<br />");
|
||||
});
|
||||
model.Description= stringBuilder.ToString();
|
||||
model.Description = stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,13 +138,13 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
|
||||
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
return attributes.Length > 0 ? attributes[0].Description : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class AddRequiredHeaderParameter : IOperationFilter
|
||||
{
|
||||
public static string HeaderKey { get; set; } = "__tenant";
|
||||
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
if (operation.Parameters == null)
|
||||
@@ -150,8 +155,8 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
AllowEmptyValue = true,
|
||||
Description="租户id或者租户名称(可空为默认租户)"
|
||||
Description = "租户id或者租户名称(可空为默认租户)"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Hangfire.Server;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Threading;
|
||||
using Volo.Abp.Uow;
|
||||
|
||||
namespace Yi.Framework.BackgroundWorkers.Hangfire;
|
||||
|
||||
public class UnitOfWorkHangfireFilter : IServerFilter, ISingletonDependency
|
||||
{
|
||||
private const string CurrentJobUow = "HangfireUnitOfWork";
|
||||
private readonly IUnitOfWorkManager _unitOfWorkManager;
|
||||
|
||||
public UnitOfWorkHangfireFilter(IUnitOfWorkManager unitOfWorkManager)
|
||||
{
|
||||
_unitOfWorkManager = unitOfWorkManager;
|
||||
}
|
||||
|
||||
public void OnPerforming(PerformingContext context)
|
||||
{
|
||||
var uow = _unitOfWorkManager.Begin();
|
||||
context.Items.Add(CurrentJobUow, uow);
|
||||
}
|
||||
|
||||
public void OnPerformed(PerformedContext context)
|
||||
{
|
||||
AsyncHelper.RunSync(()=>OnPerformedAsync(context));
|
||||
}
|
||||
|
||||
private async Task OnPerformedAsync(PerformedContext context)
|
||||
{
|
||||
if (context.Items.TryGetValue(CurrentJobUow, out var obj)
|
||||
&& obj is IUnitOfWork uow)
|
||||
{
|
||||
if (context.Exception == null && !uow.IsCompleted)
|
||||
{
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await uow.RollbackAsync();
|
||||
}
|
||||
uow.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Volo.Abp.BackgroundWorkers.Hangfire" Version="$(AbpVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,35 @@
|
||||
using Hangfire;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Volo.Abp.BackgroundWorkers;
|
||||
using Volo.Abp.BackgroundWorkers.Hangfire;
|
||||
|
||||
namespace Yi.Framework.BackgroundWorkers.Hangfire;
|
||||
|
||||
[DependsOn(typeof(AbpBackgroundWorkersHangfireModule))]
|
||||
public class YiFrameworkBackgroundWorkersHangfireModule : AbpModule
|
||||
{
|
||||
public override void PreConfigureServices(ServiceConfigurationContext context)
|
||||
{
|
||||
context.Services.AddConventionalRegistrar(new YiHangfireConventionalRegistrar());
|
||||
}
|
||||
|
||||
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
|
||||
{
|
||||
//定时任务自动注入,Abp默认只有在Quartz才实现
|
||||
var backgroundWorkerManager = context.ServiceProvider.GetRequiredService<IBackgroundWorkerManager>();
|
||||
var works = context.ServiceProvider.GetServices<IHangfireBackgroundWorker>();
|
||||
|
||||
foreach (var work in works)
|
||||
{
|
||||
//如果为空,默认使用服务器本地utc时间
|
||||
work.TimeZone ??= TimeZoneInfo.Local;
|
||||
await backgroundWorkerManager.AddAsync(work);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
|
||||
{
|
||||
var services = context.ServiceProvider;
|
||||
GlobalJobFilters.Filters.Add(services.GetRequiredService<UnitOfWorkHangfireFilter>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Volo.Abp.BackgroundWorkers.Hangfire;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
|
||||
namespace Yi.Framework.BackgroundWorkers.Hangfire;
|
||||
|
||||
public class YiHangfireConventionalRegistrar : DefaultConventionalRegistrar
|
||||
{
|
||||
protected override bool IsConventionalRegistrationDisabled(Type type)
|
||||
{
|
||||
return !typeof(IHangfireBackgroundWorker).IsAssignableFrom(type) || base.IsConventionalRegistrationDisabled(type);
|
||||
}
|
||||
|
||||
protected override List<Type> GetExposedServiceTypes(Type type)
|
||||
{
|
||||
return new List<Type>()
|
||||
{
|
||||
typeof(IHangfireBackgroundWorker)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using Hangfire.Dashboard;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Users;
|
||||
|
||||
namespace Yi.Framework.BackgroundWorkers.Hangfire;
|
||||
|
||||
public class YiTokenAuthorizationFilter : IDashboardAsyncAuthorizationFilter, ITransientDependency
|
||||
{
|
||||
private const string Bearer = "Bearer: ";
|
||||
private string RequireUser { get; set; } = "cc";
|
||||
private TimeSpan ExpiresTime { get; set; } = TimeSpan.FromMinutes(10);
|
||||
private IServiceProvider _serviceProvider;
|
||||
|
||||
public YiTokenAuthorizationFilter(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public YiTokenAuthorizationFilter SetRequireUser(string userName)
|
||||
{
|
||||
RequireUser = userName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public YiTokenAuthorizationFilter SetExpiresTime(TimeSpan expiresTime)
|
||||
{
|
||||
ExpiresTime = expiresTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool Authorize(DashboardContext context)
|
||||
{
|
||||
var httpContext = context.GetHttpContext();
|
||||
var _currentUser = _serviceProvider.GetRequiredService<ICurrentUser>();
|
||||
//如果验证通过,设置cookies
|
||||
if (_currentUser.IsAuthenticated)
|
||||
{
|
||||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
Expires = DateTimeOffset.Now + ExpiresTime, // 设置 cookie 过期时间,10分钟
|
||||
};
|
||||
|
||||
|
||||
var authorization = httpContext.Request.Headers["Authorization"].ToString();
|
||||
if (!string.IsNullOrWhiteSpace(authorization))
|
||||
{
|
||||
var token = httpContext.Request.Headers["Authorization"].ToString().Substring(Bearer.Length - 1);
|
||||
httpContext.Response.Cookies.Append("Token", token, cookieOptions);
|
||||
}
|
||||
|
||||
if (_currentUser.UserName == RequireUser)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
SetChallengeResponse(httpContext);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetChallengeResponse(HttpContext httpContext)
|
||||
{
|
||||
httpContext.Response.StatusCode = 401;
|
||||
httpContext.Response.ContentType = "text/html; charset=utf-8";
|
||||
string html = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Token 输入</title>
|
||||
<script>
|
||||
function sendToken() {
|
||||
// 获取输入的 token
|
||||
var token = document.getElementById("tokenInput").value;
|
||||
token = token.replace('Bearer ','');
|
||||
// 构建请求 URL
|
||||
var url = "/hangfire";
|
||||
// 发送 GET 请求
|
||||
fetch(url,{
|
||||
headers: {
|
||||
'Content-Type': 'application/json', // 设置内容类型为 JSON
|
||||
'Authorization': 'Bearer '+encodeURIComponent(token), // 设置授权头,例如使用 Bearer token
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.text(); // 或使用 response.json() 如果返回的是 JSON
|
||||
}
|
||||
throw new Error('Network response was not ok.');
|
||||
})
|
||||
.then(data => {
|
||||
// 处理成功返回的数据
|
||||
document.open();
|
||||
document.write(data);
|
||||
document.close();
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误
|
||||
console.error('There has been a problem with your fetch operation:', error);
|
||||
alert("请求失败: " + error.message);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="text-align: center;">
|
||||
<h1>Yi-hangfire</h1>
|
||||
<h1>输入您的Token,我们将验证您是否为管理员</h1>
|
||||
<textarea id="tokenInput" placeholder="请输入 token" style="width: 80%;height: 120px;margin: 0 10%;"></textarea>
|
||||
<button onclick="sendToken()">校验</button>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
httpContext.Response.WriteAsync(html);
|
||||
}
|
||||
|
||||
public Task<bool> AuthorizeAsync(DashboardContext context)
|
||||
{
|
||||
return Task.FromResult(Authorize(context));
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,10 @@ namespace Yi.Framework.Core.Enums
|
||||
/// </summary>
|
||||
public enum FileTypeEnum
|
||||
{
|
||||
File,
|
||||
Image,
|
||||
Thumbnail,
|
||||
Excel,
|
||||
Temp
|
||||
file,
|
||||
image,
|
||||
thumbnail,
|
||||
excel,
|
||||
temp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ namespace Yi.Framework.Core.Helper
|
||||
{
|
||||
var extension = Path.GetExtension(fileName);
|
||||
if (ImageType.Contains(extension.ToLower()))
|
||||
return FileTypeEnum.Image;
|
||||
return FileTypeEnum.File;
|
||||
return FileTypeEnum.image;
|
||||
return FileTypeEnum.file;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Yi.Framework.Core.Json;
|
||||
|
||||
public class DatetimeJsonConverter : JsonConverter<DateTime>
|
||||
{
|
||||
private string _format;
|
||||
public DatetimeJsonConverter(string format="yyyy-MM-dd HH:mm:ss")
|
||||
{
|
||||
_format = format;
|
||||
}
|
||||
|
||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if(reader.TokenType==JsonTokenType.String)
|
||||
{
|
||||
if (DateTime.TryParse(reader.GetString(), out DateTime dateTime)) return dateTime;
|
||||
}
|
||||
return reader.GetDateTime();
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString(_format));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using SqlSugar;
|
||||
using ArgumentException = System.ArgumentException;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore.Abstractions
|
||||
{
|
||||
@@ -53,6 +54,5 @@ namespace Yi.Framework.SqlSugarCore.Abstractions
|
||||
/// 开启Saas多租户
|
||||
/// </summary>
|
||||
public bool EnabledSaasMultiTenancy { get; set; } = false;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore.Abstractions
|
||||
{
|
||||
public interface ISqlSugarDbConnectionCreator
|
||||
{
|
||||
DbConnOptions Options { get; }
|
||||
Action<ISqlSugarClient> OnSqlSugarClientConfig { get; set; }
|
||||
Action<object, DataAfterModel> DataExecuted { get; set; }
|
||||
Action<object, DataFilterModel> DataExecuting { get; set; }
|
||||
Action<string, SugarParameter[]> OnLogExecuting { get; set; }
|
||||
Action<string, SugarParameter[]> OnLogExecuted { get; set; }
|
||||
Action<PropertyInfo, EntityColumnInfo> EntityService { get; set; }
|
||||
|
||||
ConnectionConfig Build(Action<ConnectionConfig>? action = null);
|
||||
void SetDbAop(ISqlSugarClient currentDb);
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,14 @@ namespace Yi.Framework.SqlSugarCore.Abstractions
|
||||
{
|
||||
public interface ISqlSugarDbContext
|
||||
{
|
||||
// IAbpLazyServiceProvider LazyServiceProvider { get; set; }
|
||||
/// <summary>
|
||||
/// SqlSugarDb
|
||||
/// </summary>
|
||||
ISqlSugarClient SqlSugarClient { get; }
|
||||
DbConnOptions Options { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 数据库备份
|
||||
/// </summary>
|
||||
void BackupDataBase();
|
||||
void SetSqlSugarClient(ISqlSugarClient sqlSugarClient);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Reflection;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
public interface ISqlSugarDbContextDependencies
|
||||
{
|
||||
/// <summary>
|
||||
/// 执行顺序
|
||||
/// </summary>
|
||||
int ExecutionOrder { get; }
|
||||
|
||||
void OnSqlSugarClientConfig(ISqlSugarClient sqlSugarClient);
|
||||
void DataExecuted(object oldValue, DataAfterModel entityInfo);
|
||||
void DataExecuting(object oldValue, DataFilterModel entityInfo);
|
||||
|
||||
void OnLogExecuting(string sql, SugarParameter[] pars);
|
||||
void OnLogExecuted(string sql, SugarParameter[] pars);
|
||||
|
||||
void EntityService(PropertyInfo propertyInfo, EntityColumnInfo entityColumnInfo);
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.Uow;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore.Abstractions
|
||||
{
|
||||
|
||||
public interface ISqlSugarRepository<TEntity>:IRepository<TEntity> where TEntity : class, IEntity,new ()
|
||||
public interface ISqlSugarRepository<TEntity>:IRepository<TEntity>,IUnitOfWorkEnabled where TEntity : class, IEntity,new ()
|
||||
{
|
||||
ISqlSugarClient _Db { get; }
|
||||
ISugarQueryable<TEntity> _DbQueryable { get; }
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Auditing;
|
||||
using Volo.Abp.Data;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
using Volo.Abp.Domain.Entities.Events;
|
||||
using Volo.Abp.Guids;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.Uow;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore;
|
||||
|
||||
public class DefaultSqlSugarDbContext : SqlSugarDbContext
|
||||
{
|
||||
protected DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
|
||||
protected ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
protected IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
|
||||
protected ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
|
||||
protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
|
||||
protected IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
|
||||
public IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>();
|
||||
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
|
||||
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
|
||||
|
||||
protected IEntityChangeEventHelper EntityChangeEventHelper =>
|
||||
LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
|
||||
|
||||
public DefaultSqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void CustomDataFilter(ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
if (IsSoftDeleteFilterEnabled)
|
||||
{
|
||||
sqlSugarClient.QueryFilter.AddTableFilter<ISoftDelete>(u => u.IsDeleted == false);
|
||||
}
|
||||
|
||||
if (IsMultiTenantFilterEnabled)
|
||||
{
|
||||
//表达式里只能有具体值,不能运算
|
||||
var expressionCurrentTenant = CurrentTenant.Id ?? null;
|
||||
sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == expressionCurrentTenant);
|
||||
}
|
||||
}
|
||||
|
||||
public override void DataExecuting(object oldValue, DataFilterModel entityInfo)
|
||||
{
|
||||
//审计日志
|
||||
switch (entityInfo.OperationType)
|
||||
{
|
||||
case DataFilterType.UpdateByObject:
|
||||
|
||||
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModificationTime)))
|
||||
{
|
||||
if (!DateTime.MinValue.Equals(oldValue))
|
||||
{
|
||||
entityInfo.SetValue(DateTime.Now);
|
||||
}
|
||||
}
|
||||
else if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModifierId)))
|
||||
{
|
||||
if (typeof(Guid?) == entityInfo.EntityColumnInfo.PropertyInfo.PropertyType)
|
||||
{
|
||||
if (CurrentUser.Id != null)
|
||||
{
|
||||
entityInfo.SetValue(CurrentUser.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case DataFilterType.InsertByObject:
|
||||
|
||||
if (entityInfo.PropertyName.Equals(nameof(IEntity<Guid>.Id)))
|
||||
{
|
||||
//类型为guid
|
||||
if (typeof(Guid) == entityInfo.EntityColumnInfo.PropertyInfo.PropertyType)
|
||||
{
|
||||
//主键为空或者为默认最小值
|
||||
if (Guid.Empty.Equals(oldValue))
|
||||
{
|
||||
entityInfo.SetValue(GuidGenerator.Create());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreationTime)))
|
||||
{
|
||||
//为空或者为默认最小值
|
||||
if (DateTime.MinValue.Equals(oldValue))
|
||||
{
|
||||
entityInfo.SetValue(DateTime.Now);
|
||||
}
|
||||
}
|
||||
else if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreatorId)))
|
||||
{
|
||||
//类型为guid
|
||||
if (typeof(Guid?) == entityInfo.EntityColumnInfo.PropertyInfo.PropertyType)
|
||||
{
|
||||
if (CurrentUser.Id is not null)
|
||||
{
|
||||
entityInfo.SetValue(CurrentUser.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (entityInfo.PropertyName.Equals(nameof(IMultiTenant.TenantId)))
|
||||
{
|
||||
if (CurrentTenant.Id is not null)
|
||||
{
|
||||
entityInfo.SetValue(CurrentTenant.Id);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//实体变更领域事件
|
||||
switch (entityInfo.OperationType)
|
||||
{
|
||||
case DataFilterType.InsertByObject:
|
||||
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
|
||||
{
|
||||
EntityChangeEventHelper.PublishEntityCreatedEvent(entityInfo.EntityValue);
|
||||
}
|
||||
|
||||
break;
|
||||
case DataFilterType.UpdateByObject:
|
||||
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
|
||||
{
|
||||
//软删除,发布的是删除事件
|
||||
if (entityInfo.EntityValue is ISoftDelete softDelete)
|
||||
{
|
||||
if (softDelete.IsDeleted == true)
|
||||
{
|
||||
EntityChangeEventHelper.PublishEntityDeletedEvent(entityInfo.EntityValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityInfo.EntityValue);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case DataFilterType.DeleteByObject:
|
||||
// if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
|
||||
// {
|
||||
//这里sqlsugar有个特殊,删除会返回批量的结果
|
||||
//这里sqlsugar有第二个特殊,删除事件是行级事件
|
||||
if (entityInfo.EntityValue is IEnumerable entityValues)
|
||||
{
|
||||
foreach (var entityValue in entityValues)
|
||||
{
|
||||
EntityChangeEventHelper.PublishEntityDeletedEvent(entityValue);
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//实体领域事件-所有操作类型
|
||||
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
|
||||
{
|
||||
var eventReport = CreateEventReport(entityInfo.EntityValue);
|
||||
PublishEntityEvents(eventReport);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLogExecuting(string sql, SugarParameter[] pars)
|
||||
{
|
||||
if (Options.EnabledSqlLog)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("==========Yi-SQL执行:==========");
|
||||
sb.AppendLine(UtilMethods.GetSqlString(DbType.SqlServer, sql, pars));
|
||||
sb.AppendLine("===============================");
|
||||
Logger.CreateLogger<DefaultSqlSugarDbContext>().LogDebug(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLogExecuted(string sql, SugarParameter[] pars)
|
||||
{
|
||||
if (Options.EnabledSqlLog)
|
||||
{
|
||||
var sqllog = $"=========Yi-SQL耗时{SqlSugarClient.Ado.SqlExecutionTime.TotalMilliseconds}毫秒=====";
|
||||
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sqllog.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public override void EntityService(PropertyInfo propertyInfo, EntityColumnInfo entityColumnInfo)
|
||||
{
|
||||
if (propertyInfo.Name == nameof(IHasConcurrencyStamp.ConcurrencyStamp)) //带版本号并发更新
|
||||
{
|
||||
entityColumnInfo.IsEnableUpdateVersionValidation = true;
|
||||
}
|
||||
|
||||
if (propertyInfo.PropertyType == typeof(ExtraPropertyDictionary))
|
||||
{
|
||||
entityColumnInfo.IsIgnore = true;
|
||||
}
|
||||
|
||||
if (propertyInfo.Name == nameof(Entity<object>.Id))
|
||||
{
|
||||
entityColumnInfo.IsPrimarykey = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 创建领域事件报告
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual EntityEventReport? CreateEventReport(object entity)
|
||||
{
|
||||
var eventReport = new EntityEventReport();
|
||||
|
||||
//判断是否为领域事件-聚合根
|
||||
var generatesDomainEventsEntity = entity as IGeneratesDomainEvents;
|
||||
if (generatesDomainEventsEntity == null)
|
||||
{
|
||||
return eventReport;
|
||||
}
|
||||
|
||||
var localEvents = generatesDomainEventsEntity.GetLocalEvents()?.ToArray();
|
||||
if (localEvents != null && localEvents.Any())
|
||||
{
|
||||
eventReport.DomainEvents.AddRange(
|
||||
localEvents.Select(
|
||||
eventRecord => new DomainEventEntry(
|
||||
entity,
|
||||
eventRecord.EventData,
|
||||
eventRecord.EventOrder
|
||||
)
|
||||
)
|
||||
);
|
||||
generatesDomainEventsEntity.ClearLocalEvents();
|
||||
}
|
||||
|
||||
var distributedEvents = generatesDomainEventsEntity.GetDistributedEvents()?.ToArray();
|
||||
if (distributedEvents != null && distributedEvents.Any())
|
||||
{
|
||||
eventReport.DistributedEvents.AddRange(
|
||||
distributedEvents.Select(
|
||||
eventRecord => new DomainEventEntry(
|
||||
entity,
|
||||
eventRecord.EventData,
|
||||
eventRecord.EventOrder)
|
||||
)
|
||||
);
|
||||
generatesDomainEventsEntity.ClearDistributedEvents();
|
||||
}
|
||||
|
||||
return eventReport;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发布领域事件
|
||||
/// </summary>
|
||||
/// <param name="changeReport"></param>
|
||||
private void PublishEntityEvents(EntityEventReport changeReport)
|
||||
{
|
||||
foreach (var localEvent in changeReport.DomainEvents)
|
||||
{
|
||||
UnitOfWorkManager.Current?.AddOrReplaceLocalEvent(
|
||||
new UnitOfWorkEventRecord(localEvent.EventData.GetType(), localEvent.EventData, localEvent.EventOrder)
|
||||
);
|
||||
}
|
||||
|
||||
foreach (var distributedEvent in changeReport.DistributedEvents)
|
||||
{
|
||||
UnitOfWorkManager.Current?.AddOrReplaceDistributedEvent(
|
||||
new UnitOfWorkEventRecord(distributedEvent.EventData.GetType(), distributedEvent.EventData, distributedEvent.EventOrder)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore
|
||||
{
|
||||
public static class SqlSugarCoreExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 新增db对象,可支持多个
|
||||
/// </summary>
|
||||
/// <param name="service"></param>
|
||||
/// <param name="serviceLifetime"></param>
|
||||
/// <typeparam name="TDbContext"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddYiDbContext<TDbContext>(this IServiceCollection service, ServiceLifetime serviceLifetime = ServiceLifetime.Transient) where TDbContext : class, ISqlSugarDbContextDependencies
|
||||
{
|
||||
service.AddTransient<ISqlSugarDbContextDependencies, TDbContext>();
|
||||
return service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增db对象,可支持多个
|
||||
/// </summary>
|
||||
/// <param name="service"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <typeparam name="TDbContext"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddYiDbContext<TDbContext>(this IServiceCollection service, Action<DbConnOptions> options) where TDbContext : class, ISqlSugarDbContextDependencies
|
||||
{
|
||||
service.Configure<DbConnOptions>(options.Invoke);
|
||||
service.AddYiDbContext<TDbContext>();
|
||||
return service;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Data;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore
|
||||
{
|
||||
|
||||
public class SqlSugarDbConnectionCreator: ISqlSugarDbConnectionCreator,ITransientDependency
|
||||
{
|
||||
public SqlSugarDbConnectionCreator(IOptions<DbConnOptions> options)
|
||||
{
|
||||
Options = options.Value;
|
||||
}
|
||||
public DbConnOptions Options { get; }
|
||||
|
||||
public void SetDbAop(ISqlSugarClient currentDb)
|
||||
{
|
||||
currentDb.Aop.OnLogExecuting = this.OnLogExecuting;
|
||||
currentDb.Aop.OnLogExecuted = this.OnLogExecuted;
|
||||
currentDb.Aop.DataExecuting = this.DataExecuting;
|
||||
currentDb.Aop.DataExecuted = this.DataExecuted;
|
||||
OnSqlSugarClientConfig(currentDb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ConnectionConfig Build(Action<ConnectionConfig>? action=null)
|
||||
{
|
||||
var dbConnOptions = Options;
|
||||
#region 组装options
|
||||
if (dbConnOptions.DbType is null)
|
||||
{
|
||||
throw new ArgumentException("DbType配置为空");
|
||||
}
|
||||
var slavaConFig = new List<SlaveConnectionConfig>();
|
||||
if (dbConnOptions.EnabledReadWrite)
|
||||
{
|
||||
if (dbConnOptions.ReadUrl is null)
|
||||
{
|
||||
throw new ArgumentException("读写分离为空");
|
||||
}
|
||||
|
||||
var readCon = dbConnOptions.ReadUrl;
|
||||
|
||||
readCon.ForEach(s =>
|
||||
{
|
||||
//如果是动态saas分库,这里的连接串都不能写死,需要动态添加,这里只配置共享库的连接
|
||||
slavaConFig.Add(new SlaveConnectionConfig() { ConnectionString = s });
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 组装连接config
|
||||
var connectionConfig = new ConnectionConfig()
|
||||
{
|
||||
ConfigId= ConnectionStrings.DefaultConnectionStringName,
|
||||
DbType = dbConnOptions.DbType ?? DbType.Sqlite,
|
||||
ConnectionString = dbConnOptions.Url,
|
||||
IsAutoCloseConnection = true,
|
||||
SlaveConnectionConfigs = slavaConFig,
|
||||
//设置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()
|
||||
.Create(c).WriteState is NullabilityState.Nullable)
|
||||
{
|
||||
p.IsNullable = true;
|
||||
}
|
||||
|
||||
if (dbConnOptions.EnableUnderLine && !p.IsIgnore && !p.DbColumnName.Contains('_'))
|
||||
p.DbColumnName = UtilMethods.ToUnderLine(p.DbColumnName);// 驼峰转下划线
|
||||
|
||||
EntityService(c, p);
|
||||
}
|
||||
},
|
||||
//这里多租户有个坑,无效的
|
||||
AopEvents = new AopEvents
|
||||
{
|
||||
DataExecuted = DataExecuted,
|
||||
DataExecuting = DataExecuting,
|
||||
OnLogExecuted = OnLogExecuted,
|
||||
OnLogExecuting = OnLogExecuting
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (action is not null)
|
||||
{
|
||||
action.Invoke(connectionConfig);
|
||||
}
|
||||
#endregion
|
||||
return connectionConfig;
|
||||
}
|
||||
[DisablePropertyInjection]
|
||||
public Action<ISqlSugarClient> OnSqlSugarClientConfig { get; set; }
|
||||
|
||||
[DisablePropertyInjection]
|
||||
public Action<object, DataAfterModel> DataExecuted { get; set; }
|
||||
|
||||
[DisablePropertyInjection]
|
||||
public Action<object, DataFilterModel> DataExecuting { get; set; }
|
||||
|
||||
[DisablePropertyInjection]
|
||||
public Action<string, SugarParameter[]> OnLogExecuting { get; set; }
|
||||
|
||||
[DisablePropertyInjection]
|
||||
public Action<string, SugarParameter[]> OnLogExecuted { get; set; }
|
||||
|
||||
[DisablePropertyInjection]
|
||||
public Action<PropertyInfo, EntityColumnInfo> EntityService { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,362 +1,49 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Reflection;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Auditing;
|
||||
using Volo.Abp.Data;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
using Volo.Abp.Domain.Entities.Events;
|
||||
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
|
||||
namespace Yi.Framework.SqlSugarCore;
|
||||
|
||||
public abstract class SqlSugarDbContext : ISqlSugarDbContextDependencies
|
||||
{
|
||||
public class SqlSugarDbContext : ISqlSugarDbContext
|
||||
protected IAbpLazyServiceProvider LazyServiceProvider { get; }
|
||||
|
||||
public SqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider)
|
||||
{
|
||||
/// <summary>
|
||||
/// SqlSugar 客户端
|
||||
/// </summary>
|
||||
public ISqlSugarClient SqlSugarClient { get; private set; }
|
||||
|
||||
protected ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
private IAbpLazyServiceProvider LazyServiceProvider { get; }
|
||||
|
||||
private IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
|
||||
private ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
|
||||
private ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
|
||||
protected IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
|
||||
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
|
||||
|
||||
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
|
||||
|
||||
private IEntityChangeEventHelper EntityChangeEventHelper => LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
|
||||
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
|
||||
|
||||
private ISerializeService SerializeService=> LazyServiceProvider.LazyGetRequiredService<ISerializeService>();
|
||||
|
||||
public void SetSqlSugarClient(ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
SqlSugarClient = sqlSugarClient;
|
||||
}
|
||||
public SqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider)
|
||||
{
|
||||
LazyServiceProvider = lazyServiceProvider;
|
||||
var connectionCreator = LazyServiceProvider.LazyGetRequiredService<ISqlSugarDbConnectionCreator>();
|
||||
connectionCreator.OnSqlSugarClientConfig = OnSqlSugarClientConfig;
|
||||
connectionCreator.EntityService = EntityService;
|
||||
connectionCreator.DataExecuting = DataExecuting;
|
||||
connectionCreator.DataExecuted = DataExecuted;
|
||||
connectionCreator.OnLogExecuting = OnLogExecuting;
|
||||
connectionCreator.OnLogExecuted = OnLogExecuted;
|
||||
SqlSugarClient = new SqlSugarClient(connectionCreator.Build(action: options =>
|
||||
{
|
||||
options.ConnectionString = GetCurrentConnectionString();
|
||||
options.DbType = GetCurrentDbType();
|
||||
}));
|
||||
//统一使用aop处理
|
||||
connectionCreator.SetDbAop(SqlSugarClient);
|
||||
//替换默认序列化器
|
||||
SqlSugarClient.CurrentConnectionConfig.ConfigureExternalServices.SerializeService = SerializeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// db切换多库支持
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual string GetCurrentConnectionString()
|
||||
{
|
||||
var connectionStringResolver = LazyServiceProvider.LazyGetRequiredService<IConnectionStringResolver>();
|
||||
var connectionString = connectionStringResolver.ResolveAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(connectionString))
|
||||
{
|
||||
Check.NotNull(Options.Url, "dbUrl未配置");
|
||||
}
|
||||
return connectionString!;
|
||||
}
|
||||
|
||||
protected virtual DbType GetCurrentDbType()
|
||||
{
|
||||
if (CurrentTenant.Name is not null)
|
||||
{
|
||||
var dbTypeFromTenantName = GetDbTypeFromTenantName(CurrentTenant.Name);
|
||||
if (dbTypeFromTenantName is not null)
|
||||
{
|
||||
return dbTypeFromTenantName.Value;
|
||||
}
|
||||
}
|
||||
Check.NotNull(Options.DbType, "默认DbType未配置!");
|
||||
return Options.DbType!.Value;
|
||||
}
|
||||
|
||||
//根据租户name进行匹配db类型: Test_Sqlite,[来自AI]
|
||||
private DbType? GetDbTypeFromTenantName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 查找下划线的位置
|
||||
int underscoreIndex = name.LastIndexOf('_');
|
||||
|
||||
if (underscoreIndex == -1 || underscoreIndex == name.Length - 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 提取 枚举 部分
|
||||
string enumString = name.Substring(underscoreIndex + 1);
|
||||
|
||||
// 尝试将 尾缀 转换为枚举
|
||||
if (Enum.TryParse<DbType>(enumString, out DbType result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// 条件不满足时返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上下文对象扩展
|
||||
/// </summary>
|
||||
/// <param name="sqlSugarClient"></param>
|
||||
protected virtual void OnSqlSugarClientConfig(ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
//需自定义扩展
|
||||
if (IsSoftDeleteFilterEnabled)
|
||||
{
|
||||
sqlSugarClient.QueryFilter.AddTableFilter<ISoftDelete>(u => u.IsDeleted == false);
|
||||
}
|
||||
if (IsMultiTenantFilterEnabled)
|
||||
{
|
||||
//表达式里只能有具体值,不能运算
|
||||
var expressionCurrentTenant = CurrentTenant.Id ?? null;
|
||||
sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == expressionCurrentTenant);
|
||||
}
|
||||
CustomDataFilter(sqlSugarClient);
|
||||
}
|
||||
protected virtual void CustomDataFilter(ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
|
||||
}
|
||||
protected virtual void DataExecuted(object oldValue, DataAfterModel entityInfo)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
/// <param name="oldValue"></param>
|
||||
/// <param name="entityInfo"></param>
|
||||
protected virtual void DataExecuting(object oldValue, DataFilterModel entityInfo)
|
||||
{
|
||||
//审计日志
|
||||
switch (entityInfo.OperationType)
|
||||
{
|
||||
case DataFilterType.UpdateByObject:
|
||||
|
||||
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModificationTime)))
|
||||
{
|
||||
if (!DateTime.MinValue.Equals(oldValue))
|
||||
{
|
||||
entityInfo.SetValue(DateTime.Now);
|
||||
}
|
||||
}
|
||||
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModifierId)))
|
||||
{
|
||||
if (CurrentUser.Id != null)
|
||||
{
|
||||
entityInfo.SetValue(CurrentUser.Id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataFilterType.InsertByObject:
|
||||
if (entityInfo.PropertyName.Equals(nameof(IEntity<Guid>.Id)))
|
||||
{
|
||||
//主键为空或者为默认最小值
|
||||
if (Guid.Empty.Equals(oldValue))
|
||||
{
|
||||
entityInfo.SetValue(GuidGenerator.Create());
|
||||
}
|
||||
}
|
||||
|
||||
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreationTime)))
|
||||
{
|
||||
//为空或者为默认最小值
|
||||
if (DateTime.MinValue.Equals(oldValue))
|
||||
{
|
||||
entityInfo.SetValue(DateTime.Now);
|
||||
}
|
||||
}
|
||||
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreatorId)))
|
||||
{
|
||||
if (CurrentUser.Id is not null)
|
||||
{
|
||||
entityInfo.SetValue(CurrentUser.Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (entityInfo.PropertyName.Equals(nameof(IMultiTenant.TenantId)))
|
||||
{
|
||||
if (CurrentTenant.Id is not null)
|
||||
{
|
||||
entityInfo.SetValue(CurrentTenant.Id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//领域事件
|
||||
switch (entityInfo.OperationType)
|
||||
{
|
||||
case DataFilterType.InsertByObject:
|
||||
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
|
||||
{
|
||||
EntityChangeEventHelper.PublishEntityCreatedEvent(entityInfo.EntityValue);
|
||||
}
|
||||
break;
|
||||
case DataFilterType.UpdateByObject:
|
||||
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
|
||||
{
|
||||
//软删除,发布的是删除事件
|
||||
if (entityInfo.EntityValue is ISoftDelete softDelete)
|
||||
{
|
||||
if (softDelete.IsDeleted == true)
|
||||
{
|
||||
EntityChangeEventHelper.PublishEntityDeletedEvent(entityInfo.EntityValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityInfo.EntityValue);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case DataFilterType.DeleteByObject:
|
||||
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
|
||||
{
|
||||
//这里sqlsugar有个特殊,删除会返回批量的结果
|
||||
if (entityInfo.EntityValue is IEnumerable entityValues)
|
||||
{
|
||||
foreach (var entityValue in entityValues)
|
||||
{
|
||||
|
||||
EntityChangeEventHelper.PublishEntityDeletedEvent(entityValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="pars"></param>
|
||||
protected virtual void OnLogExecuting(string sql, SugarParameter[] pars)
|
||||
{
|
||||
if (Options.EnabledSqlLog)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("==========Yi-SQL执行:==========");
|
||||
sb.AppendLine(UtilMethods.GetSqlString(DbType.SqlServer, sql, pars));
|
||||
sb.AppendLine("===============================");
|
||||
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sb.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="pars"></param>
|
||||
protected virtual void OnLogExecuted(string sql, SugarParameter[] pars)
|
||||
{
|
||||
if (Options.EnabledSqlLog)
|
||||
{
|
||||
var sqllog = $"=========Yi-SQL耗时{SqlSugarClient.Ado.SqlExecutionTime.TotalMilliseconds}毫秒=====";
|
||||
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sqllog.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实体配置
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <param name="column"></param>
|
||||
protected virtual void EntityService(PropertyInfo property, EntityColumnInfo column)
|
||||
{
|
||||
if (property.Name == nameof(IHasConcurrencyStamp.ConcurrencyStamp)) //带版本号并发更新
|
||||
{
|
||||
// column.IsOnlyIgnoreInsert = true;
|
||||
// column.IsOnlyIgnoreUpdate = true;
|
||||
column.IsEnableUpdateVersionValidation = true;
|
||||
}
|
||||
if (property.PropertyType == typeof(ExtraPropertyDictionary))
|
||||
{
|
||||
column.IsIgnore = true;
|
||||
}
|
||||
if (property.Name == nameof(Entity<object>.Id))
|
||||
{
|
||||
column.IsPrimarykey = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void BackupDataBase()
|
||||
{
|
||||
string directoryName = "database_backup";
|
||||
string fileName = DateTime.Now.ToString($"yyyyMMdd_HHmmss") + $"_{SqlSugarClient.Ado.Connection.Database}";
|
||||
if (!Directory.Exists(directoryName))
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
switch (Options.DbType)
|
||||
{
|
||||
case DbType.MySql:
|
||||
//MySql
|
||||
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database, $"{Path.Combine(directoryName, fileName)}.sql");//mysql 只支持.net core
|
||||
break;
|
||||
|
||||
|
||||
case DbType.Sqlite:
|
||||
//Sqlite
|
||||
SqlSugarClient.DbMaintenance.BackupDataBase(null, $"{fileName}.db"); //sqlite 只支持.net core
|
||||
break;
|
||||
|
||||
|
||||
case DbType.SqlServer:
|
||||
//SqlServer
|
||||
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database, $"{Path.Combine(directoryName, fileName)}.bak"/*服务器路径*/);//第一个参数库名
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("其他数据库备份未实现");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
this.LazyServiceProvider = lazyServiceProvider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected ISqlSugarClient SqlSugarClient { get;private set; }
|
||||
public int ExecutionOrder => 0;
|
||||
|
||||
public void OnSqlSugarClientConfig(ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
SqlSugarClient = sqlSugarClient;
|
||||
CustomDataFilter(sqlSugarClient);
|
||||
}
|
||||
protected virtual void CustomDataFilter(ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void DataExecuted(object oldValue, DataAfterModel entityInfo)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void DataExecuting(object oldValue, DataFilterModel entityInfo)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnLogExecuting(string sql, SugarParameter[] pars)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnLogExecuted(string sql, SugarParameter[] pars)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void EntityService(PropertyInfo propertyInfo, EntityColumnInfo entityColumnInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Data;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.Threading;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
using Check = Volo.Abp.Check;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore
|
||||
{
|
||||
public class SqlSugarDbContextFactory : ISqlSugarDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// SqlSugar 客户端
|
||||
/// </summary>
|
||||
public ISqlSugarClient SqlSugarClient { get; private set; }
|
||||
|
||||
private IAbpLazyServiceProvider LazyServiceProvider { get; }
|
||||
|
||||
private ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
|
||||
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
|
||||
|
||||
private ISerializeService SerializeService => LazyServiceProvider.LazyGetRequiredService<ISerializeService>();
|
||||
|
||||
private IEnumerable<ISqlSugarDbContextDependencies> SqlSugarDbContextDependencies =>
|
||||
LazyServiceProvider.LazyGetRequiredService<IEnumerable<ISqlSugarDbContextDependencies>>();
|
||||
|
||||
private static readonly ConcurrentDictionary<string, ConnectionConfig> ConnectionConfigCache = new();
|
||||
|
||||
public SqlSugarDbContextFactory(IAbpLazyServiceProvider lazyServiceProvider)
|
||||
{
|
||||
LazyServiceProvider = lazyServiceProvider;
|
||||
|
||||
var connectionString = GetCurrentConnectionString();
|
||||
|
||||
var connectionConfig =BuildConnectionConfig(action: options =>
|
||||
{
|
||||
options.ConnectionString = connectionString;
|
||||
options.DbType = GetCurrentDbType();
|
||||
});
|
||||
// var connectionConfig = ConnectionConfigCache.GetOrAdd(connectionString, (_) =>
|
||||
// BuildConnectionConfig(action: options =>
|
||||
// {
|
||||
// options.ConnectionString = connectionString;
|
||||
// options.DbType = GetCurrentDbType();
|
||||
// }));
|
||||
SqlSugarClient = new SqlSugarClient(connectionConfig);
|
||||
//生命周期,以下都可以直接使用sqlsugardb了
|
||||
|
||||
// Aop及多租户连接字符串和类型,需要单独设置
|
||||
// Aop操作不能进行缓存
|
||||
SetDbAop(SqlSugarClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建Aop-sqlsugaraop在多租户模式中,需单独设置
|
||||
/// </summary>
|
||||
/// <param name="sqlSugarClient"></param>
|
||||
protected virtual void SetDbAop(ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
//替换默认序列化器
|
||||
sqlSugarClient.CurrentConnectionConfig.ConfigureExternalServices.SerializeService = SerializeService;
|
||||
|
||||
//将所有,ISqlSugarDbContextDependencies进行累加
|
||||
Action<string, SugarParameter[]> onLogExecuting = null;
|
||||
Action<string, SugarParameter[]> onLogExecuted = null;
|
||||
Action<object, DataFilterModel> dataExecuting = null;
|
||||
Action<object, DataAfterModel> dataExecuted = null;
|
||||
Action<ISqlSugarClient> onSqlSugarClientConfig = null;
|
||||
|
||||
foreach (var dependency in SqlSugarDbContextDependencies.OrderBy(x => x.ExecutionOrder))
|
||||
{
|
||||
onLogExecuting += dependency.OnLogExecuting;
|
||||
onLogExecuted += dependency.OnLogExecuted;
|
||||
dataExecuting += dependency.DataExecuting;
|
||||
dataExecuted += dependency.DataExecuted;
|
||||
|
||||
onSqlSugarClientConfig += dependency.OnSqlSugarClientConfig;
|
||||
}
|
||||
|
||||
//最先存放db操作
|
||||
onSqlSugarClientConfig(sqlSugarClient);
|
||||
|
||||
sqlSugarClient.Aop.OnLogExecuting =onLogExecuting;
|
||||
sqlSugarClient.Aop.OnLogExecuted = onLogExecuted;
|
||||
|
||||
sqlSugarClient.Aop.DataExecuting =dataExecuting;
|
||||
sqlSugarClient.Aop.DataExecuted =dataExecuted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建连接配置
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
protected virtual ConnectionConfig BuildConnectionConfig(Action<ConnectionConfig>? action = null)
|
||||
{
|
||||
var dbConnOptions = Options;
|
||||
|
||||
#region 组装options
|
||||
|
||||
if (dbConnOptions.DbType is null)
|
||||
{
|
||||
throw new ArgumentException("DbType配置为空");
|
||||
}
|
||||
|
||||
var slavaConFig = new List<SlaveConnectionConfig>();
|
||||
if (dbConnOptions.EnabledReadWrite)
|
||||
{
|
||||
if (dbConnOptions.ReadUrl is null)
|
||||
{
|
||||
throw new ArgumentException("读写分离为空");
|
||||
}
|
||||
|
||||
var readCon = dbConnOptions.ReadUrl;
|
||||
|
||||
readCon.ForEach(s =>
|
||||
{
|
||||
//如果是动态saas分库,这里的连接串都不能写死,需要动态添加,这里只配置共享库的连接
|
||||
slavaConFig.Add(new SlaveConnectionConfig() { ConnectionString = s });
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 组装连接config
|
||||
|
||||
var connectionConfig = new ConnectionConfig()
|
||||
{
|
||||
ConfigId = ConnectionStrings.DefaultConnectionStringName,
|
||||
DbType = dbConnOptions.DbType ?? DbType.Sqlite,
|
||||
ConnectionString = dbConnOptions.Url,
|
||||
IsAutoCloseConnection = true,
|
||||
SlaveConnectionConfigs = slavaConFig,
|
||||
//设置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()
|
||||
.Create(c).WriteState is NullabilityState.Nullable)
|
||||
{
|
||||
p.IsNullable = true;
|
||||
}
|
||||
|
||||
if (dbConnOptions.EnableUnderLine && !p.IsIgnore && !p.DbColumnName.Contains('_'))
|
||||
p.DbColumnName = UtilMethods.ToUnderLine(p.DbColumnName); // 驼峰转下划线
|
||||
|
||||
//将所有,ISqlSugarDbContextDependencies的EntityService进行累加
|
||||
//额外的实体服务需要这里配置,
|
||||
|
||||
Action<PropertyInfo, EntityColumnInfo> entityService = null;
|
||||
foreach (var dependency in SqlSugarDbContextDependencies.OrderBy(x => x.ExecutionOrder))
|
||||
{
|
||||
entityService += dependency.EntityService;
|
||||
}
|
||||
|
||||
entityService(c, p);
|
||||
}
|
||||
},
|
||||
//这里多租户有个坑,这里配置是无效的
|
||||
// AopEvents = new AopEvents
|
||||
// {
|
||||
// DataExecuted = DataExecuted,
|
||||
// DataExecuting = DataExecuting,
|
||||
// OnLogExecuted = OnLogExecuted,
|
||||
// OnLogExecuting = OnLogExecuting
|
||||
// }
|
||||
};
|
||||
|
||||
if (action is not null)
|
||||
{
|
||||
action.Invoke(connectionConfig);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return connectionConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// db切换多库支持
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual string GetCurrentConnectionString()
|
||||
{
|
||||
var connectionStringResolver = LazyServiceProvider.LazyGetRequiredService<IConnectionStringResolver>();
|
||||
var connectionString =
|
||||
AsyncHelper.RunSync(() => connectionStringResolver.ResolveAsync());
|
||||
|
||||
if (string.IsNullOrWhiteSpace(connectionString))
|
||||
{
|
||||
Check.NotNull(Options.Url, "dbUrl未配置");
|
||||
}
|
||||
|
||||
return connectionString!;
|
||||
}
|
||||
|
||||
protected virtual DbType GetCurrentDbType()
|
||||
{
|
||||
if (CurrentTenant.Name is not null)
|
||||
{
|
||||
var dbTypeFromTenantName = GetDbTypeFromTenantName(CurrentTenant.Name);
|
||||
if (dbTypeFromTenantName is not null)
|
||||
{
|
||||
return dbTypeFromTenantName.Value;
|
||||
}
|
||||
}
|
||||
|
||||
Check.NotNull(Options.DbType, "默认DbType未配置!");
|
||||
return Options.DbType!.Value;
|
||||
}
|
||||
|
||||
//根据租户name进行匹配db类型: Test_Sqlite,[来自AI]
|
||||
private DbType? GetDbTypeFromTenantName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 查找下划线的位置
|
||||
int underscoreIndex = name.LastIndexOf('_');
|
||||
|
||||
if (underscoreIndex == -1 || underscoreIndex == name.Length - 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 提取 枚举 部分
|
||||
string enumString = name.Substring(underscoreIndex + 1);
|
||||
|
||||
// 尝试将 尾缀 转换为枚举
|
||||
if (Enum.TryParse<DbType>(enumString, out DbType result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// 条件不满足时返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void BackupDataBase()
|
||||
{
|
||||
string directoryName = "database_backup";
|
||||
string fileName = DateTime.Now.ToString($"yyyyMMdd_HHmmss") + $"_{SqlSugarClient.Ado.Connection.Database}";
|
||||
if (!Directory.Exists(directoryName))
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
switch (Options.DbType)
|
||||
{
|
||||
case DbType.MySql:
|
||||
//MySql
|
||||
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database,
|
||||
$"{Path.Combine(directoryName, fileName)}.sql"); //mysql 只支持.net core
|
||||
break;
|
||||
|
||||
|
||||
case DbType.Sqlite:
|
||||
//Sqlite
|
||||
SqlSugarClient.DbMaintenance.BackupDataBase(null, $"{fileName}.db"); //sqlite 只支持.net core
|
||||
break;
|
||||
|
||||
|
||||
case DbType.SqlServer:
|
||||
//SqlServer
|
||||
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database,
|
||||
$"{Path.Combine(directoryName, fileName)}.bak" /*服务器路径*/); //第一个参数库名
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("其他数据库备份未实现");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.SqlSugarCore
|
||||
{
|
||||
public static class SqlsugarCoreExtensions
|
||||
{
|
||||
public static IServiceCollection AddYiDbContext<DbContext>(this IServiceCollection service, ServiceLifetime serviceLifetime = ServiceLifetime.Transient) where DbContext : class, ISqlSugarDbContext
|
||||
{
|
||||
service.Replace(new ServiceDescriptor(typeof(ISqlSugarDbContext), typeof(DbContext), serviceLifetime));
|
||||
return service;
|
||||
}
|
||||
public static IServiceCollection TryAddYiDbContext<DbContext>(this IServiceCollection service, ServiceLifetime serviceLifetime = ServiceLifetime.Transient) where DbContext : class, ISqlSugarDbContext
|
||||
{
|
||||
service.TryAdd(new ServiceDescriptor(typeof(ISqlSugarDbContext), typeof(DbContext), serviceLifetime));
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
public static IServiceCollection AddYiDbContext<DbContext>(this IServiceCollection service, Action<DbConnOptions> options) where DbContext : class, ISqlSugarDbContext
|
||||
{
|
||||
|
||||
service.Configure<DbConnOptions>(ops =>
|
||||
{
|
||||
options.Invoke(ops);
|
||||
});
|
||||
service.AddYiDbContext<DbContext>();
|
||||
return service;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,4 +17,4 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
||||
DbContext = dbContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,6 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
||||
{
|
||||
public class UnitOfWorkSqlsugarDbContextProvider<TDbContext> : ISugarDbContextProvider<TDbContext> where TDbContext : ISqlSugarDbContext
|
||||
{
|
||||
private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator;
|
||||
|
||||
public ILogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>> Logger { get; set; }
|
||||
public IServiceProvider ServiceProvider { get; set; }
|
||||
|
||||
@@ -28,8 +26,7 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
||||
IUnitOfWorkManager unitOfWorkManager,
|
||||
IConnectionStringResolver connectionStringResolver,
|
||||
ICancellationTokenProvider cancellationTokenProvider,
|
||||
ICurrentTenant currentTenant,
|
||||
ISqlSugarDbConnectionCreator dbConnectionCreator
|
||||
ICurrentTenant currentTenant
|
||||
)
|
||||
{
|
||||
UnitOfWorkManager = unitOfWorkManager;
|
||||
@@ -37,10 +34,8 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
||||
CancellationTokenProvider = cancellationTokenProvider;
|
||||
CurrentTenant = currentTenant;
|
||||
Logger = NullLogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>>.Instance;
|
||||
_dbConnectionCreator = dbConnectionCreator;
|
||||
}
|
||||
|
||||
//private static object _databaseApiLock = new object();
|
||||
|
||||
public virtual async Task<TDbContext> GetDbContextAsync()
|
||||
{
|
||||
|
||||
@@ -52,19 +47,16 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
||||
|
||||
|
||||
var unitOfWork = UnitOfWorkManager.Current;
|
||||
if (unitOfWork == null /*|| unitOfWork.Options.IsTransactional == false*/)
|
||||
if (unitOfWork == null )
|
||||
{
|
||||
var dbContext = (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>();
|
||||
//提高体验,取消工作单元强制性
|
||||
//throw new AbpException("A DbContext can only be created inside a unit of work!");
|
||||
//var dbContext = (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>();
|
||||
//如果不启用工作单元,创建一个新的db,不开启事务即可
|
||||
return dbContext;
|
||||
//return dbContext;
|
||||
|
||||
//2024-11-30,改回强制性使用工作单元,否则容易造成歧义
|
||||
throw new AbpException("DbContext 只能在工作单元内工作,当前DbContext没有工作单元,如需创建新线程并发操作,请手动创建工作单元");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//尝试当前工作单元获取db
|
||||
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
|
||||
|
||||
|
||||
@@ -6,12 +6,10 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SqlSugar;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Auditing;
|
||||
using Volo.Abp.Data;
|
||||
using Volo.Abp.Domain;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.Modularity;
|
||||
using Volo.Abp.Guids;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
using Yi.Framework.SqlSugarCore.Repositories;
|
||||
using Yi.Framework.SqlSugarCore.Uow;
|
||||
@@ -27,8 +25,33 @@ namespace Yi.Framework.SqlSugarCore
|
||||
var configuration = service.GetConfiguration();
|
||||
var section = configuration.GetSection("DbConnOptions");
|
||||
Configure<DbConnOptions>(section);
|
||||
var dbConnOptions = new DbConnOptions();
|
||||
section.Bind(dbConnOptions);
|
||||
|
||||
service.TryAddScoped<ISqlSugarDbContext, SqlSugarDbContext>();
|
||||
//很多人遗漏了这一点,不同的数据库,对于主键的使用规约不一样,需要根据数据库进行判断
|
||||
SequentialGuidType guidType;
|
||||
switch (dbConnOptions.DbType)
|
||||
{
|
||||
case DbType.MySql:
|
||||
case DbType.PostgreSQL:
|
||||
guidType= SequentialGuidType.SequentialAsString;
|
||||
break;
|
||||
case DbType.SqlServer:
|
||||
guidType = SequentialGuidType.SequentialAtEnd;
|
||||
break;
|
||||
case DbType.Oracle:
|
||||
guidType = SequentialGuidType.SequentialAsBinary;
|
||||
break;
|
||||
default:
|
||||
guidType = SequentialGuidType.SequentialAtEnd;
|
||||
break;
|
||||
}
|
||||
Configure<AbpSequentialGuidGeneratorOptions>(options =>
|
||||
{
|
||||
options.DefaultSequentialGuidType = guidType;
|
||||
});
|
||||
|
||||
service.TryAddScoped<ISqlSugarDbContext, SqlSugarDbContextFactory>();
|
||||
|
||||
//不开放sqlsugarClient
|
||||
//service.AddTransient<ISqlSugarClient>(x => x.GetRequiredService<ISqlsugarDbContext>().SqlSugarClient);
|
||||
@@ -47,6 +70,7 @@ namespace Yi.Framework.SqlSugarCore
|
||||
//将默认db传递给abp连接字符串模块
|
||||
Configure<AbpDbConnectionOptions>(x => { x.ConnectionStrings.Default = dbConfig.Url; });
|
||||
|
||||
context.Services.AddYiDbContext<DefaultSqlSugarDbContext>();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -72,7 +96,6 @@ namespace Yi.Framework.SqlSugarCore
|
||||
|
||||
|
||||
logger.LogInformation(sb.ToString());
|
||||
//Todo:准备支持多租户种子数据及CodeFirst
|
||||
|
||||
if (options.EnabledCodeFirst)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.WeChat.MiniProgram.Abstract;
|
||||
|
||||
public interface IErrorObjct: IHasErrcode, IHasErrmsg
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.WeChat.MiniProgram.Abstract;
|
||||
|
||||
public interface IHasErrcode
|
||||
{
|
||||
public int errcode { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.WeChat.MiniProgram.Abstract;
|
||||
|
||||
public interface IHasErrmsg
|
||||
{
|
||||
string errmsg { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Yi.Framework.WeChat.MiniProgram.HttpModels;
|
||||
|
||||
public class AccessTokenResponse
|
||||
{
|
||||
public string access_token { get; set; }
|
||||
|
||||
public int expires_in { get; set; }
|
||||
}
|
||||
|
||||
public class AccessTokenRequest
|
||||
{
|
||||
public string grant_type { get; set; }
|
||||
public string appid { get; set; }
|
||||
public string secret { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Yi.Framework.WeChat.MiniProgram.Abstract;
|
||||
|
||||
namespace Yi.Framework.WeChat.MiniProgram.HttpModels;
|
||||
|
||||
|
||||
public class Code2SessionResponse: IErrorObjct
|
||||
{
|
||||
public string openid { get; set; }
|
||||
public string session_key { get; set; }
|
||||
public string unionid { get; set; }
|
||||
public int errcode { get; set; }
|
||||
public string errmsg { get; set; }
|
||||
}
|
||||
|
||||
public class Code2SessionRequest
|
||||
{
|
||||
public string appid { get; set; }
|
||||
public string secret { get; set; }
|
||||
public string js_code { get; set; }
|
||||
public string grant_type => "authorization_code";
|
||||
}
|
||||
|
||||
public class Code2SessionInput
|
||||
{
|
||||
public Code2SessionInput(string js_code)
|
||||
{
|
||||
|
||||
this.js_code=js_code;
|
||||
}
|
||||
public string js_code { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Yi.Framework.WeChat.MiniProgram.Abstract;
|
||||
|
||||
namespace Yi.Framework.WeChat.MiniProgram.HttpModels;
|
||||
|
||||
public class SubscribeNoticeRequest
|
||||
{
|
||||
/// <summary>
|
||||
///用户openid,可以是小程序的openid,也可以是mp_template_msg.appid对应的公众号的openid
|
||||
/// </summary>
|
||||
public string touser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小程序模板id
|
||||
/// </summary>
|
||||
public string template_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 跳转页面
|
||||
/// </summary>
|
||||
public string page { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小程序模板消息的数据
|
||||
/// </summary>
|
||||
public Dictionary<string, keyValueItem> data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认为正式版
|
||||
/// </summary>
|
||||
public string miniprogram_state { get; set; } = "formal";
|
||||
|
||||
/// <summary>
|
||||
/// 默认为中文
|
||||
/// </summary>
|
||||
public string lang { get; set; } = "zh_CN";
|
||||
}
|
||||
|
||||
|
||||
public class SubscribeNoticeInput
|
||||
{
|
||||
/// <summary>
|
||||
///用户openid,可以是小程序的openid,也可以是mp_template_msg.appid对应的公众号的openid
|
||||
/// </summary>
|
||||
public string touser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小程序模板id
|
||||
/// </summary>
|
||||
public string template_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 跳转页面
|
||||
/// </summary>
|
||||
public string page { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众号模板消息的数据
|
||||
/// </summary>
|
||||
public Dictionary<string, keyValueItem> data { get; set; }
|
||||
}
|
||||
|
||||
public class SubscribeNoticeResponse : IErrorObjct
|
||||
{
|
||||
public int errcode { get; set; }
|
||||
public string errmsg { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public class keyValueItem
|
||||
{
|
||||
public keyValueItem(string value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public string value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Yi.Framework.WeChat.MiniProgram.HttpModels;
|
||||
|
||||
namespace Yi.Framework.WeChat.MiniProgram;
|
||||
|
||||
public interface IWeChatMiniProgramManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取用户openid
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
Task<Code2SessionResponse> Code2SessionAsync(Code2SessionInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 向用户发送订阅消息,要openid
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
Task SendSubscribeNoticeAsync(SubscribeNoticeInput input);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Volo.Abp.Caching;
|
||||
|
||||
namespace Yi.Framework.WeChat.MiniProgram.Token;
|
||||
|
||||
internal class CacheMiniProgramToken : DefaultMinProgramToken, IMiniProgramToken
|
||||
{
|
||||
private IDistributedCache<string> _cache;
|
||||
private const string CacheKey = "MiniProgramToken";
|
||||
|
||||
public CacheMiniProgramToken(IOptions<WeChatMiniProgramOptions> options, IDistributedCache<string> cache) :
|
||||
base(options)
|
||||
{
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public async Task<string> GetTokenAsync()
|
||||
{
|
||||
return await _cache.GetOrAddAsync("MiniProgramToken", async () => { return await base.GetTokenAsync(); }, () =>
|
||||
{
|
||||
return new DistributedCacheEntryOptions()
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2) - TimeSpan.FromMinutes(1)
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yi.Framework.Core.Extensions;
|
||||
using Yi.Framework.WeChat.MiniProgram.HttpModels;
|
||||
|
||||
namespace Yi.Framework.WeChat.MiniProgram.Token;
|
||||
|
||||
internal class DefaultMinProgramToken:IMiniProgramToken
|
||||
{
|
||||
private const string Url = "https://api.weixin.qq.com/cgi-bin/token";
|
||||
private WeChatMiniProgramOptions _options;
|
||||
public DefaultMinProgramToken(IOptions<WeChatMiniProgramOptions> options)
|
||||
{
|
||||
_options = options.Value;
|
||||
}
|
||||
public async Task<string> GetTokenAsync()
|
||||
{
|
||||
var token = await this.GetAccessToken();
|
||||
return token.access_token;
|
||||
}
|
||||
public async Task<AccessTokenResponse> GetAccessToken()
|
||||
{
|
||||
var req = new AccessTokenRequest();
|
||||
req.appid = _options.AppID;
|
||||
req.secret = _options.AppSecret;
|
||||
req.grant_type = "client_credential";
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
string queryString = req.ToQueryString();
|
||||
var builder = new UriBuilder(Url);
|
||||
builder.Query = queryString;
|
||||
HttpResponseMessage response = await httpClient.GetAsync(builder.ToString());
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<AccessTokenResponse>();
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.WeChat.MiniProgram.Token;
|
||||
|
||||
public interface IMiniProgramToken
|
||||
{
|
||||
public Task<string> GetTokenAsync();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Yi.Framework.WeChat.MiniProgram;
|
||||
|
||||
public class WeChatMiniProgramException: Exception
|
||||
{
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
// 加上前缀
|
||||
return "微信Api异常: " + base.Message;
|
||||
}
|
||||
}
|
||||
|
||||
public WeChatMiniProgramException()
|
||||
{
|
||||
}
|
||||
|
||||
public WeChatMiniProgramException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WeChatMiniProgramException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using Yi.Framework.WeChat.MiniProgram.Abstract;
|
||||
|
||||
namespace Yi.Framework.WeChat.MiniProgram;
|
||||
|
||||
public static class WeChatMiniProgramExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 效验请求是否成功
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
internal static void ValidateSuccess(this IErrorObjct response)
|
||||
{
|
||||
|
||||
if (response.errcode != 0)
|
||||
{
|
||||
throw new WeChatMiniProgramException(response.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ToQueryString<T>(this T obj)
|
||||
{
|
||||
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
var queryParams = new List<string>();
|
||||
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
var value = prop.GetValue(obj, null);
|
||||
if (value != null)
|
||||
{
|
||||
// 处理集合
|
||||
if (value is IEnumerable<object> enumerable)
|
||||
{
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
queryParams.Add($"{HttpUtility.UrlEncode(prop.Name)}={HttpUtility.UrlEncode(item.ToString())}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryParams.Add($"{HttpUtility.UrlEncode(prop.Name)}={HttpUtility.UrlEncode(value.ToString())}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("&", queryParams);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Yi.Framework.Core.Extensions;
|
||||
using Yi.Framework.WeChat.MiniProgram.HttpModels;
|
||||
using Yi.Framework.WeChat.MiniProgram.Token;
|
||||
|
||||
namespace Yi.Framework.WeChat.MiniProgram;
|
||||
|
||||
public class WeChatMiniProgramManager : IWeChatMiniProgramManager, ISingletonDependency
|
||||
{
|
||||
private IMiniProgramToken _weChatToken;
|
||||
private WeChatMiniProgramOptions _options;
|
||||
|
||||
public WeChatMiniProgramManager(IMiniProgramToken weChatToken, IOptions<WeChatMiniProgramOptions> options)
|
||||
{
|
||||
_weChatToken = weChatToken;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户openid
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Code2SessionResponse> Code2SessionAsync(Code2SessionInput input)
|
||||
{
|
||||
string url = "https://api.weixin.qq.com/sns/jscode2session";
|
||||
var req = new Code2SessionRequest();
|
||||
req.js_code = input.js_code;
|
||||
req.secret = _options.AppSecret;
|
||||
req.appid = _options.AppID;
|
||||
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
string queryString = req.ToQueryString();
|
||||
var builder = new UriBuilder(url);
|
||||
builder.Query = queryString;
|
||||
HttpResponseMessage response = await httpClient.GetAsync(builder.ToString());
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<Code2SessionResponse>();
|
||||
|
||||
responseBody.ValidateSuccess();
|
||||
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送模板订阅消息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
public async Task SendSubscribeNoticeAsync(SubscribeNoticeInput input)
|
||||
{
|
||||
var token = await _weChatToken.GetTokenAsync();
|
||||
string url = $"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={token}";
|
||||
var req = new SubscribeNoticeRequest
|
||||
{
|
||||
touser = input.touser,
|
||||
template_id = input.template_id,
|
||||
page = input.page,
|
||||
data = input.data,
|
||||
miniprogram_state = _options.Notice?.State??"formal"
|
||||
};
|
||||
req.template_id=req.template_id?? _options.Notice?.TemplateId;
|
||||
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
var body =new StringContent(JsonConvert.SerializeObject(req));
|
||||
HttpResponseMessage response = await httpClient.PostAsync(url, body);
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<SubscribeNoticeResponse>();
|
||||
responseBody.ValidateSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Yi.Framework.WeChat.MiniProgram;
|
||||
|
||||
public class WeChatMiniProgramOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// AppId
|
||||
/// </summary>
|
||||
public string AppID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// App密钥
|
||||
/// </summary>
|
||||
public string AppSecret { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息
|
||||
/// </summary>
|
||||
public WeChatMiniProgramNoticeItem Notice { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class WeChatMiniProgramNoticeItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板id
|
||||
/// </summary>
|
||||
public string TemplateId { get; set; }
|
||||
|
||||
public string State { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\common.props" />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
|
||||
<PackageReference Include="Volo.Abp.Caching" Version="$(AbpVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Volo.Abp.Caching;
|
||||
using Yi.Framework.Core;
|
||||
using Yi.Framework.WeChat.MiniProgram.Token;
|
||||
|
||||
namespace Yi.Framework.WeChat.MiniProgram;
|
||||
|
||||
[DependsOn(typeof(YiFrameworkCoreModule),
|
||||
typeof(AbpCachingModule))]
|
||||
public class YiFrameworkWeChatMiniProgramModule: AbpModule
|
||||
{
|
||||
public override void ConfigureServices(ServiceConfigurationContext context)
|
||||
{
|
||||
var services = context.Services;
|
||||
var configuration = context.Services.GetConfiguration();
|
||||
Configure<WeChatMiniProgramOptions>(configuration.GetSection("WeChatMiniProgram"));
|
||||
services.AddSingleton<IMiniProgramToken, CacheMiniProgramToken>();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ public class AuditingStore : IAuditingStore, ITransientDependency
|
||||
protected IUnitOfWorkManager UnitOfWorkManager { get; }
|
||||
protected AbpAuditingOptions Options { get; }
|
||||
protected IAuditLogInfoToAuditLogConverter Converter { get; }
|
||||
|
||||
public AuditingStore(
|
||||
IAuditLogRepository auditLogRepository,
|
||||
IUnitOfWorkManager unitOfWorkManager,
|
||||
@@ -52,10 +53,10 @@ public class AuditingStore : IAuditingStore, ITransientDependency
|
||||
protected virtual async Task SaveLogAsync(AuditLogInfo auditInfo)
|
||||
{
|
||||
Logger.LogDebug("Yi-请求追踪:" + JsonHelper.ObjToStr(auditInfo, "yyyy-MM-dd HH:mm:ss"));
|
||||
using (var uow = UnitOfWorkManager.Begin(true))
|
||||
using (var uow = UnitOfWorkManager.Begin())
|
||||
{
|
||||
await AuditLogRepository.InsertAsync(await Converter.ConvertAsync(auditInfo));
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class AuditLogActionEntity : Entity<Guid>, IMultiTenant
|
||||
|
||||
public virtual string? MethodName { get; protected set; }
|
||||
|
||||
[SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
||||
public virtual string? Parameters { get; protected set; }
|
||||
|
||||
public virtual DateTime? ExecutionTime { get; protected set; }
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace Yi.Framework.AuditLogging.Domain.Entities
|
||||
|
||||
public virtual string? CorrelationId { get; set; }
|
||||
|
||||
[SugarColumn(Length = 2000)]
|
||||
public virtual string? BrowserInfo { get; protected set; }
|
||||
|
||||
public virtual string? HttpMethod { get; protected set; }
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.BbsUser;
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Analyse;
|
||||
|
||||
public class MoneyTopUserDto
|
||||
/// <summary>
|
||||
/// 用户排行榜
|
||||
/// </summary>
|
||||
public class BaseAnalyseTopUserDto
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string? Nick { get; set; }
|
||||
public decimal Money { get; set; }
|
||||
public int Order { get; set; }
|
||||
public string? Icon { get; set; }
|
||||
public int Level { get; set; }
|
||||
@@ -18,5 +21,4 @@ public class MoneyTopUserDto
|
||||
/// 用户限制
|
||||
/// </summary>
|
||||
public UserLimitEnum UserLimit { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Analyse;
|
||||
|
||||
public class MoneyTopUserDto:BaseAnalyseTopUserDto
|
||||
{
|
||||
public decimal Money { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Analyse;
|
||||
|
||||
public class PointsTopUserDto:BaseAnalyseTopUserDto
|
||||
{
|
||||
public int Points { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Analyse;
|
||||
|
||||
public class ValueTopUserDto:BaseAnalyseTopUserDto
|
||||
{
|
||||
public decimal Value { get; set; }
|
||||
}
|
||||
@@ -4,8 +4,6 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Article
|
||||
{
|
||||
public class ArticleGetListOutputDto : EntityDto<Guid>
|
||||
{
|
||||
//批量查询,不给内容,性能考虑
|
||||
//public string Content { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Guid DiscussId { get; set; }
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Consts;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Article
|
||||
{
|
||||
@@ -10,5 +11,23 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Article
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
public bool HasPermission { get;internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置权限
|
||||
/// </summary>
|
||||
public void SetPassPermission()
|
||||
{
|
||||
HasPermission = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置无权限
|
||||
/// </summary>
|
||||
public void SetNoPermission()
|
||||
{
|
||||
HasPermission = false;
|
||||
Content=DiscussConst.Privacy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using Yi.Framework.Rbac.Application.Contracts.Dtos.User;
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Comment
|
||||
{
|
||||
/// <summary>
|
||||
/// 单返回,返回单条评论即可
|
||||
/// 单返回,返回单条评论即可
|
||||
/// </summary>
|
||||
public class CommentGetOutputDto : EntityDto<Guid>
|
||||
{
|
||||
@@ -17,17 +17,17 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Comment
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 用户id联表为用户对象
|
||||
/// 用户id联表为用户对象
|
||||
/// </summary>
|
||||
|
||||
public BbsUserGetOutputDto User { get; set; }
|
||||
/// <summary>
|
||||
/// 根节点的评论id
|
||||
/// 根节点的评论id
|
||||
/// </summary>
|
||||
public Guid RootId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 被回复的CommentId
|
||||
/// 被回复的CommentId
|
||||
/// </summary>
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
|
||||
@@ -31,5 +31,15 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
|
||||
/// 是否禁止评论创建功能
|
||||
/// </summary>
|
||||
public bool IsDisableCreateComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标签
|
||||
/// </summary>
|
||||
public List<Guid>? DiscussLableIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 角色
|
||||
/// </summary>
|
||||
public List<string>? PermissionRoleCodes { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
|
||||
public class DiscussGetListInputVo : PagedAndSortedResultRequestDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建者的用户名
|
||||
/// 创建者的用户名
|
||||
/// </summary>
|
||||
public string? UserName { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
@@ -15,11 +15,11 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
|
||||
|
||||
public Guid? PlateId { get; set; }
|
||||
|
||||
//默认查询非置顶
|
||||
//默认查询非置顶
|
||||
public bool? IsTop { get; set; }
|
||||
|
||||
|
||||
//查询方式
|
||||
//查询方式
|
||||
public QueryDiscussTypeEnum Type { get; set; } = QueryDiscussTypeEnum.New;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.BbsUser;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.DiscussLable;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Consts;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
using Yi.Framework.Rbac.Application.Contracts.Dtos.User;
|
||||
@@ -17,7 +18,6 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
|
||||
/// </summary>
|
||||
public bool IsAgree { get; set; } = false;
|
||||
public string Title { get; set; }
|
||||
public string Types { get; set; }
|
||||
public string? Introduction { get; set; }
|
||||
|
||||
public int AgreeNum { get; set; }
|
||||
@@ -41,57 +41,16 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
|
||||
/// 封面
|
||||
/// </summary>
|
||||
public string? Cover { get; set; }
|
||||
|
||||
//私有需要判断code权限
|
||||
public string? PrivateCode { get; set; }
|
||||
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
public List<Guid>? PermissionUserIds { get; set; }
|
||||
/// <summary>
|
||||
/// 所需角色
|
||||
/// </summary>
|
||||
public List<string>? PermissionRoleCodes { get; set; } = new List<string>();
|
||||
|
||||
public BbsUserGetListOutputDto User { get; set; }
|
||||
|
||||
public void SetBan()
|
||||
{
|
||||
Title = DiscussConst.Privacy;
|
||||
Introduction = "";
|
||||
Cover = null;
|
||||
//被禁止
|
||||
IsBan = true;
|
||||
}
|
||||
public List<Guid>? DiscussLableIds { get; set; } = new List<Guid>();
|
||||
public List<DiscussLableGetOutputDto> Lables { get; set; } = new List<DiscussLableGetOutputDto>();
|
||||
}
|
||||
|
||||
|
||||
public static class DiscussGetListOutputDtoExtension
|
||||
{
|
||||
|
||||
public static void ApplyPermissionTypeFilter(this List<DiscussGetListOutputDto> dtos, Guid userId)
|
||||
{
|
||||
dtos?.ForEach(dto =>
|
||||
{
|
||||
switch (dto.PermissionType)
|
||||
{
|
||||
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();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.BbsUser;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.DiscussLable;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.Plate;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Consts;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
using Yi.Framework.Rbac.Application.Contracts.Dtos.User;
|
||||
|
||||
@@ -9,11 +11,10 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
|
||||
public class DiscussGetOutputDto : EntityDto<Guid>
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否禁止评论创建功能
|
||||
/// 是否禁止评论创建功能
|
||||
/// </summary>
|
||||
public bool IsDisableCreateComment { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string? Types { get; set; }
|
||||
public string? Introduction { get; set; }
|
||||
public int AgreeNum { get; set; }
|
||||
public int SeeNum { get; set; }
|
||||
@@ -21,24 +22,48 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
|
||||
public string? Color { get; set; }
|
||||
|
||||
public Guid PlateId { get; set; }
|
||||
//是否置顶,默认false
|
||||
//是否置顶,默认false
|
||||
public bool IsTop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 封面
|
||||
/// 封面
|
||||
/// </summary>
|
||||
public string? Cover { get; set; }
|
||||
//是否私有,默认false
|
||||
//是否私有,默认false
|
||||
public bool IsPrivate { get; set; }
|
||||
|
||||
//私有需要判断code权限
|
||||
//私有需要判断code权限
|
||||
public string? PrivateCode { get; set; }
|
||||
public DateTime CreationTime { get; set; }
|
||||
public DiscussPermissionTypeEnum PermissionType { get; set; }
|
||||
public bool IsAgree { get; set; } = false;
|
||||
public List<Guid>? PermissionUserIds { get; set; }
|
||||
public List<string> PermissionRoleCodes { get; set; } = new List<string>();
|
||||
|
||||
|
||||
|
||||
public BbsUserGetListOutputDto User { get; set; }
|
||||
|
||||
public PlateGetOutputDto Plate { get; set; }
|
||||
|
||||
public List<Guid>? DiscussLableIds { get; set; } = new List<Guid>();
|
||||
public List<DiscussLableGetOutputDto> Lables { get; set; } =new List<DiscussLableGetOutputDto>();
|
||||
|
||||
public bool HasPermission { get;internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置权限
|
||||
/// </summary>
|
||||
public void SetPassPermission()
|
||||
{
|
||||
HasPermission = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置无权限
|
||||
/// </summary>
|
||||
public void SetNoPermission()
|
||||
{
|
||||
HasPermission = false;
|
||||
Content=DiscussConst.Privacy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,25 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
|
||||
public DiscussPermissionTypeEnum PermissionType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 封面
|
||||
/// 封面
|
||||
/// </summary>
|
||||
public string? Cover { get; set; }
|
||||
|
||||
public int OrderNum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否禁止评论创建功能
|
||||
/// 是否禁止评论创建功能
|
||||
/// </summary>
|
||||
public bool IsDisableCreateComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标签
|
||||
/// </summary>
|
||||
public List<Guid>? DiscussLableIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 需求角色
|
||||
/// </summary>
|
||||
public List<string>? PermissionRoleCodes { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.DiscussLable;
|
||||
|
||||
public class DiscussLableGetOutputDto:EntityDto<Guid>
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? Color { get; set; }
|
||||
public string? BackgroundColor { get; set; }
|
||||
}
|
||||
@@ -3,7 +3,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.MyType
|
||||
/// <summary>
|
||||
/// Label输入创建对象
|
||||
/// </summary>
|
||||
public class MyTypeCreateInputVo
|
||||
public class DiscussLableCreateInputVo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? Color { get; set; }
|
||||
@@ -0,0 +1,9 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.MyType
|
||||
{
|
||||
public class DiscussLableGetListInputVo : PagedAndSortedResultRequestDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,10 @@ using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.MyType
|
||||
{
|
||||
public class MyTypeGetListOutputDto : EntityDto<Guid>
|
||||
public class DiscussLableGetListOutputDto : EntityDto<Guid>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? Color { get; set; }
|
||||
public string? BackgroundColor { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.MyType
|
||||
{
|
||||
public class MyTypeOutputDto : EntityDto<Guid>
|
||||
public class DiscussLableOutputDto : EntityDto<Guid>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? Color { get; set; }
|
||||
@@ -1,10 +1,9 @@
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.MyType
|
||||
{
|
||||
public class MyTypeUpdateInputVo
|
||||
public class DiscussLableUpdateInputVo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? Color { get; set; }
|
||||
public string? BackgroundColor { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.MyType
|
||||
{
|
||||
public class MyTypeGetListInputVo : PagedAndSortedResultRequestDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Color { get; set; }
|
||||
public string? BackgroundColor { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Shop;
|
||||
|
||||
public class BbsShopAccountDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 钱钱
|
||||
/// </summary>
|
||||
public decimal Money { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 积分
|
||||
/// </summary>
|
||||
public int Points { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 价值
|
||||
/// </summary>
|
||||
public decimal Value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Shop;
|
||||
|
||||
public class BuyShopInputDto
|
||||
{
|
||||
public Guid GoodsId { get; set; }
|
||||
public string ContactInformation { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Shop;
|
||||
|
||||
public class ShopGetListOutput:EntityDto<Guid>
|
||||
{
|
||||
/// <summary>
|
||||
/// 上架时间
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品类型
|
||||
/// </summary>
|
||||
public GoodsTypeEnum GoodsType{ get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下架时间
|
||||
/// </summary>
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每人限购数量
|
||||
/// </summary>
|
||||
public int LimitNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前库存数量
|
||||
/// </summary>
|
||||
public int StockNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品图片url
|
||||
/// </summary>
|
||||
public string ImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public string Describe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编号
|
||||
/// </summary>
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所需钱钱
|
||||
/// </summary>
|
||||
public decimal NeedMoney { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所需价值
|
||||
/// </summary>
|
||||
public decimal NeedValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所需积分
|
||||
/// </summary>
|
||||
public decimal NeedPoints { get; set; }
|
||||
|
||||
public int OrderNum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已限制
|
||||
/// </summary>
|
||||
public bool IsLimit { get; set; }
|
||||
}
|
||||
@@ -6,15 +6,8 @@ namespace Yi.Framework.Bbs.Application.Contracts.IServices
|
||||
/// <summary>
|
||||
/// Comment服务抽象
|
||||
/// </summary>
|
||||
public interface ICommentService{
|
||||
/// <summary>
|
||||
/// 发表评论
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="UserFriendlyException"></exception>
|
||||
// [Permission("bbs:comment:add")]
|
||||
// [Authorize]
|
||||
Task<CommentGetOutputDto> CreateAsync(CommentCreateInputVo input);
|
||||
public interface ICommentService : IYiCrudAppService<CommentGetOutputDto, CommentGetListOutputDto, Guid, CommentGetListInputVo, CommentCreateInputVo, CommentUpdateInputVo>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.IServices
|
||||
/// <summary>
|
||||
/// Label服务抽象
|
||||
/// </summary>
|
||||
public interface IMyTypeService : IYiCrudAppService<MyTypeOutputDto, MyTypeGetListOutputDto, Guid, MyTypeGetListInputVo, MyTypeCreateInputVo, MyTypeUpdateInputVo>
|
||||
public interface IDiscussLableService : IYiCrudAppService<DiscussLableOutputDto, DiscussLableGetListOutputDto, Guid, DiscussLableGetListInputVo, DiscussLableCreateInputVo, DiscussLableUpdateInputVo>
|
||||
{
|
||||
|
||||
}
|
||||
@@ -8,6 +8,5 @@ namespace Yi.Framework.Bbs.Application.Contracts.IServices
|
||||
/// </summary>
|
||||
public interface IDiscussService : IYiCrudAppService<DiscussGetOutputDto, DiscussGetListOutputDto, Guid, DiscussGetListInputVo, DiscussCreateInputVo, DiscussUpdateInputVo>
|
||||
{
|
||||
Task VerifyDiscussPermissionAsync(Guid discussId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\digital-collectibles\Yi.Framework.DigitalCollectibles.Application.Contracts\Yi.Framework.DigitalCollectibles.Application.Contracts.csproj" />
|
||||
<ProjectReference Include="..\..\rbac\Yi.Framework.Rbac.Application.Contracts\Yi.Framework.Rbac.Application.Contracts.csproj" />
|
||||
<ProjectReference Include="..\Yi.Framework.Bbs.Domain.Shared\Yi.Framework.Bbs.Domain.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using FreeRedis;
|
||||
using Hangfire;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Quartz;
|
||||
using Volo.Abp.BackgroundWorkers.Quartz;
|
||||
using Volo.Abp.BackgroundWorkers.Hangfire;
|
||||
using Volo.Abp.Caching;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
@@ -15,23 +15,27 @@ using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Jobs;
|
||||
|
||||
public class AccessLogCacheJob : QuartzBackgroundWorkerBase
|
||||
public class AccessLogCacheJob : HangfireBackgroundWorkerBase
|
||||
{
|
||||
private readonly ILocalEventBus _localEventBus;
|
||||
|
||||
public AccessLogCacheJob(ILocalEventBus localEventBus)
|
||||
{
|
||||
_localEventBus = localEventBus;
|
||||
JobDetail = JobBuilder.Create<AccessLogCacheJob>().WithIdentity(nameof(AccessLogCacheJob))
|
||||
.Build();
|
||||
RecurringJobId = "访问日志写入缓存";
|
||||
//每10秒执行一次,将本地缓存转入redis,防止丢数据
|
||||
CronExpression = "*/10 * * * * *";
|
||||
//
|
||||
// JobDetail = JobBuilder.Create<AccessLogCacheJob>().WithIdentity(nameof(AccessLogCacheJob))
|
||||
// .Build();
|
||||
|
||||
//每10秒执行一次,将本地缓存转入redis,防止丢数据
|
||||
Trigger = TriggerBuilder.Create().WithIdentity(nameof(AccessLogCacheJob))
|
||||
.WithSimpleSchedule((schedule) => { schedule.WithInterval(TimeSpan.FromSeconds(10)).RepeatForever();; })
|
||||
.Build();
|
||||
// Trigger = TriggerBuilder.Create().WithIdentity(nameof(AccessLogCacheJob))
|
||||
// .WithSimpleSchedule((schedule) => { schedule.WithInterval(TimeSpan.FromSeconds(10)).RepeatForever();; })
|
||||
// .Build();
|
||||
}
|
||||
|
||||
public override async Task Execute(IJobExecutionContext context)
|
||||
|
||||
public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
await _localEventBus.PublishAsync(new AccessLogResetArgs());
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using FreeRedis;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Quartz;
|
||||
using Volo.Abp.BackgroundWorkers.Quartz;
|
||||
using Volo.Abp.BackgroundWorkers.Hangfire;
|
||||
using Volo.Abp.Caching;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
using Yi.Framework.Bbs.Domain.Entities;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Caches;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
@@ -13,7 +11,7 @@ using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Jobs;
|
||||
|
||||
public class AccessLogStoreJob : QuartzBackgroundWorkerBase
|
||||
public class AccessLogStoreJob : HangfireBackgroundWorkerBase
|
||||
{
|
||||
private readonly ISqlSugarRepository<AccessLogAggregateRoot> _repository;
|
||||
|
||||
@@ -45,18 +43,23 @@ public class AccessLogStoreJob : QuartzBackgroundWorkerBase
|
||||
public AccessLogStoreJob(ISqlSugarRepository<AccessLogAggregateRoot> repository)
|
||||
{
|
||||
_repository = repository;
|
||||
JobDetail = JobBuilder.Create<AccessLogStoreJob>().WithIdentity(nameof(AccessLogStoreJob))
|
||||
.Build();
|
||||
|
||||
|
||||
RecurringJobId = "访问日志写入数据库";
|
||||
//每分钟执行一次
|
||||
Trigger = TriggerBuilder.Create().WithIdentity(nameof(AccessLogStoreJob))
|
||||
.WithCronSchedule("0 * * * * ?")
|
||||
.Build();
|
||||
CronExpression = "0 * * * * ?";
|
||||
// JobDetail = JobBuilder.Create<AccessLogStoreJob>().WithIdentity(nameof(AccessLogStoreJob))
|
||||
// .Build();
|
||||
// //每分钟执行一次
|
||||
// Trigger = TriggerBuilder.Create().WithIdentity(nameof(AccessLogStoreJob))
|
||||
// .WithCronSchedule("0 * * * * ?")
|
||||
// .Build();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override async Task Execute(IJobExecutionContext context)
|
||||
public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
if (EnableRedisCache)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Quartz;
|
||||
using Volo.Abp.BackgroundWorkers.Quartz;
|
||||
using Volo.Abp.BackgroundWorkers.Hangfire;
|
||||
using Yi.Framework.Bbs.Domain.Managers;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Jobs;
|
||||
@@ -7,20 +6,25 @@ namespace Yi.Framework.Bbs.Application.Jobs;
|
||||
/// <summary>
|
||||
/// 每日任务job
|
||||
/// </summary>
|
||||
public class AssignmentExpireTimeOutJob : QuartzBackgroundWorkerBase
|
||||
public class AssignmentExpireTimeOutJob : HangfireBackgroundWorkerBase
|
||||
{
|
||||
private readonly AssignmentManager _assignmentManager;
|
||||
|
||||
public AssignmentExpireTimeOutJob(AssignmentManager assignmentManager)
|
||||
{
|
||||
_assignmentManager = assignmentManager;
|
||||
JobDetail = JobBuilder.Create<AssignmentExpireTimeOutJob>().WithIdentity(nameof(AssignmentExpireTimeOutJob)).Build();
|
||||
//每个小时整点执行一次
|
||||
Trigger = TriggerBuilder.Create().WithIdentity(nameof(AssignmentExpireTimeOutJob)).WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
|
||||
RecurringJobId = "每日任务系统超时检测";
|
||||
//每分钟执行一次
|
||||
CronExpression = "0 * * * * ?";
|
||||
//
|
||||
// JobDetail = JobBuilder.Create<AssignmentExpireTimeOutJob>().WithIdentity(nameof(AssignmentExpireTimeOutJob)).Build();
|
||||
// //每个小时整点执行一次
|
||||
// Trigger = TriggerBuilder.Create().WithIdentity(nameof(AssignmentExpireTimeOutJob)).WithCronSchedule("0 0 * * * ?")
|
||||
// .Build();
|
||||
}
|
||||
|
||||
public override async Task Execute(IJobExecutionContext context)
|
||||
|
||||
public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
await _assignmentManager.ExpireTimeoutAsync();
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
using Quartz;
|
||||
using Volo.Abp.BackgroundWorkers.Quartz;
|
||||
using Volo.Abp.BackgroundWorkers.Hangfire;
|
||||
using Yi.Framework.Bbs.Domain.Managers;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Jobs
|
||||
{
|
||||
public class InterestRecordsJob : QuartzBackgroundWorkerBase
|
||||
public class InterestRecordsJob : HangfireBackgroundWorkerBase
|
||||
{
|
||||
private BankManager _bankManager;
|
||||
public InterestRecordsJob(BankManager bankManager)
|
||||
{
|
||||
_bankManager = bankManager;
|
||||
JobDetail = JobBuilder.Create<InterestRecordsJob>().WithIdentity(nameof(InterestRecordsJob)).Build();
|
||||
|
||||
|
||||
RecurringJobId = "银行利息积分刷新";
|
||||
//每个小时整点执行一次
|
||||
|
||||
Trigger = TriggerBuilder.Create().WithIdentity(nameof(InterestRecordsJob)).WithCronSchedule("0 0 * * * ?").Build();
|
||||
CronExpression = "0 0 * * * ?";
|
||||
|
||||
// JobDetail = JobBuilder.Create<InterestRecordsJob>().WithIdentity(nameof(InterestRecordsJob)).Build();
|
||||
//
|
||||
// //每个小时整点执行一次
|
||||
//
|
||||
// Trigger = TriggerBuilder.Create().WithIdentity(nameof(InterestRecordsJob)).WithCronSchedule("0 0 * * * ?").Build();
|
||||
|
||||
//测试
|
||||
// Trigger = TriggerBuilder.Create().WithIdentity(nameof(InterestRecordsJob))
|
||||
@@ -23,7 +27,8 @@ namespace Yi.Framework.Bbs.Application.Jobs
|
||||
// .RepeatForever())
|
||||
//.Build();
|
||||
}
|
||||
public override async Task Execute(IJobExecutionContext context)
|
||||
|
||||
public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
//创建一个记录,莫得了
|
||||
await _bankManager.GetCurrentInterestRate();
|
||||
|
||||
@@ -77,6 +77,56 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 作者主题,返回当前作者最新的主题
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("analyse/bbs-discuss/author/{userId}")]
|
||||
public async Task<List<DiscussGetListOutputDto>> GetAuthorDiscussAsync(
|
||||
[FromRoute] Guid userId,
|
||||
[FromQuery] PagedResultRequestDto input)
|
||||
{
|
||||
var output = await _forumManager._discussRepository._DbQueryable.Where(discuss=>discuss.CreatorId==userId)
|
||||
.Where(discuss=>discuss.PermissionType== DiscussPermissionTypeEnum.Public)
|
||||
.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
|
||||
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
|
||||
.OrderByDescending(discuss => discuss.CreationTime)
|
||||
.Select((discuss, user, info) => new DiscussGetListOutputDto
|
||||
{
|
||||
Id = discuss.Id,
|
||||
User = new BbsUserGetListOutputDto()
|
||||
{
|
||||
Id = user.Id,
|
||||
UserName = user.UserName,
|
||||
Nick = user.Nick,
|
||||
Icon = user.Icon,
|
||||
Level = info.Level,
|
||||
UserLimit = info.UserLimit
|
||||
}
|
||||
|
||||
}, true)
|
||||
.ToPageListAsync(input.SkipCount, input.MaxResultCount);
|
||||
var discussId = output.Select(x => x.Id);
|
||||
//点赞字典,key为主题id,y为用户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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Yi.Framework.Bbs.Domain.Entities;
|
||||
using Yi.Framework.Bbs.Domain.Entities.Integral;
|
||||
using Yi.Framework.Bbs.Domain.Managers;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
using Yi.Framework.DigitalCollectibles.Application.Contracts.IServices;
|
||||
using Yi.Framework.Rbac.Application.Contracts.IServices;
|
||||
using Yi.Framework.Rbac.Domain.Authorization;
|
||||
using Yi.Framework.Rbac.Domain.Shared.Consts;
|
||||
@@ -20,11 +21,16 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
|
||||
{
|
||||
private BbsUserManager _bbsUserManager;
|
||||
private IOnlineService _onlineService;
|
||||
private readonly IPointAnalyseService _pointAnalyseService;
|
||||
private readonly IValueAnalyseService _valueAnalyseService;
|
||||
|
||||
public BbsUserAnalyseService(BbsUserManager bbsUserManager, IOnlineService onlineService)
|
||||
public BbsUserAnalyseService(BbsUserManager bbsUserManager, IOnlineService onlineService,
|
||||
IPointAnalyseService pointAnalyseService, IValueAnalyseService valueAnalyseService)
|
||||
{
|
||||
_bbsUserManager = bbsUserManager;
|
||||
_onlineService = onlineService;
|
||||
_pointAnalyseService = pointAnalyseService;
|
||||
_valueAnalyseService = valueAnalyseService;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +116,9 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
|
||||
)
|
||||
.ToPageListAsync(pageIndex, input.MaxResultCount, total);
|
||||
|
||||
output.ForEach(x => { x.LevelName = _bbsUserManager._levelCacheDic[x.Level].Name; });
|
||||
var levelCache = await _bbsUserManager.GetLevelCacheMapAsync();
|
||||
|
||||
output.ForEach(x => { x.LevelName = levelCache[x.Level].Name; });
|
||||
return new PagedResultDto<MoneyTopUserDto>
|
||||
{
|
||||
Items = output,
|
||||
@@ -169,5 +177,118 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 积分排行榜
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("analyse/dc-user/points-top/{userId?}")]
|
||||
public async Task<PagedResultDto<PointsTopUserDto>> GetPointsTopAsync([FromQuery] PagedResultRequestDto input,
|
||||
[FromRoute] Guid? userId)
|
||||
{
|
||||
var result = await _pointAnalyseService.GetValueTopAsync(input, null);
|
||||
|
||||
var userIds = result.Items.Select(x => x.UserId).ToList();
|
||||
|
||||
var baseOutput = await _bbsUserManager._userRepository._DbQueryable
|
||||
.Where(u => userIds.Contains(u.Id))
|
||||
.LeftJoin<BbsUserExtraInfoEntity>((u, info) => u.Id == info.UserId)
|
||||
.Select((u, info) =>
|
||||
new BaseAnalyseTopUserDto
|
||||
{
|
||||
UserName = u.UserName,
|
||||
Nick = u.Nick,
|
||||
Icon = u.Icon,
|
||||
Level = info.Level,
|
||||
UserLimit = info.UserLimit,
|
||||
UserId = info.UserId
|
||||
}
|
||||
).ToListAsync();
|
||||
|
||||
|
||||
var output = new List<PointsTopUserDto>();
|
||||
var levelCache = await _bbsUserManager.GetLevelCacheMapAsync();
|
||||
result.Items.ToList().ForEach(x =>
|
||||
{
|
||||
var currentUserInfo = baseOutput.Where(u => u.UserId == x.UserId).FirstOrDefault();
|
||||
|
||||
if (currentUserInfo is not null)
|
||||
{
|
||||
output.Add(new PointsTopUserDto
|
||||
{
|
||||
UserName = currentUserInfo.UserName,
|
||||
Nick = currentUserInfo.Nick,
|
||||
Order = x.Order,
|
||||
Icon = currentUserInfo.Icon,
|
||||
Level = currentUserInfo.Level,
|
||||
LevelName = levelCache[currentUserInfo.Level].Name,
|
||||
UserLimit = UserLimitEnum.Normal,
|
||||
Points = x.Points
|
||||
});
|
||||
}
|
||||
});
|
||||
return new PagedResultDto<PointsTopUserDto>
|
||||
{
|
||||
Items = output,
|
||||
TotalCount = result.TotalCount
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 价值排行榜
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("analyse/dc-user/value-top/{userId?}")]
|
||||
public async Task<PagedResultDto<ValueTopUserDto>> GetValueTopAsync([FromQuery] PagedResultRequestDto input,
|
||||
[FromRoute] Guid? userId)
|
||||
{
|
||||
var result = await _valueAnalyseService.GetValueTopAsync(input, null);
|
||||
|
||||
var userIds = result.Items.Select(x => x.UserId).ToList();
|
||||
|
||||
var baseOutput = await _bbsUserManager._userRepository._DbQueryable
|
||||
.Where(u => userIds.Contains(u.Id))
|
||||
.LeftJoin<BbsUserExtraInfoEntity>((u, info) => u.Id == info.UserId)
|
||||
.Select((u, info) =>
|
||||
new BaseAnalyseTopUserDto
|
||||
{
|
||||
UserName = u.UserName,
|
||||
Nick = u.Nick,
|
||||
Icon = u.Icon,
|
||||
Level = info.Level,
|
||||
UserLimit = info.UserLimit,
|
||||
UserId = info.UserId
|
||||
}
|
||||
).ToListAsync();
|
||||
|
||||
|
||||
var output = new List<ValueTopUserDto>();
|
||||
var levelCache = await _bbsUserManager.GetLevelCacheMapAsync();
|
||||
result.Items.ToList().ForEach(x =>
|
||||
{
|
||||
var currentUserInfo = baseOutput.Where(u => u.UserId == x.UserId).FirstOrDefault();
|
||||
|
||||
if (currentUserInfo is not null)
|
||||
{
|
||||
output.Add(new ValueTopUserDto
|
||||
{
|
||||
UserName = currentUserInfo.UserName,
|
||||
Nick = currentUserInfo.Nick,
|
||||
Order = x.Order,
|
||||
Icon = currentUserInfo.Icon,
|
||||
Level = currentUserInfo.Level,
|
||||
LevelName =levelCache[currentUserInfo.Level].Name,
|
||||
UserLimit = UserLimitEnum.Normal,
|
||||
Value = x.Value
|
||||
});
|
||||
}
|
||||
});
|
||||
return new PagedResultDto<ValueTopUserDto>
|
||||
{
|
||||
Items = output,
|
||||
TotalCount = result.TotalCount
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,13 +27,19 @@ namespace Yi.Framework.Bbs.Application.Services
|
||||
var userEntity = await _bbsUserManager._userRepository.GetFirstAsync(x => x.UserName == userNameOrUserId);
|
||||
if (userEntity == null)
|
||||
{
|
||||
throw new Volo.Abp.UserFriendlyException("该用户不存在");
|
||||
throw new UserFriendlyException("该用户不存在");
|
||||
}
|
||||
userId= userEntity.Id;
|
||||
}
|
||||
|
||||
var output =await _bbsUserManager.GetBbsUserInfoAsync(userId);
|
||||
|
||||
|
||||
//不是自己
|
||||
if (CurrentUser.Id != output.Id)
|
||||
{
|
||||
output.Phone = null;
|
||||
output.Email=null;
|
||||
}
|
||||
return output!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,22 +30,21 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
/// <summary>
|
||||
/// Article服务实现
|
||||
/// </summary>
|
||||
|
||||
public class ArticleService : YiCrudAppService<ArticleAggregateRoot, ArticleGetOutputDto, ArticleGetListOutputDto, Guid, ArticleGetListInputVo, ArticleCreateInputVo, ArticleUpdateInputVo>,
|
||||
IArticleService
|
||||
public class ArticleService : YiCrudAppService<ArticleAggregateRoot, ArticleGetOutputDto, ArticleGetListOutputDto,
|
||||
Guid, ArticleGetListInputVo, ArticleCreateInputVo, ArticleUpdateInputVo>,
|
||||
IArticleService
|
||||
{
|
||||
public ArticleService(IArticleRepository articleRepository,
|
||||
ISqlSugarRepository<DiscussAggregateRoot> discussRepository,
|
||||
IDiscussService discussService,
|
||||
ForumManager forumManager) : base(articleRepository)
|
||||
{
|
||||
|
||||
_articleRepository = articleRepository;
|
||||
_discussRepository = discussRepository;
|
||||
_discussService = discussService;
|
||||
_forumManager = forumManager;
|
||||
|
||||
}
|
||||
|
||||
private ForumManager _forumManager;
|
||||
private IArticleRepository _articleRepository;
|
||||
private ISqlSugarRepository<DiscussAggregateRoot> _discussRepository;
|
||||
@@ -55,13 +54,34 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
{
|
||||
RefAsync<int> total = 0;
|
||||
|
||||
var entities = await _articleRepository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name.Contains(input.Name!))
|
||||
//.WhereIF(!string.IsNullOrEmpty(input.Code), x => x.Name.Contains(input.Code!))
|
||||
.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 _articleRepository._DbQueryable
|
||||
.WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name.Contains(input.Name!))
|
||||
.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<ArticleGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询文章
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<ArticleGetOutputDto> GetAsync(Guid id)
|
||||
{
|
||||
var entity = await _articleRepository.GetAsync(id);
|
||||
var output = entity.Adapt<ArticleGetOutputDto>();
|
||||
if (!await _forumManager.VerifyDiscussPermissionAsync(entity.DiscussId, CurrentUser.Id, CurrentUser.Roles))
|
||||
{
|
||||
output.SetNoPermission();
|
||||
}
|
||||
else
|
||||
{
|
||||
output.SetPassPermission();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文章全部树级信息
|
||||
@@ -72,17 +92,13 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
[Route("article/all/discuss-id/{discussId}")]
|
||||
public async Task<List<ArticleAllOutputDto>> GetAllAsync([FromRoute] Guid discussId)
|
||||
{
|
||||
await _discussService.VerifyDiscussPermissionAsync(discussId);
|
||||
|
||||
|
||||
var entities = await _articleRepository.GetTreeAsync(x => x.DiscussId == discussId);
|
||||
//var result = entities.Tile();
|
||||
var items = entities.Adapt<List<ArticleAllOutputDto>>();
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询文章
|
||||
/// 查询文章概述
|
||||
/// </summary>
|
||||
/// <param name="discussId"></param>
|
||||
/// <returns></returns>
|
||||
@@ -109,7 +125,7 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
[Authorize]
|
||||
public async override Task<ArticleGetOutputDto> CreateAsync(ArticleCreateInputVo input)
|
||||
{
|
||||
await VerifyDiscussCreateIdAsync(input.DiscussId);
|
||||
await VerifyPermissionAsync(input.DiscussId);
|
||||
return await base.CreateAsync(input);
|
||||
}
|
||||
|
||||
@@ -122,7 +138,7 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
public override async Task<ArticleGetOutputDto> UpdateAsync(Guid id, ArticleUpdateInputVo input)
|
||||
{
|
||||
var entity = await _articleRepository.GetByIdAsync(id);
|
||||
await VerifyDiscussCreateIdAsync(entity.DiscussId);
|
||||
await VerifyPermissionAsync(entity.DiscussId);
|
||||
return await base.UpdateAsync(id, input);
|
||||
}
|
||||
|
||||
@@ -135,7 +151,7 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
public override async Task DeleteAsync(Guid id)
|
||||
{
|
||||
var entity = await _articleRepository.GetByIdAsync(id);
|
||||
await VerifyDiscussCreateIdAsync(entity.DiscussId);
|
||||
await VerifyPermissionAsync(entity.DiscussId);
|
||||
await base.DeleteAsync(id);
|
||||
}
|
||||
|
||||
@@ -144,8 +160,10 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
/// 导入文章
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task PostImportAsync([FromQuery] ArticleImprotDto input, [FromForm][Required] IFormFileCollection file)
|
||||
public async Task PostImportAsync([FromQuery] ArticleImprotDto input,
|
||||
[FromForm] [Required] IFormFileCollection file)
|
||||
{
|
||||
await VerifyPermissionAsync(input.DiscussId);
|
||||
var fileObjs = new List<FileObject>();
|
||||
if (file.Count > 0)
|
||||
{
|
||||
@@ -172,45 +190,18 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
{
|
||||
throw new UserFriendlyException("未选择文件");
|
||||
}
|
||||
|
||||
//使用简单工厂根据传入的类型进行判断
|
||||
await _forumManager.PostImportAsync(input.DiscussId, input.ArticleParentId, fileObjs, input.ImportType);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 校验创建权限,userId为主题创建者
|
||||
/// </summary>
|
||||
/// <param name="disucssId"></param>
|
||||
/// <returns></returns>
|
||||
private async Task VerifyDiscussCreateIdAsync(Guid disucssId)
|
||||
private async Task VerifyPermissionAsync(Guid discussId)
|
||||
{
|
||||
var discuss = await _discussRepository.GetFirstAsync(x => x.Id == disucssId);
|
||||
if (discuss is null)
|
||||
if (!await _forumManager.VerifyDiscussPermissionAsync(discussId, CurrentUser.Id, isVerifyLook: false))
|
||||
{
|
||||
throw new UserFriendlyException(DiscussConst.No_Exist);
|
||||
}
|
||||
|
||||
//这块有点绕,这个版本的写法比较清晰
|
||||
bool result = false;
|
||||
|
||||
if (CurrentUser.GetPermissions().Contains(UserConst.AdminPermissionCode))
|
||||
{
|
||||
//如果是超管,直接跳过
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//如果不是超管,必须满足作者是自己,同时还有发布的权限
|
||||
if (discuss.CreatorId == CurrentUser.Id)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
throw new UserFriendlyException("权限不足,请联系主题作者或管理员申请开通");
|
||||
throw new UserFriendlyException("您无权限进行操作", "403");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.BbsUser;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.Comment;
|
||||
using Yi.Framework.Bbs.Application.Contracts.IServices;
|
||||
@@ -23,11 +21,19 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
/// <summary>
|
||||
/// 评论
|
||||
/// </summary>
|
||||
public class CommentService : ApplicationService,
|
||||
public class CommentService : YiCrudAppService<CommentAggregateRoot, CommentGetOutputDto, CommentGetListOutputDto, Guid, CommentGetListInputVo, CommentCreateInputVo, CommentUpdateInputVo>,
|
||||
ICommentService
|
||||
{
|
||||
private readonly ISqlSugarRepository<CommentAggregateRoot, Guid> _repository;
|
||||
private readonly BbsUserManager _bbsUserManager;
|
||||
public CommentService(ForumManager forumManager, ISqlSugarRepository<DiscussAggregateRoot> discussRepository, IDiscussService discussService, ISqlSugarRepository<CommentAggregateRoot, Guid> CommentRepository, BbsUserManager bbsUserManager) : base(CommentRepository)
|
||||
{
|
||||
_forumManager = forumManager;
|
||||
_discussRepository = discussRepository;
|
||||
_discussService = discussService;
|
||||
_repository = CommentRepository;
|
||||
_bbsUserManager = bbsUserManager;
|
||||
}
|
||||
|
||||
private ForumManager _forumManager { get; set; }
|
||||
|
||||
@@ -36,50 +42,108 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
private ISqlSugarRepository<DiscussAggregateRoot> _discussRepository { get; set; }
|
||||
|
||||
private IDiscussService _discussService { get; set; }
|
||||
|
||||
|
||||
public async Task<CommentGetOutputDto> Create2Async(CommentCreateInputVo input)
|
||||
/// <summary>
|
||||
/// 获取改主题下的评论,结构为二维列表,该查询无分页
|
||||
/// </summary>
|
||||
/// <param name="discussId"></param>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<PagedResultDto<CommentGetListOutputDto>> GetDiscussIdAsync([FromRoute] Guid discussId, [FromQuery] CommentGetListInputVo input)
|
||||
{
|
||||
var entity = new CommentAggregateRoot(Guid.Empty);
|
||||
return new CommentGetOutputDto();
|
||||
await _forumManager.VerifyDiscussPermissionAsync(discussId,CurrentUser.Id);
|
||||
|
||||
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.Content), x => x.Content.Contains(input.Content))
|
||||
.Where(x => x.DiscussId == discussId)
|
||||
.Includes(x => x.CreateUser)
|
||||
.ToListAsync();
|
||||
|
||||
//该实体需要进行转换
|
||||
|
||||
//同时为所有用户id进行bbs的扩展即可
|
||||
List<Guid> userIds = entities.Where(x => x.CreatorId != null).Select(x => x.CreatorId ?? Guid.Empty).ToList();
|
||||
var bbsUserInfoDic = (await _bbsUserManager.GetBbsUserInfoAsync(userIds)).ToDictionary(x => x.Id);
|
||||
|
||||
//------数据查询完成------,以下只是dto的简单组装
|
||||
|
||||
//从根目录开始组装
|
||||
//结果初始值,第一层等于全部根节点
|
||||
var allOutPut = entities.OrderByDescending(x => x.CreationTime).ToList();
|
||||
|
||||
//获取全量主题评论, 先获取顶级的,将其他子组合到顶级下,形成一个二维,先转成dto
|
||||
List<CommentGetListOutputDto> allOutoutDto = await MapToGetListOutputDtosAsync(allOutPut);
|
||||
|
||||
//开始映射额外用户信息字段
|
||||
allOutoutDto?.ForEach(x => x.CreateUser = bbsUserInfoDic[x.CreatorId ?? Guid.Empty].Adapt<BbsUserGetOutputDto>());
|
||||
|
||||
//开始组装dto的层级关系
|
||||
//将全部数据进行hash
|
||||
var dic = allOutoutDto.ToDictionary(x => x.Id);
|
||||
|
||||
foreach (var comment in allOutoutDto)
|
||||
{
|
||||
//不是根节点,需要赋值 被评论者用户信息等
|
||||
if (comment.ParentId != Guid.Empty)
|
||||
{
|
||||
if (dic.ContainsKey(comment.ParentId))
|
||||
{
|
||||
var parentComment = dic[comment.ParentId];
|
||||
comment.CommentedUser = parentComment.CreateUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//root或者parent id,根节点都是等于0的
|
||||
var id = comment.RootId;
|
||||
if (id != Guid.Empty)
|
||||
{
|
||||
dic[id].Children.Add(comment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//子类需要排序
|
||||
var rootOutoutDto = allOutoutDto.Where(x => x.ParentId == Guid.Empty).ToList();
|
||||
rootOutoutDto?.ForEach(x =>
|
||||
{
|
||||
x.Children = x.Children.OrderByDescending(x => x.CreationTime).ToList();
|
||||
});
|
||||
return new PagedResultDto<CommentGetListOutputDto>(entities.Count(), rootOutoutDto);
|
||||
}
|
||||
|
||||
[HttpGet("Create22")]
|
||||
public async Task<CommentGetOutputDto> Create22Async(CommentCreateInputVo input)
|
||||
{
|
||||
var entity = new CommentAggregateRoot(Guid.Empty);
|
||||
return new CommentGetOutputDto();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发表评论
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="UserFriendlyException"></exception>
|
||||
// [Permission("bbs:comment:add")]
|
||||
// [Authorize]
|
||||
public async Task<CommentGetOutputDto> CreateAsync(CommentCreateInputVo input)
|
||||
[Permission("bbs:comment:add")]
|
||||
[Authorize]
|
||||
public override async Task<CommentGetOutputDto> CreateAsync(CommentCreateInputVo input)
|
||||
{
|
||||
// var discuess = await _discussRepository.GetFirstAsync(x => x.Id == input.DiscussId);
|
||||
// if (discuess is null)
|
||||
// {
|
||||
// throw new UserFriendlyException(DiscussConst.No_Exist);
|
||||
// }
|
||||
if (string.IsNullOrWhiteSpace(input.Content)|| input.Content=="<p><br></p>")
|
||||
{
|
||||
throw new UserFriendlyException("评论不能为空");
|
||||
}
|
||||
|
||||
var discuess = await _discussRepository.GetFirstAsync(x => x.Id == input.DiscussId);
|
||||
if (discuess is null)
|
||||
{
|
||||
throw new UserFriendlyException(DiscussConst.No_Exist);
|
||||
}
|
||||
//不是超级管理员,且主题开启禁止评论
|
||||
|
||||
// if (discuess.IsDisableCreateComment == true && !CurrentUser.GetPermissions().Contains(UserConst.AdminPermissionCode))
|
||||
// {
|
||||
// throw new UserFriendlyException("该主题已禁止评论功能");
|
||||
// }
|
||||
if (discuess.IsDisableCreateComment == true && !CurrentUser.GetPermissions().Contains(UserConst.AdminPermissionCode))
|
||||
{
|
||||
throw new UserFriendlyException("该主题已禁止评论功能");
|
||||
}
|
||||
|
||||
|
||||
// var entity = await _forumManager.CreateCommentAsync(input.DiscussId, input.ParentId, input.RootId, input.Content);
|
||||
var entity = new CommentAggregateRoot(Guid.Empty);
|
||||
return new CommentGetOutputDto();
|
||||
var entity = await _forumManager.CreateCommentAsync(input.DiscussId, input.ParentId, input.RootId, input.Content);
|
||||
return await MapToGetOutputDtoAsync(entity);
|
||||
}
|
||||
|
||||
public CommentService(IRepository<CommentAggregateRoot, Guid> repository)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Data;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.MyType;
|
||||
using Yi.Framework.Bbs.Application.Contracts.IServices;
|
||||
using Yi.Framework.Bbs.Domain.Entities.Forum;
|
||||
using Yi.Framework.Ddd.Application;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
{
|
||||
/// <summary>
|
||||
/// DiscussLable服务实现
|
||||
/// </summary>
|
||||
public class DiscussLableService : YiCrudAppService<DiscussLableAggregateRoot, DiscussLableOutputDto,
|
||||
DiscussLableGetListOutputDto, Guid, DiscussLableGetListInputVo, DiscussLableCreateInputVo,
|
||||
DiscussLableUpdateInputVo>,
|
||||
IDiscussLableService
|
||||
{
|
||||
private ISqlSugarRepository<DiscussLableAggregateRoot, Guid> _repository;
|
||||
|
||||
public DiscussLableService(ISqlSugarRepository<DiscussLableAggregateRoot, Guid> repository) : base(repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
[HttpGet("discuss-lable/all")]
|
||||
public async Task<ListResultDto<DiscussLableGetListOutputDto>> GetAllListAsync(DiscussLableGetListInputVo input)
|
||||
{
|
||||
var order = input.Sorting ?? nameof(DiscussLableAggregateRoot.Name);
|
||||
var output = await _repository._DbQueryable
|
||||
.WhereIF(input.Name is not null, x => x.Name.Contains(input.Name))
|
||||
.OrderBy(order)
|
||||
.Select(x => new DiscussLableGetListOutputDto(), true)
|
||||
.ToListAsync();
|
||||
return new ListResultDto<DiscussLableGetListOutputDto>(output);
|
||||
}
|
||||
|
||||
public override async Task<PagedResultDto<DiscussLableGetListOutputDto>> GetListAsync(
|
||||
DiscussLableGetListInputVo input)
|
||||
{
|
||||
RefAsync<int> total = 0;
|
||||
var order = input.Sorting ?? nameof(DiscussLableAggregateRoot.Name);
|
||||
var output = await _repository._DbQueryable
|
||||
.WhereIF(input.Name is not null, x => x.Name.Contains(input.Name))
|
||||
.OrderBy(order)
|
||||
.Select(x => new DiscussLableGetListOutputDto(), true)
|
||||
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
|
||||
return new PagedResultDto<DiscussLableGetListOutputDto>(total, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
@@ -9,10 +10,12 @@ using Volo.Abp.EventBus.Local;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.BbsUser;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.DiscussLable;
|
||||
using Yi.Framework.Bbs.Application.Contracts.IServices;
|
||||
using Yi.Framework.Bbs.Domain.Entities;
|
||||
using Yi.Framework.Bbs.Domain.Entities.Forum;
|
||||
using Yi.Framework.Bbs.Domain.Managers;
|
||||
using Yi.Framework.Bbs.Domain.Repositories;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Consts;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Etos;
|
||||
@@ -36,16 +39,17 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
private ISqlSugarRepository<DiscussTopEntity> _discussTopRepository;
|
||||
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
|
||||
private BbsUserManager _bbsUserManager;
|
||||
|
||||
private IDiscussLableRepository _discussLableRepository;
|
||||
public DiscussService(BbsUserManager bbsUserManager, ForumManager forumManager,
|
||||
ISqlSugarRepository<DiscussTopEntity> discussTopRepository,
|
||||
ISqlSugarRepository<PlateAggregateRoot> plateEntityRepository, ILocalEventBus localEventBus,
|
||||
ISqlSugarRepository<AgreeEntity> agreeRepository) : base(forumManager._discussRepository)
|
||||
ISqlSugarRepository<AgreeEntity> agreeRepository, IDiscussLableRepository discussLableRepository) : base(forumManager._discussRepository)
|
||||
{
|
||||
_forumManager = forumManager;
|
||||
_plateEntityRepository = plateEntityRepository;
|
||||
_localEventBus = localEventBus;
|
||||
_agreeRepository = agreeRepository;
|
||||
_discussLableRepository = discussLableRepository;
|
||||
_discussTopRepository = discussTopRepository;
|
||||
_bbsUserManager = bbsUserManager;
|
||||
}
|
||||
@@ -55,8 +59,7 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
|
||||
|
||||
private ISqlSugarRepository<PlateAggregateRoot> _plateEntityRepository { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 单查
|
||||
/// </summary>
|
||||
@@ -65,15 +68,14 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
public async override Task<DiscussGetOutputDto> GetAsync(Guid id)
|
||||
{
|
||||
//查询主题发布 浏览主题 事件,浏览数+1
|
||||
var item = await _forumManager._discussRepository._DbQueryable
|
||||
var output = 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(),
|
||||
IsAgree = false,
|
||||
User = new BbsUserGetListOutputDto()
|
||||
{
|
||||
UserName = user.UserName,
|
||||
@@ -94,16 +96,45 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
Logo = plate.Logo
|
||||
}
|
||||
}, true)
|
||||
.SingleAsync(discuss => discuss.Id == id);
|
||||
.FirstAsync(discuss => discuss.Id == id);
|
||||
|
||||
if (item is not null)
|
||||
if (output is null)
|
||||
{
|
||||
await VerifyDiscussPermissionAsync(item.Id);
|
||||
await _localEventBus.PublishAsync(new SeeDiscussEventArgs
|
||||
{ DiscussId = item.Id, OldSeeNum = item.SeeNum });
|
||||
throw new UserFriendlyException("该主题不存在", "404");
|
||||
}
|
||||
|
||||
//组装点赞
|
||||
var agreeCreatorList =
|
||||
(await _agreeRepository._DbQueryable.Where(x => x.DiscussId == output.Id).Select(x=>x.CreatorId).ToListAsync());
|
||||
//已登录
|
||||
if (CurrentUser.Id is not null)
|
||||
{
|
||||
output.IsAgree = agreeCreatorList.Contains(CurrentUser.Id);
|
||||
}
|
||||
|
||||
//组装标签
|
||||
var lableDic=await _discussLableRepository.GetDiscussLableCacheMapAsync();
|
||||
foreach (var lableId in output.DiscussLableIds)
|
||||
{
|
||||
if (lableDic.TryGetValue(lableId,out var item))
|
||||
{
|
||||
output.Lables.Add(item.Adapt<DiscussLableGetOutputDto>());
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
//如果没有权限
|
||||
if (!await _forumManager.VerifyDiscussPermissionAsync(output.Id,CurrentUser.Id, CurrentUser.Roles))
|
||||
{
|
||||
output.SetNoPermission();
|
||||
}
|
||||
else
|
||||
{
|
||||
output.SetPassPermission();
|
||||
}
|
||||
|
||||
await _localEventBus.PublishAsync(new SeeDiscussEventArgs
|
||||
{ DiscussId = output.Id, OldSeeNum = output.SeeNum });
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,14 +185,14 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
(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);
|
||||
|
||||
//等级、是否点赞赋值
|
||||
|
||||
var levelCacheDic= await _bbsUserManager.GetLevelCacheMapAsync();
|
||||
var lableDic=await _discussLableRepository.GetDiscussLableCacheMapAsync();
|
||||
|
||||
//组装等级、是否点赞赋值、标签
|
||||
items?.ForEach(x =>
|
||||
{
|
||||
x.User.LevelName = _bbsUserManager._levelCacheDic[x.User.Level].Name;
|
||||
x.User.LevelName = levelCacheDic[x.User.Level].Name;
|
||||
if (CurrentUser.Id is not null)
|
||||
{
|
||||
//默认fasle
|
||||
@@ -170,6 +201,15 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
x.IsAgree = userIds.Contains(CurrentUser.Id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var lableId in x.DiscussLableIds)
|
||||
{
|
||||
if (lableDic.TryGetValue(lableId,out var item))
|
||||
{
|
||||
x.Lables.Add(item.Adapt<DiscussLableGetOutputDto>());
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
return new PagedResultDto<DiscussGetListOutputDto>(total, items);
|
||||
}
|
||||
@@ -199,7 +239,6 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
Address = user.Address,
|
||||
Age = user.Age,
|
||||
CreationTime = user.CreationTime,
|
||||
|
||||
Level = info.Level,
|
||||
Introduction = user.Introduction,
|
||||
Icon = user.Icon,
|
||||
@@ -212,7 +251,20 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
}
|
||||
}, true)
|
||||
.ToListAsync();
|
||||
output?.ForEach(x => x.User.LevelName = _bbsUserManager._levelCacheDic[x.User.Level].Name);
|
||||
var levelCacheDic= await _bbsUserManager.GetLevelCacheMapAsync();
|
||||
var lableDic=await _discussLableRepository.GetDiscussLableCacheMapAsync();
|
||||
|
||||
output?.ForEach(x =>
|
||||
{
|
||||
x.User.LevelName = levelCacheDic[x.User.Level].Name;
|
||||
foreach (var lableId in x.DiscussLableIds)
|
||||
{
|
||||
if (lableDic.TryGetValue(lableId,out var item))
|
||||
{
|
||||
x.Lables.Add(item.Adapt<DiscussLableGetOutputDto>());
|
||||
}
|
||||
}
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -250,36 +302,10 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
return await MapToGetOutputDtoAsync(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验主题查询权限
|
||||
/// </summary>
|
||||
/// <param name="discussId"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="UserFriendlyException"></exception>
|
||||
public async Task VerifyDiscussPermissionAsync(Guid discussId)
|
||||
|
||||
public override Task<DiscussGetOutputDto> UpdateAsync(Guid id, DiscussUpdateInputVo input)
|
||||
{
|
||||
var discuss = await _forumManager._discussRepository.GetFirstAsync(x => x.Id == discussId);
|
||||
if (discuss is null)
|
||||
{
|
||||
throw new UserFriendlyException(DiscussConst.No_Exist);
|
||||
}
|
||||
|
||||
if (discuss.PermissionType == DiscussPermissionTypeEnum.Oneself)
|
||||
{
|
||||
if (discuss.CreatorId != CurrentUser.Id)
|
||||
{
|
||||
throw new UserFriendlyException(DiscussConst.Privacy);
|
||||
}
|
||||
}
|
||||
|
||||
if (discuss.PermissionType == DiscussPermissionTypeEnum.User)
|
||||
{
|
||||
if (discuss.CreatorId != CurrentUser.Id &&
|
||||
!discuss.PermissionUserIds.Contains(CurrentUser.Id ?? Guid.Empty))
|
||||
{
|
||||
throw new UserFriendlyException(DiscussConst.Privacy);
|
||||
}
|
||||
}
|
||||
return base.UpdateAsync(id, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Data;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.MyType;
|
||||
using Yi.Framework.Bbs.Application.Contracts.IServices;
|
||||
using Yi.Framework.Bbs.Domain.Entities.Forum;
|
||||
using Yi.Framework.Ddd.Application;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Services.Forum
|
||||
{
|
||||
/// <summary>
|
||||
/// Label服务实现
|
||||
/// </summary>
|
||||
public class MyTypeService : YiCrudAppService<MyTypeEntity, MyTypeOutputDto, MyTypeGetListOutputDto, Guid, MyTypeGetListInputVo, MyTypeCreateInputVo, MyTypeUpdateInputVo>,
|
||||
IMyTypeService
|
||||
{
|
||||
private ISqlSugarRepository<MyTypeEntity, Guid> _repository;
|
||||
public MyTypeService(ISqlSugarRepository<MyTypeEntity, Guid> repository, IDataFilter dataFilter) : base(repository)
|
||||
{
|
||||
_repository = repository;
|
||||
_dataFilter = dataFilter;
|
||||
}
|
||||
|
||||
private IDataFilter _dataFilter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前用户的主题类型
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public Task<PagedResultDto<MyTypeGetListOutputDto>> GetListCurrentAsync(MyTypeGetListInputVo input)
|
||||
{
|
||||
//过滤器需要更换
|
||||
//_dataFilter.Enable<MyTypeEntity>(x => x.UserId == CurrentUser.Id);
|
||||
|
||||
//_dataFilter.AddFilter<MyTypeEntity>(x => x.UserId == CurrentUser.Id);
|
||||
return base.GetListAsync(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<MyTypeOutputDto> CreateAsync(MyTypeCreateInputVo input)
|
||||
{
|
||||
var entity = await MapToEntityAsync(input);
|
||||
entity.UserId = CurrentUser.Id ?? Guid.Empty;
|
||||
entity.IsDeleted = false;
|
||||
var outputEntity = await _repository.InsertReturnEntityAsync(entity);
|
||||
return await MapToGetOutputDtoAsync(outputEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace Yi.Framework.Bbs.Application.Services.Integral
|
||||
|
||||
/// <summary>
|
||||
/// 大转盘
|
||||
/// Todo: 可放入领域层
|
||||
/// Todo: 可放入领域层,但是太简单了,不重要
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Authorize]
|
||||
@@ -32,13 +32,7 @@ namespace Yi.Framework.Bbs.Application.Services.Integral
|
||||
int[] values = new int[10] { 0, 10, 30, 50, 60, 80, 90, 100, 200, 666 };
|
||||
var index = GetWheelIndex();
|
||||
var value = values[index] - 50;
|
||||
|
||||
////不存在负数钱钱
|
||||
//if (value < 0)
|
||||
//{
|
||||
// value = 0;
|
||||
//}
|
||||
|
||||
|
||||
//修改钱钱,如果钱钱不足,直接会丢出去,那本次抽奖将无效
|
||||
await _localEventBus.PublishAsync(new MoneyChangeEventArgs { UserId = CurrentUser.Id!.Value, Number = value }, false);
|
||||
|
||||
@@ -47,7 +41,7 @@ namespace Yi.Framework.Bbs.Application.Services.Integral
|
||||
|
||||
private int GetWheelIndex()
|
||||
{
|
||||
int[] probabilities = { 30, 40, 30, 15, 15, 10, 4, 3, 2, 1 };
|
||||
int[] probabilities = {5 , 30, 40, 30, 20, 10, 4, 3, 2, 1 };
|
||||
|
||||
int total = 0;
|
||||
foreach (var prob in probabilities)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.EventBus.Local;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.Shop;
|
||||
using Yi.Framework.Bbs.Domain.Entities;
|
||||
using Yi.Framework.Bbs.Domain.Entities.Shop;
|
||||
using Yi.Framework.Bbs.Domain.Managers;
|
||||
using Yi.Framework.Bbs.Domain.Managers.Shop;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Etos;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Services.Shop;
|
||||
|
||||
/// <summary>
|
||||
/// bbs商城服务
|
||||
/// </summary>
|
||||
public class BbsShopService : ApplicationService
|
||||
{
|
||||
private readonly ISqlSugarRepository<BbsGoodsAggregateRoot> _repository;
|
||||
private readonly ISqlSugarRepository<BbsGoodsApplyAggregateRoot> _applyRepository;
|
||||
private readonly BbsShopManager _bbsShopManager;
|
||||
private readonly ISqlSugarRepository<BbsUserExtraInfoEntity> _bbsUserRepository;
|
||||
private ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService<ILocalEventBus>();
|
||||
|
||||
public BbsShopService(ISqlSugarRepository<BbsGoodsAggregateRoot> repository,
|
||||
ISqlSugarRepository<BbsGoodsApplyAggregateRoot> applyRepository, BbsShopManager bbsShopManager,
|
||||
ISqlSugarRepository<BbsUserExtraInfoEntity> bbsUserRepository)
|
||||
{
|
||||
_repository = repository;
|
||||
_applyRepository = applyRepository;
|
||||
_bbsShopManager = bbsShopManager;
|
||||
_bbsUserRepository = bbsUserRepository;
|
||||
}
|
||||
|
||||
//商城列表
|
||||
[Authorize]
|
||||
public async Task<PagedResultDto<ShopGetListOutput>> GetListAsync(PagedAndSortedResultRequestDto input)
|
||||
{
|
||||
var output = new List<ShopGetListOutput>();
|
||||
var userId = CurrentUser.GetId();
|
||||
RefAsync<int> total = 0;
|
||||
var entities = await _repository._DbQueryable
|
||||
.Where(x => x.EndTime > DateTime.Now)
|
||||
.OrderBy(x => x.OrderNum)
|
||||
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
|
||||
var applyEntities = await _applyRepository.GetListAsync(x => x.UserId == userId);
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var dto = entity.Adapt<ShopGetListOutput>();
|
||||
if (entity.GoodsType == GoodsTypeEnum.Apply)
|
||||
{
|
||||
//大于限购数量
|
||||
if (applyEntities.Count(x => x.GoodsId == entity.Id) >= entity.LimitNumber)
|
||||
{
|
||||
dto.IsLimit = true;
|
||||
}
|
||||
}
|
||||
|
||||
output.Add(dto);
|
||||
}
|
||||
|
||||
return new PagedResultDto<ShopGetListOutput>(total, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 购买商品
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public async Task PostBuyAsync([FromBody] BuyShopInputDto input)
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
await _bbsShopManager.BuyAsync(userId, input.GoodsId, input.ContactInformation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取该用户汇总信息(钱钱、积分、价值)
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public async Task<BbsShopAccountDto> GetAccountAsync()
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
var output = new BbsShopAccountDto();
|
||||
var money = await _bbsUserRepository._DbQueryable.Where(x => x.UserId == userId).Select(x => x.Money)
|
||||
.FirstAsync();
|
||||
var eto = new SetAccountInfoEto(userId);
|
||||
await LocalEventBus.PublishAsync(eto, false);
|
||||
//钱钱累加
|
||||
output.Money =money+ eto.Money;
|
||||
output.Points = eto.Points;
|
||||
output.Value = eto.Value;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Yi.Framework.Bbs.Domain.Shared.Caches;
|
||||
|
||||
public class DiscussLableCacheItem
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? Color { get; set; }
|
||||
public string? BackgroundColor { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.Bbs.Domain.Shared.Consts;
|
||||
|
||||
public class DiscussLableConst
|
||||
{
|
||||
public const string DiscussLableCacheKey="DiscussLable:All";
|
||||
}
|
||||
@@ -14,14 +14,9 @@ namespace Yi.Framework.Bbs.Domain.Shared.Enums
|
||||
Public = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 仅自己可见
|
||||
/// 角色要求可见
|
||||
/// </summary>
|
||||
Oneself,
|
||||
|
||||
/// <summary>
|
||||
/// 部分用户可见
|
||||
/// </summary>
|
||||
User
|
||||
Role=1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
|
||||
public enum GoodsTypeEnum
|
||||
{
|
||||
/// <summary>
|
||||
/// 申请类型
|
||||
/// </summary>
|
||||
Apply
|
||||
}
|
||||
@@ -19,5 +19,10 @@ namespace Yi.Framework.Bbs.Domain.Shared.Enums
|
||||
/// 广播
|
||||
/// </summary>
|
||||
Broadcast,
|
||||
|
||||
/// <summary>
|
||||
/// 钱钱
|
||||
/// </summary>
|
||||
Money
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ namespace Yi.Framework.Bbs.Domain.Shared.Etos
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public BbsNoticeEventArgs( NoticeTypeEnum noticeType, Guid acceptUserId, string message)
|
||||
{
|
||||
NoticeType = noticeType;
|
||||
AcceptUserId = acceptUserId;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送广播
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Yi.Framework.Bbs.Domain.Shared.Etos;
|
||||
|
||||
/// <summary>
|
||||
/// 临时用户绑定到正式用户
|
||||
/// </summary>
|
||||
public class BindAccountEto
|
||||
{
|
||||
public Guid NewUserId { get; set; }
|
||||
public Guid OldUserId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Yi.Framework.Bbs.Domain.Shared.Etos;
|
||||
|
||||
public class SetAccountInfoEto
|
||||
{
|
||||
public SetAccountInfoEto(Guid userId)
|
||||
{
|
||||
UserId = userId;
|
||||
}
|
||||
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 钱钱
|
||||
/// </summary>
|
||||
public decimal Money { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 积分
|
||||
/// </summary>
|
||||
public int Points { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 价值
|
||||
/// </summary>
|
||||
public decimal Value { get; set; }
|
||||
}
|
||||
@@ -10,7 +10,7 @@ using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
namespace Yi.Framework.Bbs.Domain.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// 评论表
|
||||
/// bbs用户表
|
||||
/// </summary>
|
||||
[SugarTable("BbsUserExtraInfo")]
|
||||
[SugarIndex($"index_{nameof(UserId)}", nameof(UserId), OrderByType.Asc)]
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Yi.Framework.Bbs.Domain.Entities.Forum
|
||||
public override Guid Id { get; protected set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
[SugarColumn(Length = 500)]
|
||||
[SugarColumn(Length = 2000)]
|
||||
public string Content { get; set; }
|
||||
|
||||
public Guid DiscussId { get; set; }
|
||||
|
||||
@@ -8,9 +8,12 @@ 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)]
|
||||
[SugarIndex($"index_{nameof(IsDeleted)}_{nameof(PlateId)}_{nameof(CreatorId)}",
|
||||
nameof(IsDeleted), OrderByType.Asc,
|
||||
nameof(PlateId), OrderByType.Asc,
|
||||
nameof(CreatorId), OrderByType.Asc
|
||||
)]
|
||||
public class DiscussAggregateRoot : AggregateRoot<Guid>, ISoftDelete, IAuditedObject
|
||||
{
|
||||
public DiscussAggregateRoot()
|
||||
@@ -24,7 +27,6 @@ namespace Yi.Framework.Bbs.Domain.Entities.Forum
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
|
||||
public override Guid Id { get; protected set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Types { get; set; }
|
||||
public string? Introduction { get; set; }
|
||||
public int AgreeNum { get; set; }
|
||||
public int SeeNum { get; set; }
|
||||
@@ -59,11 +61,14 @@ namespace Yi.Framework.Bbs.Domain.Entities.Forum
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 当PermissionType为部分用户时候,以下列表中的用户+创建者 代表拥有权限
|
||||
/// 当PermissionType为角色时候,以下列表中的角色+创建者 代表拥有权限
|
||||
/// </summary>
|
||||
[SugarColumn(IsJson = true)]//使用json处理
|
||||
public List<Guid>? PermissionUserIds { get; set; }
|
||||
[SugarColumn(IsJson = true)] //使用json处理
|
||||
public List<string>? PermissionRoleCodes { get; set; } = new List<string>();
|
||||
|
||||
[SugarColumn(IsJson = true)]//使用json处理
|
||||
public List<Guid>? DiscussLableIds{ get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否禁止评论创建功能
|
||||
/// </summary>
|
||||
|
||||
@@ -4,17 +4,13 @@ using Volo.Abp.Domain.Entities;
|
||||
|
||||
namespace Yi.Framework.Bbs.Domain.Entities.Forum
|
||||
{
|
||||
[SugarTable("MyType")]
|
||||
public class MyTypeEntity : Entity<Guid>, ISoftDelete
|
||||
[SugarTable("DiscussLable")]
|
||||
public class DiscussLableAggregateRoot : AggregateRoot<Guid>, ISoftDelete
|
||||
{
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
|
||||
public override Guid Id { get; protected set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string? Color { get; set; }
|
||||
public string? BackgroundColor { get; set; }
|
||||
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
|
||||
namespace Yi.Framework.Bbs.Domain.Entities.Forum
|
||||
{
|
||||
[SugarTable("DiscussMyType")]
|
||||
public class DiscussMyTypeEntity : Entity<Guid>
|
||||
{
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
|
||||
public override Guid Id { get; protected set; }
|
||||
|
||||
public Guid DiscussId { get; set; }
|
||||
|
||||
public Guid MyTypeId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Auditing;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Enums;
|
||||
using Yi.Framework.Core.Data;
|
||||
|
||||
namespace Yi.Framework.Bbs.Domain.Entities.Shop;
|
||||
|
||||
/// <summary>
|
||||
/// 商品定义表
|
||||
/// </summary>
|
||||
[SugarTable("BbsGoods")]
|
||||
public class BbsGoodsAggregateRoot: AggregateRoot<Guid>, IHasCreationTime,IOrderNum
|
||||
{
|
||||
/// <summary>
|
||||
/// 上架时间
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品类型
|
||||
/// </summary>
|
||||
public GoodsTypeEnum GoodsType{ get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下架时间
|
||||
/// </summary>
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每人限购数量
|
||||
/// </summary>
|
||||
public int LimitNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前库存数量
|
||||
/// </summary>
|
||||
public int StockNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品图片url
|
||||
/// </summary>
|
||||
public string ImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public string Describe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编号
|
||||
/// </summary>
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所需钱钱
|
||||
/// </summary>
|
||||
public decimal NeedMoney { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所需价值
|
||||
/// </summary>
|
||||
public decimal NeedValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所需积分
|
||||
/// </summary>
|
||||
public decimal NeedPoints { get; set; }
|
||||
|
||||
public int OrderNum { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Auditing;
|
||||
using Volo.Abp.Domain.Entities;
|
||||
|
||||
namespace Yi.Framework.Bbs.Domain.Entities.Shop;
|
||||
|
||||
/// <summary>
|
||||
/// 商品申请记录表
|
||||
/// </summary>
|
||||
[SugarTable("BbsGoodsApply")]
|
||||
public class BbsGoodsApplyAggregateRoot: AggregateRoot<Guid>, IHasCreationTime
|
||||
{
|
||||
/// <summary>
|
||||
/// 商品id
|
||||
/// </summary>
|
||||
public Guid GoodsId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 申请时间
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 申请人用户id
|
||||
/// </summary>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系方式
|
||||
/// </summary>
|
||||
public string ContactInformation { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.Bbs.Domain.Entities.Shop.ValueObjects;
|
||||
|
||||
public class ShippingAddress
|
||||
{
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user