Browse Source

Merge pull request #1387 from colinin/dev

chore: upgrade version to 9.3.6
pull/1388/head
yx lin 3 months ago
committed by GitHub
parent
commit
835949cd07
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      .github/workflows/build.yml
  2. 2
      .github/workflows/publish.yml
  3. 2
      .github/workflows/release.yml
  4. 9
      Directory.Packages.props
  5. 2
      README.md
  6. 1
      apps/vben5/apps/app-antd/package.json
  7. 10
      apps/vben5/apps/app-antd/src/locales/index.ts
  8. 1
      apps/vben5/packages/@abp/account/package.json
  9. 55
      apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue
  10. 9
      apps/vben5/packages/@abp/components/package.json
  11. 200
      apps/vben5/packages/@abp/components/src/cropper/Cropper.vue
  12. 159
      apps/vben5/packages/@abp/components/src/cropper/CropperAvatar.vue
  13. 309
      apps/vben5/packages/@abp/components/src/cropper/CropperModal.vue
  14. 2
      apps/vben5/packages/@abp/components/src/cropper/index.ts
  15. 8
      apps/vben5/packages/@abp/components/src/cropper/types.ts
  16. 20
      apps/vben5/packages/@abp/components/src/locales/index.ts
  17. 14
      apps/vben5/packages/@abp/components/src/locales/langs/en-US/cropper.json
  18. 14
      apps/vben5/packages/@abp/components/src/locales/langs/zh-CN/cropper.json
  19. 42
      apps/vben5/packages/@abp/core/src/utils/file.ts
  20. 1
      apps/vben5/packages/@abp/core/src/utils/index.ts
  21. 1
      apps/vben5/pnpm-workspace.yaml
  22. 21
      aspnet-core/LINGYUN.MicroService.All.sln
  23. 21
      aspnet-core/LINGYUN.MicroService.SingleProject.sln
  24. 7
      aspnet-core/LINGYUN.MicroService.TaskManagement.sln
  25. 7
      aspnet-core/LINGYUN.MicroService.WebhooksManagement.sln
  26. 51
      aspnet-core/LINGYUN.MicroService.WechatManagement.sln
  27. 7
      aspnet-core/LINGYUN.MicroService.Workflow.sln
  28. 20
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs
  29. 18
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json
  30. 16
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json
  31. 20
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs
  32. 48
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs
  33. 7
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj
  34. 3
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs
  35. 5
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/BlobStoringTencentConsts.cs
  36. 11
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs
  37. 2
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ICosClientFactory.cs
  38. 2
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs
  39. 45
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs
  40. 12
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/Microsoft/Extensions/DependencyInjection/BlobStoringTencentHttpClientFactoryServiceCollectionExtensions.cs
  41. 11
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/System/Net/Http/BlobStoringTencentHttpClientFactoryExtenssions.cs
  42. 4
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN/Abp/Tencent/AbpTencentCloudModule.cs
  43. 4
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs
  44. 5
      aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.csproj
  45. 156
      aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPBootstrapper.cs
  46. 25
      aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs
  47. 29
      aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs
  48. 397
      aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.xml
  49. 84
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs
  50. 40
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs
  51. 15
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs
  52. 20
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/en.json
  53. 20
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/zh-Hans.json
  54. 88
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs
  55. 11
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs
  56. 8
      aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.APM/LINGYUNG/Abp/Telemetry/APM/AbpTelemetryAPMModule.cs
  57. 30
      aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.OpenTelemetry/LINGYUN/Abp/Telemetry/OpenTelemetry/AbpTelemetryOpenTelemetryModule.cs
  58. 3
      aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xml
  59. 30
      aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xsd
  60. 21
      aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN.Abp.AspNetCore.MultiTenancy.csproj
  61. 10
      aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs
  62. 13
      aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyResolveOptions.cs
  63. 15
      aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpMultiTenancyOptionsExtensions.cs
  64. 52
      aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/OnlyDomainTenantResolveContributor.cs
  65. 12
      aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json
  66. 36
      aspnet-core/framework/wechat/LINGYUN.Abp.Identity.WeChat.Work/LINGYUN/Abp/Identity/WeChat/Work/AbpWeChatWorkClaimsPrincipalContributor.cs
  67. 12
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs
  68. 1
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs
  69. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs
  70. 1
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs
  71. 34
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolverBase.cs
  72. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs
  73. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs
  74. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs
  75. 16
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/IWeChatOfficialMessageResolver.cs
  76. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs
  77. 5
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs
  78. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessageEto.cs
  79. 5
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributor.cs
  80. 11
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributorBase.cs
  81. 39
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolver.cs
  82. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs
  83. 1
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj
  84. 6
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/AbpWeChatWorkCommonModule.cs
  85. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/AbpWeChatWorkMessageResolveOptions.cs
  86. 16
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/IWeChatWorkMessageResolver.cs
  87. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BatchJobResultEvent.cs
  88. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BookMeetingRoomEvent.cs
  89. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CancelMeetingRoomEvent.cs
  90. 272
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/SysApprovalStatusChangeEvent.cs
  91. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessageEto.cs
  92. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventResolveContributor.cs
  93. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessageEto.cs
  94. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributor.cs
  95. 12
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributorBase.cs
  96. 39
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolver.cs
  97. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xml
  98. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xsd
  99. 27
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN.Abp.WeChat.Work.ExternalContact.csproj
  100. 78
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/AbpWeChatWorkExternalContactModule.cs

2
.github/workflows/build.yml

@ -7,7 +7,7 @@ on:
- "**.csproj"
env:
DOTNET_VERSION: "9.0.304"
DOTNET_VERSION: "9.0.307"
jobs:
build:

2
.github/workflows/publish.yml

@ -4,7 +4,7 @@ on:
pull_request:
branches: [ main ]
env:
DOTNET_VERSION: "9.0.304"
DOTNET_VERSION: "9.0.307"
jobs:
publish:

2
.github/workflows/release.yml

@ -14,4 +14,4 @@ jobs:
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
automatic_release_tag: "9.3.5"
automatic_release_tag: "9.3.6"

9
Directory.Packages.props

@ -3,8 +3,8 @@
<DotNetCoreCAPPackageVersion>8.3.5</DotNetCoreCAPPackageVersion>
<ElsaPackageVersion>2.15.2</ElsaPackageVersion>
<ElsaNextPackageVersion>3.3.5</ElsaNextPackageVersion>
<VoloAbpPackageVersion>9.3.5</VoloAbpPackageVersion>
<LINGYUNAbpPackageVersion>9.3.5</LINGYUNAbpPackageVersion>
<VoloAbpPackageVersion>9.3.6</VoloAbpPackageVersion>
<LINGYUNAbpPackageVersion>9.3.6</LINGYUNAbpPackageVersion>
<MicrosoftExtensionsPackageVersion>9.0.5</MicrosoftExtensionsPackageVersion>
<MicrosoftAspNetCorePackageVersion>9.0.5</MicrosoftAspNetCorePackageVersion>
<MicrosoftEntityFrameworkCorePackageVersion>9.0.5</MicrosoftEntityFrameworkCorePackageVersion>
@ -12,7 +12,7 @@
</PropertyGroup>
<!-- Abp Framework -->
<ItemGroup>
<PackageVersion Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="4.3.5" />
<PackageVersion Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="4.3.6" />
<PackageVersion Include="Volo.Abp.Core" Version="$(VoloAbpPackageVersion)" />
<PackageVersion Include="Volo.Abp.Account.Application" Version="$(VoloAbpPackageVersion)" />
<PackageVersion Include="Volo.Abp.Account.Application.Contracts" Version="$(VoloAbpPackageVersion)" />
@ -282,6 +282,7 @@
<PackageVersion Include="aliyun-net-sdk-core" Version="1.5.10" />
<PackageVersion Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageVersion Include="AgileConfig.Client" Version="1.6.9" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />
<PackageVersion Include="Elastic.Apm.NetCoreAll" Version="1.31.0" />
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.5.0" />
<PackageVersion Include="Dapr.Client" Version="1.15.4" />
@ -333,4 +334,4 @@
<PackageVersion Include="Yarp.ReverseProxy" Version="2.1.0" />
<PackageVersion Include="Yarp.Telemetry.Consumption" Version="2.1.0" />
</ItemGroup>
</Project>
</Project>

2
README.md

