From 364910530b98ee3acc310263eb7c46b8704d0068 Mon Sep 17 00:00:00 2001 From: linju Date: Tue, 10 Jan 2023 17:11:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8Euni-admin=E5=90=8C=E6=AD=A5=E7=9B=B8?= =?UTF-8?q?=E5=85=B3uniCloud=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 3 - common/appInit.js | 5 +- manifest.json | 131 +- pages.json | 9 +- pages/list/detail.vue | 6 +- pages/list/list.nvue | 9 +- .../cloudfunctions/common/uni-stat/index.js | 23 + .../common/uni-stat/package.json | 15 + .../common/uni-stat/shared/create-api.js | 82 + .../common/uni-stat/shared/error.js | 19 + .../common/uni-stat/shared/index.js | 6 + .../common/uni-stat/shared/utils.js | 197 ++ .../common/uni-stat/stat/lib/date.js | 371 +++ .../common/uni-stat/stat/lib/index.js | 4 + .../common/uni-stat/stat/lib/uni-crypto.js | 98 + .../common/uni-stat/stat/mod/activeDevices.js | 528 ++++ .../common/uni-stat/stat/mod/activeUsers.js | 314 +++ .../common/uni-stat/stat/mod/appCrashLogs.js | 37 + .../common/uni-stat/stat/mod/base.js | 485 ++++ .../common/uni-stat/stat/mod/channel.js | 107 + .../common/uni-stat/stat/mod/device.js | 184 ++ .../common/uni-stat/stat/mod/errorLog.js | 141 + .../common/uni-stat/stat/mod/errorResult.js | 459 ++++ .../common/uni-stat/stat/mod/event.js | 72 + .../common/uni-stat/stat/mod/eventLog.js | 156 ++ .../common/uni-stat/stat/mod/eventResult.js | 268 ++ .../common/uni-stat/stat/mod/index.js | 23 + .../common/uni-stat/stat/mod/loyalty.js | 491 ++++ .../common/uni-stat/stat/mod/page.js | 83 + .../common/uni-stat/stat/mod/pageLog.js | 186 ++ .../common/uni-stat/stat/mod/pageResult.js | 522 ++++ .../common/uni-stat/stat/mod/platform.js | 160 ++ .../common/uni-stat/stat/mod/runErrors.js | 20 + .../common/uni-stat/stat/mod/scenes.js | 80 + .../common/uni-stat/stat/mod/sessionLog.js | 343 +++ .../common/uni-stat/stat/mod/setting.js | 44 + .../common/uni-stat/stat/mod/shareLog.js | 104 + .../common/uni-stat/stat/mod/statResult.js | 2151 +++++++++++++++ .../uni-stat/stat/mod/uni-pay/dao/config.js | 12 + .../uni-stat/stat/mod/uni-pay/dao/index.js | 10 + .../stat/mod/uni-pay/dao/uniIdUsers.js | 62 + .../stat/mod/uni-pay/dao/uniPayOrders.js | 99 + .../stat/mod/uni-pay/dao/uniStatPayResult.js | 36 + .../mod/uni-pay/dao/uniStatSessionLogs.js | 78 + .../mod/uni-pay/dao/uniStatUserSessionLogs.js | 78 + .../common/uni-stat/stat/mod/uni-pay/index.js | 6 + .../uni-stat/stat/mod/uni-pay/payResult.js | 500 ++++ .../common/uni-stat/stat/mod/uniIDUsers.js | 97 + .../uni-stat/stat/mod/userSessionLog.js | 229 ++ .../common/uni-stat/stat/mod/version.js | 73 + .../common/uni-stat/stat/receiver.js | 126 + .../common/uni-stat/stat/stat.js | 388 +++ .../uni-portal/createPublishHtml/index.js | 188 ++ .../createPublishHtml/lib/art-template.js | 2 + .../createPublishHtml/template.html | 238 ++ .../cloudfunctions/uni-portal}/index.js | 16 +- .../cloudfunctions/uni-portal/package.json | 7 + .../uni-sms-co/build-template-data.js | 26 + .../cloudfunctions/uni-sms-co/index.obj.js | 374 +++ .../cloudfunctions/uni-sms-co/package.json | 10 + .../uni-sms-co/schema-name-adapter.js | 15 + .../cloudfunctions/uni-sms-co/utils.js | 42 + .../cloudfunctions/uni-stat-cron/index.js | 6 + .../cloudfunctions/uni-stat-cron/package.json | 27 + .../uni-stat-receiver/index.obj.js | 29 + .../uni-stat-receiver/package.json | 16 + .../uni-upgrade-center/checkVersion}/index.js | 28 +- .../uni-upgrade-center/index.js | 91 + .../uni-upgrade-center/package.json | 7 + .../uni-app-manager.param.json | 7 +- ...6\345\272\223\347\256\241\347\220\206.jql" | 12 - .../database/JQL\346\237\245\350\257\242.jql" | 9 - uniCloud-aliyun/database/db_init.json | 694 ++++- uniCloud-aliyun/database/default.jql | 12 - .../database/opendb-admin-menus.schema.json | 5 +- .../database/opendb-app-versions.schema.json | 100 +- .../database/opendb-department.schema.json | 47 - .../database/opendb-mall-goods.schema.json | 123 - .../database/opendb-sms-log.schema.json | 63 + .../database/opendb-sms-task.schema.json | 76 + .../database/opendb-sms-template.schema.json | 32 + .../database/opendb-tempdata.schema.json | 22 + .../database/uni-id-tag.schema.json | 8 +- .../database/uni-pay-orders.schema.json | 241 ++ .../uni-stat-active-devices.schema.json | 67 + .../uni-stat-active-users.schema.json | 55 + .../uni-stat-app-channels.schema.json | 44 + .../uni-stat-app-crash-logs.schema.json | 137 + .../uni-stat-app-platforms.schema.json | 46 + .../uni-stat-app-versions.schema.json | 38 + .../database/uni-stat-error-logs.schema.json | 102 + .../uni-stat-error-result.schema.json | 96 + .../database/uni-stat-event-logs.schema.json | 116 + .../uni-stat-event-result.schema.json | 82 + .../database/uni-stat-events.schema.json | 38 + .../uni-stat-loyalty-result.schema.json | 84 + .../database/uni-stat-mp-scenes.schema.json | 34 + .../database/uni-stat-page-logs.schema.json | 79 + .../database/uni-stat-page-result.schema.json | 114 + .../database/uni-stat-pages.schema.json | 34 + .../database/uni-stat-pay-result.schema.json | 169 ++ .../database/uni-stat-result.schema.json | 156 ++ .../database/uni-stat-run-errors.schema.json | 33 + .../uni-stat-session-logs.schema.json | 192 ++ .../database/uni-stat-share-logs.schema.json | 55 + .../uni-stat-user-session-logs.schema.json | 82 + uni_modules/Sansnn-uQRCode/changelog.md | 19 +- uni_modules/Sansnn-uQRCode/common/cache.js | 1 + uni_modules/Sansnn-uQRCode/common/queue.js | 41 + .../Sansnn-uQRCode/common/types/cache.d.ts | 3 + .../Sansnn-uQRCode/common/types/queue.d.ts | 4 + .../components/u-qrcode/u-qrcode.vue | 1900 +++++++------ .../components/uqrcode/uqrcode.vue | 1900 +++++++------ .../js_sdk/gcanvas/bridge/bridge-weex.js | 480 ++-- .../context-2d/FillStyleLinearGradient.js | 34 +- .../gcanvas/context-2d/FillStylePattern.js | 14 +- .../context-2d/FillStyleRadialGradient.js | 32 +- .../gcanvas/context-2d/RenderingContext.js | 1332 ++++----- .../gcanvas/context-webgl/ActiveInfo.js | 20 +- .../js_sdk/gcanvas/context-webgl/Buffer.js | 40 +- .../gcanvas/context-webgl/Framebuffer.js | 40 +- .../js_sdk/gcanvas/context-webgl/GLenum.js | 594 ++-- .../js_sdk/gcanvas/context-webgl/GLmethod.js | 282 +- .../js_sdk/gcanvas/context-webgl/GLtype.js | 46 +- .../js_sdk/gcanvas/context-webgl/Program.js | 40 +- .../gcanvas/context-webgl/Renderbuffer.js | 40 +- .../gcanvas/context-webgl/RenderingContext.js | 2380 ++++++++--------- .../js_sdk/gcanvas/context-webgl/Shader.js | 42 +- .../context-webgl/ShaderPrecisionFormat.js | 20 +- .../js_sdk/gcanvas/context-webgl/Texture.js | 42 +- .../gcanvas/context-webgl/UniformLocation.js | 42 +- .../gcanvas/context-webgl/classUtils.js | 4 +- .../js_sdk/gcanvas/env/canvas.js | 146 +- .../js_sdk/gcanvas/env/image.js | 190 +- .../Sansnn-uQRCode/js_sdk/gcanvas/env/tool.js | 46 +- .../Sansnn-uQRCode/js_sdk/gcanvas/index.js | 76 +- .../Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js | 39 +- uni_modules/Sansnn-uQRCode/license.md | 402 +-- uni_modules/Sansnn-uQRCode/package.json | 7 +- uni_modules/Sansnn-uQRCode/readme.md | 688 +++-- .../components/uni-badge/uni-badge.vue | 6 +- uni_modules/uni-captcha/changelog.md | 3 + uni_modules/uni-captcha/package.json | 11 +- .../common/uni-captcha/index.js | 2 +- uni_modules/uni-config-center/changelog.md | 10 +- uni_modules/uni-config-center/package.json | 17 +- .../common/uni-config-center/index.js | 2 +- .../common/uni-config-center/package.json | 2 +- uni_modules/uni-data-picker/changelog.md | 134 +- .../components/uni-data-picker/keypress.js | 90 +- .../uni-data-pickerview/uni-data-picker.js | 1126 ++++---- .../uni-data-pickerview.vue | 6 +- uni_modules/uni-data-picker/package.json | 178 +- uni_modules/uni-data-picker/readme.md | 42 +- uni_modules/uni-data-select/changelog.md | 2 + .../uni-data-select/uni-data-select.vue | 41 +- uni_modules/uni-data-select/package.json | 2 +- uni_modules/uni-datetime-picker/changelog.md | 2 + .../uni-datetime-picker/time-picker.vue | 30 +- .../uni-datetime-picker.vue | 19 +- uni_modules/uni-datetime-picker/package.json | 2 +- uni_modules/uni-easyinput/changelog.md | 66 +- .../uni-easyinput/uni-easyinput.vue | 1156 ++++---- uni_modules/uni-easyinput/package.json | 2 +- uni_modules/uni-file-picker/changelog.md | 2 + .../uni-file-picker/choose-and-upload-file.js | 6 +- .../uni-file-picker/uni-file-picker.vue | 17 +- uni_modules/uni-file-picker/package.json | 11 +- uni_modules/uni-id-pages/changelog.md | 38 +- .../uni-id-pages/common/login-page.mixin.js | 2 +- .../uni-id-pages/common/login-page.scss | 3 +- uni_modules/uni-id-pages/common/store.js | 290 +- .../uni-id-pages-avatar.vue | 14 +- .../uni-id-pages-bind-mobile.vue | 14 +- .../uni-id-pages-email-form.vue | 21 +- .../uni-id-pages-fab-login.vue | 49 +- .../uni-id-pages-sms-form.vue | 30 +- .../uni-id-pages-user-profile.vue | 2 +- uni_modules/uni-id-pages/config.js | 13 +- uni_modules/uni-id-pages/init.js | 28 +- uni_modules/uni-id-pages/package.json | 2 +- .../pages/common/webview/webview.vue | 2 +- .../pages/login/login-smscode.vue | 9 +- .../pages/login/login-withoutpwd.vue | 22 +- .../pages/login/login-withpwd.vue | 11 +- .../pages/register/register-admin.vue | 12 +- .../pages/register/register-by-email.vue | 8 +- .../uni-id-pages/pages/register/register.vue | 9 +- .../pages/retrieve/retrieve-by-email.vue | 22 +- .../uni-id-pages/pages/retrieve/retrieve.vue | 10 +- .../userinfo/bind-mobile/bind-mobile.vue | 13 +- .../pages/userinfo/change_pwd/change_pwd.vue | 8 +- .../pages/userinfo/cropImage/cropImage.vue | 2 +- .../cropImage/limeClipper/limeClipper.vue | 12 +- .../pages/userinfo/deactivate/deactivate.vue | 5 +- .../pages/userinfo/set-pwd/set-pwd.vue | 170 ++ .../uni-id-pages/pages/userinfo/userinfo.vue | 98 +- .../uni-id-co/common/constants.js | 9 +- .../cloudfunctions/uni-id-co/common/error.js | 5 +- .../uni-id-co/common/universal.js | 47 + .../cloudfunctions/uni-id-co/common/utils.js | 21 +- .../uni-id-co/common/validator.js | 17 +- .../uni-id-co/config/permission.js | 3 + .../cloudfunctions/uni-id-co/index.obj.js | 42 +- .../cloudfunctions/uni-id-co/lang/en.js | 5 +- .../cloudfunctions/uni-id-co/lang/zh-hans.js | 5 +- .../uni-id-co/lib/utils/account.js | 55 +- .../uni-id-co/lib/utils/captcha.js | 4 +- .../uni-id-co/lib/utils/config.js | 2 +- .../uni-id-co/lib/utils/login.js | 26 +- .../uni-id-co/lib/utils/logout.js | 16 +- .../uni-id-co/lib/utils/password.js | 281 +- .../cloudfunctions/uni-id-co/lib/utils/qq.js | 24 +- .../uni-id-co/lib/utils/register.js | 28 +- .../uni-id-co/lib/utils/relate.js | 6 +- .../uni-id-co/lib/utils/unified-login.js | 12 +- .../uni-id-co/lib/utils/univerify.js | 2 +- .../uni-id-co/lib/utils/utils.js | 18 + .../uni-id-co/lib/utils/verify-code.js | 2 +- .../uni-id-co/lib/utils/weixin.js | 48 +- .../uni-id-co/middleware/auth.js | 2 +- .../uni-id-co/middleware/index.js | 1 + .../uni-id-co/middleware/uni-id-log.js | 2 +- .../middleware/verify-request-sign.js | 39 + .../uni-id-co/module/account/index.js | 1 + .../module/account/reset-pwd-by-email.js | 13 +- .../module/account/reset-pwd-by-sms.js | 13 +- .../uni-id-co/module/account/set-pwd.js | 83 + .../uni-id-co/module/account/update-pwd.js | 6 +- .../uni-id-co/module/admin/add-user.js | 5 +- .../uni-id-co/module/admin/update-user.js | 21 +- .../uni-id-co/module/external/index.js | 4 + .../uni-id-co/module/external/login.js | 29 + .../uni-id-co/module/external/register.js | 52 + .../uni-id-co/module/login/login-by-qq.js | 2 +- .../module/login/login-by-weixin-mobile.js | 5 +- .../uni-id-co/module/login/login-by-weixin.js | 65 +- .../uni-id-co/module/multi-end/utils.js | 4 +- .../module/register/register-admin.js | 2 +- .../uni-id-co/module/relate/bind-qq.js | 4 +- .../uni-id-co/module/relate/bind-weixin.js | 4 +- .../uni-id-co/module/relate/unbind-qq.js | 2 +- .../uni-id-co/module/utils/index.js | 3 +- .../uni-id-co/module/utils/refresh-token.js | 2 +- .../secure-network-handshake-by-weixin.js | 73 + .../uni-id-co/module/utils/set-push-cid.js | 4 +- .../uni-id-co/module/verify/create-captcha.js | 2 +- .../module/verify/refresh-captcha.js | 2 +- .../module/verify/send-email-code.js | 14 +- .../uni-id-co/module/verify/send-sms-code.js | 2 +- .../cloudfunctions/uni-id-co/package.json | 8 +- .../database/uni-id-users.schema.json | 2 +- uni_modules/uni-id/changelog.md | 2 + uni_modules/uni-id/package.json | 22 +- uni_modules/uni-id/uni-id.zip | Bin 0 -> 42348 bytes .../cloudfunctions/common/uni-id/index.js | 2 +- .../cloudfunctions/common/uni-id/package.json | 5 +- uni_modules/uni-list/changelog.md | 20 + .../uni-list-chat/uni-list-chat.vue | 45 +- .../uni-list-item/uni-list-item.vue | 980 +++---- .../uni-list/components/uni-list/uni-list.vue | 189 +- uni_modules/uni-list/package.json | 11 +- uni_modules/uni-nav-bar/changelog.md | 6 + .../components/uni-nav-bar/uni-nav-bar.vue | 17 +- uni_modules/uni-nav-bar/package.json | 11 +- uni_modules/uni-popup/changelog.md | 4 + .../uni-popup-dialog/uni-popup-dialog.vue | 29 +- .../components/uni-popup/uni-popup.vue | 946 +++---- uni_modules/uni-popup/package.json | 13 +- uni_modules/uni-table/changelog.md | 2 + .../components/uni-table/uni-table.vue | 2 +- .../components/uni-th/filter-dropdown.vue | 24 +- .../uni-table/components/uni-th/uni-th.vue | 117 +- .../components/uni-tr/table-checkbox.vue | 14 +- uni_modules/uni-table/package.json | 11 +- uni_modules/uni-ui/changelog.md | 5 + uni_modules/uni-ui/package.json | 2 +- .../uni-upgrade-center-app/changelog.md | 2 + .../uni-upgrade-center-app/package.json | 4 +- .../uni-upgrade-center-app/pages_init.json | 18 - .../static/app_update_close.png | Bin 7644 -> 0 bytes .../uni-upgrade-center-app/static/bg_top.png | Bin 30486 -> 0 bytes .../static/button_bg.png | Bin 4156 -> 0 bytes .../uniCloud/cloudfunctions/temp.png | Bin 7644 -> 0 bytes .../database/opendb-app-list.schema.json | 391 ++- 285 files changed, 24331 insertions(+), 8760 deletions(-) create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/package.json create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/create-api.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/error.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/utils.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/date.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/uni-crypto.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeDevices.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeUsers.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/appCrashLogs.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/base.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/channel.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/device.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/errorLog.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/errorResult.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/event.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/eventLog.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/eventResult.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/loyalty.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/page.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/pageLog.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/pageResult.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/platform.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/runErrors.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/scenes.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/sessionLog.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/setting.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/shareLog.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/statResult.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/config.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniIdUsers.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniPayOrders.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatPayResult.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatSessionLogs.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatUserSessionLogs.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/payResult.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uniIDUsers.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/userSessionLog.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/version.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/receiver.js create mode 100644 uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/stat.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/lib/art-template.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/template.html rename {uni_modules/uni-upgrade-center/uniCloud/cloudfunctions/upgrade-center => uniCloud-aliyun/cloudfunctions/uni-portal}/index.js (55%) create mode 100644 uniCloud-aliyun/cloudfunctions/uni-portal/package.json create mode 100644 uniCloud-aliyun/cloudfunctions/uni-sms-co/build-template-data.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-sms-co/index.obj.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-sms-co/package.json create mode 100644 uniCloud-aliyun/cloudfunctions/uni-sms-co/schema-name-adapter.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-sms-co/utils.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-stat-cron/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-stat-cron/package.json create mode 100644 uniCloud-aliyun/cloudfunctions/uni-stat-receiver/index.obj.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-stat-receiver/package.json rename {uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version => uniCloud-aliyun/cloudfunctions/uni-upgrade-center/checkVersion}/index.js (88%) create mode 100644 uniCloud-aliyun/cloudfunctions/uni-upgrade-center/index.js create mode 100644 uniCloud-aliyun/cloudfunctions/uni-upgrade-center/package.json rename uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version/check-version.param.json => uniCloud-aliyun/cloudfunctions/uni-upgrade-center/uni-app-manager.param.json (60%) delete mode 100644 "uniCloud-aliyun/database/JQL\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206.jql" delete mode 100644 "uniCloud-aliyun/database/JQL\346\237\245\350\257\242.jql" delete mode 100644 uniCloud-aliyun/database/default.jql delete mode 100644 uniCloud-aliyun/database/opendb-department.schema.json delete mode 100644 uniCloud-aliyun/database/opendb-mall-goods.schema.json create mode 100644 uniCloud-aliyun/database/opendb-sms-log.schema.json create mode 100644 uniCloud-aliyun/database/opendb-sms-task.schema.json create mode 100644 uniCloud-aliyun/database/opendb-sms-template.schema.json create mode 100644 uniCloud-aliyun/database/opendb-tempdata.schema.json create mode 100644 uniCloud-aliyun/database/uni-pay-orders.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-active-devices.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-active-users.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-app-channels.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-app-crash-logs.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-app-platforms.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-app-versions.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-error-logs.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-error-result.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-event-logs.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-event-result.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-events.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-loyalty-result.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-mp-scenes.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-page-logs.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-page-result.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-pages.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-pay-result.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-result.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-run-errors.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-session-logs.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-share-logs.schema.json create mode 100644 uniCloud-aliyun/database/uni-stat-user-session-logs.schema.json create mode 100644 uni_modules/Sansnn-uQRCode/common/cache.js create mode 100644 uni_modules/Sansnn-uQRCode/common/queue.js create mode 100644 uni_modules/Sansnn-uQRCode/common/types/cache.d.ts create mode 100644 uni_modules/Sansnn-uQRCode/common/types/queue.d.ts create mode 100644 uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd.vue create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/universal.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/utils.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/verify-request-sign.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/set-pwd.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/login.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/register.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/secure-network-handshake-by-weixin.js create mode 100644 uni_modules/uni-id/uni-id.zip delete mode 100644 uni_modules/uni-upgrade-center-app/pages_init.json delete mode 100644 uni_modules/uni-upgrade-center-app/static/app_update_close.png delete mode 100644 uni_modules/uni-upgrade-center-app/static/bg_top.png delete mode 100644 uni_modules/uni-upgrade-center-app/static/button_bg.png delete mode 100644 uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/temp.png diff --git a/App.vue b/App.vue index e3f3a825..65eaabd5 100644 --- a/App.vue +++ b/App.vue @@ -18,9 +18,6 @@ console.log('App Launch') this.globalData.$i18n = this.$i18n this.globalData.$t = str => this.$t(str) - - console.log('uni.getPushClientId',uni.getPushClientId); - initApp(); uniIdPageInit() diff --git a/common/appInit.js b/common/appInit.js index 24e56723..49ae52d6 100644 --- a/common/appInit.js +++ b/common/appInit.js @@ -1,7 +1,7 @@ import uniStarterConfig from '@/uni-starter.config.js'; //应用初始化页 // #ifdef APP-PLUS -import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update'; +import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update'; import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version'; // 实现,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面 https://ext.dcloud.net.cn/plugin?id=5095 @@ -154,7 +154,8 @@ function initAppVersion() { // 有新版本 getApp({ allowDefault: true - }).appVersion.hasNew = true; + }).appVersion.hasNew = true; + console.log(checkUpdate()); } }) }); diff --git a/manifest.json b/manifest.json index da54b188..253530e7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,71 +1,68 @@ { - "name": "", - "appid": "", - "description": "云端一体应用快速开发基本项目模版", - "versionName": "", - "versionCode": "100", - "transformPx": false, - "app-plus": { - "usingComponents": true, - "nvueStyleCompiler": "uni-app", - "compilerVersion": 3, - "splashscreen": { - "alwaysShowBeforeRender": true, - "waiting": true, - "autoclose": true, - "delay": 0 + "name" : "uni-starter", + "appid" : "__UNI__07F7150", + "description" : "云端一体应用快速开发基本项目模版", + "versionName" : "1.0.0", + "versionCode" : "100", + "transformPx" : false, + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + "modules" : {}, + "distribute" : { + "android" : { + "permissions" : [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + }, + "ios" : {}, + "sdkConfigs" : { + "push" : { + "unipush" : null + } + } + } }, - "modules": { + "quickapp" : {}, + "mp-weixin" : { + "appid" : "wx999bf02c8e05dfc9", + "setting" : { + "urlCheck" : false + }, + "usingComponents" : true }, - "distribute": { - "android": { - "permissions": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ] - }, - "ios": { - }, - "sdkConfigs": { - "push": { - "unipush": null - } - } - } - }, - "quickapp": { - }, - "mp-weixin": { - "appid": "", - "setting": { - "urlCheck": false + "mp-alipay" : { + "usingComponents" : true + }, + "mp-baidu" : { + "usingComponents" : true + }, + "mp-toutiao" : { + "usingComponents" : true + }, + "uniStatistics" : { + "enable" : false }, - "usingComponents": true - }, - "mp-alipay": { - "usingComponents": true - }, - "mp-baidu": { - "usingComponents": true - }, - "mp-toutiao": { - "usingComponents": true - }, - "uniStatistics": { - "enable": false - }, - "vueVersion": "2" -} \ No newline at end of file + "vueVersion" : "2" +} diff --git a/pages.json b/pages.json index 6cff5c3c..f0e17ba5 100644 --- a/pages.json +++ b/pages.json @@ -192,7 +192,14 @@ "navigationBarTitleText": "注册管理员账号" } } - ], + ,{ + "path": "uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd", + "style": { + "enablePullDownRefresh": false, + "navigationBarTitleText": "设置密码" + } +} +], "globalStyle": { // #ifdef H5 "h5": { diff --git a/pages/list/detail.vue b/pages/list/detail.vue index 87ca7bb2..be8d257a 100644 --- a/pages/list/detail.vue +++ b/pages/list/detail.vue @@ -20,7 +20,7 @@ @@ -79,7 +80,7 @@ colList(){ return [ db.collection('opendb-news-articles').where(this.where).field('avatar,title,last_modify_date,user_id').getTemp(), - db.collection('uni-id-users').field('_id,username').getTemp() + db.collection('uni-id-users').field('_id,nickname').getTemp() ] } }, diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/index.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/index.js new file mode 100644 index 00000000..209175d3 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/index.js @@ -0,0 +1,23 @@ +const { + createApi +} = require('./shared/index') + +let reportDataReceiver, dataStatCron +module.exports = { + //uni统计数据上报数据接收器初始化 + initReceiver: (options = {}) => { + if(!reportDataReceiver) { + reportDataReceiver = require('./stat/receiver') + } + options.clientType = options.clientType || __ctx__.PLATFORM + return createApi(reportDataReceiver, options) + }, + //uni统计数据统计模块初始化 + initStat: (options = {}) => { + if(!dataStatCron) { + dataStatCron = require('./stat/stat') + } + options.clientType = options.clientType || __ctx__.PLATFORM + return createApi(dataStatCron, options) + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/package.json b/uniCloud-aliyun/cloudfunctions/common/uni-stat/package.json new file mode 100644 index 00000000..e2c1c536 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/package.json @@ -0,0 +1,15 @@ +{ + "name": "uni-stat", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "uni-config-center": "file:../../../../uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center" + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/create-api.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/create-api.js new file mode 100644 index 00000000..eaff3e2e --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/create-api.js @@ -0,0 +1,82 @@ +const { + isFn, + isPlainObject +} = require('./utils') + +/** + * 实例参数处理,注意:不进行递归处理 + * @param {Object} params 初始参数 + * @param {Object} rule 规则集 + * @returns {Object} 处理后的参数 + */ +function parseParams (params = {}, rule) { + if (!rule || !params) { + return params + } + const internalKeys = ['_pre', '_purify', '_post'] + // 转换之前的处理 + if (rule._pre) { + params = rule._pre(params) + } + // 净化参数 + let purify = { shouldDelete: new Set([]) } + if (rule._purify) { + const _purify = rule._purify + for (const purifyKey in _purify) { + _purify[purifyKey] = new Set(_purify[purifyKey]) + } + purify = Object.assign(purify, _purify) + } + if (isPlainObject(rule)) { + for (const key in rule) { + const parser = rule[key] + if (isFn(parser) && internalKeys.indexOf(key) === -1) { + params[key] = parser(params) + } else if (typeof parser === 'string' && internalKeys.indexOf(key) === -1) { + // 直接转换属性名称的删除旧属性名 + params[key] = params[parser] + purify.shouldDelete.add(parser) + } + } + } else if (isFn(rule)) { + params = rule(params) + } + + if (purify.shouldDelete) { + for (const item of purify.shouldDelete) { + delete params[item] + } + } + + // 转换之后的处理 + if (rule._post) { + params = rule._post(params) + } + + return params +} + +/** + * 返回一个提供应用上下文的应用实例。应用实例挂载的整个组件树共享同一个上下文 + * @param {class} ApiClass 实例类 + * @param {Object} options 参数 + * @returns {Object} 实例类对象 + */ +module.exports = function createApi (ApiClass, options) { + const apiInstance = new ApiClass(options) + return new Proxy(apiInstance, { + get: function (obj, prop) { + if (typeof obj[prop] === 'function' && prop.indexOf('_') !== 0 && obj._protocols && obj._protocols[prop]) { + const protocol = obj._protocols[prop] + return async function (params) { + params = parseParams(params, protocol.args) + let result = await obj[prop](params) + result = parseParams(result, protocol.returnValue) + return result + } + } else { + return obj[prop] + } + } + }) +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/error.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/error.js new file mode 100644 index 00000000..98d47637 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/error.js @@ -0,0 +1,19 @@ +/** + * @class UniCloudError 错误处理模块 + */ +module.exports = class UniCloudError extends Error { + constructor (options) { + super(options.message) + this.errMsg = options.message || '' + Object.defineProperties(this, { + message: { + get () { + return `errCode: ${options.code || ''} | errMsg: ` + this.errMsg + }, + set (msg) { + this.errMsg = msg + } + } + }) + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/index.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/index.js new file mode 100644 index 00000000..ee2ff28c --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/index.js @@ -0,0 +1,6 @@ +module.exports = { + UniCloudError: require('./error'), + createApi: require('./create-api'), + ... require('./utils') +} + diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/utils.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/utils.js new file mode 100644 index 00000000..56b71b40 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/shared/utils.js @@ -0,0 +1,197 @@ +const _toString = Object.prototype.toString +const hasOwnProperty = Object.prototype.hasOwnProperty + +/** + * 检查对象是否包含某个属性 + * @param {Object} obj 对象 + * @param {String} key 属性键值 + */ +function hasOwn(obj, key) { + return hasOwnProperty.call(obj, key) +} + +/** + * 参数是否为JavaScript的简单对象 + * @param {Object} obj + * @returns {Boolean} true|false + */ +function isPlainObject(obj) { + return _toString.call(obj) === '[object Object]' +} + +/** + * 是否为函数 + * @param {String} fn 函数名 + */ +function isFn(fn) { + return typeof fn === 'function' +} + +/** + * 深度克隆对象 + * @param {Object} obj + */ +function deepClone(obj) { + return JSON.parse(JSON.stringify(obj)) +} + + +/** + * 解析客户端上报的参数 + * @param {String} primitiveParams 原始参数 + * @param {Object} context 附带的上下文 + */ +function parseUrlParams(primitiveParams, context) { + if (!primitiveParams) { + return primitiveParams + } + + let params = {} + if(typeof primitiveParams === 'string') { + params = primitiveParams.split('&').reduce((res, cur) => { + const arr = cur.split('=') + return Object.assign({ + [arr[0]]: arr[1] + }, res) + }, {}) + } else { + //转换参数类型--兼容性 + for(let key in primitiveParams) { + if(typeof primitiveParams[key] === 'number') { + params[key] = primitiveParams[key] + '' + } else { + params[key] = primitiveParams[key] + } + } + } + + //原以下数据要从客户端上报,现调整为如果以下参数客户端未上报,则通过请求附带的context参数中获取 + const convertParams = { + //appid + ak: 'appId', + //当前登录用户编号 + uid: 'uid', + //设备编号 + did: 'deviceId', + //uni-app 运行平台,与条件编译平台相同。 + up: 'uniPlatform', + //操作系统名称 + p: 'osName', + //因为p参数可能会被前端覆盖掉,所以这里单独拿出来一个osName + on: 'osName', + //客户端ip + ip: 'clientIP', + //客户端的UA + ua: 'userAgent', + //当前服务空间编号 + spid: 'spaceId', + //当前服务空间提供商 + sppd: 'provider', + //应用版本号 + v: 'appVersion', + //rom 名称 + rn: 'romName', + //rom 版本 + rv: 'romVersion', + //操作系统版本 + sv: 'osVersion', + //操作系统语言 + lang: 'osLanguage', + //操作系统主题 + ot: 'osTheme', + //设备类型 + dtp: 'deviceType', + //设备品牌 + brand: 'deviceBrand', + //设备型号 + md: 'deviceModel', + //设备像素比 + pr: 'devicePixelRatio', + //可使用窗口宽度 + ww: 'windowWidth', + //可使用窗口高度 + wh: 'windowHeight', + //屏幕宽度 + sw: 'screenWidth', + //屏幕高度 + sh: 'screenHeight', + } + context = context ? context : {} + for (let key in convertParams) { + if (!params[key] && context[convertParams[key]]) { + params[key] = context[convertParams[key]] + } + } + + return params +} + +/** + * 解析url + * @param {String} url + */ +function parseUrl(url) { + if (typeof url !== "string" || !url) { + return false + } + const urlInfo = url.split('?') + + baseurl = urlInfo[0] + if (baseurl !== '/' && baseurl.indexOf('/') === 0) { + baseurl = baseurl.substr(1) + } + + return { + path: baseurl, + query: urlInfo[1] ? decodeURI(urlInfo[1]) : '' + } +} + +//加载配置中心-uni-config-center +let createConfig +try { + createConfig = require('uni-config-center') +} catch (e) {} + +/** + * 获取配置文件信息 + * @param {String} file 配置文件名称 + * @param {String} key 配置参数键值 + */ +function getConfig(file, key) { + if (!file) { + return false + } + + const uniConfig = createConfig && createConfig({ + pluginId: 'uni-stat' + }) + + if (!uniConfig || !uniConfig.hasFile(file + '.json')) { + console.error('Not found the config file') + return false + } + + const config = uniConfig.requireFile(file) + + return key ? config[key] : config +} + +/** + * 休眠 + * @param {Object} ms 休眠时间(毫秒) + */ +function sleep(ms) { + return new Promise(resolve => setTimeout(() => resolve(), ms)) +} + +module.exports = { + hasOwn, + isPlainObject, + isFn, + deepClone, + parseUrlParams, + parseUrl, + getConfig, + sleep +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/date.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/date.js new file mode 100644 index 00000000..88310711 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/date.js @@ -0,0 +1,371 @@ +/** + * @class DateTime + * @description 日期处理模块 + */ +module.exports = class DateTime { + constructor() { + //默认日期展示格式 + this.defaultDateFormat = 'Y-m-d H:i:s' + //默认时区 + this.defaultTimezone = 8 + this.setTimeZone(this.defaultTimezone) + } + + /** + * 设置时区 + * @param {Number} timezone 时区 + */ + setTimeZone(timezone) { + if (timezone) { + this.timezone = parseInt(timezone) + } + return this + } + + /** + * 获取 Date对象 + * @param {Date|Time} time + */ + getDateObj(time) { + return time ? new Date(time) : new Date() + } + + /** + * 获取毫秒/秒级时间戳 + * @param {DateTime} datetime 日期 例:'2022-04-21 00:00:00' + * @param {Boolean} showSenconds 是否显示为秒级时间戳 + */ + getTime(datetime, showSenconds) { + let time = this.getDateObj(datetime).getTime() + if (showSenconds) { + time = Math.trunc(time / 1000) + } + return time + } + + /** + * 获取日期 + * @param {String} dateFormat 日期格式 + * @param {Time} time 时间戳 + */ + getDate(dateFormat, time) { + return this.dateFormat(dateFormat, time) + } + + + /** + * 获取日期在不同时区下的时间戳 + * @param {Date|Time}} time 日期或时间戳 + * @param {Object} timezone 时区 + */ + getTimeByTimeZone(time, timezone) { + this.setTimeZone(timezone) + const thisDate = time ? new Date(time) : new Date() + const localTime = thisDate.getTime() + const offset = thisDate.getTimezoneOffset() + const utc = offset * 60000 + localTime + return utc + (3600000 * this.timezone) + } + + /** + * 获取时间信息 + * @param {Time} time 时间戳 + * @param {Boolean} full 是否完整展示, 为true时小于10的位会自动补0 + */ + getTimeInfo(time, full = true) { + time = this.getTimeByTimeZone(time) + const date = this.getDateObj(time) + const retData = { + nYear: date.getFullYear(), + nMonth: date.getMonth() + 1, + nWeek: date.getDay() || 7, + nDay: date.getDate(), + nHour: date.getHours(), + nMinutes: date.getMinutes(), + nSeconds: date.getSeconds() + } + + if (full) { + for (const k in retData) { + if (retData[k] < 10) { + retData[k] = '0' + retData[k] + } + } + } + return retData + } + + /** + * 时间格式转换 + * @param {String} format 展示格式如:Y-m-d H:i:s + * @param {Time} time 时间戳 + */ + dateFormat(format, time) { + const timeInfo = this.getTimeInfo(time) + format = format || this.defaultDateFormat + let date = format + if (format.indexOf('Y') > -1) { + date = date.replace(/Y/, timeInfo.nYear) + } + if (format.indexOf('m') > -1) { + date = date.replace(/m/, timeInfo.nMonth) + } + if (format.indexOf('d') > -1) { + date = date.replace(/d/, timeInfo.nDay) + } + if (format.indexOf('H') > -1) { + date = date.replace(/H/, timeInfo.nHour) + } + if (format.indexOf('i') > -1) { + date = date.replace(/i/, timeInfo.nMinutes) + } + if (format.indexOf('s') > -1) { + date = date.replace(/s/, timeInfo.nSeconds) + } + return date + } + + /** + * 获取utc格式时间 + * @param {Date|Time} datetime 日期或时间戳 + */ + getUTC(datetime) { + return this.getDateObj(datetime).toUTCString() + } + + /** + * 获取ISO 格式时间 + * @param {Date|Time} datetime 日期或时间戳 + */ + getISO(datetime) { + return this.getDateObj(datetime).toISOString() + } + + /** + * 获取两时间相差天数 + * @param {Time} time1 时间戳 + * @param {Time} time2 时间戳 + */ + getDiffDays(time1, time2) { + if (!time1) { + return false + } + time2 = time2 ? time2 : this.getTime() + + let diffTime = time2 - time1 + if (diffTime < 0) { + diffTime = Math.abs(diffTime) + } + + return Math.ceil(diffTime / 86400000) + } + + /** + * 字符串转时间戳 + * @param {Object} str 字符串类型的时间戳 + */ + strToTime(str) { + if (Array.from(str).length === 10) { + str += '000' + } + return this.getTime(parseInt(str)) + } + + /** + * 根据设置的天数获取指定日期N天后(前)的时间戳 + * @param {Number} days 天数 + * @param {Date|Time} time 指定的日期或时间戳 + * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳) + */ + getTimeBySetDays(days, time, getAll = false) { + const date = this.getDateObj(time) + date.setDate(date.getDate() + days) + let startTime = date.getTime() + if (!getAll) { + const realdate = this.getDate('Y-m-d 00:00:00', startTime) + startTime = this.getTimeByDateAndTimezone(realdate) + } + return startTime + } + + /** + * 根据设置的小时数获取指定日期N小时后(前)的时间戳 + * @param {Number} hours 小时数 + * @param {Date|Time} time 指定的日期或时间戳 + * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定时间初始时间戳(该小时00:00的时间戳) + */ + getTimeBySetHours(hours, time, getAll = false) { + const date = this.getDateObj(time) + date.setHours(date.getHours() + hours) + let startTime = date.getTime() + if (!getAll) { + const realdate = this.getDate('Y-m-d H:00:00', startTime) + startTime = this.getTimeByDateAndTimezone(realdate) + } + return startTime + } + + /** + * 根据设置的周数获取指定日期N周后(前)的时间戳 + * @param {Number} weeks 周数 + * @param {Date|Time} time 指定的日期或时间戳 + * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳) + */ + getTimeBySetWeek(weeks, time, getAll = false) { + const date = this.getDateObj(time) + const dateInfo = this.getTimeInfo(time) + const day = dateInfo.nWeek + const offsetDays = 1 - day + weeks = weeks * 7 + offsetDays + date.setDate(date.getDate() + weeks) + let startTime = date.getTime() + if (!getAll) { + const realdate = this.getDate('Y-m-d 00:00:00', startTime) + startTime = this.getTimeByDateAndTimezone(realdate) + } + return startTime + } + + /** + * 根据设置的月数获取指定日期N月后(前)的时间戳 + * @param {Number} monthes 月数 + * @param {Date|Time} time 指定的日期或时间戳 + * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳) + */ + getTimeBySetMonth(monthes, time, getAll = false) { + const date = this.getDateObj(time) + date.setMonth(date.getMonth() + monthes) + let startTime = date.getTime() + if (!getAll) { + const realdate = this.getDate('Y-m-01 00:00:00', startTime) + startTime = this.getTimeByDateAndTimezone(realdate) + } + return startTime + } + + /** + * 根据设置的季度数获取指定日期N个季度后(前)的时间戳 + * @param {Number} quarter 季度 + * @param {Date|Time} time 指定的日期或时间戳 + * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳) + */ + getTimeBySetQuarter(quarter, time, getAll = false) { + const date = this.getDateObj(time) + const dateInfo = this.getTimeInfo(time) + date.setMonth(date.getMonth() + quarter * 3) + const month = date.getMonth() + 1; + let quarterN; + let mm; + if ([1,2,3].indexOf(month) > -1) { + // 第1季度 + mm = "01"; + } else if ([4,5,6].indexOf(month) > -1) { + // 第2季度 + mm = "04"; + } else if ([7,8,9].indexOf(month) > -1) { + // 第3季度 + mm = "07"; + } else if ([10,11,12].indexOf(month) > -1) { + // 第4季度 + mm = "10"; + } + let yyyy = date.getFullYear(); + let startTime = date.getTime() + if (!getAll) { + const realdate = this.getDate(`${yyyy}-${mm}-01 00:00:00`, startTime) + startTime = this.getTimeByDateAndTimezone(realdate) + } + return startTime + } + + /** + * 根据设置的年数获取指定日期N年后(前)的时间戳 + * @param {Number} year 月数 + * @param {Date|Time} time 指定的日期或时间戳 + * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳) + */ + getTimeBySetYear(year, time, getAll = false) { + const date = this.getDateObj(time) + date.setFullYear(date.getFullYear() + year) + let startTime = date.getTime() + if (!getAll) { + const realdate = this.getDate('Y-01-01 00:00:00', startTime) + startTime = this.getTimeByDateAndTimezone(realdate) + } + return startTime + } + + + /** + * 根据时区获取指定时间的偏移时间 + * @param {Date|Time} 指定的日期或时间戳 + * @param {Number} timezone 时区 + */ + getTimeByDateAndTimezone(date, timezone) { + if (!timezone) { + timezone = this.timezone + } + const thisDate = this.getDateObj(date) + const thisTime = thisDate.getTime() + const offset = thisDate.getTimezoneOffset() + const offsetTime = offset * 60000 + timezone * 3600000 + return thisTime - offsetTime + } + + /** + * 根据指定的时间类型获取时间范围 + * @param {String} type 时间类型 hour:小时 day:天 week:周 month:月 + * @param {Number} offset 时间的偏移量 + * @param {Date|Time} thistime 指定的日期或时间戳 + * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳) + */ + getTimeDimensionByType(type, offset = 0, thistime, getAll = false) { + let startTime = 0 + let endTime = 0 + switch (type) { + case 'hour': { + startTime = this.getTimeBySetHours(offset, thistime, getAll) + endTime = getAll ? startTime : startTime + 3599999 + break + } + case 'day': { + startTime = this.getTimeBySetDays(offset, thistime, getAll) + endTime = getAll ? startTime : startTime + 86399999 + break + } + case 'week': { + startTime = this.getTimeBySetWeek(offset, thistime, getAll) + endTime = getAll ? startTime + 86400000 * 6 : startTime + 86400000 * 6 + 86399999 + break + } + case 'month': { + startTime = this.getTimeBySetMonth(offset, thistime, getAll) + const date = this.getDateObj(this.getDate('Y-m-d H:i:s', startTime)) + const nextMonthFirstDayTime = new Date(date.getFullYear(), date.getMonth() + 1, 1).getTime() + endTime = getAll ? nextMonthFirstDayTime - 86400000 : this.getTimeByDateAndTimezone( + nextMonthFirstDayTime) - 1 + break + } + case 'quarter': { + startTime = this.getTimeBySetQuarter(offset, thistime, getAll) + const date = this.getDateObj(this.getDate('Y-m-d H:i:s', startTime)) + const nextMonthFirstDayTime = new Date(date.getFullYear(), date.getMonth() + 3, 1).getTime() + endTime = getAll ? nextMonthFirstDayTime - 86400000 : this.getTimeByDateAndTimezone( + nextMonthFirstDayTime) - 1 + break + } + case 'year': { + startTime = this.getTimeBySetYear(offset, thistime, getAll) + const date = this.getDateObj(this.getDate('Y-m-d H:i:s', startTime)) + const nextFirstDayTime = new Date(date.getFullYear() + 1, 0, 1).getTime() + endTime = getAll ? nextFirstDayTime - 86400000 : this.getTimeByDateAndTimezone( + nextFirstDayTime) - 1 + break + } + } + return { + startTime, + endTime + } + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/index.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/index.js new file mode 100644 index 00000000..aaeff55f --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/index.js @@ -0,0 +1,4 @@ +module.exports = { + DateTime: require('./date'), + UniCrypto: require('./uni-crypto') +} \ No newline at end of file diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/uni-crypto.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/uni-crypto.js new file mode 100644 index 00000000..c8f16f99 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/uni-crypto.js @@ -0,0 +1,98 @@ +/** + * @class UniCrypto 数据加密服务 + * @function init 初始化函数 + * @function showConfig 返回配置信息函数 + * @function getCrypto 返回原始crypto对象函数 + * @function aesEncode AES加密函数 + * @function aesDecode AES解密函数 + * @function md5 MD5加密函数 + */ +const crypto = require('crypto') +module.exports = class UniCrypto { + constructor(config) { + this.init(config) + } + + /** + * 配置初始化函数 + * @param {Object} config + */ + init(config) { + this.config = { + //AES加密默认参数 + AES: { + mod: 'aes-128-cbc', + pasword: 'UniStat!010', + iv: 'UniStativ', + charset: 'utf8', + encodeReturnType: 'base64' + }, + //MD5加密默认参数 + MD5: { + encodeReturnType: 'hex' + }, + ...config || {} + } + return this + } + + /** + * 返回配置信息函数 + */ + showConfig() { + return this.config + } + + /** + * 返回原始crypto对象函数 + */ + getCrypto() { + return crypto + } + + /** + * AES加密函数 + * @param {String} data 加密数据明文 + * @param {String} encodeReturnType 返回加密数据类型,如:base64 + * @param {String} key 密钥 + * @param {String} iv 偏移量 + * @param {String} mod 模式 + * @param {String} charset 编码 + */ + aesEncode(data, encodeReturnType, key, iv, mod, charset) { + const cipher = crypto.createCipheriv(mod || this.config.AES.mod, key || this.config.AES.pasword, iv || + this.config.AES.iv) + let crypted = cipher.update(data, charset || this.config.AES.charset, 'binary') + crypted += cipher.final('binary') + crypted = Buffer.from(crypted, 'binary').toString(encodeReturnType || this.config.AES.encodeReturnType) + return crypted + } + + /** + * AES解密函数 + * @param {Object} crypted 加密数据密文 + * @param {Object} encodeReturnType 返回加密数据类型,如:base64 + * @param {Object} key 密钥 + * @param {Object} iv 偏移量 + * @param {Object} mod 模式 + * @param {Object} charset 编码 + */ + aesDecode(crypted, encodeReturnType, key, iv, mod, charset) { + crypted = Buffer.from(crypted, encodeReturnType || this.config.AES.encodeReturnType).toString('binary') + const decipher = crypto.createDecipheriv(mod || this.config.AES.mod, key || this.config.AES.pasword, + iv || this.config.AES.iv) + let decoded = decipher.update(crypted, 'binary', charset || this.config.AES.charset) + decoded += decipher.final(charset || this.config.AES.charset) + return decoded + } + + /** + * @param {Object} str 加密字符串 + * @param {Object} encodeReturnType encodeReturnType 返回加密数据类型,如:hex(转为16进制) + */ + md5(str, encodeReturnType) { + const md5Mod = crypto.createHash('md5') + md5Mod.update(str) + return md5Mod.digest(encodeReturnType || this.config.MD5.encodeReturnType) + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeDevices.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeDevices.js new file mode 100644 index 00000000..c8df84e4 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeDevices.js @@ -0,0 +1,528 @@ +/** + * @class ActiveDevices 活跃设备模型 - 每日跑批合并,仅添加本周/本月首次访问的设备。 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const Version = require('./version') +const SessionLog = require('./sessionLog') +const { + DateTime, + UniCrypto +} = require('../lib') +module.exports = class ActiveDevices extends BaseMod { + constructor() { + super() + this.tableName = 'active-devices' + this.platforms = [] + this.channels = [] + this.versions = [] + } + + /** + * @desc 活跃设备统计 - 为周统计/月统计提供周活/月活数据 + * @param {date|time} date + * @param {bool} reset + */ + async stat(date, reset) { + const dateTime = new DateTime() + const dateDimension = dateTime.getTimeDimensionByType('day', -1, date) + this.startTime = dateDimension.startTime + // 查看当前时间段数据是否已存在,防止重复生成 + if (!reset) { + const checkRes = await this.getCollection(this.tableName).where({ + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }).get() + if (checkRes.data.length > 0) { + console.log('data have exists') + return { + code: 1003, + msg: 'Devices data in this time have already existed' + } + } + } else { + const delRes = await this.delete(this.tableName, { + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }) + console.log('Delete old data result:', JSON.stringify(delRes)) + } + + const sessionLog = new SessionLog() + const statRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + is_first_visit: 1, + create_time: 1, + device_id: 1 + }, + match: { + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + device_id: '$device_id' + }, + is_new: { + $max: '$is_first_visit' + }, + create_time: { + $min: '$create_time' + } + }, + sort: { + create_time: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + // if (this.debug) { + // console.log('statRes', JSON.stringify(statRes)) + // } + if (statRes.data.length > 0) { + const uniCrypto = new UniCrypto() + // 同应用、平台、渠道、版本的数据合并 + const statData = []; + let statKey; + let data + + const statOldRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + is_first_visit: 1, + create_time: 1, + old_device_id: 1 + }, + match: { + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + }, + old_device_id: {$exists: true} + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + old_device_id: '$old_device_id' + }, + create_time: { + $min: '$create_time' + } + }, + sort: { + create_time: 1 + }, + getAll: true + }) + if (this.debug) { + console.log('statOldRes', JSON.stringify(statOldRes)) + } + for (const sti in statRes.data) { + data = statRes.data[sti] + statKey = uniCrypto.md5(data._id.appid + data._id.platform + data._id.version + data._id + .channel) + if (!statData[statKey]) { + statData[statKey] = { + appid: data._id.appid, + platform: data._id.platform, + version: data._id.version, + channel: data._id.channel, + device_ids: [], + old_device_ids: [], + info: [], + old_info: [] + } + statData[statKey].device_ids.push(data._id.device_id) + statData[statKey].info[data._id.device_id] = { + is_new: data.is_new, + create_time: data.create_time + } + } else { + statData[statKey].device_ids.push(data._id.device_id) + statData[statKey].info[data._id.device_id] = { + is_new: data.is_new, + create_time: data.create_time + } + } + } + if(statOldRes.data.length) { + const oldDeviceIds = [] + for(const osti in statOldRes.data) { + if(!statOldRes.data[osti]._id.old_device_id) { + continue + } + oldDeviceIds.push(statOldRes.data[osti]._id.old_device_id) + } + if(oldDeviceIds.length) { + const statOldDidRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + is_first_visit: 1, + create_time: 1, + device_id: 1 + }, + match: { + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + }, + device_id: {$in: oldDeviceIds} + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + old_device_id: '$device_id' + }, + create_time: { + $min: '$create_time' + } + }, + sort: { + create_time: 1 + }, + getAll: true + }) + + if(statOldDidRes.data.length){ + for(const osti in statOldDidRes.data) { + data = statOldDidRes.data[osti] + statKey = uniCrypto.md5(data._id.appid + data._id.platform + data._id.version + data._id + .channel) + if(!data._id.old_device_id) { + continue + } + + if (!statData[statKey]) { + statData[statKey] = { + appid: data._id.appid, + platform: data._id.platform, + version: data._id.version, + channel: data._id.channel, + device_ids: [], + old_device_ids: [], + old_info: [] + } + statData[statKey].old_device_ids.push(data._id.old_device_id) + } else { + statData[statKey].old_device_ids.push(data._id.old_device_id) + } + if(!statData[statKey].old_info[data._id.old_device_id]) { + statData[statKey].old_info[data._id.old_device_id] = { + create_time: data.create_time + } + } + } + } + } + } + this.fillData = [] + for (const sk in statData) { + await this.getFillData(statData[sk]) + } + + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + } + return res + } + + /** + * 获取填充数据 + * @param {Object} data + */ + async getFillData(data) { + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data.platform]) { + platformInfo = this.platforms[data.platform] + } else { + const platform = new Platform() + platformInfo = await platform.getPlatformAndCreate(data.platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[data.platform] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + + // 渠道信息 + let channelInfo = null + const channelKey = data.appid + '_' + platformInfo._id + '_' + data.channel + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey] + } else { + const channel = new Channel() + channelInfo = await channel.getChannelAndCreate(data.appid, platformInfo._id, data.channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + + // 版本信息 + let versionInfo = null + const versionKey = data.appid + '_' + data.platform + '_' + data.version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const version = new Version() + versionInfo = await version.getVersionAndCreate(data.appid, data.platform, data.version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + const datetime = new DateTime() + const dateDimension = datetime.getTimeDimensionByType('week', 0, this.startTime) + const dateMonthDimension = datetime.getTimeDimensionByType('month', 0, this.startTime) + + if(data.device_ids) { + // 取出本周已经存储的device_id + const weekHaveDeviceList = [] + const haveWeekList = await this.selectAll(this.tableName, { + appid: data.appid, + version_id: versionInfo._id, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + device_id: { + $in: data.device_ids + }, + dimension: 'week', + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }, { + device_id: 1 + }) + if (haveWeekList.data.length > 0) { + for (const hui in haveWeekList.data) { + weekHaveDeviceList.push(haveWeekList.data[hui].device_id) + } + } + if (this.debug) { + console.log('weekHaveDeviceList', JSON.stringify(weekHaveDeviceList)) + } + + // 取出本月已经存储的device_id + const monthHaveDeviceList = [] + const haveMonthList = await this.selectAll(this.tableName, { + appid: data.appid, + version_id: versionInfo._id, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + device_id: { + $in: data.device_ids + }, + dimension: 'month', + create_time: { + $gte: dateMonthDimension.startTime, + $lte: dateMonthDimension.endTime + } + }, { + device_id: 1 + }) + if (haveMonthList.data.length > 0) { + for (const hui in haveMonthList.data) { + monthHaveDeviceList.push(haveMonthList.data[hui].device_id) + } + } + if (this.debug) { + console.log('monthHaveDeviceList', JSON.stringify(monthHaveDeviceList)) + } + //数据填充 + for (const ui in data.device_ids) { + //周活跃数据填充 + if (!weekHaveDeviceList.includes(data.device_ids[ui])) { + this.fillData.push({ + appid: data.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + is_new: data.info[data.device_ids[ui]].is_new, + device_id: data.device_ids[ui], + dimension: 'week', + create_time: data.info[data.device_ids[ui]].create_time + }) + } + //月活跃数据填充 + if (!monthHaveDeviceList.includes(data.device_ids[ui])) { + this.fillData.push({ + appid: data.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + is_new: data.info[data.device_ids[ui]].is_new, + device_id: data.device_ids[ui], + dimension: 'month', + create_time: data.info[data.device_ids[ui]].create_time + }) + } + } + } + + if(data.old_device_ids) { + // 取出本周已经存储的old_device_id + const weekHaveOldDeviceList = [] + const haveOldWeekList = await this.selectAll(this.tableName, { + appid: data.appid, + version_id: versionInfo._id, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + device_id: { + $in: data.old_device_ids + }, + dimension: 'week-old', + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }, { + device_id: 1 + }) + if (haveOldWeekList.data.length > 0) { + for (const hui in haveOldWeekList.data) { + weekHaveOldDeviceList.push(haveOldWeekList.data[hui].device_id) + } + } + if (this.debug) { + console.log('weekHaveOldDeviceList', JSON.stringify(weekHaveOldDeviceList)) + } + + // 取出本月已经存储的old_device_id + const monthHaveOldDeviceList = [] + const haveOldMonthList = await this.selectAll(this.tableName, { + appid: data.appid, + version_id: versionInfo._id, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + device_id: { + $in: data.old_device_ids + }, + dimension: 'month-old', + create_time: { + $gte: dateMonthDimension.startTime, + $lte: dateMonthDimension.endTime + } + }, { + device_id: 1 + }) + if (haveOldMonthList.data.length > 0) { + for (const hui in haveOldMonthList.data) { + monthHaveOldDeviceList.push(haveOldMonthList.data[hui].device_id) + } + } + if (this.debug) { + console.log('monthHaveOldDeviceList', JSON.stringify(monthHaveOldDeviceList)) + } + //数据填充 + for (const ui in data.old_device_ids) { + //周活跃数据填充 + if (!weekHaveOldDeviceList.includes(data.old_device_ids[ui])) { + this.fillData.push({ + appid: data.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + is_new: 0, + device_id: data.old_device_ids[ui], + dimension: 'week-old', + create_time: data.old_info[data.old_device_ids[ui]].create_time + }) + } + //月活跃数据填充 + if (!monthHaveOldDeviceList.includes(data.old_device_ids[ui])) { + this.fillData.push({ + appid: data.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + is_new: 0, + device_id: data.old_device_ids[ui], + dimension: 'month-old', + create_time: data.old_info[data.old_device_ids[ui]].create_time + }) + } + } + } + + return true + } + + /** + * 日志清理,此处日志为临时数据并不需要自定义清理,默认为固定值即可 + */ + async clean() { + // 清除周数据,周留存统计最高需要10周数据,多余的为无用数据 + const weeks = 10 + console.log('Clean device\'s weekly logs - week:', weeks) + + const dateTime = new DateTime() + + const res = await this.delete(this.tableName, { + dimension: 'week', + create_time: { + $lt: dateTime.getTimeBySetWeek(0 - weeks) + } + }) + + if (!res.code) { + console.log('Clean device\'s weekly logs - res:', res) + } + + // 清除月数据,月留存统计最高需要10个月数据,多余的为无用数据 + const monthes = 10 + console.log('Clean device\'s monthly logs - month:', monthes) + const monthRes = await this.delete(this.tableName, { + dimension: 'month', + create_time: { + $lt: dateTime.getTimeBySetMonth(0 - monthes) + } + }) + if (!monthRes.code) { + console.log('Clean device\'s monthly logs - res:', res) + } + return monthRes + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeUsers.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeUsers.js new file mode 100644 index 00000000..475e3ea4 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeUsers.js @@ -0,0 +1,314 @@ +/** + * @class ActiveUsers 活跃用户模型 - 每日跑批合并,仅添加本周/本月首次访问的用户。 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const Version = require('./version') +const UserSessionLog = require('./userSessionLog') +const { + DateTime, + UniCrypto +} = require('../lib') +module.exports = class ActiveUsers extends BaseMod { + constructor() { + super() + this.tableName = 'active-users' + this.platforms = [] + this.channels = [] + this.versions = [] + } + + async stat(date, reset) { + const dateTime = new DateTime() + const dateDimension = dateTime.getTimeDimensionByType('day', -1, date) + this.startTime = dateDimension.startTime + // 查看当前时间段数据是否已存在,防止重复生成 + if (!reset) { + const checkRes = await this.getCollection(this.tableName).where({ + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }).get() + if (checkRes.data.length > 0) { + console.log('data have exists') + return { + code: 1003, + msg: 'Users data in this time have already existed' + } + } + } else { + const delRes = await this.delete(this.tableName, { + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }) + console.log('Delete old data result:', JSON.stringify(delRes)) + } + + const userSessionLog = new UserSessionLog() + const statRes = await this.aggregate(userSessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + create_time: 1, + uid: 1 + }, + match: { + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + uid: '$uid' + }, + create_time: { + $min: '$create_time' + } + }, + sort: { + create_time: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + // if (this.debug) { + // console.log('statRes', JSON.stringify(statRes)) + // } + if (statRes.data.length > 0) { + const uniCrypto = new UniCrypto() + // 同应用、平台、渠道、版本的数据合并 + const statData = []; + let statKey; + let data + + for (const sti in statRes.data) { + data = statRes.data[sti] + statKey = uniCrypto.md5(data._id.appid + data._id.platform + data._id.version + data._id + .channel) + if (!statData[statKey]) { + statData[statKey] = { + appid: data._id.appid, + platform: data._id.platform, + version: data._id.version, + channel: data._id.channel, + uids: [], + info: [] + } + statData[statKey].uids.push(data._id.uid) + statData[statKey].info[data._id.uid] = { + create_time: data.create_time + } + } else { + statData[statKey].uids.push(data._id.uid) + statData[statKey].info[data._id.uid] = { + create_time: data.create_time + } + } + } + + this.fillData = [] + for (const sk in statData) { + await this.getFillData(statData[sk]) + } + + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + } + return res + } + + async getFillData(data) { + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data.platform]) { + platformInfo = this.platforms[data.platform] + } else { + const platform = new Platform() + platformInfo = await platform.getPlatformAndCreate(data.platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[data.platform] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + + // 渠道信息 + let channelInfo = null + const channelKey = data.appid + '_' + platformInfo._id + '_' + data.channel + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey] + } else { + const channel = new Channel() + channelInfo = await channel.getChannelAndCreate(data.appid, platformInfo._id, data.channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + + // 版本信息 + let versionInfo = null + const versionKey = data.appid + '_' + data.platform + '_' + data.version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const version = new Version() + versionInfo = await version.getVersionAndCreate(data.appid, data.platform, data.version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + // 是否在本周内已存在 + const datetime = new DateTime() + const dateDimension = datetime.getTimeDimensionByType('week', 0, this.startTime) + + // 取出本周已经存储的uid + const weekHaveUserList = [] + const haveWeekList = await this.selectAll(this.tableName, { + appid: data.appid, + version_id: versionInfo._id, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + uid: { + $in: data.uids + }, + dimension: 'week', + create_time: { + $gte: dateDimension.startTime, + $lte: dateDimension.endTime + } + }, { + uid: 1 + }) + + if (this.debug) { + console.log('haveWeekList', JSON.stringify(haveWeekList)) + } + + if (haveWeekList.data.length > 0) { + for (const hui in haveWeekList.data) { + weekHaveUserList.push(haveWeekList.data[hui].uid) + } + } + + // 取出本月已经存储的uid + const dateMonthDimension = datetime.getTimeDimensionByType('month', 0, this.startTime) + const monthHaveUserList = [] + const haveMonthList = await this.selectAll(this.tableName, { + appid: data.appid, + version_id: versionInfo._id, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + uid: { + $in: data.uids + }, + dimension: 'month', + create_time: { + $gte: dateMonthDimension.startTime, + $lte: dateMonthDimension.endTime + } + }, { + uid: 1 + }) + + if (this.debug) { + console.log('haveMonthList', JSON.stringify(haveMonthList)) + } + + if (haveMonthList.data.length > 0) { + for (const hui in haveMonthList.data) { + monthHaveUserList.push(haveMonthList.data[hui].uid) + } + } + + for (const ui in data.uids) { + if (!weekHaveUserList.includes(data.uids[ui])) { + this.fillData.push({ + appid: data.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + uid: data.uids[ui], + dimension: 'week', + create_time: data.info[data.uids[ui]].create_time + }) + } + + if (!monthHaveUserList.includes(data.uids[ui])) { + this.fillData.push({ + appid: data.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + uid: data.uids[ui], + dimension: 'month', + create_time: data.info[data.uids[ui]].create_time + }) + } + } + + return true + } + + /** + * 日志清理,此处日志为临时数据并不需要自定义清理,默认为固定值即可 + */ + async clean() { + // 清除周数据,周留存统计最高需要10周数据,多余的为无用数据 + const weeks = 10 + console.log('Clean user\'s weekly logs - week:', weeks) + + const dateTime = new DateTime() + + const res = await this.delete(this.tableName, { + dimension: 'week', + create_time: { + $lt: dateTime.getTimeBySetWeek(0 - weeks) + } + }) + + if (!res.code) { + console.log('Clean user\'s weekly logs - res:', res) + } + + // 清除月数据,月留存统计最高需要10个月数据,多余的为无用数据 + const monthes = 10 + console.log('Clean user\'s monthly logs - month:', monthes) + const monthRes = await this.delete(this.tableName, { + dimension: 'month', + create_time: { + $lt: dateTime.getTimeBySetMonth(0 - monthes) + } + }) + if (!monthRes.code) { + console.log('Clean user\'s monthly logs - res:', res) + } + return monthRes + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/appCrashLogs.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/appCrashLogs.js new file mode 100644 index 00000000..221602d4 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/appCrashLogs.js @@ -0,0 +1,37 @@ +/** + * @class AppCrashLogs 原生应用崩溃日志模型 + * @function clean 原生应用崩溃日志清理函数 + */ +const BaseMod = require('./base') +const { + DateTime, + UniCrypto +} = require('../lib') +module.exports = class AppCrashLogs extends BaseMod { + constructor() { + super() + this.tableName = 'app-crash-logs' + } + + /** + * 原生应用崩溃日志清理函数 + * @param {Number} days 保留天数 + */ + async clean(days = 7) { + days = Math.max(parseInt(days), 1) + console.log('clean app crash logs - day:', days) + + const dateTime = new DateTime() + + const res = await this.delete(this.tableName, { + create_time: { + $lt: dateTime.getTimeBySetDays(0 - days) + } + }) + + if (!res.code) { + console.log('clean app crash log:', res) + } + return res + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/base.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/base.js new file mode 100644 index 00000000..2914df25 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/base.js @@ -0,0 +1,485 @@ +/** + * @class BaseMod 数据模型基类,提供基础服务支持 + */ +const { + getConfig +} = require('../../shared') +//基类 +module.exports = class BaseMod { + constructor() { + //配置信息 + this.config = getConfig('config') + //开启/关闭debug + this.debug = this.config.debug + //主键 + this.primaryKey = '_id' + //单次查询最多返回 500 条数据(阿里云500,腾讯云1000,这里取最小值) + this.selectMaxLimit = 500 + //数据表前缀 + this.tablePrefix = 'uni-stat' + //数据表连接符 + this.tableConnectors = '-' + //数据表名 + this.tableName = '' + //参数 + this.params = {} + //数据库连接 + this._dbConnection() + //redis连接 + this._redisConnection() + } + + /** + * 建立uniCloud数据库连接 + */ + _dbConnection() { + if (!this.db) { + try { + this.db = uniCloud.database() + this.dbCmd = this.db.command + this.dbAggregate = this.dbCmd.aggregate + } catch (e) { + console.error('database connection failed: ' + e) + throw new Error('database connection failed: ' + e) + } + } + } + + /** + * 建立uniCloud redis连接 + */ + _redisConnection() { + if (this.config.redis && !this.redis) { + try { + this.redis = uniCloud.redis() + } catch (e) { + console.log('redis server connection failed: ' + e) + } + } + } + + /** + * 获取uni统计配置项 + * @param {String} key + */ + getConfig(key) { + return this.config[key] + } + + /** + * 获取带前缀的数据表名称 + * @param {String} tab 表名 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + getTableName(tab, useDBPre = true) { + tab = tab || this.tableName + const table = (useDBPre && this.tablePrefix && tab.indexOf(this.tablePrefix) !== 0) ? this.tablePrefix + this + .tableConnectors + tab : tab + return table + } + + /** + * 获取数据集 + * @param {String} tab表名 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + getCollection(tab, useDBPre = true) { + return this.db.collection(this.getTableName(tab, useDBPre)) + } + + /** + * 获取reids缓存 + * @param {String} key reids缓存键值 + */ + async getCache(key) { + if (!this.redis || !key) { + return false + } + let cacheResult = await this.redis.get(key) + + if (this.debug) { + console.log('get cache result by key:' + key, cacheResult) + } + + if (cacheResult) { + try { + cacheResult = JSON.parse(cacheResult) + } catch (e) { + if (this.debug) { + console.log('json parse error: ' + e) + } + } + } + return cacheResult + } + + /** + * 设置redis缓存 + * @param {String} key 键值 + * @param {String} val 值 + * @param {Number} expireTime 过期时间 + */ + async setCache(key, val, expireTime) { + if (!this.redis || !key) { + return false + } + + if (val instanceof Object) { + val = JSON.stringify(val) + } + + if (this.debug) { + console.log('set cache result by key:' + key, val) + } + + return await this.redis.set(key, val, 'EX', expireTime || this.config.cachetime) + } + + /** + * 清除redis缓存 + * @param {String} key 键值 + */ + async clearCache(key) { + if (!this.redis || !key) { + return false + } + + if (this.debug) { + console.log('delete cache by key:' + key) + } + + return await this.redis.del(key) + } + + /** + * 通过数据表主键(_id)获取数据 + * @param {String} tab 表名 + * @param {String} id 主键值 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + async getById(tab, id, useDBPre = true) { + const condition = {} + condition[this.primaryKey] = id + const info = await this.getCollection(tab, useDBPre).where(condition).get() + return (info && info.data.length > 0) ? info.data[0] : [] + } + + /** + * 插入数据到数据表 + * @param {String} tab 表名 + * @param {Object} params 字段参数 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + async insert(tab, params, useDBPre = true) { + params = params || this.params + return await this.getCollection(tab, useDBPre).add(params) + } + + /** + * 修改数据表数据 + * @param {String} tab 表名 + * @param {Object} params 字段参数 + * @param {Object} condition 条件 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + async update(tab, params, condition, useDBPre = true) { + params = params || this.params + return await this.getCollection(tab).where(condition).update(params) + } + + /** + * 删除数据表数据 + * @param {String} tab 表名 + * @param {Object} condition 条件 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + async delete(tab, condition, useDBPre = true) { + if (!condition) { + return false + } + return await this.getCollection(tab, useDBPre).where(condition).remove() + } + + /** + * 批量插入 - 云服务空间对单条mongo语句执行时间有限制,所以批量插入需限制每次执行条数 + * @param {String} tab 表名 + * @param {Object} data 数据集合 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + async batchInsert(tab, data, useDBPre = true) { + let batchInsertNum = this.getConfig('batchInsertNum') || 3000 + batchInsertNum = Math.min(batchInsertNum, 5000) + const insertNum = Math.ceil(data.length / batchInsertNum) + let start; + let end; + let fillData; + let insertRes; + const res = { + code: 0, + msg: 'success', + data: { + inserted: 0 + } + } + for (let p = 0; p < insertNum; p++) { + start = p * batchInsertNum + end = Math.min(start + batchInsertNum, data.length) + fillData = [] + for (let i = start; i < end; i++) { + fillData.push(data[i]) + } + if (fillData.length > 0) { + insertRes = await this.insert(tab, fillData, useDBPre) + if (insertRes && insertRes.inserted) { + res.data.inserted += insertRes.inserted + } + } + } + return res + } + + /** + * 批量删除 - 云服务空间对单条mongo语句执行时间有限制,所以批量删除需限制每次执行条数 + * @param {String} tab 表名 + * @param {Object} condition 条件 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + async batchDelete(tab, condition, useDBPre = true) { + const batchDeletetNum = 5000; + let deleteIds; + let delRes; + let thisCondition + const res = { + code: 0, + msg: 'success', + data: { + deleted: 0 + } + } + let run = true + while (run) { + const dataRes = await this.getCollection(tab).where(condition).limit(batchDeletetNum).get() + if (dataRes && dataRes.data.length > 0) { + deleteIds = [] + for (let i = 0; i < dataRes.data.length; i++) { + deleteIds.push(dataRes.data[i][this.primaryKey]) + } + if (deleteIds.length > 0) { + thisCondition = {} + thisCondition[this.primaryKey] = { + $in: deleteIds + } + delRes = await this.delete(tab, thisCondition, useDBPre) + if (delRes && delRes.deleted) { + res.data.deleted += delRes.deleted + } + } + } else { + run = false + } + } + return res + } + + /** + * 基础查询 + * @param {String} tab 表名 + * @param {Object} params 查询参数 where:where条件,field:返回字段,skip:跳过的文档数,limit:返回的记录数,orderBy:排序,count:返回查询结果的数量 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + async select(tab, params, useDBPre = true) { + const { + where, + field, + skip, + limit, + orderBy, + count + } = params + + const query = this.getCollection(tab, useDBPre) + + //拼接where条件 + if (where) { + if (where.length > 0) { + where.forEach(key => { + query.where(where[key]) + }) + } else { + query.where(where) + } + } + + //排序 + if (orderBy) { + Object.keys(orderBy).forEach(key => { + query.orderBy(key, orderBy[key]) + }) + } + + //指定跳过的文档数 + if (skip) { + query.skip(skip) + } + + //指定返回的记录数 + if (limit) { + query.limit(limit) + } + + //指定返回字段 + if (field) { + query.field(field) + } + + //指定返回查询结果数量 + if (count) { + return await query.count() + } + + //返回查询结果数据 + return await query.get() + } + + /** + * 查询并返回全部数据 + * @param {String} tab 表名 + * @param {Object} condition 条件 + * @param {Object} field 指定查询返回字段 + * @param {Boolean} useDBPre 是否使用数据表前缀 + */ + async selectAll(tab, condition, field = {}, useDBPre = true) { + const countRes = await this.getCollection(tab, useDBPre).where(condition).count() + if (countRes && countRes.total > 0) { + const pageCount = Math.ceil(countRes.total / this.selectMaxLimit) + let res, returnData + for (let p = 0; p < pageCount; p++) { + res = await this.getCollection(tab, useDBPre).where(condition).orderBy(this.primaryKey, 'asc').skip(p * + this.selectMaxLimit).limit(this.selectMaxLimit).field(field).get() + if (!returnData) { + returnData = res + } else { + returnData.affectedDocs += res.affectedDocs + for (const i in res.data) { + returnData.data.push(res.data[i]) + } + } + } + return returnData + } + return { + affectedDocs: 0, + data: [] + } + } + + /** + * 聚合查询 + * @param {String} tab 表名 + * @param {Object} params 聚合参数 + */ + async aggregate(tab, params) { + let { + project, + match, + lookup, + group, + skip, + limit, + sort, + getAll, + useDBPre, + addFields + } = params + //useDBPre 是否使用数据表前缀 + useDBPre = (useDBPre !== null && useDBPre !== undefined) ? useDBPre : true + const query = this.getCollection(tab, useDBPre).aggregate() + + //设置返回字段 + if (project) { + query.project(project) + } + + //设置匹配条件 + if (match) { + query.match(match) + } + + //数据表关联 + if (lookup) { + query.lookup(lookup) + } + + //分组 + if (group) { + if (group.length > 0) { + for (const gi in group) { + query.group(group[gi]) + } + } else { + query.group(group) + } + } + + //添加字段 + if (addFields) { + query.addFields(addFields) + } + + //排序 + if (sort) { + query.sort(sort) + } + + //分页 + if (skip) { + query.skip(skip) + } + if (limit) { + query.limit(limit) + } else if (!getAll) { + query.limit(this.selectMaxLimit) + } + + //如果未指定全部返回则直接返回查询结果 + if (!getAll) { + return await query.end() + } + + //若指定了全部返回则分页查询全部结果后再返回 + const resCount = await query.group({ + _id: {}, + aggregate_count: { + $sum: 1 + } + }).end() + + if (resCount && resCount.data.length > 0 && resCount.data[0].aggregate_count > 0) { + //分页查询 + const total = resCount.data[0].aggregate_count + const pageCount = Math.ceil(total / this.selectMaxLimit) + let res, returnData + params.limit = this.selectMaxLimit + params.getAll = false + //结果合并 + for (let p = 0; p < pageCount; p++) { + params.skip = p * params.limit + res = await this.aggregate(tab, params) + if (!returnData) { + returnData = res + } else { + returnData.affectedDocs += res.affectedDocs + for (const i in res.data) { + returnData.data.push(res.data[i]) + } + } + } + return returnData + } else { + return { + affectedDocs: 0, + data: [] + } + } + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/channel.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/channel.js new file mode 100644 index 00000000..d7c81bb4 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/channel.js @@ -0,0 +1,107 @@ +/** + * @class Channel 渠道模型 + */ +const BaseMod = require('./base') +const Scenes = require('./scenes') +const { + DateTime +} = require('../lib') +module.exports = class Channel extends BaseMod { + constructor() { + super() + this.tableName = 'app-channels' + this.scenes = new Scenes() + } + + /** + * 获取渠道信息 + * @param {String} appid + * @param {String} platformId 平台编号 + * @param {String} channel 渠道代码 + */ + async getChannel(appid, platformId, channel) { + const cacheKey = 'uni-stat-channel-' + appid + '-' + platformId + '-' + channel + let channelData = await this.getCache(cacheKey) + if (!channelData) { + const channelInfo = await this.getCollection(this.tableName).where({ + appid: appid, + platform_id: platformId, + channel_code: channel + }).limit(1).get() + channelData = [] + if (channelInfo.data.length > 0) { + channelData = channelInfo.data[0] + if (channelData.channel_name === '') { + const scenesName = await this.scenes.getScenesNameByPlatformId(platformId, channel) + if (scenesName) { + await this.update(this.tableName, { + channel_name: scenesName, + update_time: new DateTime().getTime() + }, { + _id: channelData._id + }) + } + } + await this.setCache(cacheKey, channelData) + } + } + return channelData + } + + /** + * 获取渠道信息没有则进行创建 + * @param {String} appid + * @param {String} platformId + * @param {String} channel + */ + async getChannelAndCreate(appid, platformId, channel) { + if (!appid || !platformId) { + return [] + } + + const channelInfo = await this.getChannel(appid, platformId, channel) + if (channelInfo.length === 0) { + const thisTime = new DateTime().getTime() + const insertParam = { + appid: appid, + platform_id: platformId, + channel_code: channel, + channel_name: await this.scenes.getScenesNameByPlatformId(platformId, channel), + create_time: thisTime, + update_time: thisTime + } + const res = await this.insert(this.tableName, insertParam) + if (res && res.id) { + return Object.assign(insertParam, { + _id: res.id + }) + } + } + return channelInfo + } + + /** + * 获取渠道_id + * @param {String} appid + * @param {String} platformId + * @param {String} channel + */ + async getChannelId(appid, platformId, channel) { + const channelInfo = await this.getChannel(appid, platformId, channel) + return channelInfo.length > 0 ? channelInfo._id : '' + } + + /** + * 获取渠道码或者场景值 + * @param {Object} params 上报参数 + */ + getChannelCode(params) { + //小程序未上报渠道则使用场景值 + if (params.ch) { + return params.ch + } else if (params.sc && params.ut.indexOf('mp-') === 0) { + return params.sc + } + return this.scenes.defualtCode + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/device.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/device.js new file mode 100644 index 00000000..d1d0e7a6 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/device.js @@ -0,0 +1,184 @@ +/** + * @class Device 设备模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const { + DateTime +} = require('../lib') +module.exports = class Device extends BaseMod { + constructor() { + super() + this.tableName = 'opendb-device' + this.tablePrefix = false + this.cacheKeyPre = 'uni-stat-device-' + } + + /** + * 通过设备编号获取设备信息 + * @param {Object} deviceId 设备编号 + */ + async getDeviceById(deviceId) { + const cacheKey = this.cacheKeyPre + deviceId + let deviceData = await this.getCache(cacheKey) + if (!deviceData) { + const deviceRes = await this.getCollection().where({ + device_id: deviceId + }).get() + deviceData = [] + if (deviceRes.data.length > 0) { + deviceData = deviceRes.data[0] + await this.setCache(cacheKey, deviceData) + } + } + return deviceData + } + + /** + * 设置设备信息 + * @param {Object} params 上报参数 + */ + async setDevice(params) { + // 设备信息 + if (!params.did) { + return { + code: 200, + msg: 'Parameter "did" not found' + } + } + const deviceData = await this.getDeviceById(params.did) + //不存在则添加 + if(deviceData.length === 0) { + return await this.addDevice(params) + } else { + return await this.updateDevice(params, deviceData) + } + } + + /** + * 添加设备信息 + * @param {Object} params 上报参数 + */ + async addDevice(params) { + const dateTime = new DateTime() + const platform = new Platform() + const fillParams = { + device_id: params.did, + appid: params.ak, + vendor: params.brand ? params.brand : '', + push_clientid: params.cid ? params.cid : '', + imei: params.imei ? params.imei : '', + oaid: params.oaid ? params.oaid : '', + idfa: params.idfa ? params.idfa : '', + imsi: params.imsi ? params.imsi : '', + model: params.md ? params.md : '', + uni_platform: params.up ? params.up : '', + os_name: params.on ? params.on : platform.getOsName(params.p), + os_version: params.sv ? params.sv : '', + os_language: params.lang ? params.lang : '', + os_theme: params.ot ? params.ot : '', + pixel_ratio: params.pr ? params.pr : '', + network_model: params.net ? params.net : '', + window_width: params.ww ? params.ww : '', + window_height: params.wh ? params.wh : '', + screen_width: params.sw ? params.sw : '', + screen_height: params.sh ? params.sh : '', + rom_name: params.rn ? params.rn : '', + rom_version: params.rv ? params.rv : '', + location_ip: params.ip ? params.ip : '', + location_latitude: params.lat ? parseFloat(params.lat) : 0, + location_longitude: params.lng ? parseFloat(params.lng) : 0, + location_country: params.cn ? params.cn : '', + location_province: params.pn ? params.pn : '', + location_city: params.ct ? params.ct : '', + create_date: dateTime.getTime(), + last_update_date: dateTime.getTime() + } + const res = await this.insert(this.tableName, fillParams) + if (res && res.id) { + return { + code: 0, + msg: 'success', + } + } else { + return { + code: 500, + msg: 'Device data filled error' + } + } + } + + /** + * 修改设备信息 + * @param {Object} params + * @param {Object} deviceData + */ + async updateDevice(params, deviceData) { + //最新的参数 + const dateTime = new DateTime() + const platform = new Platform() + console.log('device params', params) + const newDeviceParams = { + appid: params.ak, + push_clientid: params.cid ? params.cid : '', + imei: params.imei ? params.imei : '', + oaid: params.oaid ? params.oaid : '', + idfa: params.idfa ? params.idfa : '', + imsi: params.imsi ? params.imsi : '', + uni_platform: params.up ? params.up : '', + os_name: params.on ? params.on : platform.getOsName(params.p), + os_version: params.sv ? params.sv : '', + os_language: params.lang ? params.lang : '', + pixel_ratio: params.pr ? params.pr : '', + network_model: params.net ? params.net : '', + window_width: params.ww ? params.ww : '', + window_height: params.wh ? params.wh : '', + screen_width: params.sw ? params.sw : '', + screen_height: params.sh ? params.sh : '', + rom_name: params.rn ? params.rn : '', + rom_version: params.rv ? params.rv : '', + location_ip: params.ip ? params.ip : '', + location_latitude: params.lat ? parseFloat(params.lat) : '', + location_longitude: params.lng ? parseFloat(params.lng) : '', + location_country: params.cn ? params.cn : '', + location_province: params.pn ? params.pn : '', + location_city: params.ct ? params.ct : '', + } + + //检查是否有需要更新的数据 + const updateData = {} + for(let key in newDeviceParams) { + if(newDeviceParams[key] && newDeviceParams[key] !== deviceData[key]) { + updateData[key] = newDeviceParams[key] + } + } + + if(Object.keys(updateData).length) { + if(this.debug) { + console.log('Device need to update', updateData) + } + //数据更新 + updateData.last_update_date = dateTime.getTime() + await this.update(this.tableName, updateData, {device_id: params.did}) + } else { + if(this.debug) { + console.log('Device not need update', newDeviceParams) + } + } + + return { + code: 0, + msg: 'success' + } + } + + async bindPush(params) { + if (!params.cid) { + return { + code: 200, + msg: 'Parameter "cid" not found' + } + } + return await this.setDevice(params) + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/errorLog.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/errorLog.js new file mode 100644 index 00000000..2cd8b47e --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/errorLog.js @@ -0,0 +1,141 @@ +/** + * @class ErrorLog 错误日志模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const { + DateTime, + UniCrypto +} = require('../lib') +module.exports = class ErrorLog extends BaseMod { + constructor() { + super() + this.tableName = 'error-logs' + } + + /** + * 错误日志数据填充 + * @param {Object} reportParams 上报参数 + */ + async fill(reportParams) { + let params, errorHash, errorCount, cacheKey; + const fillParams = [] + const platform = new Platform() + const dateTime = new DateTime() + const uniCrypto = new UniCrypto() + const channel = new Channel() + const { + needCheck, + checkTime + } = this.getConfig('errorCheck') + const errorCheckTime = Math.max(checkTime, 1) + let spaceId + let spaceProvider + for (const rk in reportParams) { + params = reportParams[rk] + errorHash = uniCrypto.md5(params.em) + cacheKey = 'error-count-' + errorHash + // 校验在指定时间段内是否已存在相同的错误项 + if (needCheck) { + errorCount = await this.getCache(cacheKey) + if (!errorCount) { + errorCount = await this.getCollection(this.tableName).where({ + error_hash: errorHash, + create_time: { + $gte: dateTime.getTime() - errorCheckTime * 60000 + } + }).count() + if (errorCount && errorCount.total > 0) { + await this.setCache(cacheKey, errorCount, errorCheckTime * 60) + } + } + + if (errorCount && errorCount.total > 0) { + if (this.debug) { + console.log('This error have already existsed: ' + params.em) + } + continue + } + } + + //获取云端信息 + spaceId = null + spaceProvider = null + if (params.spi) { + //云函数调用参数 + spaceId = params.spi.spaceId + spaceProvider = params.spi.provider + } else { + //云对象调用参数 + if (params.spid) { + spaceId = params.spid + } + if (params.sppd) { + spaceProvider = params.sppd + } + } + + // 填充数据 + fillParams.push({ + appid: params.ak, + version: params.v ? params.v : '', + platform: platform.getPlatformCode(params.ut, params.p), + channel: channel.getChannelCode(params), + device_id: params.did, + uid: params.uid ? params.uid : '', + os: params.on ? params.on : platform.getOsName(params.p), + ua: params.ua ? params.ua : '', + page_url: params.url ? params.url : '', + space_id: spaceId ? spaceId : '', + space_provider: spaceProvider ? spaceProvider : '', + platform_version: params.mpv ? params.mpv : '', + error_msg: params.em ? params.em : '', + error_hash: errorHash, + create_time: dateTime.getTime() + }) + } + + if (fillParams.length === 0) { + return { + code: 200, + msg: 'Invild param' + } + } + + const res = await this.insert(this.tableName, fillParams) + if (res && res.inserted) { + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'Filled error' + } + } + } + + /** + * 错误日志清理 + * @param {Number} days 日志保留天数 + */ + async clean(days) { + days = Math.max(parseInt(days), 1) + console.log('clean error logs - day:', days) + + const dateTime = new DateTime() + + const res = await this.delete(this.tableName, { + create_time: { + $lt: dateTime.getTimeBySetDays(0 - days) + } + }) + + if (!res.code) { + console.log('clean error log:', res) + } + return res + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/errorResult.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/errorResult.js new file mode 100644 index 00000000..690cc028 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/errorResult.js @@ -0,0 +1,459 @@ +/** + * @class ErrorResult 错误结果统计模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const Version = require('./version') +const ErrorLog = require('./errorLog') +const AppCrashLogs = require('./appCrashLogs') +const SessionLog = require('./sessionLog') +const { + DateTime +} = require('../lib') +module.exports = class ErrorResult extends BaseMod { + constructor() { + super() + this.tableName = 'error-result' + this.platforms = [] + this.channels = [] + this.versions = [] + this.errors = [] + } + + /** + * 错误结果统计 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async stat(type, date, reset) { + //前端js错误统计 + const resJs = await this.statJs(type, date, reset) + //原生应用崩溃错误统计 + const resCrash = await this.statCrash(type, date, reset) + + return { + code: 0, + msg: 'success', + data: { + resJs, + resCrash + } + } + } + + /** + * 前端js错误结果统计 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async statJs(type, date, reset) { + const allowedType = ['day'] + if (!allowedType.includes(type)) { + return { + code: 1002, + msg: 'This type is not allowed' + } + } + this.fillType = type + const dateTime = new DateTime() + const dateDimension = dateTime.getTimeDimensionByType(type, -1, date) + this.startTime = dateDimension.startTime + this.endTime = dateDimension.endTime + + if (this.debug) { + console.log('dimension time', this.startTime + '--' + this.endTime) + } + + // 查看当前时间段日志是否已存在,防止重复生成 + if (!reset) { + const checkRes = await this.getCollection(this.tableName).where({ + type: 'js', + start_time: this.startTime, + end_time: this.endTime + }).get() + if (checkRes.data.length > 0) { + console.log('error log have existed') + return { + code: 1003, + msg: 'This log have existed' + } + } + } else { + const delRes = await this.delete(this.tableName, { + type: 'js', + start_time: this.startTime, + end_time: this.endTime + }) + console.log('delete old data result:', JSON.stringify(delRes)) + } + + // 数据获取 + this.errorLog = new ErrorLog() + const statRes = await this.aggregate(this.errorLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + error_hash: 1, + create_time: 1 + }, + match: { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + error_hash: '$error_hash' + }, + error_count: { + $sum: 1 + } + }, + sort: { + error_count: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + if (this.debug) { + console.log('statRes', JSON.stringify(statRes)) + } + if (statRes.data.length > 0) { + this.fillData = [] + for (const i in statRes.data) { + await this.fillJs(statRes.data[i]) + } + + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + } + return res + } + + /** + * 前端js错误统计结果数据填充 + * @param {Object} data 数据集合 + */ + async fillJs(data) { + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data._id.platform]) { + //暂存下数据,减少读库 + platformInfo = this.platforms[data._id.platform] + } else { + const platform = new Platform() + platformInfo = await platform.getPlatformAndCreate(data._id.platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[data._id.platform] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + + // 渠道信息 + let channelInfo = null + const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey] + } else { + const channel = new Channel() + channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + + // 版本信息 + let versionInfo = null + const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const version = new Version() + versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + // 错误信息 + let errorInfo = null + if (this.errors && this.errors[data._id.error_hash]) { + errorInfo = this.errors[data._id.error_hash] + } else { + const cacheKey = 'uni-stat-errors-' + data._id.error_hash + errorInfo = await this.getCache(cacheKey) + if (!errorInfo) { + errorInfo = await this.getCollection(this.errorLog.tableName).where({ + error_hash: data._id.error_hash + }).limit(1).get() + if (!errorInfo || errorInfo.data.length === 0) { + errorInfo.error_msg = '' + } else { + errorInfo = errorInfo.data[0] + await this.setCache(cacheKey, errorInfo) + } + } + this.errors[data._id.error_hash] = errorInfo + } + + // 最近一次报错时间 + const matchCondition = data._id + Object.assign(matchCondition, { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }) + const lastErrorLog = await this.getCollection(this.errorLog.tableName).where(matchCondition).orderBy( + 'create_time', 'desc').limit(1).get() + let lastErrorTime = '' + if (lastErrorLog && lastErrorLog.data.length > 0) { + lastErrorTime = lastErrorLog.data[0].create_time + } + + //数据填充 + const datetime = new DateTime() + const insertParams = { + appid: data._id.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + type: 'js', + hash: data._id.error_hash, + msg: errorInfo.error_msg, + count: data.error_count, + last_time: lastErrorTime, + dimension: this.fillType, + stat_date: datetime.getDate('Ymd', this.startTime), + start_time: this.startTime, + end_time: this.endTime + } + + this.fillData.push(insertParams) + + return insertParams + } + + + /** + * 原生应用错误结果统计 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async statCrash(type, date, reset) { + const allowedType = ['day'] + if (!allowedType.includes(type)) { + return { + code: 1002, + msg: 'This type is not allowed' + } + } + this.fillType = type + const dateTime = new DateTime() + const dateDimension = dateTime.getTimeDimensionByType(type, -1, date) + this.startTime = dateDimension.startTime + this.endTime = dateDimension.endTime + + if (this.debug) { + console.log('dimension time', this.startTime + '--' + this.endTime) + } + + // 查看当前时间段日志是否已存在,防止重复生成 + if (!reset) { + const checkRes = await this.getCollection(this.tableName).where({ + type: 'crash', + start_time: this.startTime, + end_time: this.endTime + }).get() + if (checkRes.data.length > 0) { + console.log('error log have existed') + return { + code: 1003, + msg: 'This log have existed' + } + } + } else { + const delRes = await this.delete(this.tableName, { + type: 'crash', + start_time: this.startTime, + end_time: this.endTime + }) + console.log('delete old data result:', JSON.stringify(delRes)) + } + + // 数据获取 + this.crashLogs = new AppCrashLogs() + const statRes = await this.aggregate(this.crashLogs.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + create_time: 1 + }, + match: { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel' + }, + error_count: { + $sum: 1 + } + }, + sort: { + error_count: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + if (this.debug) { + console.log('statRes', JSON.stringify(statRes)) + } + if (statRes.data.length > 0) { + this.fillData = [] + for (const i in statRes.data) { + await this.fillCrash(statRes.data[i]) + } + + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + } + return res + } + + async fillCrash(data) { + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data._id.platform]) { + //暂存下数据,减少读库 + platformInfo = this.platforms[data._id.platform] + } else { + const platform = new Platform() + platformInfo = await platform.getPlatformAndCreate(data._id.platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[data._id.platform] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + + // 渠道信息 + let channelInfo = null + data._id.channel = data._id.channel ? data._id.channel : '1001' + const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey] + } else { + const channel = new Channel() + channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + + // 版本信息 + let versionInfo = null + const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const version = new Version() + versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + //app启动次数 + const sessionLog = new SessionLog() + const sessionTimesRes = await this.getCollection(sessionLog.tableName).where({ + appid: data._id.appid, + version: data._id.version, + platform: data._id.platform, + channel: data._id.channel, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }).count() + + let sessionTimes = 0 + if(sessionTimesRes && sessionTimesRes.total > 0) { + sessionTimes = sessionTimesRes.total + } else { + console.log('Not found session logs') + return false + } + + + //数据填充 + const datetime = new DateTime() + const insertParams = { + appid: data._id.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + type: 'crash', + count: data.error_count, + app_launch_count: sessionTimes, + dimension: this.fillType, + stat_date: datetime.getDate('Ymd', this.startTime), + start_time: this.startTime, + end_time: this.endTime + } + + this.fillData.push(insertParams) + + return insertParams + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/event.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/event.js new file mode 100644 index 00000000..43cf62b2 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/event.js @@ -0,0 +1,72 @@ +/** + * @class StatEvent 事件统计模型 + */ +const BaseMod = require('./base') +const { + DateTime +} = require('../lib') +module.exports = class StatEvent extends BaseMod { + constructor() { + super() + this.tableName = 'events' + this.defaultEvent = this.getConfig('event') || { + login: '登录', + register: '注册', + click: '点击', + share: '分享', + pay_success: '支付成功', + pay_fail: '支付失败' + } + } + + /** + * 获取事件信息 + * @param {String} appid: DCloud appid + * @param {String} eventKey 事件键值 + */ + async getEvent(appid, eventKey) { + const cacheKey = 'uni-stat-event-' + appid + '-' + eventKey + let eventData = await this.getCache(cacheKey) + if (!eventData) { + const eventInfo = await this.getCollection(this.tableName).where({ + appid: appid, + event_key: eventKey + }).get() + eventData = [] + if (eventInfo.data.length > 0) { + eventData = eventInfo.data[0] + await this.setCache(cacheKey, eventData) + } + } + return eventData + } + + + /** + * 获取事件信息不存在则创建 + * @param {String} appid: DCloud appid + * @param {String} eventKey 事件键值 + */ + async getEventAndCreate(appid, eventKey) { + const eventInfo = await this.getEvent(appid, eventKey) + if (eventInfo.length === 0) { + const thisTime = new DateTime().getTime() + const insertParam = { + appid: appid, + event_key: eventKey, + event_name: this.defaultEvent[eventKey] ? this.defaultEvent[eventKey] : '', + create_time: thisTime, + update_time: thisTime + } + const res = await this.insert(this.tableName, insertParam) + + if (res && res.id) { + return Object.assign(insertParam, { + _id: res.id + }) + } + } + + return eventInfo + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/eventLog.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/eventLog.js new file mode 100644 index 00000000..a19d7c14 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/eventLog.js @@ -0,0 +1,156 @@ +/** + * @class EventLog 事件日志模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const StatEvent = require('./event') +const SessionLog = require('./sessionLog') +const ShareLog = require('./shareLog') +const { + DateTime +} = require('../lib') +module.exports = class EventLog extends BaseMod { + constructor() { + super() + this.tableName = 'event-logs' + this.sessionLogInfo = [] + } + + /** + * 事件日志填充 + * @param {Object} reportParams 上报参数 + */ + async fill(reportParams) { + let params; + let sessionKey, sessionLogKey; + let sessionLogInfo; + const sessionData = [] + const fillParams = [] + const shareParams = [] + const sessionLog = new SessionLog() + const event = new StatEvent() + const platform = new Platform() + const dateTime = new DateTime() + const channel = new Channel() + for (const rk in reportParams) { + params = reportParams[rk] + + //暂存下会话数据,减少读库 + sessionKey = params.ak + params.did + params.p + if (!this.sessionLogInfo[sessionKey]) { + // 会话日志 + sessionLogInfo = await sessionLog.getSession(params) + if (sessionLogInfo.code) { + return sessionLogInfo + } + if (this.debug) { + console.log('sessionLogInfo', JSON.stringify(sessionLogInfo)) + } + this.sessionLogInfo[sessionKey] = sessionLogInfo + } else { + sessionLogInfo = this.sessionLogInfo[sessionKey] + } + + // 会话数据 + sessionLogKey = sessionLogInfo.data.sessionLogId.toString() + if (!sessionData[sessionLogKey]) { + sessionData[sessionLogKey] = { + eventCount: sessionLogInfo.data.eventCount + 1, + addEventCount: 1, + uid: sessionLogInfo.data.uid, + createTime: sessionLogInfo.data.createTime + } + } else { + sessionData[sessionLogKey].eventCount++ + sessionData[sessionLogKey].addEventCount++ + } + + // 事件 + const eventInfo = await event.getEventAndCreate(params.ak, params.e_n) + + // 填充数据 + fillParams.push({ + appid: params.ak, + version: params.v ? params.v : '', + platform: platform.getPlatformCode(params.ut, params.p), + channel: channel.getChannelCode(params), + device_id: params.did, + uid: params.uid ? params.uid : '', + session_id: sessionLogInfo.data.sessionLogId, + page_id: sessionLogInfo.data.pageId, + event_key: eventInfo.event_key, + param: params.e_v ? params.e_v : '', + // 版本 + sdk_version: params.mpsdk ? params.mpsdk : '', + platform_version: params.mpv ? params.mpv : '', + // 设备相关 + device_os_name: params.on ? params.on : platform.getOsName(params.p), + device_os_version: params.sv ? params.sv : '', + device_vendor: params.brand ? params.brand : '', + device_model: params.md ? params.md : '', + device_language: params.lang ? params.lang : '', + device_pixel_ratio: params.pr ? params.pr : '', + device_window_width: params.ww ? params.ww : '', + device_window_height: params.wh ? params.wh : '', + device_screen_width: params.sw ? params.sw : '', + device_screen_height: params.sh ? params.sh : '', + create_time: dateTime.getTime() + }) + // 分享数据 + if (eventInfo.event_key === 'share') { + shareParams.push(params) + } + } + + if (fillParams.length === 0) { + return { + code: 200, + msg: 'Invild param' + } + } + + if (shareParams.length > 0) { + const shareLog = new ShareLog() + await shareLog.fill(shareParams, this.sessionLogInfo) + } + + const res = await this.insert(this.tableName, fillParams) + if (res && res.inserted) { + for (const sid in sessionData) { + await sessionLog.updateSession(sid, sessionData[sid]) + } + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'Filled error' + } + } + } + + /** + * 事件日志清理 + * @param {Number} days 保留天数 + */ + async clean(days) { + days = Math.max(parseInt(days), 1) + console.log('clean event logs - day:', days) + + const dateTime = new DateTime() + //删除过期数据 + const res = await this.delete(this.tableName, { + create_time: { + $lt: dateTime.getTimeBySetDays(0 - days) + } + }) + + if (!res.code) { + console.log('clean event log:', res) + } + return res + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/eventResult.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/eventResult.js new file mode 100644 index 00000000..a6ca8ea0 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/eventResult.js @@ -0,0 +1,268 @@ +/** + * @class EventResult 事件结果统计 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const Version = require('./version') +const EventLog = require('./eventLog') +const { + DateTime +} = require('../lib') +module.exports = class EventResult extends BaseMod { + constructor() { + super() + this.tableName = 'event-result' + this.platforms = [] + this.channels = [] + this.versions = [] + } + + /** + * 事件数据统计 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async stat(type, date, reset) { + const allowedType = ['day'] + if (!allowedType.includes(type)) { + return { + code: 1002, + msg: 'This type is not allowed' + } + } + this.fillType = type + const dateTime = new DateTime() + const dateDimension = dateTime.getTimeDimensionByType(type, -1, date) + this.startTime = dateDimension.startTime + this.endTime = dateDimension.endTime + if (this.debug) { + console.log('dimension time', this.startTime + '--' + this.endTime) + } + + // 查看当前时间段日志是否已存在,防止重复生成 + if (!reset) { + const checkRes = await this.getCollection(this.tableName).where({ + start_time: this.startTime, + end_time: this.endTime + }).get() + if (checkRes.data.length > 0) { + console.log('event log have existed') + return { + code: 1003, + msg: 'This log have existed' + } + } + } else { + const delRes = await this.delete(this.tableName, { + start_time: this.startTime, + end_time: this.endTime + }) + console.log('delete old data result:', JSON.stringify(delRes)) + } + + // 数据获取 + this.eventLog = new EventLog() + const statRes = await this.aggregate(this.eventLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + event_key: 1, + device_id: 1, + create_time: 1 + }, + match: { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + event_key: '$event_key' + }, + event_count: { + $sum: 1 + } + }, + sort: { + event_count: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + if (this.debug) { + console.log('statRes', JSON.stringify(statRes)) + } + if (statRes.data.length > 0) { + this.fillData = [] + for (const i in statRes.data) { + await this.fill(statRes.data[i]) + } + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + } + return res + } + + /** + * 事件统计数据填充 + * @param {Object} data 数据集合 + */ + async fill(data) { + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data._id.platform]) { + //暂存下数据,减少读库 + platformInfo = this.platforms[data._id.platform] + } else { + const platform = new Platform() + platformInfo = await platform.getPlatformAndCreate(data._id.platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[data._id.platform] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + + // 渠道信息 + let channelInfo = null + const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey] + } else { + const channel = new Channel() + channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + + // 版本信息 + let versionInfo = null + const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const version = new Version() + versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + const matchCondition = data._id + Object.assign(matchCondition, { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }) + if (this.debug) { + console.log('matchCondition', JSON.stringify(matchCondition)) + } + + // 触发事件设备数统计 + const statEventDeviceRes = await this.aggregate(this.eventLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + event_key: 1, + device_id: 1, + create_time: 1 + }, + match: matchCondition, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + let eventDeviceCount = 0 + if (statEventDeviceRes.data.length > 0) { + eventDeviceCount = statEventDeviceRes.data[0].total_devices + } + + // 触发事件用户数统计 + const statEventUserRes = await this.aggregate(this.eventLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + event_key: 1, + uid: 1, + create_time: 1 + }, + match: { + ...matchCondition, + uid: { + $ne: '' + } + }, + group: [{ + _id: { + uid: '$uid' + } + }, { + _id: {}, + total_users: { + $sum: 1 + } + }] + }) + + let eventUserCount = 0 + if (statEventUserRes.data.length > 0) { + eventUserCount = statEventUserRes.data[0].total_users + } + + const datetime = new DateTime() + const insertParams = { + appid: data._id.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + event_key: data._id.event_key, + event_count: data.event_count, + device_count: eventDeviceCount, + user_count: eventUserCount, + dimension: this.fillType, + stat_date: datetime.getDate('Ymd', this.startTime), + start_time: this.startTime, + end_time: this.endTime + } + this.fillData.push(insertParams) + return insertParams + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/index.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/index.js new file mode 100644 index 00000000..d4f92877 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/index.js @@ -0,0 +1,23 @@ +/** + * 基础对外模型 + */ +module.exports = { + BaseMod: require('./base'), + SessionLog: require('./sessionLog'), + UserSessionLog: require('./userSessionLog'), + PageLog: require('./pageLog'), + EventLog: require('./eventLog'), + ShareLog: require('./shareLog'), + ErrorLog: require('./errorLog'), + AppCrashLogs: require('./appCrashLogs'), + StatResult: require('./statResult'), + ActiveUsers: require('./activeUsers'), + ActiveDevices: require('./activeDevices'), + PageResult: require('./pageResult'), + EventResult: require('./eventResult'), + ErrorResult: require('./errorResult'), + Loyalty: require('./loyalty'), + RunErrors: require('./runErrors'), + uniPay: require('./uni-pay'), + Setting: require('./setting'), +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/loyalty.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/loyalty.js new file mode 100644 index 00000000..25a37193 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/loyalty.js @@ -0,0 +1,491 @@ +/** + * 设备/用户忠诚度(粘性)统计模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const Version = require('./version') +const SessionLog = require('./sessionLog') +const UserSessionLog = require('./userSessionLog') +const { + DateTime +} = require('../lib') +module.exports = class Loyalty extends BaseMod { + constructor() { + super() + this.tableName = 'loyalty-result' + this.platforms = [] + this.channels = [] + this.versions = [] + } + + /** + * 设备/用户忠诚度(粘性)统计 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async stat(type, date, reset) { + const allowedType = ['day'] + if (!allowedType.includes(type)) { + return { + code: 1002, + msg: 'This type is not allowed' + } + } + + this.fillType = type + const dateTime = new DateTime() + const dateDimension = dateTime.getTimeDimensionByType(type, -1, date) + this.startTime = dateDimension.startTime + this.endTime = dateDimension.endTime + + if (this.debug) { + console.log('this time', dateTime.getTime()) + console.log('dimension time', this.startTime + '--' + this.endTime) + } + + // 查看当前时间段日志是否已存在,防止重复生成 + if (!reset) { + const checkRes = await this.getCollection(this.tableName).where({ + start_time: this.startTime, + end_time: this.endTime + }).get() + if (checkRes.data.length > 0) { + console.log('loyalty log have existed') + return { + code: 1003, + msg: 'This log have existed' + } + } + } else { + const delRes = await this.delete(this.tableName, { + start_time: this.startTime, + end_time: this.endTime + }) + console.log('delete old data result:', JSON.stringify(delRes)) + } + + // 数据获取 + this.sessionLog = new SessionLog() + const statRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + page_count: 1, + duration: 1, + create_time: 1 + }, + match: { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel' + }, + page_count_sum: { + $sum: '$page_count' + }, + duration_sum: { + $sum: '$duration' + } + }, + sort: { + page_count_sum: 1, + duration_sum: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + if (this.debug) { + console.log('statRes', JSON.stringify(statRes)) + } + if (statRes.data.length > 0) { + this.fillData = [] + for (const i in statRes.data) { + await this.fill(statRes.data[i]) + } + + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + } + return res + } + + /** + * 设备/用户忠诚度(粘性)数据填充 + * @param {Object} data 数据集合 + */ + async fill(data) { + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data._id.platform]) { + platformInfo = this.platforms[data._id.platform] + } else { + const platform = new Platform() + platformInfo = await platform.getPlatformAndCreate(data._id.platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[data._id.platform] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + + // 渠道信息 + let channelInfo = null + const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey] + } else { + const channel = new Channel() + channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + + // 版本信息 + let versionInfo = null + const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const version = new Version() + versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + // 访问深度-用户数统计和访问次数 + const pageMark = [1, 2, 3, 4, [5, 10], [10]] + + const matchCondition = Object.assign(data._id, { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }) + + const visitDepthData = { + visit_devices: {}, + visit_users: {}, + visit_times: {} + } + + const userSessionLog = new UserSessionLog() + //根据各访问页面数区间统计 + for (const pi in pageMark) { + let pageMarkCondition = { + page_count: pageMark[pi] + } + + if (Array.isArray(pageMark[pi])) { + if (pageMark[pi].length === 2) { + pageMarkCondition = { + page_count: { + $gte: pageMark[pi][0], + $lte: pageMark[pi][1] + } + } + } else { + pageMarkCondition = { + page_count: { + $gt: pageMark[pi][0] + } + } + } + } + + // 访问次数(会话次数)统计 + const searchCondition = { + ...matchCondition, + ...pageMarkCondition + } + const vistRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + page_count: 1, + create_time: 1 + }, + match: searchCondition, + group: { + _id: {}, + total_visits: { + $sum: 1 + } + } + }) + + if (this.debug) { + console.log('vistResCondtion', JSON.stringify(searchCondition)) + console.log('vistRes', JSON.stringify(vistRes)) + } + let vistCount = 0 + if (vistRes.data.length > 0) { + vistCount = vistRes.data[0].total_visits + } + + // 设备数统计 + const deviceRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + page_count: 1, + create_time: 1, + device_id: 1 + }, + match: searchCondition, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('searchCondition', JSON.stringify(searchCondition)) + console.log('deviceRes', JSON.stringify(deviceRes)) + } + + let deviceCount = 0 + if (deviceRes.data.length > 0) { + deviceCount = deviceRes.data[0].total_devices + } + + // 用户数统计 + const userRes = await this.aggregate(userSessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + page_count: 1, + create_time: 1, + uid: 1 + }, + match: searchCondition, + group: [{ + _id: { + uid: '$uid' + } + }, { + _id: {}, + total_users: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('userResCondtion', JSON.stringify(searchCondition)) + console.log('userRes', JSON.stringify(userRes)) + } + + let userCount = 0 + if (userRes.data.length > 0) { + userCount = userRes.data[0].total_users + } + + const pageKey = 'p_' + (Array.isArray(pageMark[pi]) ? pageMark[pi][0] : pageMark[pi]) + visitDepthData.visit_devices[pageKey] = deviceCount + visitDepthData.visit_users[pageKey] = userCount + visitDepthData.visit_times[pageKey] = vistCount + } + + // 访问时长-用户数统计和访问次数 + const durationMark = [ + [0, 2], + [3, 5], + [6, 10], + [11, 20], + [21, 30], + [31, 50], + [51, 100], + [100] + ] + const durationData = { + visit_devices: {}, + visit_users: {}, + visit_times: {} + } + //根据各访问时长区间统计 + for (const di in durationMark) { + let durationMarkCondition = { + duration: durationMark[di] + } + + if (Array.isArray(durationMark[di])) { + if (durationMark[di].length === 2) { + durationMarkCondition = { + duration: { + $gte: durationMark[di][0], + $lte: durationMark[di][1] + } + } + } else { + durationMarkCondition = { + duration: { + $gt: durationMark[di][0] + } + } + } + } + + // 访问次数(会话次数)统计 + const searchCondition = { + ...matchCondition, + ...durationMarkCondition + } + if (this.debug) { + console.log('searchCondition', JSON.stringify(searchCondition)) + } + const vistRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + duration: 1, + create_time: 1 + }, + match: searchCondition, + group: { + _id: {}, + total_visits: { + $sum: 1 + } + } + }) + + if (this.debug) { + console.log('vistRes', JSON.stringify(vistRes)) + } + let vistCount = 0 + if (vistRes.data.length > 0) { + vistCount = vistRes.data[0].total_visits + } + + // 设备数统计 + const deviceRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + device_id: 1, + duration: 1, + create_time: 1 + }, + match: searchCondition, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('userRes', JSON.stringify(deviceRes)) + } + + let deviceCount = 0 + if (deviceRes.data.length > 0) { + deviceCount = deviceRes.data[0].total_devices + } + + // 用户数统计 + const userRes = await this.aggregate(userSessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + uid: 1, + duration: 1, + create_time: 1 + }, + match: searchCondition, + group: [{ + _id: { + uid: '$uid' + } + }, { + _id: {}, + total_users: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('userRes', JSON.stringify(userRes)) + } + + let userCount = 0 + if (userRes.data.length > 0) { + userCount = userRes.data[0].total_users + } + + const pageKey = 's_' + (Array.isArray(durationMark[di]) ? durationMark[di][0] : durationMark[di]) + durationData.visit_devices[pageKey] = deviceCount + durationData.visit_users[pageKey] = userCount + durationData.visit_times[pageKey] = vistCount + } + + // 数据填充 + const datetime = new DateTime() + const insertParams = { + appid: data._id.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + visit_depth_data: visitDepthData, + duration_data: durationData, + stat_date: datetime.getDate('Ymd', this.startTime), + start_time: this.startTime, + end_time: this.endTime + } + + this.fillData.push(insertParams) + return insertParams + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/page.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/page.js new file mode 100644 index 00000000..1f2a0ac4 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/page.js @@ -0,0 +1,83 @@ +/** + * @class Page 页面模型 + */ +const BaseMod = require('./base') +const { + parseUrl +} = require('../../shared') +const { + DateTime +} = require('../lib') +module.exports = class Page extends BaseMod { + constructor() { + super() + this.tableName = 'pages' + } + + /** + * 获取页面信息 + * @param {String} appid + * @param {String} url 页面地址 + */ + async getPage(appid, url) { + const cacheKey = 'uni-stat-page-' + appid + '-' + url + let pageData = await this.getCache(cacheKey) + if (!pageData) { + const pageInfo = await this.getCollection(this.tableName).where({ + appid: appid, + path: url + }).limit(1).get() + pageData = [] + if (pageInfo.data.length > 0) { + pageData = pageInfo.data[0] + await this.setCache(cacheKey, pageData) + } + } + return pageData + } + + /** + * 获取页面信息不存在则创建 + * @param {String} appid + * @param {String} url 页面地址 + * @param {Object} title 页面标题 + */ + async getPageAndCreate(appid, url, title) { + //获取url信息 + const urlInfo = parseUrl(url) + if (!urlInfo) { + return false + } + const baseurl = urlInfo.path + const pageInfo = await this.getPage(appid, baseurl) + //页面不存在则创建 + if (pageInfo.length === 0) { + const thisTime = new DateTime().getTime() + const insertParam = { + appid: appid, + path: baseurl, + title: title, + page_params: [], + create_time: thisTime, + update_time: thisTime + } + const res = await this.insert(this.tableName, insertParam) + + if (res && res.id) { + return Object.assign(insertParam, { + _id: res.id + }) + } + } else if (!pageInfo.title && title) { + const cacheKey = 'uni-stat-page-' + appid + '-' + baseurl + await this.clearCache(cacheKey) + await this.update(this.tableName, { + title: title + }, { + _id: pageInfo._id + }) + } + + return pageInfo + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/pageLog.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/pageLog.js new file mode 100644 index 00000000..552ff298 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/pageLog.js @@ -0,0 +1,186 @@ +/** + * @class PageLog 页面日志模型 + */ +const BaseMod = require('./base') +const Page = require('./page') +const Platform = require('./platform') +const Channel = require('./channel') +const SessionLog = require('./sessionLog') +const { + DateTime +} = require('../lib') +const { + parseUrl +} = require('../../shared') +module.exports = class PageLog extends BaseMod { + constructor() { + super() + this.tableName = 'page-logs' + this.sessionLogInfo = [] + } + + /** + * 页面日志数据填充 + * @param {Object} reportParams 上报参数 + */ + async fill(reportParams) { + let params; + let sessionKey + let sessionLogKey + let sessionLogInfo + let pageKey + let pageInfo + let referPageInfo + const sessionData = [] + const pageData = [] + const fillParams = [] + const sessionLog = new SessionLog() + const page = new Page() + const platform = new Platform() + const dateTime = new DateTime() + const channel = new Channel() + for (const pk in reportParams) { + params = reportParams[pk] + if (['3', '4'].includes(params.lt) && !params.url && params.urlref) { + params.url = params.urlref + } + + // 页面信息 + pageKey = params.ak + params.url + if (pageData[pageKey]) { + pageInfo = pageData[pageKey] + } else { + pageInfo = await page.getPageAndCreate(params.ak, params.url, params.ttpj) + if (!pageInfo || pageInfo.length === 0) { + console.log('Not found this page by param:', JSON.stringify(params)) + continue + } + pageData[pageKey] = pageInfo + } + + // 会话日志,暂存下会话数据,减少读库 + sessionKey = params.ak + params.did + params.p + if (!this.sessionLogInfo[sessionKey]) { + sessionLogInfo = await sessionLog.getSession(params) + if (sessionLogInfo.code) { + return sessionLogInfo + } + if (this.debug) { + console.log('sessionLogInfo', JSON.stringify(sessionLogInfo)) + } + this.sessionLogInfo[sessionKey] = sessionLogInfo + } else { + sessionLogInfo = this.sessionLogInfo[sessionKey] + } + + // 会话数据 + sessionLogKey = sessionLogInfo.data.sessionLogId.toString() + if (!sessionData[sessionLogKey]) { + //临时存储减少查询次数 + sessionData[sessionLogKey] = { + pageCount: sessionLogInfo.data.pageCount + 1, + addPageCount: 1, + createTime: sessionLogInfo.data.createTime, + pageId: pageInfo._id, + uid: sessionLogInfo.data.uid + } + + if (this.debug) { + console.log('add sessionData - ' + sessionLogKey, sessionData) + } + + } else { + sessionData[sessionLogKey].pageCount += 1 + sessionData[sessionLogKey].addPageCount += 1 + sessionData[sessionLogKey].pageId = pageInfo._id + + if (this.debug) { + console.log('update sessionData - ' + sessionLogKey, sessionData) + } + } + + // 上级页面信息 + pageKey = params.ak + params.urlref + if (pageData[pageKey]) { + referPageInfo = pageData[pageKey] + } else { + referPageInfo = await page.getPageAndCreate(params.ak, params.urlref, params.ttpj) + if (!referPageInfo || referPageInfo.length === 0) { + referPageInfo = {_id:''} + } + pageData[pageKey] = referPageInfo + } + + //当前页面url信息 + const urlInfo = parseUrl(params.url) + + // 填充数据 + fillParams.push({ + appid: params.ak, + version: params.v ? params.v : '', + platform: platform.getPlatformCode(params.ut, params.p), + channel: channel.getChannelCode(params), + device_id: params.did, + uid: params.uid ? params.uid : '', + session_id: sessionLogInfo.data.sessionLogId, + page_id: pageInfo._id, + query_string: urlInfo.query, + //上级页面相关 + previous_page_id: referPageInfo._id, + previous_page_duration: params.urlref_ts ? parseInt(params.urlref_ts) : 0, + previous_page_is_entry: referPageInfo._id === sessionLogInfo.data.entryPageId ? 1 : 0, + create_time: dateTime.getTime() + }) + } + + if (fillParams.length === 0) { + console.log('No page params') + return { + code: 200, + msg: 'Invild param' + } + } + + //日志数据入库 + const res = await this.insert(this.tableName, fillParams) + if (res && res.inserted) { + // 更新会话数据 + const nowTime = dateTime.getTime() + for (const sid in sessionData) { + await sessionLog.updateSession(sid, sessionData[sid]) + } + + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'Filled error' + } + } + } + + /** + * 页面日志清理 + * @param {Number} days 页面日志保留天数 + */ + async clean(days) { + days = Math.max(parseInt(days), 1) + console.log('clean page logs - day:', days) + + const dateTime = new DateTime() + + const res = await this.delete(this.tableName, { + create_time: { + $lt: dateTime.getTimeBySetDays(0 - days) + } + }) + + if (!res.code) { + console.log('clean page log:', res) + } + return res + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/pageResult.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/pageResult.js new file mode 100644 index 00000000..b334c4f3 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/pageResult.js @@ -0,0 +1,522 @@ +/** + * @class PageResult 页面结果统计模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const Version = require('./version') +const SessionLog = require('./sessionLog') +const PageLog = require('./pageLog') +const ShareLog = require('./shareLog') +const { + DateTime +} = require('../lib') +module.exports = class PageResult extends BaseMod { + constructor() { + super() + this.tableName = 'page-result' + this.platforms = [] + this.channels = [] + this.versions = [] + } + + /** + * 数据统计 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async stat(type, date, reset) { + //允许的类型 + const allowedType = ['day'] + if (!allowedType.includes(type)) { + return { + code: 1002, + msg: 'This type is not allowed' + } + } + this.fillType = type + //获取当前统计的时间范围 + const dateTime = new DateTime() + const dateDimension = dateTime.getTimeDimensionByType(type, -1, date) + this.startTime = dateDimension.startTime + this.endTime = dateDimension.endTime + if (this.debug) { + console.log('dimension time', this.startTime + '--' + this.endTime) + } + + // 查看当前时间段日志是否已存在,防止重复执行 + if (!reset) { + const checkRes = await this.getCollection(this.tableName).where({ + start_time: this.startTime, + end_time: this.endTime + }).get() + if (checkRes.data.length > 0) { + console.error('This page stat log have exists') + return { + code: 1003, + msg: 'This page stat log have existed' + } + } + } else { + const delRes = await this.delete(this.tableName, { + start_time: this.startTime, + end_time: this.endTime + }) + console.log('Delete old data result:', JSON.stringify(delRes)) + } + + // 数据获取 + this.pageLog = new PageLog() + const statRes = await this.aggregate(this.pageLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + page_id: 1, + create_time: 1 + }, + match: { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + page_id: '$page_id' + }, + visit_times: { + $sum: 1 + } + }, + sort: { + visit_times: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + if (this.debug) { + console.log('Page statRes', JSON.stringify(statRes)) + } + if (statRes.data.length > 0) { + this.fillData = [] + //获取填充数据 + for (const i in statRes.data) { + await this.fill(statRes.data[i]) + } + + //数据批量入库 + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + } + return res + } + + /** + * 页面统计数据填充 + * @param {Object} data 统计数据 + */ + async fill(data) { + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data._id.platform]) { + //暂存下数据,减少读库 + platformInfo = this.platforms[data._id.platform] + } else { + const platform = new Platform() + platformInfo = await platform.getPlatformAndCreate(data._id.platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[data._id.platform] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + + // 渠道信息 + let channelInfo = null + const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey] + } else { + const channel = new Channel() + channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + + // 版本信息 + let versionInfo = null + const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const version = new Version() + versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + const matchCondition = data._id + Object.assign(matchCondition, { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }) + if (this.debug) { + console.log('matchCondition', JSON.stringify(matchCondition)) + } + + // 当前页面访问设备数 + const statPageDeviceRes = await this.aggregate(this.pageLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + device_id: 1, + page_id: 1, + create_time: 1 + }, + match: matchCondition, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + let pageVisitDevices = 0 + if (statPageDeviceRes.data.length > 0) { + pageVisitDevices = statPageDeviceRes.data[0].total_devices + } + + // 当前页面访问人数 + const statPageUserRes = await this.aggregate(this.pageLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + uid: 1, + page_id: 1, + create_time: 1 + }, + match: { + ...matchCondition, + uid: { + $ne: '' + } + }, + group: [{ + _id: { + uid: '$uid' + } + }, { + _id: {}, + total_users: { + $sum: 1 + } + }] + }) + + let pageVisitUsers = 0 + if (statPageUserRes.data.length > 0) { + pageVisitUsers = statPageUserRes.data[0].total_users + } + + // 退出次数 + const sessionLog = new SessionLog() + let existTimes = 0 + const existRes = await this.getCollection(sessionLog.tableName).where({ + appid: data._id.appid, + version: data._id.version, + platform: data._id.platform, + channel: data._id.channel, + exit_page_id: data._id.page_id, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }).count() + if (existRes && existRes.total > 0) { + existTimes = existRes.total + } + + // 访问时长 + const statPageDurationRes = await this.aggregate(this.pageLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + previous_page_id: 1, + previous_page_duration: 1, + create_time: 1 + }, + match: { + appid: data._id.appid, + version: data._id.version, + platform: data._id.platform, + channel: data._id.channel, + previous_page_id: data._id.page_id, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: {}, + total_duration: { + $sum: '$previous_page_duration' + } + } + }) + + let totalDuration = 0 + if (statPageDurationRes.data.length > 0) { + totalDuration = statPageDurationRes.data[0].total_duration + } + + // 分享次数 + const shareLog = new ShareLog() + const statShareRes = await this.aggregate(shareLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + page_id: 1, + create_time: 1 + }, + match: { + appid: data._id.appid, + version: data._id.version, + platform: data._id.platform, + channel: data._id.channel, + page_id: data._id.page_id, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: {}, + share_count: { + $sum: 1 + } + } + }) + + let shareCount = 0 + if (statShareRes.data.length > 0) { + shareCount = statShareRes.data[0].share_count + } + + // 作为入口页的总次数和总访问时长 + const statPageEntryCountRes = await this.aggregate(this.pageLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + previous_page_id: 1, + previous_page_duration: 1, + previous_page_is_entry: 1, + create_time: 1 + }, + match: { + appid: data._id.appid, + version: data._id.version, + platform: data._id.platform, + channel: data._id.channel, + previous_page_id: data._id.page_id, + previous_page_is_entry: 1, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: {}, + entry_count: { + $sum: 1 + }, + entry_duration: { + $sum: '$previous_page_duration' + } + } + }) + + let entryCount = 0 + let entryDuration = 0 + if (statPageEntryCountRes.data.length > 0) { + entryCount = statPageEntryCountRes.data[0].entry_count + entryDuration = statPageEntryCountRes.data[0].entry_duration + } + + // 作为入口页的总设备数 + const statPageEntryDevicesRes = await this.aggregate(this.pageLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + device_id: 1, + previous_page_id: 1, + previous_page_is_entry: 1, + create_time: 1 + }, + match: { + appid: data._id.appid, + version: data._id.version, + platform: data._id.platform, + channel: data._id.channel, + previous_page_id: data._id.page_id, + previous_page_is_entry: 1, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + entry_devices: { + $sum: 1 + } + }] + }) + + let entryDevices = 0 + if (statPageEntryDevicesRes.data.length > 0) { + entryDevices = statPageEntryDevicesRes.data[0].entry_devices + } + + // 作为入口页的总人数 + const statPageEntryUsersRes = await this.aggregate(this.pageLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + uid: 1, + previous_page_id: 1, + previous_page_is_entry: 1, + create_time: 1 + }, + match: { + appid: data._id.appid, + version: data._id.version, + platform: data._id.platform, + channel: data._id.channel, + previous_page_id: data._id.page_id, + previous_page_is_entry: 1, + uid: { + $ne: '' + }, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: [{ + _id: { + uid: '$uid' + } + }, { + _id: {}, + entry_users: { + $sum: 1 + } + }] + }) + + let entryUsers = 0 + if (statPageEntryUsersRes.data.length > 0) { + entryUsers = statPageEntryUsersRes.data[0].entry_users + } + + // 跳出率 + let bounceTimes = 0 + const bounceRes = await this.getCollection(sessionLog.tableName).where({ + appid: data._id.appid, + version: data._id.version, + platform: data._id.platform, + channel: data._id.channel, + entry_page_id: data._id.page_id, + page_count: 1, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }).count() + if (bounceRes && bounceRes.total > 0) { + bounceTimes = bounceRes.total + } + let bounceRate = 0 + if (bounceTimes > 0 && data.visit_times > 0) { + bounceRate = bounceTimes * 100 / data.visit_times + bounceRate = parseFloat(bounceRate.toFixed(2)) + } + + // 数据填充 + const datetime = new DateTime() + const insertParams = { + appid: data._id.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + page_id: data._id.page_id, + visit_times: data.visit_times, + visit_devices: pageVisitDevices, + visit_users: pageVisitUsers, + exit_times: existTimes, + duration: totalDuration > 0 ? totalDuration : 1, + share_count: shareCount, + entry_users: entryUsers, + entry_devices: entryDevices, + entry_count: entryCount, + entry_duration: entryDuration, + bounce_rate: bounceRate, + dimension: this.fillType, + stat_date: datetime.getDate('Ymd', this.startTime), + start_time: this.startTime, + end_time: this.endTime + } + + this.fillData.push(insertParams) + return insertParams + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/platform.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/platform.js new file mode 100644 index 00000000..aa52824d --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/platform.js @@ -0,0 +1,160 @@ +/** + * @class Platform 应用平台模型 + */ +const BaseMod = require('./base') +const { + DateTime +} = require('../lib') +module.exports = class Platform extends BaseMod { + constructor() { + super() + this.tableName = 'app-platforms' + } + + /** + * 获取平台信息 + * @param {String} platform 平台代码 + * @param {String} os 系统 + */ + async getPlatform(platform, os) { + const cacheKey = 'uni-stat-platform-' + platform + '-' + os + let platformData = await this.getCache(cacheKey) + if (!platformData) { + const platformCode = this.getPlatformCode(platform, os) + const platformInfo = await this.getCollection(this.tableName).where({ + code: platformCode + }).limit(1).get() + platformData = [] + if (platformInfo.data.length > 0) { + platformData = platformInfo.data[0] + await this.setCache(cacheKey, platformData) + } + } + return platformData + } + + /** + * 获取平台信息没有则创建 + * @param {String} platform 平台代码 + * @param {String} os 系统 + */ + async getPlatformAndCreate(platform, os) { + if (!platform) { + return false + } + const platformInfo = await this.getPlatform(platform, os) + + if (platformInfo.length === 0) { + const platformCode = this.getPlatformCode(platform, os) + const insertParam = { + code: platformCode, + name: platformCode, + create_time: new DateTime().getTime() + } + const res = await this.insert(this.tableName, insertParam) + if (res && res.id) { + return Object.assign(insertParam, { + _id: res.id + }) + } + } + return platformInfo + } + + /** + * 获取平台代码 + * @param {String} platform 平台代码 + * @param {String} os 系统 + */ + getPlatformCode(platform, os) { + let platformCode = platform + + //兼容客户端上报参数 + switch(platform) { + //h5|web + case 'h5': + platformCode = 'web' + break + //微信小程序 + case 'wx': + platformCode = 'mp-weixin' + break + //百度小程序 + case 'bd': + platformCode = 'mp-baidu' + break + //支付宝小程序 + case 'ali': + platformCode = 'mp-alipay' + break + //字节跳动小程序 + case 'tt': + platformCode = 'mp-toutiao' + break + //qq小程序 + case 'qq': + platformCode = 'mp-qq' + break + //快应用联盟 + case 'qn': + platformCode = 'quickapp-webview-union' + break + //快应用(webview) + case 'qw': + platformCode = 'quickapp-webview' + break + //快应用华为 + case 'qi': + platformCode = 'quickapp-webview-huawei' + break + //360小程序 + case '360': + platformCode = 'mp-360' + break + //京东小程序 + case 'jd': + platformCode = 'mp-jd' + break + //钉钉小程序 + case 'dt': + platformCode = 'mp-dingtalk' + break + //快手小程序 + case 'ks': + platformCode = 'mp-kuaishou' + break + //飞书小程序 + case 'lark': + platformCode = 'mp-lark' + break + //原生应用 + case 'n': + case 'app-plus': + case 'app': + os = this.getOsName(os) + if (os === 'ios') { + platformCode = 'ios' + } else { + platformCode = 'android' + } + break + } + return platformCode + } + + /** + * 获取系统名称 + * @param {Object} os系统标识 + */ + getOsName(os) { + if(!os) { + return '' + } + //兼容老版上报参数 + const osSetting = { + i: 'ios', + a: 'android' + } + return osSetting[os] ? osSetting[os] : os + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/runErrors.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/runErrors.js new file mode 100644 index 00000000..74aee238 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/runErrors.js @@ -0,0 +1,20 @@ +/** + * @class RunErrors 运行错误日志 + */ +const BaseMod = require('./base') +module.exports = class RunErrors extends BaseMod { + constructor() { + super() + this.tableName = 'run-errors' + } + + /** + * 创建日志 + * @param {Object} params 参数 + */ + async create(params) { + if (!params) return + const res = await this.insert(this.tableName, params) + return res + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/scenes.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/scenes.js new file mode 100644 index 00000000..35793c08 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/scenes.js @@ -0,0 +1,80 @@ +/** + * @class Scenes 场景值模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +module.exports = class Scenes extends BaseMod { + constructor() { + super() + this.tableName = 'mp-scenes' + this.defualtCode = '1001' + } + + /** + * 获取场景值 + * @param {String} platform 平台代码 + * @param {String} code 场景值代码 + */ + async getScenes(platform, code) { + const cacheKey = 'uni-stat-scenes-' + platform + '-' + code + let scenesData = await this.getCache(cacheKey) + if (!scenesData) { + const scenesInfo = await this.getCollection(this.tableName).where({ + platform: platform, + scene_code: code + }).limit(1).get() + scenesData = [] + if (scenesInfo.data.length > 0) { + scenesData = scenesInfo.data[0] + await this.setCache(cacheKey, scenesData) + } + } + return scenesData + } + + /** + * 通过平台编号获取场景值 + * @param {String} platformId 平台编号 + * @param {String} code 场景值代码 + */ + async getScenesByPlatformId(platformId, code) { + const platform = new Platform() + let platformInfo = await this.getCollection(platform.tableName).where({ + _id: platformId + }).limit(1).get() + let scenesData + if (platformInfo.data.length > 0) { + platformInfo = platformInfo.data[0] + scenesData = await this.getScenes(platformInfo.code, code) + } else { + scenesData = [] + } + return scenesData + } + + /** + * 获取场景值名称 + * @param {String} platform 平台代码 + * @param {String} code 场景值代码 + */ + async getScenesName(platform, code) { + const scenesData = await this.getScenes(platform, code) + if (scenesData.length === 0) { + return '' + } + return scenesData.scene_name + } + + /** + * 通过平台编号获取场景值名称 + * @param {String} platformId 平台编号 + * @param {String} code 场景值代码 + */ + async getScenesNameByPlatformId(platformId, code) { + const scenesData = await this.getScenesByPlatformId(platformId, code) + if (scenesData.length === 0) { + return code === this.defualtCode ? '默认' : '' + } + return scenesData.scene_name + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/sessionLog.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/sessionLog.js new file mode 100644 index 00000000..17c43a9f --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/sessionLog.js @@ -0,0 +1,343 @@ +/** + * @class SessionLog 基础会话日志模型 + */ +const BaseMod = require('./base') +const Page = require('./page') +const Platform = require('./platform') +const Channel = require('./channel') +const UserSessionLog = require('./userSessionLog') +const Device = require('./device') +const { + DateTime +} = require('../lib') +module.exports = class SessionLog extends BaseMod { + constructor() { + super() + this.tableName = 'session-logs' + } + + + /** + * 会话日志批量填充 + * @param {Object} reportParams 上报参数 + */ + async batchFill(reportParams) { + let params, pageInfo, nowTime, firstVistTime, lastVistTime; + const fillParams = [] + + const page = new Page() + const platform = new Platform() + const dateTime = new DateTime() + const channel = new Channel() + const device = new Device() + let res + for (const pk in reportParams) { + params = reportParams[pk] + res = await this.fill(params) + if (res.code) { + console.error(res.msg) + } else { + //添加设备信息 + await device.setDevice(params) + } + } + return res + } + + /** + * 会话日志填充 + * @param {Object} params 上报参数 + */ + async fill(params) { + // 应用信息 + if (!params.ak) { + return { + code: 200, + msg: 'Parameter "ak" not found' + } + } + + // 平台信息 + if (!params.ut) { + return { + code: 200, + msg: 'Parameter "ut" not found' + } + } + + // 设备信息 + if (!params.did) { + return { + code: 200, + msg: 'Parameter "did" not found' + } + } + + // 页面信息 + const page = new Page() + const pageInfo = await page.getPageAndCreate(params.ak, params.url, params.ttpj) + if (!pageInfo || pageInfo.length === 0) { + return { + code: 300, + msg: 'Not found this entry page' + } + } + if (this.debug) { + console.log('pageInfo', JSON.stringify(pageInfo)) + } + const platform = new Platform() + const dateTime = new DateTime() + const channel = new Channel() + const nowTime = dateTime.getTime() + const firstVistTime = params.fvts ? dateTime.strToTime(params.fvts) : nowTime + const lastVistTime = (params.lvts && params.lvts !== '0') ? dateTime.strToTime(params.lvts) : 0 + + const fillParams = { + appid: params.ak, + version: params.v ? params.v : '', + platform: platform.getPlatformCode(params.ut, params.p), + channel: channel.getChannelCode(params), + type: params.cst ? parseInt(params.cst) : 0, + // 访问设备 + device_id: params.did, + //是否为首次访问,判断标准:最后一次访问时间为0 + is_first_visit: (params.lt === '1' && !lastVistTime) ? 1 : 0, + first_visit_time: firstVistTime, + last_visit_time: nowTime, + visit_count: params.tvc ? parseInt(params.tvc) : 1, + // 用户相关 + last_visit_user_id: params.uid ? params.uid : '', + // 页面相关 + entry_page_id: pageInfo._id, + exit_page_id: pageInfo._id, + page_count: 0, + event_count: 0, + duration: 1, + // 版本 + sdk_version: params.mpsdk ? params.mpsdk : '', + platform_version: params.mpv ? params.mpv : '', + // 设备相关 + device_os_name: params.on ? params.on : platform.getOsName(params.p), + device_os_version: params.sv ? params.sv : '', + device_vendor: params.brand ? params.brand : '', + device_model: params.md ? params.md : '', + device_language: params.lang ? params.lang : '', + device_pixel_ratio: params.pr ? params.pr : '', + device_window_width: params.ww ? params.ww : '', + device_window_height: params.wh ? params.wh : '', + device_screen_width: params.sw ? params.sw : '', + device_screen_height: params.sh ? params.sh : '', + // 地区相关 + location_ip: params.ip ? params.ip : '', + location_latitude: params.lat ? parseFloat(params.lat) : -1, + location_longitude: params.lng ? parseFloat(params.lng) : -1, + location_country: params.cn ? params.cn : '', + location_province: params.pn ? params.pn : '', + location_city: params.ct ? params.ct : '', + is_finish: 0, + create_time: nowTime + } + + if(this.isHaveOldDeviceId(params)) { + fillParams.old_device_id = params.odid + } + + const res = await this.insert(this.tableName, fillParams) + + if (res && res.id) { + + //填充用户的会话日志 + if (params.uid) { + await new UserSessionLog().fill({ + ...params, + page_id: pageInfo._id, + sid: res.id + }) + } + + return { + code: 0, + msg: 'success', + data: { + pageId: pageInfo._id, + sessionLogId: res.id, + entryPageId: fillParams.entry_page_id, + eventCount: fillParams.event_count, + startTime: fillParams.first_visit_time, + createTime: fillParams.create_time, + pageCount: fillParams.page_count, + uid: fillParams.last_visit_user_id + } + } + } else { + return { + code: 500, + msg: 'Session log filled error' + } + } + } + + /** + * 判断是否包含老版本sdk生成的device_id, 此功能用于处理前端sdk升级后 device_id 发生变化导致日活、留存数据不准确的问题 + * @param {Object} params + */ + isHaveOldDeviceId(params) { + if(params.odid && params.odid !== params.did && params.odid !== 'YluY92BA6nJ6NfixI77sFQ%3D%3D&ie=1') { + console.log('params.odid', params.odid) + return true + } + return false + } + + + /** + * 获取会话 + * @param {Object} params 上报参数 + */ + async getSession(params) { + // 页面信息 + const page = new Page() + const pageInfo = await page.getPageAndCreate(params.ak, params.url, params.ttpj) + if (!pageInfo || pageInfo.length === 0) { + return { + code: 300, + msg: 'Not found this entry page' + } + } + + const platformObj = new Platform() + const platform = platformObj.getPlatformCode(params.ut, params.p) + // 查询日志 + const sessionLogInfo = await this.getCollection(this.tableName).where({ + appid: params.ak, + platform: platform, + device_id: params.did, + is_finish: 0 + }).orderBy('create_time', 'desc').limit(1).get() + + if (sessionLogInfo.data.length > 0) { + const userSessionLog = new UserSessionLog() + const sessionLogInfoData = sessionLogInfo.data[0] + // 最后一次访问时间距现在超过半小时算上次会话已结束并生成一次新的会话 + let sessionExpireTime = this.getConfig('sessionExpireTime') + sessionExpireTime = sessionExpireTime ? sessionExpireTime : 1800 + const sessionTime = new DateTime().getTime() - sessionLogInfoData.last_visit_time + if (sessionTime >= sessionExpireTime * 1000) { + if (this.debug) { + console.log('session log time expired', sessionTime) + } + await this.update(this.tableName, { + is_finish: 1 + }, { + appid: params.ak, + platform: platform, + device_id: params.did, + is_finish: 0 + }) + //关闭用户会话 + await userSessionLog.closeUserSession() + + return await this.fill(params) + } else { + //如果当前会话切换了用户则生成新的用户会话 + if (params.uid != sessionLogInfoData.last_visit_user_id) { + await userSessionLog.checkUserSession({ + ...params, + page_id: pageInfo._id, + sid: sessionLogInfoData._id, + last_visit_user_id: sessionLogInfoData.last_visit_user_id + }) + + await this.update(this.tableName, { + last_visit_user_id: params.uid ? params.uid : '' + }, { + _id: sessionLogInfoData._id + }) + } + + return { + code: 0, + msg: 'success', + data: { + pageId: pageInfo._id, + sessionLogId: sessionLogInfoData._id, + entryPageId: sessionLogInfoData.entry_page_id, + eventCount: sessionLogInfoData.event_count, + startTime: sessionLogInfoData.first_visit_time, + createTime: sessionLogInfoData.create_time, + pageCount: sessionLogInfoData.page_count, + uid: sessionLogInfoData.last_visit_user_id + } + } + } + } else { + return await this.fill(params) + } + } + + /** + * 更新会话信息 + * @param {String} sid 会话编号 + * @param {Object} data 更新数据 + */ + async updateSession(sid, data) { + const nowTime = new DateTime().getTime() + const accessTime = nowTime - data.createTime + const accessSenconds = accessTime > 1000 ? parseInt(accessTime / 1000) : 1 + + const updateData = { + last_visit_time: nowTime, + duration: accessSenconds, + } + //访问页面数量 + if (data.addPageCount) { + updateData.page_count = data.pageCount + } + //最终访问的页面编号 + if (data.pageId) { + updateData.exit_page_id = data.pageId + } + //产生事件次数 + if (data.eventCount) { + updateData.event_count = data.eventCount + } + + if (this.debug) { + console.log('update session log by sid-' + sid, updateData) + } + + //更新会话 + await this.update(this.tableName, updateData, { + _id: sid + }) + + //更新用户会话 + if (data.uid) { + data.nowTime = nowTime + await new UserSessionLog().updateUserSession(sid, data) + } + + return true + } + + /** + * 清理日志数据 + * @param {Number} days 保留天数, 留存统计需要计算30天后留存率,因此至少应保留31天的日志数据 + */ + async clean(days) { + days = Math.max(parseInt(days), 1) + console.log('clean session logs - day:', days) + + const dateTime = new DateTime() + const res = await this.delete(this.tableName, { + create_time: { + $lt: dateTime.getTimeBySetDays(0 - days) + } + }) + + if (!res.code) { + console.log('clean session log:', res) + } + return res + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/setting.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/setting.js new file mode 100644 index 00000000..a0702ed3 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/setting.js @@ -0,0 +1,44 @@ +/** + * @class Version 应用版本模型 + */ +const BaseMod = require('./base') +const { + DateTime +} = require('../lib') +module.exports = class Setting extends BaseMod { + constructor() { + super() + this.tableName = 'opendb-tempdata' + this.tablePrefix = false + this.settingKey = "uni-stat-setting" + } + + /** + * 获取统计云端配置 + */ + async getSetting() { + const res = await this.getCollection(this.tableName).doc(this.settingKey).get(); + if (res.data && res.data[0] && res.data[0].value) { + return res.data[0].value; + } else { + return { + mode: "open", + day: 7 + }; + } + } + /** + * 检测N天内是否有设备访问记录,如果有,则返回true,否则返回false + */ + async checkAutoRun(obj = {}) { + let { + day = 7 + } = obj; + const _ = this.dbCmd; + let nowTime = Date.now(); + const res = await this.getCollection("uni-stat-session-logs").where({ + create_time: _.gte(nowTime - 1000 * 3600 * 24 * day) + }).count(); + return res.total > 0 ? true : false; + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/shareLog.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/shareLog.js new file mode 100644 index 00000000..7a34bd33 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/shareLog.js @@ -0,0 +1,104 @@ +/** + * @class ShareLog 分享日志模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const SessionLog = require('./sessionLog') +const { + DateTime +} = require('../lib') +module.exports = class ShareLog extends BaseMod { + constructor() { + super() + this.tableName = 'share-logs' + } + + /** + * 分析日志填充 + * @param {Object} reportParams 上报参数 + * @param {Object} sessionLogData 会话日志数据,此参数传递可减少数据库查询 + */ + async fill(reportParams, sessionLogData) { + let params, sessionLogInfo, sessionKey; + const fillParams = [] + const sessionLog = new SessionLog() + const platform = new Platform() + const dateTime = new DateTime() + const channel = new Channel() + for (const rk in reportParams) { + params = reportParams[rk] + + //暂存下会话数据,减少读库 + sessionKey = params.ak + params.did + params.p + if (!sessionLogData[sessionKey]) { + // 会话日志 + sessionLogInfo = await sessionLog.getSession(params) + if (sessionLogInfo.code) { + return sessionLogInfo + } + if (this.debug) { + console.log('sessionLogInfo', JSON.stringify(sessionLogInfo)) + } + sessionLogData[sessionKey] = sessionLogInfo + } else { + sessionLogInfo = sessionLogData[sessionKey] + } + + // 填充数据 + fillParams.push({ + appid: params.ak, + version: params.v ? params.v : '', + platform: platform.getPlatformCode(params.ut, params.p), + channel: channel.getChannelCode(params), + device_id: params.did, + uid: params.uid ? params.uid : '', + session_id: sessionLogInfo.data.sessionLogId, + page_id: sessionLogInfo.data.pageId, + create_time: dateTime.getTime() + }) + } + + if (fillParams.length === 0) { + return { + code: 200, + msg: 'Invild param' + } + } + + const res = await this.insert(this.tableName, fillParams) + if (res && res.inserted) { + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'Filled error' + } + } + } + + /** + * 分享日志清理 + * @param {Number} days 保留天数 + */ + async clean(days) { + days = Math.max(parseInt(days), 1) + console.log('clean share logs - day:', days) + + const dateTime = new DateTime() + + const res = await this.delete(this.tableName, { + create_time: { + $lt: dateTime.getTimeBySetDays(0 - days) + } + }) + + if (!res.code) { + console.log('clean share log:', res) + } + return res + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/statResult.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/statResult.js new file mode 100644 index 00000000..2596f8fe --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/statResult.js @@ -0,0 +1,2151 @@ +/** + * @class StatResult 基础数据结果统计模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const Version = require('./version') +const SessionLog = require('./sessionLog') +const UserSessionLog = require('./userSessionLog') +const ErrorLog = require('./errorLog') +const ActiveDevices = require('./activeDevices') +const ActiveUsers = require('./activeUsers') +const UniIDUsers = require('./uniIDUsers') +const { + DateTime +} = require('../lib') +module.exports = class StatResult extends BaseMod { + constructor() { + super() + this.tableName = 'result' + this.platforms = [] + this.channels = [] + this.versions = [] + } + + /** + * 基础数据统计 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async stat(type, date, reset) { + const allowedType = ['hour', 'day', 'week', 'month'] + if (!allowedType.includes(type)) { + return { + code: 1002, + msg: 'This type is not allowed' + } + } + + if (this.debug) { + console.log('result --type:' + type + ', date:' + date + ', reset:' + reset) + } + + this.fillType = type + const dateTime = new DateTime() + const dateDimension = dateTime.getTimeDimensionByType(type, -1, date) + this.startTime = dateDimension.startTime + this.endTime = dateDimension.endTime + if (this.debug) { + console.log('dimension time', this.startTime + '--' + this.endTime) + } + + // 查看当前时间段日志是否已存在,防止重复生成 + if (!reset) { + const checkRes = await this.getCollection(this.tableName).where({ + dimension: this.fillType, + start_time: this.startTime, + end_time: this.endTime + }).get() + if (checkRes.data.length > 0) { + console.log('log have existed') + return { + code: 1003, + msg: 'This log have existed' + } + } + } else { + const delRes = await this.delete(this.tableName, { + start_time: this.startTime, + end_time: this.endTime + }) + console.log('delete old data result:', JSON.stringify(delRes)) + } + + // 周月数据单独统计 + if (['week', 'month'].includes(this.fillType)) { + return await this.statWeekOrMonth() + } + + // 数据获取 + this.sessionLog = new SessionLog() + const statRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + is_first_visit: 1, + page_count: 1, + duration: 1, + create_time: 1 + }, + match: { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel' + }, + new_device_count: { + $sum: '$is_first_visit' + }, + page_view_count: { + $sum: '$page_count' + }, + total_duration: { + $sum: '$duration' + }, + session_times: { + $sum: 1 + } + }, + sort: { + new_device_count: 1, + page_view_count: 1, + session_times: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + if (this.debug) { + console.log('statRes', JSON.stringify(statRes)) + } + + this.fillData = [] + this.composes = [] + if (statRes.data.length > 0) { + for (const i in statRes.data) { + await this.fill(statRes.data[i]) + } + } + //补充数据 + await this.replenishStat() + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + return res + } + + /** + * 按周/月统计 + */ + async statWeekOrMonth() { + const statRes = await this.aggregate(this.tableName, { + project: { + appid: 1, + version_id: 1, + platform_id: 1, + channel_id: 1, + new_device_count: 1, + new_user_count: 1, + page_visit_count: 1, + user_visit_times: 1, + app_launch_count: 1, + error_count: 1, + bounce_times: 1, + duration: 1, + user_duration: 1, + dimension: 1, + start_time: 1 + }, + match: { + dimension: 'day', + start_time: { + $gte: this.startTime, + $lte: this.endTime + } + }, + group: { + _id: { + appid: '$appid', + version_id: '$version_id', + platform_id: '$platform_id', + channel_id: '$channel_id' + }, + new_device_count: { + $sum: '$new_device_count' + }, + new_user_count: { + $sum: '$new_user_count' + }, + error_count: { + $sum: '$error_count' + }, + page_count: { + $sum: '$page_visit_count' + }, + total_duration: { + $sum: '$duration' + }, + total_user_duration: { + $sum: '$user_duration' + }, + total_user_session_times: { + $sum: '$user_session_times' + }, + session_times: { + $sum: '$app_launch_count' + }, + total_bounce_times: { + $sum: '$bounce_times' + } + }, + sort: { + new_device_count: 1, + error_count: 1, + page_count: 1 + }, + getAll: true + }) + + let res = { + code: 0, + msg: 'success' + } + if (this.debug) { + console.log('statRes', JSON.stringify(statRes)) + } + + this.activeDevices = new ActiveDevices() + this.activeUsers = new ActiveUsers() + this.fillData = [] + this.composes = [] + if (statRes.data.length > 0) { + for (const i in statRes.data) { + await this.getWeekOrMonthData(statRes.data[i]) + } + } + //补充数据 + await this.replenishStat() + if (this.fillData.length > 0) { + res = await this.batchInsert(this.tableName, this.fillData) + } + return res + } + + /** + * 获取周/月维度的填充数据 + * @param {Object} data 统计数据 + */ + async getWeekOrMonthData(data) { + + const matchCondition = { + ...data._id, + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + } + + // 查询活跃设备数 + const statVisitDeviceRes = await this.getCollection(this.activeDevices.tableName).where({ + ...matchCondition, + dimension: this.fillType + }).count() + + let activeDeviceCount = 0 + if (statVisitDeviceRes && statVisitDeviceRes.total > 0) { + activeDeviceCount = statVisitDeviceRes.total + } + + // 设备次均停留时长 + let avgSessionTime = 0 + if (data.total_duration > 0 && data.session_times > 0) { + avgSessionTime = Math.round(data.total_duration / data.session_times) + } + + // 设均停留时长 + let avgDeviceTime = 0 + if (data.total_duration > 0 && activeDeviceCount > 0) { + avgDeviceTime = Math.round(data.total_duration / activeDeviceCount) + } + + // 跳出率 + let bounceRate = 0 + if (data.total_bounce_times > 0 && data.session_times > 0) { + bounceRate = data.total_bounce_times * 100 / data.session_times + bounceRate = parseFloat(bounceRate.toFixed(2)) + } + + // 累计设备数 + let totalDevices = data.new_device_count + const totalDeviceRes = await this.getCollection(this.tableName).where({ + ...matchCondition, + dimension: this.fillType, + start_time: { + $lt: this.startTime + } + }).orderBy('start_time', 'desc').limit(1).get() + if (totalDeviceRes && totalDeviceRes.data.length > 0) { + totalDevices += totalDeviceRes.data[0].total_devices + } + + //活跃用户数 + const statVisitUserRes = await this.getCollection(this.activeUsers.tableName).where({ + ...matchCondition, + dimension: this.fillType + }).count() + let activeUserCount = 0 + if (statVisitUserRes && statVisitUserRes.total > 0) { + activeUserCount = statVisitUserRes.total + } + + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data._id.platform_id]) { + platformInfo = this.platforms[data._id.platform_id] + } else { + const platform = new Platform() + platformInfo = await this.getById(platform.tableName, data._id.platform_id) + if (!platformInfo || platformInfo.length === 0) { + platformInfo.code = '' + } + this.platforms[data._id.platform_id] = platformInfo + } + + // 渠道信息 + let channelInfo = null + if (this.channels && this.channels[data._id.channel_id]) { + channelInfo = this.channels[data._id.channel_id] + } else { + const channel = new Channel() + channelInfo = await this.getById(channel.tableName, data._id.channel_id) + if (!channelInfo || channelInfo.length === 0) { + channelInfo.channel_code = '' + } + this.channels[data._id.channel_id] = channelInfo + } + + // 版本信息 + let versionInfo = null + if (this.versions && this.versions[data._id.version_id]) { + versionInfo = this.versions[data._id.version_id] + } else { + const version = new Version() + versionInfo = await this.getById(version.tableName, data._id.version_id, false) + if (!versionInfo || versionInfo.length === 0) { + versionInfo.version = '' + } + this.versions[data._id.version_id] = versionInfo + } + + //总用户数 + const uniIDUsers = new UniIDUsers() + let totalUserCount = await uniIDUsers.getUserCount(data._id.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, { + $lte: this.endTime + }) + + //人均停留时长 + let avgUserTime = 0 + if (data.total_user_duration > 0 && activeUserCount > 0) { + avgUserTime = Math.round(data.total_user_duration / activeUserCount) + } + + //用户次均访问时长 + let avgUserSessionTime = 0 + if (data.total_user_duration > 0 && data.total_user_session_times > 0) { + avgUserSessionTime = Math.round(data.total_user_duration / data.total_user_session_times) + } + + //安卓平台活跃设备数需要减去sdk更新后device_id发生变更的设备数 + if(platformInfo.code === 'android') { + try{ + const statVisitOldDeviceRes = await this.getCollection(this.activeDevices.tableName).where({ + ...data._id, + create_time: { + $gte: this.startTime, + $lte: this.endTime + }, + dimension: this.fillType + '-old' + }).count() + if (statVisitOldDeviceRes && statVisitOldDeviceRes.total > 0) { + // 活跃设备留存数 + activeDeviceCount -= statVisitOldDeviceRes.total + //平均设备停留时长 + avgDeviceTime = Math.round(data.total_duration / activeDeviceCount) + } + } catch (e) { + console.log('server error: ' + e) + } + } + + const insertParam = { + appid: data._id.appid, + platform_id: data._id.platform_id, + channel_id: data._id.channel_id, + version_id: data._id.version_id, + //总设备数 + total_devices: totalDevices, + //本时间段新增设备数 + new_device_count: data.new_device_count, + //登录用户会话次数 + user_session_times: data.total_user_session_times, + //活跃设备数 + active_device_count: activeDeviceCount, + //总用户数 + total_users: totalUserCount, + //新增用户数 + new_user_count: data.new_user_count, + //活跃用户数 + active_user_count: activeUserCount, + //应用启动次数 = 设备会话次数 + app_launch_count: data.session_times, + //页面访问次数 + page_visit_count: data.page_view_count, + //错误次数 + error_count: data.error_count, + //会话总访问时长 + duration: data.total_duration, + //用户会话总访问时长 + user_duration: data.total_user_duration, + avg_device_session_time: avgSessionTime, + avg_user_session_time: avgUserSessionTime, + avg_device_time: avgDeviceTime, + avg_user_time: avgUserTime, + bounce_times: data.total_bounce_times, + bounce_rate: bounceRate, + retention: {}, + dimension: this.fillType, + stat_date: new DateTime().getDate('Ymd', this.startTime), + start_time: this.startTime, + end_time: this.endTime + } + + this.fillData.push(insertParam) + this.composes.push(data._id.appid + '_' + data._id.platform_id + '_' + data._id.channel_id + '_' + data + ._id.version_id) + + return insertParam + } + + /** + * 基础填充数据-目前只有小时和天维度的数据 + * @param {Object} data 统计数据 + */ + async fill(data) { + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[data._id.platform]) { + //暂存下数据,减少读库 + platformInfo = this.platforms[data._id.platform] + } else { + const platform = new Platform() + platformInfo = await platform.getPlatformAndCreate(data._id.platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[data._id.platform] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + + // 渠道信息 + let channelInfo = null + const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey] + } else { + const channel = new Channel() + channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + + // 版本信息 + let versionInfo = null + const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const version = new Version() + versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + // 访问设备数统计 + const matchCondition = data._id + Object.assign(matchCondition, { + create_time: { + $gte: this.startTime, + $lte: this.endTime + } + }) + const statVisitDeviceRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + device_id: 1, + create_time: 1 + }, + match: matchCondition, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + let activeDeviceCount = 0 + if (statVisitDeviceRes.data.length > 0) { + activeDeviceCount = statVisitDeviceRes.data[0].total_devices + } + + //安卓平台活跃设备数需要减去sdk更新后device_id发生变更的设备数 + if(platformInfo.code === 'android') { + const oldDeviceRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + old_device_id: 1, + create_time: 1 + }, + match: matchCondition, + group: { + _id: { + device_id: '$old_device_id' + }, + create_time: { + $min: '$create_time' + }, + sessionCount: { + $sum: 1 + } + }, + sort: { + create_time: 1, + sessionCount: 1 + }, + getAll: true + }) + + if(oldDeviceRes.data.length) { + const thisOldDeviceIds = [] + for (const tau in oldDeviceRes.data) { + if(oldDeviceRes.data[tau]._id.device_id) { + thisOldDeviceIds.push(oldDeviceRes.data[tau]._id.device_id) + } + } + const statVisitOldDeviceRes = await this.aggregate(this.sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + device_id: 1, + create_time: 1 + }, + match: { + ...matchCondition, + device_id: { + $in: thisOldDeviceIds + } + }, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('statVisitOldDeviceRes', JSON.stringify(statVisitOldDeviceRes)) + } + if (statVisitOldDeviceRes && statVisitOldDeviceRes.data.length > 0) { + // 活跃设备留存数 + activeDeviceCount -= statVisitOldDeviceRes.data[0].total_devices + } + } + } + + // 错误数量统计 + const errorLog = new ErrorLog() + const statErrorRes = await this.getCollection(errorLog.tableName).where(matchCondition).count() + let errorCount = 0 + if (statErrorRes && statErrorRes.total > 0) { + errorCount = statErrorRes.total + } + + // 设备的次均停留时长,设备访问总时长/设备会话次数 + let avgSessionTime = 0 + if (data.total_duration > 0 && data.session_times > 0) { + avgSessionTime = Math.round(data.total_duration / data.session_times) + } + + // 设均停留时长 + let avgDeviceTime = 0 + if (data.total_duration > 0 && activeDeviceCount > 0) { + avgDeviceTime = Math.round(data.total_duration / activeDeviceCount) + } + + // 跳出率 + let bounceTimes = 0 + const bounceRes = await this.getCollection(this.sessionLog.tableName).where({ + ...matchCondition, + page_count: 1 + }).count() + if (bounceRes && bounceRes.total > 0) { + bounceTimes = bounceRes.total + } + let bounceRate = 0 + if (bounceTimes > 0 && data.session_times > 0) { + bounceRate = bounceTimes * 100 / data.session_times + bounceRate = parseFloat(bounceRate.toFixed(2)) + } + + // 应用启动次数 = 会话次数 + const launchCount = data.session_times + + // 累计设备数 + let totalDevices = data.new_device_count + const totalDeviceRes = await this.getCollection(this.tableName).where({ + appid: data._id.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + dimension: this.fillType, + start_time: { + $lt: this.startTime + } + }).orderBy('start_time', 'desc').limit(1).get() + + if (totalDeviceRes && totalDeviceRes.data.length > 0) { + totalDevices += totalDeviceRes.data[0].total_devices + } + + //活跃用户数 + const userSessionLog = new UserSessionLog() + let activeUserCount = 0 + const activeUserCountRes = await this.aggregate(userSessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + uid: 1, + create_time: 1 + }, + match: matchCondition, + group: [{ + _id: { + uid: '$uid' + } + }, { + _id: {}, + total_users: { + $sum: 1 + } + }] + }) + + if (activeUserCountRes && activeUserCountRes.data.length > 0) { + activeUserCount = activeUserCountRes.data[0].total_users + } + + //新增用户数 + const uniIDUsers = new UniIDUsers() + let newUserCount = await uniIDUsers.getUserCount(matchCondition.appid, matchCondition.platform, + matchCondition.channel, matchCondition.version, { + $gte: this.startTime, + $lte: this.endTime + }) + + //总用户数 + let totalUserCount = await uniIDUsers.getUserCount(matchCondition.appid, matchCondition.platform, + matchCondition.channel, matchCondition.version, { + $lte: this.endTime + }) + + + //用户停留总时长及总会话次数 + let totalUserDuration = 0 + let totalUserSessionTimes = 0 + const totalUserDurationRes = await this.aggregate(userSessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + duration: 1, + create_time: 1 + }, + match: matchCondition, + group: [{ + _id: {}, + total_duration: { + $sum: "$duration" + }, + total_session_times: { + $sum: 1 + } + }] + }) + if (totalUserDurationRes && totalUserDurationRes.data.length > 0) { + totalUserDuration = totalUserDurationRes.data[0].total_duration + totalUserSessionTimes = totalUserDurationRes.data[0].total_session_times + } + + //人均停留时长 + let avgUserTime = 0 + //用户次均访问时长 + let avgUserSessionTime = 0 + if (totalUserDuration > 0 && activeUserCount > 0) { + avgUserTime = Math.round(totalUserDuration / activeUserCount) + avgUserSessionTime = Math.round(totalUserDuration / totalUserSessionTimes) + } + + //设置填充数据 + const datetime = new DateTime() + const insertParam = { + appid: data._id.appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + total_devices: totalDevices, + new_device_count: data.new_device_count, + active_device_count: activeDeviceCount, + total_users: totalUserCount, + new_user_count: newUserCount, + active_user_count: activeUserCount, + user_session_times: totalUserSessionTimes, + app_launch_count: launchCount, + page_visit_count: data.page_view_count, + error_count: errorCount, + duration: data.total_duration, + user_duration: totalUserDuration, + avg_device_session_time: avgSessionTime, + avg_user_session_time: avgUserSessionTime, + avg_device_time: avgDeviceTime, + avg_user_time: avgUserTime, + bounce_times: bounceTimes, + bounce_rate: bounceRate, + retention: {}, + dimension: this.fillType, + stat_date: datetime.getDate('Ymd', this.startTime), + start_time: this.startTime, + end_time: this.endTime + } + + //数据填充 + this.fillData.push(insertParam) + this.composes.push(data._id.appid + '_' + platformInfo._id + '_' + channelInfo._id + '_' + versionInfo + ._id) + return insertParam + } + + /** + * 基础统计数据补充,防止累计数据丢失 + */ + async replenishStat() { + if (this.debug) { + console.log('composes data', this.composes) + } + + const datetime = new DateTime() + // const { + // startTime + // } = datetime.getTimeDimensionByType(this.fillType, -1, this.startTime) + + //上一次统计时间 + let preStatRes = await this.getCollection(this.tableName).where({ + start_time: {$lt: this.startTime}, + dimension: this.fillType + }).orderBy('start_time', 'desc').limit(1).get() + let preStartTime = 0 + if(preStatRes && preStatRes.data.length > 0) { + preStartTime = preStatRes.data[0].start_time + } + + if (this.debug) { + console.log('replenishStat-preStartTime', preStartTime) + } + + if(!preStartTime) { + return false + } + + // 上一阶段数据 + const preStatData = await this.selectAll(this.tableName, { + start_time: preStartTime, + dimension: this.fillType + }, { + appid: 1, + platform_id: 1, + channel_id: 1, + version_id: 1, + total_devices: 1, + total_users: 1 + }) + + if (!preStatData || preStatData.data.length === 0) { + return false + } + + if (this.debug) { + console.log('preStatData', JSON.stringify(preStatData)) + } + + let preKey + const preKeyArr = [] + for (const pi in preStatData.data) { + preKey = preStatData.data[pi].appid + '_' + preStatData.data[pi].platform_id + '_' + preStatData + .data[pi].channel_id + '_' + preStatData.data[pi].version_id + if (!this.composes.includes(preKey) && !preKeyArr.includes(preKey)) { + preKeyArr.push(preKey) + if (this.debug) { + console.log('preKey -add', preKey) + } + this.fillData.push({ + appid: preStatData.data[pi].appid, + platform_id: preStatData.data[pi].platform_id, + channel_id: preStatData.data[pi].channel_id, + version_id: preStatData.data[pi].version_id, + total_devices: preStatData.data[pi].total_devices, + new_device_count: 0, + active_device_count: 0, + total_users: preStatData.data[pi].total_users, + new_user_count: 0, + active_user_count: 0, + user_session_times: 0, + app_launch_count: 0, + page_visit_count: 0, + error_count: 0, + duration: 0, + user_duration: 0, + avg_device_session_time: 0, + avg_user_session_time: 0, + avg_device_time: 0, + avg_user_time: 0, + bounce_times: 0, + bounce_rate: 0, + retention: {}, + dimension: this.fillType, + stat_date: datetime.getDate('Ymd', this.startTime), + start_time: this.startTime, + end_time: this.endTime + }) + } else if (this.debug) { + console.log('preKey -have', preKey) + } + } + + return true + } + + /** + * 留存数据统计 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {String} mod 统计模块 device:设备,user:用户 + */ + async retentionStat(type, date, mod = 'device') { + date = date ? date : new DateTime().getTimeBySetDays(-1, date) + const allowedType = ['day', 'week', 'month'] + if (!allowedType.includes(type)) { + return { + code: 1002, + msg: 'This type is not allowed' + } + } + let days = [] + switch (type) { + case 'week': + case 'month': + days = [1, 2, 3, 4, 5, 6, 7, 8, 9] + break + default: + days = [1, 2, 3, 4, 5, 6, 7, 14, 30] + break + } + + let res = { + code: 0, + msg: 'success' + } + + for (const day in days) { + //留存统计数据库查询较为复杂,因此这里拆分为多个维度 + if (mod && mod === 'user') { + //用户留存统计 + if (type === 'day') { + res = await this.userRetentionFillDayly(type, days[day], date) + } else { + res = await this.userRetentionFillWeekOrMonth(type, days[day], date) + } + } else { + //设备留存统计 + if (type === 'day') { + res = await this.deviceRetentionFillDayly(type, days[day], date) + } else { + res = await this.deviceRetentionFillWeekOrMonth(type, days[day], date) + } + } + } + return res + } + + /** + * 设备日留存统计数据填充 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async deviceRetentionFillDayly(type, day, date) { + if (type !== 'day') { + return { + code: 301, + msg: 'Type error:' + type + } + } + const dateTime = new DateTime() + const { + startTime, + endTime + } = dateTime.getTimeDimensionByType(type, 0 - day, date) + + if (!startTime || !endTime) { + return { + code: 1001, + msg: 'The statistic time get failed' + } + } + + // 截止时间范围 + const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date) + + // 获取当时批次的统计日志 + const resultLogRes = await this.selectAll(this.tableName, { + dimension: type, + start_time: startTime, + end_time: endTime + }) + // const resultLogRes = await this.getCollection(this.tableName).where({ + // dimension: type, + // start_time: startTime, + // end_time: endTime + // }).get() + + if (this.debug) { + console.log('resultLogRes', JSON.stringify(resultLogRes)) + } + + if (!resultLogRes || resultLogRes.data.length === 0) { + if (this.debug) { + console.log('Not found this log --' + type + ':' + day + ', start:' + startTime + ',endTime:' + + endTime) + } + return { + code: 1000, + msg: 'Not found this log' + } + } + + const sessionLog = new SessionLog() + const platform = new Platform() + const channel = new Channel() + const version = new Version() + let res = null + for (const resultIndex in resultLogRes.data) { + const resultLog = resultLogRes.data[resultIndex] + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[resultLog.platform_id]) { + platformInfo = this.platforms[resultLog.platform_id] + } else { + platformInfo = await this.getById(platform.tableName, resultLog.platform_id) + if (!platformInfo || platformInfo.length === 0) { + platformInfo.code = '' + } + this.platforms[resultLog.platform_id] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + // 渠道信息 + let channelInfo = null + if (this.channels && this.channels[resultLog.channel_id]) { + channelInfo = this.channels[resultLog.channel_id] + } else { + channelInfo = await this.getById(channel.tableName, resultLog.channel_id) + if (!channelInfo || channelInfo.length === 0) { + channelInfo.channel_code = '' + } + this.channels[resultLog.channel_id] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + // 版本信息 + let versionInfo = null + if (this.versions && this.versions[resultLog.version_id]) { + versionInfo = this.versions[resultLog.version_id] + } else { + versionInfo = await this.getById(version.tableName, resultLog.version_id, false) + if (!versionInfo || versionInfo.length === 0) { + versionInfo.version = '' + } + this.versions[resultLog.version_id] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + // 获取该批次的活跃设备数 + const activeDeviceRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + device_id: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + create_time: { + $gte: startTime, + $lte: endTime + } + }, + group: { + _id: { + device_id: '$device_id' + }, + create_time: { + $min: '$create_time' + }, + sessionCount: { + $sum: 1 + } + }, + sort: { + create_time: 1, + sessionCount: 1 + }, + getAll: true + }) + + if (this.debug) { + console.log('activeDeviceRes', JSON.stringify(activeDeviceRes)) + } + let activeDeviceRate = 0 + let activeDevices = 0 + if (activeDeviceRes && activeDeviceRes.data.length > 0) { + const thisDayActiveDevices = activeDeviceRes.data.length + const thisDayActiveDeviceIds = [] + for (const tau in activeDeviceRes.data) { + thisDayActiveDeviceIds.push(activeDeviceRes.data[tau]._id.device_id) + } + if (this.debug) { + console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds)) + } + + // 留存活跃设备查询 + const retentionActiveDeviceRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + device_id: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + device_id: { + $in: thisDayActiveDeviceIds + }, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('retentionActiveDeviceRes', JSON.stringify(retentionActiveDeviceRes)) + } + if (retentionActiveDeviceRes && retentionActiveDeviceRes.data.length > 0) { + // 活跃设备留存数 + activeDevices = retentionActiveDeviceRes.data[0].total_devices + // 活跃设备留存率 + activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2)) + } + + //安卓平台留存需要增加sdk更新后device_id发生变更的设备数 + if(platformInfo.code === 'android') { + const retentionActiveOldDeviceRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + old_device_id: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + old_device_id: { + $in: thisDayActiveDeviceIds + }, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }, + group: [{ + _id: { + device_id: '$old_device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('retentionActiveOldDeviceRes', JSON.stringify(retentionActiveOldDeviceRes)) + } + if (retentionActiveOldDeviceRes && retentionActiveOldDeviceRes.data.length > 0) { + // 活跃设备留存数 + activeDevices += retentionActiveOldDeviceRes.data[0].total_devices + // 活跃设备留存率 + activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2)) + } + } + } + + // 获取该批次新增设备数 + const newDeviceRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + is_first_visit: 1, + device_id: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + is_first_visit: 1, + create_time: { + $gte: startTime, + $lte: endTime + } + }, + group: { + _id: { + device_id: '$device_id' + }, + create_time: { + $min: '$create_time' + }, + sessionCount: { + $sum: 1 + } + }, + sort: { + create_time: 1, + sessionCount: 1 + }, + getAll: true + }) + + let newDeviceRate = 0 + let newDevices = 0 + if (newDeviceRes && newDeviceRes.data.length > 0) { + const thisDayNewDevices = newDeviceRes.data.length + const thisDayNewDeviceIds = [] + for (const tau in newDeviceRes.data) { + thisDayNewDeviceIds.push(newDeviceRes.data[tau]._id.device_id) + } + + if (this.debug) { + console.log('thisDayNewDeviceIds', JSON.stringify(thisDayNewDeviceIds)) + } + + // 留存的设备查询 + const retentionNewDeviceRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + device_id: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + device_id: { + $in: thisDayNewDeviceIds + }, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }, + group: [{ + _id: { + device_id: '$device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + if (retentionNewDeviceRes && retentionNewDeviceRes.data.length > 0) { + // 新增设备留存数 + newDevices = retentionNewDeviceRes.data[0].total_devices + // 新增设备留存率 + newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2)) + } + + //安卓平台留存需要增加sdk更新后device_id发生变更的设备数 + if(platformInfo.code === 'android') { + const retentionNewOldDeviceRes = await this.aggregate(sessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + old_device_id: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + old_device_id: { + $in: thisDayNewDeviceIds + }, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }, + group: [{ + _id: { + device_id: '$old_device_id' + } + }, { + _id: {}, + total_devices: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('retentionNewOldDeviceRes', JSON.stringify(retentionNewOldDeviceRes)) + } + + if (retentionNewOldDeviceRes && retentionNewOldDeviceRes.data.length > 0) { + // 新增设备留存数 + newDevices += retentionNewOldDeviceRes.data[0].total_devices + // 新增设备留存率 + newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2)) + } + } + } + + // 数据更新 + const retentionData = resultLog.retention + const dataKey = type.substr(0, 1) + '_' + day + if (!retentionData.active_device) { + retentionData.active_device = {} + } + retentionData.active_device[dataKey] = { + device_count: activeDevices, + device_rate: activeDeviceRate + } + if (!retentionData.new_device) { + retentionData.new_device = {} + } + retentionData.new_device[dataKey] = { + device_count: newDevices, + device_rate: newDeviceRate + } + + if (this.debug) { + console.log('retentionData', JSON.stringify(retentionData)) + } + + res = await this.update(this.tableName, { + retention: retentionData + }, { + _id: resultLog._id + }) + } + + if (res && res.updated) { + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'retention data update failed' + } + } + } + + /** + * 设备周/月留存数据填充 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async deviceRetentionFillWeekOrMonth(type, day, date) { + if (!['week', 'month'].includes(type)) { + return { + code: 301, + msg: 'Type error:' + type + } + } + const dateTime = new DateTime() + const { + startTime, + endTime + } = dateTime.getTimeDimensionByType(type, 0 - day, date) + + if (!startTime || !endTime) { + return { + code: 1001, + msg: 'The statistic time get failed' + } + } + + // 截止时间范围 + const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date) + + // 获取当时批次的统计日志 + const resultLogRes = await this.selectAll(this.tableName, { + dimension: type, + start_time: startTime, + end_time: endTime + }) + + if (this.debug) { + console.log('resultLogRes', JSON.stringify(resultLogRes)) + } + + if (!resultLogRes || resultLogRes.data.length === 0) { + if (this.debug) { + console.log('Not found this session log --' + type + ':' + day + ', start:' + startTime + + ',endTime:' + endTime) + } + return { + code: 1000, + msg: 'Not found this session log' + } + } + + const activeDevicesObj = new ActiveDevices() + let res = null; + let activeDeviceRes; + let activeDeviceRate; + let activeDevices; + let newDeviceRate; + let newDevices + for (const resultIndex in resultLogRes.data) { + const resultLog = resultLogRes.data[resultIndex] + // 获取该批次的活跃设备数 + activeDeviceRes = await this.selectAll(activeDevicesObj.tableName, { + appid: resultLog.appid, + platform_id: resultLog.platform_id, + channel_id: resultLog.channel_id, + version_id: resultLog.version_id, + dimension: type, + create_time: { + $gte: startTime, + $lte: endTime + } + }, { + device_id: 1 + }) + + if (this.debug) { + console.log('activeDeviceRes', JSON.stringify(activeDeviceRes)) + } + + activeDeviceRate = 0 + activeDevices = 0 + if (activeDeviceRes && activeDeviceRes.data.length > 0) { + const thisDayActiveDevices = activeDeviceRes.data.length + const thisDayActiveDeviceIds = [] + for (const tau in activeDeviceRes.data) { + thisDayActiveDeviceIds.push(activeDeviceRes.data[tau].device_id) + } + if (this.debug) { + console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds)) + } + // 留存活跃设备数 + const retentionActiveDeviceRes = await this.getCollection(activeDevicesObj.tableName).where({ + appid: resultLog.appid, + platform_id: resultLog.platform_id, + channel_id: resultLog.channel_id, + version_id: resultLog.version_id, + device_id: { + $in: thisDayActiveDeviceIds + }, + dimension: type, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }).count() + + if (this.debug) { + console.log('retentionActiveDeviceRes', JSON.stringify(retentionActiveDeviceRes)) + } + if (retentionActiveDeviceRes && retentionActiveDeviceRes.total > 0) { + // 活跃设备留存数 + activeDevices = retentionActiveDeviceRes.total + // 活跃设备留存率 + activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2)) + } + } + + // 获取该批次的新增设备数 + const newDeviceRes = await this.selectAll(activeDevicesObj.tableName, { + appid: resultLog.appid, + platform_id: resultLog.platform_id, + channel_id: resultLog.channel_id, + version_id: resultLog.version_id, + is_new: 1, + dimension: type, + create_time: { + $gte: startTime, + $lte: endTime + } + }, { + device_id: 1 + }) + + newDeviceRate = 0 + newDevices = 0 + if (newDeviceRes && newDeviceRes.data.length > 0) { + const thisDayNewDevices = newDeviceRes.data.length + const thisDayNewDeviceIds = [] + for (const tau in newDeviceRes.data) { + thisDayNewDeviceIds.push(newDeviceRes.data[tau].device_id) + } + + // 新增设备留存数 + const retentionNewDeviceRes = await this.getCollection(activeDevicesObj.tableName).where({ + appid: resultLog.appid, + platform_id: resultLog.platform_id, + channel_id: resultLog.channel_id, + version_id: resultLog.version_id, + device_id: { + $in: thisDayNewDeviceIds + }, + dimension: type, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }).count() + + if (retentionNewDeviceRes && retentionNewDeviceRes.total > 0) { + // 新增设备留存数 + newDevices = retentionNewDeviceRes.total + // 新增设备留存率 + newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2)) + } + } + + // 数据更新 + const retentionData = resultLog.retention + const dataKey = type.substr(0, 1) + '_' + day + if (!retentionData.active_device) { + retentionData.active_device = {} + } + retentionData.active_device[dataKey] = { + device_count: activeDevices, + device_rate: activeDeviceRate + } + if (!retentionData.new_device) { + retentionData.new_device = {} + } + retentionData.new_device[dataKey] = { + device_count: newDevices, + device_rate: newDeviceRate + } + + if (this.debug) { + console.log('retentionData', JSON.stringify(retentionData)) + } + + res = await this.update(this.tableName, { + retention: retentionData + }, { + _id: resultLog._id + }) + } + + if (res && res.updated) { + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'retention data update failed' + } + } + } + + /** + * 用户日留存数据填充 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async userRetentionFillDayly(type, day, date) { + if (type !== 'day') { + return { + code: 301, + msg: 'Type error:' + type + } + } + const dateTime = new DateTime() + const { + startTime, + endTime + } = dateTime.getTimeDimensionByType(type, 0 - day, date) + + if (!startTime || !endTime) { + return { + code: 1001, + msg: 'The statistic time get failed' + } + } + + // 截止时间范围 + const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date) + + // 获取当时批次的统计日志 + const resultLogRes = await this.selectAll(this.tableName, { + dimension: type, + start_time: startTime, + end_time: endTime + }) + + if (this.debug) { + console.log('resultLogRes', JSON.stringify(resultLogRes)) + } + + if (!resultLogRes || resultLogRes.data.length === 0) { + if (this.debug) { + console.log('Not found this log --' + type + ':' + day + ', start:' + startTime + ',endTime:' + + endTime) + } + return { + code: 1000, + msg: 'Not found this log' + } + } + + const userSessionLog = new UserSessionLog() + const uniIDUsers = new UniIDUsers() + const platform = new Platform() + const channel = new Channel() + const version = new Version() + let res = null + for (const resultIndex in resultLogRes.data) { + const resultLog = resultLogRes.data[resultIndex] + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[resultLog.platform_id]) { + platformInfo = this.platforms[resultLog.platform_id] + } else { + platformInfo = await this.getById(platform.tableName, resultLog.platform_id) + if (!platformInfo || platformInfo.length === 0) { + platformInfo.code = '' + } + this.platforms[resultLog.platform_id] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + // 渠道信息 + let channelInfo = null + if (this.channels && this.channels[resultLog.channel_id]) { + channelInfo = this.channels[resultLog.channel_id] + } else { + channelInfo = await this.getById(channel.tableName, resultLog.channel_id) + if (!channelInfo || channelInfo.length === 0) { + channelInfo.channel_code = '' + } + this.channels[resultLog.channel_id] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + // 版本信息 + let versionInfo = null + if (this.versions && this.versions[resultLog.version_id]) { + versionInfo = this.versions[resultLog.version_id] + } else { + versionInfo = await this.getById(version.tableName, resultLog.version_id, false) + if (!versionInfo || versionInfo.length === 0) { + versionInfo.version = '' + } + this.versions[resultLog.version_id] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + // 获取该时间段内的活跃用户 + const activeUserRes = await this.aggregate(userSessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + uid: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + create_time: { + $gte: startTime, + $lte: endTime + } + }, + group: { + _id: { + uid: '$uid' + }, + create_time: { + $min: '$create_time' + }, + sessionCount: { + $sum: 1 + } + }, + sort: { + create_time: 1, + sessionCount: 1 + }, + getAll: true + }) + + if (this.debug) { + console.log('activeUserRes', JSON.stringify(activeUserRes)) + } + + //活跃用户留存率 + let activeUserRate = 0 + //活跃用户留存数 + let activeUsers = 0 + if (activeUserRes && activeUserRes.data.length > 0) { + const thisDayActiveUsers = activeUserRes.data.length + //获取该时间段内的活跃用户编号,这里没用lookup联查是因为数据量较大时lookup效率很低 + const thisDayActiveUids = [] + for (let tau in activeUserRes.data) { + thisDayActiveUids.push(activeUserRes.data[tau]._id.uid) + } + + if (this.debug) { + console.log('thisDayActiveUids', JSON.stringify(thisDayActiveUids)) + } + + // 留存活跃用户数 + const retentionActiveUserRes = await this.aggregate(userSessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + uid: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + uid: { + $in: thisDayActiveUids + }, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }, + group: [{ + _id: { + uid: '$uid' + } + }, { + _id: {}, + total_users: { + $sum: 1 + } + }] + }) + + if (this.debug) { + console.log('retentionActiveUserRes', JSON.stringify(retentionActiveUserRes)) + } + if (retentionActiveUserRes && retentionActiveUserRes.data.length > 0) { + // 活跃用户留存数 + activeUsers = retentionActiveUserRes.data[0].total_users + // 活跃用户留存率 + activeUserRate = parseFloat((activeUsers * 100 / thisDayActiveUsers).toFixed(2)) + } + } + + + + //新增用户编号 + const thisDayNewUids = await uniIDUsers.getUserIds(resultLog.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, { + $gte: startTime, + $lte: endTime + }) + //新增用户留存率 + let newUserRate = 0 + //新增用户留存数 + let newUsers = 0 + if (thisDayNewUids.length > 0) { + // 现在依然活跃的用户数 + const retentionNewUserRes = await this.aggregate(userSessionLog.tableName, { + project: { + appid: 1, + version: 1, + platform: 1, + channel: 1, + uid: 1, + create_time: 1 + }, + match: { + appid: resultLog.appid, + version: versionInfo.version, + platform: platformInfo.code, + channel: channelInfo.channel_code, + uid: { + $in: thisDayNewUids + }, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }, + group: [{ + _id: { + uid: '$uid' + } + }, { + _id: {}, + total_users: { + $sum: 1 + } + }] + }) + + if (retentionNewUserRes && retentionNewUserRes.data.length > 0) { + // 新增用户留存数 + newUsers = retentionNewUserRes.data[0].total_users + // 新增用户留存率 + newUserRate = parseFloat((newUsers * 100 / thisDayNewUids.length).toFixed(2)) + } + } + + // 数据更新 + const retentionData = resultLog.retention + const dataKey = type.substr(0, 1) + '_' + day + if (!retentionData.active_user) { + retentionData.active_user = {} + } + retentionData.active_user[dataKey] = { + user_count: activeUsers, + user_rate: activeUserRate + } + if (!retentionData.new_user) { + retentionData.new_user = {} + } + retentionData.new_user[dataKey] = { + user_count: newUsers, + user_rate: newUserRate + } + + if (this.debug) { + console.log('retentionData', JSON.stringify(retentionData)) + } + + res = await this.update(this.tableName, { + retention: retentionData + }, { + _id: resultLog._id + }) + } + + if (res && res.updated) { + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'retention data update failed' + } + } + } + + + /** + * 用户周/月留存数据填充 + * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计 + * @param {Date|Time} date 指定日期或时间戳 + * @param {Boolean} reset 是否重置,为ture时会重置该批次数据 + */ + async userRetentionFillWeekOrMonth(type, day, date) { + if (!['week', 'month'].includes(type)) { + return { + code: 301, + msg: 'Type error:' + type + } + } + const dateTime = new DateTime() + const { + startTime, + endTime + } = dateTime.getTimeDimensionByType(type, 0 - day, date) + + if (!startTime || !endTime) { + return { + code: 1001, + msg: 'The statistic time get failed' + } + } + + // 截止时间范围 + const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date) + + // 获取当时批次的统计日志 + const resultLogRes = await this.selectAll(this.tableName, { + dimension: type, + start_time: startTime, + end_time: endTime + }) + + if (this.debug) { + console.log('resultLogRes', JSON.stringify(resultLogRes)) + } + + if (!resultLogRes || resultLogRes.data.length === 0) { + if (this.debug) { + console.log('Not found this session log --' + type + ':' + day + ', start:' + startTime + + ',endTime:' + endTime) + } + return { + code: 1000, + msg: 'Not found this session log' + } + } + + const activeUserObj = new ActiveUsers() + const uniIDUsers = new UniIDUsers() + const platform = new Platform() + const channel = new Channel() + const version = new Version() + let res = null + //活跃用户留存率 + let activeUserRate + //活跃用户留存数 + let activeUsers + //新增用户留存率 + let newUserRate + //新增用户留存数 + let newUsers + for (const resultIndex in resultLogRes.data) { + const resultLog = resultLogRes.data[resultIndex] + + // 平台信息 + let platformInfo = null + if (this.platforms && this.platforms[resultLog.platform_id]) { + platformInfo = this.platforms[resultLog.platform_id] + } else { + platformInfo = await this.getById(platform.tableName, resultLog.platform_id) + if (!platformInfo || platformInfo.length === 0) { + platformInfo.code = '' + } + this.platforms[resultLog.platform_id] = platformInfo + if (this.debug) { + console.log('platformInfo', JSON.stringify(platformInfo)) + } + } + // 渠道信息 + let channelInfo = null + if (this.channels && this.channels[resultLog.channel_id]) { + channelInfo = this.channels[resultLog.channel_id] + } else { + channelInfo = await this.getById(channel.tableName, resultLog.channel_id) + if (!channelInfo || channelInfo.length === 0) { + channelInfo.channel_code = '' + } + this.channels[resultLog.channel_id] = channelInfo + if (this.debug) { + console.log('channelInfo', JSON.stringify(channelInfo)) + } + } + // 版本信息 + let versionInfo = null + if (this.versions && this.versions[resultLog.version_id]) { + versionInfo = this.versions[resultLog.version_id] + } else { + versionInfo = await this.getById(version.tableName, resultLog.version_id, false) + if (!versionInfo || versionInfo.length === 0) { + versionInfo.version = '' + } + this.versions[resultLog.version_id] = versionInfo + if (this.debug) { + console.log('versionInfo', JSON.stringify(versionInfo)) + } + } + + // 获取该批次的活跃用户数 + const activeUserRes = await this.selectAll(activeUserObj.tableName, { + appid: resultLog.appid, + platform_id: resultLog.platform_id, + channel_id: resultLog.channel_id, + version_id: resultLog.version_id, + dimension: type, + create_time: { + $gte: startTime, + $lte: endTime + } + }, { + uid: 1 + }) + + if (this.debug) { + console.log('activeUserRes', JSON.stringify(activeUserRes)) + } + + activeUserRate = 0 + activeUsers = 0 + if (activeUserRes && activeUserRes.data.length > 0) { + const thisDayactiveUsers = activeUserRes.data.length + const thisDayActiveDeviceIds = [] + for (const tau in activeUserRes.data) { + thisDayActiveDeviceIds.push(activeUserRes.data[tau].uid) + } + if (this.debug) { + console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds)) + } + // 留存活跃用户数 + const retentionactiveUserRes = await this.getCollection(activeUserObj.tableName).where({ + appid: resultLog.appid, + platform_id: resultLog.platform_id, + channel_id: resultLog.channel_id, + version_id: resultLog.version_id, + uid: { + $in: thisDayActiveDeviceIds + }, + dimension: type, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }).count() + + if (this.debug) { + console.log('retentionactiveUserRes', JSON.stringify(retentionactiveUserRes)) + } + if (retentionactiveUserRes && retentionactiveUserRes.total > 0) { + // 活跃用户留存数 + activeUsers = retentionactiveUserRes.total + // 活跃用户留存率 + activeUserRate = parseFloat((activeUsers * 100 / thisDayactiveUsers).toFixed(2)) + } + } + + + //新增用户编号 + const thisDayNewUids = await uniIDUsers.getUserIds(resultLog.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, { + $gte: startTime, + $lte: endTime + }) + + // 新增用户留存率 + newUserRate = 0 + // 新增用户留存数 + newUsers = 0 + if(thisDayNewUids && thisDayNewUids.length > 0) { + // 新增用户留存数 + const retentionnewUserRes = await this.getCollection(activeUserObj.tableName).where({ + appid: resultLog.appid, + platform_id: resultLog.platform_id, + channel_id: resultLog.channel_id, + version_id: resultLog.version_id, + uid: { + $in: thisDayNewUids + }, + dimension: type, + create_time: { + $gte: lastTimeInfo.startTime, + $lte: lastTimeInfo.endTime + } + }).count() + + if (retentionnewUserRes && retentionnewUserRes.total > 0) { + // 新增用户留存数 + newUsers = retentionnewUserRes.total + // 新增用户留存率 + newUserRate = parseFloat((newUsers * 100 / thisDayNewUids.length).toFixed(2)) + } + } + + // 数据更新 + const retentionData = resultLog.retention + const dataKey = type.substr(0, 1) + '_' + day + if (!retentionData.active_user) { + retentionData.active_user = {} + } + retentionData.active_user[dataKey] = { + user_count: activeUsers, + user_rate: activeUserRate + } + if (!retentionData.new_user) { + retentionData.new_user = {} + } + retentionData.new_user[dataKey] = { + user_count: newUsers, + user_rate: newUserRate + } + + if (this.debug) { + console.log('retentionData', JSON.stringify(retentionData)) + } + + res = await this.update(this.tableName, { + retention: retentionData + }, { + _id: resultLog._id + }) + } + + if (res && res.updated) { + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'retention data update failed' + } + } + } + + + /** + * 清理实时统计的日志 + * @param {Number} days 实时统计日志保留天数 + */ + async cleanHourLog(days = 7) { + + console.log('clean hour logs - day:', days) + + const dateTime = new DateTime() + + const res = await this.delete(this.tableName, { + dimension: 'hour', + start_time: { + $lt: dateTime.getTimeBySetDays(0 - days) + } + }) + + if (!res.code) { + console.log('clean hour logs - res:', res) + } + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/config.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/config.js new file mode 100644 index 00000000..f2ff72a4 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/config.js @@ -0,0 +1,12 @@ +/** + * 表名 + */ +const dbName = { + uniIdUsers: 'uni-id-users', // 支付订单明细表 + uniPayOrders: 'uni-pay-orders', // 支付订单明细表 + uniStatPayResult: 'uni-stat-pay-result', // 统计结果存储表 + uniStatSessionLogs: 'uni-stat-session-logs', // 设备会话日志表(主要用于统计访问设备数) + uniStatUserSessionLogs: 'uni-stat-user-session-logs', // 用户会话日志表(主要用于统计访问人数) +}; + +module.exports = dbName; diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/index.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/index.js new file mode 100644 index 00000000..20c08ae1 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/index.js @@ -0,0 +1,10 @@ +/** + * 数据库操作 + */ +module.exports = { + uniIdUsers: require('./uniIdUsers'), + uniPayOrders: require('./uniPayOrders'), + uniStatPayResult: require('./uniStatPayResult'), + uniStatSessionLogs: require('./uniStatSessionLogs'), + uniStatUserSessionLogs: require('./uniStatUserSessionLogs'), +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniIdUsers.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniIdUsers.js new file mode 100644 index 00000000..a25ddf29 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniIdUsers.js @@ -0,0 +1,62 @@ +/** + * 数据库操作 + */ +const dbName = require("./config"); + +let db = uniCloud.database(); +let _ = db.command; +let $ = _.aggregate; + +class Dao { + async count(whereJson) { + let dbRes = await db.collection(dbName.uniIdUsers).where(whereJson).count() + return dbRes && dbRes.total ? dbRes.total : 0; + } + + async countNewUserOrder(obj) { + let { + whereJson, + status + } = obj; + let dbRes = await db.collection(dbName.uniIdUsers).aggregate() + .match(whereJson) + .lookup({ + from: dbName.uniPayOrders, + let: { + user_id: '$_id', + }, + pipeline: $.pipeline() + .match(_.expr($.and([ + $.eq(['$user_id', '$$user_id']), + $.in(['$status', status]) + ]))) + .limit(1) + .done(), + as: 'order', + }) + .unwind({ + path: '$order', + }) + .group({ + _id: null, + count: { + $addToSet: '$_id' + }, + }) + .addFields({ + count: { + $size: '$count' + } + }) + .end() + try { + return dbRes.data[0].count; + } catch (err) { + return 0; + } + + } +} + + +module.exports = new Dao(); diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniPayOrders.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniPayOrders.js new file mode 100644 index 00000000..ce149f21 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniPayOrders.js @@ -0,0 +1,99 @@ +/** + * 数据库操作 + */ +const BaseMod = require('../../base'); +const dbName = require("./config"); + +class Dao extends BaseMod { + + constructor() { + super() + this.tablePrefix = false; // 不使用表前缀 + } + + async group(data) { + let { + start_time, + end_time, + status: status_str + } = data; + let status; + if (status_str === "已下单") { + + } else if (status_str === "已付款") { + status = { + $gt: 0 + } + } else if (status_str === "已退款") { + status = { + $in: [2, 3] + } + } + const dbRes = await this.aggregate(dbName.uniPayOrders, { + match: { + create_date: { + $gte: start_time, + $lte: end_time + }, + status + }, + group: { + _id: { + appid: '$appid', + version: '$stat_data.app_version', + platform: '$stat_data.platform', + channel: '$stat_data.channel', + }, + status: { + $first: '$status' + }, + // 支付金额 + total_fee: { + $sum: '$total_fee' + }, + // 退款金额 + refund_fee: { + $sum: '$refund_fee' + }, + // 支付笔数 + order_count: { + $sum: 1 + }, + // 支付人数(去重复) + user_count: { + $addToSet: '$user_id' + }, + // 支付设备数(去重复) + device_count: { + $addToSet: '$device_id' + }, + + create_date: { + $min: '$create_date' + } + }, + addFields: { + user_count: { + $size: '$user_count' + }, + device_count: { + $size: '$device_count' + } + }, + // 按创建时间排序 + sort: { + create_date: 1 + }, + getAll: true + }); + + let list = dbRes.data; + list.map((item) => { + item.status_str = status_str; + }); + return list; + } +} + + +module.exports = new Dao(); diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatPayResult.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatPayResult.js new file mode 100644 index 00000000..4731f520 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatPayResult.js @@ -0,0 +1,36 @@ +/** + * 数据库操作 + */ +const BaseMod = require('../../base'); +const dbName = require("./config"); + +class Dao extends BaseMod { + + constructor() { + super() + this.tablePrefix = false; // 不使用表前缀 + } + + async list(data) { + let { + whereJson, + } = data; + const dbRes = await this.getCollection(dbName.uniStatPayResult).where(whereJson).get(); + return dbRes.data; + } + + async del(data) { + let { + whereJson + } = data; + const dbRes = await this.delete(dbName.uniStatPayResult, whereJson); + return dbRes.deleted; + } + + async adds(saveList) { + return await this.batchInsert(dbName.uniStatPayResult, saveList); + } +} + + +module.exports = new Dao(); diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatSessionLogs.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatSessionLogs.js new file mode 100644 index 00000000..7cf486a7 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatSessionLogs.js @@ -0,0 +1,78 @@ +/** + * 数据库操作 + */ +const BaseMod = require('../../base'); +const dbName = require("./config"); + +class Dao extends BaseMod { + + constructor() { + super() + this.tablePrefix = false; // 不使用表前缀 + } + + async group(data) { + let { + whereJson, + } = data; + const dbRes = await this.aggregate(dbName.uniStatSessionLogs, { + match: whereJson, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + }, + // 设备数(去重复) + device_count: { + $addToSet: '$device_id' + }, + + create_time: { + $min: '$create_time' + } + }, + addFields: { + device_count: { + $size: '$device_count' + } + }, + // 按创建时间排序 + sort: { + create_time: 1 + }, + getAll: true + }); + + return dbRes.data; + } + + async groupCount(whereJson) { + const dbRes = await this.aggregate(dbName.uniStatSessionLogs, { + match: whereJson, + group: { + _id: null, + // 设备数(去重复) + count: { + $addToSet: '$device_id' + }, + }, + addFields: { + count: { + $size: '$count' + } + }, + getAll: true + }); + try { + return dbRes.data[0].count; + } catch (err) { + return 0; + } + } + +} + + +module.exports = new Dao(); diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatUserSessionLogs.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatUserSessionLogs.js new file mode 100644 index 00000000..a39eb031 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/dao/uniStatUserSessionLogs.js @@ -0,0 +1,78 @@ +/** + * 数据库操作 + */ +const BaseMod = require('../../base'); +const dbName = require("./config"); + +class Dao extends BaseMod { + + constructor() { + super() + this.tablePrefix = false; // 不使用表前缀 + } + + async group(data) { + let { + whereJson + } = data; + const dbRes = await this.aggregate(dbName.uniStatUserSessionLogs, { + match: whereJson, + group: { + _id: { + appid: '$appid', + version: '$version', + platform: '$platform', + channel: '$channel', + }, + // 用户数(去重复) + user_count: { + $addToSet: '$uid' + }, + + create_time: { + $min: '$create_time' + } + }, + addFields: { + user_count: { + $size: '$user_count' + } + }, + // 按创建时间排序 + sort: { + create_time: 1 + }, + getAll: true + }); + + let list = dbRes.data; + return list; + } + + async groupCount(whereJson) { + const dbRes = await this.aggregate(dbName.uniStatUserSessionLogs, { + match: whereJson, + group: { + _id: null, + // 设备数(去重复) + count: { + $addToSet: '$uid' + }, + }, + addFields: { + count: { + $size: '$count' + } + }, + getAll: true + }); + try { + return dbRes.data[0].count; + } catch (err) { + return 0; + } + } +} + + +module.exports = new Dao(); diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/index.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/index.js new file mode 100644 index 00000000..1a7d43c6 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/index.js @@ -0,0 +1,6 @@ +/** + * 基础对外模型 + */ +module.exports = { + PayResult: require('./payResult'), +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/payResult.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/payResult.js new file mode 100644 index 00000000..e30a73d7 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uni-pay/payResult.js @@ -0,0 +1,500 @@ +/** + * @class ActiveDevices 活跃设备模型 - 每日跑批合并,仅添加本周/本月首次访问的设备。 + */ +const BaseMod = require('../base') +const Platform = require('../platform') +const Channel = require('../channel') +const Version = require('../version') + +const { + DateTime, + UniCrypto +} = require('../../lib') + +const dao = require('./dao') + +let db = uniCloud.database(); +let _ = db.command; +let $ = _.aggregate; + +module.exports = class PayResult extends BaseMod { + constructor() { + super() + this.platforms = [] + this.channels = [] + this.versions = [] + } + + /** + 支付金额:统计时间内,成功支付的订单金额之和(不剔除退款订单)。 + 支付笔数:统计时间内,成功支付的订单数,一个订单对应唯一一个订单号。(不剔除退款订单。) + 支付人数:统计时间内,成功支付的人数(不剔除退款订单)。 + 支付设备数:统计时间内,成功支付的设备数(不剔除退款订单)。 + 下单金额:统计时间内,成功下单的订单金额(不剔除退款订单)。 + 下单笔数:统计时间内,成功下单的订单笔数(不剔除退款订单)。 + 下单人数:统计时间内,成功下单的客户数,一人多次下单记为一人(不剔除退款订单)。 + 下单设备数:统计时间内,成功下单的设备数,一台设备多次访问被计为一台(不剔除退款订单)。 + 访问人数:统计时间内,访问人数,一人多次访问被计为一人(只统计已登录的用户)。 + 访问设备数:统计时间内,访问设备数,一台设备多次访问被计为一台(包含未登录的用户)。 + * @desc 支付统计(按日统计) + * @param {string} type 统计范围 hour:按小时统计,day:按天统计,week:按周统计,month:按月统计 quarter:按季度统计 year:按年统计 + * @param {date|time} date + * @param {bool} reset + */ + async stat(type, date, reset, config = {}) { + if (!date) date = Date.now(); + + // 以下是测试代码----------------------------------------------------------- + //reset = true; + //date = 1667318400000; + // 以上是测试代码----------------------------------------------------------- + let res = await this.run(type, date, reset, config); // 每小时 + if (type === "hour" && config.timely) { + /** + * 如果是小时纬度统计,则还需要再统计(今日实时数据) + * 2022-11-01 01:00:00 统计的是 2022-11-01 的天维度数据(即该天0点-1点数据) + * 2022-11-01 02:00:00 统计的是 2022-11-01 的天维度数据(即该天0点-2点数据) + * 2022-11-01 23:00:00 统计的是 2022-11-01 的天维度数据(即该天0点-23点数据) + * 2022-11-02 00:00:00 统计的是 2022-11-01 的天维度数据(即该天最终数据) + * 2022-11-02 01:00:00 统计的是 2022-11-02 的天维度数据(即该天0点-1点数据) + */ + date -= 1000 * 3600; // 需要减去1小时 + let tasks = []; + tasks.push(this.run("day", date, true, 0)); // 今日 + // 以下数据每6小时刷新一次 + const dateTime = new DateTime(); + const timeInfo = dateTime.getTimeInfo(date); + if ((timeInfo.nHour + 1) % 6 === 0) { + tasks.push(this.run("week", date, true, 0)); // 本周 + tasks.push(this.run("month", date, true, 0)); // 本月 + tasks.push(this.run("quarter", date, true, 0)); // 本季度 + tasks.push(this.run("year", date, true, 0)); // 本年度 + } + await Promise.all(tasks); + } + return res; + } + /** + * @desc 支付统计 + * @param {string} type 统计范围 hour:按小时统计,day:按天统计,week:按周统计,month:按月统计 quarter:按季度统计 year:按年统计 + * @param {date|time} date 哪个时间节点计算(默认已当前时间计算) + * @param {bool} reset 如果统计数据已存在,是否需要重新统计 + */ + async run(type, date, reset, offset = -1) { + let dimension = type; + const dateTime = new DateTime(); + // 获取统计的起始时间和截止时间 + const dateDimension = dateTime.getTimeDimensionByType(dimension, offset, date); + let start_time = dateDimension.startTime; + let end_time = dateDimension.endTime; + + let runStartTime = Date.now(); + let debug = true; + if (debug) { + console.log(`-----------------支付统计开始(${dimension})-----------------`); + console.log('本次统计时间:', dateTime.getDate('Y-m-d H:i:s', start_time), "-", dateTime.getDate('Y-m-d H:i:s', end_time)) + console.log('本次统计参数:', 'type:' + type, 'date:' + date, 'reset:' + reset) + } + this.startTime = start_time; + let pubWhere = { + start_time, + end_time + }; + // 查看当前时间段数据是否已存在,防止重复生成 + + if (!reset) { + let list = await dao.uniStatPayResult.list({ + whereJson: { + ...pubWhere, + dimension + } + }); + if (list.length > 0) { + console.log('data have exists') + if (debug) { + let runEndTime = Date.now(); + console.log(`耗时:${((runEndTime - runStartTime ) / 1000).toFixed(3)} 秒`) + console.log(`-----------------支付统计结束(${dimension})-----------------`); + } + return { + code: 1003, + msg: 'Pay data in this time have already existed' + } + } + } else { + let delRes = await dao.uniStatPayResult.del({ + whereJson: { + ...pubWhere, + dimension + } + }); + console.log('Delete old data result:', JSON.stringify(delRes)) + } + // 支付订单分组(已下单) + let statPayOrdersList1 = await dao.uniPayOrders.group({ + ...pubWhere, + status: "已下单" + }); + // 支付订单分组(且已付款,含退款) + let statPayOrdersList2 = await dao.uniPayOrders.group({ + ...pubWhere, + status: "已付款" + }); + // 支付订单分组(已退款) + let statPayOrdersList3 = await dao.uniPayOrders.group({ + ...pubWhere, + status: "已退款" + }); + let statPayOrdersList = statPayOrdersList1.concat(statPayOrdersList2).concat(statPayOrdersList3) + let res = { + code: 0, + msg: 'success' + } + // 将支付订单分组查询结果组装 + let statDta = {}; + if (statPayOrdersList.length > 0) { + for (let i = 0; i < statPayOrdersList.length; i++) { + let item = statPayOrdersList[i]; + let { + appid, + version, + platform, + channel, + } = item._id; + let { + status_str + } = item; + let key = `${appid}-${version}-${platform}-${channel}`; + if (!statDta[key]) { + statDta[key] = { + appid, + version, + platform, + channel, + status: {} + }; + } + let newItem = JSON.parse(JSON.stringify(item)); + delete newItem._id; + statDta[key].status[status_str] = newItem; + } + } + if (this.debug) console.log('statDta: ', statDta) + + let saveList = []; + for (let key in statDta) { + let item = statDta[key]; + let { + appid, + version, + platform, + channel, + status: statusData, + } = item; + if (!channel) channel = item.scene; + + let fieldData = { + pay_total_amount: 0, + pay_order_count: 0, + pay_user_count: 0, + pay_device_count: 0, + create_total_amount: 0, + create_order_count: 0, + create_user_count: 0, + create_device_count: 0, + refund_total_amount: 0, + refund_order_count: 0, + refund_user_count: 0, + refund_device_count: 0, + }; + + + for (let status in statusData) { + let statusItem = statusData[status]; + if (status === "已下单") { + // 已下单 + fieldData.create_total_amount += statusItem.total_fee; + fieldData.create_order_count += statusItem.order_count; + fieldData.create_user_count += statusItem.user_count; + fieldData.create_device_count += statusItem.device_count; + } else if (status === "已付款") { + // 已付款 + fieldData.pay_total_amount += statusItem.total_fee; + fieldData.pay_order_count += statusItem.order_count; + fieldData.pay_user_count += statusItem.user_count; + fieldData.pay_device_count += statusItem.device_count; + } else if (status === "已退款") { + // 已退款 + fieldData.refund_total_amount += statusItem.total_fee; + fieldData.refund_order_count += statusItem.order_count; + fieldData.refund_user_count += statusItem.user_count; + fieldData.refund_device_count += statusItem.device_count; + } + } + // 平台信息 + let platformInfo = null; + if (this.platforms && this.platforms[platform]) { + // 从缓存中读取数据 + platformInfo = this.platforms[platform] + } else { + const platformObj = new Platform() + platformInfo = await platformObj.getPlatformAndCreate(platform, null) + if (!platformInfo || platformInfo.length === 0) { + platformInfo._id = '' + } + this.platforms[platform] = platformInfo; + } + // 渠道信息 + let channelInfo = null + const channelKey = appid + '_' + platformInfo._id + '_' + channel; + if (this.channels && this.channels[channelKey]) { + channelInfo = this.channels[channelKey]; + } else { + const channelObj = new Channel() + channelInfo = await channelObj.getChannelAndCreate(appid, platformInfo._id, channel) + if (!channelInfo || channelInfo.length === 0) { + channelInfo._id = '' + } + this.channels[channelKey] = channelInfo + } + // 版本信息 + let versionInfo = null + const versionKey = appid + '_' + platform + '_' + version + if (this.versions && this.versions[versionKey]) { + versionInfo = this.versions[versionKey] + } else { + const versionObj = new Version() + versionInfo = await versionObj.getVersionAndCreate(appid, platform, version) + if (!versionInfo || versionInfo.length === 0) { + versionInfo._id = '' + } + this.versions[versionKey] = versionInfo + } + let countWhereJson = { + create_time: _.gte(start_time).lte(end_time), + appid, + version, + platform: _.in(getUniPlatform(platform)), + channel, + }; + // 活跃设备数量 + let activity_device_count = await dao.uniStatSessionLogs.groupCount(countWhereJson); + // 活跃用户数量 + let activity_user_count = await dao.uniStatUserSessionLogs.groupCount(countWhereJson); + + /* + // TODO 此处有问题,暂不使用 + // 新设备数量 + let new_device_count = await dao.uniStatSessionLogs.groupCount({ + ...countWhereJson, + is_first_visit: 1, + }); + // 新注册用户数量 + let new_user_count = await dao.uniIdUsers.count({ + register_date: _.gte(start_time).lte(end_time), + register_env: { + appid, + app_version: version, + uni_platform: _.in(getUniPlatform(platform)), + channel, + } + }); + + + // 新注册用户中下单的人数 + let new_user_create_order_count = await dao.uniIdUsers.countNewUserOrder({ + whereJson: { + register_date: _.gte(start_time).lte(end_time), + register_env: { + appid, + app_version: version, + uni_platform: _.in(getUniPlatform(platform)), + channel, + } + }, + status: [-1, 0] + }); + // 新注册用户中支付成功的人数 + let new_user_pay_order_count = await dao.uniIdUsers.countNewUserOrder({ + whereJson: { + register_date: _.gte(start_time).lte(end_time), + register_env: { + appid, + app_version: version, + uni_platform: _.in(getUniPlatform(platform)), + channel, + } + }, + status: [1, 2, 3] + }); */ + + + saveList.push({ + appid, + platform_id: platformInfo._id, + channel_id: channelInfo._id, + version_id: versionInfo._id, + dimension, + create_date: Date.now(), // 记录下当前时间 + start_time, + end_time, + stat_date: getNowDate(start_time, 8, dimension), + ...fieldData, + activity_user_count, + activity_device_count, + // new_user_count, + // new_device_count, + // new_user_create_order_count, + // new_user_pay_order_count, + }); + } + if (this.debug) console.log('saveList: ', saveList) + //return; + if (saveList.length > 0) { + res = await dao.uniStatPayResult.adds(saveList); + } + if (debug) { + let runEndTime = Date.now(); + console.log(`耗时:${((runEndTime - runStartTime ) / 1000).toFixed(3)} 秒`) + console.log(`本次共添加:${saveList.length } 条记录`) + console.log(`-----------------支付统计结束(${dimension})-----------------`); + } + return res + } + +} + + +function getUniPlatform(platform) { + let list = []; + if (["h5", "web"].indexOf(platform) > -1) { + list = ["h5", "web"]; + } else if (["app-plus", "app"].indexOf(platform) > -1) { + list = ["app-plus", "app"]; + } else { + list = [platform]; + } + return list; +} + +function getNowDate(date = new Date(), targetTimezone = 8, dimension) { + if (typeof date === "string" && !isNaN(date)) date = Number(date); + if (typeof date == "number") { + if (date.toString().length == 10) date *= 1000; + date = new Date(date); + } + const { + year, + month, + day, + hour, + minute, + second + } = getFullTime(date); + // 现在的时间 + let date_str; + if (dimension === "month") { + date_str = timeFormat(date, "yyyy-MM", targetTimezone); + } else if (dimension === "quarter") { + date_str = timeFormat(date, "yyyy-MM", targetTimezone); + } else if (dimension === "year") { + date_str = timeFormat(date, "yyyy", targetTimezone); + } else { + date_str = timeFormat(date, "yyyy-MM-dd", targetTimezone); + } + return { + date_str, + year, + month, + day, + hour, + //minute, + //second, + }; +} + +function getFullTime(date = new Date(), targetTimezone = 8) { + if (!date) { + return ""; + } + if (typeof date === "string" && !isNaN(date)) date = Number(date); + if (typeof date == "number") { + if (date.toString().length == 10) date *= 1000; + date = new Date(date); + } + const dif = date.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + const east8time = date.getTime() + timeDif; + date = new Date(east8time); + + let YYYY = date.getFullYear() + ''; + let MM = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1); + let DD = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()); + let hh = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()); + let mm = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes()); + let ss = (date.getSeconds() < 10 ? '0' + (date.getSeconds()) : date.getSeconds()); + return { + YYYY: Number(YYYY), + MM: Number(MM), + DD: Number(DD), + hh: Number(hh), + mm: Number(mm), + ss: Number(ss), + + year: Number(YYYY), + month: Number(MM), + day: Number(DD), + hour: Number(hh), + minute: Number(mm), + second: Number(ss), + }; +}; + + +/** + * 日期格式化 + */ +function timeFormat(time, fmt = 'yyyy-MM-dd hh:mm:ss', targetTimezone = 8) { + try { + if (!time) { + return ""; + } + if (typeof time === "string" && !isNaN(time)) time = Number(time); + // 其他更多是格式化有如下: + // yyyy-MM-dd hh:mm:ss|yyyy年MM月dd日 hh时MM分等,可自定义组合 + let date; + if (typeof time === "number") { + if (time.toString().length == 10) time *= 1000; + date = new Date(time); + } else { + date = time; + } + + const dif = date.getTimezoneOffset(); + const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000); + const east8time = date.getTime() + timeDif; + + date = new Date(east8time); + let opt = { + "M+": date.getMonth() + 1, //月份 + "d+": date.getDate(), //日 + "h+": date.getHours(), //小时 + "m+": date.getMinutes(), //分 + "s+": date.getSeconds(), //秒 + "q+": Math.floor((date.getMonth() + 3) / 3), //季度 + "S": date.getMilliseconds() //毫秒 + }; + if (/(y+)/.test(fmt)) { + fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); + } + for (let k in opt) { + if (new RegExp("(" + k + ")").test(fmt)) { + fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (opt[k]) : (("00" + opt[k]).substr(("" + opt[k]).length))); + } + } + return fmt; + } catch (err) { + // 若格式错误,则原值显示 + return time; + } +}; diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uniIDUsers.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uniIDUsers.js new file mode 100644 index 00000000..387bfd38 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/uniIDUsers.js @@ -0,0 +1,97 @@ +/** + * @class UniIDUsers uni-id 用户模型 + */ +const BaseMod = require('./base') +module.exports = class UniIDUsers extends BaseMod { + + constructor() { + super() + this.tableName = 'uni-id-users' + this.tablePrefix = false + } + + /** + * 获取用户数 + * @param {String} appid DCloud-appid + * @param {String} platform 平台 + * @param {String} channel 渠道 + * @param {String} version 版本 + * @param {Object} registerTime 注册时间范围 例:{$gte:开始日期时间戳, $lte:结束日期时间戳} + * @return {Number} + */ + async getUserCount(appid, platform, channel, version, registerTime) { + if(!appid || !platform) { + return false + } + const condition = this.getCondition(appid, platform, channel, version, registerTime) + let userCount = 0 + const userCountRes = await this.getCollection(this.tableName).where(condition).count() + if(userCountRes && userCountRes.total > 0) { + userCount = userCountRes.total + } + return userCount + } + + /** + * 获取用户编号列表 + * @param {String} appid DCloud-appid + * @param {String} platform 平台 + * @param {String} channel 渠道 + * @param {String} version 版本 + * @param {Object} registerTime 注册时间范围 例:{$gte:开始日期时间戳, $lte:结束日期时间戳} + * @return {Array} + */ + async getUserIds(appid, platform, channel, version, registerTime) { + if(!appid || !platform) { + return false + } + const condition = this.getCondition(appid, platform, channel, version, registerTime) + let uids = [] + const uidsRes = await this.selectAll(this.tableName, condition, { + _id: 1 + }) + + for (const u in uidsRes.data) { + uids.push(uidsRes.data[u]._id) + } + + return uids + } + + /** + * 获取查询条件 + * @param {String} appid DCloud-appid + * @param {String} platform 平台 + * @param {String} channel 渠道 + * @param {String} version 版本 + * @param {Object} registerTime 注册时间范围 例:{$gte:开始日期时间戳, $lte:结束日期时间戳} + */ + getCondition(appid, platform, channel, version, registerTime) { + + let condition = { + 'register_env.appid': appid,//DCloud appid + 'register_env.uni_platform': platform,//平台 + 'register_env.channel': channel ? channel : '1001', //渠道或场景值 + 'register_env.app_version' : version //应用版本区分 + } + + //原生应用平台 + if(['android', 'ios'].includes(platform)) { + condition['register_env.uni_platform'] = 'app'//systemInfo中uniPlatform字段android和ios都用app表示,所以此处查询需要用osName区分一下 + condition['register_env.os_name'] = platform //系统 + } + + //兼容vue2 + if(channel === '1001') { + condition['register_env.channel'] = {$in:['', '1001']} + } + + //注册时间 + if(registerTime) { + condition.register_date = registerTime + } + + return condition + } + +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/userSessionLog.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/userSessionLog.js new file mode 100644 index 00000000..71b89356 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/userSessionLog.js @@ -0,0 +1,229 @@ +/** + * @class UserSessionLog 用户会话日志模型 + */ +const BaseMod = require('./base') +const Platform = require('./platform') +const Channel = require('./channel') +const { + DateTime +} = require('../lib') +module.exports = class UserSessionLog extends BaseMod { + constructor() { + super() + this.tableName = 'user-session-logs' + } + + /** + * 用户会话日志数据填充 + * @param {Object} params 上报参数 + */ + async fill(params) { + + if (!params.sid) { + return { + code: 200, + msg: 'Not found session log' + } + } + + if (!params.uid) { + return { + code: 200, + msg: 'Parameter "uid" not found' + } + } + + const dateTime = new DateTime() + const platform = new Platform() + const channel = new Channel() + + //获取当前页面信息 + if (!params.page_id) { + const pageInfo = await page.getPageAndCreate(params.ak, params.url, params.ttpj) + if (!pageInfo || pageInfo.length === 0) { + return { + code: 300, + msg: 'Not found this entry page' + } + } + params.page_id = pageInfo._id + } + + const nowTime = dateTime.getTime() + + const fillParams = { + appid: params.ak, + version: params.v ? params.v : '', + platform: platform.getPlatformCode(params.ut, params.p), + channel: channel.getChannelCode(params), + session_id: params.sid, + uid: params.uid, + last_visit_time: nowTime, + entry_page_id: params.page_id, + exit_page_id: params.page_id, + page_count: 0, + event_count: 0, + duration: 1, + is_finish: 0, + create_time: nowTime, + } + + const res = await this.insert(this.tableName, fillParams) + + if (res && res.id) { + return { + code: 0, + msg: 'success' + } + } else { + return { + code: 500, + msg: 'User session log filled error' + } + } + } + + /** + * 检测用户会话是否有变化,并更新 + * @param {Object} params 校验参数 - sid:基础会话编号 uid:用户编号 last_visit_user_id:基础会话中最近一个访问用户的编号 + */ + async checkUserSession(params) { + if (!params.sid) { + return { + code: 200, + msg: 'Not found session log' + } + } + + if (!params.uid) { + //用户已退出会话 + if (params.last_visit_user_id) { + if (this.debug) { + console.log('user "' + params.last_visit_user_id + '" is exit session :', params.sid) + } + await this.closeUserSession(params.sid) + } + } else { + //添加用户日志 + if (!params.last_visit_user_id) { + await this.fill(params) + } + //用户已切换 + else if (params.uid != params.last_visit_user_id) { + if (this.debug) { + console.log('user "' + params.last_visit_user_id + '" change to "' + params.uid + + '" in the session :', params.sid) + } + //关闭原会话生成新用户对话 + await this.closeUserSession(params.sid) + await this.fill(params) + } + } + return { + code: 0, + msg: 'success' + } + } + + + + /** + * 关闭用户会话 + * @param {String} sid 基础会话编号 + */ + async closeUserSession(sid) { + if (this.debug) { + console.log('close user session log by sid:', sid) + } + return await this.update(this.tableName, { + is_finish: 1 + }, { + session_id: sid, + is_finish: 0 + }) + } + + + /** + * 更新会话信息 + * @param {String} sid 基础会话编号 + * @param {Object} data 更新数据 + */ + async updateUserSession(sid, data) { + + const userSession = await this.getCollection(this.tableName).where({ + session_id: sid, + uid: data.uid, + is_finish: 0 + }).orderBy('create_time', 'desc').limit(1).get() + + if (userSession.data.length === 0) { + console.log('Not found the user session', { + session_id: sid, + uid: data.uid, + is_finish: 0 + }) + return { + code: 300, + msg: 'Not found the user session' + } + } + + let nowTime = data.nowTime ? data.nowTime : new DateTime().getTime() + const accessTime = nowTime - userSession.data[0].createTime + const accessSenconds = accessTime > 1000 ? parseInt(accessTime / 1000) : 1 + + const updateData = { + last_visit_time: nowTime, + duration: accessSenconds, + } + + //访问页面数量 + if (data.addPageCount) { + updateData.page_count = userSession.data[0].page_count + data.addPageCount + } + //最终访问的页面编号 + if (data.pageId) { + updateData.exit_page_id = data.pageId + } + //产生事件次数 + if (data.eventCount) { + updateData.event_count = userSession.data[0].event_count + data.addEventCount + } + + if (this.debug) { + console.log('update user session log by sid-' + sid, updateData) + } + + await this.update(this.tableName, updateData, { + _id: userSession.data[0]._id + }) + + return { + code: 0, + msg: 'success' + } + } + + /** + * 清理用户会话日志数据 + * @param {Object} days 保留天数, 留存统计需要计算30天后留存率,因此至少应保留31天的日志数据 + */ + async clean(days = 31) { + days = Math.max(parseInt(days), 1) + console.log('clean user session logs - day:', days) + + const dateTime = new DateTime() + const res = await this.delete(this.tableName, { + create_time: { + $lt: dateTime.getTimeBySetDays(0 - days) + } + }) + + if (!res.code) { + console.log('clean user session log:', res) + } + return res + } + +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/version.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/version.js new file mode 100644 index 00000000..b7f9a669 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/version.js @@ -0,0 +1,73 @@ +/** + * @class Version 应用版本模型 + */ +const BaseMod = require('./base') +const { + DateTime +} = require('../lib') +module.exports = class Version extends BaseMod { + constructor() { + super() + this.tableName = 'opendb-app-versions' + this.tablePrefix = false + this.cacheKeyPre = 'uni-stat-app-version-' + } + + /** + * 获取版本信息 + * @param {String} appid DCloud-appid + * @param {String} platformId 平台编号 + * @param {String} appVersion 平台版本号 + */ + async getVersion(appid, platform, appVersion) { + const cacheKey = this.cacheKeyPre + appid + '-' + platform + '-' + appVersion + let versionData = await this.getCache(cacheKey) + if (!versionData) { + const versionInfo = await this.getCollection(this.tableName).where({ + appid: appid, + uni_platform: platform, + type: 'native_app', + version: appVersion + }).limit(1).get() + versionData = [] + if (versionInfo.data.length > 0) { + versionData = versionInfo.data[0] + await this.setCache(cacheKey, versionData) + } + } + return versionData + } + + + /** + * 获取版本信息没有则进行创建 + * @param {String} appid DCloud-appid + * @param {String} platform 平台代码 + * @param {String} appVersion 平台版本号 + */ + async getVersionAndCreate(appid, platform, appVersion) { + const versionInfo = await this.getVersion(appid, platform, appVersion) + if (versionInfo.length === 0) { + if (appVersion.length > 0 && !appVersion.includes('}')) { + const thisTime = new DateTime().getTime() + const insertParam = { + appid: appid, + platform: [], + uni_platform: platform, + type: 'native_app', + version: appVersion, + stable_publish: false, + create_env: 'uni-stat', + create_date: thisTime + } + const res = await this.insert(this.tableName, insertParam) + if (res && res.id) { + return Object.assign(insertParam, { + _id: res.id + }) + } + } + } + return versionInfo + } +} diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/receiver.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/receiver.js new file mode 100644 index 00000000..83dc56cc --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/receiver.js @@ -0,0 +1,126 @@ +/** + * @class UniStatReportDataReceiver uni统计上报数据接收器 + * @function report 上报数据调度处理函数 + */ +const { + parseUrlParams +} = require('../shared') +const SessionLog = require('./mod/sessionLog') +const PageLog = require('./mod/pageLog') +const EventLog = require('./mod/eventLog') +const ErrorLog = require('./mod/errorLog') +const Device = require('./mod/device') +class UniStatReportDataReceiver { + /** + * @description 上报数据调度处理函数 + * @param {Object} params 基础上报参数 + * @param {Object} context 请求附带的上下文信息 + */ + async report(params, context) { + let res = { + code: 0, + msg: 'success' + } + + if (!params || !params.requests) { + return { + code: 200, + msg: 'Invild params' + } + } + + // JSON参数解析 + const requestParam = JSON.parse(params.requests) + if (!requestParam || requestParam.length === 0) { + return { + code: 200, + msg: 'Invild params' + } + } + + // 日志填充 + const sessionParams = [] + const pageParams = [] + const eventParams = [] + const errorParams = [] + const device = new Device() + for (const ri in requestParam) { + //参数解析 + const urlParams = parseUrlParams(requestParam[ri], context) + if (!urlParams.ak) { + return { + code: 201, + msg: 'Not found appid' + } + } + + if (!urlParams.lt) { + return { + code: 202, + msg: 'Not found this log type' + } + } + + switch (parseInt(urlParams.lt)) { + // 会话日志 + case 1: { + sessionParams.push(urlParams) + break + } + // 页面日志 + case 3: + case 11: { + pageParams.push(urlParams) + break + } + // 事件日志 + case 21: { + eventParams.push(urlParams) + break + } + // 错误日志 + case 31: { + errorParams.push(urlParams) + break + } + //unipush信息绑定 + case 101: { + res = await device.bindPush(urlParams) + break + } + default: { + console.log('Invalid type by param "lt:' + urlParams.lt + '"') + break + } + } + } + + //会话日志填充 + if (sessionParams.length > 0) { + const sessionLog = new SessionLog() + res = await sessionLog.batchFill(sessionParams) + } + + //页面日志填充 + if (pageParams.length > 0) { + const pageLog = new PageLog() + res = await pageLog.fill(pageParams) + } + + //事件日志填充 + if (eventParams.length > 0) { + const eventLog = new EventLog() + res = await eventLog.fill(eventParams) + } + + //错误日志填充 + if (errorParams.length > 0) { + const errorLog = new ErrorLog() + res = await errorLog.fill(errorParams) + } + + return res + } +} + +module.exports = UniStatReportDataReceiver diff --git a/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/stat.js b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/stat.js new file mode 100644 index 00000000..35e34a4a --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/stat.js @@ -0,0 +1,388 @@ +/** + * @class UniStatDataStat uni统计-数据统计调度处理模块 + * @function cron 数据统计定时任务处理函数 + * @function stat 数据统计调度处理函数 + * @function cleanLog 日志清理调度处理函数 + */ +const { + DateTime +} = require('./lib') + +const { + sleep +} = require('../shared') + +const { + BaseMod, + SessionLog, + PageLog, + EventLog, + ShareLog, + ErrorLog, + StatResult, + ActiveDevices, + ActiveUsers, + PageResult, + EventResult, + ErrorResult, + Loyalty, + RunErrors, + UserSessionLog, + uniPay, + Setting +} = require('./mod') +class UniStatDataStat { + /** + * 数据统计定时任务处理函数 + * @param {Object} context 服务器请求上下文参数 + */ + async cron(context) { + const baseMod = new BaseMod() + const dateTime = new DateTime() + console.log('Cron start time: ', dateTime.getDate('Y-m-d H:i:s')) + + // const setting = new Setting(); + // let settingValue = await setting.getSetting() + // if (settingValue.mode === "close") { + // // 如果关闭了统计任务,则任务直接结束 + // return { + // code: 0, + // msg: 'Task is close', + // } + // } else if (settingValue.mode === "auto") { + // // 如果开启了节能模式,则判断N天内是否有设备访问记录 + // let runKey = await setting.checkAutoRun(settingValue); + // if (!runKey) { + // return { + // code: 0, + // msg: 'Task is auto close', + // } + // } + // } + + //获取运行参数 + const timeInfo = dateTime.getTimeInfo(null, false) + const cronConfig = baseMod.getConfig('cron') + const cronMin = baseMod.getConfig('cronMin') + const realtimeStat = baseMod.getConfig('realtimeStat') + // 数据跑批 + let res = null + if (cronConfig && cronConfig.length > 0) { + for (var mi in cronConfig) { + const currCronConfig = cronConfig[mi] + const cronType = currCronConfig.type + const cronTime = currCronConfig.time.split(' ') + const cronDimension = currCronConfig.dimension + + //未开启分钟级定时任务,则设置为小时级定时任务 + if (cronTime.length === 4 && !cronMin) { + cronTime.splice(3, 1) + } + + if (baseMod.debug) { + console.log('cronTime', cronTime) + } + //精度为分钟级的定时任务 + if (cronTime.length === 4) { + if (cronTime[0] !== '*') { + //周统计任务 + if (timeInfo.nWeek == cronTime[0] && timeInfo.nHour == cronTime[2] && timeInfo.nMinutes == + cronTime[3]) { + let dimension = cronDimension || 'week'; + console.log(cronType + `--${dimension} run`) + res = await this.stat({ + type: cronType, + dimension: cronDimension, + config: currCronConfig + }) + } + } else if (cronTime[1] !== '*') { + //月统计任务(包含季度统计任务和年统计任务) + if (timeInfo.nDay == cronTime[1] && timeInfo.nHour == cronTime[2] && timeInfo.nMinutes == + cronTime[3]) { + let dimension = cronDimension || 'month'; + console.log(cronType + `--${dimension} run`) + res = await this.stat({ + type: cronType, + dimension: dimension, + config: currCronConfig + }) + } + } else if (cronTime[2] !== '*') { + //日统计任务 + if (timeInfo.nHour == cronTime[2] && timeInfo.nMinutes == cronTime[3]) { + let dimension = cronDimension || 'day'; + console.log(cronType + `--${dimension} run`) + res = await this.stat({ + type: cronType, + dimension: dimension, + config: currCronConfig + }) + } + } else if (cronTime[3] !== '*') { + //实时统计任务 + if (timeInfo.nMinutes == cronTime[3] && realtimeStat) { + let dimension = cronDimension || 'hour'; + console.log(cronType + `--${dimension} run`) + res = await this.stat({ + type: cronType, + dimension: dimension, + config: currCronConfig + }) + } + } + } + //精度为小时级的定时任务 + else if (cronTime.length === 3) { + if (cronTime[0] !== '*') { + //周统计任务 + if (timeInfo.nWeek == cronTime[0] && timeInfo.nHour == cronTime[2]) { + let dimension = cronDimension || 'week'; + console.log(cronType + `--${dimension} run`) + res = await this.stat({ + type: cronType, + dimension: dimension, + config: currCronConfig + }) + } + } else if (cronTime[1] !== '*') { + //月统计任务(包含季度统计任务和年统计任务) + if (timeInfo.nDay == cronTime[1] && timeInfo.nHour == cronTime[2]) { + let dimension = cronDimension || 'month'; + console.log(cronType + `--${dimension} run`) + res = await this.stat({ + type: cronType, + dimension: dimension, + config: currCronConfig + }) + } + } else if (cronTime[2] !== '*') { + //日统计任务 + if (timeInfo.nHour == cronTime[2]) { + let dimension = cronDimension || 'day'; + console.log(cronType + `--${dimension} run`) + res = await this.stat({ + type: cronType, + dimension: dimension, + config: currCronConfig + }) + } + } else { + //实时统计任务 + if (realtimeStat) { + let dimension = cronDimension || 'hour'; + console.log(cronType + `--${dimension} run`) + res = await this.stat({ + type: cronType, + dimension: dimension, + config: currCronConfig + }) + } + } + } else { + console.error('Cron configuration error') + } + } + } + console.log('Cron end time: ', dateTime.getDate('Y-m-d H:i:s')) + return { + code: 0, + msg: 'Task have done', + lastCronResult: res + } + } + + /** + * 数据统计调度处理函数 + * @param {Object} params 统计参数 + */ + async stat(params) { + const { + type, + dimension, + date, + reset, + config + } = params + let res = { + code: 0, + msg: 'success' + } + + try { + switch (type) { + // 基础统计 + case 'stat': { + const resultStat = new StatResult() + res = await resultStat.stat(dimension, date, reset) + break + } + // 活跃设备统计归集 + case 'active-device': { + const activeDevices = new ActiveDevices() + res = await activeDevices.stat(date, reset) + break + } + // 活跃用户统计归集 + case 'active-user': { + const activeUsers = new ActiveUsers() + res = await activeUsers.stat(date, reset) + break + } + // 设备留存统计 + case 'retention-device': { + const retentionStat = new StatResult() + res = await retentionStat.retentionStat(dimension, date) + break + } + // 用户留存统计 + case 'retention-user': { + const retentionStat = new StatResult() + res = await retentionStat.retentionStat(dimension, date, 'user') + break + } + // 页面统计 + case 'page': { + const pageStat = new PageResult() + res = await pageStat.stat(dimension, date, reset) + break + } + // 事件统计 + case 'event': { + const eventStat = new EventResult() + res = await eventStat.stat(dimension, date, reset) + break + } + // 错误统计 + case 'error': { + const errorStat = new ErrorResult() + res = await errorStat.stat(dimension, date, reset) + break + } + // 设备忠诚度统计 + case 'loyalty': { + const loyaltyStat = new Loyalty() + res = await loyaltyStat.stat(dimension, date, reset) + break + } + // 日志清理 + case 'clean': { + res = await this.cleanLog() + } + // 支付统计 + case 'pay-result': { + const paymentResult = new uniPay.PayResult() + res = await paymentResult.stat(dimension, date, reset, config) + break + } + } + } catch (e) { + const maxTryTimes = 2 + if (!this.tryTimes) { + this.tryTimes = 1 + } else { + this.tryTimes++ + } + + //报错则重新尝试2次, 解决部分云服务器偶现连接超时问题 + if (this.tryTimes <= maxTryTimes) { + //休眠1秒后重新调用 + await sleep(1000) + params.reset = true + res = await this.stat(params) + } else { + // 2次尝试失败后记录错误 + console.error('server error: ' + e) + const runError = new RunErrors() + runError.create({ + mod: 'stat', + params: params, + error: e, + create_time: new DateTime().getTime() + }) + + res = { + code: 500, + msg: 'server error' + e + } + } + } + return res + } + + /** + * 日志清理调度处理函数 + */ + async cleanLog() { + const baseMod = new BaseMod() + const cleanLog = baseMod.getConfig('cleanLog') + if (!cleanLog || !cleanLog.open) { + return { + code: 100, + msg: 'The log cleanup service has not been turned on' + } + } + + const res = { + code: 0, + msg: 'success', + data: {} + } + + // 会话日志 + if (cleanLog.reserveDays.sessionLog > 0) { + const sessionLog = new SessionLog() + res.data.sessionLog = await sessionLog.clean(cleanLog.reserveDays.sessionLog) + } + + // 用户会话日志 + if (cleanLog.reserveDays.userSessionLog > 0) { + const userSessionLog = new UserSessionLog() + res.data.userSessionLog = await userSessionLog.clean(cleanLog.reserveDays.userSessionLog) + } + + // 页面日志 + if (cleanLog.reserveDays.pageLog > 0) { + const pageLog = new PageLog() + res.data.pageLog = await pageLog.clean(cleanLog.reserveDays.pageLog) + } + + // 事件日志 + if (cleanLog.reserveDays.eventLog > 0) { + const eventLog = new EventLog() + res.data.eventLog = await eventLog.clean(cleanLog.reserveDays.eventLog) + } + + // 分享日志 + if (cleanLog.reserveDays.shareLog > 0) { + const shareLog = new ShareLog() + res.data.shareLog = await shareLog.clean(cleanLog.reserveDays.shareLog) + } + + // 错误日志 + if (cleanLog.reserveDays.errorLog > 0) { + const errorLog = new ErrorLog() + res.data.errorLog = await errorLog.clean(cleanLog.reserveDays.errorLog) + } + + // 活跃设备日志 + const activeDevicesLog = new ActiveDevices() + res.data.activeDevicesLog = await activeDevicesLog.clean() + + // 活跃用户日志 + const activeUsersLog = new ActiveUsers() + res.data.activeUsersLog = await activeUsersLog.clean() + + // 实时统计日志 + const resultHourLog = new StatResult() + res.data.resultHourLog = await resultHourLog.cleanHourLog() + + //原生应用崩溃日志 + const appCrashLogs = new AppCrashLogs() + res.data.appCrashLogs = await appCrashLogs.clean() + + return res + } +} + +module.exports = UniStatDataStat diff --git a/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/index.js b/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/index.js new file mode 100644 index 00000000..350fcfc5 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/index.js @@ -0,0 +1,188 @@ +const fs = require('fs') +const path = require('path') +const TE = require('./lib/art-template.js'); + +// 标准语法的界定符规则 +TE.defaults.openTag = '{@' +TE.defaults.closeTag = '@}' + +const success = { + success: true +} +const fail = { + success: false +} + +async function translateTCB(_fileList = []) { + if (!_fileList.length) return _fileList + // 腾讯云和阿里云下载链接不同,需要处理一下,阿里云会原样返回 + const { + fileList + } = await uniCloud.getTempFileURL({ + fileList: _fileList + }); + return fileList.map((item, index) => item.tempFileURL ? item.tempFileURL : _fileList[index]) +} + +function hasValue(value) { + if (typeof value !== 'object') return !!value + if (value instanceof Array) return !!value.length + return !!(value && Object.keys(value).length) +} + +module.exports = async function(id) { + if (!id) { + return { + ...fail, + code: -1, + errMsg: 'id required' + }; + } + + // 根据sitemap配置加载页面模板,例如列表页,详情页 + let templatePage = fs.readFileSync(path.resolve(__dirname, './template.html'), 'utf8'); + if (!templatePage) { + return { + ...fail, + code: -2, + errMsg: 'page template no found' + }; + } + + const db = uniCloud.database() + let dbPublishList + try { + dbPublishList = db.collection('opendb-app-list') + } catch (e) {} + + if (!dbPublishList) return fail; + + const record = await dbPublishList.where({ + _id: id + }).get({ + getOne: true + }) + + if (record && record.data && record.data.length) { + const appInfo = record.data[0] + + const defaultOptions = { + hasApp: false, + hasMP: false, + hasH5: false, + hasQuickApp: false + } + + defaultOptions.mpNames = { + 'mp_weixin': '微信', + 'mp_alipay': '支付宝', + 'mp_baidu': '百度', + 'mp_toutiao': '字节', + 'mp_qq': 'QQ', + 'mp_dingtalk': '钉钉', + 'mp_kuaishou': '快手', + 'mp_lark': '飞书', + 'mp_jd': '京东' + } + + const imageList = []; + ['app_android'].forEach(key => { + if (!hasValue(appInfo[key])) return + imageList.push({ + key, + urlKey: 'url', + url: appInfo[key].url + }) + }) + Object.keys(defaultOptions.mpNames).concat('quickapp').forEach(key => { + if (!hasValue(appInfo[key])) return + imageList.push({ + key, + urlKey: 'qrcode_url', + url: appInfo[key].qrcode_url + }) + }); + ['icon_url'].forEach(key => { + if (!hasValue(appInfo[key])) return + imageList.push({ + key, + url: appInfo[key] + }) + }) + const filelist = await translateTCB(imageList.map(item => item.url)) + imageList.forEach((item, index) => { + if (item.urlKey) { + appInfo[item.key][item.urlKey] = filelist[index] + } else { + appInfo[item.key] = filelist[index] + } + }) + if (hasValue(appInfo.screenshot)) { + appInfo.screenshot = await translateTCB(appInfo.screenshot) + } + + { + const appInfoKeys = Object.keys(appInfo) + if (appInfoKeys.some(key => { + return key.indexOf('app_') !== -1 && hasValue(appInfo[key]) + })) { + defaultOptions.hasApp = true + } + if (appInfoKeys.some(key => { + return key.indexOf('mp') !== -1 && hasValue(appInfo[key]) + })) { + defaultOptions.hasMP = true + } + if (appInfo.h5 && appInfo.h5.url) { + defaultOptions.hasH5 = true + } + if (appInfo.quickapp && appInfo.quickapp.qrcode_url) { + defaultOptions.hasQuickApp = true + } + + // app + if (defaultOptions.hasApp && appInfo.app_android && appInfo.app_android.url) { + defaultOptions.android_url = appInfo.app_android.url + } else { + defaultOptions.android_url = '' + } + if (defaultOptions.hasApp && appInfo.app_ios && appInfo.app_ios.url) { + defaultOptions.ios_url = appInfo.app_ios.url + } else { + defaultOptions.ios_url = '' + } + + // mp + defaultOptions.mpKeys = Object.keys(appInfo).filter(key => { + return key.indexOf('mp') !== -1 && hasValue(appInfo[key]) + }) + } + + const html = TE.render(templatePage)(Object.assign({}, appInfo, defaultOptions)); + + if (!(defaultOptions.hasApp || defaultOptions.hasH5 || defaultOptions.hasMP || defaultOptions + .hasQuickApp)) { + return { + ...fail, + code: -100, + errMsg: '缺少应用信息,App、小程序、H5、快应用请至少填写一项' + } + } + + return { + ...success, + mpserverlessComposedResponse: true, // 使用阿里云返回集成响应是需要此字段为true + statusCode: 200, + headers: { + 'content-type': 'text/html' + }, + body: html + }; + } + + return { + ...fail, + code: -3, + errMsg: 'no record' + }; +} diff --git a/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/lib/art-template.js b/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/lib/art-template.js new file mode 100644 index 00000000..7698812f --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/lib/art-template.js @@ -0,0 +1,2 @@ +/*!art-template - Template Engine | http://aui.github.com/artTemplate/*/ +!function(){function a(a){return a.replace(t,"").replace(u,",").replace(v,"").replace(w,"").replace(x,"").split(y)}function b(a){return"'"+a.replace(/('|\\)/g,"\\$1").replace(/\r/g,"\\r").replace(/\n/g,"\\n")+"'"}function c(c,d){function e(a){return m+=a.split(/\n/).length-1,k&&(a=a.replace(/\s+/g," ").replace(//g,"")),a&&(a=s[1]+b(a)+s[2]+"\n"),a}function f(b){var c=m;if(j?b=j(b,d):g&&(b=b.replace(/\n/g,function(){return m++,"$line="+m+";"})),0===b.indexOf("=")){var e=l&&!/^=[=#]/.test(b);if(b=b.replace(/^=[=#]?|[\s;]*$/g,""),e){var f=b.replace(/\s*\([^\)]+\)/,"");n[f]||/^(include|print)$/.test(f)||(b="$escape("+b+")")}else b="$string("+b+")";b=s[1]+b+s[2]}return g&&(b="$line="+c+";"+b),r(a(b),function(a){if(a&&!p[a]){var b;b="print"===a?u:"include"===a?v:n[a]?"$utils."+a:o[a]?"$helpers."+a:"$data."+a,w+=a+"="+b+",",p[a]=!0}}),b+"\n"}var g=d.debug,h=d.openTag,i=d.closeTag,j=d.parser,k=d.compress,l=d.escape,m=1,p={$data:1,$filename:1,$utils:1,$helpers:1,$out:1,$line:1},q="".trim,s=q?["$out='';","$out+=",";","$out"]:["$out=[];","$out.push(",");","$out.join('')"],t=q?"$out+=text;return $out;":"$out.push(text);",u="function(){var text=''.concat.apply('',arguments);"+t+"}",v="function(filename,data){data=data||$data;var text=$utils.$include(filename,data,$filename);"+t+"}",w="'use strict';var $utils=this,$helpers=$utils.$helpers,"+(g?"$line=0,":""),x=s[0],y="return new String("+s[3]+");";r(c.split(h),function(a){a=a.split(i);var b=a[0],c=a[1];1===a.length?x+=e(b):(x+=f(b),c&&(x+=e(c)))});var z=w+x+y;g&&(z="try{"+z+"}catch(e){throw {filename:$filename,name:'Render Error',message:e.message,line:$line,source:"+b(c)+".split(/\\n/)[$line-1].replace(/^\\s+/,'')};}");try{var A=new Function("$data","$filename",z);return A.prototype=n,A}catch(B){throw B.temp="function anonymous($data,$filename) {"+z+"}",B}}var d=function(a,b){return"string"==typeof b?q(b,{filename:a}):g(a,b)};d.version="3.0.0",d.config=function(a,b){e[a]=b};var e=d.defaults={openTag:"<%",closeTag:"%>",escape:!0,cache:!0,compress:!1,parser:null},f=d.cache={};d.render=function(a,b){return q(a,b)};var g=d.renderFile=function(a,b){var c=d.get(a)||p({filename:a,name:"Render Error",message:"Template not found"});return b?c(b):c};d.get=function(a){var b;if(f[a])b=f[a];else if("object"==typeof document){var c=document.getElementById(a);if(c){var d=(c.value||c.innerHTML).replace(/^\s*|\s*$/g,"");b=q(d,{filename:a})}}return b};var h=function(a,b){return"string"!=typeof a&&(b=typeof a,"number"===b?a+="":a="function"===b?h(a.call(a)):""),a},i={"<":"<",">":">",'"':""","'":"'","&":"&"},j=function(a){return i[a]},k=function(a){return h(a).replace(/&(?![\w#]+;)|[<>"']/g,j)},l=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},m=function(a,b){var c,d;if(l(a))for(c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)},n=d.utils={$helpers:{},$include:g,$string:h,$escape:k,$each:m};d.helper=function(a,b){o[a]=b};var o=d.helpers=n.$helpers;d.onerror=function(a){var b="Template Error\n\n";for(var c in a)b+="<"+c+">\n"+a[c]+"\n\n";"object"==typeof console&&console.error(b)};var p=function(a){return d.onerror(a),function(){return"{Template Error}"}},q=d.compile=function(a,b){function d(c){try{return new i(c,h)+""}catch(d){return b.debug?p(d)():(b.debug=!0,q(a,b)(c))}}b=b||{};for(var g in e)void 0===b[g]&&(b[g]=e[g]);var h=b.filename;try{var i=c(a,b)}catch(j){return j.filename=h||"anonymous",j.name="Syntax Error",p(j)}return d.prototype=i.prototype,d.toString=function(){return i.toString()},h&&b.cache&&(f[h]=d),d},r=n.$each,s="break,case,catch,continue,debugger,default,delete,do,else,false,finally,for,function,if,in,instanceof,new,null,return,switch,this,throw,true,try,typeof,var,void,while,with,abstract,boolean,byte,char,class,const,double,enum,export,extends,final,float,goto,implements,import,int,interface,long,native,package,private,protected,public,short,static,super,synchronized,throws,transient,volatile,arguments,let,yield,undefined",t=/\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|\s*\.\s*[$\w\.]+/g,u=/[^\w$]+/g,v=new RegExp(["\\b"+s.replace(/,/g,"\\b|\\b")+"\\b"].join("|"),"g"),w=/^\d[^,]*|,\d[^,]*/g,x=/^,+|,+$/g,y=/^$|,+/;e.openTag="{{",e.closeTag="}}";var z=function(a,b){var c=b.split(":"),d=c.shift(),e=c.join(":")||"";return e&&(e=", "+e),"$helpers."+d+"("+a+e+")"};e.parser=function(a){a=a.replace(/^\s/,"");var b=a.split(" "),c=b.shift(),e=b.join(" ");switch(c){case"if":a="if("+e+"){";break;case"else":b="if"===b.shift()?" if("+b.join(" ")+")":"",a="}else"+b+"{";break;case"/if":a="}";break;case"each":var f=b[0]||"$data",g=b[1]||"as",h=b[2]||"$value",i=b[3]||"$index",j=h+","+i;"as"!==g&&(f="[]"),a="$each("+f+",function("+j+"){";break;case"/each":a="});";break;case"echo":a="print("+e+");";break;case"print":case"include":a=c+"("+b.join(",")+");";break;default:if(/^\s*\|\s*[\w\$]/.test(e)){var k=!0;0===a.indexOf("#")&&(a=a.substr(1),k=!1);for(var l=0,m=a.split("|"),n=m.length,o=m[l++];n>l;l++)o=z(o,m[l]);a=(k?"=":"=#")+o}else a=d.helpers[c]?"=#"+c+"("+b.join(",")+");":"="+a}return a},"function"==typeof define?define(function(){return d}):"undefined"!=typeof exports?module.exports=d:this.template=d}(); \ No newline at end of file diff --git a/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/template.html b/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/template.html new file mode 100644 index 00000000..7f9bdd25 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-portal/createPublishHtml/template.html @@ -0,0 +1,238 @@ + + + + + + + + + {@name@} + + + +
+ +
+ +
+ +

{@name@}

+

{@introduction@}

+
+ + {@if hasApp || hasMP || hasH5 || hasQuickApp@} +
+
+ {@if hasApp@} + App + {@/if@} + {@if hasMP@} + 小程序 + {@/if@} + {@if hasH5@} + H5 + {@/if@} + {@if hasQuickApp@} + 快应用 + {@/if@} +
+
+ {@if hasApp@} +
+ +

扫码获取

+ 下载安装 + + +

Android平台尚未发布,敬请期待~

+
+ {@/if@} + {@if hasMP@} +
+
+
+ {@each mpKeys@} +
+
+
+ {@mpNames[$value]@} +
+
+ {@/each@} +
+
+ {@each mpKeys@} +
+ + +

长按图片识别小程序

+
+ {@/each@} +
+
+
+ {@/if@} + {@if hasH5@} +
+
+ + {@h5.url@} +
+
+ {@/if@} + {@if hasQuickApp@} +
+ +

快应用

+

扫描二维码或复制名称后可在手机应用市场中搜索快应用

+
+ {@/if@} +
+
+ {@/if@} + + +
+
+ +
+
+
+ +
+ + +
+

{@name@}

+

{@introduction@}

+
+ + {@if hasApp || hasMP || hasH5 || hasQuickApp@} +
+
+ {@if hasApp@} + App + {@/if@} + {@if hasMP@} + 小程序 + {@/if@} + {@if hasH5@} + H5 + {@/if@} + {@if hasQuickApp@} + 快应用 + {@/if@} +
+
+ {@if hasApp@} + + {@/if@} + {@if hasMP@} +
+ {@each mpKeys@} +
+ +
扫二维码识别小程序
+
{@mpNames[$value]@}小程序
+
+ {@/each@} +
+ {@/if@} + {@if hasH5@} +
+
+
+ {@h5.url@} +
+
+ {@/if@} + {@if hasQuickApp@} +
+ +

快应用

+

扫描二维码或复制名称后可在手机应用市场中搜索快应用

+
+ {@/if@} +
+
+ {@/if@} +
+ + {@if description && description.length@} +
+

应用描述

+
{@description@}
+
+ {@/if@} + + {@if screenshot && screenshot.length@} +
+

应用截图

+
+
+
    + {@each screenshot@} +
  • + +
  • + {@/each@} +
+
+ {@/if@} +
+

复制成功

+
+
+ + + + + \ No newline at end of file diff --git a/uni_modules/uni-upgrade-center/uniCloud/cloudfunctions/upgrade-center/index.js b/uniCloud-aliyun/cloudfunctions/uni-portal/index.js similarity index 55% rename from uni_modules/uni-upgrade-center/uniCloud/cloudfunctions/upgrade-center/index.js rename to uniCloud-aliyun/cloudfunctions/uni-portal/index.js index f27a8857..9b61ff4a 100644 --- a/uni_modules/uni-upgrade-center/uniCloud/cloudfunctions/upgrade-center/index.js +++ b/uniCloud-aliyun/cloudfunctions/uni-portal/index.js @@ -1,4 +1,12 @@ 'use strict'; +const success = { + success: true +} +const fail = { + success: false +} +const createPublishHtml = require('./createPublishHtml') + exports.main = async (event, context) => { //event为客户端上传的参数 console.log('event : ', event) @@ -7,13 +15,11 @@ exports.main = async (event, context) => { let params = event.data || event.params; switch (event.action) { - case 'deleteFile': - res = await uniCloud.deleteFile({ - fileList: params.fileList - }) + case 'createPublishHtml': + res = createPublishHtml(params.id) break; } //返回数据给客户端 return res -}; +}; diff --git a/uniCloud-aliyun/cloudfunctions/uni-portal/package.json b/uniCloud-aliyun/cloudfunctions/uni-portal/package.json new file mode 100644 index 00000000..1532acd0 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-portal/package.json @@ -0,0 +1,7 @@ +{ + "name": "uni-portal", + "dependencies": {}, + "extensions": { + "uni-cloud-jql": {} + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/cloudfunctions/uni-sms-co/build-template-data.js b/uniCloud-aliyun/cloudfunctions/uni-sms-co/build-template-data.js new file mode 100644 index 00000000..5985e316 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-sms-co/build-template-data.js @@ -0,0 +1,26 @@ +module.exports = (templateData, user) => { + const data = {} + for (const template of templateData) { + const isDynamic = /\{.*?\}/.test(template.value) + + // 仅支持uni-id-users + if (isDynamic) { + const [collection, field] = template.value.replace(/\{|\}/g, '').split('.') + data[template.field] = collection === 'uni-id-users' ? user[field] || template.value: template.value + } else { + data[template.field] = template.value + } + // switch (template.type) { + // case 'static': + // data[template.field] = template.value + // break + // case 'dynamic': + // data[template.field] = user[template.value] || '' + // break + // default: + // throw new Error(`template type [${template.type}] not supported`) + // } + } + + return data +} diff --git a/uniCloud-aliyun/cloudfunctions/uni-sms-co/index.obj.js b/uniCloud-aliyun/cloudfunctions/uni-sms-co/index.obj.js new file mode 100644 index 00000000..7df0ac84 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-sms-co/index.obj.js @@ -0,0 +1,374 @@ +// 云对象教程: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj +// jsdoc语法提示教程:https://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/129 +const createConfig = require('uni-config-center') +const buildTemplateData = require('./build-template-data') +const { parserDynamicField, checkIsStaticTemplate } = require('./utils') +const schemaNameAdapter = require('./schema-name-adapter') + +const uniSmsCo = uniCloud.importObject('uni-sms-co') +const db = uniCloud.database() +const smsConfig = createConfig({ + pluginId: 'uni-sms-co' +}).config() + +function errCode(code) { + return 'uni-sms-co-' + code +} + +const tableNames = { + template: 'opendb-sms-template', + task: 'opendb-sms-task', + log: 'opendb-sms-log' +} + +module.exports = { + _before: async function () { // 通用预处理器 + if (!smsConfig.smsKey || smsConfig.smsKey.length <= 20 || !smsConfig.smsSecret || smsConfig.smsSecret.length <= 20) { + throw new Error('请先配置smsKey和smsSecret') + } + + this.tableNames = tableNames + + /** + * 优化 schema 的命名规范,需要兼容 uni-admin@2.1.6 以下版本 + * 如果是在uni-admin@2.1.6版本以上创建的项目可以将其注释 + * */ + await schemaNameAdapter.call(this) + }, + _after: function (error, result) { + if (error) { + if (error instanceof Error) { + return { + errCode: 'error', + errMsg: error.message + } + } + + if (error.errCode) { + return error + } + + throw error + } + + return result + }, + /** + * 创建短信任务 + * @param {Object} to + * @param {Boolean} to.all=false 全部用户发送 + * @param {String} to.type=user to.all=true时用来区分发送类型 + * @param {Array} to.receiver 用户ID's / 用户标签ID's + * @param {String} templateId 短信模板ID + * @param {Array} templateData 短信模板数据 + * @param {String} options.taskName 任务名称 + */ + async createSmsTask(to, templateId, templateData, options = {}) { + if (!templateId) { + return { + errCode: errCode('template-id-required'), + errMsg: '缺少templateId' + } + } + + if (!to.all && (!to.receiver || to.receiver.length <= 0)) { + return { + errCode: errCode('send-users-is-null'), + errMsg: '请选择要发送的用户' + } + } + + const clientInfo = this.getClientInfo() + + const {data: templates} = await db.collection(this.tableNames.template).where({_id: templateId}).get() + if (templates.length <= 0) { + return { + errCode: errCode('template-not-found'), + errMsg: '短信模板不存在' + } + } + const [template] = templates + + // 创建短信任务 + const task = await db.collection(this.tableNames.task).add({ + app_id: clientInfo.appId, + name: options.taskName, + template_id: templateId, + template_contnet: template.content, + vars: templateData, + to, + send_qty: 0, + success_qty: 0, + fail_qty: 0, + create_date: Date.now() + }) + + uniSmsCo.createUserSmsMessage(task.id) + + return new Promise(resolve => setTimeout(() => resolve({ + errCode: 0, + errMsg: '任务创建成功', + taskId: task.id + }), 300)) + }, + async createUserSmsMessage(taskId, execData = {}) { + const parallel = 100 + let beforeId + const { data: tasks } = await db.collection(this.tableNames.task).where({ _id: taskId }).get() + + if (tasks.length <= 0) { + return { + errCode: errCode('task-id-not-found'), + errMsg: '任务ID不存在' + } + } + + const [task] = tasks + const query = { + mobile: db.command.exists(true) + } + + // 指定用户发送 + if (!task.to.all && task.to.type === 'user') { + let index = 0 + if (execData.beforeId) { + const i = task.to.receiver.findIndex(id => id === execData.beforeId) + index = i !== -1 ? i + 1 : 0 + } + + const receiver = task.to.receiver.slice(index, index + parallel) + query._id = db.command.in(receiver) + beforeId = receiver[receiver.length - 1] + } + + // 指定用户标签 + if (task.to.type === 'userTags') { + query.tags = db.command.in(task.to.receiver) + } + + // 全部用户 + if (task.to.all && execData.beforeId) { + query._id = db.command.gt(execData.beforeId) + } + + // 动态数据仅支持uni-id-users表字段 + const dynamicField = parserDynamicField(task.vars) + const userFields = dynamicField['uni-id-users'] ? dynamicField['uni-id-users'].reduce((res, field) => { + res[field] = true + return res + }, {}): {} + const { data: users } = await db.collection('uni-id-users') + .where(query) + .field({ + mobile: true, + ...userFields + }) + .limit(parallel) + .orderBy('_id', 'asc') + .get() + + if (users.length <= 0) { + // 更新要发送的短信数量 + const count = await db.collection(this.tableNames.log).where({ task_id: taskId }).count() + await db.collection(this.tableNames.task).where({ _id: taskId }).update({ + send_qty: count.total + }) + + // 开始发送 + uniSmsCo.sendSms(taskId) + + return new Promise(resolve => setTimeout(() => resolve({ + errCode: 0, + errMsg: '创建完成' + }), 500)) + } + + if (!beforeId) { + beforeId = users[users.length - 1]._id + } + + let docs = [] + for (const user of users) { + const varData = await buildTemplateData(task.vars, user) + docs.push({ + uid: user._id, + task_id: taskId, + mobile: user.mobile, + var_data: varData, + status: 0, + create_date: Date.now() + }) + } + + await db.collection(this.tableNames.log).add(docs) + + uniSmsCo.createUserSmsMessage(taskId, { beforeId }) + + return new Promise(resolve => setTimeout(() => resolve(), 500)) + }, + async sendSms(taskId) { + const { data: tasks } = await db.collection(this.tableNames.task).where({ _id: taskId }).get() + if (tasks.length <= 0) { + console.warn(`task [${taskId}] not found`) + return + } + + const [task] = tasks + const isStaticTemplate = !task.vars.length + + let sendData = { + appId: task.app_id, + smsKey: smsConfig.smsKey, + smsSecret: smsConfig.smsSecret, + templateId: task.template_id, + data: {} + } + + const { data: records } = await db.collection(this.tableNames.log) + .where({ task_id: taskId, status: 0 }) + .limit(isStaticTemplate ? 50 : 1) + .field({ mobile: true, var_data: true }) + .get() + + if (records.length <= 0) { + return { + errCode: 0, + errMsg: '发送完成' + } + } + + if (isStaticTemplate) { + sendData.phoneList = records.reduce((res, user) => { + res.push(user.mobile) + return res + }, []) + } else { + const [record] = records + sendData.phone = record.mobile + sendData.data = record.var_data + } + + try { + // await sendSms(sendData) + await uniCloud.sendSms(sendData) + // 修改发送状态为已发送 + await db.collection(this.tableNames.log).where({ + _id: db.command.in(records.map(record => record._id)) + }).update({ + status: 1, + send_date: Date.now() + }) + // 更新任务的短信成功数 + await db.collection(this.tableNames.task).where({ _id: taskId }) + .update({ + success_qty: db.command.inc(records.length) + }) + } catch (e) { + console.error('[sendSms Fail]', e) + // 修改发送状态为发送失败 + await db.collection(this.tableNames.log).where({ + _id: db.command.in(records.map(record => record._id)) + }).update({ + status: 2, + reason: e.errMsg || '未知原因', + send_date: Date.now() + }) + // 更新任务的短信失败数 + await db.collection(this.tableNames.task).where({ _id: taskId }) + .update({ + fail_qty: db.command.inc(records.length) + }) + } + + uniSmsCo.sendSms(taskId) + + return new Promise(resolve => setTimeout(() => resolve(), 500)) + }, + async template() { + const {data: templates = []} = await db.collection(this.tableNames.template).get() + return templates + }, + async task (id) { + const {data: tasks} = await db.collection(this.tableNames.task).where({_id: id}).get() + if (tasks.length <= 0) { + return null + } + + return tasks[0] + }, + async updateTemplates (templates) { + if (templates.length <= 0) { + return { + errCode: errCode('template-is-null'), + errMsg: '缺少模板信息' + } + } + + let group = [] + for (const template of templates) { + group.push( + db.collection(this.tableNames.template).doc(String(template.templateId)).set({ + name: template.templateName, + content: template.templateContent, + type: template.templateType, + sign: template.templateSign + }) + ) + } + + await Promise.all(group) + + return { + errCode: 0, + errMsg: '更新成功' + } + }, + async preview (to, templateId, templateData) { + const count = 1 + let query = { + mobile: db.command.exists(true) + } + + // 指定用户发送 + if (!to.all && to.type === 'user') { + const receiver = to.receiver.slice(0, 10) + query._id = db.command.in(receiver) + } + + // 指定用户标签 + if (to.type === 'userTags') { + query.tags = db.command.in(to.receiver) + } + + const {data: users} = await db.collection('uni-id-users').where(query).limit(count).get() + console.log({users, query}) + if (users.length <= 0) { + return { + errCode: errCode('users-is-null'), + errMsg: '请添加要发送的用户' + } + } + + const {data: templates} = await db.collection(this.tableNames.template).where({_id: templateId}).get() + if (templates.length <= 0) { + return { + errCode: errCode('template-not-found'), + errMsg: '模板不存在' + } + } + const [template] = templates + + let docs = [] + for (const user of users) { + const varData = buildTemplateData(templateData, user) + const content = template.content.replace(/\$\{(.*?)\}/g, ($1, param) => varData[param] || $1) + docs.push(`【${template.sign}】${content}`) + } + + return { + errCode: 0, + errMsg: '', + list: docs + } + } +} diff --git a/uniCloud-aliyun/cloudfunctions/uni-sms-co/package.json b/uniCloud-aliyun/cloudfunctions/uni-sms-co/package.json new file mode 100644 index 00000000..b8ec9209 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-sms-co/package.json @@ -0,0 +1,10 @@ +{ + "name": "uni-sms-co", + "dependencies": { + "uni-config-center": "file:../../../uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center" + }, + "extensions": { + "uni-cloud-sms": {}, + "uni-cloud-jql": {} + } +} diff --git a/uniCloud-aliyun/cloudfunctions/uni-sms-co/schema-name-adapter.js b/uniCloud-aliyun/cloudfunctions/uni-sms-co/schema-name-adapter.js new file mode 100644 index 00000000..c37638c0 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-sms-co/schema-name-adapter.js @@ -0,0 +1,15 @@ +const db = uniCloud.database() + +module.exports = async function () { + try { + const count = await db.collection('batch-sms-template').count() + + if (count.total > 0) { + this.tableNames = { + template: 'batch-sms-template', + task: 'batch-sms-task', + log: 'batch-sms-result' + } + } + } catch (e) {} +} diff --git a/uniCloud-aliyun/cloudfunctions/uni-sms-co/utils.js b/uniCloud-aliyun/cloudfunctions/uni-sms-co/utils.js new file mode 100644 index 00000000..e538c171 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-sms-co/utils.js @@ -0,0 +1,42 @@ +exports.chunk = function (arr, num) { + const list = [] + let current = [] + + for (const item of arr) { + current.push(item); + if (current.length === num) { + list.push(current) + current = [] + } + } + + if (current.length) list.push(current) + + return list +} + +exports.checkIsStaticTemplate = function (data = []) { + let isStatic = data.length <= 0 + + for (const template of data) { + if (template.type === 'static') { + isStatic = true + break + } + } + + return isStatic +} + +exports.parserDynamicField = function (templateData) { + return templateData.reduce((res, template) => { + if (/\{.*?\}/.test(template.value)) { + const [collection, field] = template.value.replace(/\{|\}/g, '').split('.') + if (!res[collection]) { + res[collection] = [] + } + res[collection].push(field) + } + return res + }, {}) +} \ No newline at end of file diff --git a/uniCloud-aliyun/cloudfunctions/uni-stat-cron/index.js b/uniCloud-aliyun/cloudfunctions/uni-stat-cron/index.js new file mode 100644 index 00000000..a926115c --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-stat-cron/index.js @@ -0,0 +1,6 @@ +'use strict'; +const uniStat = require('uni-stat') +exports.main = async (event, context) => { + //数据跑批处理函数 + return await uniStat.initStat().cron(context) +}; diff --git a/uniCloud-aliyun/cloudfunctions/uni-stat-cron/package.json b/uniCloud-aliyun/cloudfunctions/uni-stat-cron/package.json new file mode 100644 index 00000000..816e9c79 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-stat-cron/package.json @@ -0,0 +1,27 @@ +{ + "name": "uni-stat-cron", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "uni-stat": "file:../common/uni-stat" + }, + "cloudfunction-config": { + "concurrency": 1, + "memorySize": 512, + "timeout": 600, + "triggers": [ + { + "name": "uni-stat-cron", + "type": "timer", + "config": "0 0 * * * * *" + } + ] + }, + "extensions": {} +} \ No newline at end of file diff --git a/uniCloud-aliyun/cloudfunctions/uni-stat-receiver/index.obj.js b/uniCloud-aliyun/cloudfunctions/uni-stat-receiver/index.obj.js new file mode 100644 index 00000000..b5890f25 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-stat-receiver/index.obj.js @@ -0,0 +1,29 @@ +const uniStat = require('uni-stat') +const uniID = require('uni-id-common') +module.exports = { + report: async function (params = {}) { + //客户端信息 + const clientInfo = this.getClientInfo() + //云服务信息 + const cloudInfo = this.getCloudInfo() + //token信息 + const token = this.getUniIdToken() + //当前登录用户id + let uid + if(token) { + const tokenRes = await uniID.createInstance({ + clientInfo + }).checkToken(token) + + if(tokenRes.uid) { + uid = tokenRes.uid + } + } + //数据上报 + return await uniStat.initReceiver().report(params, { + ...clientInfo, + ...cloudInfo, + uid + }) + } +} diff --git a/uniCloud-aliyun/cloudfunctions/uni-stat-receiver/package.json b/uniCloud-aliyun/cloudfunctions/uni-stat-receiver/package.json new file mode 100644 index 00000000..c244e2e0 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-stat-receiver/package.json @@ -0,0 +1,16 @@ +{ + "name": "uni-stat-receiver", + "dependencies": { + "uni-stat": "file:../common/uni-stat", + "uni-id-common": "file:../../../uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common" + }, + "cloudfunction-config": { + "concurrency": 1, + "memorySize": 128, + "timeout": 60, + "triggers": [] + }, + "extensions": { + "uni-cloud-jql": {} + } +} diff --git a/uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version/index.js b/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/checkVersion/index.js similarity index 88% rename from uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version/index.js rename to uniCloud-aliyun/cloudfunctions/uni-upgrade-center/checkVersion/index.js index 76f42474..bec1cf3c 100644 --- a/uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version/index.js +++ b/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/checkVersion/index.js @@ -1,5 +1,5 @@ 'use strict'; -exports.main = async (event, context) => { +module.exports = async (event, context) => { /** * 检测升级 使用说明 * 上传包: @@ -46,11 +46,11 @@ exports.main = async (event, context) => { platform = /iPhone|iPad/.test(event.headers) ? platform_iOS : platform_Android; } else if (context.OS) { - platform = context.OS === 'android' - ? platform_Android - : context.OS === 'ios' - ? platform_iOS - : platform_Android; + platform = context.OS === 'android' ? + platform_Android : + context.OS === 'ios' ? + platform_iOS : + platform_Android; } if (appid && appVersion && wgtVersion && platform) { @@ -71,13 +71,13 @@ exports.main = async (event, context) => { const hasWgtPackage = !!Object.keys(wgtVersionInDb).length; // 取两个版本中版本号最大的包,版本一样,使用wgt包 - let stablePublishDb = hasAppPackage - ? hasWgtPackage - ? compare(wgtVersionInDb.version, appVersionInDb.version) >= 0 - ? wgtVersionInDb - : appVersionInDb - : appVersionInDb - : wgtVersionInDb; + let stablePublishDb = hasAppPackage ? + hasWgtPackage ? + compare(wgtVersionInDb.version, appVersionInDb.version) >= 0 ? + wgtVersionInDb : + appVersionInDb : + appVersionInDb : + wgtVersionInDb; const { version, @@ -145,7 +145,7 @@ function compare(v1 = '0', v2 = '0') { if (curV1 > curV2) { result = 1 break; - } else if(curV1 < curV2) { + } else if (curV1 < curV2) { result = -1 break; } diff --git a/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/index.js b/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/index.js new file mode 100644 index 00000000..9f60a2de --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/index.js @@ -0,0 +1,91 @@ +'use strict'; +const success = { + success: true +} +const fail = { + success: false +} +const checkVersion = require('./checkVersion') + +exports.main = async (event, context) => { + //event为客户端上传的参数 + + const db = uniCloud.database() + const appListDBName = 'opendb-app-list' + const appVersionDBName = 'opendb-app-versions' + let res = {}; + + if (event.headers) { + try { + if (event.httpMethod.toLocaleLowerCase() === 'get') { + event = event.queryStringParameters; + } else { + event = JSON.parse(event.body); + } + } catch (e) { + return { + code: 500, + msg: '请求错误' + }; + } + } + + let params = event.data || event.params; + switch (event.action) { + case 'checkVersion': + res = await checkVersion(event, context) + break; + case 'deleteFile': + res = await uniCloud.deleteFile({ + fileList: params.fileList + }) + break; + case 'setNewAppData': + params.value.create_date = Date.now() + res = await db.collection(appListDBName).doc(params.id).set(params.value) + break; + case 'getAppInfo': + let dbAppList + try { + dbAppList = db.collection(appListDBName) + } catch (e) {} + + if (!dbAppList) return fail; + + const dbAppListRecord = await dbAppList.where({ + appid: params.appid + }).get() + + if (dbAppListRecord && dbAppListRecord.data.length) + return Object.assign({}, success, dbAppListRecord.data[0]) + + //返回数据给客户端 + return fail + break; + case 'getAppVersionInfo': + let dbVersionList + try { + dbVersionList = db.collection(appVersionDBName) + } catch (e) {} + + if (!dbVersionList) return fail; + + const dbVersionListrecord = await dbVersionList.where({ + appid: params.appid, + platform: params.platform, + type: "native_app", + stable_publish: true + }) + .orderBy('create_date', 'desc') + .get(); + + if (dbVersionListrecord && dbVersionListrecord.data && dbVersionListrecord.data.length > 0) + return Object.assign({}, dbVersionListrecord.data[0], success) + + return fail + break; + } + + //返回数据给客户端 + return res +}; diff --git a/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/package.json b/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/package.json new file mode 100644 index 00000000..ad9fb7af --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/package.json @@ -0,0 +1,7 @@ +{ + "name": "uni-app-manager", + "dependencies": {}, + "extensions": { + "uni-cloud-jql": {} + } +} \ No newline at end of file diff --git a/uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version/check-version.param.json b/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/uni-app-manager.param.json similarity index 60% rename from uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version/check-version.param.json rename to uniCloud-aliyun/cloudfunctions/uni-upgrade-center/uni-app-manager.param.json index b45ad7c3..5a866fbe 100644 --- a/uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version/check-version.param.json +++ b/uniCloud-aliyun/cloudfunctions/uni-upgrade-center/uni-app-manager.param.json @@ -3,7 +3,8 @@ // 配置教程参考:https://uniapp.dcloud.net.cn/uniCloud/quickstart?id=runparam { - "appid": "__YOUR_APPID__", - "appVersion": "2.2.0", - "wgtVersion": "2.2.2" + "action": "checkVersion", + "appid": "__UNI__HelloUNIApp", + "appVersion": "1.0.0", + "wgtVersion": "1.0.0" } diff --git "a/uniCloud-aliyun/database/JQL\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206.jql" "b/uniCloud-aliyun/database/JQL\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206.jql" deleted file mode 100644 index 5292b027..00000000 --- "a/uniCloud-aliyun/database/JQL\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206.jql" +++ /dev/null @@ -1,12 +0,0 @@ -// 本文件用于,使用JQL语法操作项目关联的uniCloud空间的数据库,方便开发调试和远程数据库管理 -// 编写clientDB的js API(也支持常规js语法,比如var),可以对云数据库进行增删改查操作。不支持uniCloud-db组件写法 -// 可以全部运行,也可以选中部分代码运行。点击工具栏上的运行按钮或者按下【F5】键运行代码 -// 如果文档中存在多条JQL语句,只有最后一条语句生效 -// 如果混写了普通js,最后一条语句需是数据库操作语句 -// 此处代码运行不受DB Schema的权限控制,移植代码到实际业务中注意在schema中配好permission -// 不支持clientDB的action -// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database?id=limit -// 详细JQL语法,请参考 https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=jsquery - -// 下面示例查询uni-id-users表的所有数据 -db.collection('uni-id-users').where('user_id._id=="610a6daa506cc7000100c07f"').get(); diff --git "a/uniCloud-aliyun/database/JQL\346\237\245\350\257\242.jql" "b/uniCloud-aliyun/database/JQL\346\237\245\350\257\242.jql" deleted file mode 100644 index 680e8987..00000000 --- "a/uniCloud-aliyun/database/JQL\346\237\245\350\257\242.jql" +++ /dev/null @@ -1,9 +0,0 @@ -// 本查询文件用于,使用JQL语法查询项目关联的uniCloud空间的数据库,方便开发调试 -// 选中查询代码,点击工具栏上的运行按钮或者【F5】运行查询语句 -// 如果没有选中代码,直接运行,则会执行整个文档的JQL语句。但如果文档中存在多条JQL语句,只有最后一条语句生效 -// 本文档支持简单js,但不支持clientDB的action -// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database?id=limit -// 详细JQL语法,请参考 https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=jsquery - -// 下面示例查询uni-id-users表的所有数据 -db.collection('uni-id-users').get(); diff --git a/uniCloud-aliyun/database/db_init.json b/uniCloud-aliyun/database/db_init.json index 20f4b1f4..8a073406 100644 --- a/uniCloud-aliyun/database/db_init.json +++ b/uniCloud-aliyun/database/db_init.json @@ -1,70 +1,59 @@ -// 在本文件中可配置云数据库初始化,数据格式见:https://uniapp.dcloud.io/uniCloud/cf-database?id=db_init +// 在本文件中可配置云数据库初始化,数据格式见:https://uniapp.dcloud.io/uniCloud/hellodb?id=db-init // 编写完毕后对本文件点右键,可按配置规则创建表和添加数据 { - "opendb-verify-codes": { - "data": [] - }, - "uni-id-roles": { - "data": [] - }, - "uni-id-permissions": { - "data": [] - }, - "uni-id-log": { - "data": [] + "uni-id-users": { + "data": [{ + "_id": "_uni_starter_test_user_id", + "username": "uni-starter预置用户名", + "nickname": "测试用户昵称", + "avatar": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png", + "mobile": "18888888888", + "mobile_confirmed": 1 + }] }, - "opendb-admin-menus": { + "opendb-banner": { "data": [{ - "menu_id": "system_management", - "name": "系统管理", - "icon": "uni-icons-gear", - "url": "", - "sort": 1000, - "parent_id": "", - "permission": [], - "enable": true, - "create_date": 1602662469396 - }, { - "menu_id": "system_user", - "name": "用户管理", - "icon": "uni-icons-person", - "url": "/pages/system/user/list", - "sort": 1010, - "parent_id": "system_management", - "permission": [], - "enable": true, - "create_date": 1602662469398 - }, { - "menu_id": "system_role", - "name": "角色管理", - "icon": "uni-icons-personadd", - "url": "/pages/system/role/list", - "sort": 1020, - "parent_id": "system_management", - "permission": [], - "enable": true, - "create_date": 1602662469397 - }, { - "menu_id": "system_permission", - "name": "权限管理", - "icon": "uni-icons-locked", - "url": "/pages/system/permission/list", - "sort": 1030, - "parent_id": "system_management", - "permission": [], - "enable": true, - "create_date": 1602662469396 - }, { - "menu_id": "system_menu", - "name": "菜单管理", - "icon": "uni-icons-settings", - "url": "/pages/system/menu/list", - "sort": 1040, - "parent_id": "system_management", - "permission": [], - "enable": true, - "create_date": 1602662469396 - }] + "status": true, + "bannerfile": { + "name": "094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg", + "extname": "jpg", + "fileType": "image", + "url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/b88a7e17-35f0-4d0d-bc32-93f8909baf03.jpg", + "size": 70880, + "image": { + "width": 500, + "height": 333, + "location": "blob:http://localhost:8081/a3bfaab4-7ee6-44d5-a171-dc8225d83598" + }, + "path": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/b88a7e17-35f0-4d0d-bc32-93f8909baf03.jpg" + }, + "open_url": "https://www.dcloud.io/", + "title": "测试", + "sort": 1, + "category_id": "", + "description": "" + }, + { + "status": true, + "bannerfile": { + "name": "094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg", + "extname": "jpg", + "fileType": "image", + "url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg", + "size": 70880, + "image": { + "width": 500, + "height": 333, + "location": "blob:http://localhost:8081/1a6f718a-4012-476a-9172-590fef2cc518" + }, + "path": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg" + }, + "open_url": "https://www.dcloud.io/", + "title": "", + "category_id": "", + "description": "" + } + ] }, "opendb-news-articles": { "data": [{ @@ -73,7 +62,7 @@ "content": "

随着微信、阿里、百度、头条、QQ纷纷推出小程序,开发者的开发维护成本持续上升,负担过重。这点已经成为共识,现在连小程序平台厂商也充分意识到了。

\n

阿里小程序团队,为了减轻开发者的负担,在官方的小程序开发者工具中整合了多端框架。

\n

经过阿里团队仔细评估,uni-app 在产品完成度、跨平台支持度、开发者社区、可持续发展等多方面优势明显,最终选定 uni-app内置于阿里小程序开发工具中,为开发者提供多端开发解决方案。

\n

经过之前1个月的公测,10月10日,阿里小程序正式发布0.70版开发者工具,通过 uni-app 实现多端开发,成为本次版本更新的亮点功能!

\n

如下图,在阿里小程序工具左侧主导航选择 uni-app,创建项目,即可开发。

\n
\n


阿里小程序开发工具更新说明详见:https://docs.alipay.com/mini/ide/0.70-stable

\n

 

\n

集成uni-app,这对于阿里团队而言,并不是一个容易做出的决定。毕竟 uni-app 是一个三方产品,要经过复杂的评审流程。

\n

这一方面突显出阿里团队以开发者需求为本的优秀价值观,另一方面也证明 uni-app的产品确实过硬。

\n

很多开发者都有多端需求,但又没有足够精力去了解、评估 uni-app,而处于观望态度。现在大家可以更放心的使用 uni-app 了,它没有让阿里失望,也不会让你失望。

\n

自从uni-app推出以来,DCloud也取得了高速的发展,目前拥有370万开发者,框架运行在4.6亿手机用户设备上,月活达到1.35亿(仅包括部分接入DCloud统计平台的数据)。并且数据仍在高速增长中,在市场占有率上处于遥遥领先的位置。

\n

本次阿里小程序工具集成 uni-app,会让 uni-app 继续快速爆发,取得更大的成功。

\n

后续DCloud还将深化与阿里的合作,在serverless等领域给开发者提供更多优质服务。

\n

使用多端框架开发各端应用,是多赢的模式。开发者减轻了负担,获得了更多新流量。而小程序平台厂商,也能保证自己平台上的各种应用可以被及时的更新。

\n

DCloud欢迎更多小程序平台厂商,与我们一起合作,为开发者、平台、用户的多赢而努力。

\n

进一步了解uni-app,详见:https://uniapp.dcloud.io

\n

欢迎扫码关注DCloud公众号,转发消息到朋友圈。

", "avatar": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-aliyun-gacrhzeynhss7c6d04/249516a0-3941-11eb-899d-733ae62bed2f.jpg", "type": 0, - "user_id": "123", + "user_id": "_uni_starter_test_user_id", "comment_count": 0, "like_count": 0, "comment_status": 0, @@ -99,59 +88,536 @@ "stable_publish": false, "type": "native_app", "create_date": 1616771628150 + }], + "index": [{ + "IndexName": "appid", + "MgoKeySchema": { + "MgoIndexKeys": [{ + "Name": "appid", + "Direction": "1" + }, { + "Name": "uni_platform", + "Direction": "1" + }, { + "Name": "create_env", + "Direction": "1" + }], + "MgoIsUnique": false + } + }, { + "IndexName": "查找上线发行应用", + "MgoKeySchema": { + "MgoIndexKeys": [{ + "Name": "appid", + "Direction": "1" + }, { + "Name": "platform", + "Direction": "1" + }, { + "Name": "stable_publish", + "Direction": "1" + }, { + "Name": "uni_platform", + "Direction": "1" + }, { + "Name": "create_env", + "Direction": "1" + }], + "MgoIsUnique": false + } }] }, - "uni-id-users": { - "data": [{ - "_id": "123", - "username": "预置用户", - "nickname": "测试", - "avatar": "https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/d84c6de0-6080-11eb-bdc1-8bd33eb6adaa.png", - "mobile": "18888888888", - "mobile_confirmed": 1 + "opendb-verify-codes": { + "data": [] + }, + "opendb-app-list": { + "data": [], + "index": [{ + "IndexName": "appid", + "MgoKeySchema": { + "MgoIndexKeys": [{ + "Name": "appid", + "Direction": "1" + }], + "MgoIsUnique": true + } + }, { + "IndexName": "name", + "MgoKeySchema": { + "MgoIndexKeys": [{ + "Name": "name", + "Direction": "1" + }], + "MgoIsUnique": false + } }] }, - "opendb-banner": { + "uni-id-roles": { + "data": [] + }, + "uni-id-permissions": { + "data": [] + }, + "uni-id-log": { + "data": [] + }, + "opendb-admin-menus": { "data": [{ - "status": true, - "bannerfile": { - "name": "094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg", - "extname": "jpg", - "fileType": "image", - "url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/b88a7e17-35f0-4d0d-bc32-93f8909baf03.jpg", - "size": 70880, - "image": { - "width": 500, - "height": 333, - "location": "blob:http://localhost:8081/a3bfaab4-7ee6-44d5-a171-dc8225d83598" - }, - "path": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/b88a7e17-35f0-4d0d-bc32-93f8909baf03.jpg" + "menu_id": "index", + "name": "首页", + "icon": "uni-icons-home", + "url": "/", + "sort": 100, + "parent_id": "", + "permission": [], + "enable": true, + "create_date": 1602662469396 + }, { + "menu_id": "system_management", + "name": "系统管理", + "icon": "admin-icons-fl-xitong", + "url": "", + "sort": 1000, + "parent_id": "", + "permission": [], + "enable": true, + "create_date": 1602662469396 + }, { + "menu_id": "system_user", + "name": "用户管理", + "icon": "admin-icons-manager-user", + "url": "/pages/system/user/list", + "sort": 1010, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469398 + }, { + "menu_id": "system_role", + "name": "角色管理", + "icon": "admin-icons-manager-role", + "url": "/pages/system/role/list", + "sort": 1020, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469397 + }, { + "menu_id": "system_permission", + "name": "权限管理", + "icon": "admin-icons-manager-permission", + "url": "/pages/system/permission/list", + "sort": 1030, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469396 + }, { + "menu_id": "system_menu", + "name": "菜单管理", + "icon": "admin-icons-manager-menu", + "url": "/pages/system/menu/list", + "sort": 1040, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469396 + }, { + "menu_id": "system_app", + "name": "应用管理", + "icon": "admin-icons-manager-app", + "url": "/pages/system/app/list", + "sort": 1035, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469399 + }, { + "menu_id": "system_update", + "name": "App升级中心", + "icon": "uni-icons-cloud-upload", + "url": "/uni_modules/uni-upgrade-center/pages/version/list", + "sort": 1036, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1656491532434 + }, { + "menu_id": "system_tag", + "name": "标签管理", + "icon": "admin-icons-manager-tag", + "url": "/pages/system/tag/list", + "sort": 1037, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662479389 + }, { + "permission": [], + "enable": true, + "menu_id": "safety_statistics", + "name": "安全审计", + "icon": "admin-icons-safety", + "url": "", + "sort": 3100, + "parent_id": "", + "create_date": 1638356430871 + }, { + "permission": [], + "enable": true, + "menu_id": "safety_statistics_user_log", + "name": "用户日志", + "icon": "", + "url": "/pages/system/safety/list", + "sort": 3101, + "parent_id": "safety_statistics", + "create_date": 1638356430871 + }, { + "permission": [], + "enable": true, + "menu_id": "uni-stat", + "name": "uni 统计", + "icon": "admin-icons-tongji", + "url": "", + "sort": 2100, + "parent_id": "", + "create_date": 1638356430871 + }, { + "parent_id": "uni-stat", + "permission": [], + "enable": true, + "menu_id": "uni-stat-device", + "name": "设备统计", + "icon": "admin-icons-shebeitongji", + "url": "", + "sort": 2120, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-device", + "permission": [], + "enable": true, + "menu_id": "uni-stat-device-overview", + "name": "概况", + "icon": "", + "url": "/pages/uni-stat/device/overview/overview", + "sort": 2121, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-device", + "permission": [], + "enable": true, + "menu_id": "uni-stat-device-activity", + "name": "活跃度", + "icon": "", + "url": "/pages/uni-stat/device/activity/activity", + "sort": 2122, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-device", + "permission": [], + "enable": true, + "menu_id": "uni-stat-device-trend", + "name": "趋势分析", + "icon": "", + "url": "/pages/uni-stat/device/trend/trend", + "sort": 2123, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-device", + "permission": [], + "enable": true, + "menu_id": "uni-stat-device-retention", + "name": "留存", + "icon": "", + "url": "/pages/uni-stat/device/retention/retention", + "sort": 2124, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-device", + "permission": [], + "enable": true, + "menu_id": "uni-stat-device-comparison", + "name": "平台对比", + "icon": "", + "url": "/pages/uni-stat/device/comparison/comparison", + "sort": 2125, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-device", + "permission": [], + "enable": true, + "menu_id": "uni-stat-device-stickiness", + "name": "粘性", + "icon": "", + "url": "/pages/uni-stat/device/stickiness/stickiness", + "sort": 2126, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat", + "permission": [], + "enable": true, + "menu_id": "uni-stat-user", + "name": "注册用户统计", + "icon": "admin-icons-yonghutongji", + "url": "", + "sort": 2122, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-user", + "permission": [], + "enable": true, + "menu_id": "uni-stat-user-overview", + "name": "概况", + "icon": "", + "url": "/pages/uni-stat/user/overview/overview", + "sort": 2121, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-user", + "permission": [], + "enable": true, + "menu_id": "uni-stat-user-activity", + "name": "活跃度", + "icon": "", + "url": "/pages/uni-stat/user/activity/activity", + "sort": 2122, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-user", + "permission": [], + "enable": true, + "icon": "", + "menu_id": "uni-stat-user-trend", + "name": "趋势分析", + "url": "/pages/uni-stat/user/trend/trend", + "sort": 2123, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-user", + "permission": [], + "enable": true, + "menu_id": "uni-stat-user-retention", + "name": "留存", + "icon": "", + "url": "/pages/uni-stat/user/retention/retention", + "sort": 2124, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-user", + "permission": [], + "enable": true, + "menu_id": "uni-stat-user-comparison", + "name": "平台对比", + "icon": "", + "url": "/pages/uni-stat/user/comparison/comparison", + "sort": 2125, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-user", + "permission": [], + "enable": true, + "menu_id": "uni-stat-user-stickiness", + "name": "粘性", + "icon": "", + "url": "/pages/uni-stat/user/stickiness/stickiness", + "sort": 2126, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat", + "permission": [], + "enable": true, + "menu_id": "uni-stat-page-analysis", + "name": "页面统计", + "icon": "admin-icons-page-ent", + "url": "", + "sort": 2123, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-page-analysis", + "permission": [], + "enable": true, + "menu_id": "uni-stat-page-res", + "name": "受访页", + "icon": "", + "url": "/pages/uni-stat/page-res/page-res", + "sort": 2131, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-page-analysis", + "permission": [], + "enable": true, + "menu_id": "uni-stat-page-ent", + "name": "入口页", + "icon": "", + "url": "/pages/uni-stat/page-ent/page-ent", + "sort": 2132, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat", + "permission": [], + "enable": true, + "menu_id": "uni-stat-senceChannel", + "name": "渠道/场景值分析", + "icon": "admin-icons-qudaofenxi", + "url": "", + "sort": 2150, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-senceChannel", + "permission": [], + "enable": true, + "menu_id": "uni-stat-senceChannel-scene", + "name": "场景值(小程序)", + "icon": "", + "url": "/pages/uni-stat/scene/scene", + "sort": 2151, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-senceChannel", + "permission": [], + "enable": true, + "menu_id": "uni-stat-senceChannel-channel", + "name": "渠道(app)", + "icon": "", + "url": "/pages/uni-stat/channel/channel", + "sort": 2152, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat", + "permission": [], + "enable": true, + "menu_id": "uni-stat-event-event", + "name": "自定义事件", + "icon": "admin-icons-shijianfenxi", + "url": "/pages/uni-stat/event/event", + "sort": 2160, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat", + "permission": [], + "enable": true, + "menu_id": "uni-stat-error", + "name": "错误统计", + "icon": "admin-icons-cuowutongji", + "url": "", + "sort": 2170, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-error", + "permission": [], + "enable": true, + "menu_id": "uni-stat-error-js", + "name": "js报错", + "icon": "", + "url": "/pages/uni-stat/error/js/js", + "sort": 2171, + "create_date": 1638356902516 + }, { + "parent_id": "uni-stat-error", + "permission": [], + "enable": true, + "menu_id": "uni-stat-error-app", + "name": "app崩溃", + "icon": "", + "url": "/pages/uni-stat/error/app/app", + "sort": 2172, + "create_date": 1638356902516 }, - "open_url": "https://www.dcloud.io/", - "title": "测试", - "sort": 1, - "category_id": "", - "description": "" - }, - { - "status": true, - "bannerfile": { - "name": "094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg", - "extname": "jpg", - "fileType": "image", - "url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg", - "size": 70880, - "image": { - "width": 500, - "height": 333, - "location": "blob:http://localhost:8081/1a6f718a-4012-476a-9172-590fef2cc518" - }, - "path": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg" - }, - "open_url": "https://www.dcloud.io/", - "title": "", - "category_id": "", - "description": "" - }] - } -} \ No newline at end of file + { + "menu_id": "uni-stat-pay", + "name": "支付统计", + "icon": "uni-icons-circle", + "url": "", + "sort": 2122, + "parent_id": "uni-stat", + "permission": [], + "enable": true, + "create_date": 1667386977981 + }, { + "menu_id": "uni-stat-pay-overview", + "name": "概况", + "icon": "", + "url": "/pages/uni-stat/pay-order/overview/overview", + "sort": 21221, + "parent_id": "uni-stat-pay", + "permission": [], + "enable": true, + "create_date": 1667387038602 + }, + { + "menu_id": "uni-stat-pay-funnel", + "name": "转换漏斗分析", + "icon": "", + "url": "/pages/uni-stat/pay-order/funnel/funnel", + "sort": 21222, + "parent_id": "uni-stat-pay", + "permission": [], + "enable": true, + "create_date": 1668430092890 + }, + { + "menu_id": "uni-stat-pay-ranking", + "name": "价值用户排行", + "icon": "", + "url": "/pages/uni-stat/pay-order/ranking/ranking", + "sort": 21223, + "parent_id": "uni-stat-pay", + "permission": [], + "enable": true, + "create_date": 1668430128302 + }, + { + "menu_id": "uni-stat-pay-order-list", + "name": "订单明细", + "icon": "", + "url": "/pages/uni-stat/pay-order/list/list", + "sort": 21224, + "parent_id": "uni-stat-pay", + "permission": [], + "enable": true, + "create_date": 1667387078947 + } + ] + }, + "uni-id-tag": {}, + "uni-id-device": {}, + "opendb-device": {}, + "opendb-department": {}, + "opendb-sms-task": {}, + "opendb-sms-log": {}, + "opendb-sms-template": {}, + "opendb-open-data": {}, + "uni-stat-app-versions": {}, + "uni-stat-active-devices": {}, + "uni-stat-active-users": {}, + "uni-stat-app-channels": {}, + "uni-stat-app-crash-logs": {}, + "uni-stat-app-platforms": {}, + "uni-stat-error-logs": {}, + "uni-stat-error-result": {}, + "uni-stat-event-logs": {}, + "uni-stat-event-result": {}, + "uni-stat-events": {}, + "uni-stat-loyalty-result": {}, + "uni-stat-mp-scenes": {}, + "uni-stat-page-logs": {}, + "uni-stat-page-result": {}, + "uni-stat-pages": {}, + "uni-stat-result": {}, + "uni-stat-run-errors": {}, + "uni-stat-session-logs": {}, + "uni-stat-share-logs": {}, + "uni-stat-user-session-logs": {}, + "uni-pay-orders": {}, + "uni-stat-pay-result": {}, + "opendb-tempdata": {}, + "opendb-feedback": {}, + "opendb-news-categories": {}, + "opendb-news-comments": {}, + "opendb-news-favorite": {}, + "opendb-search-hot": {}, + "opendb-search-log": {}, + "opendb-sign-in": {}, + "read-news-log": {}, + "uni-id-scores": {} +} diff --git a/uniCloud-aliyun/database/default.jql b/uniCloud-aliyun/database/default.jql deleted file mode 100644 index 35d21de2..00000000 --- a/uniCloud-aliyun/database/default.jql +++ /dev/null @@ -1,12 +0,0 @@ -// 本文件用于,使用JQL语法操作项目关联的uniCloud空间的数据库,方便开发调试和远程数据库管理 -// 编写clientDB的js API(也支持常规js语法,比如var),可以对云数据库进行增删改查操作。不支持uniCloud-db组件写法 -// 可以全部运行,也可以选中部分代码运行。点击工具栏上的运行按钮或者按下【F5】键运行代码 -// 如果文档中存在多条JQL语句,只有最后一条语句生效 -// 如果混写了普通js,最后一条语句需是数据库操作语句 -// 此处代码运行不受DB Schema的权限控制,移植代码到实际业务中注意在schema中配好permission -// 不支持clientDB的action -// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database.html#limit -// 详细JQL语法,请参考:https://uniapp.dcloud.net.cn/uniCloud/jql.html - -// 下面示例查询uni-id-users表的所有数据 -db.collection('uni-id-users').get(); diff --git a/uniCloud-aliyun/database/opendb-admin-menus.schema.json b/uniCloud-aliyun/database/opendb-admin-menus.schema.json index cfeef1b4..41045ed5 100644 --- a/uniCloud-aliyun/database/opendb-admin-menus.schema.json +++ b/uniCloud-aliyun/database/opendb-admin-menus.schema.json @@ -2,7 +2,10 @@ "bsonType": "object", "required": ["name", "menu_id"], "permission": { - "read": true + "read": true, + "create": "'CREATE_OPENDB_ADMIN_MENUS' in auth.permission", + "update": "'UPDATE_OPENDB_ADMIN_MENUS' in auth.permission", + "delete": "'DELETE_OPENDB_ADMIN_MENUS' in auth.permission" }, "properties": { "_id": { diff --git a/uniCloud-aliyun/database/opendb-app-versions.schema.json b/uniCloud-aliyun/database/opendb-app-versions.schema.json index 6d1fa8b7..853798eb 100644 --- a/uniCloud-aliyun/database/opendb-app-versions.schema.json +++ b/uniCloud-aliyun/database/opendb-app-versions.schema.json @@ -1,11 +1,18 @@ +// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema { "bsonType": "object", - "required": ["appid", "platform", "version", "url", "contents", "type"], + "required": [ + "appid", + "uni_platform", + "version", + "type", + "create_env" + ], "permission": { - "read": false, - "create": false, - "update": false, - "delete": false + "read": "'READ_OPENDB_APP_VERSIONS' in auth.permission", + "create": "'CREATE_OPENDB_APP_VERSIONS' in auth.permission", + "update": "'UPDATE_OPENDB_APP_VERSIONS' in auth.permission", + "delete": "'DELETE_OPENDB_APP_VERSIONS' in auth.permission" }, "properties": { "_id": { @@ -56,25 +63,31 @@ }, "platform": { "bsonType": "array", - "enum": [{ - "value": "Android", - "text": "安卓" - }, { - "value": "iOS", - "text": "苹果" - }], + "enum": [ + { + "value": "Android", + "text": "安卓" + }, + { + "value": "iOS", + "text": "苹果" + } + ], "description": "更新平台,Android || iOS || [Android, iOS]", "label": "平台" }, "type": { "bsonType": "string", - "enum": [{ - "value": "native_app", - "text": "原生App安装包" - }, { - "value": "wgt", - "text": "Wgt资源包" - }], + "enum": [ + { + "value": "native_app", + "text": "原生App安装包" + }, + { + "value": "wgt", + "text": "Wgt资源包" + } + ], "description": "安装包类型,native_app || wgt", "label": "安装包类型" }, @@ -90,8 +103,8 @@ }, "url": { "bsonType": "string", - "description": "可下载安装包地址", - "label": "包地址" + "description": "可下载或跳转的链接", + "label": "链接" }, "stable_publish": { "bsonType": "bool", @@ -119,6 +132,47 @@ "componentForEdit": { "name": "uni-dateformat" } + }, + "uni_platform": { + "bsonType": "string", + "description": "uni平台信息,如:mp-weixin/web/ios/android", + "label": "uni 平台" + }, + "create_env": { + "bsonType": "string", + "description": "创建来源,uni-stat:uni统计自动创建,upgrade-center:升级中心管理员创建" + }, + "store_list": { + "bsonType": "array", + "description": "发布的应用市场", + "label": "应用市场", + "properties": { + "id": { + "bsonType": "string", + "description": "应用id,自动生成", + "label": "id" + }, + "name": { + "bsonType": "string", + "description": "应用名称", + "label": "应用名称" + }, + "scheme": { + "bsonType": "string", + "description": "应用 scheme", + "label": "应用 scheme" + }, + "enable": { + "bsonType": "bool", + "description": "是否启用" + }, + "priority": { + "bsonType": "int", + "description": "按照从大到小排序", + "label": "优先级" + } + } } - } -} + }, + "version": "0.0.1" +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/opendb-department.schema.json b/uniCloud-aliyun/database/opendb-department.schema.json deleted file mode 100644 index 3996e9ab..00000000 --- a/uniCloud-aliyun/database/opendb-department.schema.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "bsonType": "object", - "required": ["name"], - "permission": { - "read": true, - "create": false, - "update": false, - "delete": false - }, - "properties": { - "_id": { - "description": "ID,系统自动生成" - }, - "parent_id": { - "bsonType": "string", - "description": "父级部门ID", - "parentKey": "_id" - }, - "name": { - "bsonType": "string", - "description": "部门名称", - "title": "部门名称", - "trim": "both" - }, - "level": { - "bsonType": "int", - "description": "部门层级,为提升检索效率而作的冗余设计" - }, - "sort": { - "bsonType": "int", - "description": "部门在当前层级下的顺序,由小到大", - "title": "显示顺序" - }, - "manager_uid": { - "bsonType": "string", - "description": "部门主管的userid, 参考`uni-id-users` 表", - "foreignKey": "uni-id-users._id" - }, - "create_date": { - "bsonType": "timestamp", - "description": "部门创建时间", - "forceDefaultValue": { - "$env": "now" - } - } - } -} diff --git a/uniCloud-aliyun/database/opendb-mall-goods.schema.json b/uniCloud-aliyun/database/opendb-mall-goods.schema.json deleted file mode 100644 index 3b6e3092..00000000 --- a/uniCloud-aliyun/database/opendb-mall-goods.schema.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "bsonType": "object", - "permission": { - "create": false, - "delete": false, - "read": "doc.is_on_sale == true", - "update": false - }, - "properties": { - "_id": { - "description": "存储文档 ID(商品 ID),系统自动生成" - }, - "add_date": { - "bsonType": "timestamp", - "defaultValue": { - "$env": "now" - }, - "description": "上架时间" - }, - "category_id": { - "bsonType": "string", - "description": "分类 id,参考`opendb-mall-categories`表", - "foreignKey": "opendb-mall-categories._id" - }, - "comment_count": { - "bsonType": "int", - "description": "累计评论数" - }, - "goods_banner_imgs": { - "bsonType": "array", - "description": "商品详情页的banner图地址" - }, - "goods_desc": { - "bsonType": "string", - "description": "商品详细描述", - "title": "详细描述", - "trim": "both" - }, - "goods_sn": { - "bsonType": "string", - "description": "商品的唯一货号", - "title": "货号", - "trim": "both" - }, - "goods_thumb": { - "bsonType": "string", - "description": "商品缩略图,用于在列表或搜索结果中预览显示", - "pattern": "^(http:\/\/|https:\/\/|\/|.\/|@\/)\\S", - "title": "缩略图地址", - "trim": "both" - }, - "is_alone_sale": { - "bsonType": "bool", - "description": "是否能单独销售;如果不能单独销售,则只能作为某商品的配件或者赠品销售" - }, - "is_best": { - "bsonType": "bool", - "description": "是否精品" - }, - "is_hot": { - "bsonType": "bool", - "description": "是否热销" - }, - "is_new": { - "bsonType": "bool", - "description": "是否新品", - "title": "是否新品" - }, - "is_on_sale": { - "bsonType": "bool", - "description": "是否上架销售", - "title": "是否上架" - }, - "is_real": { - "bsonType": "bool", - "description": "是否实物", - "title": "是否为实物" - }, - "keywords": { - "bsonType": "string", - "description": "商品关键字,为搜索引擎收录使用", - "title": "关键字", - "trim": "both" - }, - "last_modify_date": { - "bsonType": "timestamp", - "defaultValue": { - "$env": "now" - }, - "description": "最后修改时间" - }, - "month_sell_count": { - "bsonType": "int", - "description": "月销量" - }, - "name": { - "bsonType": "string", - "description": "商品名称", - "title": "名称", - "trim": "both" - }, - "remain_count": { - "bsonType": "int", - "description": "库存数量", - "title": "库存数量" - }, - "seller_note": { - "bsonType": "string", - "description": "商家备注,仅商家可见", - "permission": { - "read": false - }, - "trim": "both" - }, - "total_sell_count": { - "bsonType": "int", - "description": "总销量" - } - }, - "required": ["goods_sn", "name", "remain_count", "month_sell_count", "total_sell_count", "comment_count", "is_real", - "is_on_sale", "is_alone_sale", "is_best", "is_new", "is_hot" - ] -} diff --git a/uniCloud-aliyun/database/opendb-sms-log.schema.json b/uniCloud-aliyun/database/opendb-sms-log.schema.json new file mode 100644 index 00000000..05a49324 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-sms-log.schema.json @@ -0,0 +1,63 @@ +{ + "bsonType": "object", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "task_id": { + "bsonType": "string", + "description": "任务ID", + "foreignKey": "batch-sms-task._id" + }, + "uid": { + "bsonType": "string", + "description": "用户ID", + "foreignKey": "uni-id-users._id" + }, + "mobile": { + "bsonType": "int", + "description": "手机号" + }, + "var_data": { + "bsonType": "object", + "description": "变量数据" + }, + "status": { + "bsonType": "int", + "description": "发送状态", + "defaultValue": 0, + "enum": [{ + "text": "未发送", + "value": 0 + }, { + "text": "已发送", + "value": 1 + }, { + "text": "发送失败", + "value": 2 + }] + }, + "reason": { + "bsonType": "string", + "description": "发送失败原因" + }, + "send_date": { + "bsonType": "timestamp", + "description": "发送时间" + }, + "ccreate_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/opendb-sms-task.schema.json b/uniCloud-aliyun/database/opendb-sms-task.schema.json new file mode 100644 index 00000000..6e19a7e5 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-sms-task.schema.json @@ -0,0 +1,76 @@ +{ + "bsonType": "object", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "name": { + "bsonType": "string", + "description": "任务名称", + "trim": "both" + }, + "app_id": { + "bsonType": "string", + "description": "App ID", + "trim": "both" + }, + "template_id": { + "bsonType": "string", + "description": "短信模板ID", + "trim": "both" + }, + "template_content": { + "bsonType": "string", + "description": "短信模板内容", + "trim": "both" + }, + "vars": { + "bsonType": "array", + "description": "短信变量" + }, + "to": { + "bsonType": "object", + "description": "短信接收者信息", + "properties": { + "all": { + "bsonType": "bool", + "description": "全部用户发送" + }, + "type": { + "bsonType": "string", + "description": "to.all=true时用来区分发送类型, 可选值 user | userTags" + }, + "receiver": { + "bsonType": "array", + "description": "用户ID's \/ 用户标签ID's" + } + } + }, + "send_qty": { + "bsonType": "int", + "description": "发送总数" + }, + "success_qty": { + "bsonType": "int", + "description": "成功总数" + }, + "fail_qty": { + "bsonType": "int", + "description": "失败总数" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/opendb-sms-template.schema.json b/uniCloud-aliyun/database/opendb-sms-template.schema.json new file mode 100644 index 00000000..c6e21dbe --- /dev/null +++ b/uniCloud-aliyun/database/opendb-sms-template.schema.json @@ -0,0 +1,32 @@ +// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema +{ + "bsonType": "object", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "模板ID" + }, + "name": { + "bsonType": "string", + "description": "模板名称" + }, + "content": { + "bsonType": "string", + "description": "模板内容" + }, + "type": { + "bsonType": "int", + "description": "模板类型" + }, + "sign": { + "bsonType": "string", + "description": "模板签名" + } + } +} diff --git a/uniCloud-aliyun/database/opendb-tempdata.schema.json b/uniCloud-aliyun/database/opendb-tempdata.schema.json new file mode 100644 index 00000000..6a816de4 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-tempdata.schema.json @@ -0,0 +1,22 @@ +{ + "bsonType": "object", + "required": ["value", "expired"], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "value": { + "description": "值" + }, + "expired": { + "description": "过期时间", + "bsonType": "timestamp" + } + } +} diff --git a/uniCloud-aliyun/database/uni-id-tag.schema.json b/uniCloud-aliyun/database/uni-id-tag.schema.json index 1d1c8153..18ac09da 100644 --- a/uniCloud-aliyun/database/uni-id-tag.schema.json +++ b/uniCloud-aliyun/database/uni-id-tag.schema.json @@ -2,10 +2,10 @@ "bsonType": "object", "required": ["tagid", "name"], "permission": { - "read": false, - "create": false, - "update": false, - "delete": false + "read": "'READ_UNI_ID_TAG' in auth.permission", + "create": "'CREATE_UNI_ID_TAG' in auth.permission", + "update": "'UPDATE_UNI_ID_TAG' in auth.permission", + "delete": "'DELETE_UNI_ID_TAG' in auth.permission" }, "properties": { "_id": { diff --git a/uniCloud-aliyun/database/uni-pay-orders.schema.json b/uniCloud-aliyun/database/uni-pay-orders.schema.json new file mode 100644 index 00000000..6f0942eb --- /dev/null +++ b/uniCloud-aliyun/database/uni-pay-orders.schema.json @@ -0,0 +1,241 @@ +{ + "bsonType": "object", + "required": [], + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "provider": { + "title": "支付供应商", + "bsonType": "string", + "enum": [{ + "text": "微信支付", + "value": "wxpay" + }, + { + "text": "支付宝", + "value": "alipay" + }, + { + "text": "苹果应用内支付", + "value": "appleiap" + } + ], + "description": "支付供应商 如 wxpay alipay 参考 https://uniapp.dcloud.net.cn/api/plugins/provider.html#" + }, + "provider_pay_type": { + "title": "支付方式", + "bsonType": "string", + "description": "支付供应商的支付类型(插件内部标记支付类型的标识,不需要用户传)", + "trim": "both" + }, + "uni_platform": { + "title": "应用平台", + "bsonType": "string", + "description": "uni客户端平台,如:web、mp-weixin、mp-alipay、app等", + "trim": "both" + }, + "status": { + "title": "订单状态", + "bsonType": "int", + "enum": [{ + "text": "已关闭", + "value": -1 + }, + { + "text": "未支付", + "value": 0 + }, + { + "text": "已支付", + "value": 1 + }, + { + "text": "已部分退款", + "value": 2 + }, + { + "text": "已全额退款", + "value": 3 + } + ], + "description": "订单状态 -1 已关闭 0:未支付 1:已支付 2:已部分退款 3:已全额退款", + "defaultValue": 0 + }, + "type": { + "title": "订单类型", + "bsonType": "string", + "description": "订单类型 goods:订单付款 recharge:余额充值付款 vip:vip充值付款 等等,可自定义", + "trim": "both" + }, + "order_no": { + "title": "业务系统订单号", + "bsonType": "string", + "minLength": 20, + "maxLength": 28, + "description": "业务系统订单号,控制在20-28位(不可以是24位,24位在阿里云空间可能会有问题,可重复,代表1个业务订单会有多次付款的情况)", + "trim": "both" + }, + "out_trade_no": { + "title": "支付插件订单号", + "bsonType": "string", + "description": "支付插件订单号(需控制唯一,不传则由插件自动生成)", + "trim": "both" + }, + "transaction_id": { + "title": "交易单号", + "bsonType": "string", + "description": "交易单号(支付平台订单号,由支付平台控制唯一)", + "trim": "both" + }, + "user_id": { + "title": "用户ID", + "bsonType": "string", + "description": "用户id,参考uni-id-users表", + "foreignKey": "uni-id-users._id" + }, + "nickname": { + "title": "用户昵称", + "bsonType": "string", + "description": "用户昵称冗余", + "trim": "both" + }, + "device_id": { + "bsonType": "string", + "description": "客户端设备ID" + }, + "client_ip": { + "title": "客户端IP", + "bsonType": "string", + "description": "创建支付的客户端ip", + "trim": "both" + }, + "openid": { + "title": "openid", + "bsonType": "string", + "description": "发起支付的用户openid", + "trim": "both" + }, + "description": { + "title": "支付描述", + "bsonType": "string", + "description": "支付描述,如:uniCloud个人版包月套餐", + "trim": "both" + }, + "err_msg": { + "title": "支付失败原因", + "bsonType": "string", + "description": "支付失败原因", + "trim": "both" + }, + "total_fee": { + "title": "订单总金额", + "bsonType": "int", + "description": "订单总金额,单位为分,100等于1元" + }, + "refund_fee": { + "title": "订单总退款金额", + "bsonType": "int", + "description": "订单总退款金额,单位为分,100等于1元" + }, + "refund_count": { + "title": "当前退款笔数", + "bsonType": "int", + "description": "当前退款笔数 (退款单号为 out_trade_no-refund_count)" + }, + "refund_list": { + "title": "退款详情", + "bsonType": "array", + "description": "退款详情" + }, + "provider_appid": { + "title": "开放平台appid", + "bsonType": "string", + "description": "公众号appid,小程序appid,app开放平台appid 等", + "trim": "both" + }, + "appid": { + "title": "DCloud AppId", + "bsonType": "string", + "description": "dcloud_appid", + "trim": "both" + }, + "user_order_success": { + "title": "回调状态", + "bsonType": "bool", + "description": "用户异步通知逻辑是否全部执行完成,且无异常(建议前端通过此参数是否为true来判断是否支付成功)" + }, + "custom": { + "title": "自定义数据", + "bsonType": "object", + "description": "自定义数据(用户自定义数据)" + }, + "original_data": { + "title": "异步通知原始数据", + "bsonType": "object", + "description": "异步回调通知返回的原始数据,微信v2是xml转json后的数据,微信v3和支付宝是原始json" + }, + "create_date": { + "title": "创建时间", + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "pay_date": { + "title": "支付时间", + "bsonType": "timestamp", + "description": "支付时间" + }, + "notify_date": { + "title": "异步通知时间", + "bsonType": "timestamp", + "description": "订单通知支付成功时间" + }, + "cancel_date": { + "title": "取消时间", + "bsonType": "timestamp", + "description": "订单取消时间" + }, + "stat_data": { + "title": "uni统计相关数据", + "bsonType": "object", + "description": "uni统计相关数据", + "properties": { + "platform": { + "bsonType": "string", + "description": "与uni_platform唯一区别是APP区分 android 和 ios" + }, + "app_version": { + "bsonType": "string", + "description": "客户端版本号 (字符串形式)如1.0.0" + }, + "app_version_code": { + "bsonType": "string", + "description": "客户端版本号(数字形式) 如100" + }, + "app_wgt_version": { + "bsonType": "string", + "description": "客户端热更新版本号" + }, + "os": { + "bsonType": "string", + "description": "设备的操作系统 如 android ios" + }, + "ua": { + "bsonType": "string", + "description": "客户端userAgent" + }, + "channel": { + "bsonType": "string", + "description": "客户端渠道" + }, + "scene": { + "bsonType": "string", + "description": "小程序场景值" + } + } + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-active-devices.schema.json b/uniCloud-aliyun/database/uni-stat-active-devices.schema.json new file mode 100644 index 00000000..aaa378ab --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-active-devices.schema.json @@ -0,0 +1,67 @@ +// 活跃设备表 +{ + "bsonType": "object", + "description": "给周月维度的设备基础统计和留存统计提供数据,每日跑批合并,仅添加本周/本月首次访问的设备", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台ID,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_id": { + "bsonType": "string", + "description": "渠道\/场景值ID,对应uni-stat-app-channels._id", + "foreignKey": "uni-stat-app-channels._id" + }, + "version_id": { + "bsonType": "string", + "description": "应用版本ID,对应opendb-app-versions._id", + "foreignKey": "opendb-app-versions._id" + }, + "device_id": { + "bsonType": "string", + "description": "客户端携带的设备标识" + }, + "is_new": { + "bsonType": "int", + "description": "是否为新设备", + "defaultValue": 0, + "enum": [{ + "text": "否", + "value": 0 + }, { + "text": "是", + "value": 1 + }] + }, + "dimension": { + "bsonType": "string", + "description": "时间范围 week:周,month:月", + "enum": [{ + "text": "月", + "value": "month" + }, { + "text": "周", + "value": "week" + }] + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/uni-stat-active-users.schema.json b/uniCloud-aliyun/database/uni-stat-active-users.schema.json new file mode 100644 index 00000000..090c3224 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-active-users.schema.json @@ -0,0 +1,55 @@ +// 活跃用户表 +{ + "bsonType": "object", + "description": "给周月维度的用户基础统计和留存统计提供数据,每日跑批合并,仅添加本周/本月首次访问的用户。", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台ID,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_id": { + "bsonType": "string", + "description": "渠道\/场景值ID,对应uni-stat-app-channels._id", + "foreignKey": "uni-stat-app-channels._id" + }, + "version_id": { + "bsonType": "string", + "description": "应用版本ID,对应opendb-app-versions._id", + "foreignKey": "opendb-app-versions._id" + }, + "uid": { + "bsonType": "string", + "description": "用户编号, 对应uni-id-users._id" + }, + "dimension": { + "bsonType": "string", + "description": "时间范围 week:周,month:月", + "enum": [{ + "text": "月", + "value": "month" + }, { + "text": "周", + "value": "week" + }] + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/uni-stat-app-channels.schema.json b/uniCloud-aliyun/database/uni-stat-app-channels.schema.json new file mode 100644 index 00000000..457c7ef4 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-app-channels.schema.json @@ -0,0 +1,44 @@ +// 应用渠道表 +{ + "bsonType": "object", + "description": "提供渠道和场景值数据", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_APP_CHANNELS' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "统计应用ID,对应opendb-app-list.appid", + "foreignKey": "opendb-app-list.appid" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_code": { + "bsonType": "int", + "description": "客户端上报的渠道代码" + }, + "channel_name": { + "bsonType": "string", + "description": "渠道名称,用户可编辑" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + }, + "last_modify_time": { + "bsonType": "timestamp", + "description": "最后修改时间" + } + } +} + diff --git a/uniCloud-aliyun/database/uni-stat-app-crash-logs.schema.json b/uniCloud-aliyun/database/uni-stat-app-crash-logs.schema.json new file mode 100644 index 00000000..6fd04058 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-app-crash-logs.schema.json @@ -0,0 +1,137 @@ +// 原生应用崩溃日志表 +{ + "bsonType": "object", + "description": "记录原生应用的崩溃日志", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_APP_CRASH_LOGS' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "用户端上报的应用ID" + }, + "version": { + "bsonType": "string", + "description": "用户端上报的应用版本号。manifest.json中的version->name的值" + }, + "platform": { + "bsonType": "string", + "description": "用户端上报的平台code" + }, + "channel": { + "bsonType": "string", + "description": "用户端上报的渠道code\/场景值" + }, + "sdk_version": { + "bsonType": "string", + "description": "基础库版本号" + }, + "device_id": { + "bsonType": "string", + "description": "客户端携带的设备标识" + }, + "device_net": { + "bsonType": "string", + "description": "设备网络型号wifi\/3G\/4G\/" + }, + "device_os": { + "bsonType": "string", + "description": "系统版本:iOS平台为系统版本号,如15.1;Android平台为API等级,如30" + }, + "device_os_version": { + "bsonType": "string", + "description": "系统版本名称:iOS平台与os字段一致;Android平台为版本名称,如5.1.1" + }, + "device_vendor": { + "bsonType": "string", + "description": "设备供应商 " + }, + "device_model": { + "bsonType": "string", + "description": "设备型号" + }, + "device_is_root": { + "bsonType": "int", + "description": "是否root:1表示root;0表示未root" + }, + "device_os_name": { + "bsonType": "string", + "description": "系统名称:用于区别Android和鸿蒙,仅Android支持" + }, + "device_batt_level": { + "bsonType": "int", + "description": "设备电池电量:取值范围0-100,仅Android支持" + }, + "device_batt_temp": { + "bsonType": "string", + "description": "电池温度,仅Android支持" + }, + "device_memory_use_size": { + "bsonType": "int", + "description": "系统已使用内存,单位为Byte,仅Android支持" + }, + "device_memory_total_size": { + "bsonType": "int", + "description": "系统总内存,单位为Byte,仅Android支持" + }, + "device_disk_use_size": { + "bsonType": "int", + "description": "系统磁盘已使用大小,单位为Byte,仅Android支持" + }, + "device_disk_total_size": { + "bsonType": "int", + "description": "系统磁盘总大小,单位为Byte,仅Android支持" + }, + "device_abis": { + "bsonType": "string", + "description": "设备支持的CPU架构:多个使用,分割,如arm64-v8a,armeabi-v7a,armeabi,仅Android支持" + }, + "app_count": { + "bsonType": "int", + "description": "运行的app个数:包括运行的uni小程序数目。独立App时值为1" + }, + "app_use_memory_size": { + "bsonType": "int", + "description": "APP使用的内存量,单位为Byte" + }, + "app_webview_count": { + "bsonType": "int", + "description": "打开Webview窗口的个数" + }, + "app_use_duration": { + "bsonType": "int", + "description": "APP使用时长:单位为s" + }, + "app_run_fore": { + "bsonType": "int", + "description": "是否前台运行:1表示前台运行,0表示后台运行" + }, + "package_name": { + "bsonType": "string", + "description": "原生应用包名" + }, + "package_version": { + "bsonType": "string", + "description": "Android的apk版本名称;iOS的ipa版本名称" + }, + "page_url": { + "bsonType": "string", + "description": "页面url" + }, + "error_msg": { + "bsonType": "string", + "description": "错误信息" + }, + "create_time": { + "bsonType": "timestamp", + "description": "客户端记录到的崩溃时间" + } + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/uni-stat-app-platforms.schema.json b/uniCloud-aliyun/database/uni-stat-app-platforms.schema.json new file mode 100644 index 00000000..e66fc37d --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-app-platforms.schema.json @@ -0,0 +1,46 @@ +// 应用平台表 +{ + "bsonType": "object", + "description": "提供应用的平台字典", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_APP_PLATFORMS' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "code": { + "bsonType": "string", + "description": "平台代码,前端上报" + }, + "name": { + "bsonType": "string", + "description": "平台名称,管理端显示" + }, + "order": { + "bsonType": "int", + "description": "序号,前端页面排序使用", + "defaultValue": 0 + }, + "enable": { + "bsonType": "bool", + "description": "是否启动", + "defaultValue": true, + "enum": [{ + "text": "否", + "value": false + }, { + "text": "是", + "value": true + }] + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-app-versions.schema.json b/uniCloud-aliyun/database/uni-stat-app-versions.schema.json new file mode 100644 index 00000000..f23f59c7 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-app-versions.schema.json @@ -0,0 +1,38 @@ +{ + "bsonType": "object", + "description": "提供应用的版本号字典", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "统计应用ID,对应opendb-app-list.appid", + "foreignKey": "opendb-app-list.appid" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "version": { + "bsonType": "string", + "description": "应用版本" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + }, + "last_modify_time": { + "bsonType": "timestamp", + "description": "最后修改时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-error-logs.schema.json b/uniCloud-aliyun/database/uni-stat-error-logs.schema.json new file mode 100644 index 00000000..8b087ab0 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-error-logs.schema.json @@ -0,0 +1,102 @@ +// 应用错误日志表 +{ + "bsonType": "object", + "description": "记录上报的应用运行错误日志", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_ERROR_LOGS' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "用户端上报的应用ID" + }, + "version": { + "bsonType": "string", + "description": "用户端上报的应用版本号" + }, + "platform": { + "bsonType": "string", + "description": "用户端上报的平台code" + }, + "channel": { + "bsonType": "string", + "description": "用户端上报的渠道code\/场景值" + }, + "error_type": { + "bsonType": "int", + "description": "错误类型", + "defaultValue": 0, + "enum": [{ + "text": "未知", + "value": 0 + }, { + "text": "表示webview页面js异常(uni-app项目对应vue页面)", + "value": 2 + }, { + "text": "表示uni框架js异常(仅uni-app项目)", + "value": 4 + }, { + "text": "表示控制页js异常(仅uni-app项目)", + "value": 5 + }, { + "text": "表示nvue页面js异常(仅uni-app项目)", + "value": 6 + }] + }, + "device_id": { + "bsonType": "string", + "description": "客户端携带的设备标识" + }, + "uid": { + "bsonType": "string", + "description": "用户编号, 对应uni-id-users._id" + }, + "os": { + "bsonType": "string", + "description": "客户端操作系统" + }, + "ua": { + "bsonType": "string", + "description": "客户端user-agent信息" + }, + "space_id": { + "bsonType": "string", + "description": "服务空间编号" + }, + "space_provider": { + "bsonType": "string", + "description": "服务空间提供商" + }, + "sdk_version": { + "bsonType": "string", + "description": "小程序基础库版本号" + }, + "platform_version": { + "bsonType": "string", + "description": "微信、支付宝宿主App的版本号" + }, + "error_msg": { + "bsonType": "string", + "description": "错误信息" + }, + "error_hash": { + "bsonType": "string", + "description": "错误hash码" + }, + "page_url": { + "bsonType": "string", + "description": "页面url" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-error-result.schema.json b/uniCloud-aliyun/database/uni-stat-error-result.schema.json new file mode 100644 index 00000000..60a95078 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-error-result.schema.json @@ -0,0 +1,96 @@ +// 错误数据统计结果表 +{ + "bsonType": "object", + "description": "存储汇总的错误日志的数据", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_ERROR_RESULT' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台ID,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_id": { + "bsonType": "string", + "description": "渠道\/场景值ID,对应uni-stat-app-channels._id", + "foreignKey": "uni-stat-app-channels._id" + }, + "version_id": { + "bsonType": "string", + "description": "应用版本ID,对应opendb-app-versions._id", + "foreignKey": "opendb-app-versions._id" + }, + "type": { + "bsonType": "string", + "description": "错误类型", + "enum": [{ + "text": "前端js错误", + "value": "js" + }, { + "text": "原生应用崩溃错误", + "value": "crash" + }] + }, + "hash": { + "bsonType": "string", + "description": "错误hash码" + }, + "msg": { + "bsonType": "string", + "description": "错误信息" + }, + "count": { + "bsonType":"int", + "description":"报错次数" + }, + "app_launch_count": { + "bsonType": "int", + "description": "本时间段App启动或从后台切到前台的次数" + }, + "last_time": { + "bsonType":"timestamp", + "description":"最近一次报错事件" + }, + "dimension": { + "bsonType": "string", + "description": "统计范围 day:按天统计,hour:按小时统计", + "enum": [{ + "text": "月", + "value": "month" + }, { + "text": "周", + "value": "week" + },{ + "text": "天", + "value": "day" + }, { + "text": "小时", + "value": "hour" + }] + }, + "stat_date":{ + "bsonType":"int", + "description":"统计日期,格式yyyymmdd,例:20211201" + }, + "start_time":{ + "bsonType":"timestamp", + "description":"开始时间" + }, + "end_time":{ + "bsonType":"timestamp", + "description":"结束时间" + } + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/uni-stat-event-logs.schema.json b/uniCloud-aliyun/database/uni-stat-event-logs.schema.json new file mode 100644 index 00000000..e0d6f6aa --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-event-logs.schema.json @@ -0,0 +1,116 @@ +// 应用事件日志表 +{ + "bsonType": "object", + "description": "记录上报的事件日志", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_EVENT_LOGS' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "客户端上报的应用ID" + }, + "version": { + "bsonType": "string", + "description": "客户端上报的应用版本号" + }, + "platform": { + "bsonType": "string", + "foreignKey": "uni-stat-app-platforms.code", + "description": "客户端上报的平台code" + }, + "channel": { + "bsonType": "string", + "description": "客户端上报的渠道code\/场景值" + }, + "device_id": { + "bsonType": "string", + "description": "客户端携带的设备标识" + }, + "uid": { + "bsonType": "string", + "description": "用户编号, 对应uni-id-users._id" + }, + "session_id": { + "bsonType": "string", + "description": "访问会话日志ID,对应uni-stat-session-logs._id", + "foreignKey": "uni-stat-session-logs._id" + }, + "page_id": { + "bsonType": "string", + "description": "页面ID,对应uni-stat-pages._id", + "foreignKey": "uni-stat-pages._id" + }, + "event_key": { + "bsonType": "string", + "description": "客户端上报的key" + }, + "param": { + "bsonType": "string", + "description": "事件参数" + }, + "sdk_version": { + "bsonType": "string", + "description": "基础库版本号" + }, + "platform_version": { + "bsonType": "string", + "description": "平台版本,如微信、支付宝宿主App版本号" + }, + "device_os": { + "bsonType": "int", + "description": "设备系统编号,1:安卓,2:iOS,3:PC" + }, + "device_os_version": { + "bsonType": "string", + "description": "设备系统版本" + }, + "device_net": { + "bsonType": "string", + "description": "设备网络型号wifi\/3G\/4G\/" + }, + "device_vendor": { + "bsonType": "string", + "description": "设备供应商 " + }, + "device_model": { + "bsonType": "string", + "description": "设备型号" + }, + "device_language": { + "bsonType": "string", + "description": "设备语言包" + }, + "device_pixel_ratio": { + "bsonType": "string", + "description": "设备像素比 " + }, + "device_window_width": { + "bsonType": "string", + "description": "设备窗口宽度 " + }, + "device_window_height": { + "bsonType": "string", + "description": "设备窗口高度" + }, + "device_screen_width": { + "bsonType": "string", + "description": "设备屏幕宽度" + }, + "device_screen_height": { + "bsonType": "string", + "description": "设备屏幕高度" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-event-result.schema.json b/uniCloud-aliyun/database/uni-stat-event-result.schema.json new file mode 100644 index 00000000..29977b55 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-event-result.schema.json @@ -0,0 +1,82 @@ +// 事件统计结果表 +{ + "bsonType": "object", + "description": "存储汇总的事件日志的数据", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_EVENT_RESULT' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台ID,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_id": { + "bsonType": "string", + "description": "渠道\/场景值ID,对应uni-stat-app-channels._id", + "foreignKey": "uni-stat-app-channels._id" + }, + "version_id": { + "bsonType": "string", + "description": "应用版本ID,对应opendb-app-versions._id", + "foreignKey": "opendb-app-versions._id" + }, + "event_key": { + "bsonType": "string", + "description": "事件key,对应uni-stat-events.event_key", + "foreignKey": "uni-stat-events.event_key" + }, + "event_count": { + "bsonType": "int", + "description": "触发次数" + }, + "device_count": { + "bsonType": "int", + "description": "触发该事件的设备数" + }, + "user_count": { + "bsonType": "int", + "description": "触发该事件的用户数" + }, + "dimension": { + "bsonType": "string", + "description": "统计范围 day:按天统计,hour:按小时统计", + "enum": [{ + "text": "月", + "value": "month" + }, { + "text": "周", + "value": "week" + }, { + "text": "天", + "value": "day" + }, { + "text": "小时", + "value": "hour" + }] + }, + "stat_date": { + "bsonType": "int", + "description": "统计日期,格式yyyymmdd,例:20211201" + }, + "start_time": { + "bsonType": "timestamp", + "description": "开始时间" + }, + "end_time": { + "bsonType": "timestamp", + "description": "结束时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-events.schema.json b/uniCloud-aliyun/database/uni-stat-events.schema.json new file mode 100644 index 00000000..669e504a --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-events.schema.json @@ -0,0 +1,38 @@ +// 应用事件表 +{ + "bsonType": "object", + "description": "提供应用的事件字典", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_EVENTS' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "统计应用ID,对应opendb-app-list.appid", + "foreignKey": "opendb-app-list.appid" + }, + "event_key": { + "bsonType": "string", + "description": "事件键值" + }, + "event_name": { + "bsonType": "string", + "description": "事件名称" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + }, + "update_time": { + "bsonType": "timestamp", + "description": "last_modify_time" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-loyalty-result.schema.json b/uniCloud-aliyun/database/uni-stat-loyalty-result.schema.json new file mode 100644 index 00000000..fb93a498 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-loyalty-result.schema.json @@ -0,0 +1,84 @@ +// 用户忠诚度统计表 +{ + "bsonType": "object", + "description": "存储汇总的设备/用户的粘性数据", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_LOYALTY_RESULT' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台ID,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_id": { + "bsonType": "string", + "description": "渠道\/场景值ID,对应uni-stat-app-channels._id", + "foreignKey": "uni-stat-app-channels._id" + }, + "version_id": { + "bsonType": "string", + "description": "应用版本ID,对应opendb-app-versions._id", + "foreignKey": "opendb-app-versions._id" + }, + "visit_depth_data": { + "bsonType": "object", + "description": "访问深度数据", + "properties": { + "visit_users": { + "bsonType": "object", + "description": "访问用户数" + }, + "visit_devices": { + "bsonType": "object", + "description": "访问设备数" + }, + "visit_times": { + "bsonType": "object", + "description": "访问次数" + } + } + }, + "duration_data": { + "bsonType": "object", + "description": "访问时长数据", + "properties": { + "visit_users": { + "bsonType": "object", + "description": "访问用户数" + }, + "visit_devices": { + "bsonType": "object", + "description": "访问设备数" + }, + "visit_times": { + "bsonType": "object", + "description": "访问次数" + } + } + }, + "stat_date": { + "bsonType": "int", + "description": "统计日期,格式yyyymmdd,例:20211201" + }, + "start_time": { + "bsonType": "timestamp", + "description": "开始时间" + }, + "end_time": { + "bsonType": "timestamp", + "description": "结束时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-mp-scenes.schema.json b/uniCloud-aliyun/database/uni-stat-mp-scenes.schema.json new file mode 100644 index 00000000..2da376aa --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-mp-scenes.schema.json @@ -0,0 +1,34 @@ +// 小程序场景值对照表 +{ + "bsonType": "object", + "description": "提供应用渠道和小程序场景值的数据字典", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "platform": { + "bsonType": "string", + "description": "应用平台,对应uni-stat-app-platforms.code", + "foreignKey": "uni-stat-app-platforms.code" + }, + "scene_code": { + "bsonType": "string", + "description": "场景代码" + }, + "scene_name": { + "bsonType": "string", + "description": "场景名称" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/uni-stat-page-logs.schema.json b/uniCloud-aliyun/database/uni-stat-page-logs.schema.json new file mode 100644 index 00000000..6135bccc --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-page-logs.schema.json @@ -0,0 +1,79 @@ +// 应用页面访问日志表 +{ + "bsonType": "object", + "description": "记录上报的页面访问日志", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_PAGE_LOGS' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID" + }, + "version": { + "bsonType": "string", + "description": "用户端上报的应用版本号" + }, + "platform": { + "bsonType": "string", + "description": "用户端上报的平台code" + }, + "channel": { + "bsonType": "string", + "description": "用户端上报的渠道code\/场景值" + }, + "device_id": { + "bsonType": "string", + "description": "客户端携带的设备标识" + }, + "uid": { + "bsonType": "string", + "description": "用户编号, 对应uni-id-users._id" + }, + "session_id": { + "bsonType": "string", + "description": "访问会话日志ID,对应uni-stat-session-logs._id", + "foreignKey": "uni-stat-session-logs._id" + }, + "page_id": { + "bsonType": "string", + "description": "当前页面ID,对应uni-stat-pages._id", + "foreignKey": "uni-stat-pages._id" + }, + "previous_page_id": { + "bsonType": "string", + "description": "上级页面ID,为空表示第一个页面, 对应uni-stat-pages._id" + }, + "previous_page_duration": { + "bsonType": "int", + "description": "上级页面停留时间,单位秒,前端上报" + }, + "previous_page_is_entry": { + "bsonType": "int", + "defaultValue": 0, + "description": " 上级页面是否为入口页, 0否 1是", + "enum": [{ + "text": "否", + "value": 0 + }, { + "text": "是", + "value": 1 + }] + }, + "query_string": { + "bsonType": "string", + "description": "页面参数" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-page-result.schema.json b/uniCloud-aliyun/database/uni-stat-page-result.schema.json new file mode 100644 index 00000000..63ae4f47 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-page-result.schema.json @@ -0,0 +1,114 @@ +// 页面统计结果表 +{ + "bsonType": "object", + "description": "存储汇总的页面访问日志的数据", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_PAGE_RESULT' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台ID,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_id": { + "bsonType": "string", + "description": "渠道\/场景值ID,对应uni-stat-app-channels._id", + "foreignKey": "uni-stat-app-channels._id" + }, + "version_id": { + "bsonType": "string", + "description": "应用版本ID,对应opendb-app-versions._id", + "foreignKey": "opendb-app-versions._id" + }, + "page_id": { + "bsonType": "string", + "description": "页面表ID,对应页面表ID,对应uni-stat-pages._id", + "foreignKey": "uni-stat-pages._id" + }, + "visit_times": { + "bsonType": "int", + "description": "访问次数" + }, + "visit_devices": { + "bsonType": "int", + "description": "访问设备数" + }, + "exit_times": { + "bsonType": "int", + "description": "退出次数" + }, + "duration": { + "bsonType": "int", + "description": "访问总时长,单位秒" + }, + "share_count": { + "bsonType": "int", + "description": "分享次数" + }, + "entry_devices": { + "bsonType": "int", + "description": "当前页作为入口页的设备数" + }, + "entry_users": { + "bsonType": "int", + "description": "当前页作为入口页的用户数" + }, + "entry_count": { + "bsonType": "int", + "description": "当前页作为入口页的总次数" + }, + "entry_duration": { + "bsonType": "int", + "description": "当前页作为入口时,本页面的总访问时长,单位秒" + }, + "bounce_times": { + "bsonType": "int", + "description": "跳出次数" + }, + "bounce_rate": { + "bsonType": "double", + "description": "跳出率" + }, + "dimension": { + "bsonType": "string", + "description": "统计范围 day:按天统计,hour:按小时统计", + "enum": [{ + "text": "月", + "value": "month" + }, { + "text": "周", + "value": "week" + }, { + "text": "天", + "value": "day" + }, { + "text": "小时", + "value": "hour" + }] + }, + "stat_date": { + "bsonType": "int", + "description": "统计日期,格式yyyymmdd,例:20211201" + }, + "start_time": { + "bsonType": "timestamp", + "description": "开始时间" + }, + "end_time": { + "bsonType": "timestamp", + "description": "结束时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-pages.schema.json b/uniCloud-aliyun/database/uni-stat-pages.schema.json new file mode 100644 index 00000000..e048b37f --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-pages.schema.json @@ -0,0 +1,34 @@ +// 应用页面表 +{ + "bsonType": "object", + "description": "提供应用的页面字典", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_PAGES' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "统计应用ID,对应opendb-app-list.appid", + "foreignKey": "opendb-app-list.appid" + }, + "path": { + "bsonType": "string", + "description": "页面路径,如`\/pages\/index\/index`" + }, + "title": { + "bsonType": "string", + "description": "页面标题" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-pay-result.schema.json b/uniCloud-aliyun/database/uni-stat-pay-result.schema.json new file mode 100644 index 00000000..655d4d85 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-pay-result.schema.json @@ -0,0 +1,169 @@ +{ + "bsonType": "object", + "description": "存储统计汇总的支付数据", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID,对应opendb-app-list.appid", + "foreignKey": "opendb-app-list.appid" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台ID,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_id": { + "bsonType": "string", + "description": "渠道/场景值ID,对应uni-stat-app-channels._id", + "foreignKey": "uni-stat-app-channels._id" + }, + "version_id": { + "bsonType": "string", + "description": "应用版本ID,对应opendb-app-versions._id", + "foreignKey": "opendb-app-versions._id" + }, + "pay_total_amount": { + "bsonType": "int", + "description": "支付金额:统计时间内,成功支付的订单金额之和(不剔除退款订单)。单位分。" + }, + "pay_order_count": { + "bsonType": "int", + "description": "支付笔数:统计时间内,成功支付的订单数,一个订单对应唯一一个订单号。(不剔除退款订单。)" + }, + "pay_user_count": { + "bsonType": "int", + "description": "支付人数:统计时间内,成功支付的人数(不剔除退款订单)。" + }, + "pay_device_count": { + "bsonType": "int", + "description": "支付设备数:统计时间内,成功支付的设备数(不剔除退款订单)。" + }, + "create_total_amount": { + "bsonType": "int", + "description": "下单金额:统计时间内,成功下单的订单金额(不剔除退款订单)。单位分。" + }, + "create_order_count": { + "bsonType": "int", + "description": "下单笔数:统计时间内,成功下单的订单笔数(不剔除退款订单)。" + }, + "create_user_count": { + "bsonType": "int", + "description": "下单人数:统计时间内,成功下单的客户数,一人多次下单记为一人(不剔除退款订单)。" + }, + "create_device_count": { + "bsonType": "int", + "description": "下单设备数:统计时间内,成功下单的设备数,一台设备多次访问被计为一台(不剔除退款订单)。" + }, + "refund_total_amount": { + "bsonType": "int", + "description": "成功退款金额:统计时间内,成功退款的金额。以成功退款时间点为准。单位分。" + }, + "refund_order_count": { + "bsonType": "int", + "description": "成功退款订单数:统计时间内,成功退款的订单数。以成功退款时间点为准。" + }, + "refund_user_count": { + "bsonType": "int", + "description": "成功退款人数:统计时间内,成功退款的人数(不剔除退款订单)。" + }, + "refund_device_count": { + "bsonType": "int", + "description": "成功退款设备数:统计时间内,成功退款的设备数(不剔除退款订单)。" + }, + "activity_user_count": { + "bsonType": "int", + "description": "访问人数:统计时间内,访问人数,一人多次访问被计为一人(只统计已登录的用户)。" + }, + "activity_device_count": { + "bsonType": "int", + "description": "访问设备数:统计时间内,访问设备数,一台设备多次访问被计为一台(包含未登录的用户)。" + }, + "new_user_count": { + "bsonType": "int", + "description": "新增注册人数:统计时间内,注册人数。" + }, + "new_device_count": { + "bsonType": "int", + "description": "新增新设备数:统计时间内,新设备数。" + }, + "new_user_create_order_count": { + "bsonType": "int", + "description": "新用户下单人数:统计时间内,新增注册人数中下单的人数。" + }, + "new_user_pay_order_count": { + "bsonType": "int", + "description": "新用户支付人数:统计时间内,新增注册人数中下成功支付的人数。" + }, + "dimension": { + "bsonType": "string", + "description": "统计范围 hour:按小时统计,day:按天统计,week:按周统计,month:按月统计 quarter:按季度统计 year:按年统计", + "enum": [{ + "text": "年", + "value": "year" + }, { + "text": "季度", + "value": "quarter" + }, { + "text": "月", + "value": "month" + }, { + "text": "周", + "value": "week" + }, { + "text": "天", + "value": "day" + }, { + "text": "小时", + "value": "hour" + }] + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间" + }, + "start_time": { + "bsonType": "timestamp", + "description": "统计开始时间" + }, + "end_time": { + "bsonType": "timestamp", + "description": "统计结束时间" + }, + "stat_date": { + "bsonType": "object", + "description": "统计日期参数", + "properties": { + "date_str": { + "bsonType": "string", + "description": "如:2021-07-27" + }, + "year": { + "bsonType": "int", + "description": "年" + }, + "month": { + "bsonType": "int", + "description": "月" + }, + "day": { + "bsonType": "int", + "description": "日" + }, + "hour": { + "bsonType": "int", + "description": "时" + } + } + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-result.schema.json b/uniCloud-aliyun/database/uni-stat-result.schema.json new file mode 100644 index 00000000..640cadae --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-result.schema.json @@ -0,0 +1,156 @@ +// 应用统计结果表 +{ + "bsonType": "object", + "description": "存储统计汇总的会话数据包括不限于设备\/用户的数量、访问量、活跃度(日活、周活、月活)、留存率(日留存、周留存、月留存)、跳出率、访问时长等数据", + "required": [], + "permission": { + "read": "'READ_UNI_STAT_RESULT' in auth.permission", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用ID,对应opendb-app-list.appid", + "foreignKey": "opendb-app-list.appid" + }, + "platform_id": { + "bsonType": "string", + "description": "应用平台ID,对应uni-stat-app-platforms._id", + "foreignKey": "uni-stat-app-platforms._id" + }, + "channel_id": { + "bsonType": "string", + "description": "渠道\/场景值ID,对应uni-stat-app-channels._id", + "foreignKey": "uni-stat-app-channels._id" + }, + "version_id": { + "bsonType": "string", + "description": "应用版本ID,对应opendb-app-versions._id", + "foreignKey": "opendb-app-versions._id" + }, + "total_users": { + "bsonType": "int", + "description": "历史累计总用户数" + }, + "new_user_count": { + "bsonType": "int", + "description": "本时间段新增用户数" + }, + "active_user_count": { + "bsonType": "int", + "description": "本时间段活跃用户数" + }, + "total_devices": { + "bsonType": "int", + "description": "历史累计总设备数" + }, + "new_device_count": { + "bsonType": "int", + "description": "本时间段新增设备数" + }, + "user_session_times": { + "bsonType": "int", + "description": "本时间段用户的会话次数" + }, + "active_device_count": { + "bsonType": "int", + "description": "本时间段活跃设备数" + }, + "app_launch_count": { + "bsonType": "int", + "description": "本时间段App启动或从后台切到前台的次数" + }, + "error_count": { + "bsonType": "int", + "description": "本时间段报错次数" + }, + "duration": { + "bsonType": "int", + "description": "时间段内,所有会话访问总时长,单位秒" + }, + "user_duration": { + "bsonType": "int", + "description": "本次登录用户的会话总时长,单位为秒" + }, + "avg_device_session_time": { + "bsonType": "int", + "description": "设备的次均停留时长,单位秒" + }, + "avg_device_time": { + "bsonType": "int", + "defaultValue": "设均停留时长(平均每台设备的停留时长),单位秒" + }, + "avg_user_session_time": { + "bsonType": "int", + "description": "用户的次均停留时长,单位秒" + }, + "avg_user_time": { + "bsonType": "int", + "defaultValue": "人均停留时长(平均每个登录用户的停留时长),单位秒" + }, + "bounce_times": { + "bsonType": "int", + "description": "跳出次数" + }, + "bounce_rate": { + "bsonType": "double", + "description": "跳出率" + }, + "retention": { + "bsonType": "object", + "description": "留存信息", + "properties": { + "active_user": { + "bsonType": "object", + "description": "活跃用户留存信息" + }, + "new_user": { + "bsonType": "object", + "description": "新增用户留存信息" + }, + "active_device": { + "bsonType": "object", + "description": "活跃设备留存信息" + }, + "new_device": { + "bsonType": "object", + "description": "新增设备留存信息" + } + } + }, + "dimension": { + "bsonType": "string", + "description": "统计范围 day:按天统计,hour:按小时统计", + "enum": [{ + "text": "月", + "value": "month" + }, { + "text": "周", + "value": "week" + }, { + "text": "天", + "value": "day" + }, { + "text": "小时", + "value": "hour" + }] + }, + "stat_date": { + "bsonType": "int", + "description": "统计日期,格式yyyymmdd,例:20211201" + }, + "start_time": { + "bsonType": "timestamp", + "description": "开始时间" + }, + "end_time": { + "bsonType": "timestamp", + "description": "结束时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-run-errors.schema.json b/uniCloud-aliyun/database/uni-stat-run-errors.schema.json new file mode 100644 index 00000000..1ca0f503 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-run-errors.schema.json @@ -0,0 +1,33 @@ +// 运行错误日志表 +{ + "bsonType": "object", + "description": "记录数据统计时运行出错的日志", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "mod": { + "bsonType": "string", + "description": "运行模块" + }, + "params": { + "bsonType": "object", + "description": "运行参数" + }, + "error": { + "bsonType": "string", + "description": "错误信息" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-session-logs.schema.json b/uniCloud-aliyun/database/uni-stat-session-logs.schema.json new file mode 100644 index 00000000..3782a770 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-session-logs.schema.json @@ -0,0 +1,192 @@ +// 应用会话日志表 +{ + "bsonType": "object", + "description": "记录设备访问时产生的会话日志", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "客户端上报的应用ID" + }, + "version": { + "bsonType": "string", + "description": "客户端上报的应用版本号" + }, + "platform": { + "bsonType": "string", + "description": "客户端上报的平台code" + }, + "channel": { + "bsonType": "string", + "description": "客户端上报的渠道code\/场景值" + }, + "type": { + "bsonType": "string", + "description": "会话类型", + "defaultValue": 1, + "enum": [{ + "text": "正常进入上报", + "value": 1 + }, { + "text": "后台进前台超时上报", + "value": 2 + }, { + "text": "页面停留超时上报", + "value": 3 + }] + }, + "device_id": { + "bsonType": "string", + "description": "客户端携带的设备标识" + }, + "last_visit_user_id": { + "bsonType": "string", + "description": "本次会话最终访问用户的ID, uni-id-users._id,客户端上报" + }, + "is_first_visit": { + "bsonType": "int", + "description": "是否为首次访问", + "defaultValue": 0, + "enum": [{ + "text": "否", + "value": 0 + }, { + "text": "是", + "value": 1 + }] + }, + "first_visit_time": { + "bsonType": "timestamp", + "description": "用户首次访问时间" + }, + "last_visit_time": { + "bsonType": "timestamp", + "description": "用户最后一次访问时间" + }, + "total_visit_count": { + "bsonType": "int", + "description": "用户累计访问次数,客户端上报" + }, + "entry_page_id": { + "bsonType": "string", + "description": "本次会话入口页面ID, 同uni-stat-pagesd" + }, + "exit_page_id": { + "bsonType": "string", + "description": "本次会话退出页面ID, 同uni-stat-pagesd" + }, + "page_count": { + "bsonType": "int", + "description": "本次会话浏览的页面数" + }, + "event_count": { + "bsonType": "int", + "description": "本次会话产生的事件数" + }, + "duration": { + "bsonType": "int", + "description": "本次会话时长,单位为秒,服务端计算" + }, + "sdk_version": { + "bsonType": "string", + "description": "基础库版本号" + }, + "platform_version": { + "bsonType": "string", + "description": "平台版本,如微信、支付宝宿主App版本号" + }, + "device_os": { + "bsonType": "int", + "description": "设备系统编号,1:安卓,2:iOS,3:PC" + }, + "device_os_version": { + "bsonType": "string", + "description": "设备系统版本" + }, + "device_net": { + "bsonType": "string", + "description": "设备网络型号wifi\/3G\/4G\/" + }, + "device_vendor": { + "bsonType": "string", + "description": "设备供应商 " + }, + "device_model": { + "bsonType": "string", + "description": "设备型号" + }, + "device_language": { + "bsonType": "string", + "description": "设备语言包" + }, + "device_pixel_ratio": { + "bsonType": "string", + "description": "设备像素比 " + }, + "device_window_width": { + "bsonType": "string", + "description": "设备窗口宽度 " + }, + "device_window_height": { + "bsonType": "string", + "description": "设备窗口高度" + }, + "device_screen_width": { + "bsonType": "string", + "description": "设备屏幕宽度" + }, + "device_screen_height": { + "bsonType": "string", + "description": "设备屏幕高度" + }, + "location_ip": { + "bsonType": "string", + "description": "ip" + }, + "location_latitude": { + "bsonType": "double", + "description": "纬度" + }, + "location_longitude": { + "bsonType": "double", + "description": "经度" + }, + "location_country": { + "bsonType": "string", + "description": "国家" + }, + "location_province": { + "bsonType": "string", + "description": "省份" + }, + "location_city": { + "bsonType": "string", + "description": "城市" + }, + "is_finish": { + "bsonType": "int", + "defaultValue": 0, + "description": "本次会话是否结束,0:否,1是", + "enum": [{ + "text": "否", + "value": 0 + }, { + "text": "是", + "value": 1 + }] + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} diff --git a/uniCloud-aliyun/database/uni-stat-share-logs.schema.json b/uniCloud-aliyun/database/uni-stat-share-logs.schema.json new file mode 100644 index 00000000..b66c3246 --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-share-logs.schema.json @@ -0,0 +1,55 @@ +// 应用分享日志表 +{ + "bsonType": "object", + "description": "记录触发分享事件的日志", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "客户端上报的应用ID" + }, + "version": { + "bsonType": "string", + "description": "客户端上报的应用版本号" + }, + "platform": { + "bsonType": "string", + "description": "客户端上报的平台code" + }, + "channel": { + "bsonType": "string", + "description": "客户端上报的渠道code\/场景值" + }, + "device_id": { + "bsonType": "string", + "description": "客户端携带的设备标识" + }, + "uid": { + "bsonType": "string", + "description": "用户编号, 对应uni-id-users._id" + }, + "session_id": { + "bsonType": "string", + "description": "访问会话日志ID,对应uni-stat-session-logs._id", + "foreignKey": "uni-stat-session-logs._id" + }, + "page_id": { + "bsonType": "string", + "description": "当前页面ID,对应uni-stat-pagesd", + "foreignKey": "uni-stat-pagesd" + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/uni-stat-user-session-logs.schema.json b/uniCloud-aliyun/database/uni-stat-user-session-logs.schema.json new file mode 100644 index 00000000..93728feb --- /dev/null +++ b/uniCloud-aliyun/database/uni-stat-user-session-logs.schema.json @@ -0,0 +1,82 @@ +// 用户会话日志表 +{ + "bsonType": "object", + "description": "记录登录用户的会话日志", + "required": [], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "客户端携带的应用ID" + }, + "version": { + "bsonType": "string", + "description": "客户端上报的应用版本号" + }, + "platform": { + "bsonType": "string", + "description": "客户端上报的平台code" + }, + "channel": { + "bsonType": "string", + "description": "客户端上报的渠道code\/场景值" + }, + "session_id": { + "bsonType": "string", + "description": "访问会话日志ID,对应uni-stat-session-logs._id", + "foreignKey": "uni-stat-session-logs._id" + }, + "uid": { + "bsonType": "string", + "description": "本次会话最终访问用户的ID, uni-id-users._id" + }, + "last_visit_time": { + "bsonType": "timestamp", + "description": "用户最后一次访问时间" + }, + "entry_page_id": { + "bsonType": "string", + "description": "本次会话入口页面ID, 同uni-stat-pagesd" + }, + "exit_page_id": { + "bsonType": "string", + "description": "本次会话退出页面ID, 同uni-stat-pagesd" + }, + "page_count": { + "bsonType": "int", + "description": "本次会话浏览的页面数" + }, + "event_count": { + "bsonType": "int", + "description": "本次会话产生的事件数" + }, + "duration": { + "bsonType": "int", + "description": "本次会话时长,单位为秒,服务端计算" + }, + "is_finish": { + "bsonType": "int", + "defaultValue": 0, + "description": "本次会话是否结束,0:否,1是", + "enum": [{ + "text": "否", + "value": 0 + }, { + "text": "是", + "value": 1 + }] + }, + "create_time": { + "bsonType": "timestamp", + "description": "创建时间" + } + } +} \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/changelog.md b/uni_modules/Sansnn-uQRCode/changelog.md index 2fc96642..faf7abd3 100644 --- a/uni_modules/Sansnn-uQRCode/changelog.md +++ b/uni_modules/Sansnn-uQRCode/changelog.md @@ -1,7 +1,12 @@ -## 3.5.1(2022-08-11) -v3.5.1 - -修复vue3引入报错; -修复vue导出临时文件失败; -修复在其他dom元素更新时,可能导致二维码重新渲染; -修复size重新赋值导致组件偏移、闪烁。 +## 4.0.6(2022-12-12) +修复`getDrawModules`,第一次获取结果正常,后续获取`tile`模块不存在的问题; +修复安卓type:normal因Canvas API使用了小数或为0的参数导致生成异常的问题(注:安卓非2d Canvas部分API参数不支持携带小数,部分API参数必须大于0)。 +## 4.0.1(2022-11-28) +优化组件loading属性的表现; +新增组件type选项normal,以便于在某些条件编译初始为type=2d时还可以选择使用非2d组件类型; +修复组件条件编译在其他编辑器语法提示报错; +修复原生对es5的支持。 +## 4.0.0(2022-11-21) +v4版本源代码全面开放,开源地址:[https://github.com/Sansnn/uQRCode](https://github.com/Sansnn/uQRCode); + +升级说明:v4为大版本更新,虽然已尽可能兼容上一代版本,但不可避免的还是存在一些细节差异,若更新后出现问题,请参考对照[v3 文档](https://uqrcode.cn/doc/v3),[v4 文档](https://uqrcode.cn/doc)进行修改。 diff --git a/uni_modules/Sansnn-uQRCode/common/cache.js b/uni_modules/Sansnn-uQRCode/common/cache.js new file mode 100644 index 00000000..d897d268 --- /dev/null +++ b/uni_modules/Sansnn-uQRCode/common/cache.js @@ -0,0 +1 @@ +export const cacheImageList = []; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/common/queue.js b/uni_modules/Sansnn-uQRCode/common/queue.js new file mode 100644 index 00000000..c89d76b8 --- /dev/null +++ b/uni_modules/Sansnn-uQRCode/common/queue.js @@ -0,0 +1,41 @@ +function Queue() { + let waitingQueue = this.waitingQueue = []; + let isRunning = this.isRunning = false; // 记录是否有未完成的任务 + + function execute(task, resolve, reject) { + task() + .then((data) => { + resolve(data); + }) + .catch((e) => { + reject(e); + }) + .finally(() => { + // 等待任务队列中如果有任务,则触发它;否则设置isRunning = false,表示无任务状态 + if (waitingQueue.length) { + const next = waitingQueue.shift(); + execute(next.task, next.resolve, next.reject); + } else { + isRunning = false; + } + }); + } + this.exec = function(task) { + return new Promise((resolve, reject) => { + if (isRunning) { + waitingQueue.push({ + task, + resolve, + reject + }); + } else { + isRunning = true; + execute(task, resolve, reject); + } + }); + } +} + +/* 队列实例,某些平台一起使用多个组件时需要通过队列逐一绘制,否则部分绘制方法异常,nvue端的iOS gcanvas尤其明显,在不通过队列绘制时会出现图片丢失的情况 */ +export const queueDraw = new Queue(); +export const queueLoadImage = new Queue(); \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/common/types/cache.d.ts b/uni_modules/Sansnn-uQRCode/common/types/cache.d.ts new file mode 100644 index 00000000..657e9fa2 --- /dev/null +++ b/uni_modules/Sansnn-uQRCode/common/types/cache.d.ts @@ -0,0 +1,3 @@ +declare module '*/common/cache' { + export const cacheImageList: Array; +} \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/common/types/queue.d.ts b/uni_modules/Sansnn-uQRCode/common/types/queue.d.ts new file mode 100644 index 00000000..9c5aafc3 --- /dev/null +++ b/uni_modules/Sansnn-uQRCode/common/types/queue.d.ts @@ -0,0 +1,4 @@ +declare module '*/common/queue' { + export const queueDraw: any; + export const queueLoadImage: any; +} \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/components/u-qrcode/u-qrcode.vue b/uni_modules/Sansnn-uQRCode/components/u-qrcode/u-qrcode.vue index 8ae7f425..8ae9598a 100644 --- a/uni_modules/Sansnn-uQRCode/components/u-qrcode/u-qrcode.vue +++ b/uni_modules/Sansnn-uQRCode/components/u-qrcode/u-qrcode.vue @@ -1,771 +1,1131 @@ - - - + + - - + // #ifdef VUE3 + import { + toRaw + } from 'vue'; + // #endif + + /* 引入uQRCode核心js */ + import UQRCode from '../../js_sdk/uqrcode/uqrcode'; + + /* 引入nvue所需模块 */ + // #ifdef APP-NVUE + import { + enable, + WeexBridge + } from '../../js_sdk/gcanvas'; + const modal = weex.requireModule('modal'); + // #endif + + /* 引入队列 */ + import { + queueDraw, + queueLoadImage + } from '../../common/queue'; + /* 引入缓存图片 */ + import { + cacheImageList + } from '../../common/cache'; + + let instance = null; + + export default { + name: 'uqrcode', + props: { + /** + * canvas组件id + */ + canvasId: { + type: String, + required: true // canvasId在微信小程序初始值不能为空,created中赋值也不行,必须给一个值,否则挂载组件后无法绘制。不考虑用随机id,uuid + }, + /** + * 二维码内容 + */ + value: { + type: [String, Number] + }, + /** + * 选项 + */ + options: { + type: Object, + default: () => { + return {}; + } + }, + /** + * 二维码大小 + */ + size: { + type: [String, Number], + default: 200 + }, + /** + * 二维码尺寸单位 + */ + sizeUnit: { + type: String, + default: 'px' + }, + /** + * 导出的文件类型 + */ + fileType: { + type: String, + default: 'png' + }, + /** + * 是否初始化组件后就开始生成 + */ + start: { + type: Boolean, + default: true + }, + /** + * 是否数据发生改变自动重绘 + */ + auto: { + type: Boolean, + default: true + }, + /** + * 隐藏组件 + */ + hide: { + type: Boolean, + default: false + }, + /** + * canvas 类型,微信小程序默认使用2d,非2d微信官方已放弃维护,问题比较多 + * 注意:微信小程序type2d手机上正常,PC上微信内打开小程序toDataURL报错,看后期微信官方团队会不会做兼容,不兼容的话只能在自行判断在PC使用非2d,或者直接提示用户请在手机上操作,微信团队的海报中心小程序就是这么做的 + */ + type: { + type: String, + default: () => { + // #ifdef MP-WEIXIN + return '2d'; + // #endif + // #ifndef MP-WEIXIN + return 'normal'; + // #endif + } + }, + /** + * 队列绘制,主要针对NVue端 + */ + queue: { + type: Boolean, + default: false + }, + /** + * 是否队列加载图片,可减少canvas发起的网络资源请求,节省服务器资源 + */ + isQueueLoadImage: { + type: Boolean, + default: false + }, + /** + * loading态 + */ + loading: { + type: Boolean, + default: undefined + }, + /** + * H5保存即自动下载(在支持的环境下),默认false为仅弹层提示用户需要长按图片保存,不会自动下载 + */ + h5SaveIsDownload: { + type: Boolean, + default: false + }, + /** + * H5下载名称 + */ + h5DownloadName: { + type: String, + default: 'uQRCode' + } + }, + data() { + return { + canvas: undefined, + canvasType: undefined, + canvasContext: undefined, + makeDelegate: undefined, + drawDelegate: undefined, + toTempFilePathDelegate: undefined, + makeExecuted: false, + makeing: false, + drawing: false, + isError: false, + error: undefined, + isH5Save: false, + tempFilePath: '', + templateOptions: { + size: 0, + width: 0, // 组件宽度 + height: 0, + canvasWidth: 0, // canvas宽度 + canvasHeight: 0, + canvasTransform: '', + canvasDisplay: false + }, + uqrcodeOptions: { + data: '' + }, + plugins: [], + makeingPattern: [ + [ + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true] + ], + [ + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, true, true, true, false, true, true, true], + [true, true, true, true, true, true, false, true, true, true], + [true, true, true, true, true, true, false, true, true, true] + ], + [ + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, true, true, true, true, false, false, false], + [true, true, true, true, true, true, true, false, false, false], + [true, true, true, true, true, true, true, false, false, false], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true] + ], + [ + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true] + ] + ] + }; + }, + watch: { + type: { + handler(val) { + const types = ['2d']; + if (types.includes(val)) { + this.canvasType = val; + } else { + this.canvasType = undefined; + } + }, + immediate: true + }, + value: { + handler() { + if (this.auto) { + this.remake(); + } + } + }, + size: { + handler() { + if (this.auto) { + this.remake(); + } + } + }, + options: { + handler() { + if (this.auto) { + this.remake(); + } + }, + deep: true + }, + makeing: { + handler(val) { + if (!val) { + if (typeof this.toTempFilePathDelegate === 'function') { + this.toTempFilePathDelegate(); + } + } + } + } + }, + mounted() { + this.templateOptions.size = this.sizeUnit == 'rpx' ? uni.upx2px(this.size) : this.size; + this.templateOptions.width = this.templateOptions.size; + this.templateOptions.height = this.templateOptions.size; + this.templateOptions.canvasWidth = this.templateOptions.size; + this.templateOptions.canvasHeight = this.templateOptions.size; + if (this.canvasType == '2d') { + // #ifndef MP-WEIXIN + this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size / + this.templateOptions.canvasHeight})`; + // #endif + } else { + this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size / + this.templateOptions.canvasHeight})`; + } + if (this.start) { + this.make(); + } + }, + methods: { + /** + * 获取模板选项 + */ + getTemplateOptions() { + var size = this.sizeUnit == 'rpx' ? uni.upx2px(this.size) : this.size; + return deepReplace(this.templateOptions, { + size, + width: size, + height: size + }); + }, + /** + * 获取插件选项 + */ + getUqrcodeOptions() { + return deepReplace(this.options, { + data: String(this.value), + size: Number(this.templateOptions.size) + }); + }, + /** + * 重置画布 + */ + resetCanvas(callback) { + this.templateOptions.canvasDisplay = false; + this.$nextTick(() => { + this.templateOptions.canvasDisplay = true; + this.$nextTick(() => { + callback && callback(); + }); + }); + }, + /** + * 绘制二维码 + */ + async draw(callback = {}, isDrawDelegate = false) { + if (typeof callback.success != 'function') { + callback.success = () => {}; + } + if (typeof callback.fail != 'function') { + callback.fail = () => {}; + } + if (typeof callback.complete != 'function') { + callback.complete = () => {}; + } + + if (this.drawing) { + if (!isDrawDelegate) { + this.drawDelegate = () => { + this.draw(callback, true); + }; + return; + } + } else { + this.drawing = true; + } + + if (!this.canvasId) { + console.error('[uQRCode]: canvasId must be set!'); + this.isError = true; + this.drawing = false; + callback.fail({ + errMsg: '[uQRCode]: canvasId must be set!' + }); + callback.complete({ + errMsg: '[uQRCode]: canvasId must be set!' + }); + return; + } + if (!this.value) { + console.error('[uQRCode]: value must be set!'); + this.isError = true; + this.drawing = false; + callback.fail({ + errMsg: '[uQRCode]: value must be set!' + }); + callback.complete({ + errMsg: '[uQRCode]: value must be set!' + }); + return; + } + + /* 组件数据 */ + this.templateOptions = this.getTemplateOptions(); + /* uQRCode选项 */ + this.uqrcodeOptions = this.getUqrcodeOptions(); + /* 纠错等级兼容字母写法 */ + if (typeof this.uqrcodeOptions.errorCorrectLevel === 'string') { + this.uqrcodeOptions.errorCorrectLevel = UQRCode.errorCorrectLevel[this.uqrcodeOptions.errorCorrectLevel]; + } + /* nvue不支持动态修改gcanvas尺寸,除nvue外,默认使用useDynamicSize */ + // #ifndef APP-NVUE + if (typeof this.options.useDynamicSize === 'undefined') { + this.uqrcodeOptions.useDynamicSize = true; + } + // #endif + // #ifdef APP-NVUE + if (typeof this.options.useDynamicSize === 'undefined') { + this.uqrcodeOptions.useDynamicSize = false; + } + // if (typeof this.options.drawReserve === 'undefined') { + // this.uqrcodeOptions.drawReserve = true; + // } + // #endif + + /* 获取uQRCode实例 */ + const qr = instance = new UQRCode(); + /* 注册扩展 */ + this.plugins.forEach(p => qr.register(p.plugin)); + /* 设置uQRCode选项 */ + qr.setOptions(this.uqrcodeOptions); + /* 调用制作二维码方法 */ + qr.make(); + + /* 获取canvas上下文 */ + let canvasContext = null; + // #ifndef APP-NVUE + if (this.canvasType === '2d') { + // #ifdef MP-WEIXIN + /* 微信小程序获取canvas2d上下文方式 */ + const canvas = (this.canvas = await new Promise(resolve => { + uni + .createSelectorQuery() + .in(this) // 在组件内使用需要 + .select(`#${this.canvasId}`) + .fields({ + node: true, + size: true + }) + .exec(res => { + resolve(res[0].node); + }); + })); + canvasContext = this.canvasContext = canvas.getContext('2d'); + /* 2d的组件设置宽高与实际canvas绘制宽高不是一个,打个比方,组件size=200,canvas.width设置为100,那么绘制出来就是100=200,组件size=400,canvas.width设置为800,绘制大小还是800=400,所以无需理会下方返回的dynamicSize是多少,按dpr重新赋值给canvas即可 */ + this.templateOptions.canvasWidth = qr.size; + this.templateOptions.canvasHeight = qr.size; + this.templateOptions.canvasTransform = ''; + /* 使用dynamicSize+scale,可以解决小块间出现白线问题,dpr可以解决模糊问题 */ + const dpr = uni.getSystemInfoSync().pixelRatio; + canvas.width = qr.dynamicSize * dpr; + canvas.height = qr.dynamicSize * dpr; + canvasContext.scale(dpr, dpr); + /* 微信小程序获取图像方式 */ + qr.loadImage = this.getLoadImage(function(src) { + /* 小程序下获取网络图片信息需先配置download域名白名单才能生效 */ + return new Promise((resolve, reject) => { + const img = canvas.createImage(); + img.src = src; + img.onload = () => { + resolve(img); + }; + img.onerror = err => { + reject(err); + }; + }); + }); + // #endif + // #ifndef MP-WEIXIN + /* 非微信小程序不支持2d,切换回uniapp获取canvas上下文方式 */ + canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this); + /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */ + this.templateOptions.canvasWidth = qr.dynamicSize; + this.templateOptions.canvasHeight = qr.dynamicSize; + this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size / + this.templateOptions.canvasHeight})`; + /* uniapp获取图像方式 */ + qr.loadImage = this.getLoadImage(function(src) { + return new Promise((resolve, reject) => { + if (src.startsWith('http')) { + uni.getImageInfo({ + src, + success: res => { + resolve(res.path); + }, + fail: err => { + reject(err); + } + }); + } else { + if (src.startsWith('.')) { + console.error('[uQRCode]: 本地图片路径仅支持绝对路径!'); + throw new Error('[uQRCode]: local image path only supports absolute path!'); + } else { + resolve(src); + } + } + }); + }); + // #endif + } else { + /* uniapp获取canvas上下文方式 */ + canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this); + /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */ + this.templateOptions.canvasWidth = qr.dynamicSize; + this.templateOptions.canvasHeight = qr.dynamicSize; + this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size / + this.templateOptions.canvasHeight})`; + /* uniapp获取图像方式 */ + qr.loadImage = this.getLoadImage(function(src) { + return new Promise((resolve, reject) => { + /* getImageInfo在微信小程序的bug:本地路径返回路径会把开头的/或../移除,导致路径错误,解决方法:限制只能使用绝对路径 */ + if (src.startsWith('http')) { + uni.getImageInfo({ + src, + success: res => { + resolve(res.path); + }, + fail: err => { + reject(err); + } + }); + } else { + if (src.startsWith('.')) { + console.error('[uQRCode]: 本地图片路径仅支持绝对路径!'); + throw new Error('[uQRCode]: local image path only supports absolute path!'); + } else { + resolve(src); + } + } + }); + }); + } + // #endif + // #ifdef APP-NVUE + /* NVue获取canvas上下文方式 */ + const gcanvas = this.$refs['gcanvas']; + const canvas = enable(gcanvas, { + bridge: WeexBridge + }); + canvasContext = this.canvasContext = canvas.getContext('2d'); + /* NVue获取图像方式 */ + qr.loadImage = this.getLoadImage(function(src) { + return new Promise((resolve, reject) => { + /* getImageInfo在nvue的bug:获取同一个路径的图片信息,同一时间第一次获取成功,后续失败,猜测是写入本地时产生文件写入冲突,所以没有返回,特别是对于网络资源 --- 已实现队列绘制,已解决此问题 */ + if (src.startsWith('.')) { + console.error('[uQRCode]: 本地图片路径仅支持绝对路径!'); + throw new Error('[uQRCode]: local image path only supports absolute path!'); + } else { + uni.getImageInfo({ + src, + success: res => { + resolve(res.path); + }, + fail: err => { + reject(err); + } + }); + } + }); + }); + // #endif + + /* 设置uQRCode实例的canvas上下文 */ + qr.canvasContext = canvasContext; + /* 延时等待页面重新绘制完毕 */ + setTimeout(() => { + /* 从插件获取具体要调用哪一个扩展函数 */ + var plugin = this.plugins.find(p => p.name == qr.style); + var drawCanvasName = plugin ? plugin.drawCanvas : 'drawCanvas'; + /* 虽然qr[drawCanvasName]是直接返回Promise的,但由于js内部this指向问题,故不能直接exec(qr[drawCanvasName])此方式执行,需要改成exec(() => qr[drawCanvasName]())才能正确获取this */ + var drawCanvas; + if (this.queue) { + drawCanvas = () => queueDraw.exec(() => qr[drawCanvasName]()); + // drawCanvas = () => queueDraw.exec(() => new Promise((resolve, reject) => { + // setTimeout(() => { + // qr[drawCanvasName]().then(resolve).catch(reject); + // }, 1000); + // })); + } else { + drawCanvas = () => qr[drawCanvasName](); + } + /* 调用绘制方法将二维码图案绘制到canvas上 */ + drawCanvas() + .then(() => { + if (this.drawDelegate) { + /* 高频重绘纠正 */ + let delegate = this.drawDelegate; + this.drawDelegate = undefined; + delegate(); + } else { + this.drawing = false; + callback.success(); + } + }) + .catch(err => { + console.log(err); + if (this.drawDelegate) { + /* 高频重绘纠正 */ + let delegate = this.drawDelegate; + this.drawDelegate = undefined; + delegate(); + } else { + this.drawing = false; + this.isError = true; + callback.fail(err); + } + }) + .finally(() => { + callback.complete(); + }); + }, 300); + }, + /** + * 生成二维码 + */ + make(callback = {}) { + this.makeExecuted = true; + this.makeing = true; + this.isError = false; + + if (typeof callback.success != 'function') { + callback.success = () => {}; + } + if (typeof callback.fail != 'function') { + callback.fail = () => {}; + } + if (typeof callback.complete != 'function') { + callback.complete = () => {}; + } + + this.resetCanvas(() => { + clearTimeout(this.makeDelegate); + this.makeDelegate = setTimeout(() => { + this.draw({ + success: () => { + setTimeout(() => { + callback.success(); + this.complete(true); + }, 300); + }, + fail: err => { + callback.fail(err); + this.error = err; + this.complete(false, err.errMsg); + }, + complete: () => { + callback.complete(); + this.makeing = false; + } + }); + }, 300); + }); + }, + /** + * 重新生成 + */ + remake(callback) { + this.$emit('change'); + this.make(callback); + }, + /** + * 生成完成 + */ + complete(success = true, errMsg = '') { + if (success) { + this.$emit('complete', { + success + }); + } else { + this.$emit('complete', { + success, + errMsg + }); + } + }, + /** + * 导出临时路径 + */ + toTempFilePath(callback = {}) { + if (typeof callback.success != 'function') { + callback.success = () => {}; + } + if (typeof callback.fail != 'function') { + callback.fail = () => {}; + } + if (typeof callback.complete != 'function') { + callback.complete = () => {}; + } + + if (!this.makeExecuted) { + console.error('[uQRCode]: make() 方法从未调用!请先成功调用 make() 后再进行操作。'); + var err = { + errMsg: '[uQRCode]: make() method has never been executed! please execute make() successfully before operating.' + }; + callback.fail(err); + callback.complete(err); + return; + } + + if (this.isError) { + callback.fail(this.error); + callback.complete(this.error); + return; + } + + if (this.makeing) { + /* 如果还在生成状态,那当前操作将托管到委托,监听生成完成后再通过委托复调当前方法 */ + this.toTempFilePathDelegate = () => { + this.toTempFilePath(callback); + }; + return; + } else { + this.toTempFilePathDelegate = null; + } + + // #ifndef APP-NVUE + if (this.canvasType === '2d') { + // #ifdef MP-WEIXIN + try { + let dataURL = null; + // #ifdef VUE3 + dataURL = toRaw(this.canvas) + .toDataURL(); + // #endif + // #ifndef VUE3 + dataURL = this.canvas.toDataURL(); + // #endif + callback.success({ + tempFilePath: dataURL + }); + callback.complete({ + tempFilePath: dataURL + }); + } catch (e) { + callback.fail(e); + callback.complete(e); + } + // #endif + } else { + uni.canvasToTempFilePath({ + canvasId: this.canvasId, + fileType: this.fileType, + width: Number(this.templateOptions.canvasWidth), + height: Number(this.templateOptions.canvasHeight), + destWidth: Number(this.templateOptions.size), + destHeight: Number(this.templateOptions.size), + success: res => { + callback.success(res); + }, + fail: err => { + callback.fail(err); + }, + complete: () => { + callback.complete(); + } + }, + this + ); + } + // #endif + // #ifdef APP-NVUE + const dpr = uni.getSystemInfoSync().pixelRatio; + this.canvasContext.toTempFilePath( + 0, + 0, + this.templateOptions.canvasWidth * dpr, + this.templateOptions.canvasHeight * dpr, + this.templateOptions.size * dpr, + this.templateOptions.size * dpr, + '', + 1, + res => { + callback.success(res); + callback.complete(res); + } + ); + // #endif + }, + /** + * 保存 + */ + save(callback = {}) { + if (typeof callback.success != 'function') { + callback.success = () => {}; + } + if (typeof callback.fail != 'function') { + callback.fail = () => {}; + } + if (typeof callback.complete != 'function') { + callback.complete = () => {}; + } + + this.toTempFilePath({ + success: res => { + // #ifndef H5 + if (this.canvasType === '2d') { + // #ifdef MP-WEIXIN + /* 需要将 data:image/png;base64, 这段去除 writeFile 才能正常打开文件,否则是损坏文件,无法打开 */ + const reg = new RegExp('^data:image/png;base64,', 'g'); + const dataURL = res.tempFilePath.replace(reg, ''); + const fs = wx.getFileSystemManager(); + const tempFilePath = `${wx.env.USER_DATA_PATH}/${new Date().getTime()}${ + Math.random() + .toString() + .split('.')[1] + }.png`; + fs.writeFile({ + filePath: tempFilePath, // 要写入的文件路径 (本地路径) + data: dataURL, // base64图片 + encoding: 'base64', + success: res1 => { + uni.saveImageToPhotosAlbum({ + filePath: tempFilePath, + success: res2 => { + callback.success(res2); + }, + fail: err2 => { + callback.fail(err2); + }, + complete: () => { + callback.complete(); + } + }); + }, + fail: err => { + callback.fail(err); + }, + complete: () => { + callback.complete(); + } + }); + // #endif + } else { + uni.saveImageToPhotosAlbum({ + filePath: res.tempFilePath, + success: res1 => { + callback.success(res1); + }, + fail: err1 => { + callback.fail(err1); + }, + complete: () => { + callback.complete(); + } + }); + } + // #endif + + // #ifdef H5 + /* 可以在电脑浏览器下载,移动端iOS不行,安卓微信浏览器不行,安卓外部浏览器可以 */ + this.isH5Save = true; + this.tempFilePath = res.tempFilePath; + if (this.h5SaveIsDownload) { + const aEle = document.createElement('a'); + aEle.download = this.h5DownloadName; // 设置下载的文件名,默认是'下载' + aEle.href = res.tempFilePath; + document.body.appendChild(aEle); + aEle.click(); + aEle.remove(); // 下载之后把创建的元素删除 + } + callback.success({ + errMsg: 'ok' + }); + callback.complete({ + errMsg: 'ok' + }); + // #endif + }, + fail: err => { + callback.fail(err); + callback.complete(err); + } + }); + }, + /** + * 注册click事件 + */ + onClick(e) { + this.$emit('click', e); + }, + /** + * 获取实例 + */ + getInstance() { + return instance; + }, + /** + * 注册扩展,组件仅支持注册type为style的drawCanvas扩展 + * @param {Object} plugin + */ + registerStyle(plugin) { + if (plugin.Type != 'style') { + console.warn('[uQRCode]: registerStyle 仅支持注册 style 类型的扩展!'); + return { + errMsg: 'registerStyle 仅支持注册 style 类型的扩展!' + }; + } + if (typeof plugin === 'function') { + this.plugins.push({ + plugin, + name: plugin.Name, + drawCanvas: plugin.DrawCanvas + }); + } + }, + getLoadImage(loadImage) { + var that = this; + if (typeof loadImage == 'function') { + return function(src) { + /* 判断是否是队列加载图片的 */ + if (that.isQueueLoadImage) { + /* 解决iOS APP||NVUE同时绘制多个二维码导致图片丢失需使用队列 */ + return queueLoadImage.exec(() => { + return new Promise((resolve, reject) => { + setTimeout(() => { + const cache = cacheImageList.find(x => x.src == src); + if (cache) { + resolve(cache.img); + } else { + loadImage(src) + .then(img => { + cacheImageList.push({ + src, + img + }); + resolve(img); + }) + .catch(err => { + reject(err); + }); + } + }, 10); + }); + }); + } else { + return loadImage(src); + } + }; + } else { + return function(src) { + return Promise.resolve(src); + }; + } + } + } + }; + + /** + * 对象属性深度替换 + * @param {Object} o 原始对象/默认对象/被替换的对象 + * @param {Object} r 从这个对象里取值替换到o对象里 + * @return {Object} 替换后的新对象 + */ + function deepReplace(o = {}, r = {}, c = false) { + let obj; + if (c) { + // 从源替换 + obj = o; + } else { + // 不替换源,copy一份备份来替换 + obj = { + ...o + }; + } + for (let k in r) { + var vr = r[k]; + if (vr != undefined) { + if (vr.constructor == Object) { + obj[k] = this.deepReplace(obj[k], vr); + } else if (vr.constructor == String && !vr) { + obj[k] = obj[k]; + } else { + obj[k] = vr; + } + } + } + return obj; + } + + + diff --git a/uni_modules/Sansnn-uQRCode/components/uqrcode/uqrcode.vue b/uni_modules/Sansnn-uQRCode/components/uqrcode/uqrcode.vue index 8ae7f425..8ae9598a 100644 --- a/uni_modules/Sansnn-uQRCode/components/uqrcode/uqrcode.vue +++ b/uni_modules/Sansnn-uQRCode/components/uqrcode/uqrcode.vue @@ -1,771 +1,1131 @@ - - - + + - - + // #ifdef VUE3 + import { + toRaw + } from 'vue'; + // #endif + + /* 引入uQRCode核心js */ + import UQRCode from '../../js_sdk/uqrcode/uqrcode'; + + /* 引入nvue所需模块 */ + // #ifdef APP-NVUE + import { + enable, + WeexBridge + } from '../../js_sdk/gcanvas'; + const modal = weex.requireModule('modal'); + // #endif + + /* 引入队列 */ + import { + queueDraw, + queueLoadImage + } from '../../common/queue'; + /* 引入缓存图片 */ + import { + cacheImageList + } from '../../common/cache'; + + let instance = null; + + export default { + name: 'uqrcode', + props: { + /** + * canvas组件id + */ + canvasId: { + type: String, + required: true // canvasId在微信小程序初始值不能为空,created中赋值也不行,必须给一个值,否则挂载组件后无法绘制。不考虑用随机id,uuid + }, + /** + * 二维码内容 + */ + value: { + type: [String, Number] + }, + /** + * 选项 + */ + options: { + type: Object, + default: () => { + return {}; + } + }, + /** + * 二维码大小 + */ + size: { + type: [String, Number], + default: 200 + }, + /** + * 二维码尺寸单位 + */ + sizeUnit: { + type: String, + default: 'px' + }, + /** + * 导出的文件类型 + */ + fileType: { + type: String, + default: 'png' + }, + /** + * 是否初始化组件后就开始生成 + */ + start: { + type: Boolean, + default: true + }, + /** + * 是否数据发生改变自动重绘 + */ + auto: { + type: Boolean, + default: true + }, + /** + * 隐藏组件 + */ + hide: { + type: Boolean, + default: false + }, + /** + * canvas 类型,微信小程序默认使用2d,非2d微信官方已放弃维护,问题比较多 + * 注意:微信小程序type2d手机上正常,PC上微信内打开小程序toDataURL报错,看后期微信官方团队会不会做兼容,不兼容的话只能在自行判断在PC使用非2d,或者直接提示用户请在手机上操作,微信团队的海报中心小程序就是这么做的 + */ + type: { + type: String, + default: () => { + // #ifdef MP-WEIXIN + return '2d'; + // #endif + // #ifndef MP-WEIXIN + return 'normal'; + // #endif + } + }, + /** + * 队列绘制,主要针对NVue端 + */ + queue: { + type: Boolean, + default: false + }, + /** + * 是否队列加载图片,可减少canvas发起的网络资源请求,节省服务器资源 + */ + isQueueLoadImage: { + type: Boolean, + default: false + }, + /** + * loading态 + */ + loading: { + type: Boolean, + default: undefined + }, + /** + * H5保存即自动下载(在支持的环境下),默认false为仅弹层提示用户需要长按图片保存,不会自动下载 + */ + h5SaveIsDownload: { + type: Boolean, + default: false + }, + /** + * H5下载名称 + */ + h5DownloadName: { + type: String, + default: 'uQRCode' + } + }, + data() { + return { + canvas: undefined, + canvasType: undefined, + canvasContext: undefined, + makeDelegate: undefined, + drawDelegate: undefined, + toTempFilePathDelegate: undefined, + makeExecuted: false, + makeing: false, + drawing: false, + isError: false, + error: undefined, + isH5Save: false, + tempFilePath: '', + templateOptions: { + size: 0, + width: 0, // 组件宽度 + height: 0, + canvasWidth: 0, // canvas宽度 + canvasHeight: 0, + canvasTransform: '', + canvasDisplay: false + }, + uqrcodeOptions: { + data: '' + }, + plugins: [], + makeingPattern: [ + [ + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true] + ], + [ + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, true, true, true, false, true, true, true], + [true, true, true, true, true, true, false, true, true, true], + [true, true, true, true, true, true, false, true, true, true] + ], + [ + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, true, true, true, true, false, false, false], + [true, true, true, true, true, true, true, false, false, false], + [true, true, true, true, true, true, true, false, false, false], + [true, true, true, false, false, false, false, true, true, true], + [true, true, true, false, false, false, false, true, true, true] + ], + [ + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, false, false, false, false, false, false, false], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, true, true, true, true, true] + ] + ] + }; + }, + watch: { + type: { + handler(val) { + const types = ['2d']; + if (types.includes(val)) { + this.canvasType = val; + } else { + this.canvasType = undefined; + } + }, + immediate: true + }, + value: { + handler() { + if (this.auto) { + this.remake(); + } + } + }, + size: { + handler() { + if (this.auto) { + this.remake(); + } + } + }, + options: { + handler() { + if (this.auto) { + this.remake(); + } + }, + deep: true + }, + makeing: { + handler(val) { + if (!val) { + if (typeof this.toTempFilePathDelegate === 'function') { + this.toTempFilePathDelegate(); + } + } + } + } + }, + mounted() { + this.templateOptions.size = this.sizeUnit == 'rpx' ? uni.upx2px(this.size) : this.size; + this.templateOptions.width = this.templateOptions.size; + this.templateOptions.height = this.templateOptions.size; + this.templateOptions.canvasWidth = this.templateOptions.size; + this.templateOptions.canvasHeight = this.templateOptions.size; + if (this.canvasType == '2d') { + // #ifndef MP-WEIXIN + this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size / + this.templateOptions.canvasHeight})`; + // #endif + } else { + this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size / + this.templateOptions.canvasHeight})`; + } + if (this.start) { + this.make(); + } + }, + methods: { + /** + * 获取模板选项 + */ + getTemplateOptions() { + var size = this.sizeUnit == 'rpx' ? uni.upx2px(this.size) : this.size; + return deepReplace(this.templateOptions, { + size, + width: size, + height: size + }); + }, + /** + * 获取插件选项 + */ + getUqrcodeOptions() { + return deepReplace(this.options, { + data: String(this.value), + size: Number(this.templateOptions.size) + }); + }, + /** + * 重置画布 + */ + resetCanvas(callback) { + this.templateOptions.canvasDisplay = false; + this.$nextTick(() => { + this.templateOptions.canvasDisplay = true; + this.$nextTick(() => { + callback && callback(); + }); + }); + }, + /** + * 绘制二维码 + */ + async draw(callback = {}, isDrawDelegate = false) { + if (typeof callback.success != 'function') { + callback.success = () => {}; + } + if (typeof callback.fail != 'function') { + callback.fail = () => {}; + } + if (typeof callback.complete != 'function') { + callback.complete = () => {}; + } + + if (this.drawing) { + if (!isDrawDelegate) { + this.drawDelegate = () => { + this.draw(callback, true); + }; + return; + } + } else { + this.drawing = true; + } + + if (!this.canvasId) { + console.error('[uQRCode]: canvasId must be set!'); + this.isError = true; + this.drawing = false; + callback.fail({ + errMsg: '[uQRCode]: canvasId must be set!' + }); + callback.complete({ + errMsg: '[uQRCode]: canvasId must be set!' + }); + return; + } + if (!this.value) { + console.error('[uQRCode]: value must be set!'); + this.isError = true; + this.drawing = false; + callback.fail({ + errMsg: '[uQRCode]: value must be set!' + }); + callback.complete({ + errMsg: '[uQRCode]: value must be set!' + }); + return; + } + + /* 组件数据 */ + this.templateOptions = this.getTemplateOptions(); + /* uQRCode选项 */ + this.uqrcodeOptions = this.getUqrcodeOptions(); + /* 纠错等级兼容字母写法 */ + if (typeof this.uqrcodeOptions.errorCorrectLevel === 'string') { + this.uqrcodeOptions.errorCorrectLevel = UQRCode.errorCorrectLevel[this.uqrcodeOptions.errorCorrectLevel]; + } + /* nvue不支持动态修改gcanvas尺寸,除nvue外,默认使用useDynamicSize */ + // #ifndef APP-NVUE + if (typeof this.options.useDynamicSize === 'undefined') { + this.uqrcodeOptions.useDynamicSize = true; + } + // #endif + // #ifdef APP-NVUE + if (typeof this.options.useDynamicSize === 'undefined') { + this.uqrcodeOptions.useDynamicSize = false; + } + // if (typeof this.options.drawReserve === 'undefined') { + // this.uqrcodeOptions.drawReserve = true; + // } + // #endif + + /* 获取uQRCode实例 */ + const qr = instance = new UQRCode(); + /* 注册扩展 */ + this.plugins.forEach(p => qr.register(p.plugin)); + /* 设置uQRCode选项 */ + qr.setOptions(this.uqrcodeOptions); + /* 调用制作二维码方法 */ + qr.make(); + + /* 获取canvas上下文 */ + let canvasContext = null; + // #ifndef APP-NVUE + if (this.canvasType === '2d') { + // #ifdef MP-WEIXIN + /* 微信小程序获取canvas2d上下文方式 */ + const canvas = (this.canvas = await new Promise(resolve => { + uni + .createSelectorQuery() + .in(this) // 在组件内使用需要 + .select(`#${this.canvasId}`) + .fields({ + node: true, + size: true + }) + .exec(res => { + resolve(res[0].node); + }); + })); + canvasContext = this.canvasContext = canvas.getContext('2d'); + /* 2d的组件设置宽高与实际canvas绘制宽高不是一个,打个比方,组件size=200,canvas.width设置为100,那么绘制出来就是100=200,组件size=400,canvas.width设置为800,绘制大小还是800=400,所以无需理会下方返回的dynamicSize是多少,按dpr重新赋值给canvas即可 */ + this.templateOptions.canvasWidth = qr.size; + this.templateOptions.canvasHeight = qr.size; + this.templateOptions.canvasTransform = ''; + /* 使用dynamicSize+scale,可以解决小块间出现白线问题,dpr可以解决模糊问题 */ + const dpr = uni.getSystemInfoSync().pixelRatio; + canvas.width = qr.dynamicSize * dpr; + canvas.height = qr.dynamicSize * dpr; + canvasContext.scale(dpr, dpr); + /* 微信小程序获取图像方式 */ + qr.loadImage = this.getLoadImage(function(src) { + /* 小程序下获取网络图片信息需先配置download域名白名单才能生效 */ + return new Promise((resolve, reject) => { + const img = canvas.createImage(); + img.src = src; + img.onload = () => { + resolve(img); + }; + img.onerror = err => { + reject(err); + }; + }); + }); + // #endif + // #ifndef MP-WEIXIN + /* 非微信小程序不支持2d,切换回uniapp获取canvas上下文方式 */ + canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this); + /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */ + this.templateOptions.canvasWidth = qr.dynamicSize; + this.templateOptions.canvasHeight = qr.dynamicSize; + this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size / + this.templateOptions.canvasHeight})`; + /* uniapp获取图像方式 */ + qr.loadImage = this.getLoadImage(function(src) { + return new Promise((resolve, reject) => { + if (src.startsWith('http')) { + uni.getImageInfo({ + src, + success: res => { + resolve(res.path); + }, + fail: err => { + reject(err); + } + }); + } else { + if (src.startsWith('.')) { + console.error('[uQRCode]: 本地图片路径仅支持绝对路径!'); + throw new Error('[uQRCode]: local image path only supports absolute path!'); + } else { + resolve(src); + } + } + }); + }); + // #endif + } else { + /* uniapp获取canvas上下文方式 */ + canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this); + /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */ + this.templateOptions.canvasWidth = qr.dynamicSize; + this.templateOptions.canvasHeight = qr.dynamicSize; + this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size / + this.templateOptions.canvasHeight})`; + /* uniapp获取图像方式 */ + qr.loadImage = this.getLoadImage(function(src) { + return new Promise((resolve, reject) => { + /* getImageInfo在微信小程序的bug:本地路径返回路径会把开头的/或../移除,导致路径错误,解决方法:限制只能使用绝对路径 */ + if (src.startsWith('http')) { + uni.getImageInfo({ + src, + success: res => { + resolve(res.path); + }, + fail: err => { + reject(err); + } + }); + } else { + if (src.startsWith('.')) { + console.error('[uQRCode]: 本地图片路径仅支持绝对路径!'); + throw new Error('[uQRCode]: local image path only supports absolute path!'); + } else { + resolve(src); + } + } + }); + }); + } + // #endif + // #ifdef APP-NVUE + /* NVue获取canvas上下文方式 */ + const gcanvas = this.$refs['gcanvas']; + const canvas = enable(gcanvas, { + bridge: WeexBridge + }); + canvasContext = this.canvasContext = canvas.getContext('2d'); + /* NVue获取图像方式 */ + qr.loadImage = this.getLoadImage(function(src) { + return new Promise((resolve, reject) => { + /* getImageInfo在nvue的bug:获取同一个路径的图片信息,同一时间第一次获取成功,后续失败,猜测是写入本地时产生文件写入冲突,所以没有返回,特别是对于网络资源 --- 已实现队列绘制,已解决此问题 */ + if (src.startsWith('.')) { + console.error('[uQRCode]: 本地图片路径仅支持绝对路径!'); + throw new Error('[uQRCode]: local image path only supports absolute path!'); + } else { + uni.getImageInfo({ + src, + success: res => { + resolve(res.path); + }, + fail: err => { + reject(err); + } + }); + } + }); + }); + // #endif + + /* 设置uQRCode实例的canvas上下文 */ + qr.canvasContext = canvasContext; + /* 延时等待页面重新绘制完毕 */ + setTimeout(() => { + /* 从插件获取具体要调用哪一个扩展函数 */ + var plugin = this.plugins.find(p => p.name == qr.style); + var drawCanvasName = plugin ? plugin.drawCanvas : 'drawCanvas'; + /* 虽然qr[drawCanvasName]是直接返回Promise的,但由于js内部this指向问题,故不能直接exec(qr[drawCanvasName])此方式执行,需要改成exec(() => qr[drawCanvasName]())才能正确获取this */ + var drawCanvas; + if (this.queue) { + drawCanvas = () => queueDraw.exec(() => qr[drawCanvasName]()); + // drawCanvas = () => queueDraw.exec(() => new Promise((resolve, reject) => { + // setTimeout(() => { + // qr[drawCanvasName]().then(resolve).catch(reject); + // }, 1000); + // })); + } else { + drawCanvas = () => qr[drawCanvasName](); + } + /* 调用绘制方法将二维码图案绘制到canvas上 */ + drawCanvas() + .then(() => { + if (this.drawDelegate) { + /* 高频重绘纠正 */ + let delegate = this.drawDelegate; + this.drawDelegate = undefined; + delegate(); + } else { + this.drawing = false; + callback.success(); + } + }) + .catch(err => { + console.log(err); + if (this.drawDelegate) { + /* 高频重绘纠正 */ + let delegate = this.drawDelegate; + this.drawDelegate = undefined; + delegate(); + } else { + this.drawing = false; + this.isError = true; + callback.fail(err); + } + }) + .finally(() => { + callback.complete(); + }); + }, 300); + }, + /** + * 生成二维码 + */ + make(callback = {}) { + this.makeExecuted = true; + this.makeing = true; + this.isError = false; + + if (typeof callback.success != 'function') { + callback.success = () => {}; + } + if (typeof callback.fail != 'function') { + callback.fail = () => {}; + } + if (typeof callback.complete != 'function') { + callback.complete = () => {}; + } + + this.resetCanvas(() => { + clearTimeout(this.makeDelegate); + this.makeDelegate = setTimeout(() => { + this.draw({ + success: () => { + setTimeout(() => { + callback.success(); + this.complete(true); + }, 300); + }, + fail: err => { + callback.fail(err); + this.error = err; + this.complete(false, err.errMsg); + }, + complete: () => { + callback.complete(); + this.makeing = false; + } + }); + }, 300); + }); + }, + /** + * 重新生成 + */ + remake(callback) { + this.$emit('change'); + this.make(callback); + }, + /** + * 生成完成 + */ + complete(success = true, errMsg = '') { + if (success) { + this.$emit('complete', { + success + }); + } else { + this.$emit('complete', { + success, + errMsg + }); + } + }, + /** + * 导出临时路径 + */ + toTempFilePath(callback = {}) { + if (typeof callback.success != 'function') { + callback.success = () => {}; + } + if (typeof callback.fail != 'function') { + callback.fail = () => {}; + } + if (typeof callback.complete != 'function') { + callback.complete = () => {}; + } + + if (!this.makeExecuted) { + console.error('[uQRCode]: make() 方法从未调用!请先成功调用 make() 后再进行操作。'); + var err = { + errMsg: '[uQRCode]: make() method has never been executed! please execute make() successfully before operating.' + }; + callback.fail(err); + callback.complete(err); + return; + } + + if (this.isError) { + callback.fail(this.error); + callback.complete(this.error); + return; + } + + if (this.makeing) { + /* 如果还在生成状态,那当前操作将托管到委托,监听生成完成后再通过委托复调当前方法 */ + this.toTempFilePathDelegate = () => { + this.toTempFilePath(callback); + }; + return; + } else { + this.toTempFilePathDelegate = null; + } + + // #ifndef APP-NVUE + if (this.canvasType === '2d') { + // #ifdef MP-WEIXIN + try { + let dataURL = null; + // #ifdef VUE3 + dataURL = toRaw(this.canvas) + .toDataURL(); + // #endif + // #ifndef VUE3 + dataURL = this.canvas.toDataURL(); + // #endif + callback.success({ + tempFilePath: dataURL + }); + callback.complete({ + tempFilePath: dataURL + }); + } catch (e) { + callback.fail(e); + callback.complete(e); + } + // #endif + } else { + uni.canvasToTempFilePath({ + canvasId: this.canvasId, + fileType: this.fileType, + width: Number(this.templateOptions.canvasWidth), + height: Number(this.templateOptions.canvasHeight), + destWidth: Number(this.templateOptions.size), + destHeight: Number(this.templateOptions.size), + success: res => { + callback.success(res); + }, + fail: err => { + callback.fail(err); + }, + complete: () => { + callback.complete(); + } + }, + this + ); + } + // #endif + // #ifdef APP-NVUE + const dpr = uni.getSystemInfoSync().pixelRatio; + this.canvasContext.toTempFilePath( + 0, + 0, + this.templateOptions.canvasWidth * dpr, + this.templateOptions.canvasHeight * dpr, + this.templateOptions.size * dpr, + this.templateOptions.size * dpr, + '', + 1, + res => { + callback.success(res); + callback.complete(res); + } + ); + // #endif + }, + /** + * 保存 + */ + save(callback = {}) { + if (typeof callback.success != 'function') { + callback.success = () => {}; + } + if (typeof callback.fail != 'function') { + callback.fail = () => {}; + } + if (typeof callback.complete != 'function') { + callback.complete = () => {}; + } + + this.toTempFilePath({ + success: res => { + // #ifndef H5 + if (this.canvasType === '2d') { + // #ifdef MP-WEIXIN + /* 需要将 data:image/png;base64, 这段去除 writeFile 才能正常打开文件,否则是损坏文件,无法打开 */ + const reg = new RegExp('^data:image/png;base64,', 'g'); + const dataURL = res.tempFilePath.replace(reg, ''); + const fs = wx.getFileSystemManager(); + const tempFilePath = `${wx.env.USER_DATA_PATH}/${new Date().getTime()}${ + Math.random() + .toString() + .split('.')[1] + }.png`; + fs.writeFile({ + filePath: tempFilePath, // 要写入的文件路径 (本地路径) + data: dataURL, // base64图片 + encoding: 'base64', + success: res1 => { + uni.saveImageToPhotosAlbum({ + filePath: tempFilePath, + success: res2 => { + callback.success(res2); + }, + fail: err2 => { + callback.fail(err2); + }, + complete: () => { + callback.complete(); + } + }); + }, + fail: err => { + callback.fail(err); + }, + complete: () => { + callback.complete(); + } + }); + // #endif + } else { + uni.saveImageToPhotosAlbum({ + filePath: res.tempFilePath, + success: res1 => { + callback.success(res1); + }, + fail: err1 => { + callback.fail(err1); + }, + complete: () => { + callback.complete(); + } + }); + } + // #endif + + // #ifdef H5 + /* 可以在电脑浏览器下载,移动端iOS不行,安卓微信浏览器不行,安卓外部浏览器可以 */ + this.isH5Save = true; + this.tempFilePath = res.tempFilePath; + if (this.h5SaveIsDownload) { + const aEle = document.createElement('a'); + aEle.download = this.h5DownloadName; // 设置下载的文件名,默认是'下载' + aEle.href = res.tempFilePath; + document.body.appendChild(aEle); + aEle.click(); + aEle.remove(); // 下载之后把创建的元素删除 + } + callback.success({ + errMsg: 'ok' + }); + callback.complete({ + errMsg: 'ok' + }); + // #endif + }, + fail: err => { + callback.fail(err); + callback.complete(err); + } + }); + }, + /** + * 注册click事件 + */ + onClick(e) { + this.$emit('click', e); + }, + /** + * 获取实例 + */ + getInstance() { + return instance; + }, + /** + * 注册扩展,组件仅支持注册type为style的drawCanvas扩展 + * @param {Object} plugin + */ + registerStyle(plugin) { + if (plugin.Type != 'style') { + console.warn('[uQRCode]: registerStyle 仅支持注册 style 类型的扩展!'); + return { + errMsg: 'registerStyle 仅支持注册 style 类型的扩展!' + }; + } + if (typeof plugin === 'function') { + this.plugins.push({ + plugin, + name: plugin.Name, + drawCanvas: plugin.DrawCanvas + }); + } + }, + getLoadImage(loadImage) { + var that = this; + if (typeof loadImage == 'function') { + return function(src) { + /* 判断是否是队列加载图片的 */ + if (that.isQueueLoadImage) { + /* 解决iOS APP||NVUE同时绘制多个二维码导致图片丢失需使用队列 */ + return queueLoadImage.exec(() => { + return new Promise((resolve, reject) => { + setTimeout(() => { + const cache = cacheImageList.find(x => x.src == src); + if (cache) { + resolve(cache.img); + } else { + loadImage(src) + .then(img => { + cacheImageList.push({ + src, + img + }); + resolve(img); + }) + .catch(err => { + reject(err); + }); + } + }, 10); + }); + }); + } else { + return loadImage(src); + } + }; + } else { + return function(src) { + return Promise.resolve(src); + }; + } + } + } + }; + + /** + * 对象属性深度替换 + * @param {Object} o 原始对象/默认对象/被替换的对象 + * @param {Object} r 从这个对象里取值替换到o对象里 + * @return {Object} 替换后的新对象 + */ + function deepReplace(o = {}, r = {}, c = false) { + let obj; + if (c) { + // 从源替换 + obj = o; + } else { + // 不替换源,copy一份备份来替换 + obj = { + ...o + }; + } + for (let k in r) { + var vr = r[k]; + if (vr != undefined) { + if (vr.constructor == Object) { + obj[k] = this.deepReplace(obj[k], vr); + } else if (vr.constructor == String && !vr) { + obj[k] = obj[k]; + } else { + obj[k] = vr; + } + } + } + return obj; + } + + + diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/bridge/bridge-weex.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/bridge/bridge-weex.js index 27086ec3..cec30ab8 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/bridge/bridge-weex.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/bridge/bridge-weex.js @@ -1,241 +1,241 @@ -const isWeex = typeof WXEnvironment !== 'undefined'; -const isWeexIOS = isWeex && /ios/i.test(WXEnvironment.platform); -const isWeexAndroid = isWeex && !isWeexIOS; - -import GLmethod from '../context-webgl/GLmethod'; - -const GCanvasModule = - (typeof weex !== 'undefined' && weex.requireModule) ? (weex.requireModule('gcanvas')) : - (typeof __weex_require__ !== 'undefined') ? (__weex_require__('@weex-module/gcanvas')) : {}; - -let isDebugging = false; - -let isComboDisabled = false; - -const logCommand = (function () { - const methodQuery = []; - Object.keys(GLmethod).forEach(key => { - methodQuery[GLmethod[key]] = key; - }) - const queryMethod = (id) => { - return methodQuery[parseInt(id)] || 'NotFoundMethod'; - } - const logCommand = (id, cmds) => { - const mId = cmds.split(',')[0]; - const mName = queryMethod(mId); - console.log(`=== callNative - componentId:${id}; method: ${mName}; cmds: ${cmds}`); - } - return logCommand; -})(); - -function joinArray(arr, sep) { - let res = ''; - for (let i = 0; i < arr.length; i++) { - if (i !== 0) { - res += sep; - } - res += arr[i]; - } - return res; -} - -const commandsCache = {} - -const GBridge = { - - callEnable: (ref, configArray) => { - - commandsCache[ref] = []; - - return GCanvasModule.enable({ - componentId: ref, - config: configArray - }); - }, - - callEnableDebug: () => { - isDebugging = true; - }, - - callEnableDisableCombo: () => { - isComboDisabled = true; - }, - - callSetContextType: function (componentId, context_type) { - GCanvasModule.setContextType(context_type, componentId); - }, - - callReset: function(id){ - GCanvasModule.resetComponent && canvasModule.resetComponent(componentId); - }, - - render: isWeexIOS ? function (componentId) { - return GCanvasModule.extendCallNative({ - contextId: componentId, - type: 0x60000001 - }); - } : function (componentId) { - return callGCanvasLinkNative(componentId, 0x60000001, 'render'); - }, - - render2d: isWeexIOS ? function (componentId, commands, callback) { - - if (isDebugging) { - console.log('>>> >>> render2d ==='); - console.log('>>> commands: ' + commands); - } - - GCanvasModule.render([commands, callback?true:false], componentId, callback); - - } : function (componentId, commands,callback) { - - if (isDebugging) { - console.log('>>> >>> render2d ==='); - console.log('>>> commands: ' + commands); - } - - callGCanvasLinkNative(componentId, 0x20000001, commands); - if(callback){ - callback(); - } - }, - - callExtendCallNative: isWeexIOS ? function (componentId, cmdArgs) { - - throw 'should not be here anymore ' + cmdArgs; - - } : function (componentId, cmdArgs) { - - throw 'should not be here anymore ' + cmdArgs; - - }, - - - flushNative: isWeexIOS ? function (componentId) { - - const cmdArgs = joinArray(commandsCache[componentId], ';'); - commandsCache[componentId] = []; - - if (isDebugging) { - console.log('>>> >>> flush native ==='); - console.log('>>> commands: ' + cmdArgs); - } - - const result = GCanvasModule.extendCallNative({ - "contextId": componentId, - "type": 0x60000000, - "args": cmdArgs - }); - - const res = result && result.result; - - if (isDebugging) { - console.log('>>> result: ' + res); - } - - return res; - - } : function (componentId) { - - const cmdArgs = joinArray(commandsCache[componentId], ';'); - commandsCache[componentId] = []; - - if (isDebugging) { - console.log('>>> >>> flush native ==='); - console.log('>>> commands: ' + cmdArgs); - } - - const result = callGCanvasLinkNative(componentId, 0x60000000, cmdArgs); - - if (isDebugging) { - console.log('>>> result: ' + result); - } - - return result; - }, - - callNative: function (componentId, cmdArgs, cache) { - - if (isDebugging) { - logCommand(componentId, cmdArgs); - } - - commandsCache[componentId].push(cmdArgs); - - if (!cache || isComboDisabled) { - return GBridge.flushNative(componentId); - } else { - return undefined; - } - }, - - texImage2D(componentId, ...args) { - if (isWeexIOS) { - if (args.length === 6) { - const [target, level, internalformat, format, type, image] = args; - GBridge.callNative( - componentId, - GLmethod.texImage2D + ',' + 6 + ',' + target + ',' + level + ',' + internalformat + ',' + format + ',' + type + ',' + image.src - ) - } else if (args.length === 9) { - const [target, level, internalformat, width, height, border, format, type, image] = args; - GBridge.callNative( - componentId, - GLmethod.texImage2D + ',' + 9 + ',' + target + ',' + level + ',' + internalformat + ',' + width + ',' + height + ',' + border + ',' + - + format + ',' + type + ',' + (image ? image.src : 0) - ) - } - } else if (isWeexAndroid) { - if (args.length === 6) { - const [target, level, internalformat, format, type, image] = args; - GCanvasModule.texImage2D(componentId, target, level, internalformat, format, type, image.src); - } else if (args.length === 9) { - const [target, level, internalformat, width, height, border, format, type, image] = args; - GCanvasModule.texImage2D(componentId, target, level, internalformat, width, height, border, format, type, (image ? image.src : 0)); - } - } - }, - - texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image) { - if (isWeexIOS) { - if (arguments.length === 8) { - GBridge.callNative( - componentId, - GLmethod.texSubImage2D + ',' + 6 + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset, + ',' + format + ',' + type + ',' + image.src - ) - } - } else if (isWeexAndroid) { - GCanvasModule.texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image.src); - } - }, - - bindImageTexture(componentId, src, imageId) { - GCanvasModule.bindImageTexture([src, imageId], componentId); - }, - - perloadImage([url, id], callback) { - GCanvasModule.preLoadImage([url, id], function (image) { - image.url = url; - image.id = id; - callback(image); - }); - }, - - measureText(text, fontStyle, componentId) { - return GCanvasModule.measureText([text, fontStyle], componentId); - }, - - getImageData (componentId, x, y, w, h, callback) { - GCanvasModule.getImageData([x, y,w,h],componentId,callback); - }, - - putImageData (componentId, data, x, y, w, h, callback) { - GCanvasModule.putImageData([x, y,w,h,data],componentId,callback); - }, - - toTempFilePath(componentId, x, y, width, height, destWidth, destHeight, fileType, quality, callback){ - GCanvasModule.toTempFilePath([x, y, width,height, destWidth, destHeight, fileType, quality], componentId, callback); - } -} - +const isWeex = typeof WXEnvironment !== 'undefined'; +const isWeexIOS = isWeex && /ios/i.test(WXEnvironment.platform); +const isWeexAndroid = isWeex && !isWeexIOS; + +import GLmethod from '../context-webgl/GLmethod'; + +const GCanvasModule = + (typeof weex !== 'undefined' && weex.requireModule) ? (weex.requireModule('gcanvas')) : + (typeof __weex_require__ !== 'undefined') ? (__weex_require__('@weex-module/gcanvas')) : {}; + +let isDebugging = false; + +let isComboDisabled = false; + +const logCommand = (function () { + const methodQuery = []; + Object.keys(GLmethod).forEach(key => { + methodQuery[GLmethod[key]] = key; + }) + const queryMethod = (id) => { + return methodQuery[parseInt(id)] || 'NotFoundMethod'; + } + const logCommand = (id, cmds) => { + const mId = cmds.split(',')[0]; + const mName = queryMethod(mId); + console.log(`=== callNative - componentId:${id}; method: ${mName}; cmds: ${cmds}`); + } + return logCommand; +})(); + +function joinArray(arr, sep) { + let res = ''; + for (let i = 0; i < arr.length; i++) { + if (i !== 0) { + res += sep; + } + res += arr[i]; + } + return res; +} + +const commandsCache = {} + +const GBridge = { + + callEnable: (ref, configArray) => { + + commandsCache[ref] = []; + + return GCanvasModule.enable({ + componentId: ref, + config: configArray + }); + }, + + callEnableDebug: () => { + isDebugging = true; + }, + + callEnableDisableCombo: () => { + isComboDisabled = true; + }, + + callSetContextType: function (componentId, context_type) { + GCanvasModule.setContextType(context_type, componentId); + }, + + callReset: function(id){ + GCanvasModule.resetComponent && canvasModule.resetComponent(componentId); + }, + + render: isWeexIOS ? function (componentId) { + return GCanvasModule.extendCallNative({ + contextId: componentId, + type: 0x60000001 + }); + } : function (componentId) { + return callGCanvasLinkNative(componentId, 0x60000001, 'render'); + }, + + render2d: isWeexIOS ? function (componentId, commands, callback) { + + if (isDebugging) { + console.log('>>> >>> render2d ==='); + console.log('>>> commands: ' + commands); + } + + GCanvasModule.render([commands, callback?true:false], componentId, callback); + + } : function (componentId, commands,callback) { + + if (isDebugging) { + console.log('>>> >>> render2d ==='); + console.log('>>> commands: ' + commands); + } + + callGCanvasLinkNative(componentId, 0x20000001, commands); + if(callback){ + callback(); + } + }, + + callExtendCallNative: isWeexIOS ? function (componentId, cmdArgs) { + + throw 'should not be here anymore ' + cmdArgs; + + } : function (componentId, cmdArgs) { + + throw 'should not be here anymore ' + cmdArgs; + + }, + + + flushNative: isWeexIOS ? function (componentId) { + + const cmdArgs = joinArray(commandsCache[componentId], ';'); + commandsCache[componentId] = []; + + if (isDebugging) { + console.log('>>> >>> flush native ==='); + console.log('>>> commands: ' + cmdArgs); + } + + const result = GCanvasModule.extendCallNative({ + "contextId": componentId, + "type": 0x60000000, + "args": cmdArgs + }); + + const res = result && result.result; + + if (isDebugging) { + console.log('>>> result: ' + res); + } + + return res; + + } : function (componentId) { + + const cmdArgs = joinArray(commandsCache[componentId], ';'); + commandsCache[componentId] = []; + + if (isDebugging) { + console.log('>>> >>> flush native ==='); + console.log('>>> commands: ' + cmdArgs); + } + + const result = callGCanvasLinkNative(componentId, 0x60000000, cmdArgs); + + if (isDebugging) { + console.log('>>> result: ' + result); + } + + return result; + }, + + callNative: function (componentId, cmdArgs, cache) { + + if (isDebugging) { + logCommand(componentId, cmdArgs); + } + + commandsCache[componentId].push(cmdArgs); + + if (!cache || isComboDisabled) { + return GBridge.flushNative(componentId); + } else { + return undefined; + } + }, + + texImage2D(componentId, ...args) { + if (isWeexIOS) { + if (args.length === 6) { + const [target, level, internalformat, format, type, image] = args; + GBridge.callNative( + componentId, + GLmethod.texImage2D + ',' + 6 + ',' + target + ',' + level + ',' + internalformat + ',' + format + ',' + type + ',' + image.src + ) + } else if (args.length === 9) { + const [target, level, internalformat, width, height, border, format, type, image] = args; + GBridge.callNative( + componentId, + GLmethod.texImage2D + ',' + 9 + ',' + target + ',' + level + ',' + internalformat + ',' + width + ',' + height + ',' + border + ',' + + + format + ',' + type + ',' + (image ? image.src : 0) + ) + } + } else if (isWeexAndroid) { + if (args.length === 6) { + const [target, level, internalformat, format, type, image] = args; + GCanvasModule.texImage2D(componentId, target, level, internalformat, format, type, image.src); + } else if (args.length === 9) { + const [target, level, internalformat, width, height, border, format, type, image] = args; + GCanvasModule.texImage2D(componentId, target, level, internalformat, width, height, border, format, type, (image ? image.src : 0)); + } + } + }, + + texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image) { + if (isWeexIOS) { + if (arguments.length === 8) { + GBridge.callNative( + componentId, + GLmethod.texSubImage2D + ',' + 6 + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset, + ',' + format + ',' + type + ',' + image.src + ) + } + } else if (isWeexAndroid) { + GCanvasModule.texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image.src); + } + }, + + bindImageTexture(componentId, src, imageId) { + GCanvasModule.bindImageTexture([src, imageId], componentId); + }, + + perloadImage([url, id], callback) { + GCanvasModule.preLoadImage([url, id], function (image) { + image.url = url; + image.id = id; + callback(image); + }); + }, + + measureText(text, fontStyle, componentId) { + return GCanvasModule.measureText([text, fontStyle], componentId); + }, + + getImageData (componentId, x, y, w, h, callback) { + GCanvasModule.getImageData([x, y,w,h],componentId,callback); + }, + + putImageData (componentId, data, x, y, w, h, callback) { + GCanvasModule.putImageData([x, y,w,h,data],componentId,callback); + }, + + toTempFilePath(componentId, x, y, width, height, destWidth, destHeight, fileType, quality, callback){ + GCanvasModule.toTempFilePath([x, y, width,height, destWidth, destHeight, fileType, quality], componentId, callback); + } +} + export default GBridge; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleLinearGradient.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleLinearGradient.js index 3e7f03ad..97e9f0fb 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleLinearGradient.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleLinearGradient.js @@ -1,18 +1,18 @@ -class FillStyleLinearGradient { - - constructor(x0, y0, x1, y1) { - this._start_pos = { _x: x0, _y: y0 }; - this._end_pos = { _x: x1, _y: y1 }; - this._stop_count = 0; - this._stops = [0, 0, 0, 0, 0]; - } - - addColorStop = function (pos, color) { - if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) { - this._stops[this._stop_count] = { _pos: pos, _color: color }; - this._stop_count++; - } - } -} - +class FillStyleLinearGradient { + + constructor(x0, y0, x1, y1) { + this._start_pos = { _x: x0, _y: y0 }; + this._end_pos = { _x: x1, _y: y1 }; + this._stop_count = 0; + this._stops = [0, 0, 0, 0, 0]; + } + + addColorStop = function (pos, color) { + if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) { + this._stops[this._stop_count] = { _pos: pos, _color: color }; + this._stop_count++; + } + } +} + export default FillStyleLinearGradient; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStylePattern.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStylePattern.js index 6e4f646f..6c9c9218 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStylePattern.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStylePattern.js @@ -1,8 +1,8 @@ -class FillStylePattern { - constructor(img, pattern) { - this._style = pattern; - this._img = img; - } -} - +class FillStylePattern { + constructor(img, pattern) { + this._style = pattern; + this._img = img; + } +} + export default FillStylePattern; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleRadialGradient.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleRadialGradient.js index 77905965..e6ab4f39 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleRadialGradient.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleRadialGradient.js @@ -1,17 +1,17 @@ -class FillStyleRadialGradient { - constructor(x0, y0, r0, x1, y1, r1) { - this._start_pos = { _x: x0, _y: y0, _r: r0 }; - this._end_pos = { _x: x1, _y: y1, _r: r1 }; - this._stop_count = 0; - this._stops = [0, 0, 0, 0, 0]; - } - - addColorStop(pos, color) { - if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) { - this._stops[this._stop_count] = { _pos: pos, _color: color }; - this._stop_count++; - } - } -} - +class FillStyleRadialGradient { + constructor(x0, y0, r0, x1, y1, r1) { + this._start_pos = { _x: x0, _y: y0, _r: r0 }; + this._end_pos = { _x: x1, _y: y1, _r: r1 }; + this._stop_count = 0; + this._stops = [0, 0, 0, 0, 0]; + } + + addColorStop(pos, color) { + if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) { + this._stops[this._stop_count] = { _pos: pos, _color: color }; + this._stop_count++; + } + } +} + export default FillStyleRadialGradient; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/RenderingContext.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/RenderingContext.js index e6b8f48e..3e663ee7 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/RenderingContext.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/RenderingContext.js @@ -1,666 +1,666 @@ -import FillStylePattern from './FillStylePattern'; -import FillStyleLinearGradient from './FillStyleLinearGradient'; -import FillStyleRadialGradient from './FillStyleRadialGradient'; -import GImage from '../env/image.js'; -import { - ArrayBufferToBase64, - Base64ToUint8ClampedArray -} from '../env/tool.js'; - -export default class CanvasRenderingContext2D { - - _drawCommands = ''; - - _globalAlpha = 1.0; - - _fillStyle = 'rgb(0,0,0)'; - _strokeStyle = 'rgb(0,0,0)'; - - _lineWidth = 1; - _lineCap = 'butt'; - _lineJoin = 'miter'; - - _miterLimit = 10; - - _globalCompositeOperation = 'source-over'; - - _textAlign = 'start'; - _textBaseline = 'alphabetic'; - - _font = '10px sans-serif'; - - _savedGlobalAlpha = []; - - timer = null; - componentId = null; - - _notCommitDrawImageCache = []; - _needRedrawImageCache = []; - _redrawCommands = ''; - _autoSaveContext = true; - // _imageMap = new GHashMap(); - // _textureMap = new GHashMap(); - - constructor() { - this.className = 'CanvasRenderingContext2D'; - //this.save() - } - - setFillStyle(value) { - this.fillStyle = value; - } - - set fillStyle(value) { - this._fillStyle = value; - - if (typeof(value) == 'string') { - this._drawCommands = this._drawCommands.concat("F" + value + ";"); - } else if (value instanceof FillStylePattern) { - const image = value._img; - if (!image.complete) { - image.onload = () => { - var index = this._needRedrawImageCache.indexOf(image); - if (index > -1) { - this._needRedrawImageCache.splice(index, 1); - CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); - this._redrawflush(true); - } - } - this._notCommitDrawImageCache.push(image); - } else { - CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); - } - - //CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); - this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";"); - } else if (value instanceof FillStyleLinearGradient) { - var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + - value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + - value._stop_count; - for (var i = 0; i < value._stop_count; ++i) { - command += ("," + value._stops[i]._pos + "," + value._stops[i]._color); - } - this._drawCommands = this._drawCommands.concat(command + ";"); - } else if (value instanceof FillStyleRadialGradient) { - var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r - .toFixed(2) + "," + - value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + value._end_pos._r.toFixed(2) + "," + - value._stop_count; - for (var i = 0; i < value._stop_count; ++i) { - command += ("," + value._stops[i]._pos + "," + value._stops[i]._color); - } - this._drawCommands = this._drawCommands.concat(command + ";"); - } - } - - get fillStyle() { - return this._fillStyle; - } - - get globalAlpha() { - return this._globalAlpha; - } - - setGlobalAlpha(value) { - this.globalAlpha = value; - } - - set globalAlpha(value) { - this._globalAlpha = value; - this._drawCommands = this._drawCommands.concat("a" + value.toFixed(2) + ";"); - } - - - get strokeStyle() { - return this._strokeStyle; - } - - setStrokeStyle(value) { - this.strokeStyle = value; - } - - set strokeStyle(value) { - - this._strokeStyle = value; - - if (typeof(value) == 'string') { - this._drawCommands = this._drawCommands.concat("S" + value + ";"); - } else if (value instanceof FillStylePattern) { - CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); - this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";"); - } else if (value instanceof FillStyleLinearGradient) { - var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + - value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + - value._stop_count; - - for (var i = 0; i < value._stop_count; ++i) { - command += ("," + value._stops[i]._pos + "," + value._stops[i]._color); - } - this._drawCommands = this._drawCommands.concat(command + ";"); - } else if (value instanceof FillStyleRadialGradient) { - var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r - .toFixed(2) + "," + - value._end_pos._x.toFixed(2) + "," + value._end_pos._y + ",".toFixed(2) + value._end_pos._r.toFixed(2) + "," + - value._stop_count; - - for (var i = 0; i < value._stop_count; ++i) { - command += ("," + value._stops[i]._pos + "," + value._stops[i]._color); - } - this._drawCommands = this._drawCommands.concat(command + ";"); - } - } - - get lineWidth() { - return this._lineWidth; - } - - setLineWidth(value) { - this.lineWidth = value; - } - - set lineWidth(value) { - this._lineWidth = value; - this._drawCommands = this._drawCommands.concat("W" + value + ";"); - } - - get lineCap() { - return this._lineCap; - } - - setLineCap(value) { - this.lineCap = value; - } - - set lineCap(value) { - this._lineCap = value; - this._drawCommands = this._drawCommands.concat("C" + value + ";"); - } - - get lineJoin() { - return this._lineJoin; - } - - setLineJoin(value) { - this.lineJoin = value - } - - set lineJoin(value) { - this._lineJoin = value; - this._drawCommands = this._drawCommands.concat("J" + value + ";"); - } - - get miterLimit() { - return this._miterLimit; - } - - setMiterLimit(value) { - this.miterLimit = value - } - - set miterLimit(value) { - this._miterLimit = value; - this._drawCommands = this._drawCommands.concat("M" + value + ";"); - } - - get globalCompositeOperation() { - return this._globalCompositeOperation; - } - - set globalCompositeOperation(value) { - - this._globalCompositeOperation = value; - let mode = 0; - switch (value) { - case "source-over": - mode = 0; - break; - case "source-atop": - mode = 5; - break; - case "source-in": - mode = 0; - break; - case "source-out": - mode = 2; - break; - case "destination-over": - mode = 4; - break; - case "destination-atop": - mode = 4; - break; - case "destination-in": - mode = 4; - break; - case "destination-out": - mode = 3; - break; - case "lighter": - mode = 1; - break; - case "copy": - mode = 2; - break; - case "xor": - mode = 6; - break; - default: - mode = 0; - } - - this._drawCommands = this._drawCommands.concat("B" + mode + ";"); - } - - get textAlign() { - return this._textAlign; - } - - setTextAlign(value) { - this.textAlign = value - } - - set textAlign(value) { - - this._textAlign = value; - let Align = 0; - switch (value) { - case "start": - Align = 0; - break; - case "end": - Align = 1; - break; - case "left": - Align = 2; - break; - case "center": - Align = 3; - break; - case "right": - Align = 4; - break; - default: - Align = 0; - } - - this._drawCommands = this._drawCommands.concat("A" + Align + ";"); - } - - get textBaseline() { - return this._textBaseline; - } - - setTextBaseline(value) { - this.textBaseline = value - } - - set textBaseline(value) { - this._textBaseline = value; - let baseline = 0; - switch (value) { - case "alphabetic": - baseline = 0; - break; - case "middle": - baseline = 1; - break; - case "top": - baseline = 2; - break; - case "hanging": - baseline = 3; - break; - case "bottom": - baseline = 4; - break; - case "ideographic": - baseline = 5; - break; - default: - baseline = 0; - break; - } - - this._drawCommands = this._drawCommands.concat("E" + baseline + ";"); - } - - get font() { - return this._font; - } - - setFontSize(size) { - var str = this._font; - var strs = str.trim().split(/\s+/); - for (var i = 0; i < strs.length; i++) { - var values = ["normal", "italic", "oblique", "normal", "small-caps", "normal", "bold", - "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900", - "normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", - "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" - ]; - - if (-1 == values.indexOf(strs[i].trim())) { - if (typeof size === 'string') { - strs[i] = size; - } else if (typeof size === 'number') { - strs[i] = String(size) + 'px'; - } - break; - } - } - this.font = strs.join(" "); - } - - set font(value) { - this._font = value; - this._drawCommands = this._drawCommands.concat("j" + value + ";"); - } - - setTransform(a, b, c, d, tx, ty) { - this._drawCommands = this._drawCommands.concat("t" + - (a === 1 ? "1" : a.toFixed(2)) + "," + - (b === 0 ? "0" : b.toFixed(2)) + "," + - (c === 0 ? "0" : c.toFixed(2)) + "," + - (d === 1 ? "1" : d.toFixed(2)) + "," + tx.toFixed(2) + "," + ty.toFixed(2) + ";"); - } - - transform(a, b, c, d, tx, ty) { - this._drawCommands = this._drawCommands.concat("f" + - (a === 1 ? "1" : a.toFixed(2)) + "," + - (b === 0 ? "0" : b.toFixed(2)) + "," + - (c === 0 ? "0" : c.toFixed(2)) + "," + - (d === 1 ? "1" : d.toFixed(2)) + "," + tx + "," + ty + ";"); - } - - resetTransform() { - this._drawCommands = this._drawCommands.concat("m;"); - } - - scale(a, d) { - this._drawCommands = this._drawCommands.concat("k" + a.toFixed(2) + "," + - d.toFixed(2) + ";"); - } - - rotate(angle) { - this._drawCommands = this._drawCommands - .concat("r" + angle.toFixed(6) + ";"); - } - - translate(tx, ty) { - this._drawCommands = this._drawCommands.concat("l" + tx.toFixed(2) + "," + ty.toFixed(2) + ";"); - } - - save() { - this._savedGlobalAlpha.push(this._globalAlpha); - this._drawCommands = this._drawCommands.concat("v;"); - } - - restore() { - this._drawCommands = this._drawCommands.concat("e;"); - this._globalAlpha = this._savedGlobalAlpha.pop(); - } - - createPattern(img, pattern) { - if (typeof img === 'string') { - var imgObj = new GImage(); - imgObj.src = img; - img = imgObj; - } - return new FillStylePattern(img, pattern); - } - - createLinearGradient(x0, y0, x1, y1) { - return new FillStyleLinearGradient(x0, y0, x1, y1); - } - - createRadialGradient = function(x0, y0, r0, x1, y1, r1) { - return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1); - }; - - createCircularGradient = function(x0, y0, r0) { - return new FillStyleRadialGradient(x0, y0, 0, x0, y0, r0); - }; - - strokeRect(x, y, w, h) { - this._drawCommands = this._drawCommands.concat("s" + x + "," + y + "," + w + "," + h + ";"); - } - - - clearRect(x, y, w, h) { - this._drawCommands = this._drawCommands.concat("c" + x + "," + y + "," + w + - "," + h + ";"); - } - - clip() { - this._drawCommands = this._drawCommands.concat("p;"); - } - - resetClip() { - this._drawCommands = this._drawCommands.concat("q;"); - } - - closePath() { - this._drawCommands = this._drawCommands.concat("o;"); - } - - moveTo(x, y) { - this._drawCommands = this._drawCommands.concat("g" + x.toFixed(2) + "," + y.toFixed(2) + ";"); - } - - lineTo(x, y) { - this._drawCommands = this._drawCommands.concat("i" + x.toFixed(2) + "," + y.toFixed(2) + ";"); - } - - quadraticCurveTo = function(cpx, cpy, x, y) { - this._drawCommands = this._drawCommands.concat("u" + cpx + "," + cpy + "," + x + "," + y + ";"); - } - - bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) { - this._drawCommands = this._drawCommands.concat( - "z" + cp1x.toFixed(2) + "," + cp1y.toFixed(2) + "," + cp2x.toFixed(2) + "," + cp2y.toFixed(2) + "," + - x.toFixed(2) + "," + y.toFixed(2) + ";"); - } - - arcTo(x1, y1, x2, y2, radius) { - this._drawCommands = this._drawCommands.concat("h" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + radius + ";"); - } - - beginPath() { - this._drawCommands = this._drawCommands.concat("b;"); - } - - - fillRect(x, y, w, h) { - this._drawCommands = this._drawCommands.concat("n" + x + "," + y + "," + w + - "," + h + ";"); - } - - rect(x, y, w, h) { - this._drawCommands = this._drawCommands.concat("w" + x + "," + y + "," + w + "," + h + ";"); - } - - fill() { - this._drawCommands = this._drawCommands.concat("L;"); - } - - stroke(path) { - this._drawCommands = this._drawCommands.concat("x;"); - } - - arc(x, y, radius, startAngle, endAngle, anticlockwise) { - - let ianticlockwise = 0; - if (anticlockwise) { - ianticlockwise = 1; - } - - this._drawCommands = this._drawCommands.concat( - "y" + x.toFixed(2) + "," + y.toFixed(2) + "," + - radius.toFixed(2) + "," + startAngle + "," + endAngle + "," + ianticlockwise + - ";" - ); - } - - fillText(text, x, y) { - let tmptext = text.replace(/!/g, "!!"); - tmptext = tmptext.replace(/,/g, "!,"); - tmptext = tmptext.replace(/;/g, "!;"); - this._drawCommands = this._drawCommands.concat("T" + tmptext + "," + x + "," + y + ",0.0;"); - } - - strokeText = function(text, x, y) { - let tmptext = text.replace(/!/g, "!!"); - tmptext = tmptext.replace(/,/g, "!,"); - tmptext = tmptext.replace(/;/g, "!;"); - this._drawCommands = this._drawCommands.concat("U" + tmptext + "," + x + "," + y + ",0.0;"); - } - - measureText(text) { - return CanvasRenderingContext2D.GBridge.measureText(text, this.font, this.componentId); - } - - isPointInPath = function(x, y) { - throw new Error('GCanvas not supported yet'); - } - - drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) { - if (typeof image === 'string') { - var imgObj = new GImage(); - imgObj.src = image; - image = imgObj; - } - if (image instanceof GImage) { - if (!image.complete) { - imgObj.onload = () => { - var index = this._needRedrawImageCache.indexOf(image); - if (index > -1) { - this._needRedrawImageCache.splice(index, 1); - CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); - this._redrawflush(true); - } - } - this._notCommitDrawImageCache.push(image); - } else { - CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); - } - var srcArgs = [image, sx, sy, sw, sh, dx, dy, dw, dh]; - var args = []; - for (var arg in srcArgs) { - if (typeof(srcArgs[arg]) != 'undefined') { - args.push(srcArgs[arg]); - } - } - this.__drawImage.apply(this, args); - //this.__drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh); - } - } - - __drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) { - const numArgs = arguments.length; - - function drawImageCommands() { - - if (numArgs === 3) { - const x = parseFloat(sx) || 0.0; - const y = parseFloat(sy) || 0.0; - - return ("d" + image._id + ",0,0," + - image.width + "," + image.height + "," + - x + "," + y + "," + image.width + "," + image.height + ";"); - } else if (numArgs === 5) { - const x = parseFloat(sx) || 0.0; - const y = parseFloat(sy) || 0.0; - const width = parseInt(sw) || image.width; - const height = parseInt(sh) || image.height; - - return ("d" + image._id + ",0,0," + - image.width + "," + image.height + "," + - x + "," + y + "," + width + "," + height + ";"); - } else if (numArgs === 9) { - sx = parseFloat(sx) || 0.0; - sy = parseFloat(sy) || 0.0; - sw = parseInt(sw) || image.width; - sh = parseInt(sh) || image.height; - dx = parseFloat(dx) || 0.0; - dy = parseFloat(dy) || 0.0; - dw = parseInt(dw) || image.width; - dh = parseInt(dh) || image.height; - - return ("d" + image._id + "," + - sx + "," + sy + "," + sw + "," + sh + "," + - dx + "," + dy + "," + dw + "," + dh + ";"); - } - } - this._drawCommands += drawImageCommands(); - } - - _flush(reserve, callback) { - const commands = this._drawCommands; - this._drawCommands = ''; - CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback); - this._needRender = false; - } - - _redrawflush(reserve, callback) { - const commands = this._redrawCommands; - CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback); - if (this._needRedrawImageCache.length == 0) { - this._redrawCommands = ''; - } - } - - draw(reserve, callback) { - if (!reserve) { - this._globalAlpha = this._savedGlobalAlpha.pop(); - this._savedGlobalAlpha.push(this._globalAlpha); - this._redrawCommands = this._drawCommands; - this._needRedrawImageCache = this._notCommitDrawImageCache; - if (this._autoSaveContext) { - this._drawCommands = ("v;" + this._drawCommands); - this._autoSaveContext = false; - } else { - this._drawCommands = ("e;X;v;" + this._drawCommands); - } - } else { - this._needRedrawImageCache = this._needRedrawImageCache.concat(this._notCommitDrawImageCache); - this._redrawCommands += this._drawCommands; - if (this._autoSaveContext) { - this._drawCommands = ("v;" + this._drawCommands); - this._autoSaveContext = false; - } - } - this._notCommitDrawImageCache = []; - if (this._flush) { - this._flush(reserve, callback); - } - } - - getImageData(x, y, w, h, callback) { - CanvasRenderingContext2D.GBridge.getImageData(this.componentId, x, y, w, h, function(res) { - res.data = Base64ToUint8ClampedArray(res.data); - if (typeof(callback) == 'function') { - callback(res); - } - }); - } - - putImageData(data, x, y, w, h, callback) { - if (data instanceof Uint8ClampedArray) { - data = ArrayBufferToBase64(data); - CanvasRenderingContext2D.GBridge.putImageData(this.componentId, data, x, y, w, h, function(res) { - if (typeof(callback) == 'function') { - callback(res); - } - }); - } - } - - toTempFilePath(x, y, width, height, destWidth, destHeight, fileType, quality, callback) { - CanvasRenderingContext2D.GBridge.toTempFilePath(this.componentId, x, y, width, height, destWidth, destHeight, - fileType, quality, - function(res) { - if (typeof(callback) == 'function') { - callback(res); - } - }); - } -} +import FillStylePattern from './FillStylePattern'; +import FillStyleLinearGradient from './FillStyleLinearGradient'; +import FillStyleRadialGradient from './FillStyleRadialGradient'; +import GImage from '../env/image.js'; +import { + ArrayBufferToBase64, + Base64ToUint8ClampedArray +} from '../env/tool.js'; + +export default class CanvasRenderingContext2D { + + _drawCommands = ''; + + _globalAlpha = 1.0; + + _fillStyle = 'rgb(0,0,0)'; + _strokeStyle = 'rgb(0,0,0)'; + + _lineWidth = 1; + _lineCap = 'butt'; + _lineJoin = 'miter'; + + _miterLimit = 10; + + _globalCompositeOperation = 'source-over'; + + _textAlign = 'start'; + _textBaseline = 'alphabetic'; + + _font = '10px sans-serif'; + + _savedGlobalAlpha = []; + + timer = null; + componentId = null; + + _notCommitDrawImageCache = []; + _needRedrawImageCache = []; + _redrawCommands = ''; + _autoSaveContext = true; + // _imageMap = new GHashMap(); + // _textureMap = new GHashMap(); + + constructor() { + this.className = 'CanvasRenderingContext2D'; + //this.save() + } + + setFillStyle(value) { + this.fillStyle = value; + } + + set fillStyle(value) { + this._fillStyle = value; + + if (typeof(value) == 'string') { + this._drawCommands = this._drawCommands.concat("F" + value + ";"); + } else if (value instanceof FillStylePattern) { + const image = value._img; + if (!image.complete) { + image.onload = () => { + var index = this._needRedrawImageCache.indexOf(image); + if (index > -1) { + this._needRedrawImageCache.splice(index, 1); + CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); + this._redrawflush(true); + } + } + this._notCommitDrawImageCache.push(image); + } else { + CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); + } + + //CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); + this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";"); + } else if (value instanceof FillStyleLinearGradient) { + var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + + value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + + value._stop_count; + for (var i = 0; i < value._stop_count; ++i) { + command += ("," + value._stops[i]._pos + "," + value._stops[i]._color); + } + this._drawCommands = this._drawCommands.concat(command + ";"); + } else if (value instanceof FillStyleRadialGradient) { + var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r + .toFixed(2) + "," + + value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + value._end_pos._r.toFixed(2) + "," + + value._stop_count; + for (var i = 0; i < value._stop_count; ++i) { + command += ("," + value._stops[i]._pos + "," + value._stops[i]._color); + } + this._drawCommands = this._drawCommands.concat(command + ";"); + } + } + + get fillStyle() { + return this._fillStyle; + } + + get globalAlpha() { + return this._globalAlpha; + } + + setGlobalAlpha(value) { + this.globalAlpha = value; + } + + set globalAlpha(value) { + this._globalAlpha = value; + this._drawCommands = this._drawCommands.concat("a" + value.toFixed(2) + ";"); + } + + + get strokeStyle() { + return this._strokeStyle; + } + + setStrokeStyle(value) { + this.strokeStyle = value; + } + + set strokeStyle(value) { + + this._strokeStyle = value; + + if (typeof(value) == 'string') { + this._drawCommands = this._drawCommands.concat("S" + value + ";"); + } else if (value instanceof FillStylePattern) { + CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); + this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";"); + } else if (value instanceof FillStyleLinearGradient) { + var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + + value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + + value._stop_count; + + for (var i = 0; i < value._stop_count; ++i) { + command += ("," + value._stops[i]._pos + "," + value._stops[i]._color); + } + this._drawCommands = this._drawCommands.concat(command + ";"); + } else if (value instanceof FillStyleRadialGradient) { + var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r + .toFixed(2) + "," + + value._end_pos._x.toFixed(2) + "," + value._end_pos._y + ",".toFixed(2) + value._end_pos._r.toFixed(2) + "," + + value._stop_count; + + for (var i = 0; i < value._stop_count; ++i) { + command += ("," + value._stops[i]._pos + "," + value._stops[i]._color); + } + this._drawCommands = this._drawCommands.concat(command + ";"); + } + } + + get lineWidth() { + return this._lineWidth; + } + + setLineWidth(value) { + this.lineWidth = value; + } + + set lineWidth(value) { + this._lineWidth = value; + this._drawCommands = this._drawCommands.concat("W" + value + ";"); + } + + get lineCap() { + return this._lineCap; + } + + setLineCap(value) { + this.lineCap = value; + } + + set lineCap(value) { + this._lineCap = value; + this._drawCommands = this._drawCommands.concat("C" + value + ";"); + } + + get lineJoin() { + return this._lineJoin; + } + + setLineJoin(value) { + this.lineJoin = value + } + + set lineJoin(value) { + this._lineJoin = value; + this._drawCommands = this._drawCommands.concat("J" + value + ";"); + } + + get miterLimit() { + return this._miterLimit; + } + + setMiterLimit(value) { + this.miterLimit = value + } + + set miterLimit(value) { + this._miterLimit = value; + this._drawCommands = this._drawCommands.concat("M" + value + ";"); + } + + get globalCompositeOperation() { + return this._globalCompositeOperation; + } + + set globalCompositeOperation(value) { + + this._globalCompositeOperation = value; + let mode = 0; + switch (value) { + case "source-over": + mode = 0; + break; + case "source-atop": + mode = 5; + break; + case "source-in": + mode = 0; + break; + case "source-out": + mode = 2; + break; + case "destination-over": + mode = 4; + break; + case "destination-atop": + mode = 4; + break; + case "destination-in": + mode = 4; + break; + case "destination-out": + mode = 3; + break; + case "lighter": + mode = 1; + break; + case "copy": + mode = 2; + break; + case "xor": + mode = 6; + break; + default: + mode = 0; + } + + this._drawCommands = this._drawCommands.concat("B" + mode + ";"); + } + + get textAlign() { + return this._textAlign; + } + + setTextAlign(value) { + this.textAlign = value + } + + set textAlign(value) { + + this._textAlign = value; + let Align = 0; + switch (value) { + case "start": + Align = 0; + break; + case "end": + Align = 1; + break; + case "left": + Align = 2; + break; + case "center": + Align = 3; + break; + case "right": + Align = 4; + break; + default: + Align = 0; + } + + this._drawCommands = this._drawCommands.concat("A" + Align + ";"); + } + + get textBaseline() { + return this._textBaseline; + } + + setTextBaseline(value) { + this.textBaseline = value + } + + set textBaseline(value) { + this._textBaseline = value; + let baseline = 0; + switch (value) { + case "alphabetic": + baseline = 0; + break; + case "middle": + baseline = 1; + break; + case "top": + baseline = 2; + break; + case "hanging": + baseline = 3; + break; + case "bottom": + baseline = 4; + break; + case "ideographic": + baseline = 5; + break; + default: + baseline = 0; + break; + } + + this._drawCommands = this._drawCommands.concat("E" + baseline + ";"); + } + + get font() { + return this._font; + } + + setFontSize(size) { + var str = this._font; + var strs = str.trim().split(/\s+/); + for (var i = 0; i < strs.length; i++) { + var values = ["normal", "italic", "oblique", "normal", "small-caps", "normal", "bold", + "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900", + "normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", + "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" + ]; + + if (-1 == values.indexOf(strs[i].trim())) { + if (typeof size === 'string') { + strs[i] = size; + } else if (typeof size === 'number') { + strs[i] = String(size) + 'px'; + } + break; + } + } + this.font = strs.join(" "); + } + + set font(value) { + this._font = value; + this._drawCommands = this._drawCommands.concat("j" + value + ";"); + } + + setTransform(a, b, c, d, tx, ty) { + this._drawCommands = this._drawCommands.concat("t" + + (a === 1 ? "1" : a.toFixed(2)) + "," + + (b === 0 ? "0" : b.toFixed(2)) + "," + + (c === 0 ? "0" : c.toFixed(2)) + "," + + (d === 1 ? "1" : d.toFixed(2)) + "," + tx.toFixed(2) + "," + ty.toFixed(2) + ";"); + } + + transform(a, b, c, d, tx, ty) { + this._drawCommands = this._drawCommands.concat("f" + + (a === 1 ? "1" : a.toFixed(2)) + "," + + (b === 0 ? "0" : b.toFixed(2)) + "," + + (c === 0 ? "0" : c.toFixed(2)) + "," + + (d === 1 ? "1" : d.toFixed(2)) + "," + tx + "," + ty + ";"); + } + + resetTransform() { + this._drawCommands = this._drawCommands.concat("m;"); + } + + scale(a, d) { + this._drawCommands = this._drawCommands.concat("k" + a.toFixed(2) + "," + + d.toFixed(2) + ";"); + } + + rotate(angle) { + this._drawCommands = this._drawCommands + .concat("r" + angle.toFixed(6) + ";"); + } + + translate(tx, ty) { + this._drawCommands = this._drawCommands.concat("l" + tx.toFixed(2) + "," + ty.toFixed(2) + ";"); + } + + save() { + this._savedGlobalAlpha.push(this._globalAlpha); + this._drawCommands = this._drawCommands.concat("v;"); + } + + restore() { + this._drawCommands = this._drawCommands.concat("e;"); + this._globalAlpha = this._savedGlobalAlpha.pop(); + } + + createPattern(img, pattern) { + if (typeof img === 'string') { + var imgObj = new GImage(); + imgObj.src = img; + img = imgObj; + } + return new FillStylePattern(img, pattern); + } + + createLinearGradient(x0, y0, x1, y1) { + return new FillStyleLinearGradient(x0, y0, x1, y1); + } + + createRadialGradient = function(x0, y0, r0, x1, y1, r1) { + return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1); + }; + + createCircularGradient = function(x0, y0, r0) { + return new FillStyleRadialGradient(x0, y0, 0, x0, y0, r0); + }; + + strokeRect(x, y, w, h) { + this._drawCommands = this._drawCommands.concat("s" + x + "," + y + "," + w + "," + h + ";"); + } + + + clearRect(x, y, w, h) { + this._drawCommands = this._drawCommands.concat("c" + x + "," + y + "," + w + + "," + h + ";"); + } + + clip() { + this._drawCommands = this._drawCommands.concat("p;"); + } + + resetClip() { + this._drawCommands = this._drawCommands.concat("q;"); + } + + closePath() { + this._drawCommands = this._drawCommands.concat("o;"); + } + + moveTo(x, y) { + this._drawCommands = this._drawCommands.concat("g" + x.toFixed(2) + "," + y.toFixed(2) + ";"); + } + + lineTo(x, y) { + this._drawCommands = this._drawCommands.concat("i" + x.toFixed(2) + "," + y.toFixed(2) + ";"); + } + + quadraticCurveTo = function(cpx, cpy, x, y) { + this._drawCommands = this._drawCommands.concat("u" + cpx + "," + cpy + "," + x + "," + y + ";"); + } + + bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) { + this._drawCommands = this._drawCommands.concat( + "z" + cp1x.toFixed(2) + "," + cp1y.toFixed(2) + "," + cp2x.toFixed(2) + "," + cp2y.toFixed(2) + "," + + x.toFixed(2) + "," + y.toFixed(2) + ";"); + } + + arcTo(x1, y1, x2, y2, radius) { + this._drawCommands = this._drawCommands.concat("h" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + radius + ";"); + } + + beginPath() { + this._drawCommands = this._drawCommands.concat("b;"); + } + + + fillRect(x, y, w, h) { + this._drawCommands = this._drawCommands.concat("n" + x + "," + y + "," + w + + "," + h + ";"); + } + + rect(x, y, w, h) { + this._drawCommands = this._drawCommands.concat("w" + x + "," + y + "," + w + "," + h + ";"); + } + + fill() { + this._drawCommands = this._drawCommands.concat("L;"); + } + + stroke(path) { + this._drawCommands = this._drawCommands.concat("x;"); + } + + arc(x, y, radius, startAngle, endAngle, anticlockwise) { + + let ianticlockwise = 0; + if (anticlockwise) { + ianticlockwise = 1; + } + + this._drawCommands = this._drawCommands.concat( + "y" + x.toFixed(2) + "," + y.toFixed(2) + "," + + radius.toFixed(2) + "," + startAngle + "," + endAngle + "," + ianticlockwise + + ";" + ); + } + + fillText(text, x, y) { + let tmptext = text.replace(/!/g, "!!"); + tmptext = tmptext.replace(/,/g, "!,"); + tmptext = tmptext.replace(/;/g, "!;"); + this._drawCommands = this._drawCommands.concat("T" + tmptext + "," + x + "," + y + ",0.0;"); + } + + strokeText = function(text, x, y) { + let tmptext = text.replace(/!/g, "!!"); + tmptext = tmptext.replace(/,/g, "!,"); + tmptext = tmptext.replace(/;/g, "!;"); + this._drawCommands = this._drawCommands.concat("U" + tmptext + "," + x + "," + y + ",0.0;"); + } + + measureText(text) { + return CanvasRenderingContext2D.GBridge.measureText(text, this.font, this.componentId); + } + + isPointInPath = function(x, y) { + throw new Error('GCanvas not supported yet'); + } + + drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) { + if (typeof image === 'string') { + var imgObj = new GImage(); + imgObj.src = image; + image = imgObj; + } + if (image instanceof GImage) { + if (!image.complete) { + imgObj.onload = () => { + var index = this._needRedrawImageCache.indexOf(image); + if (index > -1) { + this._needRedrawImageCache.splice(index, 1); + CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); + this._redrawflush(true); + } + } + this._notCommitDrawImageCache.push(image); + } else { + CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id); + } + var srcArgs = [image, sx, sy, sw, sh, dx, dy, dw, dh]; + var args = []; + for (var arg in srcArgs) { + if (typeof(srcArgs[arg]) != 'undefined') { + args.push(srcArgs[arg]); + } + } + this.__drawImage.apply(this, args); + //this.__drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh); + } + } + + __drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) { + const numArgs = arguments.length; + + function drawImageCommands() { + + if (numArgs === 3) { + const x = parseFloat(sx) || 0.0; + const y = parseFloat(sy) || 0.0; + + return ("d" + image._id + ",0,0," + + image.width + "," + image.height + "," + + x + "," + y + "," + image.width + "," + image.height + ";"); + } else if (numArgs === 5) { + const x = parseFloat(sx) || 0.0; + const y = parseFloat(sy) || 0.0; + const width = parseInt(sw) || image.width; + const height = parseInt(sh) || image.height; + + return ("d" + image._id + ",0,0," + + image.width + "," + image.height + "," + + x + "," + y + "," + width + "," + height + ";"); + } else if (numArgs === 9) { + sx = parseFloat(sx) || 0.0; + sy = parseFloat(sy) || 0.0; + sw = parseInt(sw) || image.width; + sh = parseInt(sh) || image.height; + dx = parseFloat(dx) || 0.0; + dy = parseFloat(dy) || 0.0; + dw = parseInt(dw) || image.width; + dh = parseInt(dh) || image.height; + + return ("d" + image._id + "," + + sx + "," + sy + "," + sw + "," + sh + "," + + dx + "," + dy + "," + dw + "," + dh + ";"); + } + } + this._drawCommands += drawImageCommands(); + } + + _flush(reserve, callback) { + const commands = this._drawCommands; + this._drawCommands = ''; + CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback); + this._needRender = false; + } + + _redrawflush(reserve, callback) { + const commands = this._redrawCommands; + CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback); + if (this._needRedrawImageCache.length == 0) { + this._redrawCommands = ''; + } + } + + draw(reserve, callback) { + if (!reserve) { + this._globalAlpha = this._savedGlobalAlpha.pop(); + this._savedGlobalAlpha.push(this._globalAlpha); + this._redrawCommands = this._drawCommands; + this._needRedrawImageCache = this._notCommitDrawImageCache; + if (this._autoSaveContext) { + this._drawCommands = ("v;" + this._drawCommands); + this._autoSaveContext = false; + } else { + this._drawCommands = ("e;X;v;" + this._drawCommands); + } + } else { + this._needRedrawImageCache = this._needRedrawImageCache.concat(this._notCommitDrawImageCache); + this._redrawCommands += this._drawCommands; + if (this._autoSaveContext) { + this._drawCommands = ("v;" + this._drawCommands); + this._autoSaveContext = false; + } + } + this._notCommitDrawImageCache = []; + if (this._flush) { + this._flush(reserve, callback); + } + } + + getImageData(x, y, w, h, callback) { + CanvasRenderingContext2D.GBridge.getImageData(this.componentId, x, y, w, h, function(res) { + res.data = Base64ToUint8ClampedArray(res.data); + if (typeof(callback) == 'function') { + callback(res); + } + }); + } + + putImageData(data, x, y, w, h, callback) { + if (data instanceof Uint8ClampedArray) { + data = ArrayBufferToBase64(data); + CanvasRenderingContext2D.GBridge.putImageData(this.componentId, data, x, y, w, h, function(res) { + if (typeof(callback) == 'function') { + callback(res); + } + }); + } + } + + toTempFilePath(x, y, width, height, destWidth, destHeight, fileType, quality, callback) { + CanvasRenderingContext2D.GBridge.toTempFilePath(this.componentId, x, y, width, height, destWidth, destHeight, + fileType, quality, + function(res) { + if (typeof(callback) == 'function') { + callback(res); + } + }); + } +} diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ActiveInfo.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ActiveInfo.js index b4951299..41738b15 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ActiveInfo.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ActiveInfo.js @@ -1,11 +1,11 @@ -export default class WebGLActiveInfo { - className = 'WebGLActiveInfo'; - - constructor({ - type, name, size - }) { - this.type = type; - this.name = name; - this.size = size; - } +export default class WebGLActiveInfo { + className = 'WebGLActiveInfo'; + + constructor({ + type, name, size + }) { + this.type = type; + this.name = name; + this.size = size; + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Buffer.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Buffer.js index 4800f676..a4512918 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Buffer.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Buffer.js @@ -1,21 +1,21 @@ -import {getTransferedObjectUUID} from './classUtils'; - -const name = 'WebGLBuffer'; - -function uuid(id) { - return getTransferedObjectUUID(name, id); -} - -export default class WebGLBuffer { - className = name; - - constructor(id) { - this.id = id; - } - - static uuid = uuid; - - uuid() { - return uuid(this.id); - } +import {getTransferedObjectUUID} from './classUtils'; + +const name = 'WebGLBuffer'; + +function uuid(id) { + return getTransferedObjectUUID(name, id); +} + +export default class WebGLBuffer { + className = name; + + constructor(id) { + this.id = id; + } + + static uuid = uuid; + + uuid() { + return uuid(this.id); + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Framebuffer.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Framebuffer.js index 28b46d3f..e8d806e9 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Framebuffer.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Framebuffer.js @@ -1,21 +1,21 @@ -import {getTransferedObjectUUID} from './classUtils'; - -const name = 'WebGLFrameBuffer'; - -function uuid(id) { - return getTransferedObjectUUID(name, id); -} - -export default class WebGLFramebuffer { - className = name; - - constructor(id) { - this.id = id; - } - - static uuid = uuid; - - uuid() { - return uuid(this.id); - } +import {getTransferedObjectUUID} from './classUtils'; + +const name = 'WebGLFrameBuffer'; + +function uuid(id) { + return getTransferedObjectUUID(name, id); +} + +export default class WebGLFramebuffer { + className = name; + + constructor(id) { + this.id = id; + } + + static uuid = uuid; + + uuid() { + return uuid(this.id); + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLenum.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLenum.js index ac5544d7..5fdc49fb 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLenum.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLenum.js @@ -1,298 +1,298 @@ -export default { - "DEPTH_BUFFER_BIT": 256, - "STENCIL_BUFFER_BIT": 1024, - "COLOR_BUFFER_BIT": 16384, - "POINTS": 0, - "LINES": 1, - "LINE_LOOP": 2, - "LINE_STRIP": 3, - "TRIANGLES": 4, - "TRIANGLE_STRIP": 5, - "TRIANGLE_FAN": 6, - "ZERO": 0, - "ONE": 1, - "SRC_COLOR": 768, - "ONE_MINUS_SRC_COLOR": 769, - "SRC_ALPHA": 770, - "ONE_MINUS_SRC_ALPHA": 771, - "DST_ALPHA": 772, - "ONE_MINUS_DST_ALPHA": 773, - "DST_COLOR": 774, - "ONE_MINUS_DST_COLOR": 775, - "SRC_ALPHA_SATURATE": 776, - "FUNC_ADD": 32774, - "BLEND_EQUATION": 32777, - "BLEND_EQUATION_RGB": 32777, - "BLEND_EQUATION_ALPHA": 34877, - "FUNC_SUBTRACT": 32778, - "FUNC_REVERSE_SUBTRACT": 32779, - "BLEND_DST_RGB": 32968, - "BLEND_SRC_RGB": 32969, - "BLEND_DST_ALPHA": 32970, - "BLEND_SRC_ALPHA": 32971, - "CONSTANT_COLOR": 32769, - "ONE_MINUS_CONSTANT_COLOR": 32770, - "CONSTANT_ALPHA": 32771, - "ONE_MINUS_CONSTANT_ALPHA": 32772, - "BLEND_COLOR": 32773, - "ARRAY_BUFFER": 34962, - "ELEMENT_ARRAY_BUFFER": 34963, - "ARRAY_BUFFER_BINDING": 34964, - "ELEMENT_ARRAY_BUFFER_BINDING": 34965, - "STREAM_DRAW": 35040, - "STATIC_DRAW": 35044, - "DYNAMIC_DRAW": 35048, - "BUFFER_SIZE": 34660, - "BUFFER_USAGE": 34661, - "CURRENT_VERTEX_ATTRIB": 34342, - "FRONT": 1028, - "BACK": 1029, - "FRONT_AND_BACK": 1032, - "TEXTURE_2D": 3553, - "CULL_FACE": 2884, - "BLEND": 3042, - "DITHER": 3024, - "STENCIL_TEST": 2960, - "DEPTH_TEST": 2929, - "SCISSOR_TEST": 3089, - "POLYGON_OFFSET_FILL": 32823, - "SAMPLE_ALPHA_TO_COVERAGE": 32926, - "SAMPLE_COVERAGE": 32928, - "NO_ERROR": 0, - "INVALID_ENUM": 1280, - "INVALID_VALUE": 1281, - "INVALID_OPERATION": 1282, - "OUT_OF_MEMORY": 1285, - "CW": 2304, - "CCW": 2305, - "LINE_WIDTH": 2849, - "ALIASED_POINT_SIZE_RANGE": 33901, - "ALIASED_LINE_WIDTH_RANGE": 33902, - "CULL_FACE_MODE": 2885, - "FRONT_FACE": 2886, - "DEPTH_RANGE": 2928, - "DEPTH_WRITEMASK": 2930, - "DEPTH_CLEAR_VALUE": 2931, - "DEPTH_FUNC": 2932, - "STENCIL_CLEAR_VALUE": 2961, - "STENCIL_FUNC": 2962, - "STENCIL_FAIL": 2964, - "STENCIL_PASS_DEPTH_FAIL": 2965, - "STENCIL_PASS_DEPTH_PASS": 2966, - "STENCIL_REF": 2967, - "STENCIL_VALUE_MASK": 2963, - "STENCIL_WRITEMASK": 2968, - "STENCIL_BACK_FUNC": 34816, - "STENCIL_BACK_FAIL": 34817, - "STENCIL_BACK_PASS_DEPTH_FAIL": 34818, - "STENCIL_BACK_PASS_DEPTH_PASS": 34819, - "STENCIL_BACK_REF": 36003, - "STENCIL_BACK_VALUE_MASK": 36004, - "STENCIL_BACK_WRITEMASK": 36005, - "VIEWPORT": 2978, - "SCISSOR_BOX": 3088, - "COLOR_CLEAR_VALUE": 3106, - "COLOR_WRITEMASK": 3107, - "UNPACK_ALIGNMENT": 3317, - "PACK_ALIGNMENT": 3333, - "MAX_TEXTURE_SIZE": 3379, - "MAX_VIEWPORT_DIMS": 3386, - "SUBPIXEL_BITS": 3408, - "RED_BITS": 3410, - "GREEN_BITS": 3411, - "BLUE_BITS": 3412, - "ALPHA_BITS": 3413, - "DEPTH_BITS": 3414, - "STENCIL_BITS": 3415, - "POLYGON_OFFSET_UNITS": 10752, - "POLYGON_OFFSET_FACTOR": 32824, - "TEXTURE_BINDING_2D": 32873, - "SAMPLE_BUFFERS": 32936, - "SAMPLES": 32937, - "SAMPLE_COVERAGE_VALUE": 32938, - "SAMPLE_COVERAGE_INVERT": 32939, - "COMPRESSED_TEXTURE_FORMATS": 34467, - "DONT_CARE": 4352, - "FASTEST": 4353, - "NICEST": 4354, - "GENERATE_MIPMAP_HINT": 33170, - "BYTE": 5120, - "UNSIGNED_BYTE": 5121, - "SHORT": 5122, - "UNSIGNED_SHORT": 5123, - "INT": 5124, - "UNSIGNED_INT": 5125, - "FLOAT": 5126, - "DEPTH_COMPONENT": 6402, - "ALPHA": 6406, - "RGB": 6407, - "RGBA": 6408, - "LUMINANCE": 6409, - "LUMINANCE_ALPHA": 6410, - "UNSIGNED_SHORT_4_4_4_4": 32819, - "UNSIGNED_SHORT_5_5_5_1": 32820, - "UNSIGNED_SHORT_5_6_5": 33635, - "FRAGMENT_SHADER": 35632, - "VERTEX_SHADER": 35633, - "MAX_VERTEX_ATTRIBS": 34921, - "MAX_VERTEX_UNIFORM_VECTORS": 36347, - "MAX_VARYING_VECTORS": 36348, - "MAX_COMBINED_TEXTURE_IMAGE_UNITS": 35661, - "MAX_VERTEX_TEXTURE_IMAGE_UNITS": 35660, - "MAX_TEXTURE_IMAGE_UNITS": 34930, - "MAX_FRAGMENT_UNIFORM_VECTORS": 36349, - "SHADER_TYPE": 35663, - "DELETE_STATUS": 35712, - "LINK_STATUS": 35714, - "VALIDATE_STATUS": 35715, - "ATTACHED_SHADERS": 35717, - "ACTIVE_UNIFORMS": 35718, - "ACTIVE_ATTRIBUTES": 35721, - "SHADING_LANGUAGE_VERSION": 35724, - "CURRENT_PROGRAM": 35725, - "NEVER": 512, - "LESS": 513, - "EQUAL": 514, - "LEQUAL": 515, - "GREATER": 516, - "NOTEQUAL": 517, - "GEQUAL": 518, - "ALWAYS": 519, - "KEEP": 7680, - "REPLACE": 7681, - "INCR": 7682, - "DECR": 7683, - "INVERT": 5386, - "INCR_WRAP": 34055, - "DECR_WRAP": 34056, - "VENDOR": 7936, - "RENDERER": 7937, - "VERSION": 7938, - "NEAREST": 9728, - "LINEAR": 9729, - "NEAREST_MIPMAP_NEAREST": 9984, - "LINEAR_MIPMAP_NEAREST": 9985, - "NEAREST_MIPMAP_LINEAR": 9986, - "LINEAR_MIPMAP_LINEAR": 9987, - "TEXTURE_MAG_FILTER": 10240, - "TEXTURE_MIN_FILTER": 10241, - "TEXTURE_WRAP_S": 10242, - "TEXTURE_WRAP_T": 10243, - "TEXTURE": 5890, - "TEXTURE_CUBE_MAP": 34067, - "TEXTURE_BINDING_CUBE_MAP": 34068, - "TEXTURE_CUBE_MAP_POSITIVE_X": 34069, - "TEXTURE_CUBE_MAP_NEGATIVE_X": 34070, - "TEXTURE_CUBE_MAP_POSITIVE_Y": 34071, - "TEXTURE_CUBE_MAP_NEGATIVE_Y": 34072, - "TEXTURE_CUBE_MAP_POSITIVE_Z": 34073, - "TEXTURE_CUBE_MAP_NEGATIVE_Z": 34074, - "MAX_CUBE_MAP_TEXTURE_SIZE": 34076, - "TEXTURE0": 33984, - "TEXTURE1": 33985, - "TEXTURE2": 33986, - "TEXTURE3": 33987, - "TEXTURE4": 33988, - "TEXTURE5": 33989, - "TEXTURE6": 33990, - "TEXTURE7": 33991, - "TEXTURE8": 33992, - "TEXTURE9": 33993, - "TEXTURE10": 33994, - "TEXTURE11": 33995, - "TEXTURE12": 33996, - "TEXTURE13": 33997, - "TEXTURE14": 33998, - "TEXTURE15": 33999, - "TEXTURE16": 34000, - "TEXTURE17": 34001, - "TEXTURE18": 34002, - "TEXTURE19": 34003, - "TEXTURE20": 34004, - "TEXTURE21": 34005, - "TEXTURE22": 34006, - "TEXTURE23": 34007, - "TEXTURE24": 34008, - "TEXTURE25": 34009, - "TEXTURE26": 34010, - "TEXTURE27": 34011, - "TEXTURE28": 34012, - "TEXTURE29": 34013, - "TEXTURE30": 34014, - "TEXTURE31": 34015, - "ACTIVE_TEXTURE": 34016, - "REPEAT": 10497, - "CLAMP_TO_EDGE": 33071, - "MIRRORED_REPEAT": 33648, - "FLOAT_VEC2": 35664, - "FLOAT_VEC3": 35665, - "FLOAT_VEC4": 35666, - "INT_VEC2": 35667, - "INT_VEC3": 35668, - "INT_VEC4": 35669, - "BOOL": 35670, - "BOOL_VEC2": 35671, - "BOOL_VEC3": 35672, - "BOOL_VEC4": 35673, - "FLOAT_MAT2": 35674, - "FLOAT_MAT3": 35675, - "FLOAT_MAT4": 35676, - "SAMPLER_2D": 35678, - "SAMPLER_CUBE": 35680, - "VERTEX_ATTRIB_ARRAY_ENABLED": 34338, - "VERTEX_ATTRIB_ARRAY_SIZE": 34339, - "VERTEX_ATTRIB_ARRAY_STRIDE": 34340, - "VERTEX_ATTRIB_ARRAY_TYPE": 34341, - "VERTEX_ATTRIB_ARRAY_NORMALIZED": 34922, - "VERTEX_ATTRIB_ARRAY_POINTER": 34373, - "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING": 34975, - "IMPLEMENTATION_COLOR_READ_TYPE": 35738, - "IMPLEMENTATION_COLOR_READ_FORMAT": 35739, - "COMPILE_STATUS": 35713, - "LOW_FLOAT": 36336, - "MEDIUM_FLOAT": 36337, - "HIGH_FLOAT": 36338, - "LOW_INT": 36339, - "MEDIUM_INT": 36340, - "HIGH_INT": 36341, - "FRAMEBUFFER": 36160, - "RENDERBUFFER": 36161, - "RGBA4": 32854, - "RGB5_A1": 32855, - "RGB565": 36194, - "DEPTH_COMPONENT16": 33189, - "STENCIL_INDEX8": 36168, - "DEPTH_STENCIL": 34041, - "RENDERBUFFER_WIDTH": 36162, - "RENDERBUFFER_HEIGHT": 36163, - "RENDERBUFFER_INTERNAL_FORMAT": 36164, - "RENDERBUFFER_RED_SIZE": 36176, - "RENDERBUFFER_GREEN_SIZE": 36177, - "RENDERBUFFER_BLUE_SIZE": 36178, - "RENDERBUFFER_ALPHA_SIZE": 36179, - "RENDERBUFFER_DEPTH_SIZE": 36180, - "RENDERBUFFER_STENCIL_SIZE": 36181, - "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE": 36048, - "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME": 36049, - "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL": 36050, - "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE": 36051, - "COLOR_ATTACHMENT0": 36064, - "DEPTH_ATTACHMENT": 36096, - "STENCIL_ATTACHMENT": 36128, - "DEPTH_STENCIL_ATTACHMENT": 33306, - "NONE": 0, - "FRAMEBUFFER_COMPLETE": 36053, - "FRAMEBUFFER_INCOMPLETE_ATTACHMENT": 36054, - "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT": 36055, - "FRAMEBUFFER_INCOMPLETE_DIMENSIONS": 36057, - "FRAMEBUFFER_UNSUPPORTED": 36061, - "FRAMEBUFFER_BINDING": 36006, - "RENDERBUFFER_BINDING": 36007, - "MAX_RENDERBUFFER_SIZE": 34024, - "INVALID_FRAMEBUFFER_OPERATION": 1286, - "UNPACK_FLIP_Y_WEBGL": 37440, - "UNPACK_PREMULTIPLY_ALPHA_WEBGL": 37441, - "CONTEXT_LOST_WEBGL": 37442, - "UNPACK_COLORSPACE_CONVERSION_WEBGL": 37443, - "BROWSER_DEFAULT_WEBGL": 37444 +export default { + "DEPTH_BUFFER_BIT": 256, + "STENCIL_BUFFER_BIT": 1024, + "COLOR_BUFFER_BIT": 16384, + "POINTS": 0, + "LINES": 1, + "LINE_LOOP": 2, + "LINE_STRIP": 3, + "TRIANGLES": 4, + "TRIANGLE_STRIP": 5, + "TRIANGLE_FAN": 6, + "ZERO": 0, + "ONE": 1, + "SRC_COLOR": 768, + "ONE_MINUS_SRC_COLOR": 769, + "SRC_ALPHA": 770, + "ONE_MINUS_SRC_ALPHA": 771, + "DST_ALPHA": 772, + "ONE_MINUS_DST_ALPHA": 773, + "DST_COLOR": 774, + "ONE_MINUS_DST_COLOR": 775, + "SRC_ALPHA_SATURATE": 776, + "FUNC_ADD": 32774, + "BLEND_EQUATION": 32777, + "BLEND_EQUATION_RGB": 32777, + "BLEND_EQUATION_ALPHA": 34877, + "FUNC_SUBTRACT": 32778, + "FUNC_REVERSE_SUBTRACT": 32779, + "BLEND_DST_RGB": 32968, + "BLEND_SRC_RGB": 32969, + "BLEND_DST_ALPHA": 32970, + "BLEND_SRC_ALPHA": 32971, + "CONSTANT_COLOR": 32769, + "ONE_MINUS_CONSTANT_COLOR": 32770, + "CONSTANT_ALPHA": 32771, + "ONE_MINUS_CONSTANT_ALPHA": 32772, + "BLEND_COLOR": 32773, + "ARRAY_BUFFER": 34962, + "ELEMENT_ARRAY_BUFFER": 34963, + "ARRAY_BUFFER_BINDING": 34964, + "ELEMENT_ARRAY_BUFFER_BINDING": 34965, + "STREAM_DRAW": 35040, + "STATIC_DRAW": 35044, + "DYNAMIC_DRAW": 35048, + "BUFFER_SIZE": 34660, + "BUFFER_USAGE": 34661, + "CURRENT_VERTEX_ATTRIB": 34342, + "FRONT": 1028, + "BACK": 1029, + "FRONT_AND_BACK": 1032, + "TEXTURE_2D": 3553, + "CULL_FACE": 2884, + "BLEND": 3042, + "DITHER": 3024, + "STENCIL_TEST": 2960, + "DEPTH_TEST": 2929, + "SCISSOR_TEST": 3089, + "POLYGON_OFFSET_FILL": 32823, + "SAMPLE_ALPHA_TO_COVERAGE": 32926, + "SAMPLE_COVERAGE": 32928, + "NO_ERROR": 0, + "INVALID_ENUM": 1280, + "INVALID_VALUE": 1281, + "INVALID_OPERATION": 1282, + "OUT_OF_MEMORY": 1285, + "CW": 2304, + "CCW": 2305, + "LINE_WIDTH": 2849, + "ALIASED_POINT_SIZE_RANGE": 33901, + "ALIASED_LINE_WIDTH_RANGE": 33902, + "CULL_FACE_MODE": 2885, + "FRONT_FACE": 2886, + "DEPTH_RANGE": 2928, + "DEPTH_WRITEMASK": 2930, + "DEPTH_CLEAR_VALUE": 2931, + "DEPTH_FUNC": 2932, + "STENCIL_CLEAR_VALUE": 2961, + "STENCIL_FUNC": 2962, + "STENCIL_FAIL": 2964, + "STENCIL_PASS_DEPTH_FAIL": 2965, + "STENCIL_PASS_DEPTH_PASS": 2966, + "STENCIL_REF": 2967, + "STENCIL_VALUE_MASK": 2963, + "STENCIL_WRITEMASK": 2968, + "STENCIL_BACK_FUNC": 34816, + "STENCIL_BACK_FAIL": 34817, + "STENCIL_BACK_PASS_DEPTH_FAIL": 34818, + "STENCIL_BACK_PASS_DEPTH_PASS": 34819, + "STENCIL_BACK_REF": 36003, + "STENCIL_BACK_VALUE_MASK": 36004, + "STENCIL_BACK_WRITEMASK": 36005, + "VIEWPORT": 2978, + "SCISSOR_BOX": 3088, + "COLOR_CLEAR_VALUE": 3106, + "COLOR_WRITEMASK": 3107, + "UNPACK_ALIGNMENT": 3317, + "PACK_ALIGNMENT": 3333, + "MAX_TEXTURE_SIZE": 3379, + "MAX_VIEWPORT_DIMS": 3386, + "SUBPIXEL_BITS": 3408, + "RED_BITS": 3410, + "GREEN_BITS": 3411, + "BLUE_BITS": 3412, + "ALPHA_BITS": 3413, + "DEPTH_BITS": 3414, + "STENCIL_BITS": 3415, + "POLYGON_OFFSET_UNITS": 10752, + "POLYGON_OFFSET_FACTOR": 32824, + "TEXTURE_BINDING_2D": 32873, + "SAMPLE_BUFFERS": 32936, + "SAMPLES": 32937, + "SAMPLE_COVERAGE_VALUE": 32938, + "SAMPLE_COVERAGE_INVERT": 32939, + "COMPRESSED_TEXTURE_FORMATS": 34467, + "DONT_CARE": 4352, + "FASTEST": 4353, + "NICEST": 4354, + "GENERATE_MIPMAP_HINT": 33170, + "BYTE": 5120, + "UNSIGNED_BYTE": 5121, + "SHORT": 5122, + "UNSIGNED_SHORT": 5123, + "INT": 5124, + "UNSIGNED_INT": 5125, + "FLOAT": 5126, + "DEPTH_COMPONENT": 6402, + "ALPHA": 6406, + "RGB": 6407, + "RGBA": 6408, + "LUMINANCE": 6409, + "LUMINANCE_ALPHA": 6410, + "UNSIGNED_SHORT_4_4_4_4": 32819, + "UNSIGNED_SHORT_5_5_5_1": 32820, + "UNSIGNED_SHORT_5_6_5": 33635, + "FRAGMENT_SHADER": 35632, + "VERTEX_SHADER": 35633, + "MAX_VERTEX_ATTRIBS": 34921, + "MAX_VERTEX_UNIFORM_VECTORS": 36347, + "MAX_VARYING_VECTORS": 36348, + "MAX_COMBINED_TEXTURE_IMAGE_UNITS": 35661, + "MAX_VERTEX_TEXTURE_IMAGE_UNITS": 35660, + "MAX_TEXTURE_IMAGE_UNITS": 34930, + "MAX_FRAGMENT_UNIFORM_VECTORS": 36349, + "SHADER_TYPE": 35663, + "DELETE_STATUS": 35712, + "LINK_STATUS": 35714, + "VALIDATE_STATUS": 35715, + "ATTACHED_SHADERS": 35717, + "ACTIVE_UNIFORMS": 35718, + "ACTIVE_ATTRIBUTES": 35721, + "SHADING_LANGUAGE_VERSION": 35724, + "CURRENT_PROGRAM": 35725, + "NEVER": 512, + "LESS": 513, + "EQUAL": 514, + "LEQUAL": 515, + "GREATER": 516, + "NOTEQUAL": 517, + "GEQUAL": 518, + "ALWAYS": 519, + "KEEP": 7680, + "REPLACE": 7681, + "INCR": 7682, + "DECR": 7683, + "INVERT": 5386, + "INCR_WRAP": 34055, + "DECR_WRAP": 34056, + "VENDOR": 7936, + "RENDERER": 7937, + "VERSION": 7938, + "NEAREST": 9728, + "LINEAR": 9729, + "NEAREST_MIPMAP_NEAREST": 9984, + "LINEAR_MIPMAP_NEAREST": 9985, + "NEAREST_MIPMAP_LINEAR": 9986, + "LINEAR_MIPMAP_LINEAR": 9987, + "TEXTURE_MAG_FILTER": 10240, + "TEXTURE_MIN_FILTER": 10241, + "TEXTURE_WRAP_S": 10242, + "TEXTURE_WRAP_T": 10243, + "TEXTURE": 5890, + "TEXTURE_CUBE_MAP": 34067, + "TEXTURE_BINDING_CUBE_MAP": 34068, + "TEXTURE_CUBE_MAP_POSITIVE_X": 34069, + "TEXTURE_CUBE_MAP_NEGATIVE_X": 34070, + "TEXTURE_CUBE_MAP_POSITIVE_Y": 34071, + "TEXTURE_CUBE_MAP_NEGATIVE_Y": 34072, + "TEXTURE_CUBE_MAP_POSITIVE_Z": 34073, + "TEXTURE_CUBE_MAP_NEGATIVE_Z": 34074, + "MAX_CUBE_MAP_TEXTURE_SIZE": 34076, + "TEXTURE0": 33984, + "TEXTURE1": 33985, + "TEXTURE2": 33986, + "TEXTURE3": 33987, + "TEXTURE4": 33988, + "TEXTURE5": 33989, + "TEXTURE6": 33990, + "TEXTURE7": 33991, + "TEXTURE8": 33992, + "TEXTURE9": 33993, + "TEXTURE10": 33994, + "TEXTURE11": 33995, + "TEXTURE12": 33996, + "TEXTURE13": 33997, + "TEXTURE14": 33998, + "TEXTURE15": 33999, + "TEXTURE16": 34000, + "TEXTURE17": 34001, + "TEXTURE18": 34002, + "TEXTURE19": 34003, + "TEXTURE20": 34004, + "TEXTURE21": 34005, + "TEXTURE22": 34006, + "TEXTURE23": 34007, + "TEXTURE24": 34008, + "TEXTURE25": 34009, + "TEXTURE26": 34010, + "TEXTURE27": 34011, + "TEXTURE28": 34012, + "TEXTURE29": 34013, + "TEXTURE30": 34014, + "TEXTURE31": 34015, + "ACTIVE_TEXTURE": 34016, + "REPEAT": 10497, + "CLAMP_TO_EDGE": 33071, + "MIRRORED_REPEAT": 33648, + "FLOAT_VEC2": 35664, + "FLOAT_VEC3": 35665, + "FLOAT_VEC4": 35666, + "INT_VEC2": 35667, + "INT_VEC3": 35668, + "INT_VEC4": 35669, + "BOOL": 35670, + "BOOL_VEC2": 35671, + "BOOL_VEC3": 35672, + "BOOL_VEC4": 35673, + "FLOAT_MAT2": 35674, + "FLOAT_MAT3": 35675, + "FLOAT_MAT4": 35676, + "SAMPLER_2D": 35678, + "SAMPLER_CUBE": 35680, + "VERTEX_ATTRIB_ARRAY_ENABLED": 34338, + "VERTEX_ATTRIB_ARRAY_SIZE": 34339, + "VERTEX_ATTRIB_ARRAY_STRIDE": 34340, + "VERTEX_ATTRIB_ARRAY_TYPE": 34341, + "VERTEX_ATTRIB_ARRAY_NORMALIZED": 34922, + "VERTEX_ATTRIB_ARRAY_POINTER": 34373, + "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING": 34975, + "IMPLEMENTATION_COLOR_READ_TYPE": 35738, + "IMPLEMENTATION_COLOR_READ_FORMAT": 35739, + "COMPILE_STATUS": 35713, + "LOW_FLOAT": 36336, + "MEDIUM_FLOAT": 36337, + "HIGH_FLOAT": 36338, + "LOW_INT": 36339, + "MEDIUM_INT": 36340, + "HIGH_INT": 36341, + "FRAMEBUFFER": 36160, + "RENDERBUFFER": 36161, + "RGBA4": 32854, + "RGB5_A1": 32855, + "RGB565": 36194, + "DEPTH_COMPONENT16": 33189, + "STENCIL_INDEX8": 36168, + "DEPTH_STENCIL": 34041, + "RENDERBUFFER_WIDTH": 36162, + "RENDERBUFFER_HEIGHT": 36163, + "RENDERBUFFER_INTERNAL_FORMAT": 36164, + "RENDERBUFFER_RED_SIZE": 36176, + "RENDERBUFFER_GREEN_SIZE": 36177, + "RENDERBUFFER_BLUE_SIZE": 36178, + "RENDERBUFFER_ALPHA_SIZE": 36179, + "RENDERBUFFER_DEPTH_SIZE": 36180, + "RENDERBUFFER_STENCIL_SIZE": 36181, + "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE": 36048, + "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME": 36049, + "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL": 36050, + "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE": 36051, + "COLOR_ATTACHMENT0": 36064, + "DEPTH_ATTACHMENT": 36096, + "STENCIL_ATTACHMENT": 36128, + "DEPTH_STENCIL_ATTACHMENT": 33306, + "NONE": 0, + "FRAMEBUFFER_COMPLETE": 36053, + "FRAMEBUFFER_INCOMPLETE_ATTACHMENT": 36054, + "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT": 36055, + "FRAMEBUFFER_INCOMPLETE_DIMENSIONS": 36057, + "FRAMEBUFFER_UNSUPPORTED": 36061, + "FRAMEBUFFER_BINDING": 36006, + "RENDERBUFFER_BINDING": 36007, + "MAX_RENDERBUFFER_SIZE": 34024, + "INVALID_FRAMEBUFFER_OPERATION": 1286, + "UNPACK_FLIP_Y_WEBGL": 37440, + "UNPACK_PREMULTIPLY_ALPHA_WEBGL": 37441, + "CONTEXT_LOST_WEBGL": 37442, + "UNPACK_COLORSPACE_CONVERSION_WEBGL": 37443, + "BROWSER_DEFAULT_WEBGL": 37444 }; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLmethod.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLmethod.js index f2659bef..8ca22120 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLmethod.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLmethod.js @@ -1,142 +1,142 @@ -let i = 1; - -const GLmethod = {}; - -GLmethod.activeTexture = i++; //1 -GLmethod.attachShader = i++; -GLmethod.bindAttribLocation = i++; -GLmethod.bindBuffer = i++; -GLmethod.bindFramebuffer = i++; -GLmethod.bindRenderbuffer = i++; -GLmethod.bindTexture = i++; -GLmethod.blendColor = i++; -GLmethod.blendEquation = i++; -GLmethod.blendEquationSeparate = i++; //10 -GLmethod.blendFunc = i++; -GLmethod.blendFuncSeparate = i++; -GLmethod.bufferData = i++; -GLmethod.bufferSubData = i++; -GLmethod.checkFramebufferStatus = i++; -GLmethod.clear = i++; -GLmethod.clearColor = i++; -GLmethod.clearDepth = i++; -GLmethod.clearStencil = i++; -GLmethod.colorMask = i++; //20 -GLmethod.compileShader = i++; -GLmethod.compressedTexImage2D = i++; -GLmethod.compressedTexSubImage2D = i++; -GLmethod.copyTexImage2D = i++; -GLmethod.copyTexSubImage2D = i++; -GLmethod.createBuffer = i++; -GLmethod.createFramebuffer = i++; -GLmethod.createProgram = i++; -GLmethod.createRenderbuffer = i++; -GLmethod.createShader = i++; //30 -GLmethod.createTexture = i++; -GLmethod.cullFace = i++; -GLmethod.deleteBuffer = i++; -GLmethod.deleteFramebuffer = i++; -GLmethod.deleteProgram = i++; -GLmethod.deleteRenderbuffer = i++; -GLmethod.deleteShader = i++; -GLmethod.deleteTexture = i++; -GLmethod.depthFunc = i++; -GLmethod.depthMask = i++; //40 -GLmethod.depthRange = i++; -GLmethod.detachShader = i++; -GLmethod.disable = i++; -GLmethod.disableVertexAttribArray = i++; -GLmethod.drawArrays = i++; -GLmethod.drawArraysInstancedANGLE = i++; -GLmethod.drawElements = i++; -GLmethod.drawElementsInstancedANGLE = i++; -GLmethod.enable = i++; -GLmethod.enableVertexAttribArray = i++; //50 -GLmethod.flush = i++; -GLmethod.framebufferRenderbuffer = i++; -GLmethod.framebufferTexture2D = i++; -GLmethod.frontFace = i++; -GLmethod.generateMipmap = i++; -GLmethod.getActiveAttrib = i++; -GLmethod.getActiveUniform = i++; -GLmethod.getAttachedShaders = i++; -GLmethod.getAttribLocation = i++; -GLmethod.getBufferParameter = i++; //60 -GLmethod.getContextAttributes = i++; -GLmethod.getError = i++; -GLmethod.getExtension = i++; -GLmethod.getFramebufferAttachmentParameter = i++; -GLmethod.getParameter = i++; -GLmethod.getProgramInfoLog = i++; -GLmethod.getProgramParameter = i++; -GLmethod.getRenderbufferParameter = i++; -GLmethod.getShaderInfoLog = i++; -GLmethod.getShaderParameter = i++; //70 -GLmethod.getShaderPrecisionFormat = i++; -GLmethod.getShaderSource = i++; -GLmethod.getSupportedExtensions = i++; -GLmethod.getTexParameter = i++; -GLmethod.getUniform = i++; -GLmethod.getUniformLocation = i++; -GLmethod.getVertexAttrib = i++; -GLmethod.getVertexAttribOffset = i++; -GLmethod.isBuffer = i++; -GLmethod.isContextLost = i++; //80 -GLmethod.isEnabled = i++; -GLmethod.isFramebuffer = i++; -GLmethod.isProgram = i++; -GLmethod.isRenderbuffer = i++; -GLmethod.isShader = i++; -GLmethod.isTexture = i++; -GLmethod.lineWidth = i++; -GLmethod.linkProgram = i++; -GLmethod.pixelStorei = i++; -GLmethod.polygonOffset = i++; //90 -GLmethod.readPixels = i++; -GLmethod.renderbufferStorage = i++; -GLmethod.sampleCoverage = i++; -GLmethod.scissor = i++; -GLmethod.shaderSource = i++; -GLmethod.stencilFunc = i++; -GLmethod.stencilFuncSeparate = i++; -GLmethod.stencilMask = i++; -GLmethod.stencilMaskSeparate = i++; -GLmethod.stencilOp = i++; //100 -GLmethod.stencilOpSeparate = i++; -GLmethod.texImage2D = i++; -GLmethod.texParameterf = i++; -GLmethod.texParameteri = i++; -GLmethod.texSubImage2D = i++; -GLmethod.uniform1f = i++; -GLmethod.uniform1fv = i++; -GLmethod.uniform1i = i++; -GLmethod.uniform1iv = i++; -GLmethod.uniform2f = i++; //110 -GLmethod.uniform2fv = i++; -GLmethod.uniform2i = i++; -GLmethod.uniform2iv = i++; -GLmethod.uniform3f = i++; -GLmethod.uniform3fv = i++; -GLmethod.uniform3i = i++; -GLmethod.uniform3iv = i++; -GLmethod.uniform4f = i++; -GLmethod.uniform4fv = i++; -GLmethod.uniform4i = i++; //120 -GLmethod.uniform4iv = i++; -GLmethod.uniformMatrix2fv = i++; -GLmethod.uniformMatrix3fv = i++; -GLmethod.uniformMatrix4fv = i++; -GLmethod.useProgram = i++; -GLmethod.validateProgram = i++; -GLmethod.vertexAttrib1f = i++; //new -GLmethod.vertexAttrib2f = i++; //new -GLmethod.vertexAttrib3f = i++; //new -GLmethod.vertexAttrib4f = i++; //new //130 -GLmethod.vertexAttrib1fv = i++; //new -GLmethod.vertexAttrib2fv = i++; //new -GLmethod.vertexAttrib3fv = i++; //new -GLmethod.vertexAttrib4fv = i++; //new -GLmethod.vertexAttribPointer = i++; -GLmethod.viewport = i++; - +let i = 1; + +const GLmethod = {}; + +GLmethod.activeTexture = i++; //1 +GLmethod.attachShader = i++; +GLmethod.bindAttribLocation = i++; +GLmethod.bindBuffer = i++; +GLmethod.bindFramebuffer = i++; +GLmethod.bindRenderbuffer = i++; +GLmethod.bindTexture = i++; +GLmethod.blendColor = i++; +GLmethod.blendEquation = i++; +GLmethod.blendEquationSeparate = i++; //10 +GLmethod.blendFunc = i++; +GLmethod.blendFuncSeparate = i++; +GLmethod.bufferData = i++; +GLmethod.bufferSubData = i++; +GLmethod.checkFramebufferStatus = i++; +GLmethod.clear = i++; +GLmethod.clearColor = i++; +GLmethod.clearDepth = i++; +GLmethod.clearStencil = i++; +GLmethod.colorMask = i++; //20 +GLmethod.compileShader = i++; +GLmethod.compressedTexImage2D = i++; +GLmethod.compressedTexSubImage2D = i++; +GLmethod.copyTexImage2D = i++; +GLmethod.copyTexSubImage2D = i++; +GLmethod.createBuffer = i++; +GLmethod.createFramebuffer = i++; +GLmethod.createProgram = i++; +GLmethod.createRenderbuffer = i++; +GLmethod.createShader = i++; //30 +GLmethod.createTexture = i++; +GLmethod.cullFace = i++; +GLmethod.deleteBuffer = i++; +GLmethod.deleteFramebuffer = i++; +GLmethod.deleteProgram = i++; +GLmethod.deleteRenderbuffer = i++; +GLmethod.deleteShader = i++; +GLmethod.deleteTexture = i++; +GLmethod.depthFunc = i++; +GLmethod.depthMask = i++; //40 +GLmethod.depthRange = i++; +GLmethod.detachShader = i++; +GLmethod.disable = i++; +GLmethod.disableVertexAttribArray = i++; +GLmethod.drawArrays = i++; +GLmethod.drawArraysInstancedANGLE = i++; +GLmethod.drawElements = i++; +GLmethod.drawElementsInstancedANGLE = i++; +GLmethod.enable = i++; +GLmethod.enableVertexAttribArray = i++; //50 +GLmethod.flush = i++; +GLmethod.framebufferRenderbuffer = i++; +GLmethod.framebufferTexture2D = i++; +GLmethod.frontFace = i++; +GLmethod.generateMipmap = i++; +GLmethod.getActiveAttrib = i++; +GLmethod.getActiveUniform = i++; +GLmethod.getAttachedShaders = i++; +GLmethod.getAttribLocation = i++; +GLmethod.getBufferParameter = i++; //60 +GLmethod.getContextAttributes = i++; +GLmethod.getError = i++; +GLmethod.getExtension = i++; +GLmethod.getFramebufferAttachmentParameter = i++; +GLmethod.getParameter = i++; +GLmethod.getProgramInfoLog = i++; +GLmethod.getProgramParameter = i++; +GLmethod.getRenderbufferParameter = i++; +GLmethod.getShaderInfoLog = i++; +GLmethod.getShaderParameter = i++; //70 +GLmethod.getShaderPrecisionFormat = i++; +GLmethod.getShaderSource = i++; +GLmethod.getSupportedExtensions = i++; +GLmethod.getTexParameter = i++; +GLmethod.getUniform = i++; +GLmethod.getUniformLocation = i++; +GLmethod.getVertexAttrib = i++; +GLmethod.getVertexAttribOffset = i++; +GLmethod.isBuffer = i++; +GLmethod.isContextLost = i++; //80 +GLmethod.isEnabled = i++; +GLmethod.isFramebuffer = i++; +GLmethod.isProgram = i++; +GLmethod.isRenderbuffer = i++; +GLmethod.isShader = i++; +GLmethod.isTexture = i++; +GLmethod.lineWidth = i++; +GLmethod.linkProgram = i++; +GLmethod.pixelStorei = i++; +GLmethod.polygonOffset = i++; //90 +GLmethod.readPixels = i++; +GLmethod.renderbufferStorage = i++; +GLmethod.sampleCoverage = i++; +GLmethod.scissor = i++; +GLmethod.shaderSource = i++; +GLmethod.stencilFunc = i++; +GLmethod.stencilFuncSeparate = i++; +GLmethod.stencilMask = i++; +GLmethod.stencilMaskSeparate = i++; +GLmethod.stencilOp = i++; //100 +GLmethod.stencilOpSeparate = i++; +GLmethod.texImage2D = i++; +GLmethod.texParameterf = i++; +GLmethod.texParameteri = i++; +GLmethod.texSubImage2D = i++; +GLmethod.uniform1f = i++; +GLmethod.uniform1fv = i++; +GLmethod.uniform1i = i++; +GLmethod.uniform1iv = i++; +GLmethod.uniform2f = i++; //110 +GLmethod.uniform2fv = i++; +GLmethod.uniform2i = i++; +GLmethod.uniform2iv = i++; +GLmethod.uniform3f = i++; +GLmethod.uniform3fv = i++; +GLmethod.uniform3i = i++; +GLmethod.uniform3iv = i++; +GLmethod.uniform4f = i++; +GLmethod.uniform4fv = i++; +GLmethod.uniform4i = i++; //120 +GLmethod.uniform4iv = i++; +GLmethod.uniformMatrix2fv = i++; +GLmethod.uniformMatrix3fv = i++; +GLmethod.uniformMatrix4fv = i++; +GLmethod.useProgram = i++; +GLmethod.validateProgram = i++; +GLmethod.vertexAttrib1f = i++; //new +GLmethod.vertexAttrib2f = i++; //new +GLmethod.vertexAttrib3f = i++; //new +GLmethod.vertexAttrib4f = i++; //new //130 +GLmethod.vertexAttrib1fv = i++; //new +GLmethod.vertexAttrib2fv = i++; //new +GLmethod.vertexAttrib3fv = i++; //new +GLmethod.vertexAttrib4fv = i++; //new +GLmethod.vertexAttribPointer = i++; +GLmethod.viewport = i++; + export default GLmethod; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLtype.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLtype.js index 695abcbe..d7d81bb6 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLtype.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLtype.js @@ -1,23 +1,23 @@ -const GLtype = {}; - -[ - "GLbitfield", - "GLboolean", - "GLbyte", - "GLclampf", - "GLenum", - "GLfloat", - "GLint", - "GLintptr", - "GLsizei", - "GLsizeiptr", - "GLshort", - "GLubyte", - "GLuint", - "GLushort" -].sort().map((typeName, i) => GLtype[typeName] = 1 >> (i + 1)); - -export default GLtype; - - - +const GLtype = {}; + +[ + "GLbitfield", + "GLboolean", + "GLbyte", + "GLclampf", + "GLenum", + "GLfloat", + "GLint", + "GLintptr", + "GLsizei", + "GLsizeiptr", + "GLshort", + "GLubyte", + "GLuint", + "GLushort" +].sort().map((typeName, i) => GLtype[typeName] = 1 >> (i + 1)); + +export default GLtype; + + + diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Program.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Program.js index 6f5691cd..bd69737b 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Program.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Program.js @@ -1,21 +1,21 @@ -import {getTransferedObjectUUID} from './classUtils'; - -const name = 'WebGLProgram'; - -function uuid(id) { - return getTransferedObjectUUID(name, id); -} - -export default class WebGLProgram { - className = name; - - constructor(id) { - this.id = id; - } - - static uuid = uuid; - - uuid() { - return uuid(this.id); - } +import {getTransferedObjectUUID} from './classUtils'; + +const name = 'WebGLProgram'; + +function uuid(id) { + return getTransferedObjectUUID(name, id); +} + +export default class WebGLProgram { + className = name; + + constructor(id) { + this.id = id; + } + + static uuid = uuid; + + uuid() { + return uuid(this.id); + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Renderbuffer.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Renderbuffer.js index d3182ae8..3f500f00 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Renderbuffer.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Renderbuffer.js @@ -1,21 +1,21 @@ -import {getTransferedObjectUUID} from './classUtils'; - -const name = 'WebGLRenderBuffer'; - -function uuid(id) { - return getTransferedObjectUUID(name, id); -} - -export default class WebGLRenderbuffer { - className = name; - - constructor(id) { - this.id = id; - } - - static uuid = uuid; - - uuid() { - return uuid(this.id); - } +import {getTransferedObjectUUID} from './classUtils'; + +const name = 'WebGLRenderBuffer'; + +function uuid(id) { + return getTransferedObjectUUID(name, id); +} + +export default class WebGLRenderbuffer { + className = name; + + constructor(id) { + this.id = id; + } + + static uuid = uuid; + + uuid() { + return uuid(this.id); + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/RenderingContext.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/RenderingContext.js index 5f9608f1..c004883f 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/RenderingContext.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/RenderingContext.js @@ -1,1191 +1,1191 @@ -import GLenum from './GLenum'; -import ActiveInfo from './ActiveInfo'; -import Buffer from './Buffer'; -import Framebuffer from './Framebuffer'; -import Renderbuffer from './Renderbuffer'; -import Texture from './Texture'; -import Program from './Program'; -import Shader from './Shader'; -import ShaderPrecisionFormat from './ShaderPrecisionFormat'; -import UniformLocation from './UniformLocation'; -import GLmethod from './GLmethod'; - -const processArray = (array, checkArrayType = false) => { - - function joinArray(arr, sep) { - let res = ''; - for (let i = 0; i < arr.length; i++) { - if (i !== 0) { - res += sep; - } - res += arr[i]; - } - return res; - } - - let type = 'Float32Array'; - if (checkArrayType) { - if (array instanceof Uint8Array) { - type = 'Uint8Array' - } else if (array instanceof Uint16Array) { - type = 'Uint16Array'; - } else if (array instanceof Uint32Array) { - type = 'Uint32Array'; - } else if (array instanceof Float32Array) { - type = 'Float32Array'; - } else { - throw new Error('Check array type failed. Array type is ' + typeof array); - } - } - - const ArrayTypes = { - Uint8Array: 1, - Uint16Array: 2, - Uint32Array: 4, - Float32Array: 14 - }; - return ArrayTypes[type] + ',' + btoa(joinArray(array, ',')) -} - -export default class WebGLRenderingContext { - - // static GBridge = null; - - className = 'WebGLRenderingContext'; - - constructor(canvas, type, attrs) { - this._canvas = canvas; - this._type = type; - this._version = 'WebGL 1.0'; - this._attrs = attrs; - this._map = new Map(); - - Object.keys(GLenum) - .forEach(name => Object.defineProperty(this, name, { - value: GLenum[name] - })); - } - - get canvas() { - return this._canvas; - } - - activeTexture = function (textureUnit) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.activeTexture + ',' + textureUnit, - true - ); - } - - attachShader = function (progarm, shader) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.attachShader + ',' + progarm.id + ',' + shader.id, - true - ); - } - - bindAttribLocation = function (program, index, name) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.bindAttribLocation + ',' + program.id + ',' + index + ',' + name, - true - ) - } - - bindBuffer = function (target, buffer) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.bindBuffer + ',' + target + ',' + (buffer ? buffer.id : 0), - true - ); - } - - bindFramebuffer = function (target, framebuffer) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.bindFramebuffer + ',' + target + ',' + (framebuffer ? framebuffer.id : 0), - true - ) - } - - bindRenderbuffer = function (target, renderBuffer) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.bindRenderbuffer + ',' + target + ',' + (renderBuffer ? renderBuffer.id : 0), - true - ) - } - - bindTexture = function (target, texture) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.bindTexture + ',' + target + ',' + (texture ? texture.id : 0), - true - ) - } - - blendColor = function (r, g, b, a) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.blendColor + ',' + target + ',' + r + ',' + g + ',' + b + ',' + a, - true - ) - } - - blendEquation = function (mode) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.blendEquation + ',' + mode, - true - ) - } - - blendEquationSeparate = function (modeRGB, modeAlpha) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.blendEquationSeparate + ',' + modeRGB + ',' + modeAlpha, - true - ) - } - - - blendFunc = function (sfactor, dfactor) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.blendFunc + ',' + sfactor + ',' + dfactor, - true - ); - } - - blendFuncSeparate = function (srcRGB, dstRGB, srcAlpha, dstAlpha) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.blendFuncSeparate + ',' + srcRGB + ',' + dstRGB + ',' + srcAlpha + ',' + dstAlpha, - true - ); - } - - bufferData = function (target, data, usage) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.bufferData + ',' + target + ',' + processArray(data, true) + ',' + usage, - true - ) - } - - bufferSubData = function (target, offset, data) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.bufferSubData + ',' + target + ',' + offset + ',' + processArray(data, true), - true - ) - } - - checkFramebufferStatus = function (target) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.checkFramebufferStatus + ',' + target - ); - return Number(result); - } - - clear = function (mask) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.clear + ',' + mask - ); - this._canvas._needRender = true; - } - - clearColor = function (r, g, b, a) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.clearColor + ',' + r + ',' + g + ',' + b, - true - ) - } - - clearDepth = function (depth) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.clearDepth + ',' + depth, - true - ) - } - - clearStencil = function (s) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.clearStencil + ',' + s - ); - } - - colorMask = function (r, g, b, a) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.colorMask + ',' + r + ',' + g + ',' + b + ',' + a - ) - } - - compileShader = function (shader) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.compileShader + ',' + shader.id, - true - ) - } - - compressedTexImage2D = function (target, level, internalformat, width, height, border, pixels) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.compressedTexImage2D + ',' + target + ',' + level + ',' + internalformat + ',' + - width + ',' + height + ',' + border + ',' + processArray(pixels), - true - ) - } - - compressedTexSubImage2D = function (target, level, xoffset, yoffset, width, height, format, pixels) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.compressedTexSubImage2D + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset + ',' + - width + ',' + height + ',' + format + ',' + processArray(pixels), - true - ) - } - - - copyTexImage2D = function (target, level, internalformat, x, y, width, height, border) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.copyTexImage2D + ',' + target + ',' + level + ',' + internalformat + ',' + x + ',' + y + ',' + - width + ',' + height + ',' + border, - true - ); - } - - copyTexSubImage2D = function (target, level, xoffset, yoffset, x, y, width, height) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.copyTexSubImage2D + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset + ',' + x + ',' + y + ',' + - width + ',' + height - ); - } - - createBuffer = function () { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.createBuffer + '' - ); - const buffer = new Buffer(result); - this._map.set(buffer.uuid(), buffer); - return buffer; - } - - createFramebuffer = function () { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.createFramebuffer + '' - ); - const framebuffer = new Framebuffer(result); - this._map.set(framebuffer.uuid(), framebuffer); - return framebuffer; - } - - - createProgram = function () { - const id = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.createProgram + '' - ); - const program = new Program(id); - this._map.set(program.uuid(), program); - return program; - } - - createRenderbuffer = function () { - const id = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.createRenderbuffer + '' - ) - const renderBuffer = new Renderbuffer(id); - this._map.set(renderBuffer.uuid(), renderBuffer); - return renderBuffer; - } - - createShader = function (type) { - const id = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.createShader + ',' + type - ) - const shader = new Shader(id, type); - this._map.set(shader.uuid(), shader); - return shader; - } - - createTexture = function () { - const id = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.createTexture + '' - ); - const texture = new Texture(id); - this._map.set(texture.uuid(), texture); - return texture; - } - - cullFace = function (mode) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.cullFace + ',' + mode, - true - ) - } - - - deleteBuffer = function (buffer) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.deleteBuffer + ',' + buffer.id, - true - ) - } - - deleteFramebuffer = function (framebuffer) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.deleteFramebuffer + ',' + framebuffer.id, - true - ) - } - - deleteProgram = function (program) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.deleteProgram + ',' + program.id, - true - ) - } - - deleteRenderbuffer = function (renderbuffer) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.deleteRenderbuffer + ',' + renderbuffer.id, - true - ) - } - - deleteShader = function (shader) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.deleteShader + ',' + shader.id, - true - ) - } - - deleteTexture = function (texture) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.deleteTexture + ',' + texture.id, - true - ) - } - - depthFunc = function (func) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.depthFunc + ',' + func - ) - } - - depthMask = function (flag) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.depthMask + ',' + Number(flag), - true - ) - } - - depthRange = function (zNear, zFar) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.depthRange + ',' + zNear + ',' + zFar, - true - ) - } - - detachShader = function (program, shader) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.detachShader + ',' + program.id + ',' + shader.id, - true - ) - } - - disable = function (cap) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.disable + ',' + cap, - true - ) - } - - disableVertexAttribArray = function (index) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.disableVertexAttribArray + ',' + index, - true - ); - } - - drawArrays = function (mode, first, count) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.drawArrays + ',' + mode + ',' + first + ',' + count - ) - this._canvas._needRender = true; - } - - drawElements = function (mode, count, type, offset) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.drawElements + ',' + mode + ',' + count + ',' + type + ',' + offset + ';' - ); - this._canvas._needRender = true; - } - - enable = function (cap) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.enable + ',' + cap, - true - ); - } - - enableVertexAttribArray = function (index) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.enableVertexAttribArray + ',' + index, - true - ) - } - - - flush = function () { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.flush + '' - ) - } - - framebufferRenderbuffer = function (target, attachment, textarget, texture, level) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.framebufferRenderbuffer + ',' + target + ',' + attachment + ',' + textarget + ',' + (texture ? texture.id : 0) + ',' + level, - true - ) - } - - framebufferTexture2D = function (target, attachment, textarget, texture, level) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.framebufferTexture2D + ',' + target + ',' + attachment + ',' + textarget + ',' + (texture ? texture.id : 0) + ',' + level, - true - ) - } - - frontFace = function (mode) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.frontFace + ',' + mode, - true - ) - } - - generateMipmap = function (target) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.generateMipmap + ',' + target, - true - ) - } - - getActiveAttrib = function (progarm, index) { - const resultString = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getActiveAttrib + ',' + progarm.id + ',' + index - ) - const [type, size, name] = resultString.split(','); - return new ActiveInfo({ - type: Number(type), - size: Number(size), - name - }); - } - - getActiveUniform = function (progarm, index) { - const resultString = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getActiveUniform + ',' + progarm.id + ',' + index - ); - const [type, size, name] = resultString.split(','); - return new ActiveInfo({ - type: Number(type), - size: Number(size), - name - }) - } - - getAttachedShaders = function (progarm) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getAttachedShaders + ',' + progarm.id - ); - const [type, ...ids] = result; - return ids.map(id => this._map.get(Shader.uuid(id))); - } - - getAttribLocation = function (progarm, name) { - return WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getAttribLocation + ',' + progarm.id + ',' + name - ) - } - - getBufferParameter = function (target, pname) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getBufferParameter + ',' + target + ',' + pname - ); - const [type, res] = getBufferParameter; - return res; - } - - getError = function () { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getError + '' - ) - return result; - } - - getExtension = function (name) { - return null; - } - - getFramebufferAttachmentParameter = function (target, attachment, pname) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getFramebufferAttachmentParameter + ',' + target + ',' + attachment + ',' + pname - ) - switch (pname) { - case GLenum.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - return this._map.get(Renderbuffer.uuid(result)) || this._map.get(Texture.uuid(result)) || null; - default: - return result; - } - } - - getParameter = function (pname) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getParameter + ',' + pname - ) - switch (pname) { - case GLenum.VERSION: - return this._version; - case GLenum.ARRAY_BUFFER_BINDING: // buffer - case GLenum.ELEMENT_ARRAY_BUFFER_BINDING: // buffer - return this._map.get(Buffer.uuid(result)) || null; - case GLenum.CURRENT_PROGRAM: // program - return this._map.get(Program.uuid(result)) || null; - case GLenum.FRAMEBUFFER_BINDING: // framebuffer - return this._map.get(Framebuffer.uuid(result)) || null; - case GLenum.RENDERBUFFER_BINDING: // renderbuffer - return this._map.get(Renderbuffer.uuid(result)) || null; - case GLenum.TEXTURE_BINDING_2D: // texture - case GLenum.TEXTURE_BINDING_CUBE_MAP: // texture - return this._map.get(Texture.uuid(result)) || null; - case GLenum.ALIASED_LINE_WIDTH_RANGE: // Float32Array - case GLenum.ALIASED_POINT_SIZE_RANGE: // Float32Array - case GLenum.BLEND_COLOR: // Float32Array - case GLenum.COLOR_CLEAR_VALUE: // Float32Array - case GLenum.DEPTH_RANGE: // Float32Array - case GLenum.MAX_VIEWPORT_DIMS: // Int32Array - case GLenum.SCISSOR_BOX: // Int32Array - case GLenum.VIEWPORT: // Int32Array - case GLenum.COMPRESSED_TEXTURE_FORMATS: // Uint32Array - default: - const [type, ...res] = result.split(','); - if (res.length === 1) { - return Number(res[0]); - } else { - return res.map(Number); - } - } - } - - getProgramInfoLog = function (progarm) { - return WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getProgramInfoLog + ',' + progarm.id - ) - } - - getProgramParameter = function (program, pname) { - const res = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getProgramParameter + ',' + program.id + ',' + pname - ); - - const [type, result] = res.split(',').map(i => parseInt(i)); - - if (type === 1) { - return Boolean(result); - } else if (type === 2) { - return result; - } else { - throw new Error('Unrecongized program paramater ' + res + ', type: ' + typeof res); - } - } - - - getRenderbufferParameter = function (target, pname) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getRenderbufferParameter + ',' + target + ',' + pname - ) - return result; - } - - - getShaderInfoLog = function (shader) { - return WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getShaderInfoLog + ',' + shader.id - ); - } - - getShaderParameter = function (shader, pname) { - return WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getShaderParameter + ',' + shader.id + ',' + pname - ) - } - - getShaderPrecisionFormat = function (shaderType, precisionType) { - const [rangeMin, rangeMax, precision] = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getShaderPrecisionFormat + ',' + shaderType + ',' + precisionType - ); - const shaderPrecisionFormat = new ShaderPrecisionFormat({ - rangeMin: Number(rangeMin), - rangeMax: Number(rangeMax), - precision: Number(precision) - }); - return shaderPrecisionFormat; - } - - getShaderSource = function (shader) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getShaderSource + ',' + shader.id - ); - return result; - } - - getSupportedExtensions = function () { - return Object.keys({}); - } - - getTexParameter = function (target, pname) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getTexParameter + ',' + target + ',' + pname - ) - return result; - } - - getUniformLocation = function (program, name) { - const id = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getUniformLocation + ',' + program.id + ',' + name - ); - if (id === -1) { - return null; - } else { - return new UniformLocation(Number(id)); - } - } - - getVertexAttrib = function (index, pname) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getVertexAttrib + ',' + index + ',' + pname - ); - switch (pname) { - case GLenum.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: - return this._map.get(Buffer.uuid(result)) || null; - case GLenum.CURRENT_VERTEX_ATTRIB: // Float32Array - default: - return result; - } - } - - getVertexAttribOffset = function (index, pname) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.getVertexAttribOffset + ',' + index + ',' + pname - ) - return Number(result); - } - - isBuffer = function (buffer) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.isBuffer + ',' + buffer.id - ) - return Boolean(result); - } - - isContextLost = function () { - return false; - } - - isEnabled = function (cap) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.isEnabled + ',' + cap - ) - return Boolean(result); - } - - isFramebuffer = function (framebuffer) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.isFramebuffer + ',' + framebuffer.id - ) - return Boolean(result); - } - - isProgram = function (program) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.isProgram + ',' + program.id - ) - return Boolean(result); - } - - isRenderbuffer = function (renderBuffer) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.isRenderbuffer + ',' + renderbuffer.id - ) - return Boolean(result); - } - - isShader = function (shader) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.isShader + ',' + shader.id - ) - return Boolean(result); - } - - isTexture = function (texture) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.isTexture + ',' + texture.id - ); - return Boolean(result); - } - - lineWidth = function (width) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.lineWidth + ',' + width, - true - ) - } - - linkProgram = function (program) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.linkProgram + ',' + program.id, - true - ); - } - - - pixelStorei = function (pname, param) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.pixelStorei + ',' + pname + ',' + Number(param) - ) - } - - polygonOffset = function (factor, units) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.polygonOffset + ',' + factor + ',' + units - ) - } - - readPixels = function (x, y, width, height, format, type, pixels) { - const result = WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.readPixels + ',' + x + ',' + y + ',' + width + ',' + height + ',' + format + ',' + type - ) - return result; - } - - renderbufferStorage = function (target, internalFormat, width, height) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.renderbufferStorage + ',' + target + ',' + internalFormat + ',' + width + ',' + height, - true - ) - } - - sampleCoverage = function (value, invert) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.sampleCoverage + ',' + value + ',' + Number(invert), - true - ) - } - - scissor = function (x, y, width, height) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.scissor + ',' + x + ',' + y + ',' + width + ',' + height, - true - ) - } - - shaderSource = function (shader, source) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.shaderSource + ',' + shader.id + ',' + source - ) - } - - stencilFunc = function (func, ref, mask) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.stencilFunc + ',' + func + ',' + ref + ',' + mask, - true - ) - } - - stencilFuncSeparate = function (face, func, ref, mask) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.stencilFuncSeparate + ',' + face + ',' + func + ',' + ref + ',' + mask, - true - ) - } - - stencilMask = function (mask) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.stencilMask + ',' + mask, - true - ) - } - - stencilMaskSeparate = function (face, mask) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.stencilMaskSeparate + ',' + face + ',' + mask, - true - ) - } - - stencilOp = function (fail, zfail, zpass) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.stencilOp + ',' + fail + ',' + zfail + ',' + zpass - ) - } - - stencilOpSeparate = function (face, fail, zfail, zpass) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.stencilOp + ',' + face + ',' + fail + ',' + zfail + ',' + zpass, - true - ) - } - - texImage2D = function (...args) { - WebGLRenderingContext.GBridge.texImage2D(this._canvas.id, ...args); - } - - - texParameterf = function (target, pname, param) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.texParameterf + ',' + target + ',' + pname + ',' + param, - true - ) - } - - texParameteri = function (target, pname, param) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.texParameteri + ',' + target + ',' + pname + ',' + param - ) - } - - texSubImage2D = function (...args) { - WebGLRenderingContext.GBridge.texSubImage2D(this._canvas.id, ...args); - } - - uniform1f = function (location, v0) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform1f + ',' + location.id + ',' + v0 - ) - } - - uniform1fv = function (location, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform1fv + ',' + location.id + ',' + processArray(value), - true - ) - } - - uniform1i = function (location, v0) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform1i + ',' + location.id + ',' + v0, - // true - ) - } - - uniform1iv = function (location, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform1iv + ',' + location.id + ',' + processArray(value), - true - ) - } - - uniform2f = function (location, v0, v1) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform2f + ',' + location.id + ',' + v0 + ',' + v1, - true - ) - } - - uniform2fv = function (location, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform2fv + ',' + location.id + ',' + processArray(value), - true - ) - } - - uniform2i = function (location, v0, v1) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform2i + ',' + location.id + ',' + v0 + ',' + v1, - true - ) - } - - uniform2iv = function (location, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform2iv + ',' + location.id + ',' + processArray(value), - true - ) - } - - uniform3f = function (location, v0, v1, v2) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform3f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2, - true - ) - } - - uniform3fv = function (location, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform3fv + ',' + location.id + ',' + processArray(value), - true - ) - } - - uniform3i = function (location, v0, v1, v2) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform3i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2, - true - ) - } - - uniform3iv = function (location, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform3iv + ',' + location.id + ',' + processArray(value), - true - ) - } - - uniform4f = function (location, v0, v1, v2, v3) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform4f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3, - true - ) - } - - uniform4fv = function (location, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform4fv + ',' + location.id + ',' + processArray(value), - true - ) - } - - uniform4i = function (location, v0, v1, v2, v3) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform4i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3, - true - ) - } - - uniform4iv = function (location, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniform4iv + ',' + location.id + ',' + processArray(value, true), - true - ) - } - - uniformMatrix2fv = function (location, transpose, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniformMatrix2fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value), - true - ) - } - - uniformMatrix3fv = function (location, transpose, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniformMatrix3fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value), - true - ) - } - - uniformMatrix4fv = function (location, transpose, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.uniformMatrix4fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value), - true - ); - } - - useProgram = function (progarm) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.useProgram + ',' + progarm.id + '', - true - ) - } - - - validateProgram = function (program) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.validateProgram + ',' + program.id, - true - ) - } - - vertexAttrib1f = function (index, v0) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttrib1f + ',' + index + ',' + v0, - true - ) - } - - vertexAttrib2f = function (index, v0, v1) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttrib2f + ',' + index + ',' + v0 + ',' + v1, - true - ) - } - - vertexAttrib3f = function (index, v0, v1, v2) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttrib3f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2, - true - ) - } - - vertexAttrib4f = function (index, v0, v1, v2, v3) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttrib4f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3, - true - ) - } - - vertexAttrib1fv = function (index, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttrib1fv + ',' + index + ',' + processArray(value), - true - ) - } - - vertexAttrib2fv = function (index, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttrib2fv + ',' + index + ',' + processArray(value), - true - ) - } - - vertexAttrib3fv = function (index, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttrib3fv + ',' + index + ',' + processArray(value), - true - ) - } - - vertexAttrib4fv = function (index, value) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttrib4fv + ',' + index + ',' + processArray(value), - true - ) - } - - vertexAttribPointer = function (index, size, type, normalized, stride, offset) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.vertexAttribPointer + ',' + index + ',' + size + ',' + type + ',' + Number(normalized) + ',' + stride + ',' + offset, - true - ) - } - - viewport = function (x, y, width, height) { - WebGLRenderingContext.GBridge.callNative( - this._canvas.id, - GLmethod.viewport + ',' + x + ',' + y + ',' + width + ',' + height, - true - ) - } +import GLenum from './GLenum'; +import ActiveInfo from './ActiveInfo'; +import Buffer from './Buffer'; +import Framebuffer from './Framebuffer'; +import Renderbuffer from './Renderbuffer'; +import Texture from './Texture'; +import Program from './Program'; +import Shader from './Shader'; +import ShaderPrecisionFormat from './ShaderPrecisionFormat'; +import UniformLocation from './UniformLocation'; +import GLmethod from './GLmethod'; + +const processArray = (array, checkArrayType = false) => { + + function joinArray(arr, sep) { + let res = ''; + for (let i = 0; i < arr.length; i++) { + if (i !== 0) { + res += sep; + } + res += arr[i]; + } + return res; + } + + let type = 'Float32Array'; + if (checkArrayType) { + if (array instanceof Uint8Array) { + type = 'Uint8Array' + } else if (array instanceof Uint16Array) { + type = 'Uint16Array'; + } else if (array instanceof Uint32Array) { + type = 'Uint32Array'; + } else if (array instanceof Float32Array) { + type = 'Float32Array'; + } else { + throw new Error('Check array type failed. Array type is ' + typeof array); + } + } + + const ArrayTypes = { + Uint8Array: 1, + Uint16Array: 2, + Uint32Array: 4, + Float32Array: 14 + }; + return ArrayTypes[type] + ',' + btoa(joinArray(array, ',')) +} + +export default class WebGLRenderingContext { + + // static GBridge = null; + + className = 'WebGLRenderingContext'; + + constructor(canvas, type, attrs) { + this._canvas = canvas; + this._type = type; + this._version = 'WebGL 1.0'; + this._attrs = attrs; + this._map = new Map(); + + Object.keys(GLenum) + .forEach(name => Object.defineProperty(this, name, { + value: GLenum[name] + })); + } + + get canvas() { + return this._canvas; + } + + activeTexture = function (textureUnit) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.activeTexture + ',' + textureUnit, + true + ); + } + + attachShader = function (progarm, shader) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.attachShader + ',' + progarm.id + ',' + shader.id, + true + ); + } + + bindAttribLocation = function (program, index, name) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.bindAttribLocation + ',' + program.id + ',' + index + ',' + name, + true + ) + } + + bindBuffer = function (target, buffer) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.bindBuffer + ',' + target + ',' + (buffer ? buffer.id : 0), + true + ); + } + + bindFramebuffer = function (target, framebuffer) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.bindFramebuffer + ',' + target + ',' + (framebuffer ? framebuffer.id : 0), + true + ) + } + + bindRenderbuffer = function (target, renderBuffer) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.bindRenderbuffer + ',' + target + ',' + (renderBuffer ? renderBuffer.id : 0), + true + ) + } + + bindTexture = function (target, texture) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.bindTexture + ',' + target + ',' + (texture ? texture.id : 0), + true + ) + } + + blendColor = function (r, g, b, a) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.blendColor + ',' + target + ',' + r + ',' + g + ',' + b + ',' + a, + true + ) + } + + blendEquation = function (mode) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.blendEquation + ',' + mode, + true + ) + } + + blendEquationSeparate = function (modeRGB, modeAlpha) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.blendEquationSeparate + ',' + modeRGB + ',' + modeAlpha, + true + ) + } + + + blendFunc = function (sfactor, dfactor) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.blendFunc + ',' + sfactor + ',' + dfactor, + true + ); + } + + blendFuncSeparate = function (srcRGB, dstRGB, srcAlpha, dstAlpha) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.blendFuncSeparate + ',' + srcRGB + ',' + dstRGB + ',' + srcAlpha + ',' + dstAlpha, + true + ); + } + + bufferData = function (target, data, usage) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.bufferData + ',' + target + ',' + processArray(data, true) + ',' + usage, + true + ) + } + + bufferSubData = function (target, offset, data) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.bufferSubData + ',' + target + ',' + offset + ',' + processArray(data, true), + true + ) + } + + checkFramebufferStatus = function (target) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.checkFramebufferStatus + ',' + target + ); + return Number(result); + } + + clear = function (mask) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.clear + ',' + mask + ); + this._canvas._needRender = true; + } + + clearColor = function (r, g, b, a) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.clearColor + ',' + r + ',' + g + ',' + b, + true + ) + } + + clearDepth = function (depth) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.clearDepth + ',' + depth, + true + ) + } + + clearStencil = function (s) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.clearStencil + ',' + s + ); + } + + colorMask = function (r, g, b, a) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.colorMask + ',' + r + ',' + g + ',' + b + ',' + a + ) + } + + compileShader = function (shader) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.compileShader + ',' + shader.id, + true + ) + } + + compressedTexImage2D = function (target, level, internalformat, width, height, border, pixels) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.compressedTexImage2D + ',' + target + ',' + level + ',' + internalformat + ',' + + width + ',' + height + ',' + border + ',' + processArray(pixels), + true + ) + } + + compressedTexSubImage2D = function (target, level, xoffset, yoffset, width, height, format, pixels) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.compressedTexSubImage2D + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset + ',' + + width + ',' + height + ',' + format + ',' + processArray(pixels), + true + ) + } + + + copyTexImage2D = function (target, level, internalformat, x, y, width, height, border) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.copyTexImage2D + ',' + target + ',' + level + ',' + internalformat + ',' + x + ',' + y + ',' + + width + ',' + height + ',' + border, + true + ); + } + + copyTexSubImage2D = function (target, level, xoffset, yoffset, x, y, width, height) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.copyTexSubImage2D + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset + ',' + x + ',' + y + ',' + + width + ',' + height + ); + } + + createBuffer = function () { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.createBuffer + '' + ); + const buffer = new Buffer(result); + this._map.set(buffer.uuid(), buffer); + return buffer; + } + + createFramebuffer = function () { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.createFramebuffer + '' + ); + const framebuffer = new Framebuffer(result); + this._map.set(framebuffer.uuid(), framebuffer); + return framebuffer; + } + + + createProgram = function () { + const id = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.createProgram + '' + ); + const program = new Program(id); + this._map.set(program.uuid(), program); + return program; + } + + createRenderbuffer = function () { + const id = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.createRenderbuffer + '' + ) + const renderBuffer = new Renderbuffer(id); + this._map.set(renderBuffer.uuid(), renderBuffer); + return renderBuffer; + } + + createShader = function (type) { + const id = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.createShader + ',' + type + ) + const shader = new Shader(id, type); + this._map.set(shader.uuid(), shader); + return shader; + } + + createTexture = function () { + const id = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.createTexture + '' + ); + const texture = new Texture(id); + this._map.set(texture.uuid(), texture); + return texture; + } + + cullFace = function (mode) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.cullFace + ',' + mode, + true + ) + } + + + deleteBuffer = function (buffer) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.deleteBuffer + ',' + buffer.id, + true + ) + } + + deleteFramebuffer = function (framebuffer) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.deleteFramebuffer + ',' + framebuffer.id, + true + ) + } + + deleteProgram = function (program) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.deleteProgram + ',' + program.id, + true + ) + } + + deleteRenderbuffer = function (renderbuffer) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.deleteRenderbuffer + ',' + renderbuffer.id, + true + ) + } + + deleteShader = function (shader) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.deleteShader + ',' + shader.id, + true + ) + } + + deleteTexture = function (texture) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.deleteTexture + ',' + texture.id, + true + ) + } + + depthFunc = function (func) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.depthFunc + ',' + func + ) + } + + depthMask = function (flag) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.depthMask + ',' + Number(flag), + true + ) + } + + depthRange = function (zNear, zFar) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.depthRange + ',' + zNear + ',' + zFar, + true + ) + } + + detachShader = function (program, shader) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.detachShader + ',' + program.id + ',' + shader.id, + true + ) + } + + disable = function (cap) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.disable + ',' + cap, + true + ) + } + + disableVertexAttribArray = function (index) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.disableVertexAttribArray + ',' + index, + true + ); + } + + drawArrays = function (mode, first, count) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.drawArrays + ',' + mode + ',' + first + ',' + count + ) + this._canvas._needRender = true; + } + + drawElements = function (mode, count, type, offset) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.drawElements + ',' + mode + ',' + count + ',' + type + ',' + offset + ';' + ); + this._canvas._needRender = true; + } + + enable = function (cap) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.enable + ',' + cap, + true + ); + } + + enableVertexAttribArray = function (index) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.enableVertexAttribArray + ',' + index, + true + ) + } + + + flush = function () { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.flush + '' + ) + } + + framebufferRenderbuffer = function (target, attachment, textarget, texture, level) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.framebufferRenderbuffer + ',' + target + ',' + attachment + ',' + textarget + ',' + (texture ? texture.id : 0) + ',' + level, + true + ) + } + + framebufferTexture2D = function (target, attachment, textarget, texture, level) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.framebufferTexture2D + ',' + target + ',' + attachment + ',' + textarget + ',' + (texture ? texture.id : 0) + ',' + level, + true + ) + } + + frontFace = function (mode) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.frontFace + ',' + mode, + true + ) + } + + generateMipmap = function (target) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.generateMipmap + ',' + target, + true + ) + } + + getActiveAttrib = function (progarm, index) { + const resultString = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getActiveAttrib + ',' + progarm.id + ',' + index + ) + const [type, size, name] = resultString.split(','); + return new ActiveInfo({ + type: Number(type), + size: Number(size), + name + }); + } + + getActiveUniform = function (progarm, index) { + const resultString = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getActiveUniform + ',' + progarm.id + ',' + index + ); + const [type, size, name] = resultString.split(','); + return new ActiveInfo({ + type: Number(type), + size: Number(size), + name + }) + } + + getAttachedShaders = function (progarm) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getAttachedShaders + ',' + progarm.id + ); + const [type, ...ids] = result; + return ids.map(id => this._map.get(Shader.uuid(id))); + } + + getAttribLocation = function (progarm, name) { + return WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getAttribLocation + ',' + progarm.id + ',' + name + ) + } + + getBufferParameter = function (target, pname) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getBufferParameter + ',' + target + ',' + pname + ); + const [type, res] = getBufferParameter; + return res; + } + + getError = function () { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getError + '' + ) + return result; + } + + getExtension = function (name) { + return null; + } + + getFramebufferAttachmentParameter = function (target, attachment, pname) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getFramebufferAttachmentParameter + ',' + target + ',' + attachment + ',' + pname + ) + switch (pname) { + case GLenum.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + return this._map.get(Renderbuffer.uuid(result)) || this._map.get(Texture.uuid(result)) || null; + default: + return result; + } + } + + getParameter = function (pname) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getParameter + ',' + pname + ) + switch (pname) { + case GLenum.VERSION: + return this._version; + case GLenum.ARRAY_BUFFER_BINDING: // buffer + case GLenum.ELEMENT_ARRAY_BUFFER_BINDING: // buffer + return this._map.get(Buffer.uuid(result)) || null; + case GLenum.CURRENT_PROGRAM: // program + return this._map.get(Program.uuid(result)) || null; + case GLenum.FRAMEBUFFER_BINDING: // framebuffer + return this._map.get(Framebuffer.uuid(result)) || null; + case GLenum.RENDERBUFFER_BINDING: // renderbuffer + return this._map.get(Renderbuffer.uuid(result)) || null; + case GLenum.TEXTURE_BINDING_2D: // texture + case GLenum.TEXTURE_BINDING_CUBE_MAP: // texture + return this._map.get(Texture.uuid(result)) || null; + case GLenum.ALIASED_LINE_WIDTH_RANGE: // Float32Array + case GLenum.ALIASED_POINT_SIZE_RANGE: // Float32Array + case GLenum.BLEND_COLOR: // Float32Array + case GLenum.COLOR_CLEAR_VALUE: // Float32Array + case GLenum.DEPTH_RANGE: // Float32Array + case GLenum.MAX_VIEWPORT_DIMS: // Int32Array + case GLenum.SCISSOR_BOX: // Int32Array + case GLenum.VIEWPORT: // Int32Array + case GLenum.COMPRESSED_TEXTURE_FORMATS: // Uint32Array + default: + const [type, ...res] = result.split(','); + if (res.length === 1) { + return Number(res[0]); + } else { + return res.map(Number); + } + } + } + + getProgramInfoLog = function (progarm) { + return WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getProgramInfoLog + ',' + progarm.id + ) + } + + getProgramParameter = function (program, pname) { + const res = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getProgramParameter + ',' + program.id + ',' + pname + ); + + const [type, result] = res.split(',').map(i => parseInt(i)); + + if (type === 1) { + return Boolean(result); + } else if (type === 2) { + return result; + } else { + throw new Error('Unrecongized program paramater ' + res + ', type: ' + typeof res); + } + } + + + getRenderbufferParameter = function (target, pname) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getRenderbufferParameter + ',' + target + ',' + pname + ) + return result; + } + + + getShaderInfoLog = function (shader) { + return WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getShaderInfoLog + ',' + shader.id + ); + } + + getShaderParameter = function (shader, pname) { + return WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getShaderParameter + ',' + shader.id + ',' + pname + ) + } + + getShaderPrecisionFormat = function (shaderType, precisionType) { + const [rangeMin, rangeMax, precision] = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getShaderPrecisionFormat + ',' + shaderType + ',' + precisionType + ); + const shaderPrecisionFormat = new ShaderPrecisionFormat({ + rangeMin: Number(rangeMin), + rangeMax: Number(rangeMax), + precision: Number(precision) + }); + return shaderPrecisionFormat; + } + + getShaderSource = function (shader) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getShaderSource + ',' + shader.id + ); + return result; + } + + getSupportedExtensions = function () { + return Object.keys({}); + } + + getTexParameter = function (target, pname) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getTexParameter + ',' + target + ',' + pname + ) + return result; + } + + getUniformLocation = function (program, name) { + const id = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getUniformLocation + ',' + program.id + ',' + name + ); + if (id === -1) { + return null; + } else { + return new UniformLocation(Number(id)); + } + } + + getVertexAttrib = function (index, pname) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getVertexAttrib + ',' + index + ',' + pname + ); + switch (pname) { + case GLenum.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: + return this._map.get(Buffer.uuid(result)) || null; + case GLenum.CURRENT_VERTEX_ATTRIB: // Float32Array + default: + return result; + } + } + + getVertexAttribOffset = function (index, pname) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.getVertexAttribOffset + ',' + index + ',' + pname + ) + return Number(result); + } + + isBuffer = function (buffer) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.isBuffer + ',' + buffer.id + ) + return Boolean(result); + } + + isContextLost = function () { + return false; + } + + isEnabled = function (cap) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.isEnabled + ',' + cap + ) + return Boolean(result); + } + + isFramebuffer = function (framebuffer) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.isFramebuffer + ',' + framebuffer.id + ) + return Boolean(result); + } + + isProgram = function (program) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.isProgram + ',' + program.id + ) + return Boolean(result); + } + + isRenderbuffer = function (renderBuffer) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.isRenderbuffer + ',' + renderbuffer.id + ) + return Boolean(result); + } + + isShader = function (shader) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.isShader + ',' + shader.id + ) + return Boolean(result); + } + + isTexture = function (texture) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.isTexture + ',' + texture.id + ); + return Boolean(result); + } + + lineWidth = function (width) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.lineWidth + ',' + width, + true + ) + } + + linkProgram = function (program) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.linkProgram + ',' + program.id, + true + ); + } + + + pixelStorei = function (pname, param) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.pixelStorei + ',' + pname + ',' + Number(param) + ) + } + + polygonOffset = function (factor, units) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.polygonOffset + ',' + factor + ',' + units + ) + } + + readPixels = function (x, y, width, height, format, type, pixels) { + const result = WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.readPixels + ',' + x + ',' + y + ',' + width + ',' + height + ',' + format + ',' + type + ) + return result; + } + + renderbufferStorage = function (target, internalFormat, width, height) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.renderbufferStorage + ',' + target + ',' + internalFormat + ',' + width + ',' + height, + true + ) + } + + sampleCoverage = function (value, invert) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.sampleCoverage + ',' + value + ',' + Number(invert), + true + ) + } + + scissor = function (x, y, width, height) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.scissor + ',' + x + ',' + y + ',' + width + ',' + height, + true + ) + } + + shaderSource = function (shader, source) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.shaderSource + ',' + shader.id + ',' + source + ) + } + + stencilFunc = function (func, ref, mask) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.stencilFunc + ',' + func + ',' + ref + ',' + mask, + true + ) + } + + stencilFuncSeparate = function (face, func, ref, mask) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.stencilFuncSeparate + ',' + face + ',' + func + ',' + ref + ',' + mask, + true + ) + } + + stencilMask = function (mask) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.stencilMask + ',' + mask, + true + ) + } + + stencilMaskSeparate = function (face, mask) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.stencilMaskSeparate + ',' + face + ',' + mask, + true + ) + } + + stencilOp = function (fail, zfail, zpass) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.stencilOp + ',' + fail + ',' + zfail + ',' + zpass + ) + } + + stencilOpSeparate = function (face, fail, zfail, zpass) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.stencilOp + ',' + face + ',' + fail + ',' + zfail + ',' + zpass, + true + ) + } + + texImage2D = function (...args) { + WebGLRenderingContext.GBridge.texImage2D(this._canvas.id, ...args); + } + + + texParameterf = function (target, pname, param) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.texParameterf + ',' + target + ',' + pname + ',' + param, + true + ) + } + + texParameteri = function (target, pname, param) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.texParameteri + ',' + target + ',' + pname + ',' + param + ) + } + + texSubImage2D = function (...args) { + WebGLRenderingContext.GBridge.texSubImage2D(this._canvas.id, ...args); + } + + uniform1f = function (location, v0) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform1f + ',' + location.id + ',' + v0 + ) + } + + uniform1fv = function (location, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform1fv + ',' + location.id + ',' + processArray(value), + true + ) + } + + uniform1i = function (location, v0) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform1i + ',' + location.id + ',' + v0, + // true + ) + } + + uniform1iv = function (location, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform1iv + ',' + location.id + ',' + processArray(value), + true + ) + } + + uniform2f = function (location, v0, v1) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform2f + ',' + location.id + ',' + v0 + ',' + v1, + true + ) + } + + uniform2fv = function (location, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform2fv + ',' + location.id + ',' + processArray(value), + true + ) + } + + uniform2i = function (location, v0, v1) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform2i + ',' + location.id + ',' + v0 + ',' + v1, + true + ) + } + + uniform2iv = function (location, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform2iv + ',' + location.id + ',' + processArray(value), + true + ) + } + + uniform3f = function (location, v0, v1, v2) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform3f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2, + true + ) + } + + uniform3fv = function (location, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform3fv + ',' + location.id + ',' + processArray(value), + true + ) + } + + uniform3i = function (location, v0, v1, v2) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform3i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2, + true + ) + } + + uniform3iv = function (location, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform3iv + ',' + location.id + ',' + processArray(value), + true + ) + } + + uniform4f = function (location, v0, v1, v2, v3) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform4f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3, + true + ) + } + + uniform4fv = function (location, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform4fv + ',' + location.id + ',' + processArray(value), + true + ) + } + + uniform4i = function (location, v0, v1, v2, v3) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform4i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3, + true + ) + } + + uniform4iv = function (location, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniform4iv + ',' + location.id + ',' + processArray(value, true), + true + ) + } + + uniformMatrix2fv = function (location, transpose, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniformMatrix2fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value), + true + ) + } + + uniformMatrix3fv = function (location, transpose, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniformMatrix3fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value), + true + ) + } + + uniformMatrix4fv = function (location, transpose, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.uniformMatrix4fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value), + true + ); + } + + useProgram = function (progarm) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.useProgram + ',' + progarm.id + '', + true + ) + } + + + validateProgram = function (program) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.validateProgram + ',' + program.id, + true + ) + } + + vertexAttrib1f = function (index, v0) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttrib1f + ',' + index + ',' + v0, + true + ) + } + + vertexAttrib2f = function (index, v0, v1) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttrib2f + ',' + index + ',' + v0 + ',' + v1, + true + ) + } + + vertexAttrib3f = function (index, v0, v1, v2) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttrib3f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2, + true + ) + } + + vertexAttrib4f = function (index, v0, v1, v2, v3) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttrib4f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3, + true + ) + } + + vertexAttrib1fv = function (index, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttrib1fv + ',' + index + ',' + processArray(value), + true + ) + } + + vertexAttrib2fv = function (index, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttrib2fv + ',' + index + ',' + processArray(value), + true + ) + } + + vertexAttrib3fv = function (index, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttrib3fv + ',' + index + ',' + processArray(value), + true + ) + } + + vertexAttrib4fv = function (index, value) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttrib4fv + ',' + index + ',' + processArray(value), + true + ) + } + + vertexAttribPointer = function (index, size, type, normalized, stride, offset) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.vertexAttribPointer + ',' + index + ',' + size + ',' + type + ',' + Number(normalized) + ',' + stride + ',' + offset, + true + ) + } + + viewport = function (x, y, width, height) { + WebGLRenderingContext.GBridge.callNative( + this._canvas.id, + GLmethod.viewport + ',' + x + ',' + y + ',' + width + ',' + height, + true + ) + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Shader.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Shader.js index a7638861..2917b59d 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Shader.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Shader.js @@ -1,22 +1,22 @@ -import {getTransferedObjectUUID} from './classUtils'; - -const name = 'WebGLShader'; - -function uuid(id) { - return getTransferedObjectUUID(name, id); -} - -export default class WebGLShader { - className = name; - - constructor(id, type) { - this.id = id; - this.type = type; - } - - static uuid = uuid; - - uuid() { - return uuid(this.id); - } +import {getTransferedObjectUUID} from './classUtils'; + +const name = 'WebGLShader'; + +function uuid(id) { + return getTransferedObjectUUID(name, id); +} + +export default class WebGLShader { + className = name; + + constructor(id, type) { + this.id = id; + this.type = type; + } + + static uuid = uuid; + + uuid() { + return uuid(this.id); + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ShaderPrecisionFormat.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ShaderPrecisionFormat.js index 208d6c1a..8b70e9a0 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ShaderPrecisionFormat.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ShaderPrecisionFormat.js @@ -1,11 +1,11 @@ -export default class WebGLShaderPrecisionFormat { - className = 'WebGLShaderPrecisionFormat'; - - constructor({ - rangeMin, rangeMax, precision - }) { - this.rangeMin = rangeMin; - this.rangeMax = rangeMax; - this.precision = precision; - } +export default class WebGLShaderPrecisionFormat { + className = 'WebGLShaderPrecisionFormat'; + + constructor({ + rangeMin, rangeMax, precision + }) { + this.rangeMin = rangeMin; + this.rangeMax = rangeMax; + this.precision = precision; + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Texture.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Texture.js index de4d806e..089181e5 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Texture.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Texture.js @@ -1,22 +1,22 @@ -import {getTransferedObjectUUID} from './classUtils'; - -const name = 'WebGLTexture'; - -function uuid(id) { - return getTransferedObjectUUID(name, id); -} - -export default class WebGLTexture { - className = name; - - constructor(id, type) { - this.id = id; - this.type = type; - } - - static uuid = uuid; - - uuid() { - return uuid(this.id); - } +import {getTransferedObjectUUID} from './classUtils'; + +const name = 'WebGLTexture'; + +function uuid(id) { + return getTransferedObjectUUID(name, id); +} + +export default class WebGLTexture { + className = name; + + constructor(id, type) { + this.id = id; + this.type = type; + } + + static uuid = uuid; + + uuid() { + return uuid(this.id); + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/UniformLocation.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/UniformLocation.js index f5e99dc3..f19c94c8 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/UniformLocation.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/UniformLocation.js @@ -1,22 +1,22 @@ -import {getTransferedObjectUUID} from './classUtils'; - -const name = 'WebGLUniformLocation'; - -function uuid(id) { - return getTransferedObjectUUID(name, id); -} - -export default class WebGLUniformLocation { - className = name; - - constructor(id, type) { - this.id = id; - this.type = type; - } - - static uuid = uuid; - - uuid() { - return uuid(this.id); - } +import {getTransferedObjectUUID} from './classUtils'; + +const name = 'WebGLUniformLocation'; + +function uuid(id) { + return getTransferedObjectUUID(name, id); +} + +export default class WebGLUniformLocation { + className = name; + + constructor(id, type) { + this.id = id; + this.type = type; + } + + static uuid = uuid; + + uuid() { + return uuid(this.id); + } } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/classUtils.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/classUtils.js index 88716be4..59f02e19 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/classUtils.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/classUtils.js @@ -1,3 +1,3 @@ -export function getTransferedObjectUUID(name, id) { - return `${name.toLowerCase()}-${id}`; +export function getTransferedObjectUUID(name, id) { + return `${name.toLowerCase()}-${id}`; } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/canvas.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/canvas.js index a8d9bb9c..57df6fe3 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/canvas.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/canvas.js @@ -1,74 +1,74 @@ -import GContext2D from '../context-2d/RenderingContext'; -import GContextWebGL from '../context-webgl/RenderingContext'; - -export default class GCanvas { - - // static GBridge = null; - - id = null; - - _needRender = true; - - constructor(id, { disableAutoSwap }) { - this.id = id; - - this._disableAutoSwap = disableAutoSwap; - if (disableAutoSwap) { - this._swapBuffers = () => { - GCanvas.GBridge.render(this.id); - } - } - } - - getContext(type) { - - let context = null; - - if (type.match(/webgl/i)) { - context = new GContextWebGL(this); - - context.componentId = this.id; - - if (!this._disableAutoSwap) { - const render = () => { - if (this._needRender) { - GCanvas.GBridge.render(this.id); - this._needRender = false; - } - } - setInterval(render, 16); - } - - GCanvas.GBridge.callSetContextType(this.id, 1); // 0 for 2d; 1 for webgl - } else if (type.match(/2d/i)) { - context = new GContext2D(this); - - context.componentId = this.id; - -// const render = ( callback ) => { -// -// const commands = context._drawCommands; -// context._drawCommands = ''; -// -// GCanvas.GBridge.render2d(this.id, commands, callback); -// this._needRender = false; -// } -// //draw方法触发 -// context._flush = render; -// //setInterval(render, 16); - - GCanvas.GBridge.callSetContextType(this.id, 0); - } else { - throw new Error('not supported context ' + type); - } - - return context; - - } - - reset() { - GCanvas.GBridge.callReset(this.id); - } - - +import GContext2D from '../context-2d/RenderingContext'; +import GContextWebGL from '../context-webgl/RenderingContext'; + +export default class GCanvas { + + // static GBridge = null; + + id = null; + + _needRender = true; + + constructor(id, { disableAutoSwap }) { + this.id = id; + + this._disableAutoSwap = disableAutoSwap; + if (disableAutoSwap) { + this._swapBuffers = () => { + GCanvas.GBridge.render(this.id); + } + } + } + + getContext(type) { + + let context = null; + + if (type.match(/webgl/i)) { + context = new GContextWebGL(this); + + context.componentId = this.id; + + if (!this._disableAutoSwap) { + const render = () => { + if (this._needRender) { + GCanvas.GBridge.render(this.id); + this._needRender = false; + } + } + setInterval(render, 16); + } + + GCanvas.GBridge.callSetContextType(this.id, 1); // 0 for 2d; 1 for webgl + } else if (type.match(/2d/i)) { + context = new GContext2D(this); + + context.componentId = this.id; + +// const render = ( callback ) => { +// +// const commands = context._drawCommands; +// context._drawCommands = ''; +// +// GCanvas.GBridge.render2d(this.id, commands, callback); +// this._needRender = false; +// } +// //draw方法触发 +// context._flush = render; +// //setInterval(render, 16); + + GCanvas.GBridge.callSetContextType(this.id, 0); + } else { + throw new Error('not supported context ' + type); + } + + return context; + + } + + reset() { + GCanvas.GBridge.callReset(this.id); + } + + } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/image.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/image.js index 9499a519..458d3cde 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/image.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/image.js @@ -1,96 +1,96 @@ -let incId = 1; - -const noop = function () { }; - -class GImage { - - static GBridge = null; - - constructor() { - this._id = incId++; - this._width = 0; - this._height = 0; - this._src = undefined; - this._onload = noop; - this._onerror = noop; - this.complete = false; - } - - get width() { - return this._width; - } - set width(v) { - this._width = v; - } - - get height() { - return this._height; - } - - set height(v) { - this._height = v; - } - - get src() { - return this._src; - } - - set src(v) { - - if (v.startsWith('//')) { - v = 'http:' + v; - } - - this._src = v; - - GImage.GBridge.perloadImage([this._src, this._id], (data) => { - if (typeof data === 'string') { - data = JSON.parse(data); - } - if (data.error) { - var evt = { type: 'error', target: this }; - this.onerror(evt); - } else { - this.complete = true; - this.width = typeof data.width === 'number' ? data.width : 0; - this.height = typeof data.height === 'number' ? data.height : 0; - var evt = { type: 'load', target: this }; - this.onload(evt); - } - }); - } - - addEventListener(name, listener) { - if (name === 'load') { - this.onload = listener; - } else if (name === 'error') { - this.onerror = listener; - } - } - - removeEventListener(name, listener) { - if (name === 'load') { - this.onload = noop; - } else if (name === 'error') { - this.onerror = noop; - } - } - - get onload() { - return this._onload; - } - - set onload(v) { - this._onload = v; - } - - get onerror() { - return this._onerror; - } - - set onerror(v) { - this._onerror = v; - } -} - +let incId = 1; + +const noop = function () { }; + +class GImage { + + static GBridge = null; + + constructor() { + this._id = incId++; + this._width = 0; + this._height = 0; + this._src = undefined; + this._onload = noop; + this._onerror = noop; + this.complete = false; + } + + get width() { + return this._width; + } + set width(v) { + this._width = v; + } + + get height() { + return this._height; + } + + set height(v) { + this._height = v; + } + + get src() { + return this._src; + } + + set src(v) { + + if (v.startsWith('//')) { + v = 'http:' + v; + } + + this._src = v; + + GImage.GBridge.perloadImage([this._src, this._id], (data) => { + if (typeof data === 'string') { + data = JSON.parse(data); + } + if (data.error) { + var evt = { type: 'error', target: this }; + this.onerror(evt); + } else { + this.complete = true; + this.width = typeof data.width === 'number' ? data.width : 0; + this.height = typeof data.height === 'number' ? data.height : 0; + var evt = { type: 'load', target: this }; + this.onload(evt); + } + }); + } + + addEventListener(name, listener) { + if (name === 'load') { + this.onload = listener; + } else if (name === 'error') { + this.onerror = listener; + } + } + + removeEventListener(name, listener) { + if (name === 'load') { + this.onload = noop; + } else if (name === 'error') { + this.onerror = noop; + } + } + + get onload() { + return this._onload; + } + + set onload(v) { + this._onload = v; + } + + get onerror() { + return this._onerror; + } + + set onerror(v) { + this._onerror = v; + } +} + export default GImage; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/tool.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/tool.js index d3fb398b..1174c7f9 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/tool.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/tool.js @@ -1,24 +1,24 @@ - -export function ArrayBufferToBase64 (buffer) { - var binary = ''; - var bytes = new Uint8ClampedArray(buffer); - for (var len = bytes.byteLength, i = 0; i < len; i++) { - binary += String.fromCharCode(bytes[i]); - } - return btoa(binary); -} - -export function Base64ToUint8ClampedArray(base64String) { - const padding = '='.repeat((4 - base64String.length % 4) % 4); - const base64 = (base64String + padding) - .replace(/\-/g, '+') - .replace(/_/g, '/'); - - const rawData = atob(base64); - const outputArray = new Uint8ClampedArray(rawData.length); - - for (let i = 0; i < rawData.length; ++i) { - outputArray[i] = rawData.charCodeAt(i); - } - return outputArray; + +export function ArrayBufferToBase64 (buffer) { + var binary = ''; + var bytes = new Uint8ClampedArray(buffer); + for (var len = bytes.byteLength, i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary); +} + +export function Base64ToUint8ClampedArray(base64String) { + const padding = '='.repeat((4 - base64String.length % 4) % 4); + const base64 = (base64String + padding) + .replace(/\-/g, '+') + .replace(/_/g, '/'); + + const rawData = atob(base64); + const outputArray = new Uint8ClampedArray(rawData.length); + + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + return outputArray; } \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/index.js b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/index.js index a34ad588..3dcc62ab 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/index.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/index.js @@ -1,39 +1,39 @@ -import GCanvas from './env/canvas'; -import GImage from './env/image'; - -import GWebGLRenderingContext from './context-webgl/RenderingContext'; -import GContext2D from './context-2d/RenderingContext'; - -import GBridgeWeex from './bridge/bridge-weex'; - -export let Image = GImage; - -export let WeexBridge = GBridgeWeex; - -export function enable(el, { bridge, debug, disableAutoSwap, disableComboCommands } = {}) { - - const GBridge = GImage.GBridge = GCanvas.GBridge = GWebGLRenderingContext.GBridge = GContext2D.GBridge = bridge; - - GBridge.callEnable(el.ref, [ - 0, // renderMode: 0--RENDERMODE_WHEN_DIRTY, 1--RENDERMODE_CONTINUOUSLY - -1, // hybridLayerType: 0--LAYER_TYPE_NONE 1--LAYER_TYPE_SOFTWARE 2--LAYER_TYPE_HARDWARE - false, // supportScroll - false, // newCanvasMode - 1, // compatible - 'white',// clearColor - false // sameLevel: newCanvasMode = true && true => GCanvasView and Webview is same level - ]); - - if (debug === true) { - GBridge.callEnableDebug(); - } - if (disableComboCommands) { - GBridge.callEnableDisableCombo(); - } - - var canvas = new GCanvas(el.ref, { disableAutoSwap }); - canvas.width = el.style.width; - canvas.height = el.style.height; - - return canvas; +import GCanvas from './env/canvas'; +import GImage from './env/image'; + +import GWebGLRenderingContext from './context-webgl/RenderingContext'; +import GContext2D from './context-2d/RenderingContext'; + +import GBridgeWeex from './bridge/bridge-weex'; + +export let Image = GImage; + +export let WeexBridge = GBridgeWeex; + +export function enable(el, { bridge, debug, disableAutoSwap, disableComboCommands } = {}) { + + const GBridge = GImage.GBridge = GCanvas.GBridge = GWebGLRenderingContext.GBridge = GContext2D.GBridge = bridge; + + GBridge.callEnable(el.ref, [ + 0, // renderMode: 0--RENDERMODE_WHEN_DIRTY, 1--RENDERMODE_CONTINUOUSLY + -1, // hybridLayerType: 0--LAYER_TYPE_NONE 1--LAYER_TYPE_SOFTWARE 2--LAYER_TYPE_HARDWARE + false, // supportScroll + false, // newCanvasMode + 1, // compatible + 'white',// clearColor + false // sameLevel: newCanvasMode = true && true => GCanvasView and Webview is same level + ]); + + if (debug === true) { + GBridge.callEnableDebug(); + } + if (disableComboCommands) { + GBridge.callEnableDisableCombo(); + } + + var canvas = new GCanvas(el.ref, { disableAutoSwap }); + canvas.width = el.style.width; + canvas.height = el.style.height; + + return canvas; }; \ No newline at end of file diff --git a/uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js b/uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js index 8d716925..2290ab37 100644 --- a/uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js +++ b/uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js @@ -1,11 +1,12 @@ //--------------------------------------------------------------------- -// uQRCode二维码生成插件 v3.5.1 +// uQRCode二维码生成插件 v4.0.6 // // uQRCode是一款基于Javascript环境开发的二维码生成插件,适用所有Javascript运行环境的前端应用和Node.js。 // // Copyright (c) Sansnn uQRCode All rights reserved. // -// Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) +// Licensed under the Apache License, Version 2.0. +// http://www.apache.org/licenses/LICENSE-2.0 // // github地址: // https://github.com/Sansnn/uQRCode @@ -19,29 +20,15 @@ // 复制使用请保留本段注释,感谢支持开源! // //--------------------------------------------------------------------- + //--------------------------------------------------------------------- -// QRCode for JavaScript -// -// Copyright (c) 2009 Kazuhiko Arase -// -// URL: http://www.d-project.com/ -// -// Licensed under the MIT license: -// http://www.opensource.org/licenses/mit-license.php -// -// The word "QR Code" is registered trademark of -// DENSO WAVE INCORPORATED -// http://www.denso-wave.com/qrcode/faqpatent-e.html -// +// 当前文件格式为 es,将 bundle 保留为 ES 模块文件,适用于其他打包工具以及支持 - -``` - -> `vue3`推荐使用`npm`安装,非`npm`安装直接引入`uqrcode.js`文件如果出现报错:`SyntaxError: The requested module 'uqrcode.js' does not provide an export named 'default'`,在`uqrcode.js`文件中最后一行添加`export default uQRCode`即可。 - -### 简单用法 - -`uQRCode`基于`Canvas API`封装了一套方法,建议开发者使用`canvas`生成,一键调用,非常方便。以下是示例: - -- HTML部分 -``` html - -``` - -- JS部分 -``` javascript -// 获取uQRCode实例 -var qr = new UQRCode(); -// 设置二维码内容 -qr.data = "https://doc.uqrcode.cn"; -// 设置二维码大小,必须与canvas设置的宽高一致 -qr.size = 200; -// 调用制作二维码方法 -qr.make(); -// 获取canvas元素 -var canvas = document.getElementById("qrcode"); -// 获取canvas上下文 -var canvasContext = canvas.getContext("2d"); -// 设置uQRCode实例的canvas上下文 -qr.canvasContext = canvasContext; -// 调用绘制方法将二维码图案绘制到canvas上 -qr.drawCanvas(); -``` - -### 高级用法 - -考虑到部分平台可能不支持`canvas`,所以`uQRCode`并没有强制要求和`canvas`一起使用,您还可以选择其他方式来生成二维码,例如使用`js`操作`dom`进行绘制或是使用`svg`绘制等。以下是示例: - -- js操作dom -``` html - - - - - uQRCode二维码生成 - - -
- - - - -``` - -- svg -``` html - - - - - uQRCode二维码生成 - - - - - - - -``` - -> 更多用法大家自行探索咯,期待分享哟~ - -### 导出临时文件路径 - -原生方式基于`Canvas`的,请自行参阅各平台`Canvas`的导出方式。以下是部分示例: - -- uni-app -```javascript -// 通过uni.createCanvasContext方式创建绘制上下文的,对应导出API为uni.canvasToTempFilePath -// 调用完ctx.draw()方法后不能第一时间导出,否则会异常,需要有一定的延时 -setTimeout(() => { - uni.canvasToTempFilePath( - { - canvasId: this.canvasId, - fileType: this.fileType, - width: this.canvasWidth, - height: this.canvasHeight, - success: res => { - console.log(res); - }, - fail: err => { - console.log(err); - } - }, - // this // 组件内使用必传当前实例 - ); -}, 300); -``` - -- Canvas2D -```javascript -// 得到base64 -console.log(canvas.toDataURL()); -// 得到buffer -console.log(canvas.toBuffer()); -``` - -### 保存二维码到本地相册 - -必须在导出临时文件路径成功后再执行保存。uni-app通用保存方式(H5除外): -```javascript -uni.saveImageToPhotosAlbum({ - filePath: tempFilePath, - success: res => { - console.log(res); - }, - fail: err => { - console.log(err); - } -}); -``` - -H5可以通过设置``标签`href`属性的方式进行保存: -```javascript -const aEle = document.createElement('a'); -aEle.download = 'uQRCode'; // 设置下载的文件名,默认是'下载' -aEle.href = tempFilePath; -document.body.appendChild(aEle); -aEle.click(); -aEle.remove(); // 下载之后把创建的元素删除 -``` -经过测试,PC端浏览器可以下载,部分安卓自带或第三方浏览器可以下载,安卓微信浏览器不适用,移动端iOS所有浏览器均不适用,差异较大,还是推荐各位导出文件给图片组件显示,然后提示用户通过长按图片进行保存这种方式。 - -## uni-app组件方式 - -### 安装 - -通过uni-app插件市场地址安装:[https://ext.dcloud.net.cn/plugin?id=1287](https://ext.dcloud.net.cn/plugin?id=1287)。 - -### 引入 - -uni-app默认为easycom模式,可直接键入``标签。 - -### 简单用法 - -安装`uqrcode`组件后,在`template`中键入``。设置`ref`属性可使用组件内部方法,`canvas-id`属性为组件内部的canvas组件标识,`value`属性为二维码生成对应内容。 - -``` html - -``` - -### 导出临时文件路径 - -为了保证方法调用成功,请在 [complete](/document/uni-app.md#complete) 事件返回`success=true`后调用。 - -```javascript -// uqrcode为组件的ref名称 -this.$refs.uqrcode.toTempFilePath({ - success: res => { - console.log(res); - } -}); -``` - -### 保存二维码到本地相册 - -为了保证方法调用成功,请在 [complete](/document/uni-app.md#complete) 事件返回`success=true`后调用。 - -```javascript -// uqrcode为组件的ref名称 -this.$refs.uqrcode.save({ - success: () => { - uni.showToast({ - icon: 'success', - title: '保存成功' - }); - } -}); -``` - -## 更多配置说明请前往官方文档查看:[https://doc.uqrcode.cn](https://doc.uqrcode.cn)。 \ No newline at end of file +# 介绍 + +`uQRCode`是一款基于`Javascript`环境开发的二维码生成插件,适用所有`Javascript`运行环境的前端应用和`Node.js`应用。 + +`uQRCode`可扩展性高,它支持自定义渲染二维码,可通过`uQRCode API`得到二维码绘制关键信息后,使用`canvas`、`svg`或`js`操作`dom`的方式绘制二维码图案。还可自定义二维码样式,如随机颜色、圆点、方块、块与块之间的间距等。 + +欢迎加入群聊【uQRCode交流群】:[695070434](https://jq.qq.com/?_wv=1027&k=JRjzDqiw)。 + +# 设计器 + +uQRCode发布了配套的可视化设计器,可根据自己喜好在设计器中设计二维码样式,一键生成配置代码复制到项目中,详情请在微信小程序搜索“柚子二维码”,或扫描下方小程序码体验。 + +![uQRCode设计器](https://uqrcode.cn/mp_weixin_code.jpg) + +## 设计器模板示例 + +![uQRCode设计器](https://uqrcode.cn/yz_1.png) +![uQRCode设计器](https://uqrcode.cn/yz_2.png) +![uQRCode设计器](https://uqrcode.cn/yz_3.png) +![uQRCode设计器](https://uqrcode.cn/yz_4.png) +![uQRCode设计器](https://uqrcode.cn/yz_5.png) +![uQRCode设计器](https://uqrcode.cn/yz_6.png) +![uQRCode设计器](https://uqrcode.cn/yz_7.png) +![uQRCode设计器](https://uqrcode.cn/yz_8.png) +![uQRCode设计器](https://uqrcode.cn/yz_9.png) + +# 快速上手 + +> 在`uni-app`中,我们更推荐使用组件方式来生成二维码,组件方式大大提高了页面的可读性以及避开了一些平台容易出问题的地方,当组件无法满足需求的时候,再考虑切换成原生方式。 + +官方文档:[https://uqrcode.cn/doc](https://uqrcode.cn/doc)。 + +github地址:[https://github.com/Sansnn/uQRCode](https://github.com/Sansnn/uQRCode)。 + +npm地址:[https://www.npmjs.com/package/uqrcodejs](https://www.npmjs.com/package/uqrcodejs)。 + +uni-app插件市场地址:[https://ext.dcloud.net.cn/plugin?id=1287](https://ext.dcloud.net.cn/plugin?id=1287)。 + +## 原生方式 + +原生方式仅需要获取`uqrcode.js`文件便可使用。详细配置请移步到:文档 > [原生](https://uqrcode.cn/doc/document/native.html)。 + +### 安装 + +1. 通过`npm`安装,成功后即可使用`import`或`require`进行引用。 +``` bash +# npm安装 +npm install uqrcodejs +# 或者 +npm install @uqrcode/js +``` + +2. 通过项目开源地址获取`uqrcode.js`,下载`uqrcode.js`后,将其复制到您项目指定目录,在页面中引入`uqrcode.js`文件即可开始使用。 + +### 引入 + +- 通过`import`引入。 +``` javascript +// npm安装 +import UQRCode from 'uqrcodejs'; // npm install uqrcodejs +// 或者 +import UQRCode from '@uqrcode/js'; // npm install @uqrcode/js +``` + +- `Node.js`通过`require`引入。 +``` javascript +// npm安装 +const UQRCode = require('uqrcodejs'); // npm install uqrcodejs +// 或者 +const UQRCode = require('@uqrcode/js'); // npm install @uqrcode/js +``` + +- 原生浏览器环境,在js脚本加载时添加到`window`。 +``` html + + +``` + +### 简单用法 + +`uQRCode`基于`Canvas API`封装了一套方法,建议开发者使用`canvas`生成,一键调用,非常方便。以下是示例: + +- HTML示例 + - DOM部分 + ``` html + + ``` + + - JS部分 + ``` javascript + // 获取uQRCode实例 + var qr = new UQRCode(); + // 设置二维码内容 + qr.data = "https://uqrcode.cn/doc"; + // 设置二维码大小,必须与canvas设置的宽高一致 + qr.size = 200; + // 调用制作二维码方法 + qr.make(); + // 获取canvas元素 + var canvas = document.getElementById("qrcode"); + // 获取canvas上下文 + var canvasContext = canvas.getContext("2d"); + // 设置uQRCode实例的canvas上下文 + qr.canvasContext = canvasContext; + // 调用绘制方法将二维码图案绘制到canvas上 + qr.drawCanvas(); + ``` + +- uni-app示例 + - Template部分 + ``` html + + ``` + + - JS部分 + ``` javascript + onReady() { + // 获取uQRCode实例 + var qr = new UQRCode(); + // 设置二维码内容 + qr.data = "https://uqrcode.cn/doc"; + // 设置二维码大小,必须与canvas设置的宽高一致 + qr.size = 200; + // 调用制作二维码方法 + qr.make(); + // 获取canvas上下文 + var canvasContext = uni.createCanvasContext('qrcode', this); // 如果是组件,this必须传入 + // 设置uQRCode实例的canvas上下文 + qr.canvasContext = canvasContext; + // 调用绘制方法将二维码图案绘制到canvas上 + qr.drawCanvas(); + } + ``` + +- 微信小程序,推荐使用Canvas 2D,关于Canvas 2D的使用请参考微信开放文档。 + +### 高级用法 + +考虑到部分平台可能不支持`canvas`,所以`uQRCode`并没有强制要求和`canvas`一起使用,您还可以选择其他方式来生成二维码,例如使用`js`操作`dom`进行绘制或是使用`svg`绘制等。以下是示例: + +- uni-app v-for+view + +```html + + + +``` + +- js操作dom + +``` html + + + + + uQRCode二维码生成 + + +
+ + + + +``` + +- svg +``` html + + + + + uQRCode二维码生成 + + + + + + + +``` + +> 更多用法大家自行探索咯,期待分享哟~ + +### 导出临时文件路径 + +原生方式基于`Canvas`的,请自行参阅各平台`Canvas`的导出方式。以下是部分示例: + +- uni-app +```javascript +// 通过uni.createCanvasContext方式创建绘制上下文的,对应导出API为uni.canvasToTempFilePath +// 调用完ctx.draw()方法后不能第一时间导出,否则会异常,需要有一定的延时 +setTimeout(() => { + uni.canvasToTempFilePath( + { + canvasId: this.canvasId, + fileType: this.fileType, + width: this.canvasWidth, + height: this.canvasHeight, + success: res => { + console.log(res); + }, + fail: err => { + console.log(err); + } + }, + // this // 组件内使用必传当前实例 + ); +}, 300); +``` + +- Canvas2D +```javascript +// 得到base64 +console.log(canvas.toDataURL()); +// 得到buffer +console.log(canvas.toBuffer()); +``` + +### 保存二维码到本地相册 + +必须在导出临时文件路径成功后再执行保存。uni-app通用保存方式(H5除外): +```javascript +uni.saveImageToPhotosAlbum({ + filePath: tempFilePath, + success: res => { + console.log(res); + }, + fail: err => { + console.log(err); + } +}); +``` + +H5可以通过设置`
`标签`href`属性的方式进行保存: +```javascript +const aEle = document.createElement('a'); +aEle.download = 'uQRCode'; // 设置下载的文件名,默认是'下载' +aEle.href = tempFilePath; +document.body.appendChild(aEle); +aEle.click(); +aEle.remove(); // 下载之后把创建的元素删除 +``` +经过测试,PC端浏览器可以下载,部分安卓自带或第三方浏览器可以下载,安卓微信浏览器不适用,移动端iOS所有浏览器均不适用,差异较大,还是推荐各位导出文件给图片组件显示,然后提示用户通过长按图片进行保存这种方式。 + +## uni-app组件方式 + +### 安装 + +通过uni-app插件市场地址安装:[https://ext.dcloud.net.cn/plugin?id=1287](https://ext.dcloud.net.cn/plugin?id=1287)。详细配置请移步到:文档 > [uni-app组件](https://uqrcode.cn/doc/document/uni-app.html)。 + +### 引入 + +uni-app默认为easycom模式,可直接键入``标签。 + +### 简单用法 + +安装`uqrcode`组件后,在`template`中键入``。设置`ref`属性可使用组件内部方法,`canvas-id`属性为组件内部的canvas组件标识,`value`属性为二维码生成对应内容,`options`为配置选项,可配置二维码样式,绘制Logo等,详见:[options](https://uqrcode.cn/doc/document/uni-app.html#options) 。 + +``` html + +``` + +### 导出临时文件路径 + +为了保证方法调用成功,请在 [complete](https://uqrcode.cn/doc/document/uni-app.html#complete) 事件返回`success=true`后调用。 + +```javascript +// uqrcode为组件的ref名称 +this.$refs.uqrcode.toTempFilePath({ + success: res => { + console.log(res); + } +}); +``` + +### 保存二维码到本地相册 + +为了保证方法调用成功,请在 [complete](https://uqrcode.cn/doc/document/uni-app.html#complete) 事件返回`success=true`后调用。 + +```javascript +// uqrcode为组件的ref名称 +this.$refs.uqrcode.save({ + success: () => { + uni.showToast({ + icon: 'success', + title: '保存成功' + }); + } +}); +``` + +## 更多使用说明请前往官方文档查看:[https://uqrcode.cn/doc](https://uqrcode.cn/doc)。 \ No newline at end of file diff --git a/uni_modules/uni-badge/components/uni-badge/uni-badge.vue b/uni_modules/uni-badge/components/uni-badge/uni-badge.vue index 664dc370..7b2ba31f 100644 --- a/uni_modules/uni-badge/components/uni-badge/uni-badge.vue +++ b/uni_modules/uni-badge/components/uni-badge/uni-badge.vue @@ -191,12 +191,13 @@ /* #ifndef APP-NVUE */ display: flex; overflow: hidden; - box-sizing: border-box; + box-sizing: border-box; + min-width: 20px; + font-feature-settings: "tnum"; /* #endif */ justify-content: center; flex-direction: row; height: 20px; - min-width: 20px; padding: 0 4px; line-height: 18px; color: #fff; @@ -206,7 +207,6 @@ border: 1px solid #fff; text-align: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; - font-feature-settings: "tnum"; font-size: $bage-size; /* #ifdef H5 */ z-index: 999; diff --git a/uni_modules/uni-captcha/changelog.md b/uni_modules/uni-captcha/changelog.md index f3906311..90e149f8 100644 --- a/uni_modules/uni-captcha/changelog.md +++ b/uni_modules/uni-captcha/changelog.md @@ -1,3 +1,6 @@ +## 0.6.2(2023-01-10) +- 修复 抖音小程序无法显示的Bug +- 修复 刷新时兼容 device_uuid ## 0.6.1(2022-06-23) - 修复:部分返回值,不符合响应体规范的问题 ## 0.6.0(2022-05-27) diff --git a/uni_modules/uni-captcha/package.json b/uni_modules/uni-captcha/package.json index cf586043..faa2507c 100644 --- a/uni_modules/uni-captcha/package.json +++ b/uni_modules/uni-captcha/package.json @@ -1,7 +1,7 @@ { "id": "uni-captcha", "displayName": "uni-captcha", - "version": "0.6.1", + "version": "0.6.2", "description": "云端一体图形验证码组件", "keywords": [ "captcha", @@ -14,11 +14,7 @@ "engines": { "HBuilderX": "^3.1.0" }, - "dcloudext": { - "category": [ - "uniCloud", - "云函数模板" - ], +"dcloudext": { "sale": { "regular": { "price": "0.00" @@ -35,7 +31,8 @@ "data": "无", "permissions": "无" }, - "npmurl": "" + "npmurl": "", + "type": "unicloud-template-function" }, "uni_modules": { "dependencies": [], diff --git a/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/index.js b/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/index.js index b170e625..837e6d0c 100644 --- a/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/index.js +++ b/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/index.js @@ -1 +1 @@ -"use strict";var e=require("assert"),t=require("path");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=n(e),r=n(t);const s={10001:"uni-captcha-create-fail",10002:"uni-captcha-verify-fail",10003:"uni-captcha-refresh-fail",10101:"uni-captcha-deviceId-empty",10102:"uni-captcha-text-required",10103:"uni-captcha-verify-overdue",10104:"uni-captcha-verify-fail",50403:"uni-captcha-interior-fail"};function a(e){const t=.2*Math.random()-.1;switch(e.type){case"M":case"L":e.x+=t,e.y+=t;break;case"Q":case"C":e.x+=t,e.y+=t,e.x1+=t,e.y1+=t}return e}function i(e,t,n,o,r,s,a){let i,l,c,u,p,h;if(e<=0||e>=1)throw RangeError("spliteCurveAt requires position > 0 && position < 1");return u=[],p=0,i={},l={},c={},i.x=t,i.y=n,l.x=o,l.y=r,c.x=s,c.y=a,h=e,u[p++]=i.x,u[p++]=i.y,u[p++]=i.x+=(l.x-i.x)*h,u[p++]=i.y+=(l.y-i.y)*h,l.x+=(c.x-l.x)*h,l.y+=(c.y-l.y)*h,u[p++]=i.x+(l.x-i.x)*h,u[p++]=i.y+(l.y-i.y)*h,u[p++]=l.x,u[p++]=l.y,u[p++]=c.x,u[p++]=c.y,u}function l(e,t){return Math.random()*(t-e)+e}var c=function(e,t){const n=e[0];o.default(n,"expect a string");const r=t.fontSize,s=r/t.font.unitsPerEm,c=t.font.charToGlyph(n),u=c.advanceWidth?c.advanceWidth*s:0,p=t.x-u/2,h=(t.ascender+t.descender)*s,f=t.y+h/2,d=c.getPath(p,f,r);d.commands.forEach(a),d.commands=function(e,t){const n=[];for(let o=0;ot.truncateLineProbability){const e=l(-.1,.1);n.push(r),n.push({type:"L",x:(r.x+s.x)/2+e,y:(r.y+s.y)/2+e})}else n.push(r)}else if("Q"===r.type&&o>=1){const s=e[o-1];if(("L"===s.type||"M"===s.type)&&Math.random()>t.truncateCurveProbability){const e=s.x,o=s.y,a=l(-.1,.1),c=r.x1+a,u=r.y1+a,p=r.x+a,h=r.y+a,f=i(l(t.truncateCurvePositionMin,t.truncateCurvePositionMax),e,o,c,u,p,h),d={type:"Q",x1:f[2],y1:f[3],x:f[4],y:f[5]},g={type:"L",x:f[4],y:f[5]},m={type:"Q",x1:f[6],y1:f[7],x:f[8],y:f[9]},y={type:"L",x:f[8],y:f[9]};n.push(d),n.push(g),n.push(m),n.push(y)}}else n.push(r)}return n}(d.commands,t);return d.toPathData()};function u(){this.table=new Uint16Array(16),this.trans=new Uint16Array(288)}function p(e,t){this.source=e,this.sourceIndex=0,this.tag=0,this.bitcount=0,this.dest=t,this.destLen=0,this.ltree=new u,this.dtree=new u}var h=new u,f=new u,d=new Uint8Array(30),g=new Uint16Array(30),m=new Uint8Array(30),y=new Uint16Array(30),v=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),b=new u,S=new Uint8Array(320);function x(e,t,n,o){var r,s;for(r=0;r>>=1,t}function O(e,t,n){if(!t)return n;for(;e.bitcount<24;)e.tag|=e.source[e.sourceIndex++]<>>16-t;return e.tag>>>=t,e.bitcount-=t,o+n}function w(e,t){for(;e.bitcount<24;)e.tag|=e.source[e.sourceIndex++]<>>=1,++r,n+=t.table[r],o-=t.table[r]}while(o>=0);return e.tag=s,e.bitcount-=r,t.trans[n+o]}function k(e,t,n){var o,r,s,a,i,l;for(o=O(e,5,257),r=O(e,5,1),s=O(e,4,4),a=0;a<19;++a)S[a]=0;for(a=0;a8;)e.sourceIndex--,e.bitcount-=8;if((t=256*(t=e.source[e.sourceIndex+1])+e.source[e.sourceIndex])!==(65535&~(256*e.source[e.sourceIndex+3]+e.source[e.sourceIndex+2])))return-3;for(e.sourceIndex+=4,n=t;n;--n)e.dest[e.destLen++]=e.source[e.sourceIndex++];return e.bitcount=0,0}!function(e,t){var n;for(n=0;n<7;++n)e.table[n]=0;for(e.table[7]=24,e.table[8]=152,e.table[9]=112,n=0;n<24;++n)e.trans[n]=256+n;for(n=0;n<144;++n)e.trans[24+n]=n;for(n=0;n<8;++n)e.trans[168+n]=280+n;for(n=0;n<112;++n)e.trans[176+n]=144+n;for(n=0;n<5;++n)t.table[n]=0;for(t.table[5]=32,n=0;n<32;++n)t.trans[n]=n}(h,f),x(d,g,4,3),x(m,y,2,1),d[28]=0,g[28]=258;var C=function(e,t){var n,o,r=new p(e,t);do{switch(n=E(r),O(r,2,0)){case 0:o=D(r);break;case 1:o=R(r,h,f);break;case 2:k(r,r.ltree,r.dtree),o=R(r,r.ltree,r.dtree);break;default:o=-3}if(0!==o)throw new Error("Data error")}while(!n);return r.destLenthis.x2&&(this.x2=e)),"number"==typeof t&&((isNaN(this.y1)||isNaN(this.y2))&&(this.y1=t,this.y2=t),tthis.y2&&(this.y2=t))},I.prototype.addX=function(e){this.addPoint(e,null)},I.prototype.addY=function(e){this.addPoint(null,e)},I.prototype.addBezier=function(e,t,n,o,r,s,a,i){const l=[e,t],c=[n,o],u=[r,s],p=[a,i];this.addPoint(e,t),this.addPoint(a,i);for(let e=0;e<=1;e++){const t=6*l[e]-12*c[e]+6*u[e],n=-3*l[e]+9*c[e]-9*u[e]+3*p[e],o=3*c[e]-3*l[e];if(0===n){if(0===t)continue;const n=-o/t;0=0&&n>0&&(e+=" "),e+=t(o)}return e}e=void 0!==e?e:2;let o="";for(let e=0;e=0&&e<=255,"Byte value should be between 0 and 255."),[e]},F.BYTE=H(1),A.CHAR=function(e){return[e.charCodeAt(0)]},F.CHAR=H(1),A.CHARARRAY=function(e){const t=[];for(let n=0;n>8&255,255&e]},F.USHORT=H(2),A.SHORT=function(e){return e>=32768&&(e=-(65536-e)),[e>>8&255,255&e]},F.SHORT=H(2),A.UINT24=function(e){return[e>>16&255,e>>8&255,255&e]},F.UINT24=H(3),A.ULONG=function(e){return[e>>24&255,e>>16&255,e>>8&255,255&e]},F.ULONG=H(4),A.LONG=function(e){return e>=2147483648&&(e=-(4294967296-e)),[e>>24&255,e>>16&255,e>>8&255,255&e]},F.LONG=H(4),A.FIXED=A.ULONG,F.FIXED=F.ULONG,A.FWORD=A.SHORT,F.FWORD=F.SHORT,A.UFWORD=A.USHORT,F.UFWORD=F.USHORT,A.LONGDATETIME=function(e){return[0,0,0,0,e>>24&255,e>>16&255,e>>8&255,255&e]},F.LONGDATETIME=H(8),A.TAG=function(e){return N.argument(4===e.length,"Tag should be exactly 4 ASCII characters."),[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]},F.TAG=H(4),A.Card8=A.BYTE,F.Card8=F.BYTE,A.Card16=A.USHORT,F.Card16=F.USHORT,A.OffSize=A.BYTE,F.OffSize=F.BYTE,A.SID=A.USHORT,F.SID=F.USHORT,A.NUMBER=function(e){return e>=-107&&e<=107?[e+139]:e>=108&&e<=1131?[247+((e-=108)>>8),255&e]:e>=-1131&&e<=-108?[251+((e=-e-108)>>8),255&e]:e>=-32768&&e<=32767?A.NUMBER16(e):A.NUMBER32(e)},F.NUMBER=function(e){return A.NUMBER(e).length},A.NUMBER16=function(e){return[28,e>>8&255,255&e]},F.NUMBER16=H(3),A.NUMBER32=function(e){return[29,e>>24&255,e>>16&255,e>>8&255,255&e]},F.NUMBER32=H(5),A.REAL=function(e){let t=e.toString();const n=/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(t);if(n){const o=parseFloat("1e"+((n[2]?+n[2]:0)+n[1].length));t=(Math.round(e*o)/o).toString()}let o="";for(let e=0,n=t.length;e>8&255,t[t.length]=255&o}return t},F.UTF16=function(e){return 2*e.length};const z={"x-mac-croatian":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®Š™´¨≠ŽØ∞±≤≥∆µ∂∑∏š∫ªºΩžø¿¡¬√ƒ≈Ć«Č… ÀÃÕŒœĐ—“”‘’÷◊©⁄€‹›Æ»–·‚„‰ÂćÁčÈÍÎÏÌÓÔđÒÚÛÙıˆ˜¯πË˚¸Êæˇ","x-mac-cyrillic":"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ†°Ґ£§•¶І®©™Ђђ≠Ѓѓ∞±≤≥іµґЈЄєЇїЉљЊњјЅ¬√ƒ≈∆«»… ЋћЌќѕ–—“”‘’÷„ЎўЏџ№Ёёяабвгдежзийклмнопрстуфхцчшщъыьэю","x-mac-gaelic":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØḂ±≤≥ḃĊċḊḋḞḟĠġṀæøṁṖṗɼƒſṠ«»… ÀÃÕŒœ–—“”‘’ṡẛÿŸṪ€‹›Ŷŷṫ·Ỳỳ⁊ÂÊÁËÈÍÎÏÌÓÔ♣ÒÚÛÙıÝýŴŵẄẅẀẁẂẃ","x-mac-greek":"Ĺ²É³ÖÜ΅àâä΄¨çéèê룙î‰ôö¦€ùûü†ΓΔΘΛΞΠß®©ΣΪ§≠°·Α±≤≥¥ΒΕΖΗΙΚΜΦΫΨΩάΝ¬ΟΡ≈Τ«»… ΥΧΆΈœ–―“”‘’÷ΉΊΌΎέήίόΏύαβψδεφγηιξκλμνοπώρστθωςχυζϊϋΐΰ­","x-mac-icelandic":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûüÝ°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€ÐðÞþý·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-inuit":"ᐃᐄᐅᐆᐊᐋᐱᐲᐳᐴᐸᐹᑉᑎᑏᑐᑑᑕᑖᑦᑭᑮᑯᑰᑲᑳᒃᒋᒌᒍᒎᒐᒑ°ᒡᒥᒦ•¶ᒧ®©™ᒨᒪᒫᒻᓂᓃᓄᓅᓇᓈᓐᓯᓰᓱᓲᓴᓵᔅᓕᓖᓗᓘᓚᓛᓪᔨᔩᔪᔫᔭ… ᔮᔾᕕᕖᕗ–—“”‘’ᕘᕙᕚᕝᕆᕇᕈᕉᕋᕌᕐᕿᖀᖁᖂᖃᖄᖅᖏᖐᖑᖒᖓᖔᖕᙱᙲᙳᙴᙵᙶᖖᖠᖡᖢᖣᖤᖥᖦᕼŁł","x-mac-ce":"ÄĀāÉĄÖÜáąČäčĆć鏟ĎíďĒēĖóėôöõúĚěü†°Ę£§•¶ß®©™ę¨≠ģĮįĪ≤≥īĶ∂∑łĻļĽľĹĺŅņѬ√ńŇ∆«»… ňŐÕőŌ–—“”‘’÷◊ōŔŕŘ‹›řŖŗŠ‚„šŚśÁŤťÍŽžŪÓÔūŮÚůŰűŲųÝýķŻŁżĢˇ",macintosh:"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›fifl‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-romanian":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ĂȘ∞±≤≥¥µ∂∑∏π∫ªºΩăș¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›Țț‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-turkish":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸĞğİıŞş‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙˆ˜¯˘˙˚¸˝˛ˇ"};P.MACSTRING=function(e,t,n,o){const r=z[o];if(void 0===r)return;let s="";for(let o=0;o=-128&&e<=127}function X(e,t,n){let o=0;const r=e.length;for(;t>8&255,t+256&255)}return s}A.MACSTRING=function(e,t){const n=function(e){if(!_){_={};for(let e in z)_[e]=new String(e)}const t=_[e];if(void 0===t)return;if(W){const e=W.get(t);if(void 0!==e)return e}const n=z[e];if(void 0===n)return;const o={};for(let e=0;e=128&&(r=n[r],void 0===r))return;o[t]=r}return o},F.MACSTRING=function(e,t){const n=A.MACSTRING(e,t);return void 0!==n?n.length:0},A.VARDELTAS=function(e){let t=0;const n=[];for(;t=-128&&o<=127?V(e,t,n):j(e,t,n)}return n},A.INDEX=function(e){let t=1;const n=[t],o=[];for(let r=0;r>8,t[s+1]=255&a,t=t.concat(o[n])}return t},F.TABLE=function(e){let t=0;const n=e.fields.length;for(let o=0;o0)return new ce(this.data,this.offset+t).parseStruct(e)},ce.prototype.parseListOfLists=function(e){const t=this.parseOffset16List(),n=t.length,o=this.relativeOffset,r=new Array(n);for(let o=0;o=0;r-=1){const n=pe.getUShort(e,t+4+8*r),s=pe.getUShort(e,t+4+8*r+2);if(3===n&&(0===s||1===s||10===s)){o=pe.getULong(e,t+4+8*r+4);break}}if(-1===o)throw new Error("No valid cmap sub-tables found.");const r=new pe.Parser(e,t+o);if(n.format=r.parseUShort(),12===n.format)!function(e,t){let n;t.parseUShort(),e.length=t.parseULong(),e.language=t.parseULong(),e.groupCount=n=t.parseULong(),e.glyphIndexMap={};for(let o=0;o>1,t.skip("uShort",3),e.glyphIndexMap={};const a=new pe.Parser(n,o+r+14),i=new pe.Parser(n,o+r+16+2*s),l=new pe.Parser(n,o+r+16+4*s),c=new pe.Parser(n,o+r+16+6*s);let u=o+r+16+8*s;for(let t=0;t0?(s=e.parseByte(),0==(t&r)&&(s=-s),s=n+s):s=(t&r)>0?n:n+e.parseShort(),s}function Ee(e,t,n){const o=new pe.Parser(t,n);let r,s;if(e.numberOfContours=o.parseShort(),e._xMin=o.parseShort(),e._yMin=o.parseShort(),e._xMax=o.parseShort(),e._yMax=o.parseShort(),e.numberOfContours>0){const t=e.endPointIndices=[];for(let n=0;n0){const t=o.parseByte();for(let n=0;n0){const a=[];let i;if(n>0){for(let e=0;e=0,a.push(i);let e=0;for(let t=0;t0?(2&r)>0?(n.dx=o.parseShort(),n.dy=o.parseShort()):n.matchedPoints=[o.parseUShort(),o.parseUShort()]:(2&r)>0?(n.dx=o.parseChar(),n.dy=o.parseChar()):n.matchedPoints=[o.parseByte(),o.parseByte()],(8&r)>0?n.xScale=n.yScale=o.parseF2Dot14():(64&r)>0?(n.xScale=o.parseF2Dot14(),n.yScale=o.parseF2Dot14()):(128&r)>0&&(n.xScale=o.parseF2Dot14(),n.scale01=o.parseF2Dot14(),n.scale10=o.parseF2Dot14(),n.yScale=o.parseF2Dot14()),e.components.push(n),t=!!(32&r)}if(256&r){e.instructionLength=o.parseUShort(),e.instructions=[];for(let t=0;tt.points.length-1||o.matchedPoints[1]>r.points.length-1)throw Error("Matched points out of range in "+t.name);const n=t.points[o.matchedPoints[0]];let s=r.points[o.matchedPoints[1]];const a={xScale:o.xScale,scale01:o.scale01,scale10:o.scale10,yScale:o.yScale,dx:0,dy:0};s=Oe([s],a)[0],a.dx=n.x-s.x,a.dy=n.y-s.y,e=Oe(r.points,a)}t.points=t.points.concat(e)}}return we(t.points)}var Re={getPath:we,parse:function(e,t,n,o){const r=new Ie.GlyphSet(o);for(let s=0;s>4,s=15&o;if(15===r)break;if(t+=n[r],15===s)break;t+=n[s]}return parseFloat(t)}(e);if(t>=32&&t<=246)return t-139;if(t>=247&&t<=250)return n=e.parseByte(),256*(t-247)+n+108;if(t>=251&&t<=254)return n=e.parseByte(),256*-(t-251)-n-108;throw new Error("Invalid b0 "+t)}function Pe(e,t,n){t=void 0!==t?t:0;const o=new pe.Parser(e,t),r=[];let s=[];for(n=void 0!==n?n:e.length;o.relativeOffset>1,l.length=0,d=!0}return function n(p){let x,U,T,E,O,w,k,R,D,C,L,I,M=0;for(;M1&&!d&&(v=l.shift()+h,d=!0),y+=l.pop(),b(m,y);break;case 5:for(;l.length>0;)m+=l.shift(),y+=l.shift(),i.lineTo(m,y);break;case 6:for(;l.length>0&&(m+=l.shift(),i.lineTo(m,y),0!==l.length);)y+=l.shift(),i.lineTo(m,y);break;case 7:for(;l.length>0&&(y+=l.shift(),i.lineTo(m,y),0!==l.length);)m+=l.shift(),i.lineTo(m,y);break;case 8:for(;l.length>0;)o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+l.shift(),i.curveTo(o,r,s,a,m,y);break;case 10:O=l.pop()+u,w=c[O],w&&n(w);break;case 11:return;case 12:switch(B=p[M],M+=1,B){case 35:o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),k=s+l.shift(),R=a+l.shift(),D=k+l.shift(),C=R+l.shift(),L=D+l.shift(),I=C+l.shift(),m=L+l.shift(),y=I+l.shift(),l.shift(),i.curveTo(o,r,s,a,k,R),i.curveTo(D,C,L,I,m,y);break;case 34:o=m+l.shift(),r=y,s=o+l.shift(),a=r+l.shift(),k=s+l.shift(),R=a,D=k+l.shift(),C=a,L=D+l.shift(),I=y,m=L+l.shift(),i.curveTo(o,r,s,a,k,R),i.curveTo(D,C,L,I,m,y);break;case 36:o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),k=s+l.shift(),R=a,D=k+l.shift(),C=a,L=D+l.shift(),I=C+l.shift(),m=L+l.shift(),i.curveTo(o,r,s,a,k,R),i.curveTo(D,C,L,I,m,y);break;case 37:o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),k=s+l.shift(),R=a+l.shift(),D=k+l.shift(),C=R+l.shift(),L=D+l.shift(),I=C+l.shift(),Math.abs(L-m)>Math.abs(I-y)?m=L+l.shift():y=I+l.shift(),i.curveTo(o,r,s,a,k,R),i.curveTo(D,C,L,I,m,y);break;default:console.log("Glyph "+t.index+": unknown operator 1200"+B),l.length=0}break;case 14:l.length>0&&!d&&(v=l.shift()+h,d=!0),g&&(i.closePath(),g=!1);break;case 18:S();break;case 19:case 20:S(),M+=f+7>>3;break;case 21:l.length>2&&!d&&(v=l.shift()+h,d=!0),y+=l.pop(),m+=l.pop(),b(m,y);break;case 22:l.length>1&&!d&&(v=l.shift()+h,d=!0),m+=l.pop(),b(m,y);break;case 23:S();break;case 24:for(;l.length>2;)o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+l.shift(),i.curveTo(o,r,s,a,m,y);m+=l.shift(),y+=l.shift(),i.lineTo(m,y);break;case 25:for(;l.length>6;)m+=l.shift(),y+=l.shift(),i.lineTo(m,y);o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+l.shift(),i.curveTo(o,r,s,a,m,y);break;case 26:for(l.length%2&&(m+=l.shift());l.length>0;)o=m,r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s,y=a+l.shift(),i.curveTo(o,r,s,a,m,y);break;case 27:for(l.length%2&&(y+=l.shift());l.length>0;)o=m+l.shift(),r=y,s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a,i.curveTo(o,r,s,a,m,y);break;case 28:x=p[M],U=p[M+1],l.push((x<<24|U<<16)>>16),M+=2;break;case 29:O=l.pop()+e.gsubrsBias,w=e.gsubrs[O],w&&n(w);break;case 30:for(;l.length>0&&(o=m,r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+(1===l.length?l.shift():0),i.curveTo(o,r,s,a,m,y),0!==l.length);)o=m+l.shift(),r=y,s=o+l.shift(),a=r+l.shift(),y=a+l.shift(),m=s+(1===l.length?l.shift():0),i.curveTo(o,r,s,a,m,y);break;case 31:for(;l.length>0&&(o=m+l.shift(),r=y,s=o+l.shift(),a=r+l.shift(),y=a+l.shift(),m=s+(1===l.length?l.shift():0),i.curveTo(o,r,s,a,m,y),0!==l.length);)o=m,r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+(1===l.length?l.shift():0),i.curveTo(o,r,s,a,m,y);break;default:B<32?console.log("Glyph "+t.index+": unknown operator "+B):B<247?l.push(B-139):B<251?(x=p[M],M+=1,l.push(256*(B-247)+x+108)):B<255?(x=p[M],M+=1,l.push(256*-(B-251)-x-108)):(x=p[M],U=p[M+1],T=p[M+2],E=p[M+3],M+=4,l.push((x<<24|U<<16|T<<8|E)/65536))}}}(n),t.advanceWidth=v,i}function Ve(e,t){let n,o=de.indexOf(e);return o>=0&&(n=o),o=t.indexOf(e),o>=0?n=o+de.length:(n=de.length+t.length,t.push(e)),n}function je(e,t,n){const o={};for(let r=0;r=o)throw new Error("CFF table CID Font FDSelect has bad FD index value "+s+" (FD count "+o+")");r.push(s)}else{if(3!==i)throw new Error("CFF Table CID Font FDSelect table has unsupported format "+i);{const e=a.parseCard16();let t,i=a.parseCard16();if(0!==i)throw new Error("CFF Table CID Font FDSelect format 3 range has bad initial GID "+i);for(let l=0;l=o)throw new Error("CFF table CID Font FDSelect has bad FD index value "+s+" (FD count "+o+")");if(t>n)throw new Error("CFF Table CID Font FDSelect format 3 range has bad GID "+t);for(;i=1&&(n.ulCodePageRange1=o.parseULong(),n.ulCodePageRange2=o.parseULong()),n.version>=2&&(n.sxHeight=o.parseShort(),n.sCapHeight=o.parseShort(),n.usDefaultChar=o.parseUShort(),n.usBreakChar=o.parseUShort(),n.usMaxContent=o.parseUShort()),n},make:function(e){return new oe.Table("OS/2",[{name:"version",type:"USHORT",value:3},{name:"xAvgCharWidth",type:"SHORT",value:0},{name:"usWeightClass",type:"USHORT",value:0},{name:"usWidthClass",type:"USHORT",value:0},{name:"fsType",type:"USHORT",value:0},{name:"ySubscriptXSize",type:"SHORT",value:650},{name:"ySubscriptYSize",type:"SHORT",value:699},{name:"ySubscriptXOffset",type:"SHORT",value:0},{name:"ySubscriptYOffset",type:"SHORT",value:140},{name:"ySuperscriptXSize",type:"SHORT",value:650},{name:"ySuperscriptYSize",type:"SHORT",value:699},{name:"ySuperscriptXOffset",type:"SHORT",value:0},{name:"ySuperscriptYOffset",type:"SHORT",value:479},{name:"yStrikeoutSize",type:"SHORT",value:49},{name:"yStrikeoutPosition",type:"SHORT",value:258},{name:"sFamilyClass",type:"SHORT",value:0},{name:"bFamilyType",type:"BYTE",value:0},{name:"bSerifStyle",type:"BYTE",value:0},{name:"bWeight",type:"BYTE",value:0},{name:"bProportion",type:"BYTE",value:0},{name:"bContrast",type:"BYTE",value:0},{name:"bStrokeVariation",type:"BYTE",value:0},{name:"bArmStyle",type:"BYTE",value:0},{name:"bLetterform",type:"BYTE",value:0},{name:"bMidline",type:"BYTE",value:0},{name:"bXHeight",type:"BYTE",value:0},{name:"ulUnicodeRange1",type:"ULONG",value:0},{name:"ulUnicodeRange2",type:"ULONG",value:0},{name:"ulUnicodeRange3",type:"ULONG",value:0},{name:"ulUnicodeRange4",type:"ULONG",value:0},{name:"achVendID",type:"CHARARRAY",value:"XXXX"},{name:"fsSelection",type:"USHORT",value:0},{name:"usFirstCharIndex",type:"USHORT",value:0},{name:"usLastCharIndex",type:"USHORT",value:0},{name:"sTypoAscender",type:"SHORT",value:0},{name:"sTypoDescender",type:"SHORT",value:0},{name:"sTypoLineGap",type:"SHORT",value:0},{name:"usWinAscent",type:"USHORT",value:0},{name:"usWinDescent",type:"USHORT",value:0},{name:"ulCodePageRange1",type:"ULONG",value:0},{name:"ulCodePageRange2",type:"ULONG",value:0},{name:"sxHeight",type:"SHORT",value:0},{name:"sCapHeight",type:"SHORT",value:0},{name:"usDefaultChar",type:"USHORT",value:0},{name:"usBreakChar",type:"USHORT",value:0},{name:"usMaxContext",type:"USHORT",value:0}],e)},unicodeRanges:gt,getUnicodeRange:function(e){for(let t=0;t=n.begin&&e=ye.length){const e=o.parseChar();n.names.push(o.parseString(e))}break;case 2.5:n.numberOfGlyphs=o.parseUShort(),n.offset=new Array(n.numberOfGlyphs);for(let e=0;et.value.tag?1:-1})),t.fields=t.fields.concat(o),t.fields=t.fields.concat(r),t}function kt(e,t,n){for(let n=0;n0){return e.glyphs.get(o).getMetrics()}}return n}function Rt(e){let t=0;for(let n=0;nm||void 0===l)&&m>0&&(l=m),c 123 are reserved for internal usage");f|=1<0?tt.make(w):void 0,D=yt.make(),C=$e.make(e.glyphs,{version:e.getEnglishName("version"),fullName:T,familyName:x,weightName:U,postScriptName:E,unitsPerEm:e.unitsPerEm,fontBBox:[0,d.yMin,d.ascender,d.advanceWidthMax]}),L=e.metas&&Object.keys(e.metas).length>0?Ut.make(e.metas):void 0,I=[g,m,y,v,k,S,D,C,b];R&&I.push(R),e.tables.gsub&&I.push(xt.make(e.tables.gsub)),L&&I.push(L);const M=wt(I),B=Et(M.encode()),G=M.fields;let N=!1;for(let e=0;e>>1,s=e[r].tag;if(s===t)return r;s>>1,s=e[r];if(s===t)return r;s=0)return o[r].script;if(t){const t={tag:e,script:{defaultLangSys:{reserved:0,reqFeatureIndex:65535,featureIndexes:[]},langSysRecords:[]}};return o.splice(-1-r,0,t),t.script}}},getLangSysTable:function(e,t,n){const o=this.getScriptTable(e,n);if(o){if(!t||"dflt"===t||"DFLT"===t)return o.defaultLangSys;const e=Ct(o.langSysRecords,t);if(e>=0)return o.langSysRecords[e].langSys;if(n){const n={tag:t,langSys:{reserved:0,reqFeatureIndex:65535,featureIndexes:[]}};return o.langSysRecords.splice(-1-e,0,n),n.langSys}}},getFeatureTable:function(e,t,n,o){const r=this.getLangSysTable(e,t,o);if(r){let e;const t=r.featureIndexes,s=this.font.tables[this.tableName].features;for(let o=0;o=s[o-1].tag,"Features must be added in alphabetical order."),e={tag:n,feature:{params:0,lookupListIndexes:[]}},s.push(e),t.push(o),e.feature}}},getLookupTables:function(e,t,n,o,r){const s=this.getFeatureTable(e,t,n,r),a=[];if(s){let e;const t=s.lookupListIndexes,n=this.font.tables[this.tableName].lookups;for(let r=0;r=0){const e=s.ligatureSets[c];for(let t=0;t0&&e<0?n:o<0&&e>0?-n:e*o},Zt={x:1,y:0,axis:"x",distance:function(e,t,n,o){return(n?e.xo:e.x)-(o?t.xo:t.x)},interpolate:function(e,t,n,o){let r,s,a,i,l,c,u;if(!o||o===this)return r=e.xo-t.xo,s=e.xo-n.xo,l=t.x-t.xo,c=n.x-n.xo,a=Math.abs(r),i=Math.abs(s),u=a+i,0===u?void(e.x=e.xo+(l+c)/2):void(e.x=e.xo+(l*i+c*a)/u);r=o.distance(e,t,!0,!0),s=o.distance(e,n,!0,!0),l=o.distance(t,t,!1,!0),c=o.distance(n,n,!1,!0),a=Math.abs(r),i=Math.abs(s),u=a+i,0!==u?Zt.setRelative(e,e,(l*i+c*a)/u,o,!0):Zt.setRelative(e,e,(l+c)/2,o,!0)},normalSlope:Number.NEGATIVE_INFINITY,setRelative:function(e,t,n,o,r){if(!o||o===this)return void(e.x=(r?t.xo:t.x)+n);const s=r?t.xo:t.x,a=r?t.yo:t.y,i=s+n*o.x,l=a+n*o.y;e.x=i+(e.y-l)/o.normalSlope},slope:0,touch:function(e){e.xTouched=!0},touched:function(e){return e.xTouched},untouch:function(e){e.xTouched=!1}},Qt={x:0,y:1,axis:"y",distance:function(e,t,n,o){return(n?e.yo:e.y)-(o?t.yo:t.y)},interpolate:function(e,t,n,o){let r,s,a,i,l,c,u;if(!o||o===this)return r=e.yo-t.yo,s=e.yo-n.yo,l=t.y-t.yo,c=n.y-n.yo,a=Math.abs(r),i=Math.abs(s),u=a+i,0===u?void(e.y=e.yo+(l+c)/2):void(e.y=e.yo+(l*i+c*a)/u);r=o.distance(e,t,!0,!0),s=o.distance(e,n,!0,!0),l=o.distance(t,t,!1,!0),c=o.distance(n,n,!1,!0),a=Math.abs(r),i=Math.abs(s),u=a+i,0!==u?Qt.setRelative(e,e,(l*i+c*a)/u,o,!0):Qt.setRelative(e,e,(l+c)/2,o,!0)},normalSlope:0,setRelative:function(e,t,n,o,r){if(!o||o===this)return void(e.y=(r?t.yo:t.y)+n);const s=r?t.xo:t.x,a=r?t.yo:t.y,i=s+n*o.x,l=a+n*o.y;e.y=l+o.normalSlope*(e.x-i)},slope:Number.POSITIVE_INFINITY,touch:function(e){e.yTouched=!0},touched:function(e){return e.yTouched},untouch:function(e){e.yTouched=!1}};function $t(e,t){this.x=e,this.y=t,this.axis=void 0,this.slope=t/e,this.normalSlope=-e/t,Object.freeze(this)}function Kt(e,t){const n=Math.sqrt(e*e+t*t);return t/=n,1===(e/=n)&&0===t?Zt:0===e&&1===t?Qt:new $t(e,t)}function Jt(e,t,n,o){this.x=this.xo=Math.round(64*e)/64,this.y=this.yo=Math.round(64*t)/64,this.lastPointOfContour=n,this.onCurve=o,this.prevPointOnContour=void 0,this.nextPointOnContour=void 0,this.xTouched=!1,this.yTouched=!1,Object.preventExtensions(this)}Object.freeze(Zt),Object.freeze(Qt),$t.prototype.distance=function(e,t,n,o){return this.x*Zt.distance(e,t,n,o)+this.y*Qt.distance(e,t,n,o)},$t.prototype.interpolate=function(e,t,n,o){let r,s,a,i,l,c,u;a=o.distance(e,t,!0,!0),i=o.distance(e,n,!0,!0),r=o.distance(t,t,!1,!0),s=o.distance(n,n,!1,!0),l=Math.abs(a),c=Math.abs(i),u=l+c,0!==u?this.setRelative(e,e,(r*c+s*l)/u,o,!0):this.setRelative(e,e,(r+s)/2,o,!0)},$t.prototype.setRelative=function(e,t,n,o,r){o=o||this;const s=r?t.xo:t.x,a=r?t.yo:t.y,i=s+n*o.x,l=a+n*o.y,c=o.normalSlope,u=this.slope,p=e.x,h=e.y;e.x=(u*p-c*i+l-h)/(u-c),e.y=u*(e.x-p)+h},$t.prototype.touch=function(e){e.xTouched=!0,e.yTouched=!0},Jt.prototype.nextTouched=function(e){let t=this.nextPointOnContour;for(;!e.touched(t)&&t!==this;)t=t.nextPointOnContour;return t},Jt.prototype.prevTouched=function(e){let t=this.prevPointOnContour;for(;!e.touched(t)&&t!==this;)t=t.prevPointOnContour;return t};const en=Object.freeze(new Jt(0,0)),tn={cvCutIn:17/16,deltaBase:9,deltaShift:.125,loop:1,minDis:1,autoFlip:!0};function nn(e,t){switch(this.env=e,this.stack=[],this.prog=t,e){case"glyf":this.zp0=this.zp1=this.zp2=1,this.rp0=this.rp1=this.rp2=0;case"prep":this.fv=this.pv=this.dpv=Zt,this.round=_t}}function on(e){const t=e.tZone=new Array(e.gZone.length);for(let e=0;e=176&&o<=183)r+=o-176+1;else if(o>=184&&o<=191)r+=2*(o-184+1);else if(t&&1===s&&27===o)break}while(s>0);e.ip=r}function sn(e,t){exports.DEBUG&&console.log(t.step,"SVTCA["+e.axis+"]"),t.fv=t.pv=t.dpv=e}function an(e,t){exports.DEBUG&&console.log(t.step,"SPVTCA["+e.axis+"]"),t.pv=t.dpv=e}function ln(e,t){exports.DEBUG&&console.log(t.step,"SFVTCA["+e.axis+"]"),t.fv=e}function cn(e,t){const n=t.stack,o=n.pop(),r=n.pop(),s=t.z2[o],a=t.z1[r];let i,l;exports.DEBUG&&console.log("SPVTL["+e+"]",o,r),e?(i=s.y-a.y,l=a.x-s.x):(i=a.x-s.x,l=a.y-s.y),t.pv=t.dpv=Kt(i,l)}function un(e,t){const n=t.stack,o=n.pop(),r=n.pop(),s=t.z2[o],a=t.z1[r];let i,l;exports.DEBUG&&console.log("SFVTL["+e+"]",o,r),e?(i=s.y-a.y,l=a.x-s.x):(i=a.x-s.x,l=a.y-s.y),t.fv=Kt(i,l)}function pn(e){exports.DEBUG&&console.log(e.step,"POP[]"),e.stack.pop()}function hn(e,t){const n=t.stack.pop(),o=t.z0[n],r=t.fv,s=t.pv;exports.DEBUG&&console.log(t.step,"MDAP["+e+"]",n);let a=s.distance(o,en);e&&(a=t.round(a)),r.setRelative(o,en,a,s),r.touch(o),t.rp0=t.rp1=n}function fn(e,t){const n=t.z2,o=n.length-2;let r,s,a;exports.DEBUG&&console.log(t.step,"IUP["+e.axis+"]");for(let t=0;t1?"loop "+(t.loop-i)+": ":"")+"SHP["+(e?"rp1":"rp2")+"]",o)}t.loop=1}function gn(e,t){const n=t.stack,o=e?t.rp1:t.rp2,r=(e?t.z0:t.z1)[o],s=t.fv,a=t.pv,i=n.pop(),l=t.z2[t.contours[i]];let c=l;exports.DEBUG&&console.log(t.step,"SHC["+e+"]",i);const u=a.distance(r,r,!1,!0);do{c!==r&&s.setRelative(c,c,u,a),c=c.nextPointOnContour}while(c!==l)}function mn(e,t){const n=t.stack,o=e?t.rp1:t.rp2,r=(e?t.z0:t.z1)[o],s=t.fv,a=t.pv,i=n.pop();let l,c;switch(exports.DEBUG&&console.log(t.step,"SHZ["+e+"]",i),i){case 0:l=t.tZone;break;case 1:l=t.gZone;break;default:throw new Error("Invalid zone")}const u=a.distance(r,r,!1,!0),p=l.length-2;for(let e=0;e",i),t.stack.push(Math.round(64*i))}function xn(e,t){const n=t.stack,o=n.pop(),r=t.fv,s=t.pv,a=t.ppem,i=t.deltaBase+16*(e-1),l=t.deltaShift,c=t.z0;exports.DEBUG&&console.log(t.step,"DELTAP["+e+"]",o,n);for(let e=0;e>4)!==a)continue;let u=(15&o)-8;u>=0&&u++,exports.DEBUG&&console.log(t.step,"DELTAPFIX",e,"by",u*l);const p=c[e];r.setRelative(p,p,u*l,s)}}function Un(e,t){const n=t.stack,o=n.pop();exports.DEBUG&&console.log(t.step,"ROUND[]"),n.push(64*t.round(o/64))}function Tn(e,t){const n=t.stack,o=n.pop(),r=t.ppem,s=t.deltaBase+16*(e-1),a=t.deltaShift;exports.DEBUG&&console.log(t.step,"DELTAC["+e+"]",o,n);for(let e=0;e>4)!==r)continue;let i=(15&o)-8;i>=0&&i++;const l=i*a;exports.DEBUG&&console.log(t.step,"DELTACFIX",e,"by",l),t.cvt[e]+=l}}function En(e,t){const n=t.stack,o=n.pop(),r=n.pop(),s=t.z2[o],a=t.z1[r];let i,l;exports.DEBUG&&console.log("SDPVTL["+e+"]",o,r),e?(i=s.y-a.y,l=a.x-s.x):(i=a.x-s.x,l=a.y-s.y),t.dpv=Kt(i,l)}function On(e,t){const n=t.stack,o=t.prog;let r=t.ip;exports.DEBUG&&console.log(t.step,"PUSHB["+e+"]");for(let t=0;t=0?1:-1,m=Math.abs(m),e&&(v=s.cvt[i],o&&Math.abs(m-v)":"_")+(o?"R":"_")+(0===r?"Gr":1===r?"Bl":2===r?"Wh":"")+"]",e?i+"("+s.cvt[i]+","+v+")":"",l,"(d =",g,"->",y*m,")"),s.rp1=s.rp0,s.rp2=l,t&&(s.rp0=l)}function Rn(e){(e=e||{}).empty||(Nt(e.familyName,"When creating a new Font object, familyName is required."),Nt(e.styleName,"When creating a new Font object, styleName is required."),Nt(e.unitsPerEm,"When creating a new Font object, unitsPerEm is required."),Nt(e.ascender,"When creating a new Font object, ascender is required."),Nt(e.descender,"When creating a new Font object, descender is required."),Nt(e.descender<0,"Descender should be negative (e.g. -512)."),this.names={fontFamily:{en:e.familyName||" "},fontSubfamily:{en:e.styleName||" "},fullName:{en:e.fullName||e.familyName+" "+e.styleName},postScriptName:{en:e.postScriptName||e.familyName+e.styleName},designer:{en:e.designer||" "},designerURL:{en:e.designerURL||" "},manufacturer:{en:e.manufacturer||" "},manufacturerURL:{en:e.manufacturerURL||" "},license:{en:e.license||" "},licenseURL:{en:e.licenseURL||" "},version:{en:e.version||"Version 0.1"},description:{en:e.description||" "},copyright:{en:e.copyright||" "},trademark:{en:e.trademark||" "}},this.unitsPerEm=e.unitsPerEm||1e3,this.ascender=e.ascender,this.descender=e.descender,this.createdTimestamp=e.createdTimestamp,this.tables={os2:{usWeightClass:e.weightClass||this.usWeightClasses.MEDIUM,usWidthClass:e.widthClass||this.usWidthClasses.MEDIUM,fsSelection:e.fsSelection||this.fsSelectionValues.REGULAR}}),this.supported=!0,this.glyphs=new Ie.GlyphSet(this,e.glyphs||[]),this.encoding=new ve(this),this.substitution=new It(this),this.tables=this.tables||{},Object.defineProperty(this,"hinting",{get:function(){return this._hinting?this._hinting:"truetype"===this.outlinesFormat?this._hinting=new zt(this):void 0}})}function Dn(e,t){const n=JSON.stringify(e);let o=256;for(let e in t){let r=parseInt(e);if(r&&!(r<256)){if(JSON.stringify(t[e])===n)return r;o<=r&&(o=r+1)}}return t[o]=e,o}function Cn(e,t,n){const o=Dn(t.name,n);return[{name:"tag_"+e,type:"TAG",value:t.tag},{name:"minValue_"+e,type:"FIXED",value:t.minValue<<16},{name:"defaultValue_"+e,type:"FIXED",value:t.defaultValue<<16},{name:"maxValue_"+e,type:"FIXED",value:t.maxValue<<16},{name:"flags_"+e,type:"USHORT",value:0},{name:"nameID_"+e,type:"USHORT",value:o}]}function Ln(e,t,n){const o={},r=new pe.Parser(e,t);return o.tag=r.parseTag(),o.minValue=r.parseFixed(),o.defaultValue=r.parseFixed(),o.maxValue=r.parseFixed(),r.skip("uShort",1),o.name=n[r.parseUShort()]||{},o}function In(e,t,n,o){const r=[{name:"nameID_"+e,type:"USHORT",value:Dn(t.name,o)},{name:"flags_"+e,type:"USHORT",value:0}];for(let o=0;o2)return;const n=this.font;let o=this._prepState;if(!o||o.ppem!==t){let e=this._fpgmState;if(!e){nn.prototype=tn,e=this._fpgmState=new nn("fpgm",n.tables.fpgm),e.funcs=[],e.font=n,exports.DEBUG&&(console.log("---EXEC FPGM---"),e.step=-1);try{At(e)}catch(e){return console.log("Hinting error in FPGM:"+e),void(this._errorState=3)}}nn.prototype=e,o=this._prepState=new nn("prep",n.tables.prep),o.ppem=t;const r=n.tables.cvt;if(r){const e=o.cvt=new Array(r.length),s=t/n.unitsPerEm;for(let t=0;t1))try{return Ft(e,o)}catch(e){return this._errorState<1&&(console.log("Hinting error:"+e),console.log("Note: further hinting errors are silenced")),void(this._errorState=1)}},Ft=function(e,t){const n=t.ppem/t.font.unitsPerEm,o=n;let r,s,a,i=e.components;if(nn.prototype=t,i){const l=t.font;s=[],r=[];for(let e=0;e1?"loop "+(e.loop-n)+": ":"")+"SHPIX[]",a,r),o.setRelative(i,i,r),o.touch(i)}e.loop=1},function(e){const t=e.stack,n=e.rp1,o=e.rp2;let r=e.loop;const s=e.z0[n],a=e.z1[o],i=e.fv,l=e.dpv,c=e.z2;for(;r--;){const u=t.pop(),p=c[u];exports.DEBUG&&console.log(e.step,(e.loop>1?"loop "+(e.loop-r)+": ":"")+"IP[]",u,n,"<->",o),i.interpolate(p,s,a,l),i.touch(p)}e.loop=1},yn.bind(void 0,0),yn.bind(void 0,1),function(e){const t=e.stack,n=e.rp0,o=e.z0[n];let r=e.loop;const s=e.fv,a=e.pv,i=e.z1;for(;r--;){const n=t.pop(),l=i[n];exports.DEBUG&&console.log(e.step,(e.loop>1?"loop "+(e.loop-r)+": ":"")+"ALIGNRP[]",n),s.setRelative(l,o,0,a),s.touch(l)}e.loop=1},function(e){exports.DEBUG&&console.log(e.step,"RTDG[]"),e.round=qt},vn.bind(void 0,0),vn.bind(void 0,1),function(e){const t=e.prog;let n=e.ip;const o=e.stack,r=t[++n];exports.DEBUG&&console.log(e.step,"NPUSHB[]",r);for(let e=0;en?1:0)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"GTEQ[]",n,o),t.push(o>=n?1:0)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"EQ[]",n,o),t.push(n===o?1:0)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"NEQ[]",n,o),t.push(n!==o?1:0)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"ODD[]",n),t.push(Math.trunc(n)%2?1:0)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"EVEN[]",n),t.push(Math.trunc(n)%2?0:1)},function(e){let t=e.stack.pop();exports.DEBUG&&console.log(e.step,"IF[]",t),t||(rn(e,!0),exports.DEBUG&&console.log(e.step,"EIF[]"))},function(e){exports.DEBUG&&console.log(e.step,"EIF[]")},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"AND[]",n,o),t.push(n&&o?1:0)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"OR[]",n,o),t.push(n||o?1:0)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"NOT[]",n),t.push(n?0:1)},xn.bind(void 0,1),function(e){const t=e.stack.pop();exports.DEBUG&&console.log(e.step,"SDB[]",t),e.deltaBase=t},function(e){const t=e.stack.pop();exports.DEBUG&&console.log(e.step,"SDS[]",t),e.deltaShift=Math.pow(.5,t)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"ADD[]",n,o),t.push(o+n)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"SUB[]",n,o),t.push(o-n)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"DIV[]",n,o),t.push(64*o/n)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"MUL[]",n,o),t.push(o*n/64)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"ABS[]",n),t.push(Math.abs(n))},function(e){const t=e.stack;let n=t.pop();exports.DEBUG&&console.log(e.step,"NEG[]",n),t.push(-n)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"FLOOR[]",n),t.push(64*Math.floor(n/64))},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"CEILING[]",n),t.push(64*Math.ceil(n/64))},Un.bind(void 0,0),Un.bind(void 0,1),Un.bind(void 0,2),Un.bind(void 0,3),void 0,void 0,void 0,void 0,function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"WCVTF[]",n,o),e.cvt[o]=n*e.ppem/e.font.unitsPerEm},xn.bind(void 0,2),xn.bind(void 0,3),Tn.bind(void 0,1),Tn.bind(void 0,2),Tn.bind(void 0,3),function(e){let t,n=e.stack.pop();switch(exports.DEBUG&&console.log(e.step,"SROUND[]",n),e.round=Yt,192&n){case 0:t=.5;break;case 64:t=1;break;case 128:t=2;break;default:throw new Error("invalid SROUND value")}switch(e.srPeriod=t,48&n){case 0:e.srPhase=0;break;case 16:e.srPhase=.25*t;break;case 32:e.srPhase=.5*t;break;case 48:e.srPhase=.75*t;break;default:throw new Error("invalid SROUND value")}n&=15,e.srThreshold=0===n?0:(n/8-.5)*t},function(e){let t,n=e.stack.pop();switch(exports.DEBUG&&console.log(e.step,"S45ROUND[]",n),e.round=Yt,192&n){case 0:t=Math.sqrt(2)/2;break;case 64:t=Math.sqrt(2);break;case 128:t=2*Math.sqrt(2);break;default:throw new Error("invalid S45ROUND value")}switch(e.srPeriod=t,48&n){case 0:e.srPhase=0;break;case 16:e.srPhase=.25*t;break;case 32:e.srPhase=.5*t;break;case 48:e.srPhase=.75*t;break;default:throw new Error("invalid S45ROUND value")}n&=15,e.srThreshold=0===n?0:(n/8-.5)*t},void 0,void 0,function(e){exports.DEBUG&&console.log(e.step,"ROFF[]"),e.round=Wt},void 0,function(e){exports.DEBUG&&console.log(e.step,"RUTG[]"),e.round=Vt},function(e){exports.DEBUG&&console.log(e.step,"RDTG[]"),e.round=jt},pn,pn,void 0,void 0,void 0,void 0,void 0,function(e){const t=e.stack.pop();exports.DEBUG&&console.log(e.step,"SCANCTRL[]",t)},En.bind(void 0,0),En.bind(void 0,1),function(e){const t=e.stack,n=t.pop();let o=0;exports.DEBUG&&console.log(e.step,"GETINFO[]",n),1&n&&(o=35),32&n&&(o|=4096),t.push(o)},void 0,function(e){const t=e.stack,n=t.pop(),o=t.pop(),r=t.pop();exports.DEBUG&&console.log(e.step,"ROLL[]"),t.push(o),t.push(n),t.push(r)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"MAX[]",n,o),t.push(Math.max(o,n))},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"MIN[]",n,o),t.push(Math.min(o,n))},function(e){const t=e.stack.pop();exports.DEBUG&&console.log(e.step,"SCANTYPE[]",t)},function(e){const t=e.stack.pop();let n=e.stack.pop();switch(exports.DEBUG&&console.log(e.step,"INSTCTRL[]",t,n),t){case 1:return void(e.inhibitGridFit=!!n);case 2:return void(e.ignoreCvt=!!n);default:throw new Error("invalid INSTCTRL[] selector")}},void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,On.bind(void 0,1),On.bind(void 0,2),On.bind(void 0,3),On.bind(void 0,4),On.bind(void 0,5),On.bind(void 0,6),On.bind(void 0,7),On.bind(void 0,8),wn.bind(void 0,1),wn.bind(void 0,2),wn.bind(void 0,3),wn.bind(void 0,4),wn.bind(void 0,5),wn.bind(void 0,6),wn.bind(void 0,7),wn.bind(void 0,8),kn.bind(void 0,0,0,0,0,0),kn.bind(void 0,0,0,0,0,1),kn.bind(void 0,0,0,0,0,2),kn.bind(void 0,0,0,0,0,3),kn.bind(void 0,0,0,0,1,0),kn.bind(void 0,0,0,0,1,1),kn.bind(void 0,0,0,0,1,2),kn.bind(void 0,0,0,0,1,3),kn.bind(void 0,0,0,1,0,0),kn.bind(void 0,0,0,1,0,1),kn.bind(void 0,0,0,1,0,2),kn.bind(void 0,0,0,1,0,3),kn.bind(void 0,0,0,1,1,0),kn.bind(void 0,0,0,1,1,1),kn.bind(void 0,0,0,1,1,2),kn.bind(void 0,0,0,1,1,3),kn.bind(void 0,0,1,0,0,0),kn.bind(void 0,0,1,0,0,1),kn.bind(void 0,0,1,0,0,2),kn.bind(void 0,0,1,0,0,3),kn.bind(void 0,0,1,0,1,0),kn.bind(void 0,0,1,0,1,1),kn.bind(void 0,0,1,0,1,2),kn.bind(void 0,0,1,0,1,3),kn.bind(void 0,0,1,1,0,0),kn.bind(void 0,0,1,1,0,1),kn.bind(void 0,0,1,1,0,2),kn.bind(void 0,0,1,1,0,3),kn.bind(void 0,0,1,1,1,0),kn.bind(void 0,0,1,1,1,1),kn.bind(void 0,0,1,1,1,2),kn.bind(void 0,0,1,1,1,3),kn.bind(void 0,1,0,0,0,0),kn.bind(void 0,1,0,0,0,1),kn.bind(void 0,1,0,0,0,2),kn.bind(void 0,1,0,0,0,3),kn.bind(void 0,1,0,0,1,0),kn.bind(void 0,1,0,0,1,1),kn.bind(void 0,1,0,0,1,2),kn.bind(void 0,1,0,0,1,3),kn.bind(void 0,1,0,1,0,0),kn.bind(void 0,1,0,1,0,1),kn.bind(void 0,1,0,1,0,2),kn.bind(void 0,1,0,1,0,3),kn.bind(void 0,1,0,1,1,0),kn.bind(void 0,1,0,1,1,1),kn.bind(void 0,1,0,1,1,2),kn.bind(void 0,1,0,1,1,3),kn.bind(void 0,1,1,0,0,0),kn.bind(void 0,1,1,0,0,1),kn.bind(void 0,1,1,0,0,2),kn.bind(void 0,1,1,0,0,3),kn.bind(void 0,1,1,0,1,0),kn.bind(void 0,1,1,0,1,1),kn.bind(void 0,1,1,0,1,2),kn.bind(void 0,1,1,0,1,3),kn.bind(void 0,1,1,1,0,0),kn.bind(void 0,1,1,1,0,1),kn.bind(void 0,1,1,1,0,2),kn.bind(void 0,1,1,1,0,3),kn.bind(void 0,1,1,1,1,0),kn.bind(void 0,1,1,1,1,1),kn.bind(void 0,1,1,1,1,2),kn.bind(void 0,1,1,1,1,3)],Rn.prototype.hasChar=function(e){return null!==this.encoding.charToGlyphIndex(e)},Rn.prototype.charToGlyphIndex=function(e){return this.encoding.charToGlyphIndex(e)},Rn.prototype.charToGlyph=function(e){const t=this.charToGlyphIndex(e);let n=this.glyphs.get(t);return n||(n=this.glyphs.get(0)),n},Rn.prototype.stringToGlyphs=function(e,t){t=t||this.defaultRenderOptions;const n=[];for(let t=0;t>1;e1&&console.warn("Only the first kern subtable is supported."),e.skip("uLong");const n=255&e.parseUShort();if(e.skip("uShort"),0===n){const n=e.parseUShort();e.skip("uShort",3);for(let o=0;o{const t=jn.loadSync(e);Qn.font=t,Qn.ascender=t.ascender,Qn.descender=t.descender}};const Kn=$n.options,Jn=function(e,t){return Math.round(e+Math.random()*(t-e))};const eo=function(e,t){return{text:(e+t).toString(),equation:e+"+"+t}},to=function(e,t){return{text:(e-t).toString(),equation:e+"-"+t}};function no(e,t,n){return 6*(n=(n+1)%1)<1?e+(t-e)*n*6:2*n<1?t:3*n<2?e+(t-e)*(2/3-n)*6:e}var oo={int:Jn,greyColor:function(e,t){const n=Jn(e=e||1,t=t||9).toString(16);return`#${n}${n}${n}`},captchaText:function(e){"number"==typeof e&&(e={size:e});const t=(e=e||{}).size||4,n=e.ignoreChars||"";let o=-1,r="",s=e.charPreset||Kn.charPreset;n&&(s=function(e,t){return e.split("").filter(e=>-1===t.indexOf(e))}(s,n));const a=s.length-1;for(;++o>16,o=t>>8&255,r=255&t,s=Math.max(n,o,r),a=Math.min(n,o,r);return(s+a)/510}(e):1;let r,s;o>=.5?(r=Math.round(100*o)-45,s=Math.round(100*o)-25):(r=Math.round(100*o)+25,s=Math.round(100*o)+45);const a=Jn(r,s)/100,i=a<.5?a*(a+n):a+n-a*n,l=2*a-i,c=Math.floor(255*no(l,i,t+1/3)),u=Math.floor(255*no(l,i,t));return"#"+(Math.floor(255*no(l,i,t-1/3))|u<<8|c<<16|1<<24).toString(16).slice(1)}};const ro=$n.options,so=function(e,t){e=e||oo.captchaText();const n=(t=Object.assign({},ro,t)).width,o=t.height,r=t.background||t.backgroundColor;r&&(t.color=!0);const s=r?``:"",a=[].concat(function(e,t,n){const o=n.color,r=[],s=n.inverse?7:1,a=n.inverse?15:9;let i=-1;for(;++i`)}return r}(n,o,t)).concat(function(e,t,n,o,r){const s=e.length,a=(t-2)/(s+1),i=o.inverse?10:0,l=o.inverse?14:4;let u=-1;const p=[],h=r||o.color?oo.color(o.background):oo.greyColor(i,l);for(;++u`)}return p}(e,n,o,t)).sort(()=>Math.random()-.5).join("");return`${``}${s}${a}`};var ao=so,io=oo.captchaText,lo=function(e){const t=e.text||oo.captchaText(e);return{text:t,data:so(t,e)}},co=function(e){const t=oo.mathExpr(e.mathMin,e.mathMax,e.mathOperator);return{text:t.text,data:so(t.equation,e)}},uo=ro,po=$n.loadFont;ao.randomText=io,ao.create=lo,ao.createMathExpr=co,ao.options=uo,ao.loadFont=po;var ho=ao;const fo=Object.prototype.toString;function go(e){return"[object Object]"===fo.call(e)}function mo(){"development"===process.env.NODE_ENV&&console.log(...arguments)}const yo=async function(){};function vo(e){return yo.constructor===e.constructor?async function(){const t=await e.apply(this,arguments);return go(t)&&(t.msg&&(t.message=t.msg,t.errMsg=t.msg),0===t.code?t.errCode=t.code:t.errCode=s[t.code]||t.code),t}:function(){const t=e.apply(this,arguments);return go(t)&&(t.msg&&(t.message=t.msg,t.errMsg=t.msg),0===t.code?t.errCode=t.code:t.errCode=s[t.code]||t.code),t}}const bo=uniCloud.database().collection("opendb-verify-codes");class So{async setVerifyCode({deviceId:e,code:t,expiresDate:n,scene:o}){if(!e)return{code:10101,msg:"deviceId不可为空"};if(!t)return{code:10102,msg:"验证码不可为空"};n||(n=180);const r=Date.now(),s={deviceId:e,scene:o,code:t.toLocaleLowerCase(),state:0,ip:__ctx__.CLIENTIP,created_date:r,expired_date:r+1e3*n};return mo("addRes",await bo.add(s)),{code:0,deviceId:e}}async verifyCode({deviceId:e,code:t,scene:n}){if(!e)return{code:10101,msg:"deviceId不可为空"};if(!t)return{code:10102,msg:"验证码不可为空"};const o=Date.now(),r={deviceId:e,scene:n,code:t.toLocaleLowerCase(),state:0},s=await bo.where(r).orderBy("created_date","desc").limit(1).get();if(mo("verifyRecord:",s),s&&s.data&&s.data.length>0){const e=s.data[0];if(e.expired_date{e.scene&&delete e.scene,this.pluginConfig.scene[n]=Object.assign({},t,e[n])})}}}{constructor(){super(),this.DEVICEID2opts={}}mergeConfig(e){const t=go(this.pluginConfig.scene)?this.pluginConfig.scene[e.scene]:e.scene;return Object.assign({},go(t)?t:this.pluginConfig,e)}async create(e={}){if(!e.scene)throw new Error("scene验证码场景不可为空");e=this.mergeConfig(e);let{scene:t,expiresDate:n,deviceId:o,...r}=e;if(o=o||__ctx__.DEVICEID,!o)throw new Error("deviceId不可为空");const s=new So;try{const{text:a,base64:i}=function(e={}){const{uniPlatform:t=""}=e;let n;n=e.mathExpr?ho.createMathExpr(e):ho.create(e);let o="data:image/svg+xml;utf8,"+n.data.replace(/#/g,"%23");return(!t||"string"==typeof t&&-1===t.indexOf("mp-"))&&(o=o.replace(/"/g,"'").replace(//g,"%3E")),{text:n.text,base64:o}}(r),l=await s.setVerifyCode({deviceId:o,code:a,expiresDate:n,scene:t});return l.code>0?{...l,code:10001}:(this.DEVICEID2opts[o]=e,{code:0,msg:"验证码获取成功",captchaBase64:i})}catch(e){return{code:10001,msg:"验证码生成失败:"+e.message}}}async verify({deviceId:e,captcha:t,scene:n}){if(!(e=e||__ctx__.DEVICEID))throw new Error("deviceId不可为空");if(!n)throw new Error("scene验证码场景不可为空");const o=new So;try{const r=await o.verifyCode({deviceId:e,code:t,scene:n});return r.code>0?r:{code:0,msg:"验证码通过"}}catch(e){return{code:10002,msg:"验证码校验失败:"+e.message}}}async refresh(e={}){let{scene:t,expiresDate:n,deviceId:o,...r}=e;if(o=o||__ctx__.DEVICEID,!o)throw new Error("deviceId不可为空");if(!t)throw new Error("scene验证码场景不可为空");const s=await bo.where({deviceId:o,scene:t}).orderBy("created_date","desc").limit(1).get();if(s&&s.data&&s.data.length>0){const e=s.data[0];await bo.doc(e._id).update({state:2}),Object.keys(r).length>0&&(this.DEVICEID2opts[o]=Object.assign({},this.DEVICEID2opts[o],r));let a={};try{a=await this.create(Object.assign({},this.DEVICEID2opts[o],{deviceId:o,scene:t,expiresDate:n}))}catch(e){return{code:50403,msg:e.message}}return a.code>0?{...a,code:50403}:{code:0,msg:"验证码刷新成功",captchaBase64:a.captchaBase64}}return{code:10003,msg:`验证码刷新失败:无此设备在 ${t} 场景信息,请重新获取`}}}const To=new So;Object.keys(To).forEach(e=>{Uo.prototype[e]=vo(To[e])});const Eo=new Uo,Oo=new Proxy(Eo,{get(e,t){if(t in e)return"function"==typeof e[t]?vo(e[t]).bind(Oo):e[t]}});module.exports=Oo; +"use strict";var e=require("assert"),t=require("path");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=n(e),r=n(t);const s={10001:"uni-captcha-create-fail",10002:"uni-captcha-verify-fail",10003:"uni-captcha-refresh-fail",10101:"uni-captcha-deviceId-required",10102:"uni-captcha-text-required",10103:"uni-captcha-verify-overdue",10104:"uni-captcha-verify-fail",50403:"uni-captcha-interior-fail"};function a(e){const t=.2*Math.random()-.1;switch(e.type){case"M":case"L":e.x+=t,e.y+=t;break;case"Q":case"C":e.x+=t,e.y+=t,e.x1+=t,e.y1+=t}return e}function i(e,t,n,o,r,s,a){let i,l,c,u,p,h;if(e<=0||e>=1)throw RangeError("spliteCurveAt requires position > 0 && position < 1");return u=[],p=0,i={},l={},c={},i.x=t,i.y=n,l.x=o,l.y=r,c.x=s,c.y=a,h=e,u[p++]=i.x,u[p++]=i.y,u[p++]=i.x+=(l.x-i.x)*h,u[p++]=i.y+=(l.y-i.y)*h,l.x+=(c.x-l.x)*h,l.y+=(c.y-l.y)*h,u[p++]=i.x+(l.x-i.x)*h,u[p++]=i.y+(l.y-i.y)*h,u[p++]=l.x,u[p++]=l.y,u[p++]=c.x,u[p++]=c.y,u}function l(e,t){return Math.random()*(t-e)+e}var c=function(e,t){const n=e[0];o.default(n,"expect a string");const r=t.fontSize,s=r/t.font.unitsPerEm,c=t.font.charToGlyph(n),u=c.advanceWidth?c.advanceWidth*s:0,p=t.x-u/2,h=(t.ascender+t.descender)*s,f=t.y+h/2,d=c.getPath(p,f,r);d.commands.forEach(a),d.commands=function(e,t){const n=[];for(let o=0;ot.truncateLineProbability){const e=l(-.1,.1);n.push(r),n.push({type:"L",x:(r.x+s.x)/2+e,y:(r.y+s.y)/2+e})}else n.push(r)}else if("Q"===r.type&&o>=1){const s=e[o-1];if(("L"===s.type||"M"===s.type)&&Math.random()>t.truncateCurveProbability){const e=s.x,o=s.y,a=l(-.1,.1),c=r.x1+a,u=r.y1+a,p=r.x+a,h=r.y+a,f=i(l(t.truncateCurvePositionMin,t.truncateCurvePositionMax),e,o,c,u,p,h),d={type:"Q",x1:f[2],y1:f[3],x:f[4],y:f[5]},g={type:"L",x:f[4],y:f[5]},m={type:"Q",x1:f[6],y1:f[7],x:f[8],y:f[9]},y={type:"L",x:f[8],y:f[9]};n.push(d),n.push(g),n.push(m),n.push(y)}}else n.push(r)}return n}(d.commands,t);return d.toPathData()};function u(){this.table=new Uint16Array(16),this.trans=new Uint16Array(288)}function p(e,t){this.source=e,this.sourceIndex=0,this.tag=0,this.bitcount=0,this.dest=t,this.destLen=0,this.ltree=new u,this.dtree=new u}var h=new u,f=new u,d=new Uint8Array(30),g=new Uint16Array(30),m=new Uint8Array(30),y=new Uint16Array(30),v=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),b=new u,S=new Uint8Array(320);function x(e,t,n,o){var r,s;for(r=0;r>>=1,t}function O(e,t,n){if(!t)return n;for(;e.bitcount<24;)e.tag|=e.source[e.sourceIndex++]<>>16-t;return e.tag>>>=t,e.bitcount-=t,o+n}function w(e,t){for(;e.bitcount<24;)e.tag|=e.source[e.sourceIndex++]<>>=1,++r,n+=t.table[r],o-=t.table[r]}while(o>=0);return e.tag=s,e.bitcount-=r,t.trans[n+o]}function k(e,t,n){var o,r,s,a,i,l;for(o=O(e,5,257),r=O(e,5,1),s=O(e,4,4),a=0;a<19;++a)S[a]=0;for(a=0;a8;)e.sourceIndex--,e.bitcount-=8;if((t=256*(t=e.source[e.sourceIndex+1])+e.source[e.sourceIndex])!==(65535&~(256*e.source[e.sourceIndex+3]+e.source[e.sourceIndex+2])))return-3;for(e.sourceIndex+=4,n=t;n;--n)e.dest[e.destLen++]=e.source[e.sourceIndex++];return e.bitcount=0,0}!function(e,t){var n;for(n=0;n<7;++n)e.table[n]=0;for(e.table[7]=24,e.table[8]=152,e.table[9]=112,n=0;n<24;++n)e.trans[n]=256+n;for(n=0;n<144;++n)e.trans[24+n]=n;for(n=0;n<8;++n)e.trans[168+n]=280+n;for(n=0;n<112;++n)e.trans[176+n]=144+n;for(n=0;n<5;++n)t.table[n]=0;for(t.table[5]=32,n=0;n<32;++n)t.trans[n]=n}(h,f),x(d,g,4,3),x(m,y,2,1),d[28]=0,g[28]=258;var C=function(e,t){var n,o,r=new p(e,t);do{switch(n=E(r),O(r,2,0)){case 0:o=D(r);break;case 1:o=R(r,h,f);break;case 2:k(r,r.ltree,r.dtree),o=R(r,r.ltree,r.dtree);break;default:o=-3}if(0!==o)throw new Error("Data error")}while(!n);return r.destLenthis.x2&&(this.x2=e)),"number"==typeof t&&((isNaN(this.y1)||isNaN(this.y2))&&(this.y1=t,this.y2=t),tthis.y2&&(this.y2=t))},I.prototype.addX=function(e){this.addPoint(e,null)},I.prototype.addY=function(e){this.addPoint(null,e)},I.prototype.addBezier=function(e,t,n,o,r,s,a,i){const l=[e,t],c=[n,o],u=[r,s],p=[a,i];this.addPoint(e,t),this.addPoint(a,i);for(let e=0;e<=1;e++){const t=6*l[e]-12*c[e]+6*u[e],n=-3*l[e]+9*c[e]-9*u[e]+3*p[e],o=3*c[e]-3*l[e];if(0===n){if(0===t)continue;const n=-o/t;0=0&&n>0&&(e+=" "),e+=t(o)}return e}e=void 0!==e?e:2;let o="";for(let e=0;e=0&&e<=255,"Byte value should be between 0 and 255."),[e]},F.BYTE=H(1),A.CHAR=function(e){return[e.charCodeAt(0)]},F.CHAR=H(1),A.CHARARRAY=function(e){const t=[];for(let n=0;n>8&255,255&e]},F.USHORT=H(2),A.SHORT=function(e){return e>=32768&&(e=-(65536-e)),[e>>8&255,255&e]},F.SHORT=H(2),A.UINT24=function(e){return[e>>16&255,e>>8&255,255&e]},F.UINT24=H(3),A.ULONG=function(e){return[e>>24&255,e>>16&255,e>>8&255,255&e]},F.ULONG=H(4),A.LONG=function(e){return e>=2147483648&&(e=-(4294967296-e)),[e>>24&255,e>>16&255,e>>8&255,255&e]},F.LONG=H(4),A.FIXED=A.ULONG,F.FIXED=F.ULONG,A.FWORD=A.SHORT,F.FWORD=F.SHORT,A.UFWORD=A.USHORT,F.UFWORD=F.USHORT,A.LONGDATETIME=function(e){return[0,0,0,0,e>>24&255,e>>16&255,e>>8&255,255&e]},F.LONGDATETIME=H(8),A.TAG=function(e){return N.argument(4===e.length,"Tag should be exactly 4 ASCII characters."),[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]},F.TAG=H(4),A.Card8=A.BYTE,F.Card8=F.BYTE,A.Card16=A.USHORT,F.Card16=F.USHORT,A.OffSize=A.BYTE,F.OffSize=F.BYTE,A.SID=A.USHORT,F.SID=F.USHORT,A.NUMBER=function(e){return e>=-107&&e<=107?[e+139]:e>=108&&e<=1131?[247+((e-=108)>>8),255&e]:e>=-1131&&e<=-108?[251+((e=-e-108)>>8),255&e]:e>=-32768&&e<=32767?A.NUMBER16(e):A.NUMBER32(e)},F.NUMBER=function(e){return A.NUMBER(e).length},A.NUMBER16=function(e){return[28,e>>8&255,255&e]},F.NUMBER16=H(3),A.NUMBER32=function(e){return[29,e>>24&255,e>>16&255,e>>8&255,255&e]},F.NUMBER32=H(5),A.REAL=function(e){let t=e.toString();const n=/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(t);if(n){const o=parseFloat("1e"+((n[2]?+n[2]:0)+n[1].length));t=(Math.round(e*o)/o).toString()}let o="";for(let e=0,n=t.length;e>8&255,t[t.length]=255&o}return t},F.UTF16=function(e){return 2*e.length};const z={"x-mac-croatian":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®Š™´¨≠ŽØ∞±≤≥∆µ∂∑∏š∫ªºΩžø¿¡¬√ƒ≈Ć«Č… ÀÃÕŒœĐ—“”‘’÷◊©⁄€‹›Æ»–·‚„‰ÂćÁčÈÍÎÏÌÓÔđÒÚÛÙıˆ˜¯πË˚¸Êæˇ","x-mac-cyrillic":"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ†°Ґ£§•¶І®©™Ђђ≠Ѓѓ∞±≤≥іµґЈЄєЇїЉљЊњјЅ¬√ƒ≈∆«»… ЋћЌќѕ–—“”‘’÷„ЎўЏџ№Ёёяабвгдежзийклмнопрстуфхцчшщъыьэю","x-mac-gaelic":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØḂ±≤≥ḃĊċḊḋḞḟĠġṀæøṁṖṗɼƒſṠ«»… ÀÃÕŒœ–—“”‘’ṡẛÿŸṪ€‹›Ŷŷṫ·Ỳỳ⁊ÂÊÁËÈÍÎÏÌÓÔ♣ÒÚÛÙıÝýŴŵẄẅẀẁẂẃ","x-mac-greek":"Ĺ²É³ÖÜ΅àâä΄¨çéèê룙î‰ôö¦€ùûü†ΓΔΘΛΞΠß®©ΣΪ§≠°·Α±≤≥¥ΒΕΖΗΙΚΜΦΫΨΩάΝ¬ΟΡ≈Τ«»… ΥΧΆΈœ–―“”‘’÷ΉΊΌΎέήίόΏύαβψδεφγηιξκλμνοπώρστθωςχυζϊϋΐΰ­","x-mac-icelandic":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûüÝ°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€ÐðÞþý·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-inuit":"ᐃᐄᐅᐆᐊᐋᐱᐲᐳᐴᐸᐹᑉᑎᑏᑐᑑᑕᑖᑦᑭᑮᑯᑰᑲᑳᒃᒋᒌᒍᒎᒐᒑ°ᒡᒥᒦ•¶ᒧ®©™ᒨᒪᒫᒻᓂᓃᓄᓅᓇᓈᓐᓯᓰᓱᓲᓴᓵᔅᓕᓖᓗᓘᓚᓛᓪᔨᔩᔪᔫᔭ… ᔮᔾᕕᕖᕗ–—“”‘’ᕘᕙᕚᕝᕆᕇᕈᕉᕋᕌᕐᕿᖀᖁᖂᖃᖄᖅᖏᖐᖑᖒᖓᖔᖕᙱᙲᙳᙴᙵᙶᖖᖠᖡᖢᖣᖤᖥᖦᕼŁł","x-mac-ce":"ÄĀāÉĄÖÜáąČäčĆć鏟ĎíďĒēĖóėôöõúĚěü†°Ę£§•¶ß®©™ę¨≠ģĮįĪ≤≥īĶ∂∑łĻļĽľĹĺŅņѬ√ńŇ∆«»… ňŐÕőŌ–—“”‘’÷◊ōŔŕŘ‹›řŖŗŠ‚„šŚśÁŤťÍŽžŪÓÔūŮÚůŰűŲųÝýķŻŁżĢˇ",macintosh:"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›fifl‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-romanian":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ĂȘ∞±≤≥¥µ∂∑∏π∫ªºΩăș¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›Țț‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-turkish":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸĞğİıŞş‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙˆ˜¯˘˙˚¸˝˛ˇ"};P.MACSTRING=function(e,t,n,o){const r=z[o];if(void 0===r)return;let s="";for(let o=0;o=-128&&e<=127}function X(e,t,n){let o=0;const r=e.length;for(;t>8&255,t+256&255)}return s}A.MACSTRING=function(e,t){const n=function(e){if(!W){W={};for(let e in z)W[e]=new String(e)}const t=W[e];if(void 0===t)return;if(_){const e=_.get(t);if(void 0!==e)return e}const n=z[e];if(void 0===n)return;const o={};for(let e=0;e=128&&(r=n[r],void 0===r))return;o[t]=r}return o},F.MACSTRING=function(e,t){const n=A.MACSTRING(e,t);return void 0!==n?n.length:0},A.VARDELTAS=function(e){let t=0;const n=[];for(;t=-128&&o<=127?V(e,t,n):j(e,t,n)}return n},A.INDEX=function(e){let t=1;const n=[t],o=[];for(let r=0;r>8,t[s+1]=255&a,t=t.concat(o[n])}return t},F.TABLE=function(e){let t=0;const n=e.fields.length;for(let o=0;o0)return new ce(this.data,this.offset+t).parseStruct(e)},ce.prototype.parseListOfLists=function(e){const t=this.parseOffset16List(),n=t.length,o=this.relativeOffset,r=new Array(n);for(let o=0;o=0;r-=1){const n=pe.getUShort(e,t+4+8*r),s=pe.getUShort(e,t+4+8*r+2);if(3===n&&(0===s||1===s||10===s)){o=pe.getULong(e,t+4+8*r+4);break}}if(-1===o)throw new Error("No valid cmap sub-tables found.");const r=new pe.Parser(e,t+o);if(n.format=r.parseUShort(),12===n.format)!function(e,t){let n;t.parseUShort(),e.length=t.parseULong(),e.language=t.parseULong(),e.groupCount=n=t.parseULong(),e.glyphIndexMap={};for(let o=0;o>1,t.skip("uShort",3),e.glyphIndexMap={};const a=new pe.Parser(n,o+r+14),i=new pe.Parser(n,o+r+16+2*s),l=new pe.Parser(n,o+r+16+4*s),c=new pe.Parser(n,o+r+16+6*s);let u=o+r+16+8*s;for(let t=0;t0?(s=e.parseByte(),0==(t&r)&&(s=-s),s=n+s):s=(t&r)>0?n:n+e.parseShort(),s}function Ee(e,t,n){const o=new pe.Parser(t,n);let r,s;if(e.numberOfContours=o.parseShort(),e._xMin=o.parseShort(),e._yMin=o.parseShort(),e._xMax=o.parseShort(),e._yMax=o.parseShort(),e.numberOfContours>0){const t=e.endPointIndices=[];for(let n=0;n0){const t=o.parseByte();for(let n=0;n0){const a=[];let i;if(n>0){for(let e=0;e=0,a.push(i);let e=0;for(let t=0;t0?(2&r)>0?(n.dx=o.parseShort(),n.dy=o.parseShort()):n.matchedPoints=[o.parseUShort(),o.parseUShort()]:(2&r)>0?(n.dx=o.parseChar(),n.dy=o.parseChar()):n.matchedPoints=[o.parseByte(),o.parseByte()],(8&r)>0?n.xScale=n.yScale=o.parseF2Dot14():(64&r)>0?(n.xScale=o.parseF2Dot14(),n.yScale=o.parseF2Dot14()):(128&r)>0&&(n.xScale=o.parseF2Dot14(),n.scale01=o.parseF2Dot14(),n.scale10=o.parseF2Dot14(),n.yScale=o.parseF2Dot14()),e.components.push(n),t=!!(32&r)}if(256&r){e.instructionLength=o.parseUShort(),e.instructions=[];for(let t=0;tt.points.length-1||o.matchedPoints[1]>r.points.length-1)throw Error("Matched points out of range in "+t.name);const n=t.points[o.matchedPoints[0]];let s=r.points[o.matchedPoints[1]];const a={xScale:o.xScale,scale01:o.scale01,scale10:o.scale10,yScale:o.yScale,dx:0,dy:0};s=Oe([s],a)[0],a.dx=n.x-s.x,a.dy=n.y-s.y,e=Oe(r.points,a)}t.points=t.points.concat(e)}}return we(t.points)}var Re={getPath:we,parse:function(e,t,n,o){const r=new Ie.GlyphSet(o);for(let s=0;s>4,s=15&o;if(15===r)break;if(t+=n[r],15===s)break;t+=n[s]}return parseFloat(t)}(e);if(t>=32&&t<=246)return t-139;if(t>=247&&t<=250)return n=e.parseByte(),256*(t-247)+n+108;if(t>=251&&t<=254)return n=e.parseByte(),256*-(t-251)-n-108;throw new Error("Invalid b0 "+t)}function Pe(e,t,n){t=void 0!==t?t:0;const o=new pe.Parser(e,t),r=[];let s=[];for(n=void 0!==n?n:e.length;o.relativeOffset>1,l.length=0,d=!0}return function n(p){let x,U,T,E,O,w,k,R,D,C,L,I,M=0;for(;M1&&!d&&(v=l.shift()+h,d=!0),y+=l.pop(),b(m,y);break;case 5:for(;l.length>0;)m+=l.shift(),y+=l.shift(),i.lineTo(m,y);break;case 6:for(;l.length>0&&(m+=l.shift(),i.lineTo(m,y),0!==l.length);)y+=l.shift(),i.lineTo(m,y);break;case 7:for(;l.length>0&&(y+=l.shift(),i.lineTo(m,y),0!==l.length);)m+=l.shift(),i.lineTo(m,y);break;case 8:for(;l.length>0;)o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+l.shift(),i.curveTo(o,r,s,a,m,y);break;case 10:O=l.pop()+u,w=c[O],w&&n(w);break;case 11:return;case 12:switch(B=p[M],M+=1,B){case 35:o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),k=s+l.shift(),R=a+l.shift(),D=k+l.shift(),C=R+l.shift(),L=D+l.shift(),I=C+l.shift(),m=L+l.shift(),y=I+l.shift(),l.shift(),i.curveTo(o,r,s,a,k,R),i.curveTo(D,C,L,I,m,y);break;case 34:o=m+l.shift(),r=y,s=o+l.shift(),a=r+l.shift(),k=s+l.shift(),R=a,D=k+l.shift(),C=a,L=D+l.shift(),I=y,m=L+l.shift(),i.curveTo(o,r,s,a,k,R),i.curveTo(D,C,L,I,m,y);break;case 36:o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),k=s+l.shift(),R=a,D=k+l.shift(),C=a,L=D+l.shift(),I=C+l.shift(),m=L+l.shift(),i.curveTo(o,r,s,a,k,R),i.curveTo(D,C,L,I,m,y);break;case 37:o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),k=s+l.shift(),R=a+l.shift(),D=k+l.shift(),C=R+l.shift(),L=D+l.shift(),I=C+l.shift(),Math.abs(L-m)>Math.abs(I-y)?m=L+l.shift():y=I+l.shift(),i.curveTo(o,r,s,a,k,R),i.curveTo(D,C,L,I,m,y);break;default:console.log("Glyph "+t.index+": unknown operator 1200"+B),l.length=0}break;case 14:l.length>0&&!d&&(v=l.shift()+h,d=!0),g&&(i.closePath(),g=!1);break;case 18:S();break;case 19:case 20:S(),M+=f+7>>3;break;case 21:l.length>2&&!d&&(v=l.shift()+h,d=!0),y+=l.pop(),m+=l.pop(),b(m,y);break;case 22:l.length>1&&!d&&(v=l.shift()+h,d=!0),m+=l.pop(),b(m,y);break;case 23:S();break;case 24:for(;l.length>2;)o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+l.shift(),i.curveTo(o,r,s,a,m,y);m+=l.shift(),y+=l.shift(),i.lineTo(m,y);break;case 25:for(;l.length>6;)m+=l.shift(),y+=l.shift(),i.lineTo(m,y);o=m+l.shift(),r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+l.shift(),i.curveTo(o,r,s,a,m,y);break;case 26:for(l.length%2&&(m+=l.shift());l.length>0;)o=m,r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s,y=a+l.shift(),i.curveTo(o,r,s,a,m,y);break;case 27:for(l.length%2&&(y+=l.shift());l.length>0;)o=m+l.shift(),r=y,s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a,i.curveTo(o,r,s,a,m,y);break;case 28:x=p[M],U=p[M+1],l.push((x<<24|U<<16)>>16),M+=2;break;case 29:O=l.pop()+e.gsubrsBias,w=e.gsubrs[O],w&&n(w);break;case 30:for(;l.length>0&&(o=m,r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+(1===l.length?l.shift():0),i.curveTo(o,r,s,a,m,y),0!==l.length);)o=m+l.shift(),r=y,s=o+l.shift(),a=r+l.shift(),y=a+l.shift(),m=s+(1===l.length?l.shift():0),i.curveTo(o,r,s,a,m,y);break;case 31:for(;l.length>0&&(o=m+l.shift(),r=y,s=o+l.shift(),a=r+l.shift(),y=a+l.shift(),m=s+(1===l.length?l.shift():0),i.curveTo(o,r,s,a,m,y),0!==l.length);)o=m,r=y+l.shift(),s=o+l.shift(),a=r+l.shift(),m=s+l.shift(),y=a+(1===l.length?l.shift():0),i.curveTo(o,r,s,a,m,y);break;default:B<32?console.log("Glyph "+t.index+": unknown operator "+B):B<247?l.push(B-139):B<251?(x=p[M],M+=1,l.push(256*(B-247)+x+108)):B<255?(x=p[M],M+=1,l.push(256*-(B-251)-x-108)):(x=p[M],U=p[M+1],T=p[M+2],E=p[M+3],M+=4,l.push((x<<24|U<<16|T<<8|E)/65536))}}}(n),t.advanceWidth=v,i}function Ve(e,t){let n,o=de.indexOf(e);return o>=0&&(n=o),o=t.indexOf(e),o>=0?n=o+de.length:(n=de.length+t.length,t.push(e)),n}function je(e,t,n){const o={};for(let r=0;r=o)throw new Error("CFF table CID Font FDSelect has bad FD index value "+s+" (FD count "+o+")");r.push(s)}else{if(3!==i)throw new Error("CFF Table CID Font FDSelect table has unsupported format "+i);{const e=a.parseCard16();let t,i=a.parseCard16();if(0!==i)throw new Error("CFF Table CID Font FDSelect format 3 range has bad initial GID "+i);for(let l=0;l=o)throw new Error("CFF table CID Font FDSelect has bad FD index value "+s+" (FD count "+o+")");if(t>n)throw new Error("CFF Table CID Font FDSelect format 3 range has bad GID "+t);for(;i=1&&(n.ulCodePageRange1=o.parseULong(),n.ulCodePageRange2=o.parseULong()),n.version>=2&&(n.sxHeight=o.parseShort(),n.sCapHeight=o.parseShort(),n.usDefaultChar=o.parseUShort(),n.usBreakChar=o.parseUShort(),n.usMaxContent=o.parseUShort()),n},make:function(e){return new oe.Table("OS/2",[{name:"version",type:"USHORT",value:3},{name:"xAvgCharWidth",type:"SHORT",value:0},{name:"usWeightClass",type:"USHORT",value:0},{name:"usWidthClass",type:"USHORT",value:0},{name:"fsType",type:"USHORT",value:0},{name:"ySubscriptXSize",type:"SHORT",value:650},{name:"ySubscriptYSize",type:"SHORT",value:699},{name:"ySubscriptXOffset",type:"SHORT",value:0},{name:"ySubscriptYOffset",type:"SHORT",value:140},{name:"ySuperscriptXSize",type:"SHORT",value:650},{name:"ySuperscriptYSize",type:"SHORT",value:699},{name:"ySuperscriptXOffset",type:"SHORT",value:0},{name:"ySuperscriptYOffset",type:"SHORT",value:479},{name:"yStrikeoutSize",type:"SHORT",value:49},{name:"yStrikeoutPosition",type:"SHORT",value:258},{name:"sFamilyClass",type:"SHORT",value:0},{name:"bFamilyType",type:"BYTE",value:0},{name:"bSerifStyle",type:"BYTE",value:0},{name:"bWeight",type:"BYTE",value:0},{name:"bProportion",type:"BYTE",value:0},{name:"bContrast",type:"BYTE",value:0},{name:"bStrokeVariation",type:"BYTE",value:0},{name:"bArmStyle",type:"BYTE",value:0},{name:"bLetterform",type:"BYTE",value:0},{name:"bMidline",type:"BYTE",value:0},{name:"bXHeight",type:"BYTE",value:0},{name:"ulUnicodeRange1",type:"ULONG",value:0},{name:"ulUnicodeRange2",type:"ULONG",value:0},{name:"ulUnicodeRange3",type:"ULONG",value:0},{name:"ulUnicodeRange4",type:"ULONG",value:0},{name:"achVendID",type:"CHARARRAY",value:"XXXX"},{name:"fsSelection",type:"USHORT",value:0},{name:"usFirstCharIndex",type:"USHORT",value:0},{name:"usLastCharIndex",type:"USHORT",value:0},{name:"sTypoAscender",type:"SHORT",value:0},{name:"sTypoDescender",type:"SHORT",value:0},{name:"sTypoLineGap",type:"SHORT",value:0},{name:"usWinAscent",type:"USHORT",value:0},{name:"usWinDescent",type:"USHORT",value:0},{name:"ulCodePageRange1",type:"ULONG",value:0},{name:"ulCodePageRange2",type:"ULONG",value:0},{name:"sxHeight",type:"SHORT",value:0},{name:"sCapHeight",type:"SHORT",value:0},{name:"usDefaultChar",type:"USHORT",value:0},{name:"usBreakChar",type:"USHORT",value:0},{name:"usMaxContext",type:"USHORT",value:0}],e)},unicodeRanges:gt,getUnicodeRange:function(e){for(let t=0;t=n.begin&&e=ye.length){const e=o.parseChar();n.names.push(o.parseString(e))}break;case 2.5:n.numberOfGlyphs=o.parseUShort(),n.offset=new Array(n.numberOfGlyphs);for(let e=0;et.value.tag?1:-1})),t.fields=t.fields.concat(o),t.fields=t.fields.concat(r),t}function kt(e,t,n){for(let n=0;n0){return e.glyphs.get(o).getMetrics()}}return n}function Rt(e){let t=0;for(let n=0;nm||void 0===l)&&m>0&&(l=m),c 123 are reserved for internal usage");f|=1<0?tt.make(w):void 0,D=yt.make(),C=$e.make(e.glyphs,{version:e.getEnglishName("version"),fullName:T,familyName:x,weightName:U,postScriptName:E,unitsPerEm:e.unitsPerEm,fontBBox:[0,d.yMin,d.ascender,d.advanceWidthMax]}),L=e.metas&&Object.keys(e.metas).length>0?Ut.make(e.metas):void 0,I=[g,m,y,v,k,S,D,C,b];R&&I.push(R),e.tables.gsub&&I.push(xt.make(e.tables.gsub)),L&&I.push(L);const M=wt(I),B=Et(M.encode()),G=M.fields;let N=!1;for(let e=0;e>>1,s=e[r].tag;if(s===t)return r;s>>1,s=e[r];if(s===t)return r;s=0)return o[r].script;if(t){const t={tag:e,script:{defaultLangSys:{reserved:0,reqFeatureIndex:65535,featureIndexes:[]},langSysRecords:[]}};return o.splice(-1-r,0,t),t.script}}},getLangSysTable:function(e,t,n){const o=this.getScriptTable(e,n);if(o){if(!t||"dflt"===t||"DFLT"===t)return o.defaultLangSys;const e=Ct(o.langSysRecords,t);if(e>=0)return o.langSysRecords[e].langSys;if(n){const n={tag:t,langSys:{reserved:0,reqFeatureIndex:65535,featureIndexes:[]}};return o.langSysRecords.splice(-1-e,0,n),n.langSys}}},getFeatureTable:function(e,t,n,o){const r=this.getLangSysTable(e,t,o);if(r){let e;const t=r.featureIndexes,s=this.font.tables[this.tableName].features;for(let o=0;o=s[o-1].tag,"Features must be added in alphabetical order."),e={tag:n,feature:{params:0,lookupListIndexes:[]}},s.push(e),t.push(o),e.feature}}},getLookupTables:function(e,t,n,o,r){const s=this.getFeatureTable(e,t,n,r),a=[];if(s){let e;const t=s.lookupListIndexes,n=this.font.tables[this.tableName].lookups;for(let r=0;r=0){const e=s.ligatureSets[c];for(let t=0;t0&&e<0?n:o<0&&e>0?-n:e*o},Zt={x:1,y:0,axis:"x",distance:function(e,t,n,o){return(n?e.xo:e.x)-(o?t.xo:t.x)},interpolate:function(e,t,n,o){let r,s,a,i,l,c,u;if(!o||o===this)return r=e.xo-t.xo,s=e.xo-n.xo,l=t.x-t.xo,c=n.x-n.xo,a=Math.abs(r),i=Math.abs(s),u=a+i,0===u?void(e.x=e.xo+(l+c)/2):void(e.x=e.xo+(l*i+c*a)/u);r=o.distance(e,t,!0,!0),s=o.distance(e,n,!0,!0),l=o.distance(t,t,!1,!0),c=o.distance(n,n,!1,!0),a=Math.abs(r),i=Math.abs(s),u=a+i,0!==u?Zt.setRelative(e,e,(l*i+c*a)/u,o,!0):Zt.setRelative(e,e,(l+c)/2,o,!0)},normalSlope:Number.NEGATIVE_INFINITY,setRelative:function(e,t,n,o,r){if(!o||o===this)return void(e.x=(r?t.xo:t.x)+n);const s=r?t.xo:t.x,a=r?t.yo:t.y,i=s+n*o.x,l=a+n*o.y;e.x=i+(e.y-l)/o.normalSlope},slope:0,touch:function(e){e.xTouched=!0},touched:function(e){return e.xTouched},untouch:function(e){e.xTouched=!1}},Qt={x:0,y:1,axis:"y",distance:function(e,t,n,o){return(n?e.yo:e.y)-(o?t.yo:t.y)},interpolate:function(e,t,n,o){let r,s,a,i,l,c,u;if(!o||o===this)return r=e.yo-t.yo,s=e.yo-n.yo,l=t.y-t.yo,c=n.y-n.yo,a=Math.abs(r),i=Math.abs(s),u=a+i,0===u?void(e.y=e.yo+(l+c)/2):void(e.y=e.yo+(l*i+c*a)/u);r=o.distance(e,t,!0,!0),s=o.distance(e,n,!0,!0),l=o.distance(t,t,!1,!0),c=o.distance(n,n,!1,!0),a=Math.abs(r),i=Math.abs(s),u=a+i,0!==u?Qt.setRelative(e,e,(l*i+c*a)/u,o,!0):Qt.setRelative(e,e,(l+c)/2,o,!0)},normalSlope:0,setRelative:function(e,t,n,o,r){if(!o||o===this)return void(e.y=(r?t.yo:t.y)+n);const s=r?t.xo:t.x,a=r?t.yo:t.y,i=s+n*o.x,l=a+n*o.y;e.y=l+o.normalSlope*(e.x-i)},slope:Number.POSITIVE_INFINITY,touch:function(e){e.yTouched=!0},touched:function(e){return e.yTouched},untouch:function(e){e.yTouched=!1}};function $t(e,t){this.x=e,this.y=t,this.axis=void 0,this.slope=t/e,this.normalSlope=-e/t,Object.freeze(this)}function Kt(e,t){const n=Math.sqrt(e*e+t*t);return t/=n,1===(e/=n)&&0===t?Zt:0===e&&1===t?Qt:new $t(e,t)}function Jt(e,t,n,o){this.x=this.xo=Math.round(64*e)/64,this.y=this.yo=Math.round(64*t)/64,this.lastPointOfContour=n,this.onCurve=o,this.prevPointOnContour=void 0,this.nextPointOnContour=void 0,this.xTouched=!1,this.yTouched=!1,Object.preventExtensions(this)}Object.freeze(Zt),Object.freeze(Qt),$t.prototype.distance=function(e,t,n,o){return this.x*Zt.distance(e,t,n,o)+this.y*Qt.distance(e,t,n,o)},$t.prototype.interpolate=function(e,t,n,o){let r,s,a,i,l,c,u;a=o.distance(e,t,!0,!0),i=o.distance(e,n,!0,!0),r=o.distance(t,t,!1,!0),s=o.distance(n,n,!1,!0),l=Math.abs(a),c=Math.abs(i),u=l+c,0!==u?this.setRelative(e,e,(r*c+s*l)/u,o,!0):this.setRelative(e,e,(r+s)/2,o,!0)},$t.prototype.setRelative=function(e,t,n,o,r){o=o||this;const s=r?t.xo:t.x,a=r?t.yo:t.y,i=s+n*o.x,l=a+n*o.y,c=o.normalSlope,u=this.slope,p=e.x,h=e.y;e.x=(u*p-c*i+l-h)/(u-c),e.y=u*(e.x-p)+h},$t.prototype.touch=function(e){e.xTouched=!0,e.yTouched=!0},Jt.prototype.nextTouched=function(e){let t=this.nextPointOnContour;for(;!e.touched(t)&&t!==this;)t=t.nextPointOnContour;return t},Jt.prototype.prevTouched=function(e){let t=this.prevPointOnContour;for(;!e.touched(t)&&t!==this;)t=t.prevPointOnContour;return t};const en=Object.freeze(new Jt(0,0)),tn={cvCutIn:17/16,deltaBase:9,deltaShift:.125,loop:1,minDis:1,autoFlip:!0};function nn(e,t){switch(this.env=e,this.stack=[],this.prog=t,e){case"glyf":this.zp0=this.zp1=this.zp2=1,this.rp0=this.rp1=this.rp2=0;case"prep":this.fv=this.pv=this.dpv=Zt,this.round=Wt}}function on(e){const t=e.tZone=new Array(e.gZone.length);for(let e=0;e=176&&o<=183)r+=o-176+1;else if(o>=184&&o<=191)r+=2*(o-184+1);else if(t&&1===s&&27===o)break}while(s>0);e.ip=r}function sn(e,t){exports.DEBUG&&console.log(t.step,"SVTCA["+e.axis+"]"),t.fv=t.pv=t.dpv=e}function an(e,t){exports.DEBUG&&console.log(t.step,"SPVTCA["+e.axis+"]"),t.pv=t.dpv=e}function ln(e,t){exports.DEBUG&&console.log(t.step,"SFVTCA["+e.axis+"]"),t.fv=e}function cn(e,t){const n=t.stack,o=n.pop(),r=n.pop(),s=t.z2[o],a=t.z1[r];let i,l;exports.DEBUG&&console.log("SPVTL["+e+"]",o,r),e?(i=s.y-a.y,l=a.x-s.x):(i=a.x-s.x,l=a.y-s.y),t.pv=t.dpv=Kt(i,l)}function un(e,t){const n=t.stack,o=n.pop(),r=n.pop(),s=t.z2[o],a=t.z1[r];let i,l;exports.DEBUG&&console.log("SFVTL["+e+"]",o,r),e?(i=s.y-a.y,l=a.x-s.x):(i=a.x-s.x,l=a.y-s.y),t.fv=Kt(i,l)}function pn(e){exports.DEBUG&&console.log(e.step,"POP[]"),e.stack.pop()}function hn(e,t){const n=t.stack.pop(),o=t.z0[n],r=t.fv,s=t.pv;exports.DEBUG&&console.log(t.step,"MDAP["+e+"]",n);let a=s.distance(o,en);e&&(a=t.round(a)),r.setRelative(o,en,a,s),r.touch(o),t.rp0=t.rp1=n}function fn(e,t){const n=t.z2,o=n.length-2;let r,s,a;exports.DEBUG&&console.log(t.step,"IUP["+e.axis+"]");for(let t=0;t1?"loop "+(t.loop-i)+": ":"")+"SHP["+(e?"rp1":"rp2")+"]",o)}t.loop=1}function gn(e,t){const n=t.stack,o=e?t.rp1:t.rp2,r=(e?t.z0:t.z1)[o],s=t.fv,a=t.pv,i=n.pop(),l=t.z2[t.contours[i]];let c=l;exports.DEBUG&&console.log(t.step,"SHC["+e+"]",i);const u=a.distance(r,r,!1,!0);do{c!==r&&s.setRelative(c,c,u,a),c=c.nextPointOnContour}while(c!==l)}function mn(e,t){const n=t.stack,o=e?t.rp1:t.rp2,r=(e?t.z0:t.z1)[o],s=t.fv,a=t.pv,i=n.pop();let l,c;switch(exports.DEBUG&&console.log(t.step,"SHZ["+e+"]",i),i){case 0:l=t.tZone;break;case 1:l=t.gZone;break;default:throw new Error("Invalid zone")}const u=a.distance(r,r,!1,!0),p=l.length-2;for(let e=0;e",i),t.stack.push(Math.round(64*i))}function xn(e,t){const n=t.stack,o=n.pop(),r=t.fv,s=t.pv,a=t.ppem,i=t.deltaBase+16*(e-1),l=t.deltaShift,c=t.z0;exports.DEBUG&&console.log(t.step,"DELTAP["+e+"]",o,n);for(let e=0;e>4)!==a)continue;let u=(15&o)-8;u>=0&&u++,exports.DEBUG&&console.log(t.step,"DELTAPFIX",e,"by",u*l);const p=c[e];r.setRelative(p,p,u*l,s)}}function Un(e,t){const n=t.stack,o=n.pop();exports.DEBUG&&console.log(t.step,"ROUND[]"),n.push(64*t.round(o/64))}function Tn(e,t){const n=t.stack,o=n.pop(),r=t.ppem,s=t.deltaBase+16*(e-1),a=t.deltaShift;exports.DEBUG&&console.log(t.step,"DELTAC["+e+"]",o,n);for(let e=0;e>4)!==r)continue;let i=(15&o)-8;i>=0&&i++;const l=i*a;exports.DEBUG&&console.log(t.step,"DELTACFIX",e,"by",l),t.cvt[e]+=l}}function En(e,t){const n=t.stack,o=n.pop(),r=n.pop(),s=t.z2[o],a=t.z1[r];let i,l;exports.DEBUG&&console.log("SDPVTL["+e+"]",o,r),e?(i=s.y-a.y,l=a.x-s.x):(i=a.x-s.x,l=a.y-s.y),t.dpv=Kt(i,l)}function On(e,t){const n=t.stack,o=t.prog;let r=t.ip;exports.DEBUG&&console.log(t.step,"PUSHB["+e+"]");for(let t=0;t=0?1:-1,m=Math.abs(m),e&&(v=s.cvt[i],o&&Math.abs(m-v)":"_")+(o?"R":"_")+(0===r?"Gr":1===r?"Bl":2===r?"Wh":"")+"]",e?i+"("+s.cvt[i]+","+v+")":"",l,"(d =",g,"->",y*m,")"),s.rp1=s.rp0,s.rp2=l,t&&(s.rp0=l)}function Rn(e){(e=e||{}).empty||(Nt(e.familyName,"When creating a new Font object, familyName is required."),Nt(e.styleName,"When creating a new Font object, styleName is required."),Nt(e.unitsPerEm,"When creating a new Font object, unitsPerEm is required."),Nt(e.ascender,"When creating a new Font object, ascender is required."),Nt(e.descender,"When creating a new Font object, descender is required."),Nt(e.descender<0,"Descender should be negative (e.g. -512)."),this.names={fontFamily:{en:e.familyName||" "},fontSubfamily:{en:e.styleName||" "},fullName:{en:e.fullName||e.familyName+" "+e.styleName},postScriptName:{en:e.postScriptName||e.familyName+e.styleName},designer:{en:e.designer||" "},designerURL:{en:e.designerURL||" "},manufacturer:{en:e.manufacturer||" "},manufacturerURL:{en:e.manufacturerURL||" "},license:{en:e.license||" "},licenseURL:{en:e.licenseURL||" "},version:{en:e.version||"Version 0.1"},description:{en:e.description||" "},copyright:{en:e.copyright||" "},trademark:{en:e.trademark||" "}},this.unitsPerEm=e.unitsPerEm||1e3,this.ascender=e.ascender,this.descender=e.descender,this.createdTimestamp=e.createdTimestamp,this.tables={os2:{usWeightClass:e.weightClass||this.usWeightClasses.MEDIUM,usWidthClass:e.widthClass||this.usWidthClasses.MEDIUM,fsSelection:e.fsSelection||this.fsSelectionValues.REGULAR}}),this.supported=!0,this.glyphs=new Ie.GlyphSet(this,e.glyphs||[]),this.encoding=new ve(this),this.substitution=new It(this),this.tables=this.tables||{},Object.defineProperty(this,"hinting",{get:function(){return this._hinting?this._hinting:"truetype"===this.outlinesFormat?this._hinting=new zt(this):void 0}})}function Dn(e,t){const n=JSON.stringify(e);let o=256;for(let e in t){let r=parseInt(e);if(r&&!(r<256)){if(JSON.stringify(t[e])===n)return r;o<=r&&(o=r+1)}}return t[o]=e,o}function Cn(e,t,n){const o=Dn(t.name,n);return[{name:"tag_"+e,type:"TAG",value:t.tag},{name:"minValue_"+e,type:"FIXED",value:t.minValue<<16},{name:"defaultValue_"+e,type:"FIXED",value:t.defaultValue<<16},{name:"maxValue_"+e,type:"FIXED",value:t.maxValue<<16},{name:"flags_"+e,type:"USHORT",value:0},{name:"nameID_"+e,type:"USHORT",value:o}]}function Ln(e,t,n){const o={},r=new pe.Parser(e,t);return o.tag=r.parseTag(),o.minValue=r.parseFixed(),o.defaultValue=r.parseFixed(),o.maxValue=r.parseFixed(),r.skip("uShort",1),o.name=n[r.parseUShort()]||{},o}function In(e,t,n,o){const r=[{name:"nameID_"+e,type:"USHORT",value:Dn(t.name,o)},{name:"flags_"+e,type:"USHORT",value:0}];for(let o=0;o2)return;const n=this.font;let o=this._prepState;if(!o||o.ppem!==t){let e=this._fpgmState;if(!e){nn.prototype=tn,e=this._fpgmState=new nn("fpgm",n.tables.fpgm),e.funcs=[],e.font=n,exports.DEBUG&&(console.log("---EXEC FPGM---"),e.step=-1);try{At(e)}catch(e){return console.log("Hinting error in FPGM:"+e),void(this._errorState=3)}}nn.prototype=e,o=this._prepState=new nn("prep",n.tables.prep),o.ppem=t;const r=n.tables.cvt;if(r){const e=o.cvt=new Array(r.length),s=t/n.unitsPerEm;for(let t=0;t1))try{return Ft(e,o)}catch(e){return this._errorState<1&&(console.log("Hinting error:"+e),console.log("Note: further hinting errors are silenced")),void(this._errorState=1)}},Ft=function(e,t){const n=t.ppem/t.font.unitsPerEm,o=n;let r,s,a,i=e.components;if(nn.prototype=t,i){const l=t.font;s=[],r=[];for(let e=0;e1?"loop "+(e.loop-n)+": ":"")+"SHPIX[]",a,r),o.setRelative(i,i,r),o.touch(i)}e.loop=1},function(e){const t=e.stack,n=e.rp1,o=e.rp2;let r=e.loop;const s=e.z0[n],a=e.z1[o],i=e.fv,l=e.dpv,c=e.z2;for(;r--;){const u=t.pop(),p=c[u];exports.DEBUG&&console.log(e.step,(e.loop>1?"loop "+(e.loop-r)+": ":"")+"IP[]",u,n,"<->",o),i.interpolate(p,s,a,l),i.touch(p)}e.loop=1},yn.bind(void 0,0),yn.bind(void 0,1),function(e){const t=e.stack,n=e.rp0,o=e.z0[n];let r=e.loop;const s=e.fv,a=e.pv,i=e.z1;for(;r--;){const n=t.pop(),l=i[n];exports.DEBUG&&console.log(e.step,(e.loop>1?"loop "+(e.loop-r)+": ":"")+"ALIGNRP[]",n),s.setRelative(l,o,0,a),s.touch(l)}e.loop=1},function(e){exports.DEBUG&&console.log(e.step,"RTDG[]"),e.round=qt},vn.bind(void 0,0),vn.bind(void 0,1),function(e){const t=e.prog;let n=e.ip;const o=e.stack,r=t[++n];exports.DEBUG&&console.log(e.step,"NPUSHB[]",r);for(let e=0;en?1:0)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"GTEQ[]",n,o),t.push(o>=n?1:0)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"EQ[]",n,o),t.push(n===o?1:0)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"NEQ[]",n,o),t.push(n!==o?1:0)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"ODD[]",n),t.push(Math.trunc(n)%2?1:0)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"EVEN[]",n),t.push(Math.trunc(n)%2?0:1)},function(e){let t=e.stack.pop();exports.DEBUG&&console.log(e.step,"IF[]",t),t||(rn(e,!0),exports.DEBUG&&console.log(e.step,"EIF[]"))},function(e){exports.DEBUG&&console.log(e.step,"EIF[]")},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"AND[]",n,o),t.push(n&&o?1:0)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"OR[]",n,o),t.push(n||o?1:0)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"NOT[]",n),t.push(n?0:1)},xn.bind(void 0,1),function(e){const t=e.stack.pop();exports.DEBUG&&console.log(e.step,"SDB[]",t),e.deltaBase=t},function(e){const t=e.stack.pop();exports.DEBUG&&console.log(e.step,"SDS[]",t),e.deltaShift=Math.pow(.5,t)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"ADD[]",n,o),t.push(o+n)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"SUB[]",n,o),t.push(o-n)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"DIV[]",n,o),t.push(64*o/n)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"MUL[]",n,o),t.push(o*n/64)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"ABS[]",n),t.push(Math.abs(n))},function(e){const t=e.stack;let n=t.pop();exports.DEBUG&&console.log(e.step,"NEG[]",n),t.push(-n)},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"FLOOR[]",n),t.push(64*Math.floor(n/64))},function(e){const t=e.stack,n=t.pop();exports.DEBUG&&console.log(e.step,"CEILING[]",n),t.push(64*Math.ceil(n/64))},Un.bind(void 0,0),Un.bind(void 0,1),Un.bind(void 0,2),Un.bind(void 0,3),void 0,void 0,void 0,void 0,function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"WCVTF[]",n,o),e.cvt[o]=n*e.ppem/e.font.unitsPerEm},xn.bind(void 0,2),xn.bind(void 0,3),Tn.bind(void 0,1),Tn.bind(void 0,2),Tn.bind(void 0,3),function(e){let t,n=e.stack.pop();switch(exports.DEBUG&&console.log(e.step,"SROUND[]",n),e.round=Yt,192&n){case 0:t=.5;break;case 64:t=1;break;case 128:t=2;break;default:throw new Error("invalid SROUND value")}switch(e.srPeriod=t,48&n){case 0:e.srPhase=0;break;case 16:e.srPhase=.25*t;break;case 32:e.srPhase=.5*t;break;case 48:e.srPhase=.75*t;break;default:throw new Error("invalid SROUND value")}n&=15,e.srThreshold=0===n?0:(n/8-.5)*t},function(e){let t,n=e.stack.pop();switch(exports.DEBUG&&console.log(e.step,"S45ROUND[]",n),e.round=Yt,192&n){case 0:t=Math.sqrt(2)/2;break;case 64:t=Math.sqrt(2);break;case 128:t=2*Math.sqrt(2);break;default:throw new Error("invalid S45ROUND value")}switch(e.srPeriod=t,48&n){case 0:e.srPhase=0;break;case 16:e.srPhase=.25*t;break;case 32:e.srPhase=.5*t;break;case 48:e.srPhase=.75*t;break;default:throw new Error("invalid S45ROUND value")}n&=15,e.srThreshold=0===n?0:(n/8-.5)*t},void 0,void 0,function(e){exports.DEBUG&&console.log(e.step,"ROFF[]"),e.round=_t},void 0,function(e){exports.DEBUG&&console.log(e.step,"RUTG[]"),e.round=Vt},function(e){exports.DEBUG&&console.log(e.step,"RDTG[]"),e.round=jt},pn,pn,void 0,void 0,void 0,void 0,void 0,function(e){const t=e.stack.pop();exports.DEBUG&&console.log(e.step,"SCANCTRL[]",t)},En.bind(void 0,0),En.bind(void 0,1),function(e){const t=e.stack,n=t.pop();let o=0;exports.DEBUG&&console.log(e.step,"GETINFO[]",n),1&n&&(o=35),32&n&&(o|=4096),t.push(o)},void 0,function(e){const t=e.stack,n=t.pop(),o=t.pop(),r=t.pop();exports.DEBUG&&console.log(e.step,"ROLL[]"),t.push(o),t.push(n),t.push(r)},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"MAX[]",n,o),t.push(Math.max(o,n))},function(e){const t=e.stack,n=t.pop(),o=t.pop();exports.DEBUG&&console.log(e.step,"MIN[]",n,o),t.push(Math.min(o,n))},function(e){const t=e.stack.pop();exports.DEBUG&&console.log(e.step,"SCANTYPE[]",t)},function(e){const t=e.stack.pop();let n=e.stack.pop();switch(exports.DEBUG&&console.log(e.step,"INSTCTRL[]",t,n),t){case 1:return void(e.inhibitGridFit=!!n);case 2:return void(e.ignoreCvt=!!n);default:throw new Error("invalid INSTCTRL[] selector")}},void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,On.bind(void 0,1),On.bind(void 0,2),On.bind(void 0,3),On.bind(void 0,4),On.bind(void 0,5),On.bind(void 0,6),On.bind(void 0,7),On.bind(void 0,8),wn.bind(void 0,1),wn.bind(void 0,2),wn.bind(void 0,3),wn.bind(void 0,4),wn.bind(void 0,5),wn.bind(void 0,6),wn.bind(void 0,7),wn.bind(void 0,8),kn.bind(void 0,0,0,0,0,0),kn.bind(void 0,0,0,0,0,1),kn.bind(void 0,0,0,0,0,2),kn.bind(void 0,0,0,0,0,3),kn.bind(void 0,0,0,0,1,0),kn.bind(void 0,0,0,0,1,1),kn.bind(void 0,0,0,0,1,2),kn.bind(void 0,0,0,0,1,3),kn.bind(void 0,0,0,1,0,0),kn.bind(void 0,0,0,1,0,1),kn.bind(void 0,0,0,1,0,2),kn.bind(void 0,0,0,1,0,3),kn.bind(void 0,0,0,1,1,0),kn.bind(void 0,0,0,1,1,1),kn.bind(void 0,0,0,1,1,2),kn.bind(void 0,0,0,1,1,3),kn.bind(void 0,0,1,0,0,0),kn.bind(void 0,0,1,0,0,1),kn.bind(void 0,0,1,0,0,2),kn.bind(void 0,0,1,0,0,3),kn.bind(void 0,0,1,0,1,0),kn.bind(void 0,0,1,0,1,1),kn.bind(void 0,0,1,0,1,2),kn.bind(void 0,0,1,0,1,3),kn.bind(void 0,0,1,1,0,0),kn.bind(void 0,0,1,1,0,1),kn.bind(void 0,0,1,1,0,2),kn.bind(void 0,0,1,1,0,3),kn.bind(void 0,0,1,1,1,0),kn.bind(void 0,0,1,1,1,1),kn.bind(void 0,0,1,1,1,2),kn.bind(void 0,0,1,1,1,3),kn.bind(void 0,1,0,0,0,0),kn.bind(void 0,1,0,0,0,1),kn.bind(void 0,1,0,0,0,2),kn.bind(void 0,1,0,0,0,3),kn.bind(void 0,1,0,0,1,0),kn.bind(void 0,1,0,0,1,1),kn.bind(void 0,1,0,0,1,2),kn.bind(void 0,1,0,0,1,3),kn.bind(void 0,1,0,1,0,0),kn.bind(void 0,1,0,1,0,1),kn.bind(void 0,1,0,1,0,2),kn.bind(void 0,1,0,1,0,3),kn.bind(void 0,1,0,1,1,0),kn.bind(void 0,1,0,1,1,1),kn.bind(void 0,1,0,1,1,2),kn.bind(void 0,1,0,1,1,3),kn.bind(void 0,1,1,0,0,0),kn.bind(void 0,1,1,0,0,1),kn.bind(void 0,1,1,0,0,2),kn.bind(void 0,1,1,0,0,3),kn.bind(void 0,1,1,0,1,0),kn.bind(void 0,1,1,0,1,1),kn.bind(void 0,1,1,0,1,2),kn.bind(void 0,1,1,0,1,3),kn.bind(void 0,1,1,1,0,0),kn.bind(void 0,1,1,1,0,1),kn.bind(void 0,1,1,1,0,2),kn.bind(void 0,1,1,1,0,3),kn.bind(void 0,1,1,1,1,0),kn.bind(void 0,1,1,1,1,1),kn.bind(void 0,1,1,1,1,2),kn.bind(void 0,1,1,1,1,3)],Rn.prototype.hasChar=function(e){return null!==this.encoding.charToGlyphIndex(e)},Rn.prototype.charToGlyphIndex=function(e){return this.encoding.charToGlyphIndex(e)},Rn.prototype.charToGlyph=function(e){const t=this.charToGlyphIndex(e);let n=this.glyphs.get(t);return n||(n=this.glyphs.get(0)),n},Rn.prototype.stringToGlyphs=function(e,t){t=t||this.defaultRenderOptions;const n=[];for(let t=0;t>1;e1&&console.warn("Only the first kern subtable is supported."),e.skip("uLong");const n=255&e.parseUShort();if(e.skip("uShort"),0===n){const n=e.parseUShort();e.skip("uShort",3);for(let o=0;o{const t=jn.loadSync(e);Qn.font=t,Qn.ascender=t.ascender,Qn.descender=t.descender}};const Kn=$n.options,Jn=function(e,t){return Math.round(e+Math.random()*(t-e))};const eo=function(e,t){return{text:(e+t).toString(),equation:e+"+"+t}},to=function(e,t){return{text:(e-t).toString(),equation:e+"-"+t}};function no(e,t,n){return 6*(n=(n+1)%1)<1?e+(t-e)*n*6:2*n<1?t:3*n<2?e+(t-e)*(2/3-n)*6:e}var oo={int:Jn,greyColor:function(e,t){const n=Jn(e=e||1,t=t||9).toString(16);return`#${n}${n}${n}`},captchaText:function(e){"number"==typeof e&&(e={size:e});const t=(e=e||{}).size||4,n=e.ignoreChars||"";let o=-1,r="",s=e.charPreset||Kn.charPreset;n&&(s=function(e,t){return e.split("").filter(e=>-1===t.indexOf(e))}(s,n));const a=s.length-1;for(;++o>16,o=t>>8&255,r=255&t,s=Math.max(n,o,r),a=Math.min(n,o,r);return(s+a)/510}(e):1;let r,s;o>=.5?(r=Math.round(100*o)-45,s=Math.round(100*o)-25):(r=Math.round(100*o)+25,s=Math.round(100*o)+45);const a=Jn(r,s)/100,i=a<.5?a*(a+n):a+n-a*n,l=2*a-i,c=Math.floor(255*no(l,i,t+1/3)),u=Math.floor(255*no(l,i,t));return"#"+(Math.floor(255*no(l,i,t-1/3))|u<<8|c<<16|1<<24).toString(16).slice(1)}};const ro=$n.options,so=function(e,t){e=e||oo.captchaText();const n=(t=Object.assign({},ro,t)).width,o=t.height,r=t.background||t.backgroundColor;r&&(t.color=!0);const s=r?``:"",a=[].concat(function(e,t,n){const o=n.color,r=[],s=n.inverse?7:1,a=n.inverse?15:9;let i=-1;for(;++i`)}return r}(n,o,t)).concat(function(e,t,n,o,r){const s=e.length,a=(t-2)/(s+1),i=o.inverse?10:0,l=o.inverse?14:4;let u=-1;const p=[],h=r||o.color?oo.color(o.background):oo.greyColor(i,l);for(;++u`)}return p}(e,n,o,t)).sort(()=>Math.random()-.5).join("");return`${``}${s}${a}`};var ao=so,io=oo.captchaText,lo=function(e){const t=e.text||oo.captchaText(e);return{text:t,data:so(t,e)}},co=function(e){const t=oo.mathExpr(e.mathMin,e.mathMax,e.mathOperator);return{text:t.text,data:so(t.equation,e)}},uo=ro,po=$n.loadFont;ao.randomText=io,ao.create=lo,ao.createMathExpr=co,ao.options=uo,ao.loadFont=po;var ho=ao;const fo=Object.prototype.toString;function go(e){return"[object Object]"===fo.call(e)}function mo(){"development"===process.env.NODE_ENV&&console.log(...arguments)}const yo=async function(){};function vo(e){return yo.constructor===e.constructor?async function(){const t=await e.apply(this,arguments);return go(t)&&(t.msg&&(t.message=t.msg,t.errMsg=t.msg),0===t.code?t.errCode=t.code:t.errCode=s[t.code]||t.code),t}:function(){const t=e.apply(this,arguments);return go(t)&&(t.msg&&(t.message=t.msg,t.errMsg=t.msg),0===t.code?t.errCode=t.code:t.errCode=s[t.code]||t.code),t}}const bo=uniCloud.database(),So=bo.collection("opendb-verify-codes");class xo{async setVerifyCode({clientIP:e,deviceId:t,code:n,expiresDate:o,scene:r}){if(!t)return{code:10101,msg:"deviceId不可为空"};if(!n)return{code:10102,msg:"验证码不可为空"};o||(o=180);const s=Date.now(),a={device_uuid:t,scene:r,code:n.toLocaleLowerCase(),state:0,ip:e,created_date:s,expired_date:s+1e3*o};return mo("addRes",await So.add(a)),{code:0,deviceId:t}}async verifyCode({deviceId:e,code:t,scene:n}){if(!e)return{code:10101,msg:"deviceId不可为空"};if(!t)return{code:10102,msg:"验证码不可为空"};const o=Date.now(),r={device_uuid:e,scene:n,code:t.toLocaleLowerCase(),state:0},s=await So.where(r).orderBy("created_date","desc").limit(1).get();if(mo("verifyRecord:",s),s&&s.data&&s.data.length>0){const e=s.data[0];if(e.expired_date{e.scene&&delete e.scene,this.pluginConfig.scene[n]=Object.assign({},t,e[n])})}}}{constructor(){super(),this.DEVICEID2opts={}}mergeConfig(e){const t=go(this.pluginConfig.scene)?this.pluginConfig.scene[e.scene]:e.scene;return Object.assign({},go(t)?t:this.pluginConfig,e)}async create(e={}){if(!e.scene)throw new Error("scene验证码场景不可为空");e=this.mergeConfig(e);let{scene:t,expiresDate:n,deviceId:o,clientIP:r,...s}=e;if(o=o||__ctx__.DEVICEID,r=r||__ctx__.CLIENTIP,!o)throw new Error("deviceId不可为空");const a=new xo;try{const{text:i,base64:l}=function(e={}){const{uniPlatform:t=""}=e;let n;n=e.mathExpr?ho.createMathExpr(e):ho.create(e);let o="data:image/svg+xml;utf8,"+n.data.replace(/#/g,"%23");return(!t||["mp-toutiao","h5","web"].indexOf(t)>-1)&&(o=o.replace(/"/g,"'").replace(//g,"%3E")),{text:n.text,base64:o}}(s),c=await a.setVerifyCode({clientIP:r,deviceId:o,code:i,expiresDate:n,scene:t});return c.code>0?{...c,code:10001}:(this.DEVICEID2opts[o]=e,{code:0,msg:"验证码获取成功",captchaBase64:l})}catch(e){return{code:10001,msg:"验证码生成失败:"+e.message}}}async verify({deviceId:e,captcha:t,scene:n}){if(!(e=e||__ctx__.DEVICEID))throw new Error("deviceId不可为空");if(!n)throw new Error("scene验证码场景不可为空");const o=new xo;try{const r=await o.verifyCode({deviceId:e,code:t,scene:n});return r.code>0?r:{code:0,msg:"验证码通过"}}catch(e){return{code:10002,msg:"验证码校验失败:"+e.message}}}async refresh(e={}){let{scene:t,expiresDate:n,deviceId:o,...r}=e;if(o=o||__ctx__.DEVICEID,!o)throw new Error("deviceId不可为空");if(!t)throw new Error("scene验证码场景不可为空");const s=await So.where(bo.command.or([{device_uuid:o,scene:t},{deviceId:o,scene:t}])).orderBy("created_date","desc").limit(1).get();if(s&&s.data&&s.data.length>0){const e=s.data[0];await So.doc(e._id).update({state:2}),Object.keys(r).length>0&&(this.DEVICEID2opts[o]=Object.assign({},this.DEVICEID2opts[o],r));let a={};try{a=await this.create(Object.assign({},this.DEVICEID2opts[o],{deviceId:o,scene:t,expiresDate:n}))}catch(e){return{code:50403,msg:e.message}}return a.code>0?{...a,code:50403}:{code:0,msg:"验证码刷新成功",captchaBase64:a.captchaBase64}}return{code:10003,msg:`验证码刷新失败:无此设备在 ${t} 场景信息,请重新获取`}}}const Eo=new xo;Object.keys(Eo).forEach(e=>{To.prototype[e]=vo(Eo[e])});const Oo=new To,wo=new Proxy(Oo,{get(e,t){if(t in e)return"function"==typeof e[t]?vo(e[t]).bind(wo):e[t]}});module.exports=wo; diff --git a/uni_modules/uni-config-center/changelog.md b/uni_modules/uni-config-center/changelog.md index 4d2eb92f..5af52574 100644 --- a/uni_modules/uni-config-center/changelog.md +++ b/uni_modules/uni-config-center/changelog.md @@ -1,4 +1,6 @@ -## 0.0.2(2021-04-16) -- 修改插件package信息 -## 0.0.1(2021-03-15) -- 初始化项目 +## 0.0.3(2022-11-11) +- 修复 config 方法获取根节点为数组格式配置时错误的转化为了对象的Bug +## 0.0.2(2021-04-16) +- 修改插件package信息 +## 0.0.1(2021-03-15) +- 初始化项目 diff --git a/uni_modules/uni-config-center/package.json b/uni_modules/uni-config-center/package.json index d97bd27a..0798109c 100644 --- a/uni_modules/uni-config-center/package.json +++ b/uni_modules/uni-config-center/package.json @@ -1,7 +1,7 @@ { "id": "uni-config-center", "displayName": "uni-config-center", - "version": "0.0.2", + "version": "0.0.3", "description": "uniCloud 配置中心", "keywords": [ "配置", @@ -11,11 +11,7 @@ "engines": { "HBuilderX": "^3.1.0" }, - "dcloudext": { - "category": [ - "uniCloud", - "云函数模板" - ], +"dcloudext": { "sale": { "regular": { "price": "0.00" @@ -32,7 +28,8 @@ "data": "无", "permissions": "无" }, - "npmurl": "" + "npmurl": "", + "type": "unicloud-template-function" }, "directories": { "example": "../../../scripts/dist" @@ -73,8 +70,12 @@ "快应用": { "华为": "u", "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" } } } } -} +} diff --git a/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js b/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js index e14fb3b0..00ba62fd 100644 --- a/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js +++ b/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js @@ -1 +1 @@ -"use strict";var t=require("fs"),r=require("path");function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t),o=e(r),i="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var u=function(t){var r={exports:{}};return t(r,r.exports),r.exports}((function(t,r){var e="__lodash_hash_undefined__",n=9007199254740991,o="[object Arguments]",u="[object Function]",c="[object Object]",a=/^\[object .+?Constructor\]$/,f=/^(?:0|[1-9]\d*)$/,s={};s["[object Float32Array]"]=s["[object Float64Array]"]=s["[object Int8Array]"]=s["[object Int16Array]"]=s["[object Int32Array]"]=s["[object Uint8Array]"]=s["[object Uint8ClampedArray]"]=s["[object Uint16Array]"]=s["[object Uint32Array]"]=!0,s[o]=s["[object Array]"]=s["[object ArrayBuffer]"]=s["[object Boolean]"]=s["[object DataView]"]=s["[object Date]"]=s["[object Error]"]=s[u]=s["[object Map]"]=s["[object Number]"]=s[c]=s["[object RegExp]"]=s["[object Set]"]=s["[object String]"]=s["[object WeakMap]"]=!1;var l="object"==typeof i&&i&&i.Object===Object&&i,h="object"==typeof self&&self&&self.Object===Object&&self,p=l||h||Function("return this")(),_=r&&!r.nodeType&&r,v=_&&t&&!t.nodeType&&t,d=v&&v.exports===_,y=d&&l.process,g=function(){try{var t=v&&v.require&&v.require("util").types;return t||y&&y.binding&&y.binding("util")}catch(t){}}(),b=g&&g.isTypedArray;function j(t,r,e){switch(e.length){case 0:return t.call(r);case 1:return t.call(r,e[0]);case 2:return t.call(r,e[0],e[1]);case 3:return t.call(r,e[0],e[1],e[2])}return t.apply(r,e)}var w,O,m,A=Array.prototype,z=Function.prototype,M=Object.prototype,x=p["__core-js_shared__"],C=z.toString,F=M.hasOwnProperty,U=(w=/[^.]+$/.exec(x&&x.keys&&x.keys.IE_PROTO||""))?"Symbol(src)_1."+w:"",S=M.toString,I=C.call(Object),P=RegExp("^"+C.call(F).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),T=d?p.Buffer:void 0,q=p.Symbol,E=p.Uint8Array,$=T?T.allocUnsafe:void 0,D=(O=Object.getPrototypeOf,m=Object,function(t){return O(m(t))}),k=Object.create,B=M.propertyIsEnumerable,N=A.splice,L=q?q.toStringTag:void 0,R=function(){try{var t=_t(Object,"defineProperty");return t({},"",{}),t}catch(t){}}(),G=T?T.isBuffer:void 0,V=Math.max,W=Date.now,H=_t(p,"Map"),J=_t(Object,"create"),K=function(){function t(){}return function(r){if(!Mt(r))return{};if(k)return k(r);t.prototype=r;var e=new t;return t.prototype=void 0,e}}();function Q(t){var r=-1,e=null==t?0:t.length;for(this.clear();++r-1},X.prototype.set=function(t,r){var e=this.__data__,n=nt(e,t);return n<0?(++this.size,e.push([t,r])):e[n][1]=r,this},Y.prototype.clear=function(){this.size=0,this.__data__={hash:new Q,map:new(H||X),string:new Q}},Y.prototype.delete=function(t){var r=pt(this,t).delete(t);return this.size-=r?1:0,r},Y.prototype.get=function(t){return pt(this,t).get(t)},Y.prototype.has=function(t){return pt(this,t).has(t)},Y.prototype.set=function(t,r){var e=pt(this,t),n=e.size;return e.set(t,r),this.size+=e.size==n?0:1,this},Z.prototype.clear=function(){this.__data__=new X,this.size=0},Z.prototype.delete=function(t){var r=this.__data__,e=r.delete(t);return this.size=r.size,e},Z.prototype.get=function(t){return this.__data__.get(t)},Z.prototype.has=function(t){return this.__data__.has(t)},Z.prototype.set=function(t,r){var e=this.__data__;if(e instanceof X){var n=e.__data__;if(!H||n.length<199)return n.push([t,r]),this.size=++e.size,this;e=this.__data__=new Y(n)}return e.set(t,r),this.size=e.size,this};var it,ut=function(t,r,e){for(var n=-1,o=Object(t),i=e(t),u=i.length;u--;){var c=i[it?u:++n];if(!1===r(o[c],c,o))break}return t};function ct(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":L&&L in Object(t)?function(t){var r=F.call(t,L),e=t[L];try{t[L]=void 0;var n=!0}catch(t){}var o=S.call(t);n&&(r?t[L]=e:delete t[L]);return o}(t):function(t){return S.call(t)}(t)}function at(t){return xt(t)&&ct(t)==o}function ft(t){return!(!Mt(t)||function(t){return!!U&&U in t}(t))&&(At(t)?P:a).test(function(t){if(null!=t){try{return C.call(t)}catch(t){}try{return t+""}catch(t){}}return""}(t))}function st(t){if(!Mt(t))return function(t){var r=[];if(null!=t)for(var e in Object(t))r.push(e);return r}(t);var r=dt(t),e=[];for(var n in t)("constructor"!=n||!r&&F.call(t,n))&&e.push(n);return e}function lt(t,r,e,n,o){t!==r&&ut(r,(function(i,u){if(o||(o=new Z),Mt(i))!function(t,r,e,n,o,i,u){var a=yt(t,e),f=yt(r,e),s=u.get(f);if(s)return void rt(t,e,s);var l=i?i(a,f,e+"",t,r,u):void 0,h=void 0===l;if(h){var p=wt(f),_=!p&&mt(f),v=!p&&!_&&Ct(f);l=f,p||_||v?wt(a)?l=a:xt(j=a)&&Ot(j)?l=function(t,r){var e=-1,n=t.length;r||(r=Array(n));for(;++e-1&&t%1==0&&t0){if(++r>=800)return arguments[0]}else r=0;return t.apply(void 0,arguments)}}(R?function(t,r){return R(t,"toString",{configurable:!0,enumerable:!1,value:(e=r,function(){return e}),writable:!0});var e}:It);function bt(t,r){return t===r||t!=t&&r!=r}var jt=at(function(){return arguments}())?at:function(t){return xt(t)&&F.call(t,"callee")&&!B.call(t,"callee")},wt=Array.isArray;function Ot(t){return null!=t&&zt(t.length)&&!At(t)}var mt=G||function(){return!1};function At(t){if(!Mt(t))return!1;var r=ct(t);return r==u||"[object GeneratorFunction]"==r||"[object AsyncFunction]"==r||"[object Proxy]"==r}function zt(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=n}function Mt(t){var r=typeof t;return null!=t&&("object"==r||"function"==r)}function xt(t){return null!=t&&"object"==typeof t}var Ct=b?function(t){return function(r){return t(r)}}(b):function(t){return xt(t)&&zt(t.length)&&!!s[ct(t)]};function Ft(t){return Ot(t)?tt(t,!0):st(t)}var Ut,St=(Ut=function(t,r,e){lt(t,r,e)},ht((function(t,r){var e=-1,n=r.length,o=n>1?r[n-1]:void 0,i=n>2?r[2]:void 0;for(o=Ut.length>3&&"function"==typeof o?(n--,o):void 0,i&&function(t,r,e){if(!Mt(e))return!1;var n=typeof r;return!!("number"==n?Ot(e)&&vt(r,e.length):"string"==n&&r in e)&&bt(e[r],t)}(r[0],r[1],i)&&(o=n<3?void 0:o,n=1),t=Object(t);++ec.call(t,r);class f{constructor({pluginId:t,defaultConfig:r={},customMerge:e,root:n}){this.pluginId=t,this.defaultConfig=r,this.pluginConfigPath=o.default.resolve(n||__dirname,t),this.customMerge=e,this._config=void 0}resolve(t){return o.default.resolve(this.pluginConfigPath,t)}hasFile(t){return n.default.existsSync(this.resolve(t))}requireFile(t){try{return require(this.resolve(t))}catch(t){if("MODULE_NOT_FOUND"===t.code)return;throw t}}_getUserConfig(){return this.requireFile("config.json")}config(t,r){this._config||(this._config=(this.customMerge||u)(this.defaultConfig,this._getUserConfig()));let e=this._config;return t?function(t,r,e){if("number"==typeof r)return t[r];if("symbol"==typeof r)return a(t,r)?t[r]:e;const n="string"!=typeof(o=r)?o:o.split(".").reduce(((t,r)=>(r.split(/\[([^}]+)\]/g).forEach((r=>r&&t.push(r))),t)),[]);var o;let i=t;for(let t=0;t-1},X.prototype.set=function(t,r){var e=this.__data__,n=nt(e,t);return n<0?(++this.size,e.push([t,r])):e[n][1]=r,this},Y.prototype.clear=function(){this.size=0,this.__data__={hash:new Q,map:new(H||X),string:new Q}},Y.prototype.delete=function(t){var r=_t(this,t).delete(t);return this.size-=r?1:0,r},Y.prototype.get=function(t){return _t(this,t).get(t)},Y.prototype.has=function(t){return _t(this,t).has(t)},Y.prototype.set=function(t,r){var e=_t(this,t),n=e.size;return e.set(t,r),this.size+=e.size==n?0:1,this},Z.prototype.clear=function(){this.__data__=new X,this.size=0},Z.prototype.delete=function(t){var r=this.__data__,e=r.delete(t);return this.size=r.size,e},Z.prototype.get=function(t){return this.__data__.get(t)},Z.prototype.has=function(t){return this.__data__.has(t)},Z.prototype.set=function(t,r){var e=this.__data__;if(e instanceof X){var n=e.__data__;if(!H||n.length<199)return n.push([t,r]),this.size=++e.size,this;e=this.__data__=new Y(n)}return e.set(t,r),this.size=e.size,this};var it,ut=function(t,r,e){for(var n=-1,o=Object(t),i=e(t),u=i.length;u--;){var c=i[it?u:++n];if(!1===r(o[c],c,o))break}return t};function ct(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":L&&L in Object(t)?function(t){var r=F.call(t,L),e=t[L];try{t[L]=void 0;var n=!0}catch(t){}var o=S.call(t);n&&(r?t[L]=e:delete t[L]);return o}(t):function(t){return S.call(t)}(t)}function at(t){return Ct(t)&&ct(t)==o}function ft(t){return!(!xt(t)||function(t){return!!U&&U in t}(t))&&(zt(t)?P:a).test(function(t){if(null!=t){try{return C.call(t)}catch(t){}try{return t+""}catch(t){}}return""}(t))}function st(t){if(!xt(t))return function(t){var r=[];if(null!=t)for(var e in Object(t))r.push(e);return r}(t);var r=yt(t),e=[];for(var n in t)("constructor"!=n||!r&&F.call(t,n))&&e.push(n);return e}function lt(t,r,e,n,o){t!==r&&ut(r,(function(i,u){if(o||(o=new Z),xt(i))!function(t,r,e,n,o,i,u){var a=gt(t,e),f=gt(r,e),s=u.get(f);if(s)return void rt(t,e,s);var l=i?i(a,f,e+"",t,r,u):void 0,h=void 0===l;if(h){var p=Ot(f),_=!p&&At(f),v=!p&&!_&&Ft(f);l=f,p||_||v?Ot(a)?l=a:Ct(j=a)&&mt(j)?l=function(t,r){var e=-1,n=t.length;r||(r=Array(n));for(;++e-1&&t%1==0&&t0){if(++r>=800)return arguments[0]}else r=0;return t.apply(void 0,arguments)}}(pt);function jt(t,r){return t===r||t!=t&&r!=r}var wt=at(function(){return arguments}())?at:function(t){return Ct(t)&&F.call(t,"callee")&&!B.call(t,"callee")},Ot=Array.isArray;function mt(t){return null!=t&&Mt(t.length)&&!zt(t)}var At=G||function(){return!1};function zt(t){if(!xt(t))return!1;var r=ct(t);return r==u||"[object GeneratorFunction]"==r||"[object AsyncFunction]"==r||"[object Proxy]"==r}function Mt(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=n}function xt(t){var r=typeof t;return null!=t&&("object"==r||"function"==r)}function Ct(t){return null!=t&&"object"==typeof t}var Ft=b?function(t){return function(r){return t(r)}}(b):function(t){return Ct(t)&&Mt(t.length)&&!!s[ct(t)]};function Ut(t){return mt(t)?tt(t,!0):st(t)}var St,It=(St=function(t,r,e){lt(t,r,e)},ht((function(t,r){var e=-1,n=r.length,o=n>1?r[n-1]:void 0,i=n>2?r[2]:void 0;for(o=St.length>3&&"function"==typeof o?(n--,o):void 0,i&&function(t,r,e){if(!xt(e))return!1;var n=typeof r;return!!("number"==n?mt(e)&&dt(r,e.length):"string"==n&&r in e)&&jt(e[r],t)}(r[0],r[1],i)&&(o=n<3?void 0:o,n=1),t=Object(t);++ec.call(t,r);class f{constructor({pluginId:t,defaultConfig:r={},customMerge:e,root:n}){this.pluginId=t,this.defaultConfig=r,this.pluginConfigPath=o.default.resolve(n||__dirname,t),this.customMerge=e,this._config=void 0}resolve(t){return o.default.resolve(this.pluginConfigPath,t)}hasFile(t){return n.default.existsSync(this.resolve(t))}requireFile(t){try{return require(this.resolve(t))}catch(t){if("MODULE_NOT_FOUND"===t.code)return;throw t}}_getUserConfig(){return this.requireFile("config.json")}config(t,r){if(!this._config){const t=this._getUserConfig();this._config=Array.isArray(t)?t:(this.customMerge||u)(this.defaultConfig,t)}let e=this._config;return t?function(t,r,e){if("number"==typeof r)return t[r];if("symbol"==typeof r)return a(t,r)?t[r]:e;const n="string"!=typeof(o=r)?o:o.split(".").reduce(((t,r)=>(r.split(/\[([^}]+)\]/g).forEach((r=>r&&t.push(r))),t)),[]);var o;let i=t;for(let t=0;t { - if (this.disable) { - return - } - const keyName = Object.keys(keyNames).find(key => { - const keyName = $event.key - const value = keyNames[key] - return value === keyName || (Array.isArray(value) && value.includes(keyName)) - }) - if (keyName) { - // 避免和其他按键事件冲突 - setTimeout(() => { - this.$emit(keyName, {}) - }, 0) - } - } - document.addEventListener('keyup', listener) - this.$once('hook:beforeDestroy', () => { - document.removeEventListener('keyup', listener) - }) - }, - render: () => {} -} -// #endif +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + this.$once('hook:beforeDestroy', () => { + document.removeEventListener('keyup', listener) + }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js index c12fd54b..555d2c5c 100644 --- a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js @@ -1,563 +1,563 @@ -export default { - props: { - localdata: { - type: [Array, Object], - default () { - return [] - } - }, - spaceInfo: { - type: Object, - default () { - return {} - } - }, - collection: { - type: String, - default: '' - }, - action: { - type: String, - default: '' - }, - field: { - type: String, - default: '' - }, - orderby: { - type: String, - default: '' - }, - where: { - type: [String, Object], - default: '' - }, - pageData: { - type: String, - default: 'add' - }, - pageCurrent: { - type: Number, - default: 1 - }, - pageSize: { - type: Number, - default: 20 - }, - getcount: { - type: [Boolean, String], - default: false - }, - getone: { - type: [Boolean, String], - default: false - }, - gettree: { - type: [Boolean, String], - default: false - }, - manual: { - type: Boolean, - default: false - }, - value: { - type: [Array, String, Number], - default () { - return [] - } - }, - modelValue: { - type: [Array, String, Number], - default () { - return [] - } - }, - preload: { - type: Boolean, - default: false - }, - stepSearh: { - type: Boolean, - default: true - }, - selfField: { - type: String, - default: '' - }, - parentField: { - type: String, - default: '' - }, - multiple: { - type: Boolean, - default: false - }, - map: { - type: Object, - default() { - return { - text: "text", - value: "value" - } - } - } - }, - data() { - return { - loading: false, - errorMessage: '', - loadMore: { - contentdown: '', - contentrefresh: '', - contentnomore: '' - }, - dataList: [], - selected: [], - selectedIndex: 0, - page: { - current: this.pageCurrent, - size: this.pageSize, - count: 0 - } - } - }, - computed: { - isLocaldata() { - return !this.collection.length - }, - postField() { - let fields = [this.field]; - if (this.parentField) { - fields.push(`${this.parentField} as parent_value`); - } - return fields.join(','); - }, - dataValue() { - let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || this.modelValue !== undefined) - return isModelValue ? this.modelValue : this.value - }, - hasValue() { - if (typeof this.dataValue === 'number') { - return true - } - return (this.dataValue != null) && (this.dataValue.length > 0) - } - }, - created() { - this.$watch(() => { - var al = []; - ['pageCurrent', - 'pageSize', - 'spaceInfo', - 'value', - 'modelValue', - 'localdata', - 'collection', - 'action', - 'field', - 'orderby', - 'where', - 'getont', - 'getcount', - 'gettree' - ].forEach(key => { - al.push(this[key]) - }); - return al - }, (newValue, oldValue) => { - let needReset = false - for (let i = 2; i < newValue.length; i++) { - if (newValue[i] != oldValue[i]) { - needReset = true - break - } - } - if (newValue[0] != oldValue[0]) { - this.page.current = this.pageCurrent - } - this.page.size = this.pageSize - - this.onPropsChange() - }) - this._treeData = [] - }, - methods: { - onPropsChange() { - this._treeData = [] - }, - getCommand(options = {}) { - /* eslint-disable no-undef */ - let db = uniCloud.database(this.spaceInfo) - - const action = options.action || this.action - if (action) { - db = db.action(action) - } - - const collection = options.collection || this.collection - db = db.collection(collection) - - const where = options.where || this.where - if (!(!where || !Object.keys(where).length)) { - db = db.where(where) - } - - const field = options.field || this.field - if (field) { - db = db.field(field) - } - - const orderby = options.orderby || this.orderby - if (orderby) { - db = db.orderBy(orderby) - } - - const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current - const size = options.pageSize !== undefined ? options.pageSize : this.page.size - const getCount = options.getcount !== undefined ? options.getcount : this.getcount - const getTree = options.gettree !== undefined ? options.gettree : this.gettree - - const getOptions = { - getCount, - getTree - } - if (options.getTreePath) { - getOptions.getTreePath = options.getTreePath - } - - db = db.skip(size * (current - 1)).limit(size).get(getOptions) - - return db - }, - getNodeData(callback) { - if (this.loading) { - return - } - this.loading = true - this.getCommand({ - field: this.postField, - where: this._pathWhere() - }).then((res) => { - this.loading = false - this.selected = res.result.data - callback && callback() - }).catch((err) => { - this.loading = false - this.errorMessage = err - }) - }, - getTreePath(callback) { - if (this.loading) { - return - } - this.loading = true - - this.getCommand({ - field: this.postField, - getTreePath: { - startWith: `${this.selfField}=='${this.dataValue}'` - } - }).then((res) => { - this.loading = false - let treePath = [] - this._extractTreePath(res.result.data, treePath) - this.selected = treePath - callback && callback() - }).catch((err) => { - this.loading = false - this.errorMessage = err - }) - }, - loadData() { - if (this.isLocaldata) { - this._processLocalData() - return - } - - if (this.dataValue != null) { - this._loadNodeData((data) => { - this._treeData = data - this._updateBindData() - this._updateSelected() - }) - return - } - - if (this.stepSearh) { - this._loadNodeData((data) => { - this._treeData = data - this._updateBindData() - }) - } else { - this._loadAllData((data) => { - this._treeData = [] - this._extractTree(data, this._treeData, null) - this._updateBindData() - }) - } - }, - _loadAllData(callback) { - if (this.loading) { - return - } - this.loading = true - - this.getCommand({ - field: this.postField, - gettree: true, - startwith: `${this.selfField}=='${this.dataValue}'` - }).then((res) => { - this.loading = false - callback(res.result.data) - this.onDataChange() - }).catch((err) => { - this.loading = false - this.errorMessage = err - }) - }, - _loadNodeData(callback, pw) { - if (this.loading) { - return - } - this.loading = true - - this.getCommand({ - field: this.postField, - where: pw || this._postWhere(), - pageSize: 500 - }).then((res) => { - this.loading = false - callback(res.result.data) - this.onDataChange() - }).catch((err) => { - this.loading = false - this.errorMessage = err - }) - }, - _pathWhere() { - let result = [] - let where_field = this._getParentNameByField(); - if (where_field) { - result.push(`${where_field} == '${this.dataValue}'`) - } - - if (this.where) { - return `(${this.where}) && (${result.join(' || ')})` - } - - return result.join(' || ') - }, - _postWhere() { - let result = [] - let selected = this.selected - let parentField = this.parentField - if (parentField) { - result.push(`${parentField} == null || ${parentField} == ""`) - } - if (selected.length) { - for (var i = 0; i < selected.length - 1; i++) { - result.push(`${parentField} == '${selected[i].value}'`) - } - } - - let where = [] - if (this.where) { - where.push(`(${this.where})`) - } - if (result.length) { - where.push(`(${result.join(' || ')})`) - } - - return where.join(' && ') - }, - _nodeWhere() { - let result = [] - let selected = this.selected - if (selected.length) { - result.push(`${this.parentField} == '${selected[selected.length - 1].value}'`) - } - - if (this.where) { - return `(${this.where}) && (${result.join(' || ')})` - } - - return result.join(' || ') - }, - _getParentNameByField() { - const fields = this.field.split(','); - let where_field = null; - for (let i = 0; i < fields.length; i++) { - const items = fields[i].split('as'); - if (items.length < 2) { - continue; - } - if (items[1].trim() === 'value') { - where_field = items[0].trim(); - break; - } - } - return where_field - }, - _isTreeView() { - return (this.parentField && this.selfField) - }, - _updateSelected() { - var dl = this.dataList - var sl = this.selected - let textField = this.map.text - let valueField = this.map.value - for (var i = 0; i < sl.length; i++) { - var value = sl[i].value - var dl2 = dl[i] - for (var j = 0; j < dl2.length; j++) { - var item2 = dl2[j] - if (item2[valueField] === value) { - sl[i].text = item2[textField] - break - } - } - } - }, - _updateBindData(node) { - const { - dataList, - hasNodes - } = this._filterData(this._treeData, this.selected) - - let isleaf = this._stepSearh === false && !hasNodes - - if (node) { - node.isleaf = isleaf - } - - this.dataList = dataList - this.selectedIndex = dataList.length - 1 - - if (!isleaf && this.selected.length < dataList.length) { - this.selected.push({ - value: null, - text: "请选择" - }) - } - - return { - isleaf, - hasNodes - } - }, - _filterData(data, paths) { - let dataList = [] - let hasNodes = true - - dataList.push(data.filter((item) => { - return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '') - })) - for (let i = 0; i < paths.length; i++) { - var value = paths[i].value - var nodes = data.filter((item) => { - return item.parent_value === value - }) - - if (nodes.length) { - dataList.push(nodes) - } else { - hasNodes = false - } - } - - return { - dataList, - hasNodes - } - }, - _extractTree(nodes, result, parent_value) { - let list = result || [] - let valueField = this.map.value - for (let i = 0; i < nodes.length; i++) { - let node = nodes[i] - - let child = {} - for (let key in node) { - if (key !== 'children') { - child[key] = node[key] - } - } - if (parent_value !== null && parent_value !== undefined && parent_value !== '') { - child.parent_value = parent_value - } - result.push(child) - - let children = node.children - if (children) { - this._extractTree(children, result, node[valueField]) - } - } - }, - _extractTreePath(nodes, result) { - let list = result || [] - for (let i = 0; i < nodes.length; i++) { - let node = nodes[i] - - let child = {} - for (let key in node) { - if (key !== 'children') { - child[key] = node[key] - } - } - result.push(child) - - let children = node.children - if (children) { - this._extractTreePath(children, result) - } - } - }, - _findNodePath(key, nodes, path = []) { - let textField = this.map.text - let valueField = this.map.value - for (let i = 0; i < nodes.length; i++) { - let node = nodes[i] - let children = node.children - let text = node[textField] - let value = node[valueField] - - path.push({ - value, - text - }) - - if (value === key) { - return path - } - - if (children) { - const p = this._findNodePath(key, children, path) - if (p.length) { - return p - } - } - - path.pop() - } - return [] - }, - _processLocalData() { - this._treeData = [] - this._extractTree(this.localdata, this._treeData) - - var inputValue = this.dataValue - if (inputValue === undefined) { - return - } - - if (Array.isArray(inputValue)) { - inputValue = inputValue[inputValue.length - 1] - if (typeof inputValue === 'object' && inputValue[this.map.value]) { - inputValue = inputValue[this.map.value] - } - } - - this.selected = this._findNodePath(inputValue, this.localdata) - } - } -} +export default { + props: { + localdata: { + type: [Array, Object], + default () { + return [] + } + }, + spaceInfo: { + type: Object, + default () { + return {} + } + }, + collection: { + type: String, + default: '' + }, + action: { + type: String, + default: '' + }, + field: { + type: String, + default: '' + }, + orderby: { + type: String, + default: '' + }, + where: { + type: [String, Object], + default: '' + }, + pageData: { + type: String, + default: 'add' + }, + pageCurrent: { + type: Number, + default: 1 + }, + pageSize: { + type: Number, + default: 20 + }, + getcount: { + type: [Boolean, String], + default: false + }, + getone: { + type: [Boolean, String], + default: false + }, + gettree: { + type: [Boolean, String], + default: false + }, + manual: { + type: Boolean, + default: false + }, + value: { + type: [Array, String, Number], + default () { + return [] + } + }, + modelValue: { + type: [Array, String, Number], + default () { + return [] + } + }, + preload: { + type: Boolean, + default: false + }, + stepSearh: { + type: Boolean, + default: true + }, + selfField: { + type: String, + default: '' + }, + parentField: { + type: String, + default: '' + }, + multiple: { + type: Boolean, + default: false + }, + map: { + type: Object, + default() { + return { + text: "text", + value: "value" + } + } + } + }, + data() { + return { + loading: false, + errorMessage: '', + loadMore: { + contentdown: '', + contentrefresh: '', + contentnomore: '' + }, + dataList: [], + selected: [], + selectedIndex: 0, + page: { + current: this.pageCurrent, + size: this.pageSize, + count: 0 + } + } + }, + computed: { + isLocaldata() { + return !this.collection.length + }, + postField() { + let fields = [this.field]; + if (this.parentField) { + fields.push(`${this.parentField} as parent_value`); + } + return fields.join(','); + }, + dataValue() { + let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || this.modelValue !== undefined) + return isModelValue ? this.modelValue : this.value + }, + hasValue() { + if (typeof this.dataValue === 'number') { + return true + } + return (this.dataValue != null) && (this.dataValue.length > 0) + } + }, + created() { + this.$watch(() => { + var al = []; + ['pageCurrent', + 'pageSize', + 'spaceInfo', + 'value', + 'modelValue', + 'localdata', + 'collection', + 'action', + 'field', + 'orderby', + 'where', + 'getont', + 'getcount', + 'gettree' + ].forEach(key => { + al.push(this[key]) + }); + return al + }, (newValue, oldValue) => { + let needReset = false + for (let i = 2; i < newValue.length; i++) { + if (newValue[i] != oldValue[i]) { + needReset = true + break + } + } + if (newValue[0] != oldValue[0]) { + this.page.current = this.pageCurrent + } + this.page.size = this.pageSize + + this.onPropsChange() + }) + this._treeData = [] + }, + methods: { + onPropsChange() { + this._treeData = [] + }, + getCommand(options = {}) { + /* eslint-disable no-undef */ + let db = uniCloud.database(this.spaceInfo) + + const action = options.action || this.action + if (action) { + db = db.action(action) + } + + const collection = options.collection || this.collection + db = db.collection(collection) + + const where = options.where || this.where + if (!(!where || !Object.keys(where).length)) { + db = db.where(where) + } + + const field = options.field || this.field + if (field) { + db = db.field(field) + } + + const orderby = options.orderby || this.orderby + if (orderby) { + db = db.orderBy(orderby) + } + + const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current + const size = options.pageSize !== undefined ? options.pageSize : this.page.size + const getCount = options.getcount !== undefined ? options.getcount : this.getcount + const getTree = options.gettree !== undefined ? options.gettree : this.gettree + + const getOptions = { + getCount, + getTree + } + if (options.getTreePath) { + getOptions.getTreePath = options.getTreePath + } + + db = db.skip(size * (current - 1)).limit(size).get(getOptions) + + return db + }, + getNodeData(callback) { + if (this.loading) { + return + } + this.loading = true + this.getCommand({ + field: this.postField, + where: this._pathWhere() + }).then((res) => { + this.loading = false + this.selected = res.result.data + callback && callback() + }).catch((err) => { + this.loading = false + this.errorMessage = err + }) + }, + getTreePath(callback) { + if (this.loading) { + return + } + this.loading = true + + this.getCommand({ + field: this.postField, + getTreePath: { + startWith: `${this.selfField}=='${this.dataValue}'` + } + }).then((res) => { + this.loading = false + let treePath = [] + this._extractTreePath(res.result.data, treePath) + this.selected = treePath + callback && callback() + }).catch((err) => { + this.loading = false + this.errorMessage = err + }) + }, + loadData() { + if (this.isLocaldata) { + this._processLocalData() + return + } + + if (this.dataValue != null) { + this._loadNodeData((data) => { + this._treeData = data + this._updateBindData() + this._updateSelected() + }) + return + } + + if (this.stepSearh) { + this._loadNodeData((data) => { + this._treeData = data + this._updateBindData() + }) + } else { + this._loadAllData((data) => { + this._treeData = [] + this._extractTree(data, this._treeData, null) + this._updateBindData() + }) + } + }, + _loadAllData(callback) { + if (this.loading) { + return + } + this.loading = true + + this.getCommand({ + field: this.postField, + gettree: true, + startwith: `${this.selfField}=='${this.dataValue}'` + }).then((res) => { + this.loading = false + callback(res.result.data) + this.onDataChange() + }).catch((err) => { + this.loading = false + this.errorMessage = err + }) + }, + _loadNodeData(callback, pw) { + if (this.loading) { + return + } + this.loading = true + + this.getCommand({ + field: this.postField, + where: pw || this._postWhere(), + pageSize: 500 + }).then((res) => { + this.loading = false + callback(res.result.data) + this.onDataChange() + }).catch((err) => { + this.loading = false + this.errorMessage = err + }) + }, + _pathWhere() { + let result = [] + let where_field = this._getParentNameByField(); + if (where_field) { + result.push(`${where_field} == '${this.dataValue}'`) + } + + if (this.where) { + return `(${this.where}) && (${result.join(' || ')})` + } + + return result.join(' || ') + }, + _postWhere() { + let result = [] + let selected = this.selected + let parentField = this.parentField + if (parentField) { + result.push(`${parentField} == null || ${parentField} == ""`) + } + if (selected.length) { + for (var i = 0; i < selected.length - 1; i++) { + result.push(`${parentField} == '${selected[i].value}'`) + } + } + + let where = [] + if (this.where) { + where.push(`(${this.where})`) + } + if (result.length) { + where.push(`(${result.join(' || ')})`) + } + + return where.join(' && ') + }, + _nodeWhere() { + let result = [] + let selected = this.selected + if (selected.length) { + result.push(`${this.parentField} == '${selected[selected.length - 1].value}'`) + } + + if (this.where) { + return `(${this.where}) && (${result.join(' || ')})` + } + + return result.join(' || ') + }, + _getParentNameByField() { + const fields = this.field.split(','); + let where_field = null; + for (let i = 0; i < fields.length; i++) { + const items = fields[i].split('as'); + if (items.length < 2) { + continue; + } + if (items[1].trim() === 'value') { + where_field = items[0].trim(); + break; + } + } + return where_field + }, + _isTreeView() { + return (this.parentField && this.selfField) + }, + _updateSelected() { + var dl = this.dataList + var sl = this.selected + let textField = this.map.text + let valueField = this.map.value + for (var i = 0; i < sl.length; i++) { + var value = sl[i].value + var dl2 = dl[i] + for (var j = 0; j < dl2.length; j++) { + var item2 = dl2[j] + if (item2[valueField] === value) { + sl[i].text = item2[textField] + break + } + } + } + }, + _updateBindData(node) { + const { + dataList, + hasNodes + } = this._filterData(this._treeData, this.selected) + + let isleaf = this._stepSearh === false && !hasNodes + + if (node) { + node.isleaf = isleaf + } + + this.dataList = dataList + this.selectedIndex = dataList.length - 1 + + if (!isleaf && this.selected.length < dataList.length) { + this.selected.push({ + value: null, + text: "请选择" + }) + } + + return { + isleaf, + hasNodes + } + }, + _filterData(data, paths) { + let dataList = [] + let hasNodes = true + + dataList.push(data.filter((item) => { + return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '') + })) + for (let i = 0; i < paths.length; i++) { + var value = paths[i].value + var nodes = data.filter((item) => { + return item.parent_value === value + }) + + if (nodes.length) { + dataList.push(nodes) + } else { + hasNodes = false + } + } + + return { + dataList, + hasNodes + } + }, + _extractTree(nodes, result, parent_value) { + let list = result || [] + let valueField = this.map.value + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i] + + let child = {} + for (let key in node) { + if (key !== 'children') { + child[key] = node[key] + } + } + if (parent_value !== null && parent_value !== undefined && parent_value !== '') { + child.parent_value = parent_value + } + result.push(child) + + let children = node.children + if (children) { + this._extractTree(children, result, node[valueField]) + } + } + }, + _extractTreePath(nodes, result) { + let list = result || [] + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i] + + let child = {} + for (let key in node) { + if (key !== 'children') { + child[key] = node[key] + } + } + result.push(child) + + let children = node.children + if (children) { + this._extractTreePath(children, result) + } + } + }, + _findNodePath(key, nodes, path = []) { + let textField = this.map.text + let valueField = this.map.value + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i] + let children = node.children + let text = node[textField] + let value = node[valueField] + + path.push({ + value, + text + }) + + if (value === key) { + return path + } + + if (children) { + const p = this._findNodePath(key, children, path) + if (p.length) { + return p + } + } + + path.pop() + } + return [] + }, + _processLocalData() { + this._treeData = [] + this._extractTree(this.localdata, this._treeData) + + var inputValue = this.dataValue + if (inputValue === undefined) { + return + } + + if (Array.isArray(inputValue)) { + inputValue = inputValue[inputValue.length - 1] + if (typeof inputValue === 'object' && inputValue[this.map.value]) { + inputValue = inputValue[this.map.value] + } + } + + this.selected = this._findNodePath(inputValue, this.localdata) + } + } +} diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue index e6c187a7..0990868b 100644 --- a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue @@ -14,7 +14,7 @@ - + .uni-list--border-bottom { + position: absolute; + bottom: 0; + right: 0; + left: 0; + height: 1px; + -webkit-transform: scaleY(0.5); + transform: scaleY(0.5); + background-color: $uni-border-color; + } + + /* #endif */ + diff --git a/uni_modules/uni-list/package.json b/uni_modules/uni-list/package.json index 66e8bef0..87b35d42 100644 --- a/uni_modules/uni-list/package.json +++ b/uni_modules/uni-list/package.json @@ -1,7 +1,7 @@ { "id": "uni-list", "displayName": "uni-list 列表", - "version": "1.2.1", + "version": "1.2.10", "description": "List 组件 ,帮助使用者快速构建列表。", "keywords": [ "", @@ -18,11 +18,7 @@ "directories": { "example": "../../temps/example_temps" }, - "dcloudext": { - "category": [ - "前端组件", - "通用组件" - ], +"dcloudext": { "sale": { "regular": { "price": "0.00" @@ -39,7 +35,8 @@ "data": "无", "permissions": "无" }, - "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" }, "uni_modules": { "dependencies": [ diff --git a/uni_modules/uni-nav-bar/changelog.md b/uni_modules/uni-nav-bar/changelog.md index f0f6b566..4ff224d2 100644 --- a/uni_modules/uni-nav-bar/changelog.md +++ b/uni_modules/uni-nav-bar/changelog.md @@ -1,3 +1,9 @@ +## 1.3.9(2022-10-13) +- 修复 条件编译错误的bug +## 1.3.8(2022-10-12) +- 修复 nvue 环境 fixed 为 true 的情况下,无法置顶的 bug +## 1.3.7(2022-08-11) +- 修复 nvue 环境下 fixed 为 true 的情况下,无法置顶的 bug ## 1.3.6(2022-06-30) - 修复 组件示例中插槽用法无法显示内容的bug ## 1.3.5(2022-05-24) diff --git a/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue index 4a46b870..d15b06b5 100644 --- a/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue +++ b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue @@ -1,7 +1,7 @@ @@ -50,6 +52,8 @@ const getVal = (val) => typeof val === 'number' ? val + 'px' : val; /** + * + * * NavBar 自定义导航栏 * @description 导航栏组件,主要用于头部导航 * @tutorial https://ext.dcloud.net.cn/plugin?id=52 @@ -196,6 +200,11 @@ diff --git a/uni_modules/uni-popup/package.json b/uni_modules/uni-popup/package.json index 069e9ce5..6f5f72ee 100644 --- a/uni_modules/uni-popup/package.json +++ b/uni_modules/uni-popup/package.json @@ -1,7 +1,7 @@ { "id": "uni-popup", "displayName": "uni-popup 弹出层", - "version": "1.7.9", + "version": "1.8.1", "description": " Popup 组件,提供常用的弹层", "keywords": [ "uni-ui", @@ -17,12 +17,8 @@ "directories": { "example": "../../temps/example_temps" }, - "dcloudext": { - "category": [ - "前端组件", - "通用组件" - ], - "sale": { + "dcloudext": { + "sale": { "regular": { "price": "0.00" }, @@ -38,7 +34,8 @@ "data": "无", "permissions": "无" }, - "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" }, "uni_modules": { "dependencies": [ diff --git a/uni_modules/uni-table/changelog.md b/uni_modules/uni-table/changelog.md index 8233b20f..c711615a 100644 --- a/uni_modules/uni-table/changelog.md +++ b/uni_modules/uni-table/changelog.md @@ -1,3 +1,5 @@ +## 1.2.2(2022-11-29) +- 优化 主题样式 ## 1.2.1(2022-06-06) - 修复 微信小程序存在无使用组件的问题 ## 1.2.0(2021-11-19) diff --git a/uni_modules/uni-table/components/uni-table/uni-table.vue b/uni_modules/uni-table/components/uni-table/uni-table.vue index d8d49db1..1ddc567b 100644 --- a/uni_modules/uni-table/components/uni-table/uni-table.vue +++ b/uni_modules/uni-table/components/uni-table/uni-table.vue @@ -125,7 +125,7 @@ export default { } else { startIndex = theadChildren.rowspan - 1 } - let isHaveData = this.data && this.data.length.length > 0 + let isHaveData = this.data && this.data.length > 0 theadChildren.checked = true theadChildren.indeterminate = false this.trChildren.forEach((item, index) => { diff --git a/uni_modules/uni-table/components/uni-th/filter-dropdown.vue b/uni_modules/uni-table/components/uni-th/filter-dropdown.vue index bc9a0e3b..df22a714 100644 --- a/uni_modules/uni-table/components/uni-th/filter-dropdown.vue +++ b/uni_modules/uni-table/components/uni-th/filter-dropdown.vue @@ -112,6 +112,12 @@ value: 'value' } } + }, + filterDefaultValue: { + type: [Array,String], + default () { + return "" + } } }, computed: { @@ -157,7 +163,7 @@ enabled: true, isOpened: false, dataList: [], - filterValue: '', + filterValue: this.filterDefaultValue, checkedValues: [], gtValue: '', ltValue: '', @@ -286,6 +292,8 @@ diff --git a/uni_modules/uni-table/components/uni-th/uni-th.vue b/uni_modules/uni-table/components/uni-th/uni-th.vue index 121e13cb..75fa42a8 100644 --- a/uni_modules/uni-table/components/uni-th/uni-th.vue +++ b/uni_modules/uni-table/components/uni-th/uni-th.vue @@ -1,6 +1,6 @@ - -