diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 60742282a..894e61f8e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ on: - "**.csproj" env: - DOTNET_VERSION: "9.0.304" + DOTNET_VERSION: "9.0.307" jobs: build: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9020906b2..a6cd4b815 100644 --- a/.github/workflows/publish.yml +++ b/.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: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b68f94f9a..97e184479 100644 --- a/.github/workflows/release.yml +++ b/.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" diff --git a/Directory.Packages.props b/Directory.Packages.props index aacc3e141..0df58d491 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,8 +3,8 @@ 8.3.5 2.15.2 3.3.5 - 9.3.5 - 9.3.5 + 9.3.6 + 9.3.6 9.0.5 9.0.5 9.0.5 @@ -12,7 +12,7 @@ - + @@ -282,6 +282,7 @@ + @@ -333,4 +334,4 @@ - \ No newline at end of file + diff --git a/README.md b/README.md index dbcc172d0..d1143f3dd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## Build -[](https://github.com/colinin/abp-next-admin/actions/workflows/build.yml) +[](https://github.com/colinin/abp-next-admin/actions/workflows/build.yml) [](https://www.nuget.org/packages/LINGYUN.Abp.Core) ## 部署方案 diff --git a/apps/vben5/apps/app-antd/package.json b/apps/vben5/apps/app-antd/package.json index efbe91733..6b1852596 100644 --- a/apps/vben5/apps/app-antd/package.json +++ b/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:*", diff --git a/apps/vben5/apps/app-antd/src/locales/index.ts b/apps/vben5/apps/app-antd/src/locales/index.ts index 7fe299ca0..434984cfd 100644 --- a/apps/vben5/apps/app-antd/src/locales/index.ts +++ b/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, }; diff --git a/apps/vben5/packages/@abp/account/package.json b/apps/vben5/packages/@abp/account/package.json index 0b92c7213..c742f9666 100644 --- a/apps/vben5/packages/@abp/account/package.json +++ b/apps/vben5/packages/@abp/account/package.json @@ -20,6 +20,7 @@ } }, "dependencies": { + "@abp/components": "workspace:*", "@abp/core": "workspace:*", "@abp/gdpr": "workspace:*", "@abp/identity": "workspace:*", diff --git a/apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue b/apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue index bacd00aea..9f80295fa 100644 --- a/apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue +++ b/apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue @@ -1,7 +1,4 @@ + + + + + + diff --git a/apps/vben5/packages/@abp/components/src/cropper/CropperAvatar.vue b/apps/vben5/packages/@abp/components/src/cropper/CropperAvatar.vue new file mode 100644 index 000000000..c420f8d9b --- /dev/null +++ b/apps/vben5/packages/@abp/components/src/cropper/CropperAvatar.vue @@ -0,0 +1,159 @@ + + + + + + + + + + + + {{ btnText ? btnText : t('cropper.selectImage') }} + + + + + + + diff --git a/apps/vben5/packages/@abp/components/src/cropper/CropperModal.vue b/apps/vben5/packages/@abp/components/src/cropper/CropperModal.vue new file mode 100644 index 000000000..f8e263e56 --- /dev/null +++ b/apps/vben5/packages/@abp/components/src/cropper/CropperModal.vue @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/vben5/packages/@abp/components/src/cropper/index.ts b/apps/vben5/packages/@abp/components/src/cropper/index.ts new file mode 100644 index 000000000..8708c5109 --- /dev/null +++ b/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'; diff --git a/apps/vben5/packages/@abp/components/src/cropper/types.ts b/apps/vben5/packages/@abp/components/src/cropper/types.ts new file mode 100644 index 000000000..e76cc6f8e --- /dev/null +++ b/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 }; diff --git a/apps/vben5/packages/@abp/components/src/locales/index.ts b/apps/vben5/packages/@abp/components/src/locales/index.ts new file mode 100644 index 000000000..de3bd1b24 --- /dev/null +++ b/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; +} diff --git a/apps/vben5/packages/@abp/components/src/locales/langs/en-US/cropper.json b/apps/vben5/packages/@abp/components/src/locales/langs/en-US/cropper.json new file mode 100644 index 000000000..5ad017f87 --- /dev/null +++ b/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!" +} diff --git a/apps/vben5/packages/@abp/components/src/locales/langs/zh-CN/cropper.json b/apps/vben5/packages/@abp/components/src/locales/langs/zh-CN/cropper.json new file mode 100644 index 000000000..f3bba22eb --- /dev/null +++ b/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": "上传成功!" +} diff --git a/apps/vben5/packages/@abp/core/src/utils/file.ts b/apps/vben5/packages/@abp/core/src/utils/file.ts new file mode 100644 index 000000000..559c36b7b --- /dev/null +++ b/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 { + 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; + }); +} diff --git a/apps/vben5/packages/@abp/core/src/utils/index.ts b/apps/vben5/packages/@abp/core/src/utils/index.ts index 382c90803..b00fa27c5 100644 --- a/apps/vben5/packages/@abp/core/src/utils/index.ts +++ b/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'; diff --git a/apps/vben5/pnpm-workspace.yaml b/apps/vben5/pnpm-workspace.yaml index 42210ddfc..5bd673e69 100644 --- a/apps/vben5/pnpm-workspace.yaml +++ b/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 diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln index 708d441b2..e8de430b4 100644 --- a/aspnet-core/LINGYUN.MicroService.All.sln +++ b/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} diff --git a/aspnet-core/LINGYUN.MicroService.SingleProject.sln b/aspnet-core/LINGYUN.MicroService.SingleProject.sln index eb0a92875..0952778d2 100644 --- a/aspnet-core/LINGYUN.MicroService.SingleProject.sln +++ b/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} diff --git a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln index 371275b10..f25619db0 100644 --- a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln +++ b/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} diff --git a/aspnet-core/LINGYUN.MicroService.WebhooksManagement.sln b/aspnet-core/LINGYUN.MicroService.WebhooksManagement.sln index 8c77ae6e5..f3e3bb9ff 100644 --- a/aspnet-core/LINGYUN.MicroService.WebhooksManagement.sln +++ b/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} diff --git a/aspnet-core/LINGYUN.MicroService.WechatManagement.sln b/aspnet-core/LINGYUN.MicroService.WechatManagement.sln index aeee78091..22ef88c5e 100644 --- a/aspnet-core/LINGYUN.MicroService.WechatManagement.sln +++ b/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} diff --git a/aspnet-core/LINGYUN.MicroService.Workflow.sln b/aspnet-core/LINGYUN.MicroService.Workflow.sln index 18ca08446..1d2e9392f 100644 --- a/aspnet-core/LINGYUN.MicroService.Workflow.sln +++ b/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} diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs index 1d9bcc629..08ddb96d7 100644 --- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs +++ b/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 diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json index 314098cdf..0be8fdf50 100644 --- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json +++ b/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" } } \ No newline at end of file diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json index 1e9a8b872..55c21c327 100644 --- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json +++ b/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(南京-本地地域)", diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs index 0220a6ae2..855101c27 100644 --- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs +++ b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs @@ -79,4 +79,24 @@ public static class AliyunSettingNames /// public const string VisableErrorToClient = Prefix + ".VisableErrorToClient"; } + + /// + /// 云通信号码认证服务 + /// + public class SmsVerifyCode + { + public const string Prefix = AliyunSettingNames.Prefix + ".SmsVerifyCode"; + /// + /// 阿里云号码认证服务域名 + /// + public const string Domain = Prefix + ".Domain"; + /// + /// 默认签名 + /// + public const string DefaultSignName = Prefix + ".DefaultSignName"; + /// + /// 默认短信模板号 + /// + public const string DefaultTemplateCode = Prefix + ".DefaultTemplateCode"; + } } diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs index 55713d7af..5e5f98805 100644 --- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs +++ b/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(name); diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj index 73881e41b..700366a15 100644 --- a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj +++ b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj @@ -15,20 +15,21 @@ - + - + + - + diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs index f65c17d9d..d47517557 100644 --- a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs +++ b/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() .AddVirtualJson("/LINGYUN/Abp/BlobStoring/Tencent/Localization"); }); + + context.Services.AddTenantOssClient(); } } \ No newline at end of file diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/BlobStoringTencentConsts.cs b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/BlobStoringTencentConsts.cs new file mode 100644 index 000000000..39774e7df --- /dev/null +++ b/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"; +} diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs index 88d2579d5..a3142f5dc 100644 --- a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs +++ b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs @@ -33,6 +33,13 @@ public class CosClientFactory : AbstractTencentCloudClientFactory GetConfigurationAsync() + { + var configuration = ConfigurationProvider.Get(); + + return Task.FromResult(configuration.GetTencentConfiguration()); + } + protected override CosXml CreateClient(TencentBlobProviderConfiguration configuration, TencentCloudClientCacheItem cloudCache) { // 推荐全局单个对象,需要解决缓存过期事件 @@ -46,8 +53,10 @@ public class CosClientFactory : AbstractTencentCloudClientFactory CreateAsync(); Task CreateAsync(TencentBlobProviderConfiguration configuration); + + Task GetConfigurationAsync(); } diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs index b9ec97936..5ebd4b5ba 100644 --- a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs +++ b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs @@ -40,7 +40,7 @@ public class TencentBlobProviderConfiguration /// 创建命名空间时防盗链列表 /// public List CreateBucketReferer { - get => _containerConfiguration.GetConfiguration>(TencentBlobProviderConfigurationNames.CreateBucketReferer); + get => _containerConfiguration.GetConfigurationOrDefault(TencentBlobProviderConfigurationNames.CreateBucketReferer, new List()); set { if (value == null) { diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs index ac6fa4629..65429bdfa 100644 --- a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs +++ b/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); diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/Microsoft/Extensions/DependencyInjection/BlobStoringTencentHttpClientFactoryServiceCollectionExtensions.cs b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/Microsoft/Extensions/DependencyInjection/BlobStoringTencentHttpClientFactoryServiceCollectionExtensions.cs new file mode 100644 index 000000000..3deb883ca --- /dev/null +++ b/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; + } +} diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/System/Net/Http/BlobStoringTencentHttpClientFactoryExtenssions.cs b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/System/Net/Http/BlobStoringTencentHttpClientFactoryExtenssions.cs new file mode 100644 index 000000000..585e32241 --- /dev/null +++ b/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); ; + } +} diff --git a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN/Abp/Tencent/AbpTencentCloudModule.cs b/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN/Abp/Tencent/AbpTencentCloudModule.cs index 57251d25b..1ef5e57e6 100644 --- a/aspnet-core/framework/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN/Abp/Tencent/AbpTencentCloudModule.cs +++ b/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 diff --git a/aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs b/aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs index 4fb3ce253..63c09c373 100644 --- a/aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs +++ b/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) diff --git a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.csproj b/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.csproj index 3a4a9a829..2f50762b8 100644 --- a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.csproj +++ b/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.csproj @@ -10,10 +10,9 @@ false false false - - Cap分布式事件总线 true - $(SolutionDir)framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.xml + Cap分布式事件总线 + diff --git a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPBootstrapper.cs b/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPBootstrapper.cs new file mode 100644 index 000000000..55d753571 --- /dev/null +++ b/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 _logger; + private readonly IServiceProvider _serviceProvider; + + private CancellationTokenSource _cts; + private bool _disposed; + private IEnumerable _processors = default!; + + public bool IsStarted => !_cts?.IsCancellationRequested ?? false; + + public AbpCAPBootstrapper(IServiceProvider serviceProvider, ILogger 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(); + + try + { + await _serviceProvider.GetRequiredService().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(); + if (marker == null) + throw new InvalidOperationException( + "AddCap() must be added on the service collection. eg: services.AddCap(...)"); + + var messageQueueMarker = _serviceProvider.GetService(); + 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(); + 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; + } +} diff --git a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs b/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs index 39310dfd5..25b7fa275 100644 --- a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs +++ b/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(); /// /// ConfigureServices /// @@ -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() + .BootstrapAsync(_cancellationTokenSource.Token); + } + + public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context) + { + _cancellationTokenSource.Cancel(); + + return Task.CompletedTask; + } } diff --git a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs b/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs index 445567aff..81ae8168e 100644 --- a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs +++ b/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(); services.AddSingleton(); + + // 移除默认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(); + services.AddSingleton(sp => sp.GetRequiredService()); + return services; } } diff --git a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.xml b/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.xml deleted file mode 100644 index 35b6776bb..000000000 --- a/aspnet-core/framework/common/LINGYUN.Abp.EventBus.CAP/framework/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.xml +++ /dev/null @@ -1,397 +0,0 @@ - - - - LINGYUN.Abp.EventBus.CAP - - - - - 消费者查找器 - - - - - CAP配置 - - - - - Abp分布式事件配置 - - - - - 服务提供者 - - - - - Creates a new . - - - - - 查找消费者集合 - - - - - - - 获取事件处理器集合 - - - - - - - - AbpCAPEventBusModule - - - - - ConfigureServices - - - - - - 过期消息清理配置项 - - - - - 发布消息处理失败通知 - default: false - - - - - AbpECAPExecutionFailedException - - - - - MessageType - - - - - Message - - - - - constructor - - - - - - - constructor - - - - - - - - constructor - - - - - - - - - CAP消息扩展 - - - - - 尝试获取消息标头中的租户标识 - - - - - - - - 获取消息标头中的租户标识 - - - - - - - 尝试获取消息标头中的链路标识 - - - - - - - - 获取消息标头中的链路标识 - - - - - - - 重写 ISubscribeInvoker 实现 Abp 租户集成 - - - - - AbpCAPSubscribeInvoker - - - - - - - - - - 调用订阅者方法 - - - - - - - - - - - - - - - - - - 获取事件处理类实例 - - - - - - - - 通过给定的类型实例与参数调用订阅者方法 - - - - - - - - - CAP分布式事件总线 - - - - - CAP消息发布接口 - - - - - 自定义事件注册接口 - - - - - 本地事件处理器工厂对象集合 - - - - - 本地事件集合 - - - - - 当前用户 - - - - - 当前客户端 - - - - - typeof - - - - - 取消令牌 - - - - - constructor - - - - - - - - - - - - - - - - - - - - 订阅事件 - - - - - - - - 退订事件 - - 事件类型 - - - - - 退订事件 - - 事件类型 - 事件处理器 - - - - 退订事件 - - 事件类型 - 事件处理器工厂 - - - - 退订所有事件 - - 事件类型 - - - - 发布事件 - - 事件类型 - 事件数据对象 - - - - - 获取事件处理器工厂列表 - - - - - - - 自定义事件订阅者 - - - - - 订阅事件 - - - - - - - 取消订阅 - - - - - - - Executes the configured method on . This can be used whether or not - the configured method is asynchronous. - - - 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. - - The object whose method is to be executed. - Parameters to pass to the method. - The method return value. - - - - Executes the configured method on . This can only be used if the configured - method is asynchronous. - - - 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). - - The object whose method is to be executed. - Parameters to pass to the method. - An object that you can "await" to get the method return value. - - - - Provides a common awaitable structure that can - return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an - application-defined custom awaitable. - - - - - Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying - an for mapping instances of that type to a C# awaitable. - - - 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. - - - - - CAP ServiceCollectionExtensions - - - - - Adds and configures the consistence services for the consistency. - - - - - - - diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs index 31326203b..2e749fb0e 100644 --- a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs +++ b/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(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(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) diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs new file mode 100644 index 000000000..49d33db37 --- /dev/null +++ b/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 +{ + /// + /// 请求状态码, OK代表请求成功 + /// + public string Code { get; set; } + /// + /// 状态码的描述 + /// + public string Message { get; set; } + /// + /// 请求是否成功 + /// + public bool Success { get; set; } + /// + /// 请求结果数据 + /// + public AliyunSmsVerifyCodeModel Model { get; set; } +} + +public class AliyunSmsVerifyCodeModel +{ + /// + /// 请求Id + /// + public string RequestId { get; set; } + /// + /// 业务Id + /// + public string BizId { get; set; } + /// + /// 外部流水号 + /// + public string OutId { get; set; } + /// + /// 验证码, 仅当使用阿里云短信验证服务生成验证码时携带 + /// + public string VerifyCode { get; set; } +} diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs new file mode 100644 index 000000000..f9b61feae --- /dev/null +++ b/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; +/// +/// 阿里云发送短信验证码接口 +/// +public interface IAliyunSmsVerifyCodeSender +{ + /// + /// 发送短信验证码 + /// + /// + /// + Task SendAsync(SmsVerifyCodeMessage message); +} diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/en.json b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/en.json index 5ef98e5a2..df50a12f6 100644 --- a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/en.json +++ b/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", diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/zh-Hans.json b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/zh-Hans.json index f2ba1b6a5..390a43155 100644 --- a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/Localization/Resources/zh-Hans.json +++ b/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地址所在的地区被禁用", diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs new file mode 100644 index 000000000..745c9947f --- /dev/null +++ b/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 +{ + /// + /// 方案名称,如果不填则为“默认方案”。最多不超过 20 个字符。 + /// + public string SchemeName { get; set; } + /// + /// 号码国家编码。默认为 86,目前也仅支持中国国内号码发送。 + /// + public string CountryCode { get; set; } + /// + /// 上行短信扩展码。上行短信指发送给通信服务提供商的短信,用于定制某种服务、完成查询,或是办理某种业务等,需要收费,按运营商普通短信资费进行扣费。 + /// + public string SmsUpExtendCode { get; set; } + /// + /// 外部流水号。 + /// + public string OutId { get; set; } + /// + /// 验证码长度支持 4~8 位长度,默认是 4 位。 + /// + public long? CodeLength { get; set; } + /// + /// 验证码有效时长,单位秒,默认为 300 秒。 + /// + public long? ValidTime { get; set; } + /// + /// 核验规则,当有效时间内对同场景内的同号码重复发送验证码时,旧验证码如何处理。 + /// + /// + /// 1 - 覆盖处理(默认),即旧验证码会失效掉。 + /// 2 - 保留,即多个验证码都是在有效期内都可以校验通过。 + /// + public long? DuplicatePolicy { get; set; } + /// + /// 时间间隔,单位:秒。即多久间隔可以发送一次验证码,用于频控,默认 60 秒。 + /// + public long? Interval { get; set; } + /// + /// 生成的验证码类型。当参数 TemplateParam 传入占位符时,此参数必填,将由系统根据指定的规则生成验证码。 + /// + /// + /// 1 - 纯数字(默认)。 + /// 2 - 纯大写字母。 + /// 3 - 纯小写字母。 + /// 4 - 大小字母混合。 + /// 5 - 数字+大写字母混合。 + /// 6 - 数字+小写字母混合。 + /// 7 - 数字+大小写字母混合。 + /// + public long? CodeType { get; set; } + /// + /// 是否返回验证码。 + /// + public bool? ReturnVerifyCode { get; set; } + /// + /// 是否自动替换签名重试(默认开启。 + /// + public bool? AutoRetry { get; set; } + /// + /// 短信接收方手机号。 + /// + public string PhoneNumber { get; } + /// + /// 签名名称。暂不支持使用自定义签名,请使用系统赠送的签名。 + /// + public string SignName { get; } + /// + /// 短信模板 CODE。参数SignName选择赠送签名时,必须搭配赠送模板下发短信。您可在赠送模板配置页面选择适用您业务场景的模板。 + /// + public string TemplateCode { get; } + /// + /// 短信模板参数。 + /// + public SmsVerifyCodeMessageParam TemplateParam { get; } + public SmsVerifyCodeMessage( + string phoneNumber, + SmsVerifyCodeMessageParam templateParam, + string signName = null, + string templateCode = null) + { + PhoneNumber = phoneNumber; + TemplateParam = templateParam; + SignName = signName; + TemplateCode = templateCode; + } +} diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs new file mode 100644 index 000000000..4158d44c6 --- /dev/null +++ b/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; + } +} diff --git a/aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.APM/LINGYUNG/Abp/Telemetry/APM/AbpTelemetryAPMModule.cs b/aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.APM/LINGYUNG/Abp/Telemetry/APM/AbpTelemetryAPMModule.cs index 13d8bed4c..3f5ed43ce 100644 --- a/aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.APM/LINGYUNG/Abp/Telemetry/APM/AbpTelemetryAPMModule.cs +++ b/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(); } } diff --git a/aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.OpenTelemetry/LINGYUN/Abp/Telemetry/OpenTelemetry/AbpTelemetryOpenTelemetryModule.cs b/aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.OpenTelemetry/LINGYUN/Abp/Telemetry/OpenTelemetry/AbpTelemetryOpenTelemetryModule.cs index c8f162ca2..2b977113a 100644 --- a/aspnet-core/framework/telemetry/LINGYUN.Abp.Telemetry.OpenTelemetry/LINGYUN/Abp/Telemetry/OpenTelemetry/AbpTelemetryOpenTelemetryModule.cs +++ b/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(); - 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); + }); + } + } } diff --git a/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xml b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xml new file mode 100644 index 000000000..5d6962159 --- /dev/null +++ b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xsd b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN.Abp.AspNetCore.MultiTenancy.csproj b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN.Abp.AspNetCore.MultiTenancy.csproj new file mode 100644 index 000000000..89b162615 --- /dev/null +++ b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN.Abp.AspNetCore.MultiTenancy.csproj @@ -0,0 +1,21 @@ + + + + + + + net9.0 + LINGYUN.Abp.AspNetCore.MultiTenancy + LINGYUN.Abp.AspNetCore.MultiTenancy + false + false + false + Library + enable + + + + + + + diff --git a/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs new file mode 100644 index 000000000..61668fc97 --- /dev/null +++ b/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 +{ + +} diff --git a/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyResolveOptions.cs b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyResolveOptions.cs new file mode 100644 index 000000000..1b7d0abc8 --- /dev/null +++ b/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 +{ + /// + /// 仅解析域名中的租户, 默认: true + /// + public bool OnlyResolveDomain { get; set; } + public AbpAspNetCoreMultiTenancyResolveOptions() + { + OnlyResolveDomain = true; + } +} diff --git a/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpMultiTenancyOptionsExtensions.cs b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/AbpMultiTenancyOptionsExtensions.cs new file mode 100644 index 000000000..d78531af5 --- /dev/null +++ b/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) + ); + } +} diff --git a/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/OnlyDomainTenantResolveContributor.cs b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/LINGYUN/Abp/AspNetCore/MultiTenancy/OnlyDomainTenantResolveContributor.cs new file mode 100644 index 000000000..b272971ff --- /dev/null +++ b/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 GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + { + if (!httpContext.Request.Host.HasValue) + { + return Task.FromResult(null); + } + + var options = httpContext.RequestServices.GetRequiredService>(); + if (options.Value.OnlyResolveDomain) + { + // 仅仅解析域名, 如果请求的是IP地址, 则不使用这个解析贡献者 + if (IPAddress.TryParse(httpContext.Request.Host.Host, out var _)) + { + return Task.FromResult(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); + } +} diff --git a/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json b/aspnet-core/framework/tenants/LINGYUN.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json new file mode 100644 index 000000000..8ac6471cc --- /dev/null +++ b/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" + } + } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.Identity.WeChat.Work/LINGYUN/Abp/Identity/WeChat/Work/AbpWeChatWorkClaimsPrincipalContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.Identity.WeChat.Work/LINGYUN/Abp/Identity/WeChat/Work/AbpWeChatWorkClaimsPrincipalContributor.cs new file mode 100644 index 000000000..7087e0c6d --- /dev/null +++ b/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(); + + var weChatWorkUserId = await userClaimProvider?.FindUserIdentifierAsync(userId.Value); + if (!weChatWorkUserId.IsNullOrWhiteSpace()) + { + claimsIdentity.AddOrReplace(new Claim(AbpWeChatWorkClaimTypes.UserId, weChatWorkUserId)); + + context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity); + } + } + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs deleted file mode 100644 index 91af5f200..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace LINGYUN.Abp.WeChat.Common.Messages; -public class AbpWeChatMessageResolveOptions -{ - public List MessageResolvers { get; } - - public AbpWeChatMessageResolveOptions() - { - MessageResolvers = new List(); - } -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs index 78bb587b6..dd1b0a93f 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs +++ b/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; } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs deleted file mode 100644 index c708a2aa1..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Threading.Tasks; - -namespace LINGYUN.Abp.WeChat.Common.Messages; -public interface IMessageResolver -{ - Task ResolveMessageAsync(MessageResolveData messageData); -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs index 088b67e21..56f9089fc 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs +++ b/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; } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolver.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolverBase.cs similarity index 69% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolver.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolverBase.cs index 145b4efca..42952f73e 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolver.cs +++ b/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 options, + protected MessageResolverBase( IWeChatCryptoService cryptoService, IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; _cryptoService = cryptoService; - _options = options.Value; } /// /// 解析微信服务器推送消息/事件 /// /// /// - public async virtual Task ResolveMessageAsync(MessageResolveData messageData) + public async virtual Task 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; } + /// + /// 实现具体的解析微信服务器推送消息/事件方法 + /// + /// + /// + protected abstract Task ResolveMessageAsync(MessageResolveContext context); } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs index bc4096c6b..6ee32744d 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs +++ b/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); diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs index f695ba86d..b265a0739 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs +++ b/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()); options.MapMessage("location", context => context.GetWeChatMessage()); options.MapMessage("link", context => context.GetWeChatMessage()); - }); - Configure(options => - { // 事件处理器 options.MessageResolvers.AddIfNotContains(new WeChatOfficialEventResolveContributor()); // 消息处理器 diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs index d145468e7..506e08e03 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs +++ b/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 MessageResolvers { get; } public IDictionary> EventMaps { get; } public IDictionary> MessageMaps { get; } public AbpWeChatOfficialMessageResolveOptions() { + MessageResolvers = new List(); EventMaps = new Dictionary>(); MessageMaps = new Dictionary>(); } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/IWeChatOfficialMessageResolver.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/IWeChatOfficialMessageResolver.cs new file mode 100644 index 000000000..570eb0662 --- /dev/null +++ b/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; +/// +/// 微信公众号消息解析器 +/// +public interface IWeChatOfficialMessageResolver +{ + /// + /// 解析微信公众号消息 + /// + /// 公众号消息 + /// + Task ResolveAsync(MessageResolveData messageData); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs index b63df0c34..6dd84a18f 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs +++ b/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 : WeChatMessageEto where TEvent : WeChatEventMessage { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs index 9db7070ca..4f62e84c8 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs +++ b/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>().Value; var messageType = context.GetMessageData("MsgType"); var eventName = context.GetMessageData("Event"); if ("event".Equals(messageType, StringComparison.InvariantCultureIgnoreCase) && diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessageEto.cs index a5129d268..c27cc00af 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessageEto.cs +++ b/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 : WeChatMessageEto where TMessage : WeChatOfficialGeneralMessage { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributor.cs index 5a0b4aa3c..da22ec390 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributor.cs +++ b/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>().Value; var messageType = context.GetMessageData("MsgType"); if (options.MessageMaps.TryGetValue(messageType, out var messageFactory)) { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributorBase.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributorBase.cs index cf82e7a8e..998b8ed8d 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributorBase.cs +++ b/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>().Value; - return Task.CompletedTask; + return ResolveMessageAsync(context, options); } - protected abstract Task ResolveWeChatMessageAsync(IMessageResolveContext context); + protected abstract Task ResolveMessageAsync(IMessageResolveContext context, AbpWeChatOfficialMessageResolveOptions options); } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolver.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolver.cs new file mode 100644 index 000000000..6ce0d69e5 --- /dev/null +++ b/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 options) + : base(cryptoService, serviceProvider) + { + _options = options.Value; + } + + protected async override Task 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; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs index 52506017b..4c880252d 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs +++ b/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); diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj index 71d137f5f..44a9562dc 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj @@ -10,6 +10,7 @@ false false false + True diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/AbpWeChatWorkCommonModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/AbpWeChatWorkCommonModule.cs index 6e2d64ae6..5fa41c334 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/AbpWeChatWorkCommonModule.cs +++ b/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()); options.MapEvent("batch_job_result", context => context.GetWeChatMessage()); options.MapEvent("open_approval_change", context => context.GetWeChatMessage()); + options.MapEvent("sys_approval_change", context => context.GetWeChatMessage()); options.MapEvent("share_agent_change", context => context.GetWeChatMessage()); options.MapEvent("share_chain_change", context => context.GetWeChatMessage()); options.MapEvent("template_card_event", context => context.GetWeChatMessage()); @@ -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()); + options.MapEvent("cancel_meeting_room", context => context.GetWeChatMessage()); options.MapMessage("text", context => context.GetWeChatMessage()); options.MapMessage("image", context => context.GetWeChatMessage()); @@ -64,10 +67,7 @@ public class AbpWeChatWorkCommonModule : AbpModule options.MapMessage("shortvideo", context => context.GetWeChatMessage()); options.MapMessage("location", context => context.GetWeChatMessage()); options.MapMessage("link", context => context.GetWeChatMessage()); - }); - Configure(options => - { // 事件处理器 options.MessageResolvers.AddIfNotContains(new WeChatWorkEventResolveContributor()); // 消息处理器 diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/AbpWeChatWorkMessageResolveOptions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/AbpWeChatWorkMessageResolveOptions.cs index e994957b3..99be696f9 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/AbpWeChatWorkMessageResolveOptions.cs +++ b/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 MessageResolvers { get; } public IDictionary> EventMaps { get; } public IDictionary> MessageMaps { get; } public AbpWeChatWorkMessageResolveOptions() { + MessageResolvers = new List(); EventMaps = new Dictionary>(); MessageMaps = new Dictionary>(); } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/IWeChatWorkMessageResolver.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/IWeChatWorkMessageResolver.cs new file mode 100644 index 000000000..04e57b30b --- /dev/null +++ b/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; +/// +/// 企业微信消息解析器 +/// +public interface IWeChatWorkMessageResolver +{ + /// + /// 解析企业微信消息 + /// + /// 企业微信消息 + /// + Task ResolveAsync(MessageResolveData messageData); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BatchJobResultEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BatchJobResultEvent.cs index 504f8ec3c..fc86054cc 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BatchJobResultEvent.cs +++ b/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; /// -/// 进入应用事件 +/// 异步任务完成事件 /// [EventName("batch_job_result")] public class BatchJobResultEvent : WeChatWorkEventMessage diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BookMeetingRoomEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BookMeetingRoomEvent.cs new file mode 100644 index 000000000..4efc03105 --- /dev/null +++ b/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; +/// +/// 会议室预定事件 +/// +/// +/// 当用户在企业微信预定会议室时,会触发该事件回调给会议室系统应用。 +/// +[EventName("book_meeting_room")] +public class BookMeetingRoomEvent : WeChatWorkEventMessage +{ + /// + /// 会议室id + /// + [XmlElement("MeetingRoomId")] + public int MeetingRoomId { get; set; } + /// + /// 预定id,可根据该ID查询具体的会议预定情况 + /// + [XmlElement("BookingId")] + public string BookingId { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CancelMeetingRoomEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CancelMeetingRoomEvent.cs new file mode 100644 index 000000000..9fa50cadc --- /dev/null +++ b/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; +/// +/// 会议室取消事件 +/// +/// +/// 当用户在企业微信取消预定会议室时,会触发该事件回调给会议室系统应用;如果该会议室由自建应用预定,除了会议室系统应用外,也会回调给对应的自建应用。 +/// +[EventName("cancel_meeting_room")] +public class CancelMeetingRoomEvent : WeChatWorkEventMessage +{ + /// + /// 会议室id + /// + [XmlElement("MeetingRoomId")] + public int MeetingRoomId { get; set; } + /// + /// 预定id,可根据该ID查询具体的会议预定情况 + /// + [XmlElement("BookingId")] + public string BookingId { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/SysApprovalStatusChangeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/SysApprovalStatusChangeEvent.cs new file mode 100644 index 000000000..a08fa5988 --- /dev/null +++ b/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; +/// +/// 企业微信“审批应用”审批状态通知事件 +/// +[EventName("sys_approval_change")] +public class SysApprovalStatusChangeEvent : WeChatWorkEventMessage +{ + /// + /// 审批信息 + /// + [XmlElement("ApprovalInfo")] + public SysApprovalInfo ApprovalInfo { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} + +public class SysApprovalInfo +{ + /// + /// 审批编号(字符串类型) + /// + [XmlElement("SpNoStr")] + public string SpNoStr { get; set; } + /// + /// 审批申请类型名称(审批模板名称) + /// + [XmlElement("SpName")] + public string SpName { get; set; } + /// + /// 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付 + /// + [XmlElement("SpStatus")] + public byte SpStatus { get; set; } + /// + /// 审批模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。 + /// + [XmlElement("TemplateId")] + public string TemplateId { get; set; } + /// + /// 审批申请提交时间,Unix时间戳 + /// + [XmlElement("ApplyTime")] + public int ApplyTime { get; set; } + /// + /// 申请人信息 + /// + [XmlElement("Applyer")] + public SysApprovalApplyer Applyer { get; set; } + /// + /// 审批流程信息,可能有多个审批节点。 + /// + [XmlElement("SpRecord")] + public List SpRecord { get; set; } + /// + /// 抄送信息,可能有多个抄送节点 + /// + [XmlElement("Notifyer")] + public List Notifyer { get; set; } + /// + /// 审批申请备注信息,可能有多个备注节点 + /// + [XmlElement("Comments")] + public List Comments { get; set; } + /// + /// 审批流程列表 + /// + [XmlElement("ProcessList")] + public List ProcessList { get; set; } + /// + /// 审批申请状态变化类型:1-提单;2-同意;3-驳回;4-转审;5-催办;6-撤销;8-通过后撤销;10-添加备注;11-回退给指定审批人;12-添加审批人;13-加签并同意; 14-已办理; 15-已转交 + /// + [XmlElement("StatuChangeEvent")] + public byte StatuChangeEvent { get; set; } + /// + /// 审批编号 + /// + /// + /// 局校审批单不返回此字段,其他类型审批单会返回此字段,不推荐使用此字段 + /// + [XmlElement("SpNo")] + [Obsolete("局校审批单不返回此字段,其他类型审批单会返回此字段,不推荐使用此字段")] + public string SpNo { get; set; } +} + +public class SysApprovalApplyer +{ + /// + /// 申请人userid + /// + [XmlElement("UserId")] + public string UserId { get; set; } + /// + /// 申请人所在部门pid + /// + [XmlElement("Party")] + public string Party { get; set; } +} + +public class SysApprovalRecord +{ + /// + /// 审批节点状态:1-审批中;2-已同意;3-已驳回;4-已转审 + /// + [XmlElement("SpStatus")] + public byte SpStatus { get; set; } + /// + /// 节点审批方式:1-或签;2-会签 + /// + [XmlElement("ApproverAttr")] + public byte ApproverAttr { get; set; } + /// + /// 节点审批方式:1-或签;2-会签 + /// + [XmlElement("Details")] + public List Details { get; set; } +} + +public class SysApprovalRecordDetail +{ + /// + /// 分支审批人 + /// + [XmlElement("Approver")] + public SysApprovalApplyer Approver { get; set; } + /// + /// 审批意见字段 + /// + [XmlElement("Speech")] + public string Speech { get; set; } + /// + /// 分支审批人审批状态:1-审批中;2-已同意;3-已驳回;4-已转审 + /// + [XmlElement("SpStatus")] + public byte SpStatus { get; set; } + /// + /// 节点分支审批人审批操作时间,0为尚未操作 + /// + [XmlElement("SpTime")] + public int SpTime { get; set; } + /// + /// 节点分支审批人审批意见附件,赋值为media_id具体使用请参考:文档-获取临时素材 + /// + [XmlElement("Attach")] + public string Attach { get; set; } +} + +public class SysApprovalNotifyer +{ + /// + /// 节点抄送人userid + /// + [XmlElement("UserId")] + public string UserId { get; set; } +} + +public class SysApprovalComment +{ + /// + /// 备注人信息 + /// + [XmlElement("CommentUserInfo")] + public SysApprovalCommenter CommentUserInfo { get; set; } + /// + /// 备注提交时间 + /// + [XmlElement("CommentTime")] + public int CommentTime { get; set; } + /// + /// 备注文本内容 + /// + [XmlElement("CommentContent")] + public string CommentContent { get; set; } + /// + /// 备注id + /// + [XmlElement("CommentId")] + public string CommentId { get; set; } + /// + /// 备注意见附件,值是附件media_id + /// + [XmlElement("Attach")] + public string Attach { get; set; } +} + +public class SysApprovalCommenter +{ + /// + /// 节点抄送人userid + /// + [XmlElement("UserId")] + public string UserId { get; set; } +} + +public class SysApprovalProcess +{ + /// + /// 流程节点 + /// + [XmlElement("NodeList")] + public List NodeList { get; set; } +} + +public class SysApprovalProcessNode +{ + /// + /// 节点类型 1 审批人 2 抄送人 3办理人 + /// + [XmlElement("NodeType")] + public byte NodeType { get; set; } + /// + /// 节点状态 1-审批中;2-同意;3-驳回;4-转审;11-退回给指定审批人;12-加签;13-同意并加签;14-办理;15-转交 + /// + [XmlElement("SpStatus")] + public byte SpStatus { get; set; } + /// + /// 多人办理方式 1-会签;2-或签 3-依次审批 + /// + [XmlElement("ApvRel")] + public byte ApvRel { get; set; } + /// + /// 子节点列表 + /// + [XmlElement("SubNodeList")] + public List SubNodeList { get; set; } +} + +public class SysApprovalProcessSubNode +{ + /// + /// 处理人信息 + /// + [XmlElement("UserInfo")] + public SysApprovalProcesser UserInfo { get; set; } + /// + /// 审批/办理意见 + /// + [XmlElement("Speech")] + public string Speech { get; set; } + /// + /// 子节点状态 1-审批中;2-同意;3-驳回;4-转审;11-退回给指定审批人;12-加签;13-同意并加签;14-办理;15-转交 + /// + [XmlElement("SpYj")] + public byte SpYj { get; set; } + /// + /// 操作时间 + /// + [XmlElement("Sptime")] + public int Sptime { get; set; } + /// + /// 备注意见附件,值是附件media_id + /// + [XmlElement("MediaIds")] + public string MediaIds { get; set; } +} + +public class SysApprovalProcesser +{ + /// + /// 处理人userid + /// + [XmlElement("UserId")] + public string UserId { get; set; } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessageEto.cs index 3d9d67723..5a72bd16e 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessageEto.cs +++ b/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 : WeChatMessageEto where TEvent : WeChatWorkEventMessage { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventResolveContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventResolveContributor.cs index b2974c092..b4d337b00 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventResolveContributor.cs +++ b/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; /// -/// 微信公众号事件处理器 +/// 企业微信事件处理器 /// 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>().Value; var messageType = context.GetMessageData("MsgType"); var eventName = context.GetMessageData("Event"); if ("event".Equals(messageType, StringComparison.InvariantCultureIgnoreCase) && diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessageEto.cs index 734642916..fb143b78c 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessageEto.cs +++ b/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 : WeChatMessageEto where TMessage : WeChatWorkGeneralMessage { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributor.cs index acf5ec6df..0a5be5f72 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributor.cs +++ b/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; /// -/// 微信公众号消息处理器 +/// 企业微信消息处理器 /// 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>().Value; var messageType = context.GetMessageData("MsgType"); if (options.MessageMaps.TryGetValue(messageType, out var messageFactory)) { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributorBase.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributorBase.cs index 7d503ba01..8b45d819e 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolveContributorBase.cs +++ b/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>().Value; - return Task.CompletedTask; + return ResolveMessageAsync(context, options); } - protected abstract Task ResolveWeComMessageAsync(IMessageResolveContext context); + protected abstract Task ResolveMessageAsync(IMessageResolveContext context, AbpWeChatWorkMessageResolveOptions options); } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolver.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkMessageResolver.cs new file mode 100644 index 000000000..f361e7f04 --- /dev/null +++ b/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 options) + : base(cryptoService, serviceProvider) + { + _options = options.Value; + } + + protected async override Task 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; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xml b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xsd b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN.Abp.WeChat.Work.ExternalContact.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN.Abp.WeChat.Work.ExternalContact.csproj new file mode 100644 index 000000000..730bd0b71 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN.Abp.WeChat.Work.ExternalContact.csproj @@ -0,0 +1,27 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0 + LINGYUN.Abp.WeChat.Work.ExternalContact + LINGYUN.Abp.WeChat.Work.ExternalContact + false + false + false + True + enable + + + + + + + + + + + + + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/AbpWeChatWorkExternalContactModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/AbpWeChatWorkExternalContactModule.cs new file mode 100644 index 000000000..dd18a596b --- /dev/null +++ b/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(options => + { + // 企业客户变更事件 + options.MapEvent("change_external_contact", context => + { + var changeType = context.GetMessageData("ChangeType"); + return changeType switch + { + "add_external_contact" => context.GetWeChatMessage(), + "edit_external_contact" => context.GetWeChatMessage(), + "add_half_external_contact" => context.GetWeChatMessage(), + "del_external_contact" => context.GetWeChatMessage(), + "del_follow_user" => context.GetWeChatMessage(), + "transfer_fail" => context.GetWeChatMessage(), + _ => 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(); + case "update": + // 客户群变更事件 + var updateDetail = context.GetMessageData("UpdateDetail"); + return updateDetail switch + { + "add_member" => context.GetWeChatMessage(), + "del_member" => context.GetWeChatMessage(), + "change_owner" => context.GetWeChatMessage(), + "change_name" => context.GetWeChatMessage(), + "change_notice" => context.GetWeChatMessage(), + _ => throw new AbpWeChatException($"Contact change event change_external_chat:{changeType}:{updateDetail} is not mounted!"), + }; + case "dismiss": return context.GetWeChatMessage(); + 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(), + "update" => context.GetWeChatMessage(), + "delete" => context.GetWeChatMessage(), + "shuffle" => context.GetWeChatMessage(), + _ => throw new AbpWeChatException($"Contact change event change_external_tag:{changeType} is not mounted!"), + }; + }); + }); + + Configure(options => + { + options.Resources + .Get() + .AddVirtualJson("/LINGYUN/Abp/WeChat/Work/ExternalContact/Localization/Resources"); + }); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/IWeChatWorkAttachmentProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/IWeChatWorkAttachmentProvider.cs new file mode 100644 index 000000000..e35baeb31 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/IWeChatWorkAttachmentProvider.cs @@ -0,0 +1,24 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments; +/// +/// 附件资源接口 +/// +public interface IWeChatWorkAttachmentProvider +{ + /// + /// 上传附件资源 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task UploadAsync( + WeChatWorkUploadAttachmentRequest request, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Models/AttachmentType.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Models/AttachmentType.cs new file mode 100644 index 000000000..096e7bf8f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Models/AttachmentType.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Models; +/// +/// 附件类型 +/// +[Description("附件类型")] +public enum AttachmentType +{ + /// + /// 朋友圈 + /// + [Description("朋友圈")] + Moments = 1, + /// + /// 商品图册 + /// + [Description("商品图册")] + ProductCatalogue = 2, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Models/MediaType.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Models/MediaType.cs new file mode 100644 index 000000000..d122f663c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Models/MediaType.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Models; +/// +/// 媒体文件类型 +/// +[Description("媒体文件类型")] +public enum MediaType +{ + /// + /// 图片 + /// + [Description("图片")] + Image = 1, + /// + /// 视频 + /// + [Description("视频")] + Video = 2, + /// + /// 普通文件 + /// + [Description("普通文件")] + File = 3, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Request/WeChatWorkUploadAttachmentRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Request/WeChatWorkUploadAttachmentRequest.cs new file mode 100644 index 000000000..e1ac87d55 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Request/WeChatWorkUploadAttachmentRequest.cs @@ -0,0 +1,41 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Models; +using Volo.Abp; +using Volo.Abp.Content; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Request; +/// +/// 上传附件资源请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkUploadAttachmentRequest : WeChatWorkRequest +{ + /// + /// 媒体文件类型 + /// + [NotNull] + public MediaType MediaType { get; } + /// + /// 附件类型 + /// + [NotNull] + public AttachmentType AttachmentType { get; } + /// + /// 媒体文件 + /// + [NotNull] + public IRemoteStreamContent Content { get; } + public WeChatWorkUploadAttachmentRequest( + MediaType mediaType, + AttachmentType attachmentType, + IRemoteStreamContent content) + { + Check.NotNull(content, nameof(content)); + + MediaType = mediaType; + AttachmentType = attachmentType; + Content = content; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Response/WeChatWorkUploadAttachmentResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Response/WeChatWorkUploadAttachmentResponse.cs new file mode 100644 index 000000000..46f1045f7 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/Response/WeChatWorkUploadAttachmentResponse.cs @@ -0,0 +1,35 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Response; +/// +/// 上传附件资源响应结果 +/// +/// +/// 详情见: +/// +public class WeChatWorkUploadAttachmentResponse : WeChatWorkResponse +{ + /// + /// 媒体文件类型,分别有图片(image)、语音(voice)、视频(video),普通文件(file) + /// + [NotNull] + [JsonProperty("type")] + [JsonPropertyName("type")] + public string MediaType { get; set; } + /// + /// 媒体文件上传后获取的唯一标识,三天有效,可使用获取临时素材接口获取 + /// + [NotNull] + [JsonProperty("media_id")] + [JsonPropertyName("media_id")] + public string MediaId { get; set; } + /// + /// 媒体文件上传时间戳 + /// + [NotNull] + [JsonProperty("created_at")] + [JsonPropertyName("created_at")] + public long CreatedAt { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/WeChatWorkAttachmentProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/WeChatWorkAttachmentProvider.cs new file mode 100644 index 000000000..d2e9d5610 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Attachments/WeChatWorkAttachmentProvider.cs @@ -0,0 +1,41 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments.Response; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +using LINGYUN.Abp.WeChat.Work.Token; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Attachments; + +[RequiresFeature(WeChatWorkExternalContactFeatureNames.Enable)] +public class WeChatWorkAttachmentProvider : IWeChatWorkAttachmentProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkAttachmentProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task UploadAsync( + WeChatWorkUploadAttachmentRequest request, + CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.UploadAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/IWeChatWorkExternalContactProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/IWeChatWorkExternalContactProvider.cs new file mode 100644 index 000000000..4efe1d1a1 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/IWeChatWorkExternalContactProvider.cs @@ -0,0 +1,24 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts; +/// +/// 外部联系人接口 +/// +public interface IWeChatWorkExternalContactProvider +{ + /// + /// 获取已服务的外部联系人 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task GetExternalContactListAsync( + WeChatWorkGetExternalContactListRequest request, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Models/ExternalContactInfo.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Models/ExternalContactInfo.cs new file mode 100644 index 000000000..2e53003fc --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Models/ExternalContactInfo.cs @@ -0,0 +1,73 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts.Models; +/// +/// 外部联系人 +/// +public class ExternalContactInfo +{ + /// + /// 是否被成员标记为客户 + /// + [NotNull] + [JsonProperty("is_customer")] + [JsonPropertyName("is_customer")] + public bool IsCustomer { get; set; } + /// + /// 外部联系人临时ID + /// + /// + /// + /// 外部联系人临时id是一个外部联系人的唯一标识,企业可根据此id对外部联系人进行去重统计。 + /// 但外部联系人临时id仅在一轮遍历查询(从首个分页查询开始到最后一个分页查询完毕)中唯一; + /// 每次请求首个数据分页(cursor为空)时,返回的外部联系人临时id和next_cursor将发生变化。 + /// + [NotNull] + [JsonProperty("tmp_openid")] + [JsonPropertyName("tmp_openid")] + public string TmpOpenId { get; set; } + /// + /// 外部联系人的externaluserid(如果是客户才返回) + /// + [CanBeNull] + [JsonProperty("external_userid")] + [JsonPropertyName("external_userid")] + public string? ExternalUserId { get; set; } + /// + /// 脱敏后的外部联系人昵称(如果是其他外部联系人才返回) + /// + [CanBeNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string? Name { get; set; } + /// + /// 添加此外部联系人的企业成员或外部联系人所在群聊的群主userid + /// + [CanBeNull] + [JsonProperty("follow_userid")] + [JsonPropertyName("follow_userid")] + public string? FollowUserId { get; set; } + /// + /// 外部联系人所在的群聊ID(如果群聊被成员标记为客户群才返回) + /// + [CanBeNull] + [JsonProperty("chat_id")] + [JsonPropertyName("chat_id")] + public string? ChatId { get; set; } + /// + /// 外部联系人所在群聊的群名(如果群聊未被成员标记为客户群才返回) + /// + [CanBeNull] + [JsonProperty("chat_name")] + [JsonPropertyName("chat_name")] + public string? ChatName { get; set; } + /// + /// 外部联系人首次添加/进群的时间 + /// + [NotNull] + [JsonProperty("add_time")] + [JsonPropertyName("add_time")] + public long AddTime { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Request/WeChatWorkGetExternalContactListRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Request/WeChatWorkGetExternalContactListRequest.cs new file mode 100644 index 000000000..de2874273 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Request/WeChatWorkGetExternalContactListRequest.cs @@ -0,0 +1,36 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts.Request; +/// +/// 获取已服务的外部联系人请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetExternalContactListRequest : WeChatWorkRequest +{ + /// + /// 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + /// + /// + /// cursor具有有效期,请勿缓存后使用 + /// + [CanBeNull] + [JsonProperty("cursor")] + [JsonPropertyName("cursor")] + public string? Cursor { get; } + /// + /// 返回的最大记录数,整型,默认为1000 + /// + [CanBeNull] + [JsonProperty("limit")] + [JsonPropertyName("limit")] + public int? Limit { get; } + public WeChatWorkGetExternalContactListRequest(string? cursor = null, int? limit = 1000) + { + Cursor = cursor; + Limit = limit; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Response/WeChatWorkGetExternalContactListResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Response/WeChatWorkGetExternalContactListResponse.cs new file mode 100644 index 000000000..4492b93ca --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/Response/WeChatWorkGetExternalContactListResponse.cs @@ -0,0 +1,29 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts.Response; +/// +/// 获取已服务的外部联系人响应结果 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetExternalContactListResponse : WeChatWorkResponse +{ + /// + /// 外部联系人列表 + /// + [NotNull] + [JsonProperty("info_list")] + [JsonPropertyName("info_list")] + public ExternalContactInfo[] InfoList { get; set; } + /// + /// 分页游标,再下次请求时填写以获取之后分页的记录,如果已经没有更多的数据则返回空,有效期为4小时 + /// + [CanBeNull] + [JsonProperty("next_cursor")] + [JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/WeChatWorkExternalContactProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/WeChatWorkExternalContactProvider.cs new file mode 100644 index 000000000..52179841b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Contacts/WeChatWorkExternalContactProvider.cs @@ -0,0 +1,44 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts.Response; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +using LINGYUN.Abp.WeChat.Work.Token; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Contacts; + +[RequiresFeature(WeChatWorkExternalContactFeatureNames.Enable)] +public class WeChatWorkExternalContactProvider : IWeChatWorkExternalContactProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkExternalContactProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task GetExternalContactListAsync( + WeChatWorkGetExternalContactListRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetExternalContactListAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/IWeChatWorkCustomerProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/IWeChatWorkCustomerProvider.cs new file mode 100644 index 000000000..caba3bf25 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/IWeChatWorkCustomerProvider.cs @@ -0,0 +1,62 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers; +/// +/// 客户管理接口 +/// +public interface IWeChatWorkCustomerProvider +{ + /// + /// 获取客户列表 + /// + /// + /// 详情见: + /// + /// 企业成员的userid + /// + /// + Task GetCustomerListAsync( + string userId, + CancellationToken cancellationToken = default); + /// + /// 批量获取客户详情 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task BulkGetCustomerAsync( + WeChatWorkBulkGetCustomerRequest request, + CancellationToken cancellationToken = default); + /// + /// 获取客户详情 + /// + /// + /// 详情见: + /// + /// 外部联系人的userid,注意不是企业成员的账号 + /// 上次请求返回的next_cursor + /// + /// + Task GetCustomerAsync( + string externalUserid, + string? cursor = null, + CancellationToken cancellationToken = default); + /// + /// 修改客户备注信息 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task UpdateCustomerRemarkAsync( + WeChatWorkUpdateCustomerRemarkRequest request, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/IWeChatWorkCustomerStrategyProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/IWeChatWorkCustomerStrategyProvider.cs new file mode 100644 index 000000000..c7dd5cead --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/IWeChatWorkCustomerStrategyProvider.cs @@ -0,0 +1,87 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers; +/// +/// 客户联系规则组管理 +/// +/// +/// 详情见: +/// +public interface IWeChatWorkCustomerStrategyProvider +{ + /// + /// 获取规则组列表 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task GetCustomerStrategyListAsync( + WeChatWorkGetCustomerStrategyListRequest request, + CancellationToken cancellationToken = default); + /// + /// 获取规则组 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task GetCustomerStrategyAsync( + WeChatWorkGetCustomerStrategyRequest request, + CancellationToken cancellationToken = default); + /// + /// 获取规则组管理范围 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task GetCustomerStrategyRangeAsync( + WeChatWorkGetCustomerStrategyRangeRequest request, + CancellationToken cancellationToken = default); + /// + /// 创建新的规则组 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task CreateCustomerStrategyAsync( + WeChatWorkCreateCustomerStrategyRequest request, + CancellationToken cancellationToken = default); + /// + /// 编辑规则组及其管理范围 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task UpdateCustomerStrategyAsync( + WeChatWorkUpdateCustomerStrategyRequest request, + CancellationToken cancellationToken = default); + /// + /// 删除规则组 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task DeleteCustomerStrategyAsync( + WeChatWorkDeleteCustomerStrategyRequest request, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategy.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategy.cs new file mode 100644 index 000000000..27b81293a --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategy.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 规则组 +/// +public class CustomerStrategy +{ + /// + /// 规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyInfo.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyInfo.cs new file mode 100644 index 000000000..dec0bdb1f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyInfo.cs @@ -0,0 +1,46 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 规则组详情 +/// +public class CustomerStrategyInfo : CustomerStrategy +{ + /// + /// 父规则组id, 如果当前规则组没父规则组,则为0 + /// + [NotNull] + [JsonProperty("parent_id")] + [JsonPropertyName("parent_id")] + public int ParentId { get; set; } + /// + /// 规则组名称 + /// + [NotNull] + [JsonProperty("strategy_name")] + [JsonPropertyName("strategy_name")] + public string StrategyName { get; set; } + /// + /// 规则组创建时间戳 + /// + [NotNull] + [JsonProperty("create_time")] + [JsonPropertyName("create_time")] + public long CreateTime { get; set; } + /// + /// 规则组管理员userid列表 + /// + [NotNull] + [JsonProperty("admin_list")] + [JsonPropertyName("admin_list")] + public string[] AdminList { get; set; } + /// + /// 规则组权限 + /// + [NotNull] + [JsonProperty("privilege")] + [JsonPropertyName("privilege")] + public CustomerStrategyPrivilege Privilege { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyPrivilege.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyPrivilege.cs new file mode 100644 index 000000000..03ce9e057 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyPrivilege.cs @@ -0,0 +1,183 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 规则组权限 +/// +public class CustomerStrategyPrivilege +{ + /// + /// 查看客户列表,基础权限,不可取消 + /// + [NotNull] + [JsonProperty("view_customer_list")] + [JsonPropertyName("view_customer_list")] + public bool ViewCustomerList { get; set; } + /// + /// 查看客户统计数据,基础权限,不可取消 + /// + [NotNull] + [JsonProperty("view_customer_data")] + [JsonPropertyName("view_customer_data")] + public bool ViewCustomerData { get; set; } + /// + /// 查看群聊列表,基础权限,不可取消 + /// + [NotNull] + [JsonProperty("view_room_list")] + [JsonPropertyName("view_room_list")] + public bool ViewRoomList { get; set; } + /// + /// 可使用联系我,基础权限,不可取消 + /// + [NotNull] + [JsonProperty("contact_me")] + [JsonPropertyName("contact_me")] + public bool ContactMe { get; set; } + /// + /// 可加入群聊,基础权限,不可取消 + /// + [NotNull] + [JsonProperty("join_room")] + [JsonPropertyName("join_room")] + public bool JoinRoom { get; set; } + /// + /// 允许分享客户给其他成员,默认为true + /// + [NotNull] + [JsonProperty("share_customer")] + [JsonPropertyName("share_customer")] + public bool ShareCustomer { get; set; } + /// + /// 允许分配离职成员客户,默认为true + /// + [NotNull] + [JsonProperty("oper_resign_customer")] + [JsonPropertyName("oper_resign_customer")] + public bool OperResignCustomer { get; set; } + /// + /// 允许分配离职成员客户群,默认为true + /// + [NotNull] + [JsonProperty("oper_resign_group")] + [JsonPropertyName("oper_resign_group")] + public bool OperResignGroup { get; set; } + /// + /// 允许给企业客户发送消息,默认为true + /// + [NotNull] + [JsonProperty("send_customer_msg")] + [JsonPropertyName("send_customer_msg")] + public bool SendCustomerMsg { get; set; } + /// + /// 允许配置欢迎语,默认为true + /// + [NotNull] + [JsonProperty("edit_welcome_msg")] + [JsonPropertyName("edit_welcome_msg")] + public bool EditWelcomeMsg { get; set; } + /// + /// 允许查看成员联系客户统计,默认为true + /// + [NotNull] + [JsonProperty("view_behavior_data")] + [JsonPropertyName("view_behavior_data")] + public bool ViewBehaviorData { get; set; } + /// + /// 允许查看群聊数据统计,默认为true + /// + [NotNull] + [JsonProperty("view_room_data")] + [JsonPropertyName("view_room_data")] + public bool ViewRoomData { get; set; } + /// + /// 允许发送消息到企业的客户群,默认为true + /// + [NotNull] + [JsonProperty("send_group_msg")] + [JsonPropertyName("send_group_msg")] + public bool SendGroupMsg { get; set; } + /// + /// 允许对企业客户群进行去重,默认为true + /// + [NotNull] + [JsonProperty("room_deduplication")] + [JsonPropertyName("room_deduplication")] + public bool RoomDeduplication { get; set; } + /// + /// 配置快捷回复,默认为true + /// + [NotNull] + [JsonProperty("rapid_reply")] + [JsonPropertyName("rapid_reply")] + public bool RapidReply { get; set; } + /// + /// 转接在职成员的客户,默认为true + /// + [NotNull] + [JsonProperty("onjob_customer_transfer")] + [JsonPropertyName("onjob_customer_transfer")] + public bool OnjobCustomerTransfer { get; set; } + /// + /// 编辑企业成员防骚扰规则,默认为true + /// + [NotNull] + [JsonProperty("edit_anti_spam_rule")] + [JsonPropertyName("edit_anti_spam_rule")] + public bool EditAntiSpamRule { get; set; } + /// + /// 导出客户列表,默认为true + /// + [NotNull] + [JsonProperty("export_customer_list")] + [JsonPropertyName("export_customer_list")] + public bool ExportCustomerList { get; set; } + /// + /// 导出成员客户统计,默认为true + /// + [NotNull] + [JsonProperty("export_customer_data")] + [JsonPropertyName("export_customer_data")] + public bool ExportCustomerData { get; set; } + /// + /// 导出客户群列表,默认为true + /// + [NotNull] + [JsonProperty("export_customer_group_list")] + [JsonPropertyName("export_customer_group_list")] + public bool ExportCustomerGroupList { get; set; } + /// + /// 配置企业客户标签,默认为true + /// + [NotNull] + [JsonProperty("manage_customer_tag")] + [JsonPropertyName("manage_customer_tag")] + public bool ManageCustomerTag { get; set; } + + public static CustomerStrategyPrivilege Default() + { + return new CustomerStrategyPrivilege + { + ViewCustomerList = true, + ViewCustomerData = true, + ViewRoomList = true, + ContactMe = true, + JoinRoom = true, + ShareCustomer = true, + EditWelcomeMsg = true, + ViewBehaviorData = true, + ViewRoomData = true, + SendGroupMsg = true, + RoomDeduplication = true, + RapidReply = true, + OnjobCustomerTransfer = true, + EditAntiSpamRule = true, + ExportCustomerList = true, + ExportCustomerData = true, + ExportCustomerGroupList = true, + ManageCustomerTag = true, + }; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyRange.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyRange.cs new file mode 100644 index 000000000..f02127f27 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyRange.cs @@ -0,0 +1,61 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 规则组管理范围 +/// +public class CustomerStrategyRange +{ + /// + /// 节点类型 + /// + [NotNull] + [JsonProperty("type")] + [JsonPropertyName("type")] + public CustomerStrategyRangeType Type { get; set; } + /// + /// 管理范围内配置的成员userid,仅 Type为 时返回 + /// + [CanBeNull] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public string? UserId { get; set; } + /// + /// 管理范围内配置的部门partyid,仅 Type为 时返回 + /// + [CanBeNull] + [JsonProperty("partyid")] + [JsonPropertyName("partyid")] + public int? PartyId { get; set; } + + public CustomerStrategyRange() + { + + } + + private CustomerStrategyRange( + CustomerStrategyRangeType type, + string? userId = null, + int? partyId = null) + { + Type = type; + UserId = userId; + PartyId = partyId; + } + + public static CustomerStrategyRange Member(string userId) + { + Check.NotNullOrWhiteSpace(userId, nameof(userId)); + + return new CustomerStrategyRange(CustomerStrategyRangeType.Member, userId); + } + public static CustomerStrategyRange Party(int partyId) + { + Check.NotDefaultOrNull(partyId, nameof(partyId)); + + return new CustomerStrategyRange(CustomerStrategyRangeType.Part, partyId: partyId); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyRangeType.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyRangeType.cs new file mode 100644 index 000000000..eb99edafd --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/CustomerStrategyRangeType.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 节点类型 +/// +[Description("节点类型")] +public enum CustomerStrategyRangeType +{ + /// + /// 成员 + /// + [Description("成员")] + Member = 1, + /// + /// 部门 + /// + [Description("部门")] + Part = 2 +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactGender.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactGender.cs new file mode 100644 index 000000000..609f42df1 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactGender.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 外部联系人性别 +/// +[Description("外部联系人性别")] +public enum ExternalContactGender +{ + /// + /// 未知 + /// + [Description("未知")] + None = 0, + /// + /// 男性 + /// + [Description("男性")] + Male = 1, + /// + /// 女性 + /// + [Description("女性")] + FeMale = 2, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactInfo.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactInfo.cs new file mode 100644 index 000000000..cf26d5659 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactInfo.cs @@ -0,0 +1,88 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 外部联系人信息 +/// +public class ExternalContactInfo +{ + /// + /// 外部联系人的userid + /// + [NotNull] + [JsonProperty("external_userid")] + [JsonPropertyName("external_userid")] + public string ExternalUserId { get; set; } + /// + /// 外部联系人的名称 + /// + /// + /// 如果是微信用户,则返回其微信昵称。 + /// 如果是企业微信联系人,则返回其设置对外展示的别名或实名 + /// + [NotNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string Name { get; set; } + /// + /// 外部联系人头像 + /// + [CanBeNull] + [JsonProperty("avatar")] + [JsonPropertyName("avatar")] + public string? Avatar { get; set; } + /// + /// 外部联系人的类型 + /// + [NotNull] + [JsonProperty("type")] + [JsonPropertyName("type")] + public ExternalContactType Type { get; set; } + /// + /// 外部联系人性别 + /// + [NotNull] + [JsonProperty("gender")] + [JsonPropertyName("gender")] + public ExternalContactGender Gender { get; set; } + /// + /// 外部联系人在微信开放平台的唯一身份标识(微信unionid),通过此字段企业可将外部联系人与公众号/小程序用户关联起来。 + /// 仅当联系人类型是微信用户,且企业绑定了微信开发者ID有此字段 + /// + [CanBeNull] + [JsonProperty("unionid")] + [JsonPropertyName("unionid")] + public string? UnionId { get; set; } + /// + /// 外部联系人的职位,如果外部企业或用户选择隐藏职位,则不返回,仅当联系人类型是企业微信用户时有此字段 + /// + [CanBeNull] + [JsonProperty("position")] + [JsonPropertyName("position")] + public string? Position { get; set; } + /// + /// 外部联系人所在企业的简称,仅当联系人类型是企业微信用户时有此字段 + /// + [CanBeNull] + [JsonProperty("corp_name")] + [JsonPropertyName("corp_name")] + public string? CorpName { get; set; } + /// + /// 外部联系人所在企业的主体名称,仅当联系人类型是企业微信用户时有此字段 + /// 仅企业自建应用可获取 + /// + [CanBeNull] + [JsonProperty("corp_full_name")] + [JsonPropertyName("corp_full_name")] + public string? CorpFullName { get; set; } + /// + /// 外部联系人的自定义展示信息 + /// + [CanBeNull] + [JsonProperty("external_profile")] + [JsonPropertyName("external_profile")] + public ExternalProfile? ExternalProfile { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactList.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactList.cs new file mode 100644 index 000000000..7ace31afb --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactList.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 外部联系人列表 +/// +public class ExternalContactList +{ + /// + /// 客户的基本信息 + /// + [NotNull] + [JsonProperty("external_contact")] + [JsonPropertyName("external_contact")] + public ExternalContactInfo ExternalContact { get; set; } + /// + /// 企业成员客户跟进信息 + /// + [NotNull] + [JsonProperty("follow_info")] + [JsonPropertyName("follow_info")] + public FollowUser FollowUser { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactType.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactType.cs new file mode 100644 index 000000000..7545230a7 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/ExternalContactType.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 外部联系人类型 +/// +[Description("外部联系人类型")] +public enum ExternalContactType +{ + /// + /// 微信用户 + /// + [Description("微信用户")] + WeChat = 1, + /// + /// 企业微信用户 + /// + [Description("企业微信用户")] + WeChatWork = 2, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUser.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUser.cs new file mode 100644 index 000000000..6225a3b76 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUser.cs @@ -0,0 +1,92 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 添加了外部联系人的企业成员 +/// +public class FollowUser +{ + /// + /// 添加了此外部联系人的企业成员userid + /// + [NotNull] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public string UserId { get; set; } + /// + /// 发起添加的userid + /// 如果成员主动添加,为成员的userid; + /// 如果是客户主动添加,则为客户的外部联系人userid; + /// 如果是内部成员共享/管理员分配,则为对应的成员/管理员userid + /// + [NotNull] + [JsonProperty("oper_userid")] + [JsonPropertyName("oper_userid")] + public string OperUserId { get; set; } + /// + /// 企业自定义的state参数,用于区分客户具体是通过哪个「联系我」或获客链接添加 + /// + [CanBeNull] + [JsonProperty("state")] + [JsonPropertyName("state")] + public string? State { get; set; } + /// + /// 该成员对此外部联系人的备注 + /// + [CanBeNull] + [JsonProperty("remark")] + [JsonPropertyName("remark")] + public string? Remark { get; set; } + /// + /// 该成员对此外部联系人的描述 + /// + [CanBeNull] + [JsonProperty("description")] + [JsonPropertyName("description")] + public string? Description { get; set; } + /// + /// 该成员添加此外部联系人的时间 + /// + [NotNull] + [JsonProperty("createtime")] + [JsonPropertyName("createtime")] + public long Createtime { get; set; } + /// + /// 外部联系人所打标签列表 + /// + [CanBeNull] + [JsonProperty("tags")] + [JsonPropertyName("tags")] + public List? Tags { get; set; } + /// + /// 该成员对此微信客户备注的企业名称(仅微信客户有该字段) + /// + [CanBeNull] + [JsonProperty("remark_corp_name")] + [JsonPropertyName("remark_corp_name")] + public string? RemarkCorpName { get; set; } + /// + /// 该成员对此客户备注的手机号码,代开发自建应用需要管理员授权才可以获取 + /// + [CanBeNull] + [JsonProperty("remark_mobiles")] + [JsonPropertyName("remark_mobiles")] + public List? RemarkMobiles { get; set; } + /// + /// 该成员添加此客户的来源 + /// + [NotNull] + [JsonProperty("add_way")] + [JsonPropertyName("add_way")] + public FollowUserAddWay AddWay { get; set; } + /// + /// 该成员添加此客户的来源add_way为10时,对应的视频号信息 + /// + [CanBeNull] + [JsonProperty("wechat_channels")] + [JsonPropertyName("wechat_channels")] + public FollowUserWechatChannel? WechatChannel { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserAddWay.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserAddWay.cs new file mode 100644 index 000000000..79caed25c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserAddWay.cs @@ -0,0 +1,125 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 客户来源 +/// +[Description("客户来源")] +public enum FollowUserAddWay +{ + /// + /// 未知来源 + /// + [Description("未知来源")] + None = 0, + /// + /// 扫描二维码 + /// + [Description("扫描二维码")] + ScanQrCode = 1, + /// + /// 搜索手机号 + /// + [Description("搜索手机号")] + SearchPhoneNumber = 2, + /// + /// 名片分享 + /// + [Description("名片分享")] + SharedCard = 3, + /// + /// 群聊 + /// + [Description("群聊")] + GroupChat = 4, + /// + /// 手机通讯录 + /// + [Description("手机通讯录")] + PhoneBook = 5, + /// + /// 微信联系人 + /// + [Description("微信联系人")] + WeChatContact = 6, + /// + /// 安装第三方应用时自动添加的客服人员 + /// + [Description("安装第三方应用时自动添加的客服人员")] + InstallThirdPartyApp = 8, + /// + /// 搜索邮箱 + /// + [Description("搜索邮箱")] + SearchEmail = 9, + /// + /// 视频号添加 + /// + [Description("视频号添加")] + WechatChannel = 10, + /// + /// 通过日程参与人添加 + /// + [Description("通过日程参与人添加")] + SchedulePart = 11, + /// + /// 通过会议参与人添加 + /// + [Description("通过会议参与人添加")] + MeetPart = 12, + /// + /// 添加微信好友对应的企业微信 + /// + [Description("添加微信好友对应的企业微信")] + WeChatFriend = 13, + /// + /// 通过智慧硬件专属客服添加 + /// + [Description("通过智慧硬件专属客服添加")] + SmartHardware = 14, + /// + /// 通过上门服务客服添加 + /// + [Description("通过上门服务客服添加")] + DoorService = 15, + /// + /// 通过获客链接添加 + /// + [Description("通过获客链接添加")] + CustomerAcqLink = 16, + /// + /// 通过定制开发添加 + /// + [Description("通过定制开发添加")] + CustomDevelopment = 17, + /// + /// 通过需求回复添加 + /// + [Description("通过需求回复添加")] + DemandResponse = 18, + /// + /// 通过第三方售前客服添加 + /// + [Description("通过第三方售前客服添加")] + ThirdPartyPreSales = 21, + /// + /// 通过可能的商务伙伴添加 + /// + [Description("通过可能的商务伙伴添加")] + PotentialBusPart = 22, + /// + /// 通过接受微信账号收到的好友申请添加 + /// + [Description("通过接受微信账号收到的好友申请添加")] + WeChatFriendRequest = 24, + /// + /// 内部成员共享 + /// + [Description("内部成员共享")] + SharedByInternalMembers = 201, + /// + /// 管理员/负责人分配 + /// + [Description("管理员/负责人分配")] + AllocationByAdmin = 202 +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserTag.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserTag.cs new file mode 100644 index 000000000..d11bfbd9c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserTag.cs @@ -0,0 +1,39 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 外部联系人所打标签 +/// +public class FollowUserTag +{ + /// + /// 该成员添加此外部联系人所打标签的分组名称 + /// + [CanBeNull] + [JsonProperty("group_name")] + [JsonPropertyName("group_name")] + public string? GroupName { get; set; } + /// + /// 该成员添加此外部联系人所打标签名称 + /// + [NotNull] + [JsonProperty("tag_name")] + [JsonPropertyName("tag_name")] + public string TagName { get; set; } + /// + /// 该成员添加此外部联系人所打标签类型 + /// + [NotNull] + [JsonProperty("type")] + [JsonPropertyName("type")] + public FollowUserTagType Type { get; set; } + /// + /// 该成员添加此外部联系人所打企业标签的id,用户自定义类型标签(type=2)不返回 + /// + [CanBeNull] + [JsonProperty("tag_id")] + [JsonPropertyName("tag_id")] + public string? TagId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserTagType.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserTagType.cs new file mode 100644 index 000000000..d8a91a4f6 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserTagType.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 标签类型 +/// +[Description("标签类型")] +public enum FollowUserTagType +{ + /// + /// 企业设置 + /// + [Description("企业设置")] + EnterpriseSettings = 1, + /// + /// 用户自定义 + /// + [Description("用户自定义")] + UserCustom = 2, + /// + /// 规则组标签 + /// + [Description("规则组标签")] + RuleGroupTags = 3, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserWechatChannel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserWechatChannel.cs new file mode 100644 index 000000000..b1a1b6b26 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserWechatChannel.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 视频号信息 +/// +public class FollowUserWechatChannel +{ + /// + /// 视频号名称 + /// + [NotNull] + [JsonProperty("nickname")] + [JsonPropertyName("nickname")] + public string NickName { get; set; } + /// + /// 视频号添加场景 + /// + [NotNull] + [JsonProperty("source")] + [JsonPropertyName("source")] + public FollowUserWechatChannelSource Source { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserWechatChannelSource.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserWechatChannelSource.cs new file mode 100644 index 000000000..fa8eb3433 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Models/FollowUserWechatChannelSource.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +/// +/// 视频号添加场景 +/// +[Description("视频号添加场景")] +public enum FollowUserWechatChannelSource +{ + /// + /// 未知 + /// + [Description("未知")] + None = 0, + /// + /// 视频号主页 + /// + [Description("视频号主页")] + Home = 1, + /// + /// 视频号直播间 + /// + [Description("视频号直播间")] + LiveRoom = 2, + /// + /// 视频号留资服务 + /// + [Description("视频号留资服务")] + RetentionService = 3 +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkBulkGetCustomerRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkBulkGetCustomerRequest.cs new file mode 100644 index 000000000..48b84ab1a --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkBulkGetCustomerRequest.cs @@ -0,0 +1,55 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +/// +/// 创建新的规则组请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkBulkGetCustomerRequest : WeChatWorkRequest +{ + /// + /// 企业成员的userid列表,字符串类型,最多支持100个 + /// + [NotNull] + [JsonProperty("userid_list")] + [JsonPropertyName("userid_list")] + public List UserIds { get; } + /// + /// 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + /// + [CanBeNull] + [JsonProperty("cursor")] + [JsonPropertyName("cursor")] + public string? Cursor { get; } + /// + /// 返回的最大记录数,整型,最大值100,默认值50,超过最大值时取最大值 + /// + [CanBeNull] + [JsonProperty("limit")] + [JsonPropertyName("limit")] + public int Limit { get; } + public WeChatWorkBulkGetCustomerRequest( + List userIds, + string? cursor = null, + int limit = 50) + { + Check.NotNullOrEmpty(userIds, nameof(userIds)); + Check.Range(limit, nameof(limit), 1, 100); + + if (userIds.Count > 100) + { + throw new ArgumentException("The maximum number of userIds allowed in the list is only 100!"); + } + + UserIds = userIds; + Cursor = cursor; + Limit = limit; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkCreateCustomerStrategyRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkCreateCustomerStrategyRequest.cs new file mode 100644 index 000000000..1fa6506cb --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkCreateCustomerStrategyRequest.cs @@ -0,0 +1,74 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +using Newtonsoft.Json; +using System; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +/// +/// 批量获取客户详情请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkCreateCustomerStrategyRequest : WeChatWorkRequest +{ + /// + /// 父规则组id + /// + [CanBeNull] + [JsonProperty("parent_id")] + [JsonPropertyName("parent_id")] + public int? ParentId { get; set; } + /// + /// 规则组名称 + /// + [NotNull] + [JsonProperty("strategy_name")] + [JsonPropertyName("strategy_name")] + public string StrategyName { get; } + /// + /// 规则组管理员userid列表 + /// + [NotNull] + [JsonProperty("admin_list")] + [JsonPropertyName("admin_list")] + public string[] AdminList { get; } + /// + /// 规则组权限 + /// + [NotNull] + [JsonProperty("privilege")] + [JsonPropertyName("privilege")] + public CustomerStrategyPrivilege Privilege { get; } + /// + /// 规则组管理范围 + /// + [NotNull] + [JsonProperty("range")] + [JsonPropertyName("range")] + public CustomerStrategyRange[] Range { get; } + public WeChatWorkCreateCustomerStrategyRequest( + string strategyName, + string[] adminList, + CustomerStrategyPrivilege? privilege = null, + CustomerStrategyRange[]? range = null) + { + Check.NotNullOrWhiteSpace(strategyName, nameof(strategyName)); + Check.NotNullOrEmpty(adminList, nameof(adminList)); + + if (adminList.Length > 20) + { + throw new ArgumentException("Up to 20 admin list can be configured at a time!"); + } + if (range != null && range.Length > 100) + { + throw new ArgumentException("Up to 100 management range can be configured at a time!"); + } + + StrategyName = strategyName; + AdminList = adminList; + Privilege = privilege ?? CustomerStrategyPrivilege.Default(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkDeleteCustomerStrategyRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkDeleteCustomerStrategyRequest.cs new file mode 100644 index 000000000..961e2fed6 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkDeleteCustomerStrategyRequest.cs @@ -0,0 +1,28 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +/// +/// 删除规则组请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkDeleteCustomerStrategyRequest : WeChatWorkRequest +{ + /// + /// 规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; } + public WeChatWorkDeleteCustomerStrategyRequest(int strategyId) + { + Check.NotDefaultOrNull(strategyId, nameof(strategyId)); + + StrategyId = strategyId; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyListRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyListRequest.cs new file mode 100644 index 000000000..1b5240c7b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyListRequest.cs @@ -0,0 +1,39 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +/// +/// 获取规则组列表请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCustomerStrategyListRequest : WeChatWorkRequest +{ + /// + /// 分页查询游标,首次调用可不填 + /// + [CanBeNull] + [JsonProperty("cursor")] + [JsonPropertyName("cursor")] + public string? Cursor { get; } + /// + /// 分页大小,默认为1000,最大不超过1000 + /// + [CanBeNull] + [JsonProperty("limit")] + [JsonPropertyName("limit")] + public int? Limit { get; } + public WeChatWorkGetCustomerStrategyListRequest(string? cursor = null, int? limit = 1000) + { + if (limit.HasValue) + { + Check.Range(limit.Value, nameof(limit), 1, 1000); + } + + Cursor = cursor; + Limit = limit; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyRangeRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyRangeRequest.cs new file mode 100644 index 000000000..415990088 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyRangeRequest.cs @@ -0,0 +1,47 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +/// +/// 获取规则组管理范围请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCustomerStrategyRangeRequest : WeChatWorkRequest +{ + /// + /// 规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; } + /// + /// 分页查询游标,首次调用可不填 + /// + [CanBeNull] + [JsonProperty("cursor")] + [JsonPropertyName("cursor")] + public string? Cursor { get; } + /// + /// 每个分页的成员/部门节点数,默认为1000,最大为1000 + /// + [CanBeNull] + [JsonProperty("limit")] + [JsonPropertyName("limit")] + public int? Limit { get; } + public WeChatWorkGetCustomerStrategyRangeRequest(int strategyId, string? cursor = null, int? limit = 1000) + { + if (limit.HasValue) + { + Check.Range(limit.Value, nameof(limit), 1, 1000); + } + + StrategyId = strategyId; + Cursor = cursor; + Limit = limit; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyRequest.cs new file mode 100644 index 000000000..1efbfb196 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkGetCustomerStrategyRequest.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +/// +/// 获取规则组请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCustomerStrategyRequest : WeChatWorkRequest +{ + /// + /// 规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; } + public WeChatWorkGetCustomerStrategyRequest(int strategyId) + { + StrategyId = strategyId; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkUpdateCustomerRemarkRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkUpdateCustomerRemarkRequest.cs new file mode 100644 index 000000000..9501a670d --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkUpdateCustomerRemarkRequest.cs @@ -0,0 +1,86 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +/// +/// 修改客户备注信息请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkUpdateCustomerRemarkRequest : WeChatWorkRequest +{ + /// + /// 企业成员的userid + /// + [NotNull] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public string UserId { get; } + /// + /// 外部联系人userid + /// + [NotNull] + [JsonProperty("external_userid")] + [JsonPropertyName("external_userid")] + public string ExternalUserId { get; } + /// + /// 此用户对外部联系人的备注,最多20个字符 + /// + [CanBeNull] + [StringLength(20)] + [JsonProperty("remark")] + [JsonPropertyName("remark")] + public string? Remark { get; } + /// + /// 此用户对外部联系人的描述,最多150个字符 + /// + [CanBeNull] + [StringLength(150)] + [JsonProperty("description")] + [JsonPropertyName("description")] + public string? Description { get; } + /// + /// 此用户对外部联系人备注的所属公司名称,最多20个字符 + /// + [CanBeNull] + [StringLength(20)] + [JsonProperty("remark_company")] + [JsonPropertyName("remark_company")] + public string? RemarkCompany { get; } + /// + /// 此用户对外部联系人备注的手机号 + /// + [CanBeNull] + [JsonProperty("remark_mobiles")] + [JsonPropertyName("remark_mobiles")] + public List? RemarkMobiles { get; } + /// + /// 备注图片的mediaid + /// + [CanBeNull] + [JsonProperty("remark_pic_mediaid")] + [JsonPropertyName("remark_pic_mediaid")] + public string? RemarkPictureMediaId { get; } + public WeChatWorkUpdateCustomerRemarkRequest( + string userId, + string externalUserId, + string? remark = null, + string? description = null, + string? remarkCompany = null, + List? remarkMobiles = null, + string? remarkPictureMediaId = null) + { + UserId = Check.NotNullOrWhiteSpace(userId, nameof(userId)); + ExternalUserId = Check.NotNullOrWhiteSpace(externalUserId, nameof(externalUserId)); + Remark = Check.Length(remark, nameof(remark), 20); + Description = Check.Length(description, nameof(description), 150); + RemarkCompany = Check.Length(remarkCompany, nameof(remarkCompany), 20); + RemarkMobiles = remarkMobiles; + RemarkPictureMediaId = remarkPictureMediaId; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkUpdateCustomerStrategyRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkUpdateCustomerStrategyRequest.cs new file mode 100644 index 000000000..5167d3130 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Request/WeChatWorkUpdateCustomerStrategyRequest.cs @@ -0,0 +1,75 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +/// +/// 编辑规则组及其管理范围请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkUpdateCustomerStrategyRequest : WeChatWorkRequest +{ + /// + /// 规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; } + /// + /// 规则组名称 + /// + [CanBeNull] + [JsonProperty("strategy_name")] + [JsonPropertyName("strategy_name")] + public string? StrategyName { get; set; } + /// + /// 规则组管理员userid列表 + /// + [CanBeNull] + [JsonProperty("admin_list")] + [JsonPropertyName("admin_list")] + public string[]? AdminList { get; set; } + /// + /// 规则组权限 + /// + [CanBeNull] + [JsonProperty("privilege")] + [JsonPropertyName("privilege")] + public CustomerStrategyPrivilege? Privilege { get; set; } + /// + /// 新增管理范围 + /// + [NotNull] + [JsonProperty("range_add")] + [JsonPropertyName("range_add")] + public List CreateRange { get; private set; } + /// + /// 删除管理范围 + /// + [NotNull] + [JsonProperty("range_del")] + [JsonPropertyName("range_del")] + public List DeleteRange { get; private set; } + public WeChatWorkUpdateCustomerStrategyRequest( + int strategyId, + string? strategyName = null, + string[]? adminList = null, + CustomerStrategyPrivilege? privilege = null) + { + Check.NotDefaultOrNull(strategyId, nameof(strategyId)); + + StrategyId = strategyId; + StrategyName = strategyName; + AdminList = adminList; + Privilege = privilege; + + CreateRange = new List(); + DeleteRange = new List(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkBulkGetCustomerResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkBulkGetCustomerResponse.cs new file mode 100644 index 000000000..75780677a --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkBulkGetCustomerResponse.cs @@ -0,0 +1,48 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +/// +/// 批量获取客户详情响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkBulkGetCustomerResponse : WeChatWorkResponse +{ + /// + /// 外部联系人的userid列表 + /// + [NotNull] + [JsonProperty("external_contact_list")] + [JsonPropertyName("external_contact_list")] + public List ExternalUserId { get; set; } = new List(); + /// + /// 分页游标,再下次请求时填写以获取之后分页的记录,如果已经没有更多的数据则返回空 + /// + [CanBeNull] + [JsonProperty("next_cursor")] + [JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } + /// + /// 若请求中所有userid都无有效互通许可,接口直接报错701008。如果部分userid无有效互通许可,接口返回成功 + /// + [CanBeNull] + [JsonProperty("fail_info")] + [JsonPropertyName("fail_info")] + public WeChatWorkBulkGetCustomerFailInfo? FailInfo { get; set; } +} + +public class WeChatWorkBulkGetCustomerFailInfo +{ + /// + /// 无许可的userid列表 + /// + [NotNull] + [JsonProperty("next_cursor")] + [JsonPropertyName("next_cursor")] + public List UnlicensedUseridList { get; set; } = new List(); +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkCreateCustomerStrategyResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkCreateCustomerStrategyResponse.cs new file mode 100644 index 000000000..f6aec2f00 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkCreateCustomerStrategyResponse.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +/// +/// 创建新的规则组响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkCreateCustomerStrategyResponse : WeChatWorkResponse +{ + /// + /// 规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerListResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerListResponse.cs new file mode 100644 index 000000000..4f16db664 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerListResponse.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +/// +/// 获取客户列表响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCustomerListResponse : WeChatWorkResponse +{ + /// + /// 外部联系人的userid列表 + /// + [NotNull] + [JsonProperty("external_userid")] + [JsonPropertyName("external_userid")] + public List ExternalUserId { get; set; } = new List(); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerResponse.cs new file mode 100644 index 000000000..d465abe38 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerResponse.cs @@ -0,0 +1,37 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +/// +/// 获取客户详情响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCustomerResponse : WeChatWorkResponse +{ + /// + /// 外部联系人信息 + /// + [NotNull] + [JsonProperty("external_contact")] + [JsonPropertyName("external_contact")] + public ExternalContactInfo ExternalContact { get; set; } + /// + /// 添加了此外部联系人的企业成员 + /// + [NotNull] + [JsonProperty("follow_user")] + [JsonPropertyName("follow_user")] + public List FollowUser { get; set; } + /// + /// 分页的cursor,当跟进人多于500人时返回 + /// + [CanBeNull] + [JsonProperty("next_cursor")] + [JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyListResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyListResponse.cs new file mode 100644 index 000000000..b386da41c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyListResponse.cs @@ -0,0 +1,29 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +/// +/// 获取规则组列表响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCustomerStrategyListResponse : WeChatWorkResponse +{ + /// + /// 规则组列表 + /// + [NotNull] + [JsonProperty("strategy")] + [JsonPropertyName("strategy")] + public CustomerStrategy[] Strategy { get; set; } + /// + /// 分页游标,用于查询下一个分页的数据,无更多数据时不返回 + /// + [CanBeNull] + [JsonProperty("next_cursor")] + [JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyRangeResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyRangeResponse.cs new file mode 100644 index 000000000..0c7c715cd --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyRangeResponse.cs @@ -0,0 +1,30 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +/// +/// 获取规则组管理范围响应参数 +/// +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCustomerStrategyRangeResponse : WeChatWorkResponse +{ + /// + /// 规则组管理范围 + /// + [NotNull] + [JsonProperty("range")] + [JsonPropertyName("range")] + public CustomerStrategyRange[] Range { get; set; } + /// + /// 分页游标,用于查询下一个分页的数据,无更多数据时不返回 + /// + [CanBeNull] + [JsonProperty("next_cursor")] + [JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyResponse.cs new file mode 100644 index 000000000..0b3eba400 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/Response/WeChatWorkGetCustomerStrategyResponse.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +/// +/// 获取规则组详情响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCustomerStrategyResponse : WeChatWorkResponse +{ + /// + /// 规则组详情 + /// + [NotNull] + [JsonProperty("strategy")] + [JsonPropertyName("strategy")] + public CustomerStrategyInfo Strategy { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/WeChatWorkCustomerProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/WeChatWorkCustomerProvider.cs new file mode 100644 index 000000000..eb242ed94 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/WeChatWorkCustomerProvider.cs @@ -0,0 +1,93 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +using LINGYUN.Abp.WeChat.Work.Token; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers; + +[RequiresFeature(WeChatWorkExternalContactFeatureNames.Enable)] +public class WeChatWorkCustomerProvider : IWeChatWorkCustomerProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkCustomerProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task GetCustomerListAsync( + string userId, + CancellationToken cancellationToken = default) + { + Check.NotNullOrWhiteSpace(userId, nameof(userId)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetCustomerListAsync(token.AccessToken, userId, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task BulkGetCustomerAsync( + WeChatWorkBulkGetCustomerRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.BulkGetCustomerAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task GetCustomerAsync( + string externalUserid, + string? cursor = null, + CancellationToken cancellationToken = default) + { + Check.NotNullOrWhiteSpace(externalUserid, nameof(externalUserid)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetCustomerAsync(token.AccessToken, externalUserid, cursor, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task UpdateCustomerRemarkAsync( + WeChatWorkUpdateCustomerRemarkRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.UpdateCustomerRemarkAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/WeChatWorkCustomerStrategyProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/WeChatWorkCustomerStrategyProvider.cs new file mode 100644 index 000000000..f2984bb5f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Customers/WeChatWorkCustomerStrategyProvider.cs @@ -0,0 +1,111 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Customers.Response; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +using LINGYUN.Abp.WeChat.Work.Token; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Customers; + +[RequiresFeature(WeChatWorkExternalContactFeatureNames.Enable)] +public class WeChatWorkCustomerStrategyProvider : IWeChatWorkCustomerStrategyProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkCustomerStrategyProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task GetCustomerStrategyListAsync( + WeChatWorkGetCustomerStrategyListRequest request, + CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetCustomerStrategyListAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task GetCustomerStrategyAsync( + WeChatWorkGetCustomerStrategyRequest request, + CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetCustomerStrategyAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task GetCustomerStrategyRangeAsync( + WeChatWorkGetCustomerStrategyRangeRequest request, + CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetCustomerStrategyRangeAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task CreateCustomerStrategyAsync( + WeChatWorkCreateCustomerStrategyRequest request, + CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.CreateCustomerStrategyAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task UpdateCustomerStrategyAsync( + WeChatWorkUpdateCustomerStrategyRequest request, + CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.UpdateCustomerStrategyAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task DeleteCustomerStrategyAsync( + WeChatWorkDeleteCustomerStrategyRequest request, + CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.DeleteCustomerStrategyAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Features/WeChatWorkExternalContactFeatureDefinitionProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Features/WeChatWorkExternalContactFeatureDefinitionProvider.cs new file mode 100644 index 000000000..d516615eb --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Features/WeChatWorkExternalContactFeatureDefinitionProvider.cs @@ -0,0 +1,31 @@ +using LINGYUN.Abp.WeChat.Work.Features; +using LINGYUN.Abp.WeChat.Work.Localization; +using Volo.Abp.Features; +using Volo.Abp.Localization; +using Volo.Abp.Validation.StringValues; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +public class WeChatWorkExternalContactFeatureDefinitionProvider : FeatureDefinitionProvider +{ + public override void Define(IFeatureDefinitionContext context) + { + var weChatFeature = context.GetGroupOrNull(WeChatWorkFeatureNames.GroupName); + if (weChatFeature == null) + { + return; + } + + var group = weChatFeature.AddFeature(WeChatWorkExternalContactFeatureNames.GroupName); + group.CreateChild( + WeChatWorkExternalContactFeatureNames.Enable, + defaultValue: "false", + displayName: L("Features:ExternalContactEnable"), + description: L("Features:ExternalContactEnableDesc"), + valueType: new ToggleStringValueType(new BooleanValueValidator())); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Features/WeChatWorkExternalContactFeatureNames.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Features/WeChatWorkExternalContactFeatureNames.cs new file mode 100644 index 000000000..9a9a1b259 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Features/WeChatWorkExternalContactFeatureNames.cs @@ -0,0 +1,14 @@ +using LINGYUN.Abp.WeChat.Work.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +/// +/// 企业微信客户联系模块功能 +/// +public static class WeChatWorkExternalContactFeatureNames +{ + public const string GroupName = WeChatWorkFeatureNames.GroupName + ".ExternalContact"; + /// + /// 启用企业微信客户联系 + /// + public const string Enable = GroupName + ".Enable"; +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/IWeChatWorkFollowUserProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/IWeChatWorkFollowUserProvider.cs new file mode 100644 index 000000000..5772d7a9e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/IWeChatWorkFollowUserProvider.cs @@ -0,0 +1,20 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Follows.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Follows; +/// +/// 企业服务人员管理接口 +/// +public interface IWeChatWorkFollowUserProvider +{ + /// + /// 获取配置了客户联系功能的成员列表 + /// + /// + /// 详情见: + /// + /// + /// + Task GetFollowUserListAsync(CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/Response/WeChatWorkGetFollowUserListResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/Response/WeChatWorkGetFollowUserListResponse.cs new file mode 100644 index 000000000..ff0c5d728 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/Response/WeChatWorkGetFollowUserListResponse.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Follows.Response; +/// +/// 获取配置了客户联系功能的成员列表响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetFollowUserListResponse : WeChatWorkResponse +{ + /// + /// 配置了客户联系功能的成员userid列表 + /// + [NotNull] + [JsonProperty("follow_user")] + [JsonPropertyName("follow_user")] + public List FollowUser { get; set; } = new List(); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/WeChatWorkFollowUserProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/WeChatWorkFollowUserProvider.cs new file mode 100644 index 000000000..0391d6b7e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Follows/WeChatWorkFollowUserProvider.cs @@ -0,0 +1,38 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Follows.Response; +using LINGYUN.Abp.WeChat.Work.Token; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Follows; + +[RequiresFeature(WeChatWorkExternalContactFeatureNames.Enable)] +public class WeChatWorkFollowUserProvider : IWeChatWorkFollowUserProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkFollowUserProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task GetFollowUserListAsync(CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetFollowUserListAsync(token.AccessToken, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/IWeChatWorkGroupChatProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/IWeChatWorkGroupChatProvider.cs new file mode 100644 index 000000000..d0cf7d078 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/IWeChatWorkGroupChatProvider.cs @@ -0,0 +1,48 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats; +/// +/// 客户群管理接口 +/// +public interface IWeChatWorkGroupChatProvider +{ + /// + /// 获取客户群列表 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task GetGroupChatListAsync( + WeChatWorkGetGroupChatListRequest request, + CancellationToken cancellationToken = default); + /// + /// 获取客户群详情 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task GetGroupChatAsync( + WeChatWorkGetGroupChatRequest request, + CancellationToken cancellationToken = default); + /// + /// 客户群opengid转换 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task OpengIdToChatIdAsync( + WeChatWorkOpengIdToChatIdRequest request, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChat.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChat.cs new file mode 100644 index 000000000..e9d6f34bb --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChat.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 客户群 +/// +public class GroupChat +{ + /// + /// 客户群ID + /// + [NotNull] + [JsonProperty("chat_id")] + [JsonPropertyName("chat_id")] + public string ChatId { get; set; } + /// + /// 客户群跟进状态 + /// + [NotNull] + [JsonProperty("status")] + [JsonPropertyName("status")] + public GroupChatStatus Status { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatInfo.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatInfo.cs new file mode 100644 index 000000000..84c173240 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatInfo.cs @@ -0,0 +1,67 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 客户群详情 +/// +public class GroupChatInfo +{ + /// + /// 客户群ID + /// + [NotNull] + [JsonProperty("chat_id")] + [JsonPropertyName("chat_id")] + public string ChatId { get; set; } + /// + /// 群名 + /// + [NotNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string Name { get; set; } + /// + /// 群主ID + /// + [NotNull] + [JsonProperty("owner")] + [JsonPropertyName("owner")] + public string Owner { get; set; } + /// + /// 群的创建时间 + /// + [NotNull] + [JsonProperty("create_time")] + [JsonPropertyName("create_time")] + public long CreateTime { get; set; } + /// + /// 群公告 + /// + [CanBeNull] + [JsonProperty("notice")] + [JsonPropertyName("notice")] + public string Notice { get; set; } + /// + /// 群成员列表 + /// + [NotNull] + [JsonProperty("member_list")] + [JsonPropertyName("member_list")] + public GroupChatMember[] MemberList { get; set; } + /// + /// 群管理员列表 + /// + [NotNull] + [JsonProperty("admin_list")] + [JsonPropertyName("admin_list")] + public GroupChatManager[] AdminList { get; set; } + /// + /// 当前群成员版本号。可以配合客户群变更事件减少主动调用本接口的次数 + /// + [NotNull] + [JsonProperty("member_version")] + [JsonPropertyName("member_version")] + public string MemberVersion { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatInvitor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatInvitor.cs new file mode 100644 index 000000000..201decd88 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatInvitor.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 邀请者 +/// +public class GroupChatInvitor +{ + /// + /// 邀请者的userid + /// + [NotNull] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public string UserId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatManager.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatManager.cs new file mode 100644 index 000000000..1a53a0781 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatManager.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 群管理员 +/// +public class GroupChatManager +{ + /// + /// 群管理员userid + /// + [NotNull] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public string UserId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMember.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMember.cs new file mode 100644 index 000000000..937f07841 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMember.cs @@ -0,0 +1,70 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 群成员 +/// +public class GroupChatMember +{ + /// + /// 群成员id + /// + [NotNull] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public string UserId { get; set; } + /// + /// 成员类型 + /// + [NotNull] + [JsonProperty("type")] + [JsonPropertyName("type")] + public GroupChatMemberType Type { get; set; } + /// + /// 外部联系人在微信开放平台的唯一身份标识(微信unionid),通过此字段企业可将外部联系人与公众号/小程序用户关联起来。 + /// 仅当群成员类型是微信用户(包括企业成员未添加好友),且企业绑定了微信开发者ID有此字段 + /// + [CanBeNull] + [JsonProperty("unionid")] + [JsonPropertyName("unionid")] + public string? UnionId { get; set; } + /// + /// 入群时间 + /// + [NotNull] + [JsonProperty("join_time")] + [JsonPropertyName("join_time")] + public long JoinTime { get; set; } + /// + /// 入群方式 + /// + [NotNull] + [JsonProperty("join_scene")] + [JsonPropertyName("join_scene")] + public GroupChatMemberJoinScene JoinScene { get; set; } + /// + /// 邀请者。目前仅当是由本企业内部成员邀请入群时会返回该值 + /// + [CanBeNull] + [JsonProperty("invitor")] + [JsonPropertyName("invitor")] + public GroupChatInvitor? Invitor { get; set; } + /// + /// 在群里的昵称 + /// + [CanBeNull] + [JsonProperty("group_nickname")] + [JsonPropertyName("group_nickname")] + public string? GroupNickname { get; set; } + /// + /// 名字。仅当 need_name = 1 时返回 + /// 如果是微信用户,则返回其在微信中设置的名字 + /// 如果是企业微信联系人,则返回其设置对外展示的别名或实名 + /// + [CanBeNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string? Name { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMemberJoinScene.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMemberJoinScene.cs new file mode 100644 index 000000000..8163b711c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMemberJoinScene.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 入群方式 +/// +[Description("入群方式")] +public enum GroupChatMemberJoinScene +{ + /// + /// 由群成员邀请入群(直接邀请入群) + /// + [Description("直接邀请入群")] + DirectInvitation = 1, + /// + /// 由群成员邀请入群(通过邀请链接入群) + /// + [Description("通过邀请链接入群")] + InvitationLink = 2, + /// + /// 通过扫描群二维码入群 + /// + [Description("通过扫描群二维码入群")] + ScanQrCode = 3, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMemberType.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMemberType.cs new file mode 100644 index 000000000..f8f4a6219 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatMemberType.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 成员类型 +/// +[Description("成员类型")] +public enum GroupChatMemberType +{ + /// + /// 企业成员 + /// + [Description("企业成员")] + Internal = 1, + /// + /// 外部联系人 + /// + [Description("外部联系人")] + External = 2 +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatStatus.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatStatus.cs new file mode 100644 index 000000000..5393313cd --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/GroupChatStatus.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 客户群跟进状态 +/// +[Description("客户群跟进状态")] +public enum GroupChatStatus +{ + /// + /// 跟进人正常 + /// + [Description("跟进人正常")] + Normal = 0, + /// + /// 跟进人离职 + /// + [Description("跟进人离职")] + Leaves = 1, + /// + /// 离职继承中 + /// + [Description("离职继承中")] + Resiging = 2, + /// + /// 离职继承完成 + /// + [Description(" 离职继承完成")] + ResignCompleted = 3, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/OwnerFilter.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/OwnerFilter.cs new file mode 100644 index 000000000..bd0bc24c8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/OwnerFilter.cs @@ -0,0 +1,31 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 群主过滤 +/// +public class OwnerFilter +{ + /// + /// 用户ID列表。最多100个 + /// + [NotNull] + [JsonProperty("userid_list")] + [JsonPropertyName("userid_list")] + public string[] UserIdList { get; } + public OwnerFilter(string[] userIdList) + { + Check.NotNullOrEmpty(userIdList, nameof(userIdList)); + + if (userIdList.Length > 100) + { + throw new ArgumentException("The maximum number of parameters allowed for group owner filtering is only 100!"); + } + + UserIdList = userIdList; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/StatusFilter.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/StatusFilter.cs new file mode 100644 index 000000000..e4d78e910 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Models/StatusFilter.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +/// +/// 客户群跟进状态过滤 +/// +[Description("客户群跟进状态过滤")] +public enum StatusFilter +{ + /// + /// 所有列表 + /// + [Description("所有列表")] + All = 0, + /// + /// 跟进人离职 + /// + [Description("跟进人离职")] + Leaves = 1, + /// + /// 离职继承中 + /// + [Description("离职继承中")] + Resiging = 2, + /// + /// 离职继承完成 + /// + [Description(" 离职继承完成")] + ResignCompleted = 3, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkGetGroupChatListRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkGetGroupChatListRequest.cs new file mode 100644 index 000000000..5c86a3e9d --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkGetGroupChatListRequest.cs @@ -0,0 +1,50 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Request; +/// +/// 获取客户群列表请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetGroupChatListRequest : WeChatWorkRequest +{ + /// + /// 客户群跟进状态过滤 + /// + [CanBeNull] + [JsonProperty("status_filter")] + [JsonPropertyName("status_filter")] + public StatusFilter? StatusFilter { get; set; } + /// + /// 客户群跟进状态过滤 + /// + [CanBeNull] + [JsonProperty("owner_filter")] + [JsonPropertyName("owner_filter")] + public OwnerFilter[]? OwnerFilter { get; set; } + /// + /// 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用不填 + /// + [CanBeNull] + [JsonProperty("cursor")] + [JsonPropertyName("cursor")] + public string? Cursor { get; set; } + /// + /// 分页,预期请求的数据量,取值范围 1 ~ 1000 + /// + [CanBeNull] + [JsonProperty("limit")] + [JsonPropertyName("limit")] + public int Limit { get; } + public WeChatWorkGetGroupChatListRequest(int limit = 1000) + { + Check.Range(limit, nameof(limit), 1, 1000); + + Limit = limit; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkGetGroupChatRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkGetGroupChatRequest.cs new file mode 100644 index 000000000..4949e0d87 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkGetGroupChatRequest.cs @@ -0,0 +1,36 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Request; +/// +/// 获取客户群详情 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetGroupChatRequest : WeChatWorkRequest +{ + /// + /// 客户群ID + /// + [NotNull] + [JsonProperty("chat_id")] + [JsonPropertyName("chat_id")] + public string ChatId { get; } + /// + /// 是否需要返回群成员的名字group_chat.member_list.name。0-不返回;1-返回。默认不返回 + /// + [CanBeNull] + [JsonProperty("need_name")] + [JsonPropertyName("need_name")] + public int? NeedName { get; } + public WeChatWorkGetGroupChatRequest(string chatId, bool needName = false) + { + Check.NotNullOrWhiteSpace(chatId, nameof(chatId)); + + ChatId = chatId; + NeedName = needName ? 1 : 0; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkOpengIdToChatIdRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkOpengIdToChatIdRequest.cs new file mode 100644 index 000000000..0c3343a3f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Request/WeChatWorkOpengIdToChatIdRequest.cs @@ -0,0 +1,28 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Request; +/// +/// 客户群opengid转换请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkOpengIdToChatIdRequest : WeChatWorkRequest +{ + /// + /// 小程序在微信获取到的群ID + /// + [NotNull] + [JsonProperty("opengid")] + [JsonPropertyName("opengid")] + public string OpengId { get; } + public WeChatWorkOpengIdToChatIdRequest(string opengId) + { + Check.NotNullOrWhiteSpace(opengId, nameof(opengId)); + + OpengId = opengId; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkGetGroupChatListResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkGetGroupChatListResponse.cs new file mode 100644 index 000000000..1a0d2b81e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkGetGroupChatListResponse.cs @@ -0,0 +1,29 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Response; +/// +/// 获取客户群列表响应结果 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetGroupChatListResponse : WeChatWorkResponse +{ + /// + /// 客户群列表 + /// + [NotNull] + [JsonProperty("group_chat_list")] + [JsonPropertyName("group_chat_list")] + public GroupChat[] GroupChatList { get; set; } + /// + /// 分页游标,下次请求时填写以获取之后分页的记录。如果该字段返回空则表示已没有更多数据 + /// + [CanBeNull] + [JsonProperty("next_cursor")] + [JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkGetGroupChatResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkGetGroupChatResponse.cs new file mode 100644 index 000000000..8f58bbfb3 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkGetGroupChatResponse.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Response; +/// +/// 获取客户群详情响应结果 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetGroupChatResponse : WeChatWorkResponse +{ + /// + /// 客户群详情 + /// + [NotNull] + [JsonProperty("group_chat")] + [JsonPropertyName("group_chat")] + public GroupChatInfo GroupChat { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkOpengIdToChatIdResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkOpengIdToChatIdResponse.cs new file mode 100644 index 000000000..ab1b981d5 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/Response/WeChatWorkOpengIdToChatIdResponse.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Response; +/// +/// 客户群opengid转换响应结果 +/// +/// +/// 详情见: +/// +public class WeChatWorkOpengIdToChatIdResponse : WeChatWorkResponse +{ + /// + /// 客户群ID,可以用来调用获取客户群详情 + /// + [NotNull] + [JsonProperty("chat_id")] + [JsonPropertyName("chat_id")] + public string ChatId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/WeChatWorkGroupChatProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/WeChatWorkGroupChatProvider.cs new file mode 100644 index 000000000..6600d08a9 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/GroupChats/WeChatWorkGroupChatProvider.cs @@ -0,0 +1,76 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +using LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats.Response; +using LINGYUN.Abp.WeChat.Work.Token; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.GroupChats; + +[RequiresFeature(WeChatWorkExternalContactFeatureNames.Enable)] +public class WeChatWorkGroupChatProvider : IWeChatWorkGroupChatProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkGroupChatProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task GetGroupChatListAsync( + WeChatWorkGetGroupChatListRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetGroupChatListAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task GetGroupChatAsync( + WeChatWorkGetGroupChatRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetGroupChatAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task OpengIdToChatIdAsync( + WeChatWorkOpengIdToChatIdRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.OpengIdToChatIdAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Localization/Resources/en.json b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Localization/Resources/en.json new file mode 100644 index 000000000..555a57daa --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Localization/Resources/en.json @@ -0,0 +1,7 @@ +{ + "culture": "en", + "texts": { + "Features:ExternalContactEnable": "Enable External Contact", + "Features:ExternalContactEnableDesc": "Enable the ability to provide the application with an Enterprise wechat customer contact interface." + } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Localization/Resources/zh-Hans.json b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Localization/Resources/zh-Hans.json new file mode 100644 index 000000000..038e57240 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Localization/Resources/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "culture": "zh-Hans", + "texts": { + "Features:ExternalContactEnable": "启用客户联系", + "Features:ExternalContactEnableDesc": "启用以使应用拥有企业微信客户联系接口的能力." + } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChaDelMemberEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChaDelMemberEvent.cs new file mode 100644 index 000000000..c5ff046e8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChaDelMemberEvent.cs @@ -0,0 +1,25 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群成员退群事件推送 +/// +[EventName("external_chat_del_member")] +public class ExternalChaDelMemberEvent : ExternalChatChangeMemberEvent +{ + /// + /// 当是成员退群时有值。表示成员的退群方式 + /// 0 - 自己退群 + /// 1 - 群主/群管理员移出 + /// + [XmlElement("QuitScene")] + public ExternalChatMemberQuitScene QuitScene { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatAddMemberEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatAddMemberEvent.cs new file mode 100644 index 000000000..ebd36da90 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatAddMemberEvent.cs @@ -0,0 +1,25 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群成员入群事件推送 +/// +[EventName("external_chat_add_member")] +public class ExternalChatAddMemberEvent : ExternalChatChangeMemberEvent +{ + /// + /// 当是成员入群时有值。表示成员的入群方式 + /// 0 - 由成员邀请入群(包括直接邀请入群和通过邀请链接入群) + /// 3 - 通过扫描群二维码入群 + /// + [XmlElement("JoinScene")] + public ExternalChatMemberJoinScene JoinScene { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeEvent.cs new file mode 100644 index 000000000..9e7ab1eb6 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeEvent.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群变更事件推送 +/// +public abstract class ExternalChatChangeEvent : WeChatWorkEventMessage +{ + /// + /// 变更类型 + /// + [XmlElement("ChangeType")] + public string ChangeType { get; set; } + /// + /// 群ID + /// + [XmlElement("ChatId")] + public string ChatId { get; set; } +} + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeMemberEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeMemberEvent.cs new file mode 100644 index 000000000..b64e10b84 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeMemberEvent.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群成员变更事件推送 +/// +public abstract class ExternalChatChangeMemberEvent : ExternalChatUpdateEvent +{ + /// + /// 成员变更数量 + /// + [XmlElement("MemChangeCnt")] + public int MemChangeCnt { get; set; } + /// + /// 变更的成员列表 + /// + [XmlElement("MemChangeList")] + public List MemChangeList { get; set; } = new List(); + /// + /// 变更前的群成员版本号 + /// + [XmlElement("LastMemVer")] + public string LastMemVer { get; set; } + /// + /// 变更后的群成员版本号 + /// + [XmlElement("CurMemVer")] + public string CurMemVer { get; set; } +} + +public class ExternalChatChangeMember +{ + /// + /// 成员Id + /// + [XmlElement("Item")] + public string UserId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeNameEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeNameEvent.cs new file mode 100644 index 000000000..e3480f2a7 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeNameEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群群名变更事件推送 +/// +[EventName("external_chat_change_name")] +public class ExternalChatChangeNameEvent : ExternalChatUpdateEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeNoticeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeNoticeEvent.cs new file mode 100644 index 000000000..ec7d5d485 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeNoticeEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群群公告变更事件推送 +/// +[EventName("external_chat_change_notice")] +public class ExternalChatChangeNoticeEvent : ExternalChatUpdateEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeOwnerEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeOwnerEvent.cs new file mode 100644 index 000000000..ad955f907 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatChangeOwnerEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群群主变更事件推送 +/// +[EventName("external_chat_change_owner")] +public class ExternalChatChangeOwnerEvent : ExternalChatUpdateEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatCreateEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatCreateEvent.cs new file mode 100644 index 000000000..417082072 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatCreateEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群创建事件推送 +/// +[EventName("external_chat_create")] +public class ExternalChatCreateEvent : ExternalChatChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatDismissEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatDismissEvent.cs new file mode 100644 index 000000000..e3fa20623 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatDismissEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群解散事件推送 +/// +[EventName("external_chat_dismiss")] +public class ExternalChatDismissEvent : ExternalChatChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatMemberJoinScene.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatMemberJoinScene.cs new file mode 100644 index 000000000..1eb129638 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatMemberJoinScene.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 成员的入群方式 +/// +[Description("成员的入群方式")] +public enum ExternalChatMemberJoinScene +{ + /// + /// 由成员邀请入群 + /// + [Description("由成员邀请入群")] + MemberInvitation = 0, + /// + /// 通过扫描群二维码入群 + /// + [Description("通过扫描群二维码入群")] + ScanQrCode = 3 +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatMemberQuitScene.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatMemberQuitScene.cs new file mode 100644 index 000000000..1a02803ac --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatMemberQuitScene.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 成员的退群方式 +/// +[Description("成员的退群方式")] +public enum ExternalChatMemberQuitScene +{ + /// + /// 自己退群 + /// + [Description("自己退群")] + UserSelf = 0, + /// + /// 群主/群管理员移出 + /// + [Description("群主/群管理员移出")] + Admin = 1 +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatUpdateEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatUpdateEvent.cs new file mode 100644 index 000000000..7b981dcdf --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalChatUpdateEvent.cs @@ -0,0 +1,19 @@ +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户群变更事件推送 +/// +public abstract class ExternalChatUpdateEvent : ExternalChatChangeEvent +{ + /// + /// 变更详情。目前有以下几种: + /// add_member : 成员入群 + /// del_member : 成员退群 + /// change_owner : 群主变更 + /// change_name : 群名变更 + /// change_notice : 群公告变更 + /// + [XmlElement("UpdateDetail")] + public string UpdateDetail { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactChangeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactChangeEvent.cs new file mode 100644 index 000000000..9d6fe930c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactChangeEvent.cs @@ -0,0 +1,25 @@ +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 企业客户变更事件推送 +/// +public abstract class ExternalContactChangeEvent : WeChatWorkEventMessage +{ + /// + /// 变更类型 + /// + [XmlElement("ChangeType")] + public string ChangeType { get; set; } + /// + /// 企业服务人员的UserID + /// + [XmlElement("UserID")] + public string UserId { get; set; } + /// + /// 外部联系人的userid,注意不是企业成员的账号 + /// + [XmlElement("ExternalUserID")] + public string ExternalUserId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactCreateEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactCreateEvent.cs new file mode 100644 index 000000000..03ddacffb --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactCreateEvent.cs @@ -0,0 +1,28 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 添加企业客户事件推送 +/// +[EventName("external_contact_create")] +public class ExternalContactCreateEvent : ExternalContactChangeEvent +{ + /// + /// 添加此用户的「联系我」方式配置的state参数,或在获客链接中指定的customer_channel参数,可用于识别添加此用户的渠道 + /// + [XmlElement("State")] + public string State { get; set; } + /// + /// 欢迎语code,可用于发送欢迎语 + /// + [XmlElement("WelcomeCode")] + public string WelcomeCode { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactCreateHalfEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactCreateHalfEvent.cs new file mode 100644 index 000000000..943b84f86 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactCreateHalfEvent.cs @@ -0,0 +1,28 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 外部联系人免验证添加成员事件 +/// +[EventName("external_contact_create_half")] +public class ExternalContactCreateHalfEvent : ExternalContactChangeEvent +{ + /// + /// 添加此用户的「联系我」方式配置的state参数,或在获客链接中指定的customer_channel参数,可用于识别添加此用户的渠道 + /// + [XmlElement("State")] + public string State { get; set; } + /// + /// 欢迎语code,可用于发送欢迎语 + /// + [XmlElement("WelcomeCode")] + public string WelcomeCode { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactDeleteEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactDeleteEvent.cs new file mode 100644 index 000000000..c67b70d30 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactDeleteEvent.cs @@ -0,0 +1,23 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 删除企业客户事件 +/// +[EventName("external_contact_delete")] +public class ExternalContactDeleteEvent : ExternalContactChangeEvent +{ + /// + /// 删除客户的操作来源,DELETE_BY_TRANSFER表示此客户是因在职继承自动被转接成员删除 + /// + [XmlElement("Source")] + public string Source { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactDeleteFollowUserEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactDeleteFollowUserEvent.cs new file mode 100644 index 000000000..cdd043073 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactDeleteFollowUserEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 删除跟进成员事件 +/// +[EventName("external_contact_del_follow_user")] +public class ExternalContactDeleteFollowUserEvent : ExternalContactChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactTransferFailEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactTransferFailEvent.cs new file mode 100644 index 000000000..dd60b40a4 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactTransferFailEvent.cs @@ -0,0 +1,23 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 客户接替失败事件 +/// +[EventName("external_contact_transfer_fail")] +public class ExternalContactTransferFailEvent : ExternalContactChangeEvent +{ + /// + /// 接替失败的原因, customer_refused-客户拒绝, customer_limit_exceed-接替成员的客户数达到上限 + /// + [XmlElement("FailReason")] + public string FailReason { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactUpdateEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactUpdateEvent.cs new file mode 100644 index 000000000..e1f9a777c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalContactUpdateEvent.cs @@ -0,0 +1,17 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 编辑企业客户事件推送 +/// +[EventName("external_contact_update")] +public class ExternalContactUpdateEvent : ExternalContactChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagChangeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagChangeEvent.cs new file mode 100644 index 000000000..e6bc43cb7 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagChangeEvent.cs @@ -0,0 +1,20 @@ +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 企业客户标签变更事件推送 +/// +public abstract class ExternalTagChangeEvent : WeChatWorkEventMessage +{ + /// + /// 变更类型 + /// + [XmlElement("ChangeType")] + public string ChangeType { get; set; } + /// + /// 标签或标签组所属的规则组id,只回调给“客户联系”应用 + /// + [XmlElement("StrategyId")] + public string StrategyId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagCreateEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagCreateEvent.cs new file mode 100644 index 000000000..cd7e0ff33 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagCreateEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 企业客户标签创建事件推送 +/// +[EventName("change_external_tag_create")] +public class ExternalTagCreateEvent : ExternalTagChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagDeleteEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagDeleteEvent.cs new file mode 100644 index 000000000..730b10915 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagDeleteEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 企业客户标签删除事件推送 +/// +[EventName("change_external_tag_delete")] +public class ExternalTagDeleteEvent : ExternalTagChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagShuffleEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagShuffleEvent.cs new file mode 100644 index 000000000..390a0f7d9 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagShuffleEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 企业客户标签重排事件推送 +/// +[EventName("change_external_tag_shuffle")] +public class ExternalTagShuffleEvent : ExternalTagChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagUpdateEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagUpdateEvent.cs new file mode 100644 index 000000000..1e728d5c2 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Messages/Models/ExternalTagUpdateEvent.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Messages.Models; +/// +/// 企业客户标签变更事件推送 +/// +[EventName("change_external_tag_update")] +public class ExternalTagUpdateEvent : ExternalTagChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttribute.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttribute.cs new file mode 100644 index 000000000..ef161f3c2 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttribute.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +/// +/// 成员对外属性 +/// +public abstract class ExternalAttribute +{ + /// + /// 属性名称: 需要先确保在管理端有创建该属性,否则会忽略 + /// + [NotNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string Name { get; set; } + /// + /// 属性类型 + /// + [NotNull] + [JsonProperty("type")] + [JsonPropertyName("type")] + public ExternalAttributeType Type { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttributeDeserializeFactory.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttributeDeserializeFactory.cs new file mode 100644 index 000000000..fb0975217 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttributeDeserializeFactory.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Text.Json; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +internal static class ExternalAttributeDeserializeFactory +{ + /// + /// 根据属性类型创建属性(System.Text.Json) + /// + public static ExternalAttribute CreateExternalAttribute(ExternalAttributeType type, JsonElement configElement) + { + return type switch + { + ExternalAttributeType.Text => JsonSerializer.Deserialize(configElement.GetRawText())!, + ExternalAttributeType.Web => JsonSerializer.Deserialize(configElement.GetRawText())!, + ExternalAttributeType.MiniProgram => JsonSerializer.Deserialize(configElement.GetRawText())!, + _ => throw new NotSupportedException($"Attribute type {type} is not supported for the time being"), + }; + } + + /// + /// 根据属性类型创建属性(Newtonsoft.Json) + /// + public static ExternalAttribute CreateExternalAttribute(ExternalAttributeType type, JToken configToken) + { + return type switch + { + ExternalAttributeType.Text => configToken.ToObject()!, + ExternalAttributeType.Web => configToken.ToObject()!, + ExternalAttributeType.MiniProgram => configToken.ToObject()!, + _ => throw new NotSupportedException($"Attribute type {type} is not supported for the time being"), + }; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttributeType.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttributeType.cs new file mode 100644 index 000000000..8b003cf05 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalAttributeType.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +/// +/// 属性类型 +/// +[Description("属性类型")] +public enum ExternalAttributeType +{ + /// + /// 文本 + /// + [Description("文本")] + Text = 0, + /// + /// 网页 + /// + [Description("网页")] + Web = 1, + /// + /// 小程序 + /// + [Description("小程序")] + MiniProgram = 2 +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalMiniProgramAttribute.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalMiniProgramAttribute.cs new file mode 100644 index 000000000..5935b1ebf --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalMiniProgramAttribute.cs @@ -0,0 +1,43 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +/// +/// 小程序类型属性 +/// +public class ExternalMiniProgramAttribute : ExternalAttribute +{ + /// + /// 小程序 + /// + [NotNull] + [JsonProperty("miniprogram")] + [JsonPropertyName("miniprogram")] + public ExternalMiniProgramModel MiniProgram { get; set; } +} + +public class ExternalMiniProgramModel +{ + /// + /// 小程序appid,必须是有在本企业安装授权的小程序,否则会被忽略 + /// + [NotNull] + [JsonProperty("appid")] + [JsonPropertyName("appid")] + public string AppId { get; set; } + /// + /// 小程序的展示标题,长度限制12个UTF8字符 + /// + [NotNull] + [JsonProperty("title")] + [JsonPropertyName("title")] + public string Title { get; set; } + /// + /// 小程序的页面路径 + /// + [NotNull] + [JsonProperty("pagepath")] + [JsonPropertyName("pagepath")] + public string PagePath { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalProfile.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalProfile.cs new file mode 100644 index 000000000..2b625199c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalProfile.cs @@ -0,0 +1,36 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +/// +/// 成员对外信息 +/// +[System.Text.Json.Serialization.JsonConverter(typeof(ExternalProfileSystemTextJsonConverter))] +[Newtonsoft.Json.JsonConverter(typeof(ExternalProfileNewtonsoftJsonConverter))] +public class ExternalProfile +{ + /// + /// 企业对外简称,需从已认证的企业简称中选填。可在“我的企业”页中查看企业简称认证状态。 + /// + [NotNull] + [JsonProperty("external_corp_name")] + [JsonPropertyName("external_corp_name")] + public string ExternalCorpName { get; set; } + /// + /// 视频号属性。须从企业绑定到企业微信的视频号中选择,可在“我的企业”页中查看绑定的视频号。 + /// 第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取 + /// + [NotNull] + [JsonProperty("wechat_channels")] + [JsonPropertyName("wechat_channels")] + public List WechatChannels { get; set; } + /// + /// 属性列表 + /// + [NotNull] + [JsonProperty("external_attr")] + [JsonPropertyName("external_attr")] + public List ExternalAttributes { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalTextAttribute.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalTextAttribute.cs new file mode 100644 index 000000000..b8f3f7d70 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalTextAttribute.cs @@ -0,0 +1,29 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +/// +/// 文本类型属性 +/// +public class ExternalTextAttribute : ExternalAttribute +{ + /// + /// 文本 + /// + [NotNull] + [JsonProperty("text")] + [JsonPropertyName("text")] + public ExternalTextModel Text { get; set; } +} + +public class ExternalTextModel +{ + /// + /// 文本 + /// + [NotNull] + [JsonProperty("value")] + [JsonPropertyName("value")] + public string Value { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalWebAttribute.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalWebAttribute.cs new file mode 100644 index 000000000..e882697a2 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/ExternalWebAttribute.cs @@ -0,0 +1,36 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +/// +/// 网页类型属性 +/// +public class ExternalWebAttribute : ExternalAttribute +{ + /// + /// 网页 + /// + [NotNull] + [JsonProperty("web")] + [JsonPropertyName("web")] + public ExternalWebModel Web { get; set; } +} + +public class ExternalWebModel +{ + /// + /// 网页的url,必须包含http或者https头 + /// + [NotNull] + [JsonProperty("url")] + [JsonPropertyName("url")] + public string Url { get; set; } + /// + /// 网页的展示标题,长度限制12个UTF8字符 + /// + [NotNull] + [JsonProperty("title")] + [JsonPropertyName("title")] + public string Title { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/WechatChannel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/WechatChannel.cs new file mode 100644 index 000000000..953c929fa --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/WechatChannel.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +/// +/// 视频号属性 +/// +public class WechatChannel +{ + /// + /// 视频号名字(设置后,成员将对外展示该视频号) + /// + [NotNull] + [JsonProperty("nickname")] + [JsonPropertyName("nickname")] + public string NickName { get; set; } + /// + /// 对外展示视频号状态 + /// + [NotNull] + [JsonProperty("status")] + [JsonPropertyName("status")] + public WechatChannelStatus Status { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/WechatChannelStatus.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/WechatChannelStatus.cs new file mode 100644 index 000000000..3d994e996 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Models/WechatChannelStatus.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Models; +/// +/// 视频号状态 +/// +[Description("视频号状态")] +public enum WechatChannelStatus +{ + /// + /// 已确认 + /// + [Description("已确认")] + Confirmed = 0, + /// + /// 待确认 + /// + [Description("待确认")] + UnConfirmed = 1, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/IWeChatWorkCropTagProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/IWeChatWorkCropTagProvider.cs new file mode 100644 index 000000000..f943e1e3c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/IWeChatWorkCropTagProvider.cs @@ -0,0 +1,72 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags; +/// +/// 客户标签管理 +/// +public interface IWeChatWorkCropTagProvider +{ + /// + /// 获取企业标签库 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task GetCropTagListAsync( + WeChatWorkGetCropTagListRequest request, + CancellationToken cancellationToken = default); + /// + /// 添加企业客户标签 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task CreateCropTagAsync( + WeChatWorkCreateCropTagRequest request, + CancellationToken cancellationToken = default); + /// + /// 编辑企业客户标签 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task UpdateCropTagAsync( + WeChatWorkUpdateCropTagRequest request, + CancellationToken cancellationToken = default); + /// + /// 删除企业客户标签 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task DeleteCropTagAsync( + WeChatWorkDeleteCropTagRequest request, + CancellationToken cancellationToken = default); + /// + /// 编辑客户企业标签 + /// + /// + /// 详情见: + /// + /// 请求参数 + /// + /// + Task MarkCropTagAsync( + WeChatWorkMarkCropTagRequest request, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/IWeChatWorkStrategyTagProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/IWeChatWorkStrategyTagProvider.cs new file mode 100644 index 000000000..794b6e007 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/IWeChatWorkStrategyTagProvider.cs @@ -0,0 +1,60 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags; +/// +/// 规则组标签管理 +/// +public interface IWeChatWorkStrategyTagProvider +{ + /// + /// 获取指定规则组下的企业客户标签 + /// + /// + /// 详情见: https://developer.work.weixin.qq.com/document/path/94882#%E8%8E%B7%E5%8F%96%E6%8C%87%E5%AE%9A%E8%A7%84%E5%88%99%E7%BB%84%E4%B8%8B%E7%9A%84%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%88%B7%E6%A0%87%E7%AD%BE + /// + /// 请求参数 + /// + /// + Task GetStrategyTagListAsync( + WeChatWorkGetStrategyTagListRequest request, + CancellationToken cancellationToken = default); + /// + /// 为指定规则组创建企业客户标签 + /// + /// + /// 详情见: https://developer.work.weixin.qq.com/document/path/94882#%E8%8E%B7%E5%8F%96%E6%8C%87%E5%AE%9A%E8%A7%84%E5%88%99%E7%BB%84%E4%B8%8B%E7%9A%84%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%88%B7%E6%A0%87%E7%AD%BE + /// + /// 请求参数 + /// + /// + Task CreateStrategyTagAsync( + WeChatWorkCreateStrategyTagRequest request, + CancellationToken cancellationToken = default); + /// + /// 编辑指定规则组下的企业客户标签 + /// + /// + /// 详情见: https://developer.work.weixin.qq.com/document/path/94882#%E7%BC%96%E8%BE%91%E6%8C%87%E5%AE%9A%E8%A7%84%E5%88%99%E7%BB%84%E4%B8%8B%E7%9A%84%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%88%B7%E6%A0%87%E7%AD%BE + /// + /// 请求参数 + /// + /// + Task UpdateStrategyTagAsync( + WeChatWorkUpdateStrategyTagRequest request, + CancellationToken cancellationToken = default); + /// + /// 删除指定规则组下的企业客户标签 + /// + /// + /// 详情见: https://developer.work.weixin.qq.com/document/path/94882#%E5%88%A0%E9%99%A4%E6%8C%87%E5%AE%9A%E8%A7%84%E5%88%99%E7%BB%84%E4%B8%8B%E7%9A%84%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%88%B7%E6%A0%87%E7%AD%BE + /// + /// 请求参数 + /// + /// + Task DeleteStrategyTagAsync( + WeChatWorkDeleteStrategyTagRequest request, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/CropTag.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/CropTag.cs new file mode 100644 index 000000000..ebcf7ba48 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/CropTag.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +public class CropTag : Tag +{ + /// + /// 标签是否已经被删除,只在指定tag_id/group_id进行查询时返回 + /// + [NotNull] + [JsonProperty("deleted")] + [JsonPropertyName("deleted")] + public bool Deleted { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/CropTagGroup.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/CropTagGroup.cs new file mode 100644 index 000000000..db8f9770e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/CropTagGroup.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +public class CropTagGroup : TagGroup +{ + /// + /// 标签组是否已经被删除,只在指定tag_id进行查询时返回 + /// + [NotNull] + [JsonProperty("deleted")] + [JsonPropertyName("deleted")] + public bool Deleted { get; set; } + /// + /// 标签列表 + /// + [NotNull] + [JsonProperty("tag")] + [JsonPropertyName("tag")] + public CropTag[] Tag { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/StrategyTag.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/StrategyTag.cs new file mode 100644 index 000000000..f0ebd22c8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/StrategyTag.cs @@ -0,0 +1,4 @@ +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +public class StrategyTag : Tag +{ +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/StrategyTagGroup.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/StrategyTagGroup.cs new file mode 100644 index 000000000..7f49f7e41 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/StrategyTagGroup.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +public class StrategyTagGroup : TagGroup +{ + /// + /// 标签组所属的规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; set; } + /// + /// 标签列表 + /// + [NotNull] + [JsonProperty("tag")] + [JsonPropertyName("tag")] + public StrategyTag[] Tag { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/Tag.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/Tag.cs new file mode 100644 index 000000000..e625fcb66 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/Tag.cs @@ -0,0 +1,36 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +public abstract class Tag +{ + /// + /// 标签id + /// + [NotNull] + [JsonProperty("id")] + [JsonPropertyName("id")] + public string Id { get; set; } + /// + /// 标签名称 + /// + [NotNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string Name { get; set; } + /// + /// 标签创建时间 + /// + [NotNull] + [JsonProperty("create_time")] + [JsonPropertyName("create_time")] + public long CreateTime { get; set; } + /// + /// 标签排序的次序值,order值大的排序靠前。有效的值范围是[0, 2^32) + /// + [NotNull] + [JsonProperty("order")] + [JsonPropertyName("order")] + public int Order { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/TagGroup.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/TagGroup.cs new file mode 100644 index 000000000..6fcd9ddac --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Models/TagGroup.cs @@ -0,0 +1,36 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +public abstract class TagGroup +{ + /// + /// 标签组id + /// + [NotNull] + [JsonProperty("group_id")] + [JsonPropertyName("group_id")] + public string GroupId { get; set; } + /// + /// 标签组名称 + /// + [NotNull] + [JsonProperty("group_name")] + [JsonPropertyName("group_name")] + public string GroupName { get; set; } + /// + /// 标签组创建时间 + /// + [NotNull] + [JsonProperty("create_time")] + [JsonPropertyName("create_time")] + public long CreateTime { get; set; } + /// + /// 标签组排序的次序值,order值大的排序靠前。有效的值范围是[0, 2^32) + /// + [NotNull] + [JsonProperty("order")] + [JsonPropertyName("order")] + public int Order { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkCreateCropTagRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkCreateCropTagRequest.cs new file mode 100644 index 000000000..061aa937a --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkCreateCropTagRequest.cs @@ -0,0 +1,100 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +/// +/// 添加企业客户标签请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkCreateCropTagRequest : WeChatWorkRequest +{ + /// + /// 标签组id + /// + [CanBeNull] + [JsonProperty("group_id")] + [JsonPropertyName("group_id")] + public string? GroupId { get; set; } + + private string? _groupName; + /// + /// 标签组名称,最长为30个字符 + /// + [CanBeNull] + [JsonProperty("group_name")] + [JsonPropertyName("group_name")] + public string? GroupName { + get => _groupName; + set { + + Check.Length(value, nameof(GroupName), 30); + _groupName = value; + } + } + /// + /// 标签组排序的次序值,order值大的排序靠前。有效的值范围是[0, 2^32) + /// + [CanBeNull] + [JsonProperty("order")] + [JsonPropertyName("order")] + public int? Order { get; set; } + /// + /// 授权方安装的应用agentid。仅旧的第三方多应用套件需要填此参数 + /// + [CanBeNull] + [JsonProperty("agentid")] + [JsonPropertyName("agentid")] + public string? AgentId { get; set; } + /// + /// 添加的标签组 + /// + [CanBeNull] + [JsonProperty("tag")] + [JsonPropertyName("tag")] + public List Tag { get; } + + public WeChatWorkCreateCropTagRequest() + { + Tag = new List(); + } + + protected override void Validate() + { + if (GroupName.IsNullOrWhiteSpace() && + Tag.IsNullOrEmpty()) + { + throw new ArgumentException("The name of the tag group or the tag list cannot be empty at the same time!"); + } + } +} + +public class NewCropTag +{ + /// + /// 添加的标签名称,最长为30个字符 + /// + [NotNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string Name { get; } + /// + /// 标签次序值。order值大的排序靠前。有效的值范围是[0, 2^32) + /// + [CanBeNull] + [JsonProperty("order")] + [JsonPropertyName("order")] + public int? Order { get; set; } + public NewCropTag(string name, int? order = null) + { + Check.NotNullOrWhiteSpace(name, nameof(name), 30); + + Name = name; + Order = order; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkCreateStrategyTagRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkCreateStrategyTagRequest.cs new file mode 100644 index 000000000..a1e7a1bb1 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkCreateStrategyTagRequest.cs @@ -0,0 +1,88 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +public class WeChatWorkCreateStrategyTagRequest : WeChatWorkRequest +{ + /// + /// 规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; } + /// + /// 标签组id + /// + [CanBeNull] + [JsonProperty("group_id")] + [JsonPropertyName("group_id")] + public string? GroupId { get; set; } + + private string? _groupName; + /// + /// 标签组名称,最长为30个字符 + /// + [CanBeNull] + [JsonProperty("group_name")] + [JsonPropertyName("group_name")] + public string? GroupName { + get => _groupName; + set { + + Check.Length(value, nameof(GroupName), 30); + _groupName = value; + } + } + /// + /// 标签组排序的次序值,order值大的排序靠前。有效的值范围是[0, 2^32) + /// + [CanBeNull] + [JsonProperty("order")] + [JsonPropertyName("order")] + public int? Order { get; set; } + /// + /// 添加的标签组 + /// + [CanBeNull] + [JsonProperty("tag")] + [JsonPropertyName("tag")] + public List Tag { get; } + + public WeChatWorkCreateStrategyTagRequest(int strategyId) + { + Check.NotDefaultOrNull(strategyId, nameof(strategyId)); + + StrategyId = strategyId; + + Tag = new List(); + } +} + +public class NewStrategyTag +{ + /// + /// 添加的标签名称,最长为30个字符 + /// + [NotNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string Name { get; } + /// + /// 标签次序值。order值大的排序靠前。有效的值范围是[0, 2^32) + /// + [CanBeNull] + [JsonProperty("order")] + [JsonPropertyName("order")] + public int? Order { get; set; } + public NewStrategyTag(string name, int? order = null) + { + Check.NotNullOrWhiteSpace(name, nameof(name), 30); + + Name = name; + Order = order; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkDeleteCropTagRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkDeleteCropTagRequest.cs new file mode 100644 index 000000000..c5b86b07e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkDeleteCropTagRequest.cs @@ -0,0 +1,53 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +/// +/// 删除企业客户标签请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkDeleteCropTagRequest : WeChatWorkRequest +{ + /// + /// 标签组的id列表 + /// + [CanBeNull] + [JsonProperty("group_id")] + [JsonPropertyName("group_id")] + public string[]? GroupId { get; } + /// + /// 标签组的id列表 + /// + [CanBeNull] + [JsonProperty("tag_id")] + [JsonPropertyName("tag_id")] + public string[]? TagId { get; } + /// + /// 授权方安装的应用agentid。仅旧的第三方多应用套件需要填此参数 + /// + [CanBeNull] + [JsonProperty("agentid")] + [JsonPropertyName("agentid")] + public string? AgentId { get; set; } + + private WeChatWorkDeleteCropTagRequest( + string[]? groupId = null, + string[]? tagId = null) + { + GroupId = groupId; + TagId = tagId; + } + + public static WeChatWorkDeleteCropTagRequest Tag(string[] tagId) + { + return new WeChatWorkDeleteCropTagRequest(tagId: tagId); + } + + public static WeChatWorkDeleteCropTagRequest Group(string[] groupId) + { + return new WeChatWorkDeleteCropTagRequest(groupId); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkDeleteStrategyTagRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkDeleteStrategyTagRequest.cs new file mode 100644 index 000000000..018ed9505 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkDeleteStrategyTagRequest.cs @@ -0,0 +1,40 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +public class WeChatWorkDeleteStrategyTagRequest : WeChatWorkRequest +{ + /// + /// 标签组的id列表 + /// + [CanBeNull] + [JsonProperty("group_id")] + [JsonPropertyName("group_id")] + public string[]? GroupId { get; } + /// + /// 标签组的id列表 + /// + [CanBeNull] + [JsonProperty("tag_id")] + [JsonPropertyName("tag_id")] + public string[]? TagId { get; } + + private WeChatWorkDeleteStrategyTagRequest( + string[]? groupId = null, + string[]? tagId = null) + { + GroupId = groupId; + TagId = tagId; + } + + public static WeChatWorkDeleteStrategyTagRequest Tag(string[] tagId) + { + return new WeChatWorkDeleteStrategyTagRequest(tagId: tagId); + } + + public static WeChatWorkDeleteStrategyTagRequest Group(string[] groupId) + { + return new WeChatWorkDeleteStrategyTagRequest(groupId); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkGetCropTagListRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkGetCropTagListRequest.cs new file mode 100644 index 000000000..051a09701 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkGetCropTagListRequest.cs @@ -0,0 +1,33 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +/// +/// 获取企业标签库请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCropTagListRequest : WeChatWorkRequest +{ + /// + /// 要查询的标签id + /// + [CanBeNull] + [JsonProperty("tag_id")] + [JsonPropertyName("tag_id")] + public string[]? TagId { get; } + /// + /// 要查询的标签组id,返回该标签组以及其下的所有标签信息 + /// + [CanBeNull] + [JsonProperty("group_id")] + [JsonPropertyName("group_id")] + public string[]? GroupId { get; } + public WeChatWorkGetCropTagListRequest(string[]? tagId = null, string[]? groupId = null) + { + TagId = tagId; + GroupId = groupId; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkGetStrategyTagListRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkGetStrategyTagListRequest.cs new file mode 100644 index 000000000..e17cc35d0 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkGetStrategyTagListRequest.cs @@ -0,0 +1,41 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +public class WeChatWorkGetStrategyTagListRequest : WeChatWorkRequest +{ + /// + /// 规则组id + /// + [NotNull] + [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] + public int StrategyId { get; } + /// + /// 要查询的标签id + /// + [CanBeNull] + [JsonProperty("tag_id")] + [JsonPropertyName("tag_id")] + public string[]? TagId { get; } + /// + /// 要查询的标签组id,返回该标签组以及其下的所有标签信息 + /// + [CanBeNull] + [JsonProperty("group_id")] + [JsonPropertyName("group_id")] + public string[]? GroupId { get; } + public WeChatWorkGetStrategyTagListRequest( + int strategyId, + string[]? tagId = null, + string[]? groupId = null) + { + Check.NotDefaultOrNull(strategyId, nameof(strategyId)); + + StrategyId = strategyId; + TagId = tagId; + GroupId = groupId; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkMarkCropTagRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkMarkCropTagRequest.cs new file mode 100644 index 000000000..fc90029c3 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkMarkCropTagRequest.cs @@ -0,0 +1,65 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +/// +/// 编辑客户企业标签请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkMarkCropTagRequest : WeChatWorkRequest +{ + /// + /// 添加外部联系人的userid + /// + [NotNull] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public string UserId { get; } + /// + /// 外部联系人userid + /// + [NotNull] + [JsonProperty("external_userid")] + [JsonPropertyName("external_userid")] + public string ExternalUserId { get; } + /// + /// 要标记的标签列表 + /// + [NotNull] + [JsonProperty("add_tag")] + [JsonPropertyName("add_tag")] + public List CreateTag { get; } + /// + /// 要移除的标签列表 + /// + [NotNull] + [JsonProperty("remove_tag")] + [JsonPropertyName("remove_tag")] + public List RemoveTag { get; } + public WeChatWorkMarkCropTagRequest(string userId, string externalUserId) + { + Check.NotNullOrWhiteSpace(userId, nameof(userId)); + Check.NotNullOrWhiteSpace(externalUserId, nameof(externalUserId)); + + UserId = userId; + ExternalUserId = externalUserId; + + CreateTag = new List(); + RemoveTag = new List(); + } + + protected override void Validate() + { + if (CreateTag.IsNullOrEmpty() && + RemoveTag.IsNullOrEmpty()) + { + throw new ArgumentException("CreateTag and RemoveTag cannot be empty simultaneously!"); + } + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkUpdateCropTagRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkUpdateCropTagRequest.cs new file mode 100644 index 000000000..b79ec152c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkUpdateCropTagRequest.cs @@ -0,0 +1,52 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +/// +/// 编辑企业客户标签请求参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkUpdateCropTagRequest : WeChatWorkRequest +{ + /// + /// 标签或标签组的id + /// + [NotNull] + [JsonProperty("id")] + [JsonPropertyName("id")] + public string Id { get; } + /// + /// 新的标签或标签组名称,最长为30个字符 + /// + [CanBeNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string? Name { get; } + /// + /// 标签/标签组的次序值。order值大的排序靠前。有效的值范围是[0, 2^32) + /// + [CanBeNull] + [JsonProperty("order")] + [JsonPropertyName("order")] + public int? Order { get; set; } + /// + /// 授权方安装的应用agentid。仅旧的第三方多应用套件需要填此参数 + /// + [CanBeNull] + [JsonProperty("agentid")] + [JsonPropertyName("agentid")] + public string? AgentId { get; set; } + + public WeChatWorkUpdateCropTagRequest(string id, string? name = null) + { + Check.NotNullOrWhiteSpace(id, nameof(id)); + Check.Length(name, nameof(name), 30); + + Id = id; + Name = name; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkUpdateStrategyTagRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkUpdateStrategyTagRequest.cs new file mode 100644 index 000000000..b8f1f1a84 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Request/WeChatWorkUpdateStrategyTagRequest.cs @@ -0,0 +1,39 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +public class WeChatWorkUpdateStrategyTagRequest : WeChatWorkRequest +{ + /// + /// 标签或标签组的id + /// + [NotNull] + [JsonProperty("id")] + [JsonPropertyName("id")] + public string Id { get; } + /// + /// 新的标签或标签组名称,最长为30个字符 + /// + [CanBeNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string? Name { get; } + /// + /// 标签/标签组的次序值。order值大的排序靠前。有效的值范围是[0, 2^32) + /// + [CanBeNull] + [JsonProperty("order")] + [JsonPropertyName("order")] + public int? Order { get; set; } + + public WeChatWorkUpdateStrategyTagRequest(string id, string? name = null) + { + Check.NotNullOrWhiteSpace(id, nameof(id)); + Check.Length(name, nameof(name), 30); + + Id = id; + Name = name; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkCreateCropTagResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkCreateCropTagResponse.cs new file mode 100644 index 000000000..d10cb037d --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkCreateCropTagResponse.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Response; +/// +/// 添加企业客户标签响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkCreateCropTagResponse : WeChatWorkResponse +{ + /// + /// 标签组 + /// + [NotNull] + [JsonProperty("tag_group")] + [JsonPropertyName("tag_group")] + public CropTagGroup TagGroup { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkCreateStrategyTagResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkCreateStrategyTagResponse.cs new file mode 100644 index 000000000..6f40217d5 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkCreateStrategyTagResponse.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Response; +/// +/// 为指定规则组创建企业客户标签 +/// +/// +/// 详情见: https://developer.work.weixin.qq.com/document/path/94882#%E4%B8%BA%E6%8C%87%E5%AE%9A%E8%A7%84%E5%88%99%E7%BB%84%E5%88%9B%E5%BB%BA%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%88%B7%E6%A0%87%E7%AD%BE +/// +public class WeChatWorkCreateStrategyTagResponse : WeChatWorkResponse +{ + /// + /// 标签组 + /// + [NotNull] + [JsonProperty("tag_group")] + [JsonPropertyName("tag_group")] + public StrategyTagGroup TagGroup { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkGetCropTagListResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkGetCropTagListResponse.cs new file mode 100644 index 000000000..b71bcb1a1 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkGetCropTagListResponse.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Response; +/// +/// 获取企业标签库响应参数 +/// +/// +/// 详情见: +/// +public class WeChatWorkGetCropTagListResponse : WeChatWorkResponse +{ + /// + /// 标签组列表 + /// + [NotNull] + [JsonProperty("tag_group")] + [JsonPropertyName("tag_group")] + public StrategyTagGroup[] TagGroup { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkGetStrategyTagListResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkGetStrategyTagListResponse.cs new file mode 100644 index 000000000..1a77eaa69 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/Response/WeChatWorkGetStrategyTagListResponse.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Models; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Response; +/// +/// 获取指定规则组下的企业客户标签响应参数 +/// +/// +/// 详情见: https://developer.work.weixin.qq.com/document/path/94882#%E8%8E%B7%E5%8F%96%E6%8C%87%E5%AE%9A%E8%A7%84%E5%88%99%E7%BB%84%E4%B8%8B%E7%9A%84%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%88%B7%E6%A0%87%E7%AD%BE +/// +public class WeChatWorkGetStrategyTagListResponse : WeChatWorkResponse +{ + /// + /// 标签组列表 + /// + [NotNull] + [JsonProperty("tag_group")] + [JsonPropertyName("tag_group")] + public StrategyTagGroup[] TagGroup { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/WeChatWorkCropTagProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/WeChatWorkCropTagProvider.cs new file mode 100644 index 000000000..b73400b14 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/WeChatWorkCropTagProvider.cs @@ -0,0 +1,108 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Response; +using LINGYUN.Abp.WeChat.Work.Token; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags; + +[RequiresFeature(WeChatWorkExternalContactFeatureNames.Enable)] +public class WeChatWorkCropTagProvider : IWeChatWorkCropTagProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkCropTagProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task GetCropTagListAsync( + WeChatWorkGetCropTagListRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetCropTagListAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task CreateCropTagAsync( + WeChatWorkCreateCropTagRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.CreateCropTagAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task UpdateCropTagAsync( + WeChatWorkUpdateCropTagRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.UpdateCropTagAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task DeleteCropTagAsync( + WeChatWorkDeleteCropTagRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.DeleteCropTagAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task MarkCropTagAsync( + WeChatWorkMarkCropTagRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.MarkCropTagAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/WeChatWorkStrategyTagProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/WeChatWorkStrategyTagProvider.cs new file mode 100644 index 000000000..49fe5189e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.ExternalContact/LINGYUN/Abp/WeChat/Work/ExternalContact/Tags/WeChatWorkStrategyTagProvider.cs @@ -0,0 +1,92 @@ +using LINGYUN.Abp.WeChat.Work.ExternalContact.Features; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Request; +using LINGYUN.Abp.WeChat.Work.ExternalContact.Tags.Response; +using LINGYUN.Abp.WeChat.Work.Token; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.ExternalContact.Tags; + +[RequiresFeature(WeChatWorkExternalContactFeatureNames.Enable)] +public class WeChatWorkStrategyTagProvider : IWeChatWorkStrategyTagProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkStrategyTagProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task GetStrategyTagListAsync( + WeChatWorkGetStrategyTagListRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.GetStrategyTagListAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task CreateStrategyTagAsync( + WeChatWorkCreateStrategyTagRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.CreateStrategyTagAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync(); + wechatResponse.ThrowIfNotSuccess(); + return wechatResponse; + } + + public async virtual Task UpdateStrategyTagAsync( + WeChatWorkUpdateStrategyTagRequest request, + CancellationToken cancellationToken = default) + { + Check.NotNull(request, nameof(request)); + + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateWeChatWorkApiClient(); + + using var response = await client.UpdateStrategyTagAsync(token.AccessToken, request, cancellationToken); + + var wechatResponse = await response.DeserializeObjectAsync