@ -8,7 +8,7 @@
## Build
[![Build](https://github.com/colinin/abp-next-admin/actions/workflows/build.yml/badge.svg)](https://github.com/colinin/abp-next-admin/actions/workflows/build.yml)
[![Build](https://github.com/colinin/abp-next-admin/actions/workflows/build.yml/badge.svg)](https://github.com/colinin/abp-next-admin/actions/workflows/build.yml) [![NuGet](https://img.shields.io/nuget/v/LINGYUN.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/LINGYUN.Abp.Core)
## 部署方案

1
apps/vben5/apps/app-antd/package.json

@ -28,6 +28,7 @@
"dependencies": {
"@abp/account": "workspace:*",
"@abp/auditing": "workspace:*",
"@abp/components": "workspace:*",
"@abp/core": "workspace:*",
"@abp/data-protection": "workspace:*",
"@abp/demo": "workspace:*",

10
apps/vben5/apps/app-antd/src/locales/index.ts

@ -13,6 +13,7 @@ import {
} from '@vben/locales';
import { preferences } from '@vben/preferences';
import { loadComponentMessages } from '@abp/components/locales';
import { useAbpStore } from '@abp/core';
import { useLocalizationsApi } from '@abp/localization';
import { loadPaltformMessages } from '@abp/platform';
@ -35,16 +36,17 @@ const localesMap = loadLocalesMapFromDir(
* @param lang
*/
async function loadMessages(lang: SupportedLanguagesType) {
const [appLocaleMessages, platformLocales, _, abpLocales] = await Promise.all(
[
const [appLocaleMessages, compLocales, platformLocales, _, abpLocales] =
await Promise.all([
localesMap[lang]?.(),
loadComponentMessages(lang),
loadPaltformMessages(lang),
loadThirdPartyMessage(lang),
loadAbpLocale(lang),
],
);
]);
return {
...appLocaleMessages?.default,
...compLocales?.default,
...platformLocales?.default,
...abpLocales,
};

1
apps/vben5/packages/@abp/account/package.json

@ -20,6 +20,7 @@
}
},
"dependencies": {
"@abp/components": "workspace:*",
"@abp/core": "workspace:*",
"@abp/gdpr": "workspace:*",
"@abp/identity": "workspace:*",

55
apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue

@ -1,7 +1,4 @@
<script setup lang="ts">
import type { UploadChangeParam } from 'ant-design-vue';
import type { FileType } from 'ant-design-vue/es/upload/interface';
import type { ProfileDto, UpdateProfileDto } from '../../types/profile';
import { computed, ref, toValue, watchEffect } from 'vue';
@ -10,20 +7,14 @@ import { $t } from '@vben/locales';
import { preferences } from '@vben/preferences';
import { useUserStore } from '@vben/stores';
import { useSettings } from '@abp/core';
import { UploadOutlined } from '@ant-design/icons-vue';
import {
Avatar,
Button,
Card,
Form,
Input,
message,
Upload,
} from 'ant-design-vue';
import { CropperAvatar } from '@abp/components/cropper';
import { buildUUID, useSettings } from '@abp/core';
import { Button, Card, Form, Input, message } from 'ant-design-vue';
import { useProfileApi } from '../../api/useProfileApi';
type ApiFunParams = { file: Blob; fileName?: string; name: string };
const props = defineProps<{
profile: ProfileDto;
}>();
@ -43,33 +34,32 @@ const pictureState = ref<{
const userStore = useUserStore();
const { isTrue } = useSettings();
const { changePictureApi, getPictureApi } = useProfileApi();
const { changePictureApi } = useProfileApi();
const avatar = computed(() => {
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
});
async function onAvatarChange(_param: UploadChangeParam) {
async function onUploadAvatar(params: ApiFunParams) {
pictureState.value.uploading = true;
try {
await changePictureApi({
file: pictureState.value.file,
const fileName = `${buildUUID()}.${params.file.type.slice(6)}`;
const file = new File([params.file], fileName, {
type: params.file.type,
});
await changePictureApi({ file });
if (userStore.userInfo?.avatar) {
URL.revokeObjectURL(userStore.userInfo.avatar);
}
const picture = await getPictureApi();
userStore.$patch((state) => {
state.userInfo && (state.userInfo.avatar = URL.createObjectURL(picture));
});
message.success($t('AbpUi.SavedSuccessfully'));
emits('pictureChange');
} finally {
pictureState.value.uploading = false;
}
}
function onBeforeUpload(file: FileType) {
pictureState.value.file = file;
return false;
async function onAvatarChange(url: string) {
userStore.$patch((state) => {
state.userInfo && (state.userInfo.avatar = url);
});
emits('pictureChange');
}
function onSubmit() {
emits('submit', toValue(formModel));
@ -132,7 +122,7 @@ watchEffect(() => {
<div class="basis-2/4">
<div class="flex flex-col items-center">
<p>{{ $t('AbpUi.ProfilePicture') }}</p>
<Avatar :size="100">
<!-- <Avatar :size="100">
<template #icon>
<img :src="avatar" alt="" />
</template>
@ -147,7 +137,16 @@ watchEffect(() => {
<UploadOutlined />
{{ $t('abp.account.settings.changeAvatar') }}
</Button>
</Upload>
</Upload> -->
<div class="mb-8 block rounded-[50%]">
<CropperAvatar
:value="avatar"
:btn-text="$t('AbpAccount.AvatarChanged')"
width="150"
:upload-api="onUploadAvatar"
@change="onAvatarChange"
/>
</div>
</div>
</div>
</div>

9
apps/vben5/packages/@abp/components/package.json

@ -22,6 +22,14 @@
"types": "./src/codemirror/index.ts",
"default": "./src/codemirror/index.ts"
},
"./locales": {
"types": "./src/locales/index.ts",
"default": "./src/locales/index.ts"
},
"./cropper": {
"types": "./src/cropper/index.ts",
"default": "./src/cropper/index.ts"
},
"./tinymce": {
"types": "./src/tinymce/index.ts",
"default": "./src/tinymce/index.ts"
@ -48,6 +56,7 @@
"@vueuse/core": "catalog:",
"ant-design-vue": "catalog:",
"codemirror": "catalog:",
"cropperjs": "catalog:",
"lodash.isnumber": "catalog:",
"tinymce": "catalog:",
"vditor": "catalog:",

200
apps/vben5/packages/@abp/components/src/cropper/Cropper.vue

@ -0,0 +1,200 @@
<script lang="ts" setup>
import type { CSSProperties, PropType } from 'vue';
import type { Nullable } from '@vben/types';
import {
computed,
onMounted,
onUnmounted,
ref,
unref,
useAttrs,
useTemplateRef,
} from 'vue';
import { useNamespace } from '@vben/hooks';
import { useDebounceFn } from '@vueuse/core';
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
type Options = Cropper.Options;
type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>;
const props = defineProps({
src: { type: String, required: true },
alt: { type: String, default: '' },
circled: { type: Boolean, default: false },
realTimePreview: { type: Boolean, default: true },
height: { type: [String, Number], default: '360px' },
crossorigin: {
type: String as PropType<'' | 'anonymous' | 'use-credentials' | undefined>,
default: undefined,
},
imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
options: { type: Object as PropType<Options>, default: () => ({}) },
});
const emits = defineEmits(['cropend', 'ready', 'cropendError']);
const defaultOptions: Options = {
aspectRatio: 1,
zoomable: true,
zoomOnTouch: true,
zoomOnWheel: true,
cropBoxMovable: true,
cropBoxResizable: true,
toggleDragModeOnDblclick: true,
autoCrop: true,
background: true,
highlight: true,
center: true,
responsive: true,
restore: true,
checkCrossOrigin: true,
checkOrientation: true,
scalable: true,
modal: true,
guides: true,
movable: true,
rotatable: true,
};
const attrs = useAttrs();
const imgElRef = useTemplateRef<ElRef<HTMLImageElement>>('imgElRef');
const cropper = ref<Nullable<Cropper>>();
const isReady = ref(false);
const { b, is } = useNamespace('cropper-image');
const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80);
const getImageStyle = computed((): CSSProperties => {
return {
height: props.height,
maxWidth: '100%',
...props.imageStyle,
};
});
const getClass = computed(() => {
return [b(), attrs.class, is('circled', props.circled)];
});
const getWrapperStyle = computed((): CSSProperties => {
return { height: `${`${props.height}`.replace(/px/, '')}px` };
});
onMounted(init);
onUnmounted(() => {
cropper.value?.destroy();
});
async function init() {
const imgEl = unref(imgElRef);
if (!imgEl) {
return;
}
cropper.value = new Cropper(imgEl, {
...defaultOptions,
ready: () => {
isReady.value = true;
realTimeCroppered();
emits('ready', cropper.value);
},
crop() {
debounceRealTimeCroppered();
},
zoom() {
debounceRealTimeCroppered();
},
cropmove() {
debounceRealTimeCroppered();
},
...props.options,
});
}
// Real-time display preview
function realTimeCroppered() {
props.realTimePreview && croppered();
}
// event: return base64 and width and height information after cropping
function croppered() {
if (!cropper.value) {
return;
}
const imgInfo = cropper.value.getData();
const canvas = props.circled
? getRoundedCanvas()
: cropper.value.getCroppedCanvas();
canvas.toBlob((blob) => {
if (!blob) {
return;
}
const fileReader: FileReader = new FileReader();
fileReader.readAsDataURL(blob);
fileReader.onloadend = (e) => {
emits('cropend', {
imgBase64: e.target?.result ?? '',
imgInfo,
});
};
// eslint-disable-next-line unicorn/prefer-add-event-listener
fileReader.onerror = () => {
emits('cropendError');
};
}, 'image/png');
}
// Get a circular picture canvas
function getRoundedCanvas() {
const sourceCanvas = cropper.value!.getCroppedCanvas();
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d')!;
const width = sourceCanvas.width;
const height = sourceCanvas.height;
canvas.width = width;
canvas.height = height;
context.imageSmoothingEnabled = true;
context.drawImage(sourceCanvas, 0, 0, width, height);
context.globalCompositeOperation = 'destination-in';
context.beginPath();
context.arc(
width / 2,
height / 2,
Math.min(width, height) / 2,
0,
2 * Math.PI,
true,
);
context.fill();
return canvas;
}
</script>
<template>
<div :class="getClass" :style="getWrapperStyle">
<img
v-show="isReady"
ref="imgElRef"
:src="src"
:alt="alt"
:crossorigin="crossorigin"
:style="getImageStyle"
/>
</div>
</template>
<style scoped lang="scss">
$namespace: vben;
.#{$namespace}-cropper-image {
&.is-circled {
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}
}
}
</style>

159
apps/vben5/packages/@abp/components/src/cropper/CropperAvatar.vue

@ -0,0 +1,159 @@
<script setup lang="ts">
import type { ButtonProps } from 'ant-design-vue/es/button';
import type { CSSProperties, PropType } from 'vue';
import { computed, ref, unref, watch, watchEffect } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { useNamespace } from '@vben/hooks';
import { createIconifyIcon } from '@vben/icons';
import { useI18n } from '@vben/locales';
import { Button } from 'ant-design-vue';
import CropperModal from './CropperModal.vue';
interface File {
file: Blob;
fileName?: string;
name: string;
}
const props = defineProps({
width: { type: [String, Number], default: '200px' },
value: { type: String, default: '' },
showBtn: { type: Boolean, default: true },
btnProps: { type: Object as PropType<ButtonProps>, default: undefined },
btnText: { type: String, default: '' },
uploadApi: {
type: Function as PropType<(file: File) => Promise<void>>,
default: undefined,
},
});
const emits = defineEmits(['update:value', 'change']);
const UploadIcon = createIconifyIcon('ant-design:cloud-upload-outlined');
const sourceValue = ref(props.value || '');
const { b, e } = useNamespace('cropper-avatar');
const [Modal, modalApi] = useVbenModal({
connectedComponent: CropperModal,
});
const { t } = useI18n();
const getWidth = computed(() => `${`${props.width}`.replace(/px/, '')}px`);
const getIconWidth = computed(
() => `${Number.parseInt(`${props.width}`.replace(/px/, '')) / 2}px`,
);
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }));
const getImageWrapperStyle = computed(
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }),
);
watchEffect(() => {
sourceValue.value = props.value || '';
});
watch(
() => sourceValue.value,
(v: string) => {
emits('update:value', v);
},
);
function handleUploadSuccess(url: string) {
sourceValue.value = url;
emits('change', url);
}
function openModal() {
modalApi.open();
}
function closeModal() {
modalApi.close();
}
defineExpose({ openModal, closeModal });
</script>
<template>
<div :class="b()" :style="getStyle">
<div
:class="e(`image-wrapper`)"
:style="getImageWrapperStyle"
@click="openModal"
>
<div :class="e(`image-mask`)" :style="getImageWrapperStyle">
<UploadIcon
:width="getIconWidth"
:style="getImageWrapperStyle"
color="#d6d6d6"
/>
</div>
<img :src="sourceValue" v-if="sourceValue" alt="avatar" />
</div>
<Button
:class="e(`upload-btn`)"
@click="openModal"
v-if="showBtn"
v-bind="btnProps"
>
{{ btnText ? btnText : t('cropper.selectImage') }}
</Button>
<Modal
@upload-success="handleUploadSuccess"
:upload-api="uploadApi"
:src="sourceValue"
/>
</div>
</template>
<style scoped lang="scss">
$namespace: vben;
.#{$namespace}-cropper-avatar {
display: inline-block;
text-align: center;
&__image-wrapper {
overflow: hidden;
cursor: pointer;
border: var(--border);
border-radius: 50%;
img {
width: 100%;
// height: 100%;
}
}
&__image-mask {
position: absolute;
width: inherit;
height: inherit;
cursor: pointer;
background: rgb(0 0 0 / 40%);
border: inherit;
border-radius: inherit;
opacity: 0;
transition: opacity 0.4s;
::v-deep(svg) {
margin: auto;
}
}
&__image-mask:hover {
opacity: 40;
}
&__upload-btn {
margin: 10px auto;
}
}
</style>

309
apps/vben5/packages/@abp/components/src/cropper/CropperModal.vue

@ -0,0 +1,309 @@
<script setup lang="ts">
import type { UploadProps } from 'ant-design-vue';
import type { PropType } from 'vue';
import type { CropendResult, Cropper } from './types';
import { h, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { useNamespace } from '@vben/hooks';
import { createIconifyIcon } from '@vben/icons';
import { $t } from '@vben/locales';
import { isFunction } from '@vben/utils';
import { dataURLtoBlob } from '@abp/core';
import { Avatar, Button, Space, Tooltip, Upload } from 'ant-design-vue';
import CropperImage from './Cropper.vue';
type ApiFunParams = { file: Blob; fileName: string; name: string };
const props = defineProps({
circled: { type: Boolean, default: true },
uploadApi: {
type: Function as PropType<(params: ApiFunParams) => Promise<any>>,
default: undefined,
},
src: { type: String, default: '' },
});
const emits = defineEmits<{
(event: 'uploadSuccess', url: string): void;
}>();
const UploadIcon = createIconifyIcon('ant-design:upload-outlined');
const ResetIcon = createIconifyIcon('ant-design:reload-outlined');
const RotateLeftIcon = createIconifyIcon('ant-design:rotate-left-outlined');
const RotateRightIcon = createIconifyIcon('ant-design:rotate-right-outlined');
const ScaleXIcon = createIconifyIcon('vaadin:arrows-long-h');
const ScaleYIcon = createIconifyIcon('vaadin:arrows-long-v');
const ZoomInIcon = createIconifyIcon('ant-design:zoom-in-outlined');
const ZoomOutIcon = createIconifyIcon('ant-design:zoom-out-outlined');
let fileName = '';
const src = ref(props.src || '');
const previewSource = ref('');
const fileList = ref<UploadProps['fileList']>([]);
const cropper = ref<Cropper>();
let scaleX = 1;
let scaleY = 1;
const { b, e } = useNamespace('cropper-am');
const [Modal, modalApi] = useVbenModal({
class: 'w-[800px]',
fullscreen: false,
fullscreenButton: false,
confirmText: $t('cropper.confirmText'),
onConfirm: handleOk,
title: $t('cropper.title'),
});
function handleBeforeUpload(file: File) {
const reader = new FileReader();
reader.readAsDataURL(file);
src.value = '';
previewSource.value = '';
reader.addEventListener('load', (e) => {
src.value = (e.target?.result as string) ?? '';
fileName = file.name;
});
return false;
}
function handleCropend({ imgBase64 }: CropendResult) {
previewSource.value = imgBase64;
}
function handleReady(cropperInstance: Cropper) {
cropper.value = cropperInstance;
}
function handlerToolbar(event: string, arg?: number) {
if (!cropper.value) {
return;
}
if (event === 'scaleX') {
scaleX = arg = scaleX === -1 ? 1 : -1;
}
if (event === 'scaleY') {
scaleY = arg = scaleY === -1 ? 1 : -1;
}
switch (event) {
case 'reset': {
return cropper.value.reset();
}
case 'rotate': {
return cropper.value.rotate(arg!);
}
case 'scaleX': {
return cropper.value.scaleX(scaleX);
}
case 'scaleY': {
return cropper.value.scaleY(scaleY);
}
case 'zoom': {
return cropper.value.zoom(arg!);
}
}
}
async function handleOk() {
const uploadApi = props.uploadApi;
if (uploadApi && isFunction(uploadApi)) {
const blob = dataURLtoBlob(previewSource.value);
try {
modalApi.setState({ submitting: true });
await uploadApi({ name: 'file', file: blob, fileName });
emits('uploadSuccess', previewSource.value);
modalApi.close();
} finally {
modalApi.setState({ submitting: false });
}
}
}
</script>
<template>
<Modal>
<div :class="b()">
<div :class="e('left')">
<div :class="e('cropper')">
<CropperImage
v-if="src"
:src="src"
height="300px"
:circled="circled"
@cropend="handleCropend"
@ready="handleReady"
/>
</div>
<div :class="e('toolbar')">
<Upload
:file-list="fileList"
accept="image/*"
:before-upload="handleBeforeUpload"
>
<Tooltip :title="$t('cropper.selectImage')" placement="bottom">
<Button size="small" :icon="h(UploadIcon)" type="primary" />
</Tooltip>
</Upload>
<Space>
<Tooltip :title="$t('cropper.btn_reset')" placement="bottom">
<Button
type="primary"
:icon="h(ResetIcon)"
size="small"
:disabled="!src"
@click="handlerToolbar('reset')"
/>
</Tooltip>
<Tooltip :title="$t('cropper.btn_rotate_left')" placement="bottom">
<Button
type="primary"
:icon="h(RotateLeftIcon)"
size="small"
:disabled="!src"
@click="handlerToolbar('rotate', -45)"
/>
</Tooltip>
<Tooltip :title="$t('cropper.btn_rotate_right')" placement="bottom">
<Button
type="primary"
:icon="h(RotateRightIcon)"
size="small"
:disabled="!src"
@click="handlerToolbar('rotate', 45)"
/>
</Tooltip>
<Tooltip :title="$t('cropper.btn_scale_x')" placement="bottom">
<Button
type="primary"
:icon="h(ScaleXIcon)"
size="small"
:disabled="!src"
@click="handlerToolbar('scaleX')"
/>
</Tooltip>
<Tooltip :title="$t('cropper.btn_scale_y')" placement="bottom">
<Button
type="primary"
:icon="h(ScaleYIcon)"
size="small"
:disabled="!src"
@click="handlerToolbar('scaleY')"
/>
</Tooltip>
<Tooltip :title="$t('cropper.btn_zoom_in')" placement="bottom">
<Button
type="primary"
:icon="h(ZoomInIcon)"
size="small"
:disabled="!src"
@click="handlerToolbar('zoom', 0.1)"
/>
</Tooltip>
<Tooltip :title="$t('cropper.btn_zoom_out')" placement="bottom">
<Button
type="primary"
:icon="h(ZoomOutIcon)"
size="small"
:disabled="!src"
@click="handlerToolbar('zoom', -0.1)"
/>
</Tooltip>
</Space>
</div>
</div>
<div :class="e('right')">
<div :class="e(`preview`)">
<img
:src="previewSource"
v-if="previewSource"
:alt="$t('cropper.preview')"
/>
</div>
<template v-if="previewSource">
<div :class="e(`group`)">
<Avatar :src="previewSource" size="large" />
<Avatar :src="previewSource" :size="48" />
<Avatar :src="previewSource" :size="64" />
<Avatar :src="previewSource" :size="80" />
</div>
</template>
</div>
</div>
</Modal>
</template>
<style scoped lang="scss">
$namespace: vben;
.#{$namespace}-cropper-am {
display: flex;
&__left,
&__right {
height: 340px;
}
&__left {
width: 55%;
}
&__right {
width: 45%;
}
&__cropper {
height: 300px;
background: #eee;
background-image:
linear-gradient(
45deg,
rgb(0 0 0 / 25%) 25%,
transparent 0,
transparent 75%,
rgb(0 0 0 / 25%) 0
),
linear-gradient(
45deg,
rgb(0 0 0 / 25%) 25%,
transparent 0,
transparent 75%,
rgb(0 0 0 / 25%) 0
);
background-position:
0 0,
12px 12px;
background-size: 24px 24px;
}
&__toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 10px;
}
&__preview {
width: 220px;
height: 220px;
margin: 0 auto;
overflow: hidden;
border: var(--border);
border-radius: 50%;
img {
width: 100%;
height: 100%;
}
}
&__group {
display: flex;
align-items: center;
justify-content: space-around;
padding-top: 8px;
margin-top: 8px;
border-top: var(--border);
}
}
</style>

2
apps/vben5/packages/@abp/components/src/cropper/index.ts

@ -0,0 +1,2 @@
export { default as CropperAvatar } from './CropperAvatar.vue';
export { default as CropperModal } from './CropperModal.vue';

8
apps/vben5/packages/@abp/components/src/cropper/types.ts

@ -0,0 +1,8 @@
import type Cropper from 'cropperjs';
export interface CropendResult {
imgBase64: string;
imgInfo: Cropper.Data;
}
export type { Cropper };

20
apps/vben5/packages/@abp/components/src/locales/index.ts

@ -0,0 +1,20 @@
import type { SupportedLanguagesType } from '@vben/locales';
import { loadLocalesMapFromDir } from '@vben/locales';
const modules = import.meta.glob('./langs/**/*.json');
const localesMap = loadLocalesMapFromDir(
/\.\/langs\/([^/]+)\/(.*)\.json$/,
modules,
);
/**
*
* @param lang
* @returns
*/
export async function loadComponentMessages(lang: SupportedLanguagesType) {
const locales = localesMap[lang]?.();
return locales;
}

14
apps/vben5/packages/@abp/components/src/locales/langs/en-US/cropper.json

@ -0,0 +1,14 @@
{
"confirmText": "Confirm and upload",
"title": "Avatar upload",
"selectImage": "Select Image",
"btn_rotate_left": "Counterclockwise rotation",
"btn_rotate_right": "Clockwise rotation",
"btn_scale_x": "Flip horizontal",
"btn_scale_y": "Flip vertical",
"btn_zoom_in": "Zoom in",
"btn_zoom_out": "Zoom out",
"btn_reset": "Reset",
"preview": "Preivew",
"uploadSuccess": "Uploaded success!"
}

14
apps/vben5/packages/@abp/components/src/locales/langs/zh-CN/cropper.json

@ -0,0 +1,14 @@
{
"confirmText": "确认并上传",
"title": "头像上传",
"selectImage": "选择图片",
"btn_rotate_left": "逆时针旋转",
"btn_rotate_right": "顺时针旋转",
"btn_scale_x": "水平翻转",
"btn_scale_y": "垂直翻转",
"btn_zoom_in": "放大",
"btn_zoom_out": "缩小",
"btn_reset": "重置",
"preview": "预览",
"uploadSuccess": "上传成功!"
}

42
apps/vben5/packages/@abp/core/src/utils/file.ts

@ -0,0 +1,42 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/**
* @description: base64 to blob
*/
export function dataURLtoBlob(base64Buf: string): Blob {
const arr = base64Buf.split(',');
const typeItem = arr[0];
const mime = typeItem?.match(/:(.*?);/)?.[1];
const bstr = window.atob(arr[1]!);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.codePointAt(n)!;
}
return new Blob([u8arr], { type: mime });
}
/**
* img url to base64
* @param url
*/
export function urlToBase64(url: string, mineType?: string): Promise<string> {
return new Promise((resolve, reject) => {
let canvas = document.createElement('CANVAS') as HTMLCanvasElement | null;
const ctx = canvas!.getContext('2d');
const img = new Image();
img.crossOrigin = '';
img.addEventListener('load', () => {
if (!canvas || !ctx) {
return reject(new Error('canvas or ctx is null!'));
}
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
const dataURL = canvas.toDataURL(mineType || 'image/png');
canvas = null;
resolve(dataURL);
});
img.src = url;
});
}

1
apps/vben5/packages/@abp/core/src/utils/index.ts

@ -1,5 +1,6 @@
export * from './array';
export * from './date';
export * from './file';
export * from './is';
export * from './mitt';
export * from './regex';

1
apps/vben5/pnpm-workspace.yaml

@ -90,6 +90,7 @@ catalog:
codemirror: ^5.65.3
commitlint-plugin-function-rules: ^4.0.1
consola: ^3.4.2
cropperjs: ^1.5.12
cross-env: ^7.0.3
cspell: ^8.19.3
cssnano: ^7.0.6

21
aspnet-core/LINGYUN.MicroService.All.sln

@ -795,8 +795,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Web", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Web.IdentityServer", "modules\account\LINGYUN.Abp.Account.Web.IdentityServer\LINGYUN.Abp.Account.Web.IdentityServer.csproj", "{0FF0A04C-B580-4A56-9171-CF2988B5DE5A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Emailing", "modules\account\LINGYUN.Abp.Account.Emailing\LINGYUN.Abp.Account.Emailing.csproj", "{29A87F09-CC03-4DB8-B584-98073AB50AA4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Web.OpenIddict", "modules\account\LINGYUN.Abp.Account.Web.OpenIddict\LINGYUN.Abp.Account.Web.OpenIddict.csproj", "{F810C8A8-1256-440F-BAAF-7F3588291963}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.HttpApi.Client", "modules\account\LINGYUN.Abp.Account.HttpApi.Client\LINGYUN.Abp.Account.HttpApi.Client.csproj", "{FFBE3EC6-F11B-4B7C-9BAF-AFBBB12BEF59}"
@ -849,6 +847,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Exporter.Pdf.Li
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Exporter.Pdf.SpireLib", "framework\exporter\LINGYUN.Abp.Exporter.Pdf.SpireLib\LINGYUN.Abp.Exporter.Pdf.SpireLib.csproj", "{9950639D-AA4C-4FF1-A65E-9790EB561C8A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Security", "modules\account\LINGYUN.Abp.Account.Security\LINGYUN.Abp.Account.Security.csproj", "{5FA85E8E-3276-43DF-CC93-6A9847905166}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AspNetCore.MultiTenancy", "framework\tenants\LINGYUN.Abp.AspNetCore.MultiTenancy\LINGYUN.Abp.AspNetCore.MultiTenancy.csproj", "{AEEA81D6-B282-93CF-862B-9FCF1A5052F7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -2103,10 +2105,6 @@ Global
{0FF0A04C-B580-4A56-9171-CF2988B5DE5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FF0A04C-B580-4A56-9171-CF2988B5DE5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0FF0A04C-B580-4A56-9171-CF2988B5DE5A}.Release|Any CPU.Build.0 = Release|Any CPU
{29A87F09-CC03-4DB8-B584-98073AB50AA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29A87F09-CC03-4DB8-B584-98073AB50AA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29A87F09-CC03-4DB8-B584-98073AB50AA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29A87F09-CC03-4DB8-B584-98073AB50AA4}.Release|Any CPU.Build.0 = Release|Any CPU
{F810C8A8-1256-440F-BAAF-7F3588291963}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F810C8A8-1256-440F-BAAF-7F3588291963}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F810C8A8-1256-440F-BAAF-7F3588291963}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -2203,6 +2201,14 @@ Global
{9950639D-AA4C-4FF1-A65E-9790EB561C8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9950639D-AA4C-4FF1-A65E-9790EB561C8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9950639D-AA4C-4FF1-A65E-9790EB561C8A}.Release|Any CPU.Build.0 = Release|Any CPU
{5FA85E8E-3276-43DF-CC93-6A9847905166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5FA85E8E-3276-43DF-CC93-6A9847905166}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5FA85E8E-3276-43DF-CC93-6A9847905166}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FA85E8E-3276-43DF-CC93-6A9847905166}.Release|Any CPU.Build.0 = Release|Any CPU
{AEEA81D6-B282-93CF-862B-9FCF1A5052F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEEA81D6-B282-93CF-862B-9FCF1A5052F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEEA81D6-B282-93CF-862B-9FCF1A5052F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEEA81D6-B282-93CF-862B-9FCF1A5052F7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2588,7 +2594,6 @@ Global
{AE00DA82-B33A-CAF7-D9CD-D5E26608741B} = {9D1302BE-3886-49F8-B0CD-35D2AC1E5A37}
{F9A0D88F-53AE-4AC7-8E15-163C34386E7C} = {9E72FEB9-A626-4312-892B-CDD043879758}
{0FF0A04C-B580-4A56-9171-CF2988B5DE5A} = {9E72FEB9-A626-4312-892B-CDD043879758}
{29A87F09-CC03-4DB8-B584-98073AB50AA4} = {9E72FEB9-A626-4312-892B-CDD043879758}
{F810C8A8-1256-440F-BAAF-7F3588291963} = {9E72FEB9-A626-4312-892B-CDD043879758}
{FFBE3EC6-F11B-4B7C-9BAF-AFBBB12BEF59} = {9E72FEB9-A626-4312-892B-CDD043879758}
{C95DE287-9D21-4DCD-9281-A060B6D99774} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
@ -2615,6 +2620,8 @@ Global
{546E4417-5409-40F4-A125-E08329DD82BB} = {A4633711-7FB6-411A-8D08-BB9A0A778046}
{738A72FB-ED83-4127-AA3B-59BF90635F8F} = {A4633711-7FB6-411A-8D08-BB9A0A778046}
{9950639D-AA4C-4FF1-A65E-9790EB561C8A} = {A4633711-7FB6-411A-8D08-BB9A0A778046}
{5FA85E8E-3276-43DF-CC93-6A9847905166} = {9E72FEB9-A626-4312-892B-CDD043879758}
{AEEA81D6-B282-93CF-862B-9FCF1A5052F7} = {A5543E56-DA53-494D-A531-DA75091D46FF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}

21
aspnet-core/LINGYUN.MicroService.SingleProject.sln

@ -642,8 +642,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Gdpr.EntityFram
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Gdpr.Domain.Identity", "modules\gdpr\LINGYUN.Abp.Gdpr.Domain.Identity\LINGYUN.Abp.Gdpr.Domain.Identity.csproj", "{06FC3141-5F92-43A6-94C9-E2519EE9A91E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Emailing", "modules\account\LINGYUN.Abp.Account.Emailing\LINGYUN.Abp.Account.Emailing.csproj", "{9D53FA98-6BF1-AEF9-8CC3-949E24C03B76}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Web", "modules\account\LINGYUN.Abp.Account.Web\LINGYUN.Abp.Account.Web.csproj", "{4E3CE014-38BF-FF33-A107-B268E57CE8E9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Web.IdentityServer", "modules\account\LINGYUN.Abp.Account.Web.IdentityServer\LINGYUN.Abp.Account.Web.IdentityServer.csproj", "{57A61286-DE14-9313-5BAB-B077C6239377}"
@ -694,6 +692,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.OAuth",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Elsa.Designer", "modules\elsa\LINGYUN.Abp.Elsa.Designer\LINGYUN.Abp.Elsa.Designer.csproj", "{C9756AD3-3AEA-4AA8-99E3-8305D37E0903}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Work.ExternalContact", "framework\wechat\LINGYUN.Abp.WeChat.Work.ExternalContact\LINGYUN.Abp.WeChat.Work.ExternalContact.csproj", "{B4311504-B0C8-AC8D-02A5-81AD43B4087B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Account.Security", "modules\account\LINGYUN.Abp.Account.Security\LINGYUN.Abp.Account.Security.csproj", "{5FA85E8E-3276-43DF-CC93-6A9847905166}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1748,10 +1750,6 @@ Global
{06FC3141-5F92-43A6-94C9-E2519EE9A91E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06FC3141-5F92-43A6-94C9-E2519EE9A91E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{06FC3141-5F92-43A6-94C9-E2519EE9A91E}.Release|Any CPU.Build.0 = Release|Any CPU
{9D53FA98-6BF1-AEF9-8CC3-949E24C03B76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D53FA98-6BF1-AEF9-8CC3-949E24C03B76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D53FA98-6BF1-AEF9-8CC3-949E24C03B76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D53FA98-6BF1-AEF9-8CC3-949E24C03B76}.Release|Any CPU.Build.0 = Release|Any CPU
{4E3CE014-38BF-FF33-A107-B268E57CE8E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E3CE014-38BF-FF33-A107-B268E57CE8E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E3CE014-38BF-FF33-A107-B268E57CE8E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -1848,6 +1846,14 @@ Global
{C9756AD3-3AEA-4AA8-99E3-8305D37E0903}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9756AD3-3AEA-4AA8-99E3-8305D37E0903}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9756AD3-3AEA-4AA8-99E3-8305D37E0903}.Release|Any CPU.Build.0 = Release|Any CPU
{B4311504-B0C8-AC8D-02A5-81AD43B4087B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B4311504-B0C8-AC8D-02A5-81AD43B4087B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B4311504-B0C8-AC8D-02A5-81AD43B4087B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B4311504-B0C8-AC8D-02A5-81AD43B4087B}.Release|Any CPU.Build.0 = Release|Any CPU
{5FA85E8E-3276-43DF-CC93-6A9847905166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5FA85E8E-3276-43DF-CC93-6A9847905166}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5FA85E8E-3276-43DF-CC93-6A9847905166}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FA85E8E-3276-43DF-CC93-6A9847905166}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2160,7 +2166,6 @@ Global
{CDC0F589-D716-4FCE-9BBA-CD1A0B3D8409} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{57C95347-CF48-43DD-BFC9-597A43041AC5} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{06FC3141-5F92-43A6-94C9-E2519EE9A91E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{9D53FA98-6BF1-AEF9-8CC3-949E24C03B76} = {4F837B81-EA7D-472A-8482-3D5A730DF810}
{4E3CE014-38BF-FF33-A107-B268E57CE8E9} = {4F837B81-EA7D-472A-8482-3D5A730DF810}
{57A61286-DE14-9313-5BAB-B077C6239377} = {4F837B81-EA7D-472A-8482-3D5A730DF810}
{44DE8AC4-D7AC-C71C-9C77-4A1C0137D3E3} = {4F837B81-EA7D-472A-8482-3D5A730DF810}
@ -2186,6 +2191,8 @@ Global
{2379F502-BBBD-4BF2-91F7-D0E5C61E91B7} = {4F837B81-EA7D-472A-8482-3D5A730DF810}
{2E4C437A-989D-68D9-C5FB-1AE085B2CBC8} = {4F837B81-EA7D-472A-8482-3D5A730DF810}
{C9756AD3-3AEA-4AA8-99E3-8305D37E0903} = {07DFEB1E-ED92-4E97-A801-FAB2D70F4F35}
{B4311504-B0C8-AC8D-02A5-81AD43B4087B} = {91867618-0D86-4410-91C6-B1166A9ACDF9}
{5FA85E8E-3276-43DF-CC93-6A9847905166} = {4F837B81-EA7D-472A-8482-3D5A730DF810}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {711A43C0-A2F8-4E5C-9B9F-F2551E4B3FF1}

7
aspnet-core/LINGYUN.MicroService.TaskManagement.sln

@ -186,6 +186,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Saas.EntityFram
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.MultiTenancy.Saas", "modules\saas\LINGYUN.Abp.MultiTenancy.Saas\LINGYUN.Abp.MultiTenancy.Saas.csproj", "{E09374FE-6B13-D69E-B171-94C63E6B80E5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Telemetry.OpenTelemetry", "framework\telemetry\LINGYUN.Abp.Telemetry.OpenTelemetry\LINGYUN.Abp.Telemetry.OpenTelemetry.csproj", "{164E4514-4E84-F13A-E24E-2A743753CDB3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -480,6 +482,10 @@ Global
{E09374FE-6B13-D69E-B171-94C63E6B80E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E09374FE-6B13-D69E-B171-94C63E6B80E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E09374FE-6B13-D69E-B171-94C63E6B80E5}.Release|Any CPU.Build.0 = Release|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -557,6 +563,7 @@ Global
{618C7EAF-8D6C-50B5-84E4-AE733CEA4A8C} = {DE7B8B71-635D-4113-AA49-B218D6998CEC}
{F58A7141-AC0D-26EC-F22A-DF0DB1BA6955} = {DE7B8B71-635D-4113-AA49-B218D6998CEC}
{E09374FE-6B13-D69E-B171-94C63E6B80E5} = {DE7B8B71-635D-4113-AA49-B218D6998CEC}
{164E4514-4E84-F13A-E24E-2A743753CDB3} = {5A41C31A-B966-418B-B446-5BA1D7E61A62}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E1FD1F4C-D344-408B-97CF-B6F1F6D7D293}

7
aspnet-core/LINGYUN.MicroService.WebhooksManagement.sln

@ -173,6 +173,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Quartz.SqlServe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Quartz.SqlInstaller", "modules\task-management\LINGYUN.Abp.Quartz.SqlInstaller\LINGYUN.Abp.Quartz.SqlInstaller.csproj", "{0EEA73CA-D8DD-F044-045D-F070F8831E26}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Telemetry.OpenTelemetry", "framework\telemetry\LINGYUN.Abp.Telemetry.OpenTelemetry\LINGYUN.Abp.Telemetry.OpenTelemetry.csproj", "{164E4514-4E84-F13A-E24E-2A743753CDB3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -447,6 +449,10 @@ Global
{0EEA73CA-D8DD-F044-045D-F070F8831E26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0EEA73CA-D8DD-F044-045D-F070F8831E26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0EEA73CA-D8DD-F044-045D-F070F8831E26}.Release|Any CPU.Build.0 = Release|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -533,6 +539,7 @@ Global
{B6452D3F-E58C-C433-E52A-F65EE2657E01} = {8C3DF571-BAC3-48C4-B46A-AC0E0EAA9871}
{D613F393-9CEE-2D3B-33C9-90630F8348E7} = {8C3DF571-BAC3-48C4-B46A-AC0E0EAA9871}
{0EEA73CA-D8DD-F044-045D-F070F8831E26} = {8C3DF571-BAC3-48C4-B46A-AC0E0EAA9871}
{164E4514-4E84-F13A-E24E-2A743753CDB3} = {CE07B9F4-54E8-4E74-BE14-8E5C1FB7AFC8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {80ED12A5-C899-459F-A181-ADCC9D680DE5}

51
aspnet-core/LINGYUN.MicroService.WechatManagement.sln

@ -111,6 +111,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Identity.Sessio
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Telemetry.SkyWalking", "framework\telemetry\LINGYUN.Abp.Telemetry.SkyWalking\LINGYUN.Abp.Telemetry.SkyWalking.csproj", "{7CF83493-6AF5-9C6D-01A7-AC7FC11BC2CE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Telemetry.OpenTelemetry", "framework\telemetry\LINGYUN.Abp.Telemetry.OpenTelemetry\LINGYUN.Abp.Telemetry.OpenTelemetry.csproj", "{164E4514-4E84-F13A-E24E-2A743753CDB3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Identity.WeChat.Work", "framework\wechat\LINGYUN.Abp.Identity.WeChat.Work\LINGYUN.Abp.Identity.WeChat.Work.csproj", "{CCC8A29C-1FC1-044F-895A-EC6894CDC8E5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Work.ExternalContact", "framework\wechat\LINGYUN.Abp.WeChat.Work.ExternalContact\LINGYUN.Abp.WeChat.Work.ExternalContact.csproj", "{3A6D0EA9-FA3F-4252-87D9-31845D57180D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.TestsBase", "tests\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj", "{757279EC-708D-D1AD-5E9E-7B757F3F7785}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Work.Tests", "tests\LINGYUN.Abp.WeChat.Work.Tests\LINGYUN.Abp.WeChat.Work.Tests.csproj", "{9DC2A982-8FA2-93BE-5FE1-3D7F7C105C39}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Work.ExternalContact.Tests", "tests\LINGYUN.Abp.WeChat.Work.ExternalContact.Tests\LINGYUN.Abp.WeChat.Work.ExternalContact.Tests.csproj", "{6628FB84-66C0-4023-9AA0-AAC4C0CC5917}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Work.Handlers", "framework\wechat\LINGYUN.Abp.WeChat.Work.Handlers\LINGYUN.Abp.WeChat.Work.Handlers.csproj", "{E0DFCAD9-8AFE-A816-10F7-B4CA6691E910}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -281,6 +297,34 @@ Global
{7CF83493-6AF5-9C6D-01A7-AC7FC11BC2CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CF83493-6AF5-9C6D-01A7-AC7FC11BC2CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CF83493-6AF5-9C6D-01A7-AC7FC11BC2CE}.Release|Any CPU.Build.0 = Release|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Release|Any CPU.Build.0 = Release|Any CPU
{CCC8A29C-1FC1-044F-895A-EC6894CDC8E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCC8A29C-1FC1-044F-895A-EC6894CDC8E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCC8A29C-1FC1-044F-895A-EC6894CDC8E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCC8A29C-1FC1-044F-895A-EC6894CDC8E5}.Release|Any CPU.Build.0 = Release|Any CPU
{3A6D0EA9-FA3F-4252-87D9-31845D57180D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A6D0EA9-FA3F-4252-87D9-31845D57180D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A6D0EA9-FA3F-4252-87D9-31845D57180D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A6D0EA9-FA3F-4252-87D9-31845D57180D}.Release|Any CPU.Build.0 = Release|Any CPU
{757279EC-708D-D1AD-5E9E-7B757F3F7785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{757279EC-708D-D1AD-5E9E-7B757F3F7785}.Debug|Any CPU.Build.0 = Debug|Any CPU
{757279EC-708D-D1AD-5E9E-7B757F3F7785}.Release|Any CPU.ActiveCfg = Release|Any CPU
{757279EC-708D-D1AD-5E9E-7B757F3F7785}.Release|Any CPU.Build.0 = Release|Any CPU
{9DC2A982-8FA2-93BE-5FE1-3D7F7C105C39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DC2A982-8FA2-93BE-5FE1-3D7F7C105C39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DC2A982-8FA2-93BE-5FE1-3D7F7C105C39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DC2A982-8FA2-93BE-5FE1-3D7F7C105C39}.Release|Any CPU.Build.0 = Release|Any CPU
{6628FB84-66C0-4023-9AA0-AAC4C0CC5917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6628FB84-66C0-4023-9AA0-AAC4C0CC5917}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6628FB84-66C0-4023-9AA0-AAC4C0CC5917}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6628FB84-66C0-4023-9AA0-AAC4C0CC5917}.Release|Any CPU.Build.0 = Release|Any CPU
{E0DFCAD9-8AFE-A816-10F7-B4CA6691E910}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0DFCAD9-8AFE-A816-10F7-B4CA6691E910}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0DFCAD9-8AFE-A816-10F7-B4CA6691E910}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0DFCAD9-8AFE-A816-10F7-B4CA6691E910}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -337,6 +381,13 @@ Global
{CC8DDC8C-CC0C-4534-8D9F-2C345E065869} = {52701ECE-3EBD-45EC-AD2C-0AAB15322311}
{80EBBECC-EF11-4E5E-91DA-EEECED832F21} = {73ED64BB-7C39-42EA-B821-3DD697B9C36A}
{7CF83493-6AF5-9C6D-01A7-AC7FC11BC2CE} = {FFACB4F0-33E0-4F8B-A97E-8FFFA10C12E6}
{164E4514-4E84-F13A-E24E-2A743753CDB3} = {FFACB4F0-33E0-4F8B-A97E-8FFFA10C12E6}
{CCC8A29C-1FC1-044F-895A-EC6894CDC8E5} = {EDBB7BC1-46F0-4803-A572-7F8FEF433BE2}
{3A6D0EA9-FA3F-4252-87D9-31845D57180D} = {EDBB7BC1-46F0-4803-A572-7F8FEF433BE2}
{757279EC-708D-D1AD-5E9E-7B757F3F7785} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{9DC2A982-8FA2-93BE-5FE1-3D7F7C105C39} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{6628FB84-66C0-4023-9AA0-AAC4C0CC5917} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{E0DFCAD9-8AFE-A816-10F7-B4CA6691E910} = {EDBB7BC1-46F0-4803-A572-7F8FEF433BE2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EC9D01C1-EA3C-48C7-A279-4D35C8AD312E}

7
aspnet-core/LINGYUN.MicroService.Workflow.sln

@ -195,6 +195,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Quartz.SqlServe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Quartz.SqlInstaller", "modules\task-management\LINGYUN.Abp.Quartz.SqlInstaller\LINGYUN.Abp.Quartz.SqlInstaller.csproj", "{0EEA73CA-D8DD-F044-045D-F070F8831E26}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Telemetry.OpenTelemetry", "framework\telemetry\LINGYUN.Abp.Telemetry.OpenTelemetry\LINGYUN.Abp.Telemetry.OpenTelemetry.csproj", "{164E4514-4E84-F13A-E24E-2A743753CDB3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -513,6 +515,10 @@ Global
{0EEA73CA-D8DD-F044-045D-F070F8831E26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0EEA73CA-D8DD-F044-045D-F070F8831E26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0EEA73CA-D8DD-F044-045D-F070F8831E26}.Release|Any CPU.Build.0 = Release|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{164E4514-4E84-F13A-E24E-2A743753CDB3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -602,6 +608,7 @@ Global
{B5C96536-6387-44F4-A2E2-D4D683588AF5} = {7844FF58-7DBF-46E1-88B7-9764382A4EE9}
{52F5A9DE-50B8-42C6-9980-E908DC6A4B52} = {7844FF58-7DBF-46E1-88B7-9764382A4EE9}
{0EEA73CA-D8DD-F044-045D-F070F8831E26} = {7844FF58-7DBF-46E1-88B7-9764382A4EE9}
{164E4514-4E84-F13A-E24E-2A743753CDB3} = {6DA78E72-BA55-4ECF-97DB-6258174D3E2A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6BB7A5DE-DA12-44DC-BC9B-0F6CA524346F}

20
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs

@ -152,6 +152,26 @@ public class AliyunSettingAppService : ApplicationService, IAliyunSettingAppServ
await SettingManager.GetOrNullAsync(AliyunSettingNames.Sms.VisableErrorToClient, providerName, providerKey),
ValueType.Boolean,
providerName);
var smsVerifyCodeSetting = aliyunSettingGroup.AddSetting(L["DisplayName:Aliyun.SmsVerifyCode"], L["Description:Aliyun.SmsVerifyCode"]);
smsVerifyCodeSetting.AddDetail(
await SettingDefinitionManager.GetAsync(AliyunSettingNames.SmsVerifyCode.Domain),
StringLocalizerFactory,
await SettingManager.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.Domain, providerName, providerKey),
ValueType.String,
providerName);
smsVerifyCodeSetting.AddDetail(
await SettingDefinitionManager.GetAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName),
StringLocalizerFactory,
await SettingManager.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName, providerName, providerKey),
ValueType.String,
providerName);
smsVerifyCodeSetting.AddDetail(
await SettingDefinitionManager.GetAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode),
StringLocalizerFactory,
await SettingManager.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode, providerName, providerKey),
ValueType.String,
providerName);
}
#endregion

18
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json

@ -31,22 +31,6 @@
"DisplayName:DurationSeconds": "Duration Seconds,in seconds",
"Description:DurationSeconds": "Duration Seconds,in seconds",
"DisplayName:Policy": "Policy",
"Description:Policy": "Policy",
"DisplayName:Aliyun.Sms": "Sms",
"Description:Aliyun.Sms": "Sms",
"DisplayName:ActionName": "Action Name",
"Description:ActionName": "Action Name",
"DisplayName:DefaultSignName": "Default Sign Name",
"Description:DefaultSignName": "Default Sign Name",
"DisplayName:DefaultTemplateCode": "Default Template Code",
"Description:DefaultTemplateCode": "Default Template Code",
"DisplayName:DefaultPhoneNumber": "Default Phone Number",
"Description:DefaultPhoneNumber": "Default Phone Number",
"DisplayName:Domain": "Domain",
"Description:Domain": "Domain",
"DisplayName:Version": "Version",
"Description:Version": "Version",
"DisplayName:VisableErrorToClient": "Visable Error To Client",
"Description:VisableErrorToClient": "Visable Error To Client"
"Description:Policy": "Policy"
}
}

16
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json

@ -32,22 +32,6 @@
"Description:DurationSeconds": "过期时间最小值为900秒,默认3600秒",
"DisplayName:Policy": "权限策略",
"Description:Policy": "生成STS Token时可以指定一个额外的权限策略,以进一步限制STS Token的权限",
"DisplayName:Aliyun.Sms": "短信服务",
"Description:Aliyun.Sms": "阿里云短信服务",
"DisplayName:ActionName": "发送短信方法",
"Description:ActionName": "发送短信方法名称,详情见阿里云Sms服务",
"DisplayName:DefaultSignName": "默认短信签名",
"Description:DefaultSignName": "当用户未指定短信签名时的默认签名名称",
"DisplayName:DefaultTemplateCode": "默认短信模板号",
"Description:DefaultTemplateCode": "当用户未指定短信签名时的默认短信模板号",
"DisplayName:DefaultPhoneNumber": "默认接收短信手机号",
"Description:DefaultPhoneNumber": "当用户未指定短信接收方时的默认接收手机号码",
"DisplayName:Domain": "阿里云sms服务域名",
"Description:Domain": "阿里云sms服务域名",
"DisplayName:Version": "阿里云sms服务版本号",
"Description:Version": "阿里云sms服务版本号",
"DisplayName:VisableErrorToClient": "发送错误到客户端",
"Description:VisableErrorToClient": "当短信服务发送出现错误时是否发送错误详情到客户端",
"Region:HangZhou": "华东1(杭州)",
"Region:ShangHai": "华东2(上海)",
"Region:NanJing": "华东5(南京-本地地域)",

20
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs

@ -79,4 +79,24 @@ public static class AliyunSettingNames
/// </summary>
public const string VisableErrorToClient = Prefix + ".VisableErrorToClient";
}
/// <summary>
/// 云通信号码认证服务
/// </summary>
public class SmsVerifyCode
{
public const string Prefix = AliyunSettingNames.Prefix + ".SmsVerifyCode";
/// <summary>
/// 阿里云号码认证服务域名
/// </summary>
public const string Domain = Prefix + ".Domain";
/// <summary>
/// 默认签名
/// </summary>
public const string DefaultSignName = Prefix + ".DefaultSignName";
/// <summary>
/// 默认短信模板号
/// </summary>
public const string DefaultTemplateCode = Prefix + ".DefaultTemplateCode";
}
}

