import { assert } from 'oak-domain/lib/utils/assert'; import { applicationProjection } from '../types/Projection'; import WechatSDK from 'oak-external-sdk/lib/WechatSDK'; import fs from 'fs'; import { cloneDeep, unset } from 'oak-domain/lib/utils/lodash'; import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid'; import { compareVersion } from 'oak-domain/lib/utils/version'; import { OakApplicationHasToUpgrade } from 'oak-domain/lib/types/Exception'; async function getApplicationByDomain(context, options) { const { data, type, domain } = options; let applications = await context.select('application', { data, filter: { type, system: { domain$system: { url: domain, }, }, domain: { url: domain, }, }, }, {}); assert(applications.length <= 1, `应用指定域名(domainId)只能存在一项或未指定,域名「${domain}」`); if (applications.length === 0) { applications = await context.select('application', { data: data, filter: { type, system: { domain$system: { url: domain, }, }, domainId: { $exists: false, }, }, }, {}); } return applications; } function checkAppVersionSafe(application, version) { const { dangerousVersions, warningVersions, system } = application; const { oldestVersion, platform } = system; const { oldestVersion: pfOldestVersion } = platform || {}; const oldest = pfOldestVersion || oldestVersion; if (oldest) { if (compareVersion(version, oldest) < 0) { throw new OakApplicationHasToUpgrade(); } } if (dangerousVersions && dangerousVersions.includes(version)) { throw new OakApplicationHasToUpgrade(); } unset(application, 'dangerousVersions'); if (warningVersions) { application.warningVersions = warningVersions.filter(ele => ele === version); } } export async function getApplication(params, context) { const { type, domain, data, appId, version } = params; // 先找指定domain的应用,如果不存在再找系统下面的domain, 但无论怎么样都必须一项 const applications = await getApplicationByDomain(context, { type, domain, data, }); switch (type) { case 'wechatMp': { assert(applications.length === 1, `微信小程序环境下,同一个系统必须存在唯一的【${type}】应用,域名「${domain}」`); const application = applications[0]; checkAppVersionSafe(application, version); return application.id; } case 'native': { assert(applications.length === 1, `APP环境下,同一个系统必须存在唯一的【${type}】应用,域名「${domain}」`); const application = applications[0]; checkAppVersionSafe(application, version); return application.id; } case 'wechatPublic': { // 微信公众号环境下,未配置公众号,可以使用web的application if (applications.length === 0) { const webApplications = await getApplicationByDomain(context, { type: 'web', domain, data, }); assert(webApplications.length === 1, `微信公众号环境下, 可以未配置公众号,但必须存在web应用,域名「${domain}」`); const application = webApplications[0]; checkAppVersionSafe(application, version); return application.id; } assert(applications.length === 1, `微信公众号环境下,同一个系统必须存在唯一的【${type}】应用 或 多个${type}应用必须配置域名,域名「${domain}」`); const application = applications[0]; checkAppVersionSafe(application, version); return application.id; } case 'web': { assert(applications.length === 1, `web环境下,同一个系统必须存在唯一的【${type}】应用 或 多个${type}应用必须配置域名,域名「${domain}」`); const application = applications[0]; checkAppVersionSafe(application, version); return application.id; } default: { assert(false, `不支持的类型【${type}】,域名「${domain}」`); return; } } } export async function signatureJsSDK({ url, env }, context) { const application = context.getApplication(); const { type, config } = application; assert(type === 'wechatPublic' && config.type === 'wechatPublic'); const config2 = config; const { appId, appSecret } = config2; const wechatInstance = WechatSDK.getInstance(appId, 'wechatPublic', appSecret); const result = await wechatInstance.signatureJsSDK({ url }); return result; } export async function uploadWechatMedia(params, // FormData表单提交 isPermanent 变成 'true' | 'false' context) { const { applicationId, file, type: mediaType, description, extraFileId, } = params; assert(applicationId); const isPermanent = params.isPermanent === 'true'; const filename = file.originalFilename; const filetype = file.mimetype; const fileLength = file.size; const fileStream = fs.createReadStream(file.filepath); const [application] = await context.select('application', { data: cloneDeep(applicationProjection), filter: { id: applicationId, }, }, { dontCollect: true, }); const { type, config } = application; assert(type === 'wechatPublic' || type === 'wechatMp'); let wechatInstance; if (type === 'wechatPublic') { assert(config.type === 'wechatPublic'); const config2 = config; const { appId, appSecret } = config2; wechatInstance = WechatSDK.getInstance(appId, 'wechatPublic', appSecret); } else { assert(config.type === 'wechatMp'); const config2 = config; const { appId, appSecret } = config2; wechatInstance = WechatSDK.getInstance(appId, 'wechatPublic', appSecret); } let mediaId; if (isPermanent) { // 只有公众号才能上传永久素材 assert(type === 'wechatPublic'); const result = (await wechatInstance.createMaterial({ type: mediaType, media: fileStream, filename, filetype, fileLength, description: description ? JSON.parse(description) : null, })); mediaId = result.media_id; } else { const result = (await wechatInstance.createTemporaryMaterial({ type: mediaType, media: fileStream, filename, filetype, fileLength, })); mediaId = result.media_id; } if (extraFileId) { const closeRootMode = context.openRootMode(); try { await context.operate('extraFile', { id: await generateNewIdAsync(), action: 'update', data: { extra1: mediaId, }, filter: { id: extraFileId, }, }, {}); closeRootMode(); } catch (err) { closeRootMode(); throw err; } } return { mediaId, }; } export async function getMaterial(params, context) { const { mediaId, applicationId, isPermanent = false } = params; assert(applicationId); assert(mediaId); const [application] = await context.select('application', { data: cloneDeep(applicationProjection), filter: { id: applicationId, }, }, { dontCollect: true, }); assert(application); const { type, config } = application; assert(type === 'wechatPublic' || type === 'wechatMp'); let wechatInstance; if (type === 'wechatPublic') { const config2 = config; const { appId, appSecret } = config2; wechatInstance = WechatSDK.getInstance(appId, type, appSecret); } else { const config2 = config; const { appId, appSecret } = config2; wechatInstance = WechatSDK.getInstance(appId, type, appSecret); } let result; if (isPermanent) { // 只有公众号才能获取永久素材 assert(type === 'wechatPublic'); result = await wechatInstance.getMaterial({ mediaId, }); } else { result = await wechatInstance.getTemporaryMaterial({ mediaId, }); } if (result instanceof ArrayBuffer) { return Buffer.from(result).toString('base64'); } return result; } export async function deleteMaterial(params, context) { const { mediaId, applicationId } = params; assert(applicationId); assert(mediaId); const [application] = await context.select('application', { data: cloneDeep(applicationProjection), filter: { id: applicationId, }, }, { dontCollect: true, }); assert(application); const { type, config } = application; assert(type === 'wechatPublic'); const config2 = config; const { appId, appSecret } = config2; const wechatInstance = WechatSDK.getInstance(appId, type, appSecret); const result = await wechatInstance.deleteMaterial({ mediaId, }); return result; } export async function batchGetArticle(params, context) { const { applicationId, offset, count, noContent } = params; assert(applicationId); const [application] = await context.select('application', { data: cloneDeep(applicationProjection), filter: { id: applicationId, }, }, { dontCollect: true, }); assert(application); const { type, config } = application; assert(type === 'wechatPublic'); let appId, appSecret; const config2 = config; appId = config2.appId; appSecret = config2.appSecret; const wechatInstance = WechatSDK.getInstance(appId, type, appSecret); const result = await wechatInstance.batchGetArticle({ offset, count, noContent, }); return result; } export async function getArticle(params, context) { const { applicationId, articleId } = params; assert(applicationId); const [application] = await context.select('application', { data: cloneDeep(applicationProjection), filter: { id: applicationId, }, }, { dontCollect: true, }); assert(application); const { type, config } = application; assert(type === 'wechatPublic'); let appId, appSecret; const config2 = config; appId = config2.appId; appSecret = config2.appSecret; const wechatInstance = WechatSDK.getInstance(appId, type, appSecret); const result = await wechatInstance.getArticle({ articleId, }); return result; } export async function batchGetMaterialList(params, context) { const { applicationId } = params; assert(applicationId); const [application] = await context.select('application', { data: cloneDeep(applicationProjection), filter: { id: applicationId, }, }, { dontCollect: true, }); assert(application); const { type, config } = application; assert(type === 'wechatPublic'); let appId, appSecret; const config2 = config; appId = config2.appId; appSecret = config2.appSecret; const wechatInstance = WechatSDK.getInstance(appId, type, appSecret); const { type: materialType, offset, count } = params; const result = await wechatInstance.batchGetMaterialList({ type: materialType, offset, count, }); return result; }