48
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs

@ -10,6 +10,7 @@ public class AliyunSettingProvider : SettingDefinitionProvider
{
context.Add(GetAuthorizationSettings());
context.Add(GetSmsSettings());
context.Add(GetSmsVerifyCodeSettings());
}
private SettingDefinition[] GetAuthorizationSettings()
@ -169,8 +170,8 @@ public class AliyunSettingProvider : SettingDefinitionProvider
new SettingDefinition(
AliyunSettingNames.Sms.Domain,
defaultValue: "dysmsapi.aliyuncs.com",
displayName: L("DisplayName:Domain"),
description: L("Description:Domain"),
displayName: L("DisplayName:SmsDomain"),
description: L("Description:SmsDomain"),
isVisibleToClients: false
)
.WithProviders(
@ -204,6 +205,49 @@ public class AliyunSettingProvider : SettingDefinitionProvider
TenantSettingValueProvider.ProviderName)
};
}
private SettingDefinition[] GetSmsVerifyCodeSettings()
{
return new SettingDefinition[]
{
new SettingDefinition(
AliyunSettingNames.SmsVerifyCode.Domain,
defaultValue: "dypnsapi.aliyuncs.com",
displayName: L("DisplayName:SmsVerifyCodeDomain"),
description: L("Description:SmsVerifyCodeDomain"),
isVisibleToClients: false
)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
new SettingDefinition(
AliyunSettingNames.SmsVerifyCode.DefaultSignName,
displayName: L("DisplayName:DefaultSignName"),
description: L("Description:DefaultSignName"),
isVisibleToClients: false,
isEncrypted: true
)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
new SettingDefinition(
AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode,
displayName: L("DisplayName:DefaultTemplateCode"),
description: L("Description:DefaultTemplateCode"),
isVisibleToClients: false,
isEncrypted: true
)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
};
}
private ILocalizableString L(string name)
{
return LocalizableString.Create<AliyunResource>(name);

7
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj

@ -15,20 +15,21 @@
</PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\BlobStoring\Tencent\Localization\*.json" />
<None Remove="LINGYUN\Abp\BlobStoring\Tencent\Localization\*.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\BlobStoring\Tencent\Localization\*.json" />
<EmbeddedResource Include="LINGYUN\Abp\BlobStoring\Tencent\Localization\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BlobStoring" />
<PackageReference Include="Tencent.QCloud.Cos.Sdk" />
<PackageReference Include="Microsoft.Extensions.Http" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Tencent\LINGYUN.Abp.Tencent.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.Tencent\LINGYUN.Abp.Tencent.csproj" />
</ItemGroup>
</Project>

3
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs

@ -1,5 +1,6 @@
using LINGYUN.Abp.Tencent;
using LINGYUN.Abp.Tencent.Localization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.BlobStoring;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
@ -25,5 +26,7 @@ public class AbpBlobStoringTencentCloudModule : AbpModule
.Get<TencentCloudResource>()
.AddVirtualJson("/LINGYUN/Abp/BlobStoring/Tencent/Localization");
});
context.Services.AddTenantOssClient();
}
}

5
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/BlobStoringTencentConsts.cs

@ -0,0 +1,5 @@
namespace LINGYUN.Abp.BlobStoring.Tencent;
internal static class BlobStoringTencentConsts
{
public const string HttpClient = "BlobStoring.Tencent";
}

11
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs

@ -33,6 +33,13 @@ public class CosClientFactory : AbstractTencentCloudClientFactory<CosXml, Tencen
return await CreateAsync(configuration.GetTencentConfiguration());
}
public virtual Task<TencentBlobProviderConfiguration> GetConfigurationAsync<TContainer>()
{
var configuration = ConfigurationProvider.Get<TContainer>();
return Task.FromResult(configuration.GetTencentConfiguration());
}
protected override CosXml CreateClient(TencentBlobProviderConfiguration configuration, TencentCloudClientCacheItem cloudCache)
{
// 推荐全局单个对象,需要解决缓存过期事件
@ -46,8 +53,10 @@ public class CosClientFactory : AbstractTencentCloudClientFactory<CosXml, Tencen
{
var configBuilder = new CosXmlConfig.Builder();
configBuilder
.IsHttps(true)
.SetAppid(configuration.AppId)
.SetRegion(configuration.Region);
.SetRegion(configuration.Region)
.SetDebugLog(true);
var cred = new DefaultQCloudCredentialProvider(
cloudCache.SecretId,

2
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ICosClientFactory.cs

@ -8,4 +8,6 @@ public interface ICosClientFactory
Task<CosXml> CreateAsync<TContainer>();
Task<CosXml> CreateAsync(TencentBlobProviderConfiguration configuration);
Task<TencentBlobProviderConfiguration> GetConfigurationAsync<TContainer>();
}

2
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs

@ -40,7 +40,7 @@ public class TencentBlobProviderConfiguration
/// 创建命名空间时防盗链列表
/// </summary>
public List<string> CreateBucketReferer {
get => _containerConfiguration.GetConfiguration<List<string>>(TencentBlobProviderConfigurationNames.CreateBucketReferer);
get => _containerConfiguration.GetConfigurationOrDefault(TencentBlobProviderConfigurationNames.CreateBucketReferer, new List<string>());
set {
if (value == null)
{

45
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs

@ -2,10 +2,12 @@
using COSXML.Common;
using COSXML.Model.Bucket;
using COSXML.Model.Object;
using COSXML.Model.Tag;
using LINGYUN.Abp.Tencent.Features;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.BlobStoring;
@ -19,15 +21,18 @@ public class TencentCloudBlobProvider : BlobProviderBase, ITransientDependency
{
protected IFeatureChecker FeatureChecker { get; }
protected ICosClientFactory CosClientFactory { get; }
protected IHttpClientFactory HttpClientFactory { get; }
protected ITencentBlobNameCalculator TencentBlobNameCalculator { get; }
public TencentCloudBlobProvider(
IFeatureChecker featureChecker,
ICosClientFactory cosClientFactory,
IHttpClientFactory httpClientFactory,
ITencentBlobNameCalculator tencentBlobNameCalculator)
{
FeatureChecker = featureChecker;
CosClientFactory = cosClientFactory;
HttpClientFactory = httpClientFactory;
TencentBlobNameCalculator = tencentBlobNameCalculator;
}
@ -63,28 +68,28 @@ public class TencentCloudBlobProvider : BlobProviderBase, ITransientDependency
return null;
}
// TODO: 未经验证
var request = new GetObjectBytesRequest(GetBucketName(args), blobName);
var ossObject = ossClient.GetObject(request);
var memoryStream = new MemoryStream();
await memoryStream.WriteAsync(ossObject.content, 0, ossObject.content.Length);
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
var configuration = args.Configuration.GetTencentConfiguration();
// See: https://cloud.tencent.com/document/product/436/47238
var preSignatureStruct = new PreSignatureStruct
{
appid = configuration.AppId,//"1250000000"; //腾讯云账号 APPID
region = configuration.Region,//"COS_REGION"; //存储桶地域
bucket = GetBucketName(args),//"examplebucket-1250000000"; //存储桶
key = blobName, //对象键
httpMethod = "GET", //HTTP 请求方法
isHttps = true, //生成 HTTPS 请求 URL
signDurationSecond = 600, //请求签名时间为600s
headers = null, //签名中需要校验的 header
queryParameters = null //签名中需要校验的 URL 中请求参数
};
var requestSignURL = ossClient.GenerateSignURL(preSignatureStruct);
var client = HttpClientFactory.CreateTenantOssClient();
return await client.GetStreamAsync(requestSignURL);
}
public override async Task SaveAsync(BlobProviderSaveArgs args)
{
var maxStreamSizeString = await FeatureChecker.GetOrNullAsync(TencentCloudFeatures.BlobStoring.MaximumStreamSize);
if (!"0".Equals(maxStreamSizeString) ||
(int.TryParse(maxStreamSizeString, out var maxStreamSize)
&& (maxStreamSize <= 0
|| maxStreamSize < args.BlobStream.Length / 1024 / 1024)))
{
throw new BusinessException("TencentCloud:10101")
.WithData("Size", maxStreamSizeString);
}
var ossClient = await GetOssClientAsync(args);
var blobName = TencentBlobNameCalculator.Calculate(args);
var configuration = args.Configuration.GetTencentConfiguration();
@ -131,8 +136,8 @@ public class TencentCloudBlobProvider : BlobProviderBase, ITransientDependency
var bucketName = GetBucketName(args);
var request = new PutBucketRequest(bucketName);
// TODO: good! 这很Java
request.SetCosACL(CosACL.PublicReadWrite);
request.SetCosACL(CosACL.Private);
cos.PutBucket(request);

12
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/Microsoft/Extensions/DependencyInjection/BlobStoringTencentHttpClientFactoryServiceCollectionExtensions.cs

@ -0,0 +1,12 @@
using LINGYUN.Abp.BlobStoring.Tencent;
namespace Microsoft.Extensions.DependencyInjection;
internal static class BlobStoringTencentHttpClientFactoryServiceCollectionExtensions
{
public static IServiceCollection AddTenantOssClient(this IServiceCollection services)
{
services.AddHttpClient(BlobStoringTencentConsts.HttpClient);
return services;
}
}

11
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/System/Net/Http/BlobStoringTencentHttpClientFactoryExtenssions.cs

@ -0,0 +1,11 @@
using LINGYUN.Abp.BlobStoring.Tencent;
namespace System.Net.Http;
public static class BlobStoringTencentHttpClientFactoryExtenssions
{
public static HttpClient CreateTenantOssClient(
this IHttpClientFactory httpClientFactory)
{
return httpClientFactory.CreateClient(BlobStoringTencentConsts.HttpClient); ;
}
}

4
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN/Abp/Tencent/AbpTencentCloudModule.cs

@ -5,11 +5,15 @@ using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.Settings;
namespace LINGYUN.Abp.Tencent;
[DependsOn(
typeof(AbpCachingModule),
typeof(AbpFeaturesModule),
typeof(AbpSettingsModule),
typeof(AbpJsonModule),
typeof(AbpLocalizationModule))]
public class AbpTencentCloudModule : AbpModule

4
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs

@ -56,9 +56,7 @@ public class AliyunBlobProvider : BlobProviderBase, ITransientDependency
}
var ossObject = ossClient.GetObject(GetBucketName(args), blobName);
var memoryStream = new MemoryStream();
await ossObject.Content.CopyToAsync(memoryStream);
return memoryStream;
return ossObject.Content;
}
public override async Task SaveAsync(BlobProviderSaveArgs args)

5
aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.csproj

@ -10,10 +10,9 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
<Description>Cap分布式事件总线</Description>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DocumentationFile>$(SolutionDir)framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.xml</DocumentationFile>
<Description>Cap分布式事件总线</Description>
<RootNamespace />
</PropertyGroup>
<ItemGroup>

156
aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPBootstrapper.cs

@ -0,0 +1,156 @@
using DotNetCore.CAP;
using DotNetCore.CAP.Internal;
using DotNetCore.CAP.Persistence;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.EventBus.CAP;
public class AbpCAPBootstrapper : IBootstrapper
{
private readonly ILogger<IBootstrapper> _logger;
private readonly IServiceProvider _serviceProvider;
private CancellationTokenSource _cts;
private bool _disposed;
private IEnumerable<IProcessingServer> _processors = default!;
public bool IsStarted => !_cts?.IsCancellationRequested ?? false;
public AbpCAPBootstrapper(IServiceProvider serviceProvider, ILogger<IBootstrapper> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
public async Task BootstrapAsync(CancellationToken cancellationToken = default)
{
if (_cts != null)
{
_logger.LogInformation("### CAP background task is already started!");
return;
}
_logger.LogDebug("### CAP background task is starting.");
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
CheckRequirement();
_processors = _serviceProvider.GetServices<IProcessingServer>();
try
{
await _serviceProvider.GetRequiredService<IStorageInitializer>().InitializeAsync(_cts.Token).ConfigureAwait(false);
}
catch (Exception e)
{
if (e is InvalidOperationException) throw;
_logger.LogError(e, "Initializing the storage structure failed!");
}
_cts.Token.Register(() =>
{
_logger.LogDebug("### CAP background task is stopping.");
foreach (var item in _processors)
try
{
item.Dispose();
}
catch (OperationCanceledException ex)
{
_logger.LogWarning(ex, $"Expected an OperationCanceledException, but found '{ex.Message}'.");
}
});
await BootstrapCoreAsync().ConfigureAwait(false);
_disposed = false;
_logger.LogInformation("### CAP started!");
}
protected virtual async Task BootstrapCoreAsync()
{
foreach (var item in _processors)
{
try
{
_cts!.Token.ThrowIfCancellationRequested();
await item.Start(_cts!.Token);
}
catch (OperationCanceledException)
{
// ignore
}
catch (Exception ex)
{
_logger.LogError(ex, "Starting the processors throw an exception.");
}
}
}
public virtual void Dispose()
{
if (_disposed) return;
_cts?.Cancel();
_cts?.Dispose();
_cts = null;
_disposed = true;
}
public virtual async Task ExecuteAsync(CancellationToken stoppingToken)
{
await BootstrapAsync(stoppingToken).ConfigureAwait(false);
}
public virtual Task StopAsync(CancellationToken cancellationToken)
{
_cts?.Cancel();
return Task.CompletedTask;
}
private void CheckRequirement()
{
var marker = _serviceProvider.GetService<CapMarkerService>();
if (marker == null)
throw new InvalidOperationException(
"AddCap() must be added on the service collection. eg: services.AddCap(...)");
var messageQueueMarker = _serviceProvider.GetService<CapMessageQueueMakerService>();
if (messageQueueMarker == null)
throw new InvalidOperationException(
"You must be config transport provider for CAP!" + Environment.NewLine +
"==================================================================================" +
Environment.NewLine +
"======== eg: services.AddCap( options => { options.UseRabbitMQ(...) }); ========" +
Environment.NewLine +
"==================================================================================");
var databaseMarker = _serviceProvider.GetService<CapStorageMarkerService>();
if (databaseMarker == null)
throw new InvalidOperationException(
"You must be config storage provider for CAP!" + Environment.NewLine +
"===================================================================================" +
Environment.NewLine +
"======== eg: services.AddCap( options => { options.UseSqlServer(...) }); ========" +
Environment.NewLine +
"===================================================================================");
}
public ValueTask DisposeAsync()
{
Dispose();
return ValueTask.CompletedTask;
}
}

25
aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs

@ -1,8 +1,12 @@
using DotNetCore.CAP;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.EventBus;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.EventBus.CAP;
@ -12,6 +16,7 @@ namespace LINGYUN.Abp.EventBus.CAP;
[DependsOn(typeof(AbpEventBusModule))]
public class AbpCAPEventBusModule : AbpModule
{
private readonly CancellationTokenSource _cancellationTokenSource = new();
/// <summary>
/// ConfigureServices
/// </summary>
@ -48,4 +53,24 @@ public class AbpCAPEventBusModule : AbpModule
}
});
}
public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
{
AsyncHelper.RunSync(() => OnPreApplicationInitializationAsync(context));
}
public async override Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context)
{
await context
.ServiceProvider
.GetRequiredService<IBootstrapper>()
.BootstrapAsync(_cancellationTokenSource.Token);
}
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
{
_cancellationTokenSource.Cancel();
return Task.CompletedTask;
}
}

29
aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs

@ -2,7 +2,9 @@
using DotNetCore.CAP.Internal;
using DotNetCore.CAP.Serialization;
using LINGYUN.Abp.EventBus.CAP;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
namespace Microsoft.Extensions.DependencyInjection;
@ -23,6 +25,33 @@ public static class ServiceCollectionExtensions
// 替换为自己的实现
services.AddSingleton<ISubscribeInvoker, AbpCAPSubscribeInvoker>();
services.AddSingleton<ISerializer, AbpCapSerializer>();
// 移除默认CAP启动接口
services.RemoveAll(service =>
{
if (service.ServiceType.IsAssignableFrom(typeof(IBootstrapper)))
{
return true;
}
// 默认Bootstrapper
if (service.ImplementationType != null &&
service.ImplementationType.IsAssignableTo(typeof(IBootstrapper)))
{
return true;
}
// 默认Bootstrapper HostService
if (service.ImplementationFactory != null &&
service.ImplementationFactory.Method.ReturnType.IsAssignableTo(typeof(IBootstrapper)))
{
return true;
}
return false;
});
// 使用重写的接口,不使用BackgroundService
services.AddSingleton<AbpCAPBootstrapper>();
services.AddSingleton<IBootstrapper>(sp => sp.GetRequiredService<AbpCAPBootstrapper>());
return services;
}
}

397
aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.xml

@ -1,397 +0,0 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>LINGYUN.Abp.EventBus.CAP</name>
</assembly>
<members>
<member name="T:LINGYUN.Abp.EventBus.CAP.AbpCAPConsumerServiceSelector">
<summary>
消费者查找器
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.AbpCAPConsumerServiceSelector.CapOptions">
<summary>
CAP配置
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.AbpCAPConsumerServiceSelector.AbpDistributedEventBusOptions">
<summary>
Abp分布式事件配置
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.AbpCAPConsumerServiceSelector.ServiceProvider">
<summary>
服务提供者
</summary>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPConsumerServiceSelector.#ctor(System.IServiceProvider,Microsoft.Extensions.Options.IOptions{DotNetCore.CAP.CapOptions},Microsoft.Extensions.Options.IOptions{Volo.Abp.EventBus.Distributed.AbpDistributedEventBusOptions})">
<summary>
Creates a new <see cref="T:DotNetCore.CAP.Internal.ConsumerServiceSelector" />.
</summary>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPConsumerServiceSelector.FindConsumersFromInterfaceTypes(System.IServiceProvider)">
<summary>
查找消费者集合
</summary>
<param name="provider"></param>
<returns></returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPConsumerServiceSelector.GetHandlerDescription(System.Type,System.Type)">
<summary>
获取事件处理器集合
</summary>
<param name="eventType"></param>
<param name="typeInfo"></param>
<returns></returns>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.AbpCAPEventBusModule">
<summary>
AbpCAPEventBusModule
</summary>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPEventBusModule.ConfigureServices(Volo.Abp.Modularity.ServiceConfigurationContext)">
<summary>
ConfigureServices
</summary>
<param name="context"></param>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.AbpCAPEventBusOptions">
<summary>
过期消息清理配置项
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.AbpCAPEventBusOptions.NotifyFailedCallback">
<summary>
发布消息处理失败通知
default: false
</summary>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.AbpCAPExecutionFailedException">
<summary>
AbpECAPExecutionFailedException
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.AbpCAPExecutionFailedException.MessageType">
<summary>
MessageType
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.AbpCAPExecutionFailedException.Origin">
<summary>
Message
</summary>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPExecutionFailedException.#ctor(DotNetCore.CAP.Messages.MessageType,DotNetCore.CAP.Messages.Message)">
<summary>
constructor
</summary>
<param name="messageType"></param>
<param name="origin"></param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPExecutionFailedException.#ctor(DotNetCore.CAP.Messages.MessageType,DotNetCore.CAP.Messages.Message,System.String)">
<summary>
constructor
</summary>
<param name="messageType"></param>
<param name="origin"></param>
<param name="message"></param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPExecutionFailedException.#ctor(DotNetCore.CAP.Messages.MessageType,DotNetCore.CAP.Messages.Message,System.String,System.Exception)">
<summary>
constructor
</summary>
<param name="messageType"></param>
<param name="origin"></param>
<param name="message"></param>
<param name="innerException"></param>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.AbpCAPMessageExtensions">
<summary>
CAP消息扩展
</summary>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPMessageExtensions.TryGetTenantId(DotNetCore.CAP.Messages.Message,System.Nullable{System.Guid}@)">
<summary>
尝试获取消息标头中的租户标识
</summary>
<param name="message"></param>
<param name="tenantId"></param>
<returns></returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPMessageExtensions.GetTenantIdOrNull(DotNetCore.CAP.Messages.Message)">
<summary>
获取消息标头中的租户标识
</summary>
<param name="message"></param>
<returns></returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPMessageExtensions.TryGetCorrelationId(DotNetCore.CAP.Messages.Message,System.String@)">
<summary>
尝试获取消息标头中的链路标识
</summary>
<param name="message"></param>
<param name="correlationId"></param>
<returns></returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPMessageExtensions.GetCorrelationIdOrNull(DotNetCore.CAP.Messages.Message)">
<summary>
获取消息标头中的链路标识
</summary>
<param name="message"></param>
<returns></returns>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.AbpCAPSubscribeInvoker">
<summary>
重写 ISubscribeInvoker 实现 Abp 租户集成
</summary>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPSubscribeInvoker.#ctor(Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider,Volo.Abp.Tracing.ICorrelationIdProvider,DotNetCore.CAP.Serialization.ISerializer,Volo.Abp.MultiTenancy.ICurrentTenant)">
<summary>
AbpCAPSubscribeInvoker
</summary>
<param name="loggerFactory"></param>
<param name="serviceProvider"></param>
<param name="correlationIdProvider"></param>
<param name="serializer"></param>
<param name="currentTenant"></param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPSubscribeInvoker.InvokeAsync(DotNetCore.CAP.Internal.ConsumerContext,System.Threading.CancellationToken)">
<summary>
调用订阅者方法
</summary>
<param name="context"></param>
<param name="cancellationToken"></param>
<returns></returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPSubscribeInvoker.GetCapProvidedParameter(DotNetCore.CAP.Internal.ParameterDescriptor,DotNetCore.CAP.Messages.Message,System.Threading.CancellationToken)">
<summary>
</summary>
<param name="parameterDescriptor"></param>
<param name="message"></param>
<param name="cancellationToken"></param>
<returns></returns>
<exception cref="T:System.ArgumentException"></exception>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPSubscribeInvoker.GetInstance(System.IServiceProvider,DotNetCore.CAP.Internal.ConsumerContext)">
<summary>
获取事件处理类实例
</summary>
<param name="provider"></param>
<param name="context"></param>
<returns></returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPSubscribeInvoker.ExecuteWithParameterAsync(LINGYUN.Abp.EventBus.CAP.Internal.ObjectMethodExecutor,System.Object,System.Object[])">
<summary>
通过给定的类型实例与参数调用订阅者方法
</summary>
<param name="executor"></param>
<param name="class"></param>
<param name="parameter"></param>
<returns></returns>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus">
<summary>
CAP分布式事件总线
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.CapPublisher">
<summary>
CAP消息发布接口
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.CustomDistributedEventSubscriber">
<summary>
自定义事件注册接口
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.HandlerFactories">
<summary>
本地事件处理器工厂对象集合
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.EventTypes">
<summary>
本地事件集合
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.CurrentUser">
<summary>
当前用户
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.CurrentClient">
<summary>
当前客户端
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.JsonSerializer">
<summary>
typeof <see cref="T:Volo.Abp.Json.IJsonSerializer"/>
</summary>
</member>
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.CancellationTokenProvider">
<summary>
取消令牌
</summary>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.#ctor(Microsoft.Extensions.DependencyInjection.IServiceScopeFactory,Microsoft.Extensions.Options.IOptions{Volo.Abp.EventBus.Distributed.AbpDistributedEventBusOptions},DotNetCore.CAP.ICapPublisher,Volo.Abp.Users.ICurrentUser,Volo.Abp.Clients.ICurrentClient,Volo.Abp.MultiTenancy.ICurrentTenant,Volo.Abp.Json.IJsonSerializer,Volo.Abp.Uow.IUnitOfWorkManager,Volo.Abp.Guids.IGuidGenerator,Volo.Abp.Timing.IClock,Volo.Abp.Threading.ICancellationTokenProvider,LINGYUN.Abp.EventBus.CAP.ICustomDistributedEventSubscriber,Volo.Abp.EventBus.IEventHandlerInvoker,Volo.Abp.EventBus.Local.ILocalEventBus,Volo.Abp.Tracing.ICorrelationIdProvider)">
<summary>
constructor
</summary>
<param name="serviceScopeFactory"></param>
<param name="distributedEventBusOptions"></param>
<param name="capPublisher"></param>
<param name="currentUser"></param>
<param name="currentClient"></param>
<param name="currentTenant"></param>
<param name="jsonSerializer"></param>
<param name="unitOfWorkManager"></param>
<param name="cancellationTokenProvider"></param>
<param name="guidGenerator"></param>
<param name="clock"></param>
<param name="customDistributedEventSubscriber"></param>
<param name="eventHandlerInvoker"></param>
<param name="localEventBus"></param>
<param name="correlationIdProvider"></param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Subscribe(System.Type,Volo.Abp.EventBus.IEventHandlerFactory)">
<summary>
订阅事件
</summary>
<param name="eventType"></param>
<param name="factory"></param>
<returns></returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe``1(System.Func{``0,System.Threading.Tasks.Task})">
<summary>
退订事件
</summary>
<typeparam name="TEvent">事件类型</typeparam>
<param name="action"></param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe(System.Type,Volo.Abp.EventBus.IEventHandler)">
<summary>
退订事件
</summary>
<param name="eventType">事件类型</param>
<param name="handler">事件处理器</param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe(System.Type,Volo.Abp.EventBus.IEventHandlerFactory)">
<summary>
退订事件
</summary>
<param name="eventType">事件类型</param>
<param name="factory">事件处理器工厂</param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.UnsubscribeAll(System.Type)">
<summary>
退订所有事件
</summary>
<param name="eventType">事件类型</param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.PublishToEventBusAsync(System.Type,System.Object)">
<summary>
发布事件
</summary>
<param name="eventType">事件类型</param>
<param name="eventData">事件数据对象</param>
<returns></returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.GetHandlerFactories(System.Type)">
<summary>
获取事件处理器工厂列表
</summary>
<param name="eventType"></param>
<returns></returns>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.ICustomDistributedEventSubscriber">
<summary>
自定义事件订阅者
</summary>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.ICustomDistributedEventSubscriber.Subscribe(System.Type,Volo.Abp.EventBus.IEventHandlerFactory)">
<summary>
订阅事件
</summary>
<param name="eventType"></param>
<param name="factory"></param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.ICustomDistributedEventSubscriber.UnSubscribe(System.Type,Volo.Abp.EventBus.IEventHandlerFactory)">
<summary>
取消订阅
</summary>
<param name="eventType"></param>
<param name="factory"></param>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.Internal.ObjectMethodExecutor.Execute(System.Object,System.Object[])">
<summary>
Executes the configured method on <paramref name="target" />. This can be used whether or not
the configured method is asynchronous.
</summary>
<remarks>
Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than
ExecuteAsync if you know at compile time what the return type is, because then you can directly
"await" that value (via a cast), and then the generated code will be able to reference the
resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated
code will have to treat the resulting awaitable as a boxed object, because it doesn't know at
compile time what type it would be.
</remarks>
<param name="target">The object whose method is to be executed.</param>
<param name="parameters">Parameters to pass to the method.</param>
<returns>The method return value.</returns>
</member>
<member name="M:LINGYUN.Abp.EventBus.CAP.Internal.ObjectMethodExecutor.ExecuteAsync(System.Object,System.Object[])">
<summary>
Executes the configured method on <paramref name="target" />. This can only be used if the configured
method is asynchronous.
</summary>
<remarks>
If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync,
which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations
as compared with using Execute and then using "await" on the result value typecasted to the known
awaitable type. The possible extra heap allocations are for:
1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally
it's a reference type, and you normally create a new instance per call).
2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance
of it, and if it is, it will have to be boxed so the calling code can reference it as an object).
3. The async result value, if it's a value type (it has to be boxed as an object, since the calling
code doesn't know what type it's going to be).
</remarks>
<param name="target">The object whose method is to be executed.</param>
<param name="parameters">Parameters to pass to the method.</param>
<returns>An object that you can "await" to get the method return value.</returns>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.Internal.ObjectMethodExecutorAwaitable">
<summary>
Provides a common awaitable structure that <see cref="M:LINGYUN.Abp.EventBus.CAP.Internal.ObjectMethodExecutor.ExecuteAsync(System.Object,System.Object[])" /> can
return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an
application-defined custom awaitable.
</summary>
</member>
<member name="T:LINGYUN.Abp.EventBus.CAP.Internal.ObjectMethodExecutorFSharpSupport">
<summary>
Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying
an <see cref="T:System.Linq.Expressions.Expression" /> for mapping instances of that type to a C# awaitable.
</summary>
<remarks>
The main design goal here is to avoid taking a compile-time dependency on
FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references
to FSharp types have to be constructed dynamically at runtime.
</remarks>
</member>
<member name="T:Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions">
<summary>
CAP ServiceCollectionExtensions
</summary>
</member>
<member name="M:Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions.AddCAPEventBus(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{DotNetCore.CAP.CapOptions})">
<summary>
Adds and configures the consistence services for the consistency.
</summary>
<param name="services"></param>
<param name="capAction"></param>
<returns></returns>
</member>
</members>
</doc>

84
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs

@ -20,9 +20,12 @@ using Volo.Abp.Sms;
namespace LINGYUN.Abp.Sms.Aliyun;
[Dependency(ServiceLifetime.Singleton)]
[ExposeServices(typeof(ISmsSender), typeof(AliyunSmsSender))]
[ExposeServices(
typeof(ISmsSender),
typeof(IAliyunSmsVerifyCodeSender),
typeof(AliyunSmsSender))]
[RequiresFeature(AliyunFeatureNames.Sms.Enable)]
public class AliyunSmsSender : ISmsSender
public class AliyunSmsSender : ISmsSender, IAliyunSmsVerifyCodeSender
{
protected IJsonSerializer JsonSerializer { get; }
protected ISettingProvider SettingProvider { get; }
@ -48,6 +51,21 @@ public class AliyunSmsSender : ISmsSender
AliyunFeatureNames.Sms.DefaultSendLimitInterval)]
public async virtual Task SendAsync(SmsMessage smsMessage)
{
if (smsMessage.Properties.ContainsKey("SmsVerifyCode") &&
smsMessage.Properties.TryGetValue("code", out var code))
{
smsMessage.Properties.TryGetValue("SignName", out var signName);
smsMessage.Properties.TryGetValue("TemplateCode", out var templateCode);
// 调用短信验证码服务
await SendAsync(
new SmsVerifyCodeMessage(
smsMessage.PhoneNumber,
new SmsVerifyCodeMessageParam(code.ToString(), "5"),
signName?.ToString(),
templateCode?.ToString()));
return;
}
var domain = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Sms.Domain);
var action = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Sms.ActionName);
var version = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Sms.Version);
@ -56,7 +74,7 @@ public class AliyunSmsSender : ISmsSender
Check.NotNullOrWhiteSpace(action, AliyunSettingNames.Sms.ActionName);
Check.NotNullOrWhiteSpace(version, AliyunSettingNames.Sms.Version);
CommonRequest request = new CommonRequest
var request = new CommonRequest
{
Method = MethodType.POST,
Domain = domain,
@ -72,7 +90,7 @@ public class AliyunSmsSender : ISmsSender
try
{
var client = await AcsClientFactory.CreateAsync();
CommonResponse response = client.GetCommonResponse(request);
var response = client.GetCommonResponse(request);
var responseContent = Encoding.Default.GetString(response.HttpResponse.Content);
var aliyunResponse = JsonSerializer.Deserialize<AliyunSmsResponse>(responseContent);
if (!aliyunResponse.IsSuccess())
@ -94,6 +112,64 @@ public class AliyunSmsSender : ISmsSender
}
}
[RequiresLimitFeature(
AliyunFeatureNames.Sms.SendLimit,
AliyunFeatureNames.Sms.SendLimitInterval,
LimitPolicy.Month,
AliyunFeatureNames.Sms.DefaultSendLimit,
AliyunFeatureNames.Sms.DefaultSendLimitInterval)]
public async virtual Task SendAsync(SmsVerifyCodeMessage message)
{
var version = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Sms.Version);
var domain = await SettingProvider.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.Domain);
var signName = message.SignName ??
await SettingProvider.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName);
var templateCode = message.TemplateCode ??
await SettingProvider.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode);
Check.NotNullOrWhiteSpace(version, AliyunSettingNames.Sms.Version);
Check.NotNullOrWhiteSpace(domain, AliyunSettingNames.SmsVerifyCode.Domain);
Check.NotNullOrWhiteSpace(signName, AliyunSettingNames.SmsVerifyCode.DefaultSignName);
Check.NotNullOrWhiteSpace(templateCode, AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode);
var request = new CommonRequest
{
Domain = domain,
Version = version,
Product = "Dypnsapi",
Method = MethodType.POST,
Action = "SendSmsVerifyCode",
};
request.AddBodyParameters("PhoneNumber", message.PhoneNumber);
request.AddBodyParameters("SignName", signName);
request.AddBodyParameters("TemplateCode", templateCode);
request.AddBodyParameters("TemplateParam", JsonSerializer.Serialize(message.TemplateParam));
try
{
var client = await AcsClientFactory.CreateAsync();
var response = client.GetCommonResponse(request);
var responseContent = Encoding.Default.GetString(response.HttpResponse.Content);
var aliyunResponse = JsonSerializer.Deserialize<AliyunSmsVerifyCodeResponse>(responseContent);
if (!aliyunResponse.Success)
{
if (await SettingProvider.IsTrueAsync(AliyunSettingNames.Sms.VisableErrorToClient))
{
throw new UserFriendlyException(aliyunResponse.Code, aliyunResponse.Message);
}
throw new AliyunSmsException(aliyunResponse.Code, $"Text message sending failed, code:{aliyunResponse.Code}, message:{aliyunResponse.Message}!");
}
}
catch (ServerException se)
{
throw new AliyunSmsException(se.ErrorCode, $"Sending text messages to aliyun server is abnormal,type: {se.ErrorType}, error: {se.ErrorMessage}");
}
catch (ClientException ce)
{
throw new AliyunSmsException(ce.ErrorCode, $"A client exception occurred in sending SMS messages,type: {ce.ErrorType}, error: {ce.ErrorMessage}");
}
}
private async Task TryAddTemplateCodeAsync(CommonRequest request, SmsMessage smsMessage)
{
if (smsMessage.Properties.TryGetValue("TemplateCode", out object template) && template != null)

40
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs

@ -0,0 +1,40 @@
namespace LINGYUN.Abp.Sms.Aliyun;
public class AliyunSmsVerifyCodeResponse
{
/// <summary>
/// 请求状态码, OK代表请求成功
/// </summary>
public string Code { get; set; }
/// <summary>
/// 状态码的描述
/// </summary>
public string Message { get; set; }
/// <summary>
/// 请求是否成功
/// </summary>
public bool Success { get; set; }
/// <summary>
/// 请求结果数据
/// </summary>
public AliyunSmsVerifyCodeModel Model { get; set; }
}
public class AliyunSmsVerifyCodeModel
{
/// <summary>
/// 请求Id
/// </summary>
public string RequestId { get; set; }
/// <summary>
/// 业务Id
/// </summary>
public string BizId { get; set; }
/// <summary>
/// 外部流水号
/// </summary>
public string OutId { get; set; }
/// <summary>
/// 验证码, 仅当使用阿里云短信验证服务生成验证码时携带
/// </summary>
public string VerifyCode { get; set; }
}

15
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs

@ -0,0 +1,15 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.Sms.Aliyun;
/// <summary>
/// 阿里云发送短信验证码接口
/// </summary>
public interface IAliyunSmsVerifyCodeSender
{
/// <summary>
/// 发送短信验证码
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
Task SendAsync(SmsVerifyCodeMessage message);
}

20
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/en.json

@ -1,6 +1,26 @@
{
"culture": "en",
"texts": {
"DisplayName:Aliyun.Sms": "Sms",
"Description:Aliyun.Sms": "Sms",
"DisplayName:Aliyun.SmsVerifyCode": "Sms Verify Code",
"Description:Aliyun.SmsVerifyCode": "Sms Verify Code",
"DisplayName:ActionName": "Action Name",
"Description:ActionName": "Action Name",
"DisplayName:DefaultSignName": "Default Sign Name",
"Description:DefaultSignName": "Default Sign Name",
"DisplayName:DefaultTemplateCode": "Default Template Code",
"Description:DefaultTemplateCode": "Default Template Code",
"DisplayName:DefaultPhoneNumber": "Default Phone Number",
"Description:DefaultPhoneNumber": "Default Phone Number",
"DisplayName:SmsDomain": "Domain",
"Description:SmsDomain": "Domain",
"DisplayName:SmsVerifyCodeDomain": "Domain",
"Description:SmsVerifyCodeDomain": "Sms Verify Code Domain",
"DisplayName:Version": "Version",
"Description:Version": "Version",
"DisplayName:VisableErrorToClient": "Visable Error To Client",
"Description:VisableErrorToClient": "Visable Error To Client",
"SendMessageFailed": "Text message sending failed:{0}",
"SMS_SIGNATURE_SCENE_ILLEGAL": "Signature scene used by SMS is illegal",
"DENY_IP_RANGE": "Region where source IP address is disabled",

20
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/zh-Hans.json

@ -1,6 +1,26 @@
{
"culture": "zh-Hans",
"texts": {
"DisplayName:Aliyun.Sms": "短信服务",
"Description:Aliyun.Sms": "阿里云短信服务",
"DisplayName:Aliyun.SmsVerifyCode": "短信认证服务",
"Description:Aliyun.SmsVerifyCode": "阿里云短信认证服务",
"DisplayName:ActionName": "发送短信方法",
"Description:ActionName": "发送短信方法名称,详情见阿里云Sms服务",
"DisplayName:DefaultSignName": "默认短信签名",
"Description:DefaultSignName": "当用户未指定短信签名时的默认签名名称",
"DisplayName:DefaultTemplateCode": "默认短信模板号",
"Description:DefaultTemplateCode": "当用户未指定短信签名时的默认短信模板号",
"DisplayName:DefaultPhoneNumber": "默认接收短信手机号",
"Description:DefaultPhoneNumber": "当用户未指定短信接收方时的默认接收手机号码",
"DisplayName:SmsDomain": "阿里云sms服务域名",
"Description:SmsDomain": "阿里云sms服务域名",
"DisplayName:SmsVerifyCodeDomain": "阿里云号码认证服务域名",
"Description:SmsVerifyCodeDomain": "阿里云号码认证服务域名",
"DisplayName:Version": "阿里云sms服务版本号",
"Description:Version": "阿里云sms服务版本号",
"DisplayName:VisableErrorToClient": "发送错误到客户端",
"Description:VisableErrorToClient": "当短信服务发送出现错误时是否发送错误详情到客户端",
"SendMessageFailed": "发送短信错误:{0}",
"SMS_SIGNATURE_SCENE_ILLEGAL": "短信所使用签名场景非法",
"DENY_IP_RANGE": "源IP地址所在的地区被禁用",

88
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs

@ -0,0 +1,88 @@
namespace LINGYUN.Abp.Sms.Aliyun;
public class SmsVerifyCodeMessage
{
/// <summary>
/// 方案名称,如果不填则为“默认方案”。最多不超过 20 个字符。
/// </summary>
public string SchemeName { get; set; }
/// <summary>
/// 号码国家编码。默认为 86,目前也仅支持中国国内号码发送。
/// </summary>
public string CountryCode { get; set; }
/// <summary>
/// 上行短信扩展码。上行短信指发送给通信服务提供商的短信,用于定制某种服务、完成查询,或是办理某种业务等,需要收费,按运营商普通短信资费进行扣费。
/// </summary>
public string SmsUpExtendCode { get; set; }
/// <summary>
/// 外部流水号。
/// </summary>
public string OutId { get; set; }
/// <summary>
/// 验证码长度支持 4~8 位长度,默认是 4 位。
/// </summary>
public long? CodeLength { get; set; }
/// <summary>
/// 验证码有效时长,单位秒,默认为 300 秒。
/// </summary>
public long? ValidTime { get; set; }
/// <summary>
/// 核验规则,当有效时间内对同场景内的同号码重复发送验证码时,旧验证码如何处理。
/// </summary>
/// <remarks>
/// 1 - 覆盖处理(默认),即旧验证码会失效掉。<br />
/// 2 - 保留,即多个验证码都是在有效期内都可以校验通过。
/// </remarks>
public long? DuplicatePolicy { get; set; }
/// <summary>
/// 时间间隔,单位:秒。即多久间隔可以发送一次验证码,用于频控,默认 60 秒。
/// </summary>
public long? Interval { get; set; }
/// <summary>
/// 生成的验证码类型。当参数 TemplateParam 传入占位符时,此参数必填,将由系统根据指定的规则生成验证码。
/// </summary>
/// <remarks>
/// 1 - 纯数字(默认)。
/// 2 - 纯大写字母。
/// 3 - 纯小写字母。
/// 4 - 大小字母混合。
/// 5 - 数字+大写字母混合。
/// 6 - 数字+小写字母混合。
/// 7 - 数字+大小写字母混合。
/// </remarks>
public long? CodeType { get; set; }
/// <summary>
/// 是否返回验证码。
/// </summary>
public bool? ReturnVerifyCode { get; set; }
/// <summary>
/// 是否自动替换签名重试(默认开启。
/// </summary>
public bool? AutoRetry { get; set; }
/// <summary>
/// 短信接收方手机号。
/// </summary>
public string PhoneNumber { get; }
/// <summary>
/// 签名名称。暂不支持使用自定义签名,请使用系统赠送的签名。
/// </summary>
public string SignName { get; }
/// <summary>
/// 短信模板 CODE。参数SignName选择赠送签名时,必须搭配赠送模板下发短信。您可在赠送模板配置页面选择适用您业务场景的模板。
/// </summary>
public string TemplateCode { get; }
/// <summary>
/// 短信模板参数。
/// </summary>
public SmsVerifyCodeMessageParam TemplateParam { get; }
public SmsVerifyCodeMessage(
string phoneNumber,
SmsVerifyCodeMessageParam templateParam,
string signName = null,
string templateCode = null)
{
PhoneNumber = phoneNumber;
TemplateParam = templateParam;
SignName = signName;
TemplateCode = templateCode;
}
}

11
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs

@ -0,0 +1,11 @@
namespace LINGYUN.Abp.Sms.Aliyun;
public class SmsVerifyCodeMessageParam
{
public string Code { get; }
public string Min { get; }
public SmsVerifyCodeMessageParam(string code, string min = "5")
{
Code = code;
Min = min;
}
}

8
aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.APM/LINGYUNG/Abp/Telemetry/APM/AbpTelemetryAPMModule.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Telemetry.APM;
@ -7,6 +8,13 @@ public class AbpTelemetryAPMModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var isAPMEnabled = configuration["APM:IsEnabled"];
if (isAPMEnabled.IsNullOrWhiteSpace() || "false".Equals(isAPMEnabled.ToLower()))
{
return;
}
context.Services.AddAllElasticApm();
}
}

30
aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.OpenTelemetry/LINGYUN/Abp/Telemetry/OpenTelemetry/AbpTelemetryOpenTelemetryModule.cs

@ -1,6 +1,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
@ -38,7 +39,8 @@ public class AbpTelemetryOpenTelemetryModule : AbpModule
var openTelmetrySetup = context.Services.GetPreConfigureActions<OpenTelemetryBuilder>();
if (!configuration.GetValue("OpenTelemetry:IsEnabled", false))
var isOpenTelmetryEnabled = configuration["OpenTelemetry:IsEnabled"];
if (isOpenTelmetryEnabled.IsNullOrWhiteSpace() || "false".Equals(isOpenTelmetryEnabled.ToLower()))
{
return;
}
@ -50,6 +52,12 @@ public class AbpTelemetryOpenTelemetryModule : AbpModule
{
applicationName = context.Services.GetApplicationName();
}
if (applicationName.IsNullOrWhiteSpace())
{
return;
}
var openTelmetryBuilder = context.Services
.AddOpenTelemetry()
.ConfigureResource(resource =>
@ -64,6 +72,10 @@ public class AbpTelemetryOpenTelemetryModule : AbpModule
.WithMetrics(metrics =>
{
ConfigureMetrics(metrics, configuration);
})
.WithLogging(logging =>
{
ConfigureLogging(logging, configuration);
});
openTelmetrySetup.Configure(openTelmetryBuilder);
@ -157,4 +169,20 @@ public class AbpTelemetryOpenTelemetryModule : AbpModule
});
}
}
private static void ConfigureLogging(LoggerProviderBuilder logging, IConfiguration configuration)
{
if (configuration.GetValue("OpenTelemetry:Otlp:IsEnabled", false))
{
logging.AddOtlpExporter(otlpOptions =>
{
var otlpEndPoint = configuration["OpenTelemetry:Otlp:Endpoint"];
Check.NotNullOrWhiteSpace(otlpEndPoint, nameof(otlpEndPoint));
otlpOptions.Headers = configuration["OpenTelemetry:Otlp:Headers"];
otlpOptions.Endpoint = new Uri(otlpEndPoint.EnsureEndsWith('/') + "v1/logs");
otlpOptions.Protocol = configuration.GetValue("OpenTelemetry:Otlp:Protocol", otlpOptions.Protocol);
});
}
}
}

3
aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

21
aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN.Abp.AspNetCore.MultiTenancy.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AssemblyName>LINGYUN.Abp.AspNetCore.MultiTenancy</AssemblyName>
<PackageId>LINGYUN.Abp.AspNetCore.MultiTenancy</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<OutputType>Library</OutputType>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" />
</ItemGroup>
</Project>

10
aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs

@ -0,0 +1,10 @@
using Volo.Abp.Modularity;
using VoloAbpAspNetCoreMultiTenancyModule = Volo.Abp.AspNetCore.MultiTenancy.AbpAspNetCoreMultiTenancyModule;
namespace LINGYUN.Abp.AspNetCore.MultiTenancy;
[DependsOn(typeof(VoloAbpAspNetCoreMultiTenancyModule))]
public class AbpAspNetCoreMultiTenancyModule : AbpModule
{
}

13
aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyResolveOptions.cs

@ -0,0 +1,13 @@
namespace LINGYUN.Abp.AspNetCore.MultiTenancy;
public class AbpAspNetCoreMultiTenancyResolveOptions
{
/// <summary>
/// 仅解析域名中的租户, 默认: true
/// </summary>
public bool OnlyResolveDomain { get; set; }
public AbpAspNetCoreMultiTenancyResolveOptions()
{
OnlyResolveDomain = true;
}
}

15
aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpMultiTenancyOptionsExtensions.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.AspNetCore.MultiTenancy;
public static class AbpMultiTenancyOptionsExtensions
{
public static void AddOnlyDomainTenantResolver(this AbpTenantResolveOptions options, string domainFormat)
{
options.TenantResolvers.InsertAfter(
r => r is CurrentUserTenantResolveContributor,
new OnlyDomainTenantResolveContributor(domainFormat)
);
}
}

52
aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/OnlyDomainTenantResolveContributor.cs

@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Net;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Text.Formatting;
namespace LINGYUN.Abp.AspNetCore.MultiTenancy;
public class OnlyDomainTenantResolveContributor : HttpTenantResolveContributorBase
{
public const string ContributorName = "Domain";
public override string Name => ContributorName;
private static readonly string[] ProtocolPrefixes = { "http://", "https://" };
private readonly string _domainFormat;
public OnlyDomainTenantResolveContributor(string domainFormat)
{
_domainFormat = domainFormat.RemovePreFix(ProtocolPrefixes);
}
protected override Task<string?> GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext)
{
if (!httpContext.Request.Host.HasValue)
{
return Task.FromResult<string?>(null);
}
var options = httpContext.RequestServices.GetRequiredService<IOptions<AbpAspNetCoreMultiTenancyResolveOptions>>();
if (options.Value.OnlyResolveDomain)
{
// 仅仅解析域名, 如果请求的是IP地址, 则不使用这个解析贡献者
if (IPAddress.TryParse(httpContext.Request.Host.Host, out var _))
{
return Task.FromResult<string?>(null);
}
}
var hostName = httpContext.Request.Host.Value.RemovePreFix(ProtocolPrefixes);
var extractResult = FormattedStringValueExtracter.Extract(hostName, _domainFormat, ignoreCase: true);
context.Handled = true;
return Task.FromResult(extractResult.IsMatch ? extractResult.Matches[0].Value : null);
}
}

12
aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json

@ -0,0 +1,12 @@
{
"profiles": {
"LINGYUN.Abp.AspNetCore.MultiTenancy": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:61811;http://localhost:61812"
}
}
}

36
aspnet-core/framework/wechat/LINGYUN.Abp.Identity.WeChat.Work/LINGYUN/Abp/Identity/WeChat/Work/AbpWeChatWorkClaimsPrincipalContributor.cs

@ -0,0 +1,36 @@
using LINGYUN.Abp.WeChat.Work.Authorize;
using LINGYUN.Abp.WeChat.Work.Security.Claims;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace LINGYUN.Abp.Identity.WeChat.Work;
public class AbpWeChatWorkClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
{
public async virtual Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.First();
if (claimsIdentity.HasClaim(x => x.Type == AbpWeChatWorkClaimTypes.UserId))
{
return;
}
var userId = claimsIdentity.FindUserId();
if (userId.HasValue)
{
var userClaimProvider = context.ServiceProvider.GetService<IWeChatWorkUserClaimProvider>();
var weChatWorkUserId = await userClaimProvider?.FindUserIdentifierAsync(userId.Value);
if (!weChatWorkUserId.IsNullOrWhiteSpace())
{
claimsIdentity.AddOrReplace(new Claim(AbpWeChatWorkClaimTypes.UserId, weChatWorkUserId));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
}
}
}
}

12
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs

@ -1,12 +0,0 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.WeChat.Common.Messages;
public class AbpWeChatMessageResolveOptions
{
public List<IMessageResolveContributor> MessageResolvers { get; }
public AbpWeChatMessageResolveOptions()
{
MessageResolvers = new List<IMessageResolveContributor>();
}
}

1
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs

@ -1,6 +1,7 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Common.Messages;
public interface IMessageResolveContributor
{
string Name { get; }

7
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs

@ -1,7 +0,0 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Common.Messages;
public interface IMessageResolver
{
Task<MessageResolveResult> ResolveMessageAsync(MessageResolveData messageData);
}

1
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs

@ -1,6 +1,7 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Common.Messages;
public abstract class MessageResolveContributorBase : IMessageResolveContributor
{
public abstract string Name { get; }

34
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolver.cs → aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolverBase.cs

@ -1,7 +1,6 @@
using LINGYUN.Abp.WeChat.Common.Crypto;
using LINGYUN.Abp.WeChat.Common.Crypto.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Threading.Tasks;
@ -9,29 +8,25 @@ using System.Xml.Linq;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.WeChat.Common.Messages;
public class MessageResolver : IMessageResolver, ITransientDependency
public abstract class MessageResolverBase : ITransientDependency
{
private readonly IWeChatCryptoService _cryptoService;
private readonly IServiceProvider _serviceProvider;
private readonly AbpWeChatMessageResolveOptions _options;
public MessageResolver(
IOptions<AbpWeChatMessageResolveOptions> options,
protected MessageResolverBase(
IWeChatCryptoService cryptoService,
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_cryptoService = cryptoService;
_options = options.Value;
}
/// <summary>
/// 解析微信服务器推送消息/事件
/// </summary>
/// <param name="messageData"></param>
/// <returns></returns>
public async virtual Task<MessageResolveResult> ResolveMessageAsync(MessageResolveData messageData)
public async virtual Task<MessageResolveResult> ResolveAsync(MessageResolveData messageData)
{
var result = new MessageResolveResult(messageData.Data);
using (var serviceScope = _serviceProvider.CreateScope())
{
/*
@ -65,24 +60,17 @@ public class MessageResolver : IMessageResolver, ITransientDependency
// 经过解密函数得到如上真实数据
var decryptMessage = _cryptoService.Decrypt(cryptoDecryptData);
xmlDocument = XDocument.Parse(decryptMessage);
result.Input = decryptMessage;
}
var context = new MessageResolveContext(result.Input, xmlDocument, serviceScope.ServiceProvider);
var context = new MessageResolveContext(messageData.Data, xmlDocument, serviceScope.ServiceProvider);
foreach (var messageResolver in _options.MessageResolvers)
{
await messageResolver.ResolveAsync(context);
result.AppliedResolvers.Add(messageResolver.Name);
if (context.HasResolvedMessage())
{
result.Message = context.Message;
break;
}
}
return await ResolveMessageAsync(context);
}
return result;
}
/// <summary>
/// 实现具体的解析微信服务器推送消息/事件方法
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
protected abstract Task<MessageResolveResult> ResolveMessageAsync(MessageResolveContext context);
}

7
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs

@ -1,6 +1,7 @@
using LINGYUN.Abp.WeChat.Common.Crypto;
using LINGYUN.Abp.WeChat.Common.Crypto.Models;
using LINGYUN.Abp.WeChat.Common.Messages;
using LINGYUN.Abp.WeChat.Official.Messages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using Volo.Abp;
@ -13,9 +14,9 @@ public class WeChatMessageAppService : ApplicationService, IWeChatMessageAppServ
private readonly IWeChatCryptoService _cryptoService;
private readonly AbpWeChatOfficialOptionsFactory _optionsFactory;
private readonly IDistributedEventBus _distributedEventBus;
private readonly IMessageResolver _messageResolver;
private readonly IWeChatOfficialMessageResolver _messageResolver;
public WeChatMessageAppService(
IMessageResolver messageResolver,
IWeChatOfficialMessageResolver messageResolver,
IWeChatCryptoService cryptoService,
IDistributedEventBus distributedEventBus,
AbpWeChatOfficialOptionsFactory optionsFactory)
@ -67,7 +68,7 @@ public class WeChatMessageAppService : ApplicationService, IWeChatMessageAppServ
input.Nonce,
input.Data);
var result = await _messageResolver.ResolveMessageAsync(messageData);
var result = await _messageResolver.ResolveAsync(messageData);
if (result.Message == null)
{
Logger.LogWarning(input.Data);

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs

@ -40,10 +40,7 @@ public class AbpWeChatOfficialModule : AbpModule
options.MapMessage("shortvideo", context => context.GetWeChatMessage<VideoMessage>());
options.MapMessage("location", context => context.GetWeChatMessage<GeoLocationMessage>());
options.MapMessage("link", context => context.GetWeChatMessage<LinkMessage>());
});
Configure<AbpWeChatMessageResolveOptions>(options =>
{
// 事件处理器
options.MessageResolvers.AddIfNotContains(new WeChatOfficialEventResolveContributor());
// 消息处理器

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs

@ -5,10 +5,12 @@ using System.Collections.Generic;
namespace LINGYUN.Abp.WeChat.Official.Messages;
public class AbpWeChatOfficialMessageResolveOptions
{
public List<IMessageResolveContributor> MessageResolvers { get; }
public IDictionary<string, Func<IMessageResolveContext, WeChatEventMessage>> EventMaps { get; }
public IDictionary<string, Func<IMessageResolveContext, WeChatGeneralMessage>> MessageMaps { get; }
public AbpWeChatOfficialMessageResolveOptions()
{
MessageResolvers = new List<IMessageResolveContributor>();
EventMaps = new Dictionary<string, Func<IMessageResolveContext, WeChatEventMessage>>();
MessageMaps = new Dictionary<string, Func<IMessageResolveContext, WeChatGeneralMessage>>();
}

16
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/IWeChatOfficialMessageResolver.cs

@ -0,0 +1,16 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Official.Messages;
/// <summary>
/// 微信公众号消息解析器
/// </summary>
public interface IWeChatOfficialMessageResolver
{
/// <summary>
/// 解析微信公众号消息
/// </summary>
/// <param name="messageData">公众号消息</param>
/// <returns></returns>
Task<MessageResolveResult> ResolveAsync(MessageResolveData messageData);
}

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs

@ -3,7 +3,7 @@ using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WeChat.Official.Messages;
[GenericEventName(Prefix = "wechat.official.events")]
[GenericEventName(Prefix = "wechat.official.events.")]
public class WeChatOfficialEventMessageEto<TEvent> : WeChatMessageEto
where TEvent : WeChatEventMessage
{

5
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs

@ -1,6 +1,4 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
@ -12,9 +10,8 @@ public class WeChatOfficialEventResolveContributor : WeChatOfficialMessageResolv
{
public override string Name => "WeChat.Official.Event";
protected override Task ResolveWeChatMessageAsync(IMessageResolveContext context)
protected override Task ResolveMessageAsync(IMessageResolveContext context, AbpWeChatOfficialMessageResolveOptions options)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpWeChatOfficialMessageResolveOptions>>().Value;
var messageType = context.GetMessageData("MsgType");
var eventName = context.GetMessageData("Event");
if ("event".Equals(messageType, StringComparison.InvariantCultureIgnoreCase) &&

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessageEto.cs

@ -3,7 +3,7 @@ using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WeChat.Official.Messages;
[GenericEventName(Prefix = "wechat.official.messages")]
[GenericEventName(Prefix = "wechat.official.messages.")]
public class WeChatOfficialGeneralMessageEto<TMessage> : WeChatMessageEto
where TMessage : WeChatOfficialGeneralMessage
{

5
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributor.cs

@ -1,6 +1,4 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Official.Messages;
@ -11,9 +9,8 @@ public class WeChatOfficialMessageResolveContributor : WeChatOfficialMessageReso
{
public override string Name => "WeChat.Official.Message";
protected override Task ResolveWeChatMessageAsync(IMessageResolveContext context)
protected override Task ResolveMessageAsync(IMessageResolveContext context, AbpWeChatOfficialMessageResolveOptions options)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpWeChatOfficialMessageResolveOptions>>().Value;
var messageType = context.GetMessageData("MsgType");
if (options.MessageMaps.TryGetValue(messageType, out var messageFactory))
{

11
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributorBase.cs

@ -1,4 +1,6 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Official.Messages;
@ -6,13 +8,10 @@ public abstract class WeChatOfficialMessageResolveContributorBase : MessageResol
{
public override Task ResolveAsync(IMessageResolveContext context)
{
if (!context.HasMessageKey("AgentID"))
{
return ResolveWeChatMessageAsync(context);
}
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpWeChatOfficialMessageResolveOptions>>().Value;
return Task.CompletedTask;
return ResolveMessageAsync(context, options);
}
protected abstract Task ResolveWeChatMessageAsync(IMessageResolveContext context);
protected abstract Task ResolveMessageAsync(IMessageResolveContext context, AbpWeChatOfficialMessageResolveOptions options);
}

39
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolver.cs

@ -0,0 +1,39 @@
using LINGYUN.Abp.WeChat.Common.Crypto;
using LINGYUN.Abp.WeChat.Common.Messages;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Official.Messages;
public class WeChatOfficialMessageResolver : MessageResolverBase, IWeChatOfficialMessageResolver
{
private readonly AbpWeChatOfficialMessageResolveOptions _options;
public WeChatOfficialMessageResolver(
IWeChatCryptoService cryptoService,
IServiceProvider serviceProvider,
IOptions<AbpWeChatOfficialMessageResolveOptions> options)
: base(cryptoService, serviceProvider)
{
_options = options.Value;
}
protected async override Task<MessageResolveResult> ResolveMessageAsync(MessageResolveContext context)
{
var result = new MessageResolveResult(context.Origin);
foreach (var messageResolver in _options.MessageResolvers)
{
await messageResolver.ResolveAsync(context);
result.AppliedResolvers.Add(messageResolver.Name);
if (context.HasResolvedMessage())
{
result.Message = context.Message;
break;
}
}
return result;
}
}

7
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs

@ -1,6 +1,7 @@
using LINGYUN.Abp.WeChat.Common.Crypto;
using LINGYUN.Abp.WeChat.Common.Crypto.Models;
using LINGYUN.Abp.WeChat.Common.Messages;
using LINGYUN.Abp.WeChat.Work.Common.Messages;
using LINGYUN.Abp.WeChat.Work.Settings;
using Microsoft.Extensions.Logging;
using System.Linq;
@ -14,9 +15,9 @@ public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessag
{
private readonly IWeChatCryptoService _cryptoService;
private readonly IDistributedEventBus _distributedEventBus;
private readonly IMessageResolver _messageResolver;
private readonly IWeChatWorkMessageResolver _messageResolver;
public WeChatWorkMessageAppService(
IMessageResolver messageResolver,
IWeChatWorkMessageResolver messageResolver,
IWeChatCryptoService cryptoService,
IDistributedEventBus distributedEventBus)
{
@ -82,7 +83,7 @@ public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessag
input.Nonce,
input.Data);
var result = await _messageResolver.ResolveMessageAsync(messageData);
var result = await _messageResolver.ResolveAsync(messageData);
if (result.Message == null)
{
Logger.LogWarning(input.Data);

1
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj

@ -10,6 +10,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<RootNamespace />
</PropertyGroup>

6
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/AbpWeChatWorkCommonModule.cs

@ -29,6 +29,7 @@ public class AbpWeChatWorkCommonModule : AbpModule
options.MapEvent("location_select", context => context.GetWeChatMessage<GeoLocationSelectPushEevent>());
options.MapEvent("batch_job_result", context => context.GetWeChatMessage<BatchJobResultEvent>());
options.MapEvent("open_approval_change", context => context.GetWeChatMessage<ApprovalStatusChangeEvent>());
options.MapEvent("sys_approval_change", context => context.GetWeChatMessage<SysApprovalStatusChangeEvent>());
options.MapEvent("share_agent_change", context => context.GetWeChatMessage<ShareAgentChangeEvent>());
options.MapEvent("share_chain_change", context => context.GetWeChatMessage<ShareChainChangeEvent>());
options.MapEvent("template_card_event", context => context.GetWeChatMessage<TemplateCardPushEvent>());
@ -56,6 +57,8 @@ public class AbpWeChatWorkCommonModule : AbpModule
_ => throw new AbpWeChatException($"Contact change event {changeType} is not mounted!"),
};
});
options.MapEvent("book_meeting_room", context => context.GetWeChatMessage<BookMeetingRoomEvent>());
options.MapEvent("cancel_meeting_room", context => context.GetWeChatMessage<CancelMeetingRoomEvent>());
options.MapMessage("text", context => context.GetWeChatMessage<TextMessage>());
options.MapMessage("image", context => context.GetWeChatMessage<PictureMessage>());
@ -64,10 +67,7 @@ public class AbpWeChatWorkCommonModule : AbpModule
options.MapMessage("shortvideo", context => context.GetWeChatMessage<VideoMessage>());
options.MapMessage("location", context => context.GetWeChatMessage<GeoLocationMessage>());
options.MapMessage("link", context => context.GetWeChatMessage<LinkMessage>());
});
Configure<AbpWeChatMessageResolveOptions>(options =>
{
// 事件处理器
options.MessageResolvers.AddIfNotContains(new WeChatWorkEventResolveContributor());
// 消息处理器

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/AbpWeChatWorkMessageResolveOptions.cs

@ -5,10 +5,12 @@ using System.Collections.Generic;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages;
public class AbpWeChatWorkMessageResolveOptions
{
public List<IMessageResolveContributor> MessageResolvers { get; }
public IDictionary<string, Func<IMessageResolveContext, WeChatWorkEventMessage>> EventMaps { get; }
public IDictionary<string, Func<IMessageResolveContext, WeChatWorkGeneralMessage>> MessageMaps { get; }
public AbpWeChatWorkMessageResolveOptions()
{
MessageResolvers = new List<IMessageResolveContributor>();
EventMaps = new Dictionary<string, Func<IMessageResolveContext, WeChatWorkEventMessage>>();
MessageMaps = new Dictionary<string, Func<IMessageResolveContext, WeChatWorkGeneralMessage>>();
}

16
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/IWeChatWorkMessageResolver.cs

@ -0,0 +1,16 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages;
/// <summary>
/// 企业微信消息解析器
/// </summary>
public interface IWeChatWorkMessageResolver
{
/// <summary>
/// 解析企业微信消息
/// </summary>
/// <param name="messageData">企业微信消息</param>
/// <returns></returns>
Task<MessageResolveResult> ResolveAsync(MessageResolveData messageData);
}

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BatchJobResultEvent.cs

@ -4,7 +4,7 @@ using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models;
/// <summary>
/// 进入应用事件
/// 异步任务完成事件
/// </summary>
[EventName("batch_job_result")]
public class BatchJobResultEvent : WeChatWorkEventMessage

30
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BookMeetingRoomEvent.cs

@ -0,0 +1,30 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using System.Xml.Serialization;
using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models;
/// <summary>
/// 会议室预定事件
/// </summary>
/// <remarks>
/// 当用户在企业微信预定会议室时,会触发该事件回调给会议室系统应用。
/// </remarks>
[EventName("book_meeting_room")]
public class BookMeetingRoomEvent : WeChatWorkEventMessage
{
/// <summary>
/// 会议室id
/// </summary>
[XmlElement("MeetingRoomId")]
public int MeetingRoomId { get; set; }
/// <summary>
/// 预定id,可根据该ID查询具体的会议预定情况
/// </summary>
[XmlElement("BookingId")]
public string BookingId { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatWorkEventMessageEto<BookMeetingRoomEvent>(this);
}
}

30
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CancelMeetingRoomEvent.cs

@ -0,0 +1,30 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using System.Xml.Serialization;
using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models;
/// <summary>
/// 会议室取消事件
/// </summary>
/// <remarks>
/// 当用户在企业微信取消预定会议室时,会触发该事件回调给会议室系统应用;如果该会议室由自建应用预定,除了会议室系统应用外,也会回调给对应的自建应用。
/// </remarks>
[EventName("cancel_meeting_room")]
public class CancelMeetingRoomEvent : WeChatWorkEventMessage
{
/// <summary>
/// 会议室id
/// </summary>
[XmlElement("MeetingRoomId")]
public int MeetingRoomId { get; set; }
/// <summary>
/// 预定id,可根据该ID查询具体的会议预定情况
/// </summary>
[XmlElement("BookingId")]
public string BookingId { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatWorkEventMessageEto<CancelMeetingRoomEvent>(this);
}
}

272
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/SysApprovalStatusChangeEvent.cs

@ -0,0 +1,272 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models;
/// <summary>
/// 企业微信“审批应用”审批状态通知事件
/// </summary>
[EventName("sys_approval_change")]
public class SysApprovalStatusChangeEvent : WeChatWorkEventMessage
{
/// <summary>
/// 审批信息
/// </summary>
[XmlElement("ApprovalInfo")]
public SysApprovalInfo ApprovalInfo { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatWorkEventMessageEto<SysApprovalStatusChangeEvent>(this);
}
}
public class SysApprovalInfo
{
/// <summary>
/// 审批编号(字符串类型)
/// </summary>
[XmlElement("SpNoStr")]
public string SpNoStr { get; set; }
/// <summary>
/// 审批申请类型名称(审批模板名称)
/// </summary>
[XmlElement("SpName")]
public string SpName { get; set; }
/// <summary>
/// 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付
/// </summary>
[XmlElement("SpStatus")]
public byte SpStatus { get; set; }
/// <summary>
/// 审批模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。
/// </summary>
[XmlElement("TemplateId")]
public string TemplateId { get; set; }
/// <summary>
/// 审批申请提交时间,Unix时间戳
/// </summary>
[XmlElement("ApplyTime")]
public int ApplyTime { get; set; }
/// <summary>
/// 申请人信息
/// </summary>
[XmlElement("Applyer")]
public SysApprovalApplyer Applyer { get; set; }
/// <summary>
/// 审批流程信息,可能有多个审批节点。
/// </summary>
[XmlElement("SpRecord")]
public List<SysApprovalRecord> SpRecord { get; set; }
/// <summary>
/// 抄送信息,可能有多个抄送节点
/// </summary>
[XmlElement("Notifyer")]
public List<SysApprovalNotifyer> Notifyer { get; set; }
/// <summary>
/// 审批申请备注信息,可能有多个备注节点
/// </summary>
[XmlElement("Comments")]
public List<SysApprovalComment> Comments { get; set; }
/// <summary>
/// 审批流程列表
/// </summary>
[XmlElement("ProcessList")]
public List<SysApprovalProcess> ProcessList { get; set; }
/// <summary>
/// 审批申请状态变化类型:1-提单;2-同意;3-驳回;4-转审;5-催办;6-撤销;8-通过后撤销;10-添加备注;11-回退给指定审批人;12-添加审批人;13-加签并同意; 14-已办理; 15-已转交
/// </summary>
[XmlElement("StatuChangeEvent")]
public byte StatuChangeEvent { get; set; }
/// <summary>
/// 审批编号
/// </summary>
/// <remarks>
/// 局校审批单不返回此字段,其他类型审批单会返回此字段,不推荐使用此字段
/// </remarks>
[XmlElement("SpNo")]
[Obsolete("局校审批单不返回此字段,其他类型审批单会返回此字段,不推荐使用此字段")]
public string SpNo { get; set; }
}
public class SysApprovalApplyer
{
/// <summary>
/// 申请人userid
/// </summary>
[XmlElement("UserId")]
public string UserId { get; set; }
/// <summary>
/// 申请人所在部门pid
/// </summary>
[XmlElement("Party")]
public string Party { get; set; }
}
public class SysApprovalRecord
{
/// <summary>
/// 审批节点状态:1-审批中;2-已同意;3-已驳回;4-已转审
/// </summary>
[XmlElement("SpStatus")]
public byte SpStatus { get; set; }
/// <summary>
/// 节点审批方式:1-或签;2-会签
/// </summary>
[XmlElement("ApproverAttr")]
public byte ApproverAttr { get; set; }
/// <summary>
/// 节点审批方式:1-或签;2-会签
/// </summary>
[XmlElement("Details")]
public List<SysApprovalRecordDetail> Details { get; set; }
}
public class SysApprovalRecordDetail
{
/// <summary>
/// 分支审批人
/// </summary>
[XmlElement("Approver")]
public SysApprovalApplyer Approver { get; set; }
/// <summary>
/// 审批意见字段
/// </summary>
[XmlElement("Speech")]
public string Speech { get; set; }
/// <summary>
/// 分支审批人审批状态:1-审批中;2-已同意;3-已驳回;4-已转审
/// </summary>
[XmlElement("SpStatus")]
public byte SpStatus { get; set; }
/// <summary>
/// 节点分支审批人审批操作时间,0为尚未操作
/// </summary>
[XmlElement("SpTime")]
public int SpTime { get; set; }
/// <summary>
/// 节点分支审批人审批意见附件,赋值为media_id具体使用请参考:文档-获取临时素材
/// </summary>
[XmlElement("Attach")]
public string Attach { get; set; }
}
public class SysApprovalNotifyer
{
/// <summary>
/// 节点抄送人userid
/// </summary>
[XmlElement("UserId")]
public string UserId { get; set; }
}
public class SysApprovalComment
{
/// <summary>
/// 备注人信息
/// </summary>
[XmlElement("CommentUserInfo")]
public SysApprovalCommenter CommentUserInfo { get; set; }
/// <summary>
/// 备注提交时间
/// </summary>
[XmlElement("CommentTime")]
public int CommentTime { get; set; }
/// <summary>
/// 备注文本内容
/// </summary>
[XmlElement("CommentContent")]
public string CommentContent { get; set; }
/// <summary>
/// 备注id
/// </summary>
[XmlElement("CommentId")]
public string CommentId { get; set; }
/// <summary>
/// 备注意见附件,值是附件media_id
/// </summary>
[XmlElement("Attach")]
public string Attach { get; set; }
}
public class SysApprovalCommenter
{
/// <summary>
/// 节点抄送人userid
/// </summary>
[XmlElement("UserId")]
public string UserId { get; set; }
}
public class SysApprovalProcess
{
/// <summary>
/// 流程节点
/// </summary>
[XmlElement("NodeList")]
public List<SysApprovalProcessNode> NodeList { get; set; }
}
public class SysApprovalProcessNode
{
/// <summary>
/// 节点类型 1 审批人 2 抄送人 3办理人
/// </summary>
[XmlElement("NodeType")]
public byte NodeType { get; set; }
/// <summary>
/// 节点状态 1-审批中;2-同意;3-驳回;4-转审;11-退回给指定审批人;12-加签;13-同意并加签;14-办理;15-转交
/// </summary>
[XmlElement("SpStatus")]
public byte SpStatus { get; set; }
/// <summary>
/// 多人办理方式 1-会签;2-或签 3-依次审批
/// </summary>
[XmlElement("ApvRel")]
public byte ApvRel { get; set; }
/// <summary>
/// 子节点列表
/// </summary>
[XmlElement("SubNodeList")]
public List<SysApprovalProcessSubNode> SubNodeList { get; set; }
}
public class SysApprovalProcessSubNode
{
/// <summary>
/// 处理人信息
/// </summary>
[XmlElement("UserInfo")]
public SysApprovalProcesser UserInfo { get; set; }
/// <summary>
/// 审批/办理意见
/// </summary>
[XmlElement("Speech")]
public string Speech { get; set; }
/// <summary>
/// 子节点状态 1-审批中;2-同意;3-驳回;4-转审;11-退回给指定审批人;12-加签;13-同意并加签;14-办理;15-转交
/// </summary>
[XmlElement("SpYj")]
public byte SpYj { get; set; }
/// <summary>
/// 操作时间
/// </summary>
[XmlElement("Sptime")]
public int Sptime { get; set; }
/// <summary>
/// 备注意见附件,值是附件media_id
/// </summary>
[XmlElement("MediaIds")]
public string MediaIds { get; set; }
}
public class SysApprovalProcesser
{
/// <summary>
/// 处理人userid
/// </summary>
[XmlElement("UserId")]
public string UserId { get; set; }
}

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessageEto.cs

@ -3,7 +3,7 @@ using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages;
[GenericEventName(Prefix = "wechat.work.events")]
[GenericEventName(Prefix = "wechat.work.events.")]
public class WeChatWorkEventMessageEto<TEvent> : WeChatMessageEto
where TEvent : WeChatWorkEventMessage
{

7
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventResolveContributor.cs

@ -1,20 +1,17 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages;
/// <summary>
/// 微信公众号事件处理器
/// 企业微信事件处理器
/// </summary>
public class WeChatWorkEventResolveContributor : WeChatWorkMessageResolveContributorBase
{
public override string Name => "WeChat.Work.Event";
protected override Task ResolveWeComMessageAsync(IMessageResolveContext context)
protected override Task ResolveMessageAsync(IMessageResolveContext context, AbpWeChatWorkMessageResolveOptions options)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpWeChatWorkMessageResolveOptions>>().Value;
var messageType = context.GetMessageData("MsgType");
var eventName = context.GetMessageData("Event");
if ("event".Equals(messageType, StringComparison.InvariantCultureIgnoreCase) &&

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessageEto.cs

@ -3,7 +3,7 @@ using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages;
[GenericEventName(Prefix = "wechat.work.messages")]
[GenericEventName(Prefix = "wechat.work.messages.")]
public class WeChatWorkGeneralMessageEto<TMessage> : WeChatMessageEto
where TMessage : WeChatWorkGeneralMessage
{

7
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributor.cs

@ -1,19 +1,16 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages;
/// <summary>
/// 微信公众号消息处理器
/// 企业微信消息处理器
/// </summary>
public class WeChatWorkMessageResolveContributor : WeChatWorkMessageResolveContributorBase
{
public override string Name => "WeChat.Work.Message";
protected override Task ResolveWeComMessageAsync(IMessageResolveContext context)
protected override Task ResolveMessageAsync(IMessageResolveContext context, AbpWeChatWorkMessageResolveOptions options)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpWeChatWorkMessageResolveOptions>>().Value;
var messageType = context.GetMessageData("MsgType");
if (options.MessageMaps.TryGetValue(messageType, out var messageFactory))
{

12
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributorBase.cs

@ -1,4 +1,6 @@
using LINGYUN.Abp.WeChat.Common.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages;
@ -6,14 +8,10 @@ public abstract class WeChatWorkMessageResolveContributorBase : MessageResolveCo
{
public override Task ResolveAsync(IMessageResolveContext context)
{
// 企业微信消息需要企业标识字段
if (context.HasMessageKey("AgentID"))
{
return ResolveWeComMessageAsync(context);
}
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpWeChatWorkMessageResolveOptions>>().Value;
return Task.CompletedTask;
return ResolveMessageAsync(context, options);
}
protected abstract Task ResolveWeComMessageAsync(IMessageResolveContext context);
protected abstract Task ResolveMessageAsync(IMessageResolveContext context, AbpWeChatWorkMessageResolveOptions options);
}

39
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolver.cs

@ -0,0 +1,39 @@
using LINGYUN.Abp.WeChat.Common.Crypto;
using LINGYUN.Abp.WeChat.Common.Messages;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Work.Common.Messages;
public class WeChatWorkMessageResolver : MessageResolverBase, IWeChatWorkMessageResolver
{
private readonly AbpWeChatWorkMessageResolveOptions _options;
public WeChatWorkMessageResolver(
IWeChatCryptoService cryptoService,
IServiceProvider serviceProvider,
IOptions<AbpWeChatWorkMessageResolveOptions> options)
: base(cryptoService, serviceProvider)
{
_options = options.Value;
}
protected async override Task<MessageResolveResult> ResolveMessageAsync(MessageResolveContext context)
{
var result = new MessageResolveResult(context.Origin);
foreach (var messageResolver in _options.MessageResolvers)
{
await messageResolver.ResolveAsync(context);
result.AppliedResolvers.Add(messageResolver.Name);
if (context.HasResolvedMessage())
{
result.Message = context.Message;
break;
}
}
return result;
}
}

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

27
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN.Abp.WeChat.Work.ExternalContact.csproj

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
<AssemblyName>LINGYUN.Abp.WeChat.Work.ExternalContact</AssemblyName>
<PackageId>LINGYUN.Abp.WeChat.Work.ExternalContact</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\WeChat\Work\ExternalContact\Localization\Resources\*.json" />
<EmbeddedResource Include="LINGYUN\Abp\WeChat\Work\ExternalContact\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WeChat.Work\LINGYUN.Abp.WeChat.Work.csproj" />
</ItemGroup>
</Project>

78
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/AbpWeChatWorkExternalContactModule.cs

@ -0,0 +1,78 @@
using LINGYUN.Abp.WeChat.Common;
using LINGYUN.Abp.WeChat.Common.Messages;
using LINGYUN.Abp.WeChat.Work.Common.Messages;
using LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models;
using LINGYUN.Abp.WeChat.Work.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.WeChat.Work.ExternalContact;
[DependsOn(typeof(AbpWeChatWorkModule))]
public class AbpWeChatWorkExternalContactModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpWeChatWorkMessageResolveOptions>(options =>
{
// 企业客户变更事件
options.MapEvent("change_external_contact", context =>
{
var changeType = context.GetMessageData("ChangeType");
return changeType switch
{
"add_external_contact" => context.GetWeChatMessage<ExternalContactCreateEvent>(),
"edit_external_contact" => context.GetWeChatMessage<ExternalContactUpdateEvent>(),
"add_half_external_contact" => context.GetWeChatMessage<ExternalContactCreateHalfEvent>(),
"del_external_contact" => context.GetWeChatMessage<ExternalContactDeleteEvent>(),
"del_follow_user" => context.GetWeChatMessage<ExternalContactDeleteFollowUserEvent>(),
"transfer_fail" => context.GetWeChatMessage<ExternalContactTransferFailEvent>(),
_ => throw new AbpWeChatException($"Contact change event change_external_contact:{changeType} is not mounted!"),
};
});
// 客户群变更事件
options.MapEvent("change_external_chat", context =>
{
var changeType = context.GetMessageData("ChangeType");
switch (changeType)
{
case "create": return context.GetWeChatMessage<ExternalChatCreateEvent>();
case "update":
// 客户群变更事件
var updateDetail = context.GetMessageData("UpdateDetail");
return updateDetail switch
{
"add_member" => context.GetWeChatMessage<ExternalChatAddMemberEvent>(),
"del_member" => context.GetWeChatMessage<ExternalChaDelMemberEvent>(),
"change_owner" => context.GetWeChatMessage<ExternalChatChangeOwnerEvent>(),
"change_name" => context.GetWeChatMessage<ExternalChatChangeNameEvent>(),
"change_notice" => context.GetWeChatMessage<ExternalChatChangeNoticeEvent>(),
_ => throw new AbpWeChatException($"Contact change event change_external_chat:{changeType}:{updateDetail} is not mounted!"),
};
case "dismiss": return context.GetWeChatMessage<ExternalChatDismissEvent>();
default: throw new AbpWeChatException($"Contact change event change_external_chat:{changeType} is not mounted!");
}
});
// 企业客户标签事件
options.MapEvent("change_external_tag", context =>
{
var changeType = context.GetMessageData("ChangeType");
return changeType switch
{
"create" => context.GetWeChatMessage<ExternalTagCreateEvent>(),
"update" => context.GetWeChatMessage<ExternalTagUpdateEvent>(),
"delete" => context.GetWeChatMessage<ExternalTagDeleteEvent>(),
"shuffle" => context.GetWeChatMessage<ExternalTagShuffleEvent>(),
_ => throw new AbpWeChatException($"Contact change event change_external_tag:{changeType} is not mounted!"),
};
});
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<WeChatWorkResource>()
.AddVirtualJson("/LINGYUN/Abp/WeChat/Work/ExternalContact/Localization/Resources");
});
}
}

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

Loading…
Cancel
Save