From 56a24de3cb0cd60aa3949585167abc2975f59dce Mon Sep 17 00:00:00 2001 From: AKW <2497744746@qq.com> Date: Wed, 13 Dec 2023 11:41:22 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 3 + .gitignore | 173 ++++++++ Dockerfile | 26 ++ apps/jqr/__init__.py | 0 apps/jqr/admin.py | 3 + apps/jqr/apps.py | 6 + apps/jqr/choices.py | 200 +++++++++ apps/jqr/migrations/__init__.py | 0 apps/jqr/models.py | 388 ++++++++++++++++++ apps/jqr/serializers.py | 0 apps/jqr/tasks.py | 0 apps/jqr/tests.py | 3 + apps/jqr/urls.py | 6 + apps/jqr/views.py | 0 deploy.sh | 5 + docker-compose.yml | 30 ++ libs/__init__.py | 0 libs/dingding/dingding.py | 48 +++ libs/kz/__init__.py | 1 + libs/kz/kz.py | 79 ++++ libs/topsdk/__init__.py | 1 + libs/topsdk/ability132/__init__.py | 0 libs/topsdk/ability132/ability132.py | 52 +++ libs/topsdk/ability132/request/__init__.py | 0 .../request/taobao_tmc_auth_get_request.py | 43 ++ .../request/taobao_tmc_group_add_request.py | 81 ++++ .../taobao_tmc_group_delete_request.py | 81 ++++ .../request/taobao_tmc_groups_get_request.py | 81 ++++ .../taobao_tmc_messages_confirm_request.py | 81 ++++ .../taobao_tmc_messages_consume_request.py | 62 +++ .../taobao_tmc_messages_produce_request.py | 73 ++++ .../taobao_tmc_topic_group_add_request.py | 62 +++ .../taobao_tmc_topic_group_delete_request.py | 81 ++++ libs/topsdk/client.py | 132 ++++++ libs/topsdk/defaultability/__init__.py | 0 libs/topsdk/defaultability/defaultability.py | 187 +++++++++ .../topsdk/defaultability/request/__init__.py | 0 ...leme_media_activity_coupon_send_request.py | 58 +++ ..._promotion_officialactivity_get_request.py | 73 ++++ ...me_promotion_storepromotion_get_request.py | 78 ++++ ..._promotion_storepromotion_query_request.py | 143 +++++++ ...sc_union_kb_bbt_item_detail_get_request.py | 58 +++ ..._bbt_item_promotion_filter_list_request.py | 81 ++++ ...ba_alsc_union_kb_bbt_item_query_request.py | 88 ++++ ...on_kb_bbt_item_store_detail_get_request.py | 53 +++ ...b_bbt_item_store_relation_query_request.py | 58 +++ ...ba_alsc_union_kb_common_encrypt_request.py | 53 +++ ...a_alsc_union_kb_item_detail_get_request.py | 63 +++ ...n_kb_item_promotion_filter_list_request.py | 62 +++ ...ba_alsc_union_kb_item_promotion_request.py | 252 ++++++++++++ ..._kb_item_promotion_share_create_request.py | 214 ++++++++++ ...libaba_alsc_union_kb_item_query_request.py | 252 ++++++++++++ ..._union_kb_item_store_detail_get_request.py | 53 +++ ...on_kb_item_store_relation_query_request.py | 63 +++ ...baba_alsc_union_kb_order_create_request.py | 98 +++++ ...alibaba_alsc_union_kb_order_pay_request.py | 58 +++ ...ibaba_alsc_union_kb_order_query_request.py | 53 +++ ...baba_alsc_union_kb_order_refund_request.py | 83 ++++ ..._alsc_union_kb_store_item_query_request.py | 100 +++++ ...ibaba_alsc_union_kb_store_query_request.py | 252 ++++++++++++ ...c_union_kbcpa_order_details_get_request.py | 176 ++++++++ ...sc_union_kbcpa_punish_order_get_request.py | 119 ++++++ ...sc_union_kbcpa_refund_order_get_request.py | 138 +++++++ ..._union_kbcpx_positive_order_get_request.py | 252 ++++++++++++ ...sc_union_kbcpx_punish_order_get_request.py | 195 +++++++++ ...sc_union_kbcpx_refund_order_get_request.py | 195 +++++++++ ...ibaba_alsc_union_media_zone_add_request.py | 62 +++ ...ibaba_alsc_union_media_zone_get_request.py | 62 +++ ...sc_union_promotion_link_analyze_request.py | 58 +++ .../taobao_tmc_message_produce_request.py | 247 +++++++++++ .../request/taobao_tmc_user_cancel_request.py | 62 +++ .../request/taobao_tmc_user_get_request.py | 81 ++++ .../request/taobao_tmc_user_permit_request.py | 43 ++ libs/topsdk/requirements.txt | 5 + libs/topsdk/top.py | 89 ++++ libs/topsdk/util.py | 115 ++++++ libs/wechat/__init__.py | 1 + libs/wechat/worker.py | 311 ++++++++++++++ logs/.gitignore | 0 manage.py | 22 + requirements.txt | Bin 0 -> 2238 bytes start | 4 + start-celeryworker | 10 + utils/CustomField.py | 31 ++ utils/__init__.py | 0 utils/base_serializer.py | 144 +++++++ utils/base_viewsets.py | 199 +++++++++ utils/encryptor.py | 21 + utils/exceptions.py | 68 +++ utils/jwt.py | 20 + utils/jwt_authenticate.py | 101 +++++ utils/logger.py | 11 + utils/pagination.py | 49 +++ utils/redis_utils.py | 39 ++ utils/renders.py | 11 + utils/response.py | 19 + utils/throttling.py | 42 ++ utils/tools.py | 243 +++++++++++ yzk_wechat_event/__init__.py | 0 yzk_wechat_event/asgi.py | 16 + yzk_wechat_event/celery.py | 37 ++ yzk_wechat_event/gunicorn.conf.py | 17 + yzk_wechat_event/routers.py | 24 ++ yzk_wechat_event/settings/__init__.py | 0 yzk_wechat_event/settings/base.py | 263 ++++++++++++ yzk_wechat_event/settings/constant.py | 33 ++ yzk_wechat_event/urls.py | 22 + yzk_wechat_event/wsgi.py | 16 + 108 files changed, 8007 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 apps/jqr/__init__.py create mode 100644 apps/jqr/admin.py create mode 100644 apps/jqr/apps.py create mode 100644 apps/jqr/choices.py create mode 100644 apps/jqr/migrations/__init__.py create mode 100644 apps/jqr/models.py create mode 100644 apps/jqr/serializers.py create mode 100644 apps/jqr/tasks.py create mode 100644 apps/jqr/tests.py create mode 100644 apps/jqr/urls.py create mode 100644 apps/jqr/views.py create mode 100644 deploy.sh create mode 100644 docker-compose.yml create mode 100644 libs/__init__.py create mode 100644 libs/dingding/dingding.py create mode 100644 libs/kz/__init__.py create mode 100644 libs/kz/kz.py create mode 100644 libs/topsdk/__init__.py create mode 100644 libs/topsdk/ability132/__init__.py create mode 100644 libs/topsdk/ability132/ability132.py create mode 100644 libs/topsdk/ability132/request/__init__.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_auth_get_request.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_group_add_request.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_group_delete_request.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_groups_get_request.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_messages_confirm_request.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_messages_consume_request.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_messages_produce_request.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_topic_group_add_request.py create mode 100644 libs/topsdk/ability132/request/taobao_tmc_topic_group_delete_request.py create mode 100644 libs/topsdk/client.py create mode 100644 libs/topsdk/defaultability/__init__.py create mode 100644 libs/topsdk/defaultability/defaultability.py create mode 100644 libs/topsdk/defaultability/request/__init__.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_media_activity_coupon_send_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_officialactivity_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_storepromotion_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_storepromotion_query_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_detail_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_promotion_filter_list_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_query_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_store_detail_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_store_relation_query_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_common_encrypt_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_detail_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_filter_list_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_share_create_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_query_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_store_detail_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_store_relation_query_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_create_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_pay_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_query_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_refund_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_store_item_query_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kb_store_query_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_order_details_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_punish_order_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_refund_order_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_positive_order_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_punish_order_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_refund_order_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_media_zone_add_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_media_zone_get_request.py create mode 100644 libs/topsdk/defaultability/request/alibaba_alsc_union_promotion_link_analyze_request.py create mode 100644 libs/topsdk/defaultability/request/taobao_tmc_message_produce_request.py create mode 100644 libs/topsdk/defaultability/request/taobao_tmc_user_cancel_request.py create mode 100644 libs/topsdk/defaultability/request/taobao_tmc_user_get_request.py create mode 100644 libs/topsdk/defaultability/request/taobao_tmc_user_permit_request.py create mode 100644 libs/topsdk/requirements.txt create mode 100644 libs/topsdk/top.py create mode 100644 libs/topsdk/util.py create mode 100644 libs/wechat/__init__.py create mode 100644 libs/wechat/worker.py create mode 100644 logs/.gitignore create mode 100644 manage.py create mode 100644 requirements.txt create mode 100644 start create mode 100644 start-celeryworker create mode 100644 utils/CustomField.py create mode 100644 utils/__init__.py create mode 100644 utils/base_serializer.py create mode 100644 utils/base_viewsets.py create mode 100644 utils/encryptor.py create mode 100644 utils/exceptions.py create mode 100644 utils/jwt.py create mode 100644 utils/jwt_authenticate.py create mode 100644 utils/logger.py create mode 100644 utils/pagination.py create mode 100644 utils/redis_utils.py create mode 100644 utils/renders.py create mode 100644 utils/response.py create mode 100644 utils/throttling.py create mode 100644 utils/tools.py create mode 100644 yzk_wechat_event/__init__.py create mode 100644 yzk_wechat_event/asgi.py create mode 100644 yzk_wechat_event/celery.py create mode 100644 yzk_wechat_event/gunicorn.conf.py create mode 100644 yzk_wechat_event/routers.py create mode 100644 yzk_wechat_event/settings/__init__.py create mode 100644 yzk_wechat_event/settings/base.py create mode 100644 yzk_wechat_event/settings/constant.py create mode 100644 yzk_wechat_event/urls.py create mode 100644 yzk_wechat_event/wsgi.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f2c1b4e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.idea +**/.vscode +/yzk/settings/local.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2bfc31 --- /dev/null +++ b/.gitignore @@ -0,0 +1,173 @@ +.vscode +.idea +.coverage* +.DS_Store +staticfiles +mediafiles +client/node_modules +node_modules +*/node_modules +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +tests +tests/* +# C extensions +*.so +logs/*.log +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +celerybeat-*.bak +celerybeat-*.dat +celerybeat-*.dir +/yzk_wechat_event/settings/local.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d18b448 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM python:3.11.4 + +ENV APP_HOME=/app +RUN mkdir $APP_HOME +WORKDIR $APP_HOME + +LABEL maintainer='AKW' +LABEL bilibili='https://www.bilibili.com' +LABEL description='Development image for Real Estate Project' + +ENV PYTHONDONTWRITEBYTECODE 1 + +ENV PYTHONUNBUFFERED 1 + +RUN mkdir logs + +RUN mkdir ~/.pip +RUN echo "[global]" > ~/.pip/pip.conf +RUN echo "index-url = https://pypi.tuna.tsinghua.edu.cn/simple" >> ~/.pip/pip.conf +RUN pip3 install --upgrade pip + +COPY ./requirements.txt /app/requirements.txt + +RUN pip3 install -r requirements.txt + +ENTRYPOINT ["sh"] diff --git a/apps/jqr/__init__.py b/apps/jqr/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/jqr/admin.py b/apps/jqr/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/jqr/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/jqr/apps.py b/apps/jqr/apps.py new file mode 100644 index 0000000..1e6249d --- /dev/null +++ b/apps/jqr/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class JqrConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.jqr' diff --git a/apps/jqr/choices.py b/apps/jqr/choices.py new file mode 100644 index 0000000..80dd751 --- /dev/null +++ b/apps/jqr/choices.py @@ -0,0 +1,200 @@ +from django.db import models + + +class JqrHookUserGenderChoices(models.IntegerChoices): + """ + 微信用户性别 + """ + UNKNOWN = 0, '未知' + MALE = 1, '男' + FEMALE = 2, '女' + + +class JqrHookUserStatusChoices(models.IntegerChoices): + ACTIVE = 1, '已激活' + DISABLED = 2, '已禁用' + UN_ACTIVE = 4, '未激活' + QUIQ_CORP = 5, '退出企业' + + +class JqrExternalUserTypeChoices(models.IntegerChoices): + WECHAT = 1, '微信用户' + COMPANY_WECHAT = 2, '企业微信用户' + + +class JqrSendSettingPlatformTypeChoices(models.IntegerChoices): + ELM = 1, '饿了么' + MT = 2, '美团' + TB = 3, '淘宝' + JD = 4, '京东' + PDD = 5, '拼多多' + WPH = 6, '唯品会' + + +class JqrSendMsgMtActivityImageChoices(models.IntegerChoices): + ORIGIN = 1, '原始图片' + POSTER = 2, '海报图片' + + +class JqrUserAddTypeChoices(models.IntegerChoices): + UNKNOWN_SOURCE = 0, '未知来源' + SCAN_QRCODE = 1, '扫描二维码' + SEARCH_PHONE = 2, '搜索手机号' + CARD_SHARE = 3, '名片分享' + GROUP_CHAT = 4, '群聊' + PHONE_BOOK = 5, '手机通讯录' + WECHAT_CONTACT = 6, '微信联系人' + THIRD_APP_AUTO_ADD = 8, '安装第三方应用时自动添加的客服人员' + SEARCH_EMAIL = 9, '搜索邮箱' + WECHAT_VIDEO = 10, '视频号添加' + SCHEDULE_PARTICIPANT = 11, '通过日程参与人添加' + MEETING_PARTICIPANT = 12, '通过会议参与人添加' + WECHAT_BUSINESS = 13, '添加微信好友对应的企业微信' + SMART_HADWARE_DEDICATED_SERVICE = 14, '通过智慧硬件专属客服添加' + HOME_SERVICE = 15, '通过上门服务客服添加' + CUSTOMER_LINK = 16, '通过客户链接添加' + INTERNAL_COLLABORATION = 201, '内部成员共享' + ADMIN_ALLOCATE = 202, '管理员或负责人分配' + + +class JqrSendMsgMtTextConvertChoices(models.IntegerChoices): + MP = 0, 'mp' + URL = 1, 'url' + + +class JqrSendMsgElmTextConvertChoices(models.IntegerChoices): + MP = 0, 'mp' + URL = 1, 'url' + TKL = 2, '淘口令' + + +class JqrSendMsgTextTypeChoices(models.IntegerChoices): + REPLACE = 1, '替换文案' + CONVERT = 2, '转链' + + +class JqrSendMsgAttachmentItemMsgTypeChoices(models.IntegerChoices): + TEXT = 1, '文本' + ACTIVITY_POSTER = 2, '活动海报' + FIXED_IMAGE = 3, '固定图片' + ELM_ACTIVITY_IMAGE = 4, '饿了么活动图片' + MT_ACTIVITY_IMAGE = 5, '美团活动图片' + VIDEO = 6, '视频' + FIXED_LINK = 7, '固定链接' + OUR_ACTIVITY_PAGE = 8, '我们自己的活动页' + ELM_ACTIVITY_LINK = 9, '饿了么活动链接' + MT_ACTIVITY_LINK = 10, '美团活动链接' + VIDEO_NUMBER = 11, '视频号' + FIXED_MINI_PROGRAM = 12, '固定小程序' + ELM_MINI_PROGRAM = 13, '饿了么小程序' + MT_MINI_PROGRAM = 14, '美团小程序' + + +class JqrSendGroupMsgNeedSidChoices(models.IntegerChoices): + NOT_NEED = 0, '不需要' + NEED = 1, '需要' + + +class JqrSendGroupMsgIsOpenChoices(models.IntegerChoices): + NOT_OPEN = 0, '未开启' + OPEN = 1, '已开启' + + +class JqrSendGroupMsgTimeTypeChoices(models.IntegerChoices): + # 1.指定,2.每日,3.每周,4.每月 + SPECIFIED = 1, '指定' + DAILY = 2, '每日' + WEEKLY = 3, '每周' + MONTHLY = 4, '每月' + + +class JqrSendGroupMsgSendTypeChoices(models.IntegerChoices): + # 1=高级群发,2=极速群发 + ADVANCED = 1, '高级群发' + FAST = 2, '极速群发' + + +class JqrAddTypeChoices(models.IntegerChoices): + API = 1, 'API拉取' + EVENT_CALLBACK = 2, '事件回调' + HOOK = 3, 'hook' + + +class JqrWechatbizuserinfoDeleteTypeChoices(models.IntegerChoices): + EVENT_CALLBACK = 1, '事件回调' + SEND_SYNC_RECORD = 2, '发送同步记录' + + +class JqrSendMsgSendUserTypeChoices(models.IntegerChoices): + # 0, 全部, 1 部分 + ALL = 0, '全部' + PART = 1, '部分' + + +class JqrSendGroupMsgSendUserTypeChoices(models.IntegerChoices): + # 发送用户类型 0 全部,1 部分企业,2 企业用户 + ALL = 0, '所有接粉号' + CORP_PART = 1, '企业下所有接粉号' + CORP_USER = 2, '指定接粉号' + + +class JqrSendGroupMsgSendTagTypeChoices(models.IntegerChoices): + ALL_USER = 0, '所有用户' + SOME_EXTERNAL_USER = 1, '指定用户' + CORP_TAGS = 2, '满足任意标签的用户' + + +class JqrNewUserSendGroupMsgTypeChoices(models.IntegerChoices): + """新客欢迎类型""" + PRIVATE = 0, '私聊' + QUN = 1, '群聊' + ORDER = 2, '催单' + + +class JqrTimeSendGroupMsgTypeChoices(models.IntegerChoices): + """定时发送类型""" + PRIVATE = 0, '私聊' + QUN = 1, '群聊' + + +class JqrKeywordSendGroupMsgTypeChoices(models.IntegerChoices): + """关键字发送类型""" + PRIVATE = 0, '私聊' + QUN = 1, '群聊' + + +class JqrKeywordSendGroupMsgPatternChoices(models.IntegerChoices): + """关键字发送模式""" + ANY = 0, '匹配任意信息' + EXACT = 1, '精准匹配' + FUZZY = 2, '模糊匹配' + + +class JqrGroupMsgListChatTypeChoices(models.TextChoices): + # 默认为single,表示发送给客户,group表示发送给客户群 + SINGLE = 'single', '私聊' + GROUP = 'group', '群聊' + + +class JqrGroupMsgListFilterTypeChoices(models.IntegerChoices): + CORP = 0, '企业' + PERSONAL = 1, '个人' + ALL = 2, '全部' + + +class JqrGroupMsgListCreateTypeChoices(models.IntegerChoices): + CORP = 0, '企业' + PERSONAL = 1, '个人' + + +class JqrGroupMsgTaskStatusChoices(models.IntegerChoices): + UNSENT = 0, '未发送' + SENT = 2, '已发送' + + +class JqrGrouMsgSendGroupMsgStatusChoices(models.IntegerChoices): + # 发送状态:0-未发送 1-已发送 2-因客户不是好友导致发送失败 3-因客户已经收到其他群发消息导致发送失败 + UNSENT = 0, '未发送' + SENT = 1, '已发送' + FAIL_SEND_TO_FRIEND = 2, '因客户不是好友导致发送失败' + FAIL_SEND_TO_OTHER_MSG = 3, '因客户已经收到其他群发消息导致发送失败' diff --git a/apps/jqr/migrations/__init__.py b/apps/jqr/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/jqr/models.py b/apps/jqr/models.py new file mode 100644 index 0000000..3ad86ff --- /dev/null +++ b/apps/jqr/models.py @@ -0,0 +1,388 @@ +from .choices import * + + +class JqrWechatbizuserinfo(models.Model): + id = models.BigAutoField(primary_key=True) + userid = models.CharField(max_length=50, verbose_name='UserId') + corpid = models.CharField(max_length=32, verbose_name='企业微信corpid') + username = models.CharField( + max_length=50, verbose_name='用户名', null=True, blank=True) + alias = models.CharField( + max_length=64, verbose_name='别名', null=True, blank=True) + department = models.JSONField(verbose_name='部门ID集合') + maindepartment = models.IntegerField(verbose_name='主部门ID') + telephone = models.CharField( + max_length=20, verbose_name='座机', null=True, blank=True) + position = models.CharField( + max_length=64, verbose_name='职位', null=True, blank=True) + external_position = models.CharField( + max_length=64, verbose_name='对外职务', null=True, blank=True) + direct_leader = models.JSONField( + verbose_name='直属上级userid', null=True, blank=True) + is_leader_in_dept = models.JSONField( + verbose_name='表示在所在的部门内是否为部门负责人,数量与department一致', null=True, blank=True) + status = models.IntegerField( + verbose_name='激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业', + choices=JqrHookUserStatusChoices.choices) + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + dtime = models.DateTimeField(verbose_name='删除时间', null=True, blank=True) + deletetype = models.IntegerField( + verbose_name='删除方式', choices=JqrWechatbizuserinfoDeleteTypeChoices.choices, null=True, blank=True) + addtype = models.IntegerField( + verbose_name='添加方式', choices=JqrUserAddTypeChoices.choices, default=JqrAddTypeChoices.API) + + class Meta: + db_table = 'jqr_wechatbizuserinfo' + ordering = ['-ctime'] + verbose_name = '机器人用户表' + + +class JqrHookUser(models.Model): + id = models.BigAutoField(primary_key=True) + uid = models.IntegerField(verbose_name='用户Id') + corpid = models.CharField(max_length=255, verbose_name='企业唯一标识') + userid = models.CharField(max_length=50, verbose_name='接粉号userid') + vid = models.CharField(max_length=255, verbose_name='hook vid') + new_user = models.BooleanField(default=True, verbose_name='新科欢迎开关') + new_user_order = models.BooleanField( + default=False, verbose_name='新科欢迎催单开关') + time_qun = models.BooleanField(default=True, verbose_name='定时群聊开关') + time_private = models.BooleanField(default=True, verbose_name='定时私聊开关') + time_quan = models.BooleanField(default=True, verbose_name='定时朋友圈开关') + hook_time = models.DateTimeField( + null=True, blank=True, verbose_name='hook时间') + utime = models.DateTimeField(verbose_name='更新时间', null=True, blank=True) + name = models.CharField( + max_length=255, verbose_name='用户名', null=True, blank=True) + mobile = models.CharField( + max_length=30, verbose_name='手机号', null=True, blank=True) + phone = models.CharField( + max_length=30, verbose_name='手机号', null=True, blank=True) + gender = models.IntegerField( + verbose_name='性别,0表示未定义,1表示男性,2表示女性', choices=JqrHookUserGenderChoices.choices) + english_name = models.CharField( + max_length=255, verbose_name='别名', null=True, blank=True) + bind_email = models.CharField( + max_length=255, verbose_name='绑定邮箱', null=True, blank=True) + external_custom_info = models.JSONField( + verbose_name='对外展示信息', null=True, blank=True) + qrcode = models.CharField( + max_length=255, verbose_name='二维码', null=True, blank=True) + + class Meta: + unique_together = ('corpid', 'userid', 'vid') + db_table = 'jqr_hook_user' + verbose_name = '机器人hook用户详细表' + + +class JqrExternalUser(models.Model): + id = models.BigAutoField(primary_key=True) + corpid = models.CharField(max_length=32, verbose_name='企业微信corpid') + external_userid = models.CharField(max_length=60, verbose_name='外部用户id') + vid = models.CharField( + max_length=50, verbose_name='接粉号vid', null=True, blank=True) + name = models.CharField(max_length=255, verbose_name='用户名') + avatar = models.CharField( + max_length=255, verbose_name='头像', null=True, blank=True) + type = models.IntegerField( + verbose_name='外部联系人的类型', choices=JqrExternalUserTypeChoices.choices) + gender = models.IntegerField( + verbose_name='性别', choices=JqrHookUserGenderChoices.choices) + unionid = models.CharField( + max_length=255, verbose_name='微信unionid', null=True, blank=True) + position = models.CharField( + max_length=255, verbose_name='职位', null=True, blank=True) + corp_name = models.CharField( + max_length=255, verbose_name='企业简称', null=True, blank=True) + corp_full_name = models.CharField( + max_length=255, verbose_name='企业名称', null=True, blank=True) + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + dtime = models.DateTimeField(verbose_name='删除时间', null=True, blank=True) + addtype = models.IntegerField( + verbose_name='添加方式', choices=JqrUserAddTypeChoices.choices, default=JqrAddTypeChoices.API) + deletetype = models.IntegerField( + verbose_name='删除方式', choices=JqrWechatbizuserinfoDeleteTypeChoices.choices, null=True, blank=True) + + class Meta: + db_table = 'jqr_external_user' + verbose_name = '机器人外部联系人表' + ordering = ['-ctime'] + unique_together = ('corpid', 'external_userid') + + +class JqrExternalFollowUser(models.Model): + corpid = models.CharField(max_length=32, verbose_name='企业微信corpid') + external_userid = models.CharField(max_length=60, verbose_name='外部用户id') + id = models.BigAutoField(primary_key=True) + userid = models.CharField(max_length=50, verbose_name='接粉号userid') + remark = models.TextField( + verbose_name='该成员对此外部联系人的备注', null=True, blank=True) + description = models.TextField( + verbose_name='该成员对此外部联系人的描述', null=True, blank=True) + createtime = models.BigIntegerField(verbose_name='添加好友时间') + tags = models.JSONField(verbose_name='标签') + remark_mobiles = models.JSONField(verbose_name='该成员对此客户备注的手机号码') + add_way = models.IntegerField( + verbose_name='添加方式', choices=JqrUserAddTypeChoices.choices) + state = models.CharField( + max_length=255, verbose_name='企业自定义的state参数', null=True, blank=True) + oper_userid = models.CharField( + max_length=50, verbose_name='添加人userid', null=True, blank=True) + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + dtime = models.DateTimeField(verbose_name='删除时间', null=True, blank=True) + addtype = models.IntegerField( + verbose_name='添加方式', choices=JqrUserAddTypeChoices.choices, default=JqrAddTypeChoices.API) + deletetype = models.IntegerField( + verbose_name='删除方式', choices=JqrWechatbizuserinfoDeleteTypeChoices.choices, null=True, blank=True) + + class Meta: + db_table = 'jqr_external_follow_user' + verbose_name = '机器人外部联系人关系表' + ordering = ['-ctime'] + unique_together = ('corpid', 'userid', 'external_userid') + + +class JqrExternalQun(models.Model): + id = models.BigAutoField(primary_key=True) + corpid = models.CharField(max_length=32, verbose_name='企业微信corpid') + vid = models.CharField( + max_length=50, verbose_name='接粉号vid', null=True, blank=True) + chat_id = models.CharField(max_length=60, verbose_name='群id') + name = models.CharField(max_length=255, verbose_name='群名') + owner = models.CharField(max_length=50, verbose_name='接粉号userid') + create_time = models.BigIntegerField(verbose_name='群创建时间') + notice = models.TextField(verbose_name='群通知', null=True, blank=True) + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + dtime = models.DateTimeField(verbose_name='删除时间', null=True, blank=True) + addtype = models.IntegerField( + verbose_name='添加方式', choices=JqrUserAddTypeChoices.choices, default=JqrAddTypeChoices.API) + + class Meta: + db_table = 'jqr_external_qun' + verbose_name = '机器人外部联系人群表' + ordering = ['-create_time'] + + +class JqrSendSetting(models.Model): + corpid = models.CharField(max_length=32, verbose_name='企业微信corpid', null=True, blank=True) + agentid = models.CharField( + max_length=128, verbose_name='自建应用agentid', null=True, blank=True) + id = models.BigAutoField(primary_key=True) + uid = models.IntegerField(verbose_name='用户Id') + userid = models.CharField( + max_length=50, verbose_name='接粉号id', null=True, blank=True) + chat_id = models.CharField( + max_length=60, verbose_name='群id', null=True, blank=True) + scene = models.CharField( + max_length=255, verbose_name='场景', null=True, blank=True) + platform_type = models.IntegerField( + verbose_name='平台类型', choices=JqrSendSettingPlatformTypeChoices.choices) + setting = models.JSONField(verbose_name='配置') + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + is_delete = models.BooleanField(verbose_name='是否删除', default=False) + + class Meta: + db_table = 'jqr_send_setting' + verbose_name = '发送配置' + ordering = ['-ctime'] + + +class JqrBaseSendMsg(models.Model): + id = models.AutoField(primary_key=True) + uid = models.IntegerField(verbose_name='用户Id') + taskname = models.CharField(max_length=64, verbose_name='任务名称') + sendusertype = models.IntegerField( + verbose_name='发送用户类型', choices=JqrSendGroupMsgSendUserTypeChoices.choices) + senduserids = models.JSONField(verbose_name='发送用户') + sendcontent = models.JSONField(verbose_name='发送内容') + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + isopen = models.IntegerField(verbose_name='是否开启', choices=JqrSendGroupMsgIsOpenChoices.choices) + scene = models.CharField(max_length=128, verbose_name='场景', null=True, blank=True) + + class Meta: + abstract = True + + +class JqrSendGroupMsg(JqrBaseSendMsg): + needsid = models.IntegerField(verbose_name='是否需要sid', choices=JqrSendGroupMsgNeedSidChoices.choices, + default=JqrSendGroupMsgNeedSidChoices.NOT_NEED) + timetype = models.IntegerField(verbose_name='发送时间类型', choices=JqrSendGroupMsgTimeTypeChoices.choices, + default=None) + timejson = models.JSONField(verbose_name='发送时间对象', default=None) + sendtime = models.DateTimeField(verbose_name='下次具体发送时间', null=True, blank=True) + + class Meta: + db_table = 'jqr_sendgroupmsg' + verbose_name = 'JQR Send Group Message' + verbose_name_plural = 'JQR Send Group Messages' + ordering = ['-ctime'] + + def __str__(self): + return self.taskname + + +class JqrNewUserSendGroupMsg(JqrBaseSendMsg): + # 优先级 + priority = models.IntegerField(verbose_name='优先级', default=0) + type = models.IntegerField(verbose_name='类型', choices=JqrNewUserSendGroupMsgTypeChoices.choices, + default=JqrNewUserSendGroupMsgTypeChoices.PRIVATE) + + class Meta: + db_table = 'jqr_newusersendgroupmsg' + verbose_name = 'JQR New User Send Group Message' + verbose_name_plural = 'JQR New User Send Group Messages' + ordering = ['-ctime'] + + def __str__(self): + return self.taskname + + +class JqrNewUserOrderSendGroupMsg(JqrBaseSendMsg): + # 优先级 + priority = models.IntegerField(verbose_name='优先级', default=0) + + class Meta: + db_table = 'jqr_newuserordersendgroupmsg' + verbose_name = 'JQR New User Order Send Group Message' + verbose_name_plural = 'JQR New User Order Send Group Messages' + ordering = ['-ctime'] + + def __str__(self): + return self.taskname + + +class JqrTimePrivateSendGroupMsg(JqrBaseSendMsg): + sendtype = models.IntegerField(verbose_name='发送类型,1=高级群发,2=极速群发', + choices=JqrSendGroupMsgSendTypeChoices.choices, + default=JqrSendGroupMsgSendTypeChoices.ADVANCED) + needsid = models.IntegerField(verbose_name='是否需要sid', choices=JqrSendGroupMsgNeedSidChoices.choices, + default=JqrSendGroupMsgNeedSidChoices.NOT_NEED) + timetype = models.IntegerField(verbose_name='发送时间类型', choices=JqrSendGroupMsgTimeTypeChoices.choices, + default=None) + timejson = models.JSONField(verbose_name='发送时间对象', default=None) + sendtime = models.DateTimeField(verbose_name='下次具体发送时间', null=True, blank=True) + type = models.IntegerField(verbose_name='类型', choices=JqrTimeSendGroupMsgTypeChoices.choices, + default=JqrTimeSendGroupMsgTypeChoices.PRIVATE) + + class Meta: + db_table = 'jqr_timeprivatesendgroupmsg' + verbose_name = 'JQR Time Private Send Group Message' + verbose_name_plural = 'JQR Time Private Send Group Messages' + ordering = ['-ctime'] + + def __str__(self): + return self.taskname + + +class JqrTimeQunSendGroupMsg(JqrBaseSendMsg): + sendtype = models.IntegerField(verbose_name='发送类型,1=高级群发,2=极速群发', + choices=JqrSendGroupMsgSendTypeChoices.choices, + default=JqrSendGroupMsgSendTypeChoices.ADVANCED) + needsid = models.IntegerField(verbose_name='是否需要sid', choices=JqrSendGroupMsgNeedSidChoices.choices, + default=JqrSendGroupMsgNeedSidChoices.NOT_NEED) + timetype = models.IntegerField(verbose_name='发送时间类型', choices=JqrSendGroupMsgTimeTypeChoices.choices, + default=None) + timejson = models.JSONField(verbose_name='发送时间对象', default=None) + sendtime = models.DateTimeField(verbose_name='下次具体发送时间', null=True, blank=True) + + class Meta: + db_table = 'jqr_timequnsendgroupmsg' + verbose_name = 'JQR Time Private Send Group Message' + verbose_name_plural = 'JQR Time Private Send Group Messages' + ordering = ['-ctime'] + + def __str__(self): + return self.taskname + + +class JqrKeywordSendGroupMsg(JqrBaseSendMsg): + type = models.IntegerField(verbose_name='类型', choices=JqrKeywordSendGroupMsgTypeChoices.choices, + default=JqrKeywordSendGroupMsgTypeChoices.PRIVATE) + pattern = models.IntegerField(verbose_name='匹配规则', null=True, blank=True, + choices=JqrKeywordSendGroupMsgPatternChoices.choices, + default=JqrKeywordSendGroupMsgPatternChoices.ANY) + keyword_text = models.TextField(verbose_name='关键字数组', null=True, blank=True) + + class Meta: + db_table = 'jqr_keywordsendgroupmsg' + verbose_name = 'JQR Keyword Send Group Message' + verbose_name_plural = 'JQR Keyword Send Group Messages' + ordering = ['-ctime'] + + +class JqrGroupMsgList(models.Model): + corpid = models.CharField(max_length=32, verbose_name='企业id', db_index=True) + msgid = models.CharField(max_length=255, verbose_name='消息id', db_index=True) + chat_type = models.CharField(max_length=32, verbose_name='群发任务的类型', + default=JqrGroupMsgListChatTypeChoices.SINGLE, + choices=JqrGroupMsgListChatTypeChoices.choices) + filter_type = models.IntegerField( + verbose_name='创建人类型。0:企业发表 1:个人发表 2:所有,包括个人创建以及企业创建,默认情况下为所有类型', + choices=JqrGroupMsgListFilterTypeChoices.choices) + creator = models.CharField(max_length=255, verbose_name='创建人') + create_time = models.BigIntegerField(verbose_name='消息创建时间') + create_type = models.IntegerField(verbose_name='创建类型: 0:企业 1:个人', + choices=JqrGroupMsgListCreateTypeChoices.choices) + text = models.JSONField(verbose_name='文本消息内容', null=True, blank=True) + attachments = models.JSONField(verbose_name='附件消息内容', null=True, blank=True) + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + + class Meta: + db_table = 'jqr_groupmsglist' + verbose_name = '群发记录列表' + verbose_name_plural = '群发记录列表' + ordering = ['-create_time'] + unique_together = ('corpid', 'msgid') + + +class JqrGroupMsgTask(models.Model): + corpid = models.CharField(max_length=32, verbose_name='企业id', db_index=True) + msgid = models.CharField(max_length=255, verbose_name='消息id', db_index=True) + userid = models.CharField(max_length=255, verbose_name='接粉号Id') + status = models.IntegerField(verbose_name='发送状态:0-未发送 2-已发送', + choices=JqrGroupMsgTaskStatusChoices.choices) + send_time = models.BigIntegerField(verbose_name='发送时间', null=True, blank=True) + chat_type = models.CharField(max_length=32, verbose_name='群发任务的类型', + default=JqrGroupMsgListChatTypeChoices.SINGLE, + choices=JqrGroupMsgListChatTypeChoices.choices) + is_handled = models.BooleanField(verbose_name='是否已处理', default=False) + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + + class Meta: + db_table = 'jqr_groupmsgtask' + verbose_name = '群发任务' + verbose_name_plural = '群发任务' + ordering = ['-send_time'] + + +class JqrGrouMsgSendGroupMsgResult(models.Model): + corpid = models.CharField(max_length=32, verbose_name='企业id', db_index=True) + msgid = models.CharField(max_length=255, verbose_name='消息id', db_index=True) + userid = models.CharField(max_length=255, verbose_name='接粉号Id') + external_userid = models.CharField(max_length=255, verbose_name='外部联系人Id', null=True, blank=True) + chat_id = models.CharField(max_length=255, verbose_name='群聊id', null=True, blank=True) + status = models.IntegerField( + verbose_name='发送状态:0-未发送 1-已发送 2-因客户不是好友导致发送失败 3-因客户已经收到其他群发消息导致发送失败', + choices=JqrGrouMsgSendGroupMsgStatusChoices.choices, + null=True, blank=True) + send_time = models.BigIntegerField(verbose_name='发送时间', null=True, blank=True) + task_send_time = models.BigIntegerField(verbose_name='任务发送时间', null=True, blank=True) + chat_type = models.CharField(max_length=32, verbose_name='群发任务的类型', + default=JqrGroupMsgListChatTypeChoices.SINGLE, + choices=JqrGroupMsgListChatTypeChoices.choices) + ctime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + utime = models.DateTimeField(verbose_name='更新时间', auto_now=True) + + class Meta: + db_table = 'jqr_groupmsgsendresult' + verbose_name = '群发成员执行结果' + verbose_name_plural = '群发成员执行结果' + ordering = ['-send_time'] diff --git a/apps/jqr/serializers.py b/apps/jqr/serializers.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/jqr/tasks.py b/apps/jqr/tasks.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/jqr/tests.py b/apps/jqr/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/jqr/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/jqr/urls.py b/apps/jqr/urls.py new file mode 100644 index 0000000..2018cd6 --- /dev/null +++ b/apps/jqr/urls.py @@ -0,0 +1,6 @@ +from rest_framework.routers import SimpleRouter +from . import views + +router = SimpleRouter(trailing_slash=False) + +urlpatterns = router.urls diff --git a/apps/jqr/views.py b/apps/jqr/views.py new file mode 100644 index 0000000..e69de29 diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..72f85a8 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,5 @@ +#!/bin/bash +git pull origin master +docker rm -f yzk_wechat_event-api +docker rm -f yzk_wechat_event-celery_worker +docker compose up --build -d diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d6c679e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,30 @@ +version: "3.9" + +services: + api: + container_name: yzk_wechat_event-api + build: + context: . + dockerfile: ./Dockerfile + command: ./start + volumes: + - .:/app + ports: + - "8001:8000" + networks: + - yzk_wechat_event + + celery_worker: + container_name: yzk_wechat_event-celery_worker + build: + context: . + dockerfile: ./Dockerfile + command: ./start-celeryworker + volumes: + - .:/app + networks: + - yzk_wechat_event + +networks: + yzk_wechat_event: + driver: bridge diff --git a/libs/__init__.py b/libs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/dingding/dingding.py b/libs/dingding/dingding.py new file mode 100644 index 0000000..b914ed5 --- /dev/null +++ b/libs/dingding/dingding.py @@ -0,0 +1,48 @@ +import base64 +import hashlib +import hmac +import logging +import time +import urllib.parse + +import requests + +logger = logging.getLogger('apps') + + +def get_timestamp_and_sign(secret: str): + timestamp = str(round(time.time() * 1000)) + secret_enc = secret.encode('utf-8') + string_to_sign = f'{timestamp}\n{secret}' + string_to_sign_enc = string_to_sign.encode('utf-8') + hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() + sign = urllib.parse.quote_plus(base64.b64encode(hmac_code)) + return timestamp, sign + + +class DingDing(object): + + @classmethod + def send_message(cls, text, webhook, secret): + headers = { + "Content-Type": "application/json", + "Charset": "UTF-8" + } + # 构建请求数据 + data = { + "msgtype": "text", + "text": { + "content": text + }, + "at": { + "isAtAll": True + } + } + + timestamp, sign = get_timestamp_and_sign(secret) + url = f'{webhook}×tamp={timestamp}&sign={sign}' + + res = requests.post(json=data, url=url, headers=headers) + logger.info(f'钉钉消息发送--->{res.json()}') + res = res.json() + return res.get('errcode') == 0 diff --git a/libs/kz/__init__.py b/libs/kz/__init__.py new file mode 100644 index 0000000..6279f7f --- /dev/null +++ b/libs/kz/__init__.py @@ -0,0 +1 @@ +from .kz import KZ diff --git a/libs/kz/kz.py b/libs/kz/kz.py new file mode 100644 index 0000000..b25cf55 --- /dev/null +++ b/libs/kz/kz.py @@ -0,0 +1,79 @@ +import hashlib +import json +import logging +import urllib.parse + +from django.conf import settings +import requests + +loger = logging.getLogger('apps') + + +class KZ: + def __init__(self, appkey=settings.KZ.get('appkey'), secret=settings.KZ.get('secret')): + self.appkey = appkey + self.secret = secret + self.base_url = 'https://cloud.kuaizhan.com/api' + + def sign_top_request(self, params, secret=None): + secret = secret or self.secret + # 第一步:检查参数是否已经排序 + keys = sorted(params.keys()) + # 第二步:把所有参数名和参数值串在一起 + query = secret + for key in keys: + value = params[key] + if isinstance(value, bool): + value = json.dumps(value) + if key and str(key).strip() and value and str(value).strip() and key != "sign": + query += key + str(value) + query += secret + # 第三步:使用MD5加密 + return hashlib.md5(query.encode('utf-8')).hexdigest() + + def gen_short_link( + self, + link, + appkey=None, + response_format='json', + callback=None, + expire=None, + domain=None, + use_domain_pool=None, + ): + link = urllib.parse.quote(link) + params = { + 'appKey': appkey or self.appkey, + 'link': link, + 'format': response_format, + 'callback': callback, + 'expire': expire, + 'domain': domain, + 'useDomainPool': use_domain_pool, + } + sign = self.sign_top_request(params, secret=self.secret) + params['sign'] = sign + url = f'{self.base_url}/v1/km/genShortLink' + res = requests.get(url, params) + data = res.json() + if data.get('code') == 0: + return True, data.get('url') + loger.error(f'获取快码失败---->{data}') + return False, data + + def change_domain_https_forward(self, site_id=8081085619, https_forward=True, domain='elmbwc.kuaizhan.com'): + """ + API-开启域名Https跳转 + """ + data = { + 'appKey': self.appkey, + 'siteId': site_id, + 'httpsForward': https_forward, + 'domain': domain + } + sign = self.sign_top_request(data) + data['sign'] = sign + url = f'{self.base_url}/v1/tbk/changeDomainHttpsForward' + res = requests.post(url, data=data) + print(res.json()) + diff --git a/libs/topsdk/__init__.py b/libs/topsdk/__init__.py new file mode 100644 index 0000000..106a6ad --- /dev/null +++ b/libs/topsdk/__init__.py @@ -0,0 +1 @@ +from .top import TopUtils diff --git a/libs/topsdk/ability132/__init__.py b/libs/topsdk/ability132/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/topsdk/ability132/ability132.py b/libs/topsdk/ability132/ability132.py new file mode 100644 index 0000000..cc6f89b --- /dev/null +++ b/libs/topsdk/ability132/ability132.py @@ -0,0 +1,52 @@ +from libs.topsdk.client import TopApiClient, BaseRequest + +class Ability132: + + def __init__(self, client: TopApiClient): + self._client = client + + """ + 批量发送消息 + """ + def taobao_tmc_messages_produce(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 获取自定义用户分组列表 + """ + def taobao_tmc_groups_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 删除指定的分组或分组下的用户 + """ + def taobao_tmc_group_delete(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 为已开通用户添加用户分组 + """ + def taobao_tmc_group_add(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 删除消息topic分组路由 + """ + def taobao_tmc_topic_group_delete(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + topic分组路由 + """ + def taobao_tmc_topic_group_add(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 确认消费消息的状态 + """ + def taobao_tmc_messages_confirm(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 消费多条消息 + """ + def taobao_tmc_messages_consume(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + TMC授权token + """ + def taobao_tmc_auth_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) diff --git a/libs/topsdk/ability132/request/__init__.py b/libs/topsdk/ability132/request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/topsdk/ability132/request/taobao_tmc_auth_get_request.py b/libs/topsdk/ability132/request/taobao_tmc_auth_get_request.py new file mode 100644 index 0000000..2c561c2 --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_auth_get_request.py @@ -0,0 +1,43 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcAuthGetRequest(BaseRequest): + + def __init__( + self, + group: str = None + ): + """ + tmc组名 + """ + self._group = group + + @property + def group(self): + return self._group + + @group.setter + def group(self, group): + if isinstance(group, str): + self._group = group + else: + raise TypeError("group must be str") + + + def get_api_name(self): + return "taobao.tmc.auth.get" + + def to_dict(self): + request_dict = {} + if self._group is not None: + request_dict["group"] = convert_basic(self._group) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/ability132/request/taobao_tmc_group_add_request.py b/libs/topsdk/ability132/request/taobao_tmc_group_add_request.py new file mode 100644 index 0000000..c727e1c --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_group_add_request.py @@ -0,0 +1,81 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcGroupAddRequest(BaseRequest): + + def __init__( + self, + group_name: str = None, + nicks: list = None, + user_platform: str = None + ): + """ + 分组名称,同一个应用下需要保证唯一性,最长32个字符。添加分组后,消息通道会为用户的消息分配独立分组,但之前的消息还是存储于默认分组中。不能以default开头,default开头为系统默认组。 + """ + self._group_name = group_name + """ + 用户昵称列表,以半角逗号分隔,支持子账号,支持增量添加用户 + """ + self._nicks = nicks + """ + 用户所属于的平台类型,tbUIC:淘宝用户; icbu: icbu用户;ae:ae用户 + """ + self._user_platform = user_platform + + @property + def group_name(self): + return self._group_name + + @group_name.setter + def group_name(self, group_name): + if isinstance(group_name, str): + self._group_name = group_name + else: + raise TypeError("group_name must be str") + + @property + def nicks(self): + return self._nicks + + @nicks.setter + def nicks(self, nicks): + if isinstance(nicks, list): + self._nicks = nicks + else: + raise TypeError("nicks must be list") + + @property + def user_platform(self): + return self._user_platform + + @user_platform.setter + def user_platform(self, user_platform): + if isinstance(user_platform, str): + self._user_platform = user_platform + else: + raise TypeError("user_platform must be str") + + + def get_api_name(self): + return "taobao.tmc.group.add" + + def to_dict(self): + request_dict = {} + if self._group_name is not None: + request_dict["group_name"] = convert_basic(self._group_name) + + if self._nicks is not None: + request_dict["nicks"] = convert_basic_list(self._nicks) + + if self._user_platform is not None: + request_dict["user_platform"] = convert_basic(self._user_platform) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/ability132/request/taobao_tmc_group_delete_request.py b/libs/topsdk/ability132/request/taobao_tmc_group_delete_request.py new file mode 100644 index 0000000..b279619 --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_group_delete_request.py @@ -0,0 +1,81 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcGroupDeleteRequest(BaseRequest): + + def __init__( + self, + group_name: str = None, + nicks: list = None, + user_platform: str = None + ): + """ + 分组名称,分组删除后,用户的消息将会存储于默认分组中。警告:由于分组已经删除,用户之前未消费的消息将无法再获取。不能以default开头,default开头为系统默认组。 + """ + self._group_name = group_name + """ + 用户列表,不传表示删除整个分组,如果用户全部删除后,也会自动删除整个分组 + """ + self._nicks = nicks + """ + 用户所属于的平台类型,tbUIC:淘宝用户; icbu: icbu用户;ae:ae用户 + """ + self._user_platform = user_platform + + @property + def group_name(self): + return self._group_name + + @group_name.setter + def group_name(self, group_name): + if isinstance(group_name, str): + self._group_name = group_name + else: + raise TypeError("group_name must be str") + + @property + def nicks(self): + return self._nicks + + @nicks.setter + def nicks(self, nicks): + if isinstance(nicks, list): + self._nicks = nicks + else: + raise TypeError("nicks must be list") + + @property + def user_platform(self): + return self._user_platform + + @user_platform.setter + def user_platform(self, user_platform): + if isinstance(user_platform, str): + self._user_platform = user_platform + else: + raise TypeError("user_platform must be str") + + + def get_api_name(self): + return "taobao.tmc.group.delete" + + def to_dict(self): + request_dict = {} + if self._group_name is not None: + request_dict["group_name"] = convert_basic(self._group_name) + + if self._nicks is not None: + request_dict["nicks"] = convert_basic_list(self._nicks) + + if self._user_platform is not None: + request_dict["user_platform"] = convert_basic(self._user_platform) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/ability132/request/taobao_tmc_groups_get_request.py b/libs/topsdk/ability132/request/taobao_tmc_groups_get_request.py new file mode 100644 index 0000000..68c8730 --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_groups_get_request.py @@ -0,0 +1,81 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcGroupsGetRequest(BaseRequest): + + def __init__( + self, + group_names: list = None, + page_no: int = None, + page_size: int = None + ): + """ + 要查询分组的名称,多个分组用半角逗号分隔,不传代表查询所有分组信息,但不会返回组下面的用户信息。如果应用没有设置分组则返回空。组名不能以default开头,default开头是系统默认的组。 + """ + self._group_names = group_names + """ + 页码 + """ + self._page_no = page_no + """ + 每页返回多少个分组 + """ + self._page_size = page_size + + @property + def group_names(self): + return self._group_names + + @group_names.setter + def group_names(self, group_names): + if isinstance(group_names, list): + self._group_names = group_names + else: + raise TypeError("group_names must be list") + + @property + def page_no(self): + return self._page_no + + @page_no.setter + def page_no(self, page_no): + if isinstance(page_no, int): + self._page_no = page_no + else: + raise TypeError("page_no must be int") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + + def get_api_name(self): + return "taobao.tmc.groups.get" + + def to_dict(self): + request_dict = {} + if self._group_names is not None: + request_dict["group_names"] = convert_basic_list(self._group_names) + + if self._page_no is not None: + request_dict["page_no"] = convert_basic(self._page_no) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/ability132/request/taobao_tmc_messages_confirm_request.py b/libs/topsdk/ability132/request/taobao_tmc_messages_confirm_request.py new file mode 100644 index 0000000..81e9fa4 --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_messages_confirm_request.py @@ -0,0 +1,81 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcMessagesConfirmRequest(BaseRequest): + + def __init__( + self, + group_name: str = None, + s_message_ids: list = None, + f_message_ids: list = None + ): + """ + 分组名称,不传代表默认分组 + """ + self._group_name = group_name + """ + 处理成功的消息ID列表 最大 200个ID + """ + self._s_message_ids = s_message_ids + """ + 处理失败的消息ID列表--已废弃,无需传此字段 + """ + self._f_message_ids = f_message_ids + + @property + def group_name(self): + return self._group_name + + @group_name.setter + def group_name(self, group_name): + if isinstance(group_name, str): + self._group_name = group_name + else: + raise TypeError("group_name must be str") + + @property + def s_message_ids(self): + return self._s_message_ids + + @s_message_ids.setter + def s_message_ids(self, s_message_ids): + if isinstance(s_message_ids, list): + self._s_message_ids = s_message_ids + else: + raise TypeError("s_message_ids must be list") + + @property + def f_message_ids(self): + return self._f_message_ids + + @f_message_ids.setter + def f_message_ids(self, f_message_ids): + if isinstance(f_message_ids, list): + self._f_message_ids = f_message_ids + else: + raise TypeError("f_message_ids must be list") + + + def get_api_name(self): + return "taobao.tmc.messages.confirm" + + def to_dict(self): + request_dict = {} + if self._group_name is not None: + request_dict["group_name"] = convert_basic(self._group_name) + + if self._s_message_ids is not None: + request_dict["s_message_ids"] = convert_basic_list(self._s_message_ids) + + if self._f_message_ids is not None: + request_dict["f_message_ids"] = convert_basic_list(self._f_message_ids) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/ability132/request/taobao_tmc_messages_consume_request.py b/libs/topsdk/ability132/request/taobao_tmc_messages_consume_request.py new file mode 100644 index 0000000..217ff1f --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_messages_consume_request.py @@ -0,0 +1,62 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcMessagesConsumeRequest(BaseRequest): + + def __init__( + self, + group_name: str = None, + quantity: int = None + ): + """ + 用户分组名称,不传表示消费默认分组,如果应用没有设置用户分组,传入分组名称将会返回错误 + """ + self._group_name = group_name + """ + 每次批量消费消息的条数,最小值:10;最大值:200 + """ + self._quantity = quantity + + @property + def group_name(self): + return self._group_name + + @group_name.setter + def group_name(self, group_name): + if isinstance(group_name, str): + self._group_name = group_name + else: + raise TypeError("group_name must be str") + + @property + def quantity(self): + return self._quantity + + @quantity.setter + def quantity(self, quantity): + if isinstance(quantity, int): + self._quantity = quantity + else: + raise TypeError("quantity must be int") + + + def get_api_name(self): + return "taobao.tmc.messages.consume" + + def to_dict(self): + request_dict = {} + if self._group_name is not None: + request_dict["group_name"] = convert_basic(self._group_name) + + if self._quantity is not None: + request_dict["quantity"] = convert_basic(self._quantity) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/ability132/request/taobao_tmc_messages_produce_request.py b/libs/topsdk/ability132/request/taobao_tmc_messages_produce_request.py new file mode 100644 index 0000000..f853006 --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_messages_produce_request.py @@ -0,0 +1,73 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcMessagesProduceRequest(BaseRequest): + + def __init__( + self, + messages: list = None + ): + """ + tmc消息列表, 最多50条,元素结构与taobao.tmc.message.produce一致,用json表示的消息列表。例如:[{"content": "{\"tid\":1234554321,\"status\":\"X_LOGISTICS_PRINTED\",\"action_time\":\"2014-08-08 18:24:00\",\"seller_nick\": \"向阳aa\",\"operator\":\"小张\"}","topic": "taobao_jds_TradeTrace"},{"content": "{\"tid\":1234554321,\"status\":\"X_LOGISTICS_PRINTED\",\"action_time\":\"2014-08-08 18:24:00\",\"seller_nick\": \"向阳aa\",\"operator\":\"小张\"}","topic": "taobao_jds_TradeTrace"}] + """ + self._messages = messages + + @property + def messages(self): + return self._messages + + @messages.setter + def messages(self, messages): + if isinstance(messages, list): + self._messages = messages + else: + raise TypeError("messages must be list") + + + def get_api_name(self): + return "taobao.tmc.messages.produce" + + def to_dict(self): + request_dict = {} + if self._messages is not None: + request_dict["messages"] = convert_struct_list(self._messages) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class TaobaoTmcMessagesProduceTmcPublishMessage: + def __init__( + self, + content: str = None, + json_ex_content: str = None, + target_app_key: str = None, + target_group: str = None, + topic: str = None + ): + """ + 消息内容的JSON表述,必须按照topic的定义来填充 + """ + self.content = content + """ + 消息的扩增属性,用json格式表示 + """ + self.json_ex_content = json_ex_content + """ + 直发消息需要传入目标appkey + """ + self.target_app_key = target_app_key + """ + 目标分组 + """ + self.target_group = target_group + """ + 消息类型 + """ + self.topic = topic + diff --git a/libs/topsdk/ability132/request/taobao_tmc_topic_group_add_request.py b/libs/topsdk/ability132/request/taobao_tmc_topic_group_add_request.py new file mode 100644 index 0000000..c8dbd1e --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_topic_group_add_request.py @@ -0,0 +1,62 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcTopicGroupAddRequest(BaseRequest): + + def __init__( + self, + group_name: str = None, + topics: list = None + ): + """ + 消息分组名,如果不存在,会自动创建 + """ + self._group_name = group_name + """ + 消息topic名称,多个以逗号(,)分割 + """ + self._topics = topics + + @property + def group_name(self): + return self._group_name + + @group_name.setter + def group_name(self, group_name): + if isinstance(group_name, str): + self._group_name = group_name + else: + raise TypeError("group_name must be str") + + @property + def topics(self): + return self._topics + + @topics.setter + def topics(self, topics): + if isinstance(topics, list): + self._topics = topics + else: + raise TypeError("topics must be list") + + + def get_api_name(self): + return "taobao.tmc.topic.group.add" + + def to_dict(self): + request_dict = {} + if self._group_name is not None: + request_dict["group_name"] = convert_basic(self._group_name) + + if self._topics is not None: + request_dict["topics"] = convert_basic_list(self._topics) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/ability132/request/taobao_tmc_topic_group_delete_request.py b/libs/topsdk/ability132/request/taobao_tmc_topic_group_delete_request.py new file mode 100644 index 0000000..5dc1c0d --- /dev/null +++ b/libs/topsdk/ability132/request/taobao_tmc_topic_group_delete_request.py @@ -0,0 +1,81 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcTopicGroupDeleteRequest(BaseRequest): + + def __init__( + self, + group_name: str = None, + group_id: int = None, + topics: list = None + ): + """ + 消息分组名 + """ + self._group_name = group_name + """ + 消息分组Id,一般不用填写,如果分组已经被删除,则根据问题排查工具返回的ID删除路由关系 + """ + self._group_id = group_id + """ + 消息topic名称,多个以逗号(,)分割 + """ + self._topics = topics + + @property + def group_name(self): + return self._group_name + + @group_name.setter + def group_name(self, group_name): + if isinstance(group_name, str): + self._group_name = group_name + else: + raise TypeError("group_name must be str") + + @property + def group_id(self): + return self._group_id + + @group_id.setter + def group_id(self, group_id): + if isinstance(group_id, int): + self._group_id = group_id + else: + raise TypeError("group_id must be int") + + @property + def topics(self): + return self._topics + + @topics.setter + def topics(self, topics): + if isinstance(topics, list): + self._topics = topics + else: + raise TypeError("topics must be list") + + + def get_api_name(self): + return "taobao.tmc.topic.group.delete" + + def to_dict(self): + request_dict = {} + if self._group_name is not None: + request_dict["group_name"] = convert_basic(self._group_name) + + if self._group_id is not None: + request_dict["group_id"] = convert_basic(self._group_id) + + if self._topics is not None: + request_dict["topics"] = convert_basic_list(self._topics) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/client.py b/libs/topsdk/client.py new file mode 100644 index 0000000..8d7ee69 --- /dev/null +++ b/libs/topsdk/client.py @@ -0,0 +1,132 @@ +from datetime import datetime +from urllib.parse import urlencode +from abc import ABC, abstractmethod +import requests +import json + +from libs.topsdk.util import get_sign + +P_APPKEY = "app_key" +P_METHOD = "method" +P_SESSION = "session" +P_VERSION = "v" +P_FORMAT = "format" +P_TIMESTAMP = "timestamp" +P_SIGN = "sign" +P_SIGN_METHOD = "sign_method" +P_PARTNER_ID = "partner_id" +P_SIMPLIFY = "simplify" + +P_CODE = "code" +P_SUB_CODE = "sub_code" +P_MSG = "msg" +P_SUB_MSG = "sub_msg" +P_REQUEST_ID = "request_id" + + +class TopApiClient: + + def __init__(self, appkey: str, app_sercet: str, top_gateway_url: str, simplify: bool = True, timeout=10, + proxy=None, verify_ssl=True): + self.appkey = appkey + self.app_sercet = app_sercet + self.top_gateway_url = top_gateway_url + self.format = "json" + self.version = "2.0" + self.sign_method = "hmac-sha256" + self.simplify = simplify + self.timeout = timeout + self.proxy = proxy + self.verify_ssl = verify_ssl + + def execute(self, api_code: str, request_dict: dict, file_dict: dict): + return self.execute_with_session(api_code, request_dict, file_dict, "") + + def execute_with_session(self, api_code: str, request_dict: dict, file_dict: dict, session: str): + public_param = { + P_METHOD: api_code, + P_APPKEY: self.appkey, + P_FORMAT: self.format, + P_VERSION: self.version, + P_TIMESTAMP: datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + P_SIGN_METHOD: self.sign_method, + P_SIMPLIFY: str(self.simplify).lower(), + P_PARTNER_ID: "new_python3_sdk" + } + if session: + public_param[P_SESSION] = session + sign = get_sign(public_param.copy(), request_dict, self.app_sercet, self.sign_method) + public_param[P_SIGN] = sign + url = self.top_gateway_url + "?" + urlencode(public_param) + if file_dict: + response = requests.post(url, data=request_dict, files=file_dict, timeout=self.timeout, proxies=self.proxy, + verify=self.verify_ssl) + else: + response = requests.post(url, data=request_dict, timeout=self.timeout, proxies=self.proxy, + verify=self.verify_ssl) + if response.status_code != 200: + raise Exception('invalid http status ' + str(response.status_code) + ',detail body:' + response.text) + jsonobj = json.loads(response.text) + if "error_response" in jsonobj: + error = TopException() + if P_CODE in jsonobj["error_response"]: + error.top_code = jsonobj["error_response"][P_CODE] + if P_MSG in jsonobj["error_response"]: + error.msg = jsonobj["error_response"][P_MSG] + if P_SUB_CODE in jsonobj["error_response"]: + error.sub_code = jsonobj["error_response"][P_SUB_CODE] + if P_SUB_MSG in jsonobj["error_response"]: + error.sub_msg = jsonobj["error_response"][P_SUB_MSG] + if P_REQUEST_ID in jsonobj["error_response"]: + error.request_id = jsonobj["error_response"][P_REQUEST_ID] + raise error + return jsonobj + + +class TopException(Exception): + # =========================================================================== + # 业务异常类 + # =========================================================================== + def __init__(self): + self.top_code = None + self.msg = None + self.sub_code = None + self.sub_msg = None + self.request_id = None + + def mix_str(self, pstr): + if (isinstance(pstr, str)): + return pstr + elif (isinstance(pstr, str)): + return pstr.encode('utf-8') + else: + return str(pstr) + + def __str__(self, *args, **kwargs) -> str: + sb = "top_code=" + self.mix_str(self.top_code) + \ + " msg=" + self.mix_str(self.msg) + \ + " sub_code=" + self.mix_str(self.sub_code) + \ + " sub_msg=" + self.mix_str(self.sub_msg) + \ + " request_id=" + self.mix_str(self.request_id) + return sb + + +class BaseRequest(ABC): + + @abstractmethod + def to_dict(self): + """ + TOPAPI Request类需要实现此方法, 转换Request对象为dict + """ + + @abstractmethod + def get_api_name(self): + """ + TOPAPI Request类需要实现此方法, 获取api名称 + """ + + @abstractmethod + def get_file_param_dict(self): + """ + TOPAPI Request类需要实现此方法, 获取文件类型dict + """ diff --git a/libs/topsdk/defaultability/__init__.py b/libs/topsdk/defaultability/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/topsdk/defaultability/defaultability.py b/libs/topsdk/defaultability/defaultability.py new file mode 100644 index 0000000..00514d7 --- /dev/null +++ b/libs/topsdk/defaultability/defaultability.py @@ -0,0 +1,187 @@ +from libs.topsdk.client import TopApiClient, BaseRequest + +class Defaultability: + + def __init__(self, client: TopApiClient): + self._client = client + + """ + 本地生活爆爆团选品筛选集合 + """ + def alibaba_alsc_union_kb_bbt_item_promotion_filter_list(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟爆爆团商品详情 + """ + def alibaba_alsc_union_kb_bbt_item_detail_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟爆爆团商品门店关系 + """ + def alibaba_alsc_union_kb_bbt_item_store_relation_query(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟爆爆团门店详情 + """ + def alibaba_alsc_union_kb_bbt_item_store_detail_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体平台口碑选品筛选项集合 + """ + def alibaba_alsc_union_kb_item_promotion_filter_list(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体推广位查询 + """ + def alibaba_alsc_union_media_zone_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体平台口碑选品 + """ + def alibaba_alsc_union_kb_item_promotion(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 加密方法 + """ + def alibaba_alsc_union_kb_common_encrypt(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体推广位创建 + """ + def alibaba_alsc_union_media_zone_add(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟口碑商品列表 + """ + def alibaba_alsc_union_kb_item_query(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + openapi预下单订单支付 + """ + def alibaba_alsc_union_kb_order_pay(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体推广口碑CPA用户反作弊订单数据报表 + """ + def alibaba_alsc_union_kbcpa_punish_order_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟媒体出资活动红包发放 + """ + def alibaba_alsc_union_eleme_media_activity_coupon_send(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 获取用户已开通消息 + """ + def taobao_tmc_user_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟饿了么单店推广店铺列表 + """ + def alibaba_alsc_union_eleme_promotion_storepromotion_query(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟饿了么单店推广单店铺查询 + """ + def alibaba_alsc_union_eleme_promotion_storepromotion_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟饿了么推广官方活动查询 + """ + def alibaba_alsc_union_eleme_promotion_officialactivity_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 发布单条消息 + """ + def taobao_tmc_message_produce(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体推广口碑CPA用户维权订单数据报表 + """ + def alibaba_alsc_union_kbcpa_refund_order_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟口碑商户列表 + """ + def alibaba_alsc_union_kb_store_query(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体创建商品推广链接 + """ + def alibaba_alsc_union_kb_item_promotion_share_create(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟口碑门店商品列表 + """ + def alibaba_alsc_union_kb_store_item_query(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 取消用户的消息服务 + """ + def taobao_tmc_user_cancel(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟爆爆团商品列表 + """ + def alibaba_alsc_union_kb_bbt_item_query(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 为已授权的用户开通消息服务 + """ + def taobao_tmc_user_permit(self, request: BaseRequest, session: str): + return self._client.execute_with_session(request.get_api_name(), request.to_dict(), request.get_file_param_dict(), session) + """ + openapi订单创建 + """ + def alibaba_alsc_union_kb_order_create(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + openapi订单查询 + """ + def alibaba_alsc_union_kb_order_query(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + openapi订单售中退 + """ + def alibaba_alsc_union_kb_order_refund(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟推广链接推广对象解析 + """ + def alibaba_alsc_union_promotion_link_analyze(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体推广订单明细查询 + """ + def alibaba_alsc_union_kbcpa_order_details_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟口碑商品详情 + """ + def alibaba_alsc_union_kb_item_detail_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟口碑商品门店关系 + """ + def alibaba_alsc_union_kb_item_store_relation_query(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体推广订单明细报表查询 + """ + def alibaba_alsc_union_kbcpx_positive_order_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地联盟口碑门店详情 + """ + def alibaba_alsc_union_kb_item_store_detail_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体推广用户维权订单数据报表 + """ + def alibaba_alsc_union_kbcpx_refund_order_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) + """ + 本地生活媒体推广用户反作弊订单数据报表 + """ + def alibaba_alsc_union_kbcpx_punish_order_get(self, request: BaseRequest): + return self._client.execute(request.get_api_name(), request.to_dict(), request.get_file_param_dict()) diff --git a/libs/topsdk/defaultability/request/__init__.py b/libs/topsdk/defaultability/request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_media_activity_coupon_send_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_media_activity_coupon_send_request.py new file mode 100644 index 0000000..ad7f2af --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_media_activity_coupon_send_request.py @@ -0,0 +1,58 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionElemeMediaActivityCouponSendRequest(BaseRequest): + + def __init__( + self, + send_request: object = None + ): + """ + 请求对象 + """ + self._send_request = send_request + + @property + def send_request(self): + return self._send_request + + @send_request.setter + def send_request(self, send_request): + if isinstance(send_request, object): + self._send_request = send_request + else: + raise TypeError("send_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.eleme.media.activity.coupon.send" + + def to_dict(self): + request_dict = {} + if self._send_request is not None: + request_dict["send_request"] = convert_struct(self._send_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionElemeMediaActivityCouponSendMediaActivityCouponSendRequest: + def __init__( + self, + mobile: str = None, + media_activity_id: str = None + ): + """ + 领券手机号 + """ + self.mobile = mobile + """ + 媒体出资活动ID + """ + self.media_activity_id = media_activity_id + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_officialactivity_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_officialactivity_get_request.py new file mode 100644 index 0000000..1e9fe67 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_officialactivity_get_request.py @@ -0,0 +1,73 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionElemePromotionOfficialactivityGetRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 查询rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.eleme.promotion.officialactivity.get" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionElemePromotionOfficialactivityGetActivityRequest(AlibabaAlscUnionElemePromotionOfficialactivityGetRequest): + def __init__( + self, + pid: str = None, + activity_id: str = None, + sid: str = None, + include_wx_img: bool = None, + include_qr_code: bool = None + ): + """ + 渠道PID + """ + self.pid = pid + """ + 活动ID + """ + self.activity_id = activity_id + """ + 三方会员id。长度限制50 + """ + self.sid = sid + """ + 是否返回微信推广图片 + """ + self.include_wx_img = include_wx_img + """ + 是否包含二维码,如果为false,不返回二维码和图片,只有链接 + """ + self.include_qr_code = include_qr_code + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_storepromotion_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_storepromotion_get_request.py new file mode 100644 index 0000000..0d436c0 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_storepromotion_get_request.py @@ -0,0 +1,78 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionElemePromotionStorepromotionGetRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 查询rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.eleme.promotion.storepromotion.get" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionElemePromotionStorepromotionGetSingleStorePromotionRequest: + def __init__( + self, + pid: str = None, + shop_id: str = None, + activity_id: str = None, + sid: str = None, + include_wx_img: bool = None, + media_activity_id: str = None + ): + """ + 渠道PID + """ + self.pid = pid + """ + 门店ID(加密,具有时效性,建议每天更新一次) + """ + self.shop_id = shop_id + """ + 活动ID + """ + self.activity_id = activity_id + """ + 三方扩展id + """ + self.sid = sid + """ + 是否返回微信推广图片 + """ + self.include_wx_img = include_wx_img + """ + 媒体出资活动ID + """ + self.media_activity_id = media_activity_id + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_storepromotion_query_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_storepromotion_query_request.py new file mode 100644 index 0000000..faea9ac --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_eleme_promotion_storepromotion_query_request.py @@ -0,0 +1,143 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionElemePromotionStorepromotionQueryRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 查询rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.eleme.promotion.storepromotion.query" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionElemePromotionStorepromotionQueryPromotionQueryRequest: + def __init__( + self, + session_id: str = None, + pid: str = None, + longitude: str = None, + latitude: str = None, + city_id: str = None, + sort_type: str = None, + in_activity: bool = None, + has_bonus_stock: bool = None, + min_commission_rate: str = None, + page_size: int = None, + sid: str = None, + biz_type: str = None, + recall_overlay_coupon: bool = None, + filter_has_overlay_coupon: bool = None, + min_overlay_coupon_amount: str = None, + filter_first_categories: str = None, + filter_one_point_five_categories: str = None, + media_activity_id: str = None, + search_content: str = None + ): + """ + 会话ID(分页场景首次请求结果返回,后续请求必须携带,服务根据session_id相同请求次数自动翻页返回) + """ + self.session_id = session_id + """ + 渠道PID + """ + self.pid = pid + """ + 经度 + """ + self.longitude = longitude + """ + 纬度 + """ + self.latitude = latitude + """ + 城市编码(只用于经纬度覆盖多个城市时过滤) + """ + self.city_id = city_id + """ + 排序类型,默认normal,排序规则包括:{"normal":"佣金倒序","distance":"距离由近到远","commission":"佣金倒序","monthlySale":"月销量","couponAmount":"叠加券金额倒序","activityReward":"奖励金金额倒序","commissionRate":"佣金比例倒序"} + """ + self.sort_type = sort_type + """ + 是否参与奖励金活动(默认false不做过滤) + """ + self.in_activity = in_activity + """ + 否当前有c端奖励金活动库存(默认false不做过滤) + """ + self.has_bonus_stock = has_bonus_stock + """ + 店铺佣金比例下限,代表筛选店铺全店佣金大于等于0.01的店铺 + """ + self.min_commission_rate = min_commission_rate + """ + 每页数量(1~20,默认20) + """ + self.page_size = page_size + """ + 三方扩展id + """ + self.sid = sid + """ + 指定召回供给枚举 + """ + self.biz_type = biz_type + """ + in_activity=false的条件下,召回的非奖励金活动cps商家是否需要带出叠加券 + """ + self.recall_overlay_coupon = recall_overlay_coupon + """ + filter_has_overlay_coupon=true的条件下,限定只召回带叠加券的cps商户 + """ + self.filter_has_overlay_coupon = filter_has_overlay_coupon + """ + filter_has_overlay_coupon=true的情况下,限定的最小叠加券券金额,单位元 + """ + self.min_overlay_coupon_amount = min_overlay_coupon_amount + """ + 以一级类目进行类目限定,以,或者|进行类目分隔 + """ + self.filter_first_categories = filter_first_categories + """ + 1.5级类目查询,以"|"分隔 + """ + self.filter_one_point_five_categories = filter_one_point_five_categories + """ + 媒体出资活动ID + """ + self.media_activity_id = media_activity_id + """ + 检索内容(支持门店名称) + """ + self.search_content = search_content + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_detail_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_detail_get_request.py new file mode 100644 index 0000000..8549a47 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_detail_get_request.py @@ -0,0 +1,58 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbBbtItemDetailGetRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 爆爆团商品详情rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.bbt.item.detail.get" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbBbtItemDetailGetBbtItemDetailRequest: + def __init__( + self, + item_id: str = None, + city_id: str = None + ): + """ + 品ID + """ + self.item_id = item_id + """ + 城市ID + """ + self.city_id = city_id + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_promotion_filter_list_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_promotion_filter_list_request.py new file mode 100644 index 0000000..006f95f --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_promotion_filter_list_request.py @@ -0,0 +1,81 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbBbtItemPromotionFilterListRequest(BaseRequest): + + def __init__( + self, + filter_type: str = None, + biz_type: str = None, + biz_unit: int = None + ): + """ + 获取筛选项集合的类型。category类目列表,city城市列表 + """ + self._filter_type = filter_type + """ + 产品线,固定bbt + """ + self._biz_type = biz_type + """ + 固定2cps + """ + self._biz_unit = biz_unit + + @property + def filter_type(self): + return self._filter_type + + @filter_type.setter + def filter_type(self, filter_type): + if isinstance(filter_type, str): + self._filter_type = filter_type + else: + raise TypeError("filter_type must be str") + + @property + def biz_type(self): + return self._biz_type + + @biz_type.setter + def biz_type(self, biz_type): + if isinstance(biz_type, str): + self._biz_type = biz_type + else: + raise TypeError("biz_type must be str") + + @property + def biz_unit(self): + return self._biz_unit + + @biz_unit.setter + def biz_unit(self, biz_unit): + if isinstance(biz_unit, int): + self._biz_unit = biz_unit + else: + raise TypeError("biz_unit must be int") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.bbt.item.promotion.filter.list" + + def to_dict(self): + request_dict = {} + if self._filter_type is not None: + request_dict["filter_type"] = convert_basic(self._filter_type) + + if self._biz_type is not None: + request_dict["biz_type"] = convert_basic(self._biz_type) + + if self._biz_unit is not None: + request_dict["biz_unit"] = convert_basic(self._biz_unit) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_query_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_query_request.py new file mode 100644 index 0000000..9e10d1b --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_query_request.py @@ -0,0 +1,88 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbBbtItemQueryRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 爆爆团商品查询rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.bbt.item.query" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbBbtItemQueryBbtItemQueryRequest: + def __init__( + self, + page_number: int = None, + sort_type: int = None, + session_id: str = None, + category2_id: str = None, + page_size: int = None, + city_id: str = None, + include_phone: bool = None, + tripartite_appkeys: str = None + ): + """ + 页码 + """ + self.page_number = page_number + """ + 排序类型(0-时间倒序,1-佣金比例倒序) + """ + self.sort_type = sort_type + """ + 会话ID + """ + self.session_id = session_id + """ + 二级类目ID + """ + self.category2_id = category2_id + """ + 每页数目 + """ + self.page_size = page_size + """ + 城市ID + """ + self.city_id = city_id + """ + 是否返回需要手机号的商品,false仅返回不需要手机号的品;true全部返回 + """ + self.include_phone = include_phone + """ + 三方供给标识,","隔开,不为空时include_phone必须为true + """ + self.tripartite_appkeys = tripartite_appkeys + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_store_detail_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_store_detail_get_request.py new file mode 100644 index 0000000..7d47848 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_store_detail_get_request.py @@ -0,0 +1,53 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbBbtItemStoreDetailGetRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 门店详情查询rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.bbt.item.store.detail.get" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbBbtItemStoreDetailGetBbtItemShopDetailRequest: + def __init__( + self, + store_id: str = None + ): + """ + 门店ID + """ + self.store_id = store_id + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_store_relation_query_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_store_relation_query_request.py new file mode 100644 index 0000000..c2b7924 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_bbt_item_store_relation_query_request.py @@ -0,0 +1,58 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbBbtItemStoreRelationQueryRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 商品门店关系查询rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.bbt.item.store.relation.query" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbBbtItemStoreRelationQueryBbtItemShopRelationRequest: + def __init__( + self, + item_id: str = None, + city_id: str = None + ): + """ + 商品ID + """ + self.item_id = item_id + """ + 城市ID + """ + self.city_id = city_id + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_common_encrypt_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_common_encrypt_request.py new file mode 100644 index 0000000..ac14eae --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_common_encrypt_request.py @@ -0,0 +1,53 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbCommonEncryptRequest(BaseRequest): + + def __init__( + self, + encrypt_model: object = None + ): + """ + 待加密对象 + """ + self._encrypt_model = encrypt_model + + @property + def encrypt_model(self): + return self._encrypt_model + + @encrypt_model.setter + def encrypt_model(self, encrypt_model): + if isinstance(encrypt_model, object): + self._encrypt_model = encrypt_model + else: + raise TypeError("encrypt_model must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.common.encrypt" + + def to_dict(self): + request_dict = {} + if self._encrypt_model is not None: + request_dict["encrypt_model"] = convert_struct(self._encrypt_model) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbCommonEncryptBlowfishModel: + def __init__( + self, + text: str = None + ): + """ + 待加密字符串 + """ + self.text = text + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_detail_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_detail_get_request.py new file mode 100644 index 0000000..3aab310 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_detail_get_request.py @@ -0,0 +1,63 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbItemDetailGetRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 商品详情rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.item.detail.get" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbItemDetailGetKbItemDetailRequest: + def __init__( + self, + item_id: str = None, + city_id: str = None, + biz_type: str = None + ): + """ + 商品ID + """ + self.item_id = item_id + """ + 城市ID + """ + self.city_id = city_id + """ + 业务类型(cps/cpa) + """ + self.biz_type = biz_type + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_filter_list_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_filter_list_request.py new file mode 100644 index 0000000..1b98854 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_filter_list_request.py @@ -0,0 +1,62 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbItemPromotionFilterListRequest(BaseRequest): + + def __init__( + self, + filter_type: str = None, + biz_unit: int = None + ): + """ + 获取筛选项集合的类型 + """ + self._filter_type = filter_type + """ + 1-cpa,2-cps.默认不填为cpa + """ + self._biz_unit = biz_unit + + @property + def filter_type(self): + return self._filter_type + + @filter_type.setter + def filter_type(self, filter_type): + if isinstance(filter_type, str): + self._filter_type = filter_type + else: + raise TypeError("filter_type must be str") + + @property + def biz_unit(self): + return self._biz_unit + + @biz_unit.setter + def biz_unit(self, biz_unit): + if isinstance(biz_unit, int): + self._biz_unit = biz_unit + else: + raise TypeError("biz_unit must be int") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.item.promotion.filter.list" + + def to_dict(self): + request_dict = {} + if self._filter_type is not None: + request_dict["filter_type"] = convert_basic(self._filter_type) + + if self._biz_unit is not None: + request_dict["biz_unit"] = convert_basic(self._biz_unit) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_request.py new file mode 100644 index 0000000..ffa1d59 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_request.py @@ -0,0 +1,252 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbItemPromotionRequest(BaseRequest): + + def __init__( + self, + page_number: int = None, + sort_type: str = None, + page_size: int = None, + pid: str = None, + session_id: str = None, + settle_type: int = None, + filter_category_ids: str = None, + filter_city_ids: str = None, + search_keyword: str = None, + hit_item_ids: str = None, + sid: str = None, + item_type: int = None + ): + """ + 页码,默认第一页,取值范围1~50 + """ + self._page_number = page_number + """ + 排序类型 normal-默认排序 reservePrice-折后价从高到低 commission-佣金从高到低 totalSales-月销量从高到低 + """ + self._sort_type = sort_type + """ + 每页返回数据大小,默认20,最大返回20 + """ + self._page_size = page_size + """ + 推广参数 + """ + self._pid = pid + """ + 用来分页,翻页时将上一次结果的sessionId带下来 + """ + self._session_id = session_id + """ + 推广物料结算模型 1-cpa 2-cps,3spu + """ + self._settle_type = settle_type + """ + 类目筛选,多个类目逗号分隔(通过alibaba.alsc.union.kb.item.promotion.filter.list获取) + """ + self._filter_category_ids = filter_category_ids + """ + 城市id(国标)筛选,多个城市逗号分隔(通过alibaba.alsc.union.kb.item.promotion.filter.list获取) + """ + self._filter_city_ids = filter_city_ids + """ + 关键词搜索,多个词逗号分割 + """ + self._search_keyword = search_keyword + """ + 指定itemId查询推广信息,多个逗号分割 + """ + self._hit_item_ids = hit_item_ids + """ + 第三方会员id扩展 + """ + self._sid = sid + """ + 商品可售卖的端类型。1支付宝端商品,2微信端商品,3全部 + """ + self._item_type = item_type + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def sort_type(self): + return self._sort_type + + @sort_type.setter + def sort_type(self, sort_type): + if isinstance(sort_type, str): + self._sort_type = sort_type + else: + raise TypeError("sort_type must be str") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + @property + def session_id(self): + return self._session_id + + @session_id.setter + def session_id(self, session_id): + if isinstance(session_id, str): + self._session_id = session_id + else: + raise TypeError("session_id must be str") + + @property + def settle_type(self): + return self._settle_type + + @settle_type.setter + def settle_type(self, settle_type): + if isinstance(settle_type, int): + self._settle_type = settle_type + else: + raise TypeError("settle_type must be int") + + @property + def filter_category_ids(self): + return self._filter_category_ids + + @filter_category_ids.setter + def filter_category_ids(self, filter_category_ids): + if isinstance(filter_category_ids, str): + self._filter_category_ids = filter_category_ids + else: + raise TypeError("filter_category_ids must be str") + + @property + def filter_city_ids(self): + return self._filter_city_ids + + @filter_city_ids.setter + def filter_city_ids(self, filter_city_ids): + if isinstance(filter_city_ids, str): + self._filter_city_ids = filter_city_ids + else: + raise TypeError("filter_city_ids must be str") + + @property + def search_keyword(self): + return self._search_keyword + + @search_keyword.setter + def search_keyword(self, search_keyword): + if isinstance(search_keyword, str): + self._search_keyword = search_keyword + else: + raise TypeError("search_keyword must be str") + + @property + def hit_item_ids(self): + return self._hit_item_ids + + @hit_item_ids.setter + def hit_item_ids(self, hit_item_ids): + if isinstance(hit_item_ids, str): + self._hit_item_ids = hit_item_ids + else: + raise TypeError("hit_item_ids must be str") + + @property + def sid(self): + return self._sid + + @sid.setter + def sid(self, sid): + if isinstance(sid, str): + self._sid = sid + else: + raise TypeError("sid must be str") + + @property + def item_type(self): + return self._item_type + + @item_type.setter + def item_type(self, item_type): + if isinstance(item_type, int): + self._item_type = item_type + else: + raise TypeError("item_type must be int") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.item.promotion" + + def to_dict(self): + request_dict = {} + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._sort_type is not None: + request_dict["sort_type"] = convert_basic(self._sort_type) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + if self._session_id is not None: + request_dict["session_id"] = convert_basic(self._session_id) + + if self._settle_type is not None: + request_dict["settle_type"] = convert_basic(self._settle_type) + + if self._filter_category_ids is not None: + request_dict["filter_category_ids"] = convert_basic(self._filter_category_ids) + + if self._filter_city_ids is not None: + request_dict["filter_city_ids"] = convert_basic(self._filter_city_ids) + + if self._search_keyword is not None: + request_dict["search_keyword"] = convert_basic(self._search_keyword) + + if self._hit_item_ids is not None: + request_dict["hit_item_ids"] = convert_basic(self._hit_item_ids) + + if self._sid is not None: + request_dict["sid"] = convert_basic(self._sid) + + if self._item_type is not None: + request_dict["item_type"] = convert_basic(self._item_type) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_share_create_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_share_create_request.py new file mode 100644 index 0000000..352d659 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_promotion_share_create_request.py @@ -0,0 +1,214 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbItemPromotionShareCreateRequest(BaseRequest): + + def __init__( + self, + pid: str = None, + item_id: str = None, + biz_unit: int = None, + include_mini_qr_code: bool = None, + include_mini_qr_code_hyaline: bool = None, + include_img_url: bool = None, + sid: str = None, + include_wx_img_url: bool = None, + include_alipay_img_url: bool = None, + include_alipay_wathword: bool = None + ): + """ + 推广位pid + """ + self._pid = pid + """ + 商品ID,默认CPA的品,如果推广其他业务单元的品,请填写对应的biz_unit + """ + self._item_id = item_id + """ + 业务单元,1-CPA,2-CPS,3-SPU。默认1-CPA + """ + self._biz_unit = biz_unit + """ + 废弃 + """ + self._include_mini_qr_code = include_mini_qr_code + """ + 废弃 + """ + self._include_mini_qr_code_hyaline = include_mini_qr_code_hyaline + """ + 废弃 + """ + self._include_img_url = include_img_url + """ + 第三方会员id扩展 + """ + self._sid = sid + """ + 是否合成微信推广图 + """ + self._include_wx_img_url = include_wx_img_url + """ + 是否合成支付宝推广图 + """ + self._include_alipay_img_url = include_alipay_img_url + """ + 是否返回吱口令 + """ + self._include_alipay_wathword = include_alipay_wathword + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + @property + def item_id(self): + return self._item_id + + @item_id.setter + def item_id(self, item_id): + if isinstance(item_id, str): + self._item_id = item_id + else: + raise TypeError("item_id must be str") + + @property + def biz_unit(self): + return self._biz_unit + + @biz_unit.setter + def biz_unit(self, biz_unit): + if isinstance(biz_unit, int): + self._biz_unit = biz_unit + else: + raise TypeError("biz_unit must be int") + + @property + def include_mini_qr_code(self): + return self._include_mini_qr_code + + @include_mini_qr_code.setter + def include_mini_qr_code(self, include_mini_qr_code): + if isinstance(include_mini_qr_code, bool): + self._include_mini_qr_code = include_mini_qr_code + else: + raise TypeError("include_mini_qr_code must be bool") + + @property + def include_mini_qr_code_hyaline(self): + return self._include_mini_qr_code_hyaline + + @include_mini_qr_code_hyaline.setter + def include_mini_qr_code_hyaline(self, include_mini_qr_code_hyaline): + if isinstance(include_mini_qr_code_hyaline, bool): + self._include_mini_qr_code_hyaline = include_mini_qr_code_hyaline + else: + raise TypeError("include_mini_qr_code_hyaline must be bool") + + @property + def include_img_url(self): + return self._include_img_url + + @include_img_url.setter + def include_img_url(self, include_img_url): + if isinstance(include_img_url, bool): + self._include_img_url = include_img_url + else: + raise TypeError("include_img_url must be bool") + + @property + def sid(self): + return self._sid + + @sid.setter + def sid(self, sid): + if isinstance(sid, str): + self._sid = sid + else: + raise TypeError("sid must be str") + + @property + def include_wx_img_url(self): + return self._include_wx_img_url + + @include_wx_img_url.setter + def include_wx_img_url(self, include_wx_img_url): + if isinstance(include_wx_img_url, bool): + self._include_wx_img_url = include_wx_img_url + else: + raise TypeError("include_wx_img_url must be bool") + + @property + def include_alipay_img_url(self): + return self._include_alipay_img_url + + @include_alipay_img_url.setter + def include_alipay_img_url(self, include_alipay_img_url): + if isinstance(include_alipay_img_url, bool): + self._include_alipay_img_url = include_alipay_img_url + else: + raise TypeError("include_alipay_img_url must be bool") + + @property + def include_alipay_wathword(self): + return self._include_alipay_wathword + + @include_alipay_wathword.setter + def include_alipay_wathword(self, include_alipay_wathword): + if isinstance(include_alipay_wathword, bool): + self._include_alipay_wathword = include_alipay_wathword + else: + raise TypeError("include_alipay_wathword must be bool") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.item.promotion.share.create" + + def to_dict(self): + request_dict = {} + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + if self._item_id is not None: + request_dict["item_id"] = convert_basic(self._item_id) + + if self._biz_unit is not None: + request_dict["biz_unit"] = convert_basic(self._biz_unit) + + if self._include_mini_qr_code is not None: + request_dict["include_mini_qr_code"] = convert_basic(self._include_mini_qr_code) + + if self._include_mini_qr_code_hyaline is not None: + request_dict["include_mini_qr_code_hyaline"] = convert_basic(self._include_mini_qr_code_hyaline) + + if self._include_img_url is not None: + request_dict["include_img_url"] = convert_basic(self._include_img_url) + + if self._sid is not None: + request_dict["sid"] = convert_basic(self._sid) + + if self._include_wx_img_url is not None: + request_dict["include_wx_img_url"] = convert_basic(self._include_wx_img_url) + + if self._include_alipay_img_url is not None: + request_dict["include_alipay_img_url"] = convert_basic(self._include_alipay_img_url) + + if self._include_alipay_wathword is not None: + request_dict["include_alipay_wathword"] = convert_basic(self._include_alipay_wathword) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_query_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_query_request.py new file mode 100644 index 0000000..65ec156 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_query_request.py @@ -0,0 +1,252 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbItemQueryRequest(BaseRequest): + + def __init__( + self, + page_number: int = None, + page_size: int = None, + session_id: str = None, + biz_type: str = None, + sort_type: str = None, + pid: str = None, + tb_category_2_ids: str = None, + tb_category_3_ids: str = None, + city_id: str = None, + longitude: str = None, + latitude: str = None, + range: int = None + ): + """ + 页码(默认1) + """ + self._page_number = page_number + """ + 每页数目(默认10) + """ + self._page_size = page_size + """ + 会话ID(分页场景首次请求结果返回,后续请求必须携带,服务根据session_id相同请求次数自动翻页返回) + """ + self._session_id = session_id + """ + 场景类型("kb_natural";) + """ + self._biz_type = biz_type + """ + 排序类型,默认normal("normal"-门店创建时间倒序;"distance_asc"-距离最近) + """ + self._sort_type = sort_type + """ + 推广位 + """ + self._pid = pid + """ + 淘宝二级类目(逗号分隔) + """ + self._tb_category_2_ids = tb_category_2_ids + """ + 淘宝三级类目(逗号分隔) + """ + self._tb_category_3_ids = tb_category_3_ids + """ + 城市ID + """ + self._city_id = city_id + """ + 经度(经纬度、范围配合使用) + """ + self._longitude = longitude + """ + 纬度(经纬度、范围配合使用) + """ + self._latitude = latitude + """ + 范围(单位:米,经纬度、范围配合使用) + """ + self._range = range + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def session_id(self): + return self._session_id + + @session_id.setter + def session_id(self, session_id): + if isinstance(session_id, str): + self._session_id = session_id + else: + raise TypeError("session_id must be str") + + @property + def biz_type(self): + return self._biz_type + + @biz_type.setter + def biz_type(self, biz_type): + if isinstance(biz_type, str): + self._biz_type = biz_type + else: + raise TypeError("biz_type must be str") + + @property + def sort_type(self): + return self._sort_type + + @sort_type.setter + def sort_type(self, sort_type): + if isinstance(sort_type, str): + self._sort_type = sort_type + else: + raise TypeError("sort_type must be str") + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + @property + def tb_category_2_ids(self): + return self._tb_category_2_ids + + @tb_category_2_ids.setter + def tb_category_2_ids(self, tb_category_2_ids): + if isinstance(tb_category_2_ids, str): + self._tb_category_2_ids = tb_category_2_ids + else: + raise TypeError("tb_category_2_ids must be str") + + @property + def tb_category_3_ids(self): + return self._tb_category_3_ids + + @tb_category_3_ids.setter + def tb_category_3_ids(self, tb_category_3_ids): + if isinstance(tb_category_3_ids, str): + self._tb_category_3_ids = tb_category_3_ids + else: + raise TypeError("tb_category_3_ids must be str") + + @property + def city_id(self): + return self._city_id + + @city_id.setter + def city_id(self, city_id): + if isinstance(city_id, str): + self._city_id = city_id + else: + raise TypeError("city_id must be str") + + @property + def longitude(self): + return self._longitude + + @longitude.setter + def longitude(self, longitude): + if isinstance(longitude, str): + self._longitude = longitude + else: + raise TypeError("longitude must be str") + + @property + def latitude(self): + return self._latitude + + @latitude.setter + def latitude(self, latitude): + if isinstance(latitude, str): + self._latitude = latitude + else: + raise TypeError("latitude must be str") + + @property + def range(self): + return self._range + + @range.setter + def range(self, range): + if isinstance(range, int): + self._range = range + else: + raise TypeError("range must be int") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.item.query" + + def to_dict(self): + request_dict = {} + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._session_id is not None: + request_dict["session_id"] = convert_basic(self._session_id) + + if self._biz_type is not None: + request_dict["biz_type"] = convert_basic(self._biz_type) + + if self._sort_type is not None: + request_dict["sort_type"] = convert_basic(self._sort_type) + + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + if self._tb_category_2_ids is not None: + request_dict["tb_category_2_ids"] = convert_basic(self._tb_category_2_ids) + + if self._tb_category_3_ids is not None: + request_dict["tb_category_3_ids"] = convert_basic(self._tb_category_3_ids) + + if self._city_id is not None: + request_dict["city_id"] = convert_basic(self._city_id) + + if self._longitude is not None: + request_dict["longitude"] = convert_basic(self._longitude) + + if self._latitude is not None: + request_dict["latitude"] = convert_basic(self._latitude) + + if self._range is not None: + request_dict["range"] = convert_basic(self._range) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_store_detail_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_store_detail_get_request.py new file mode 100644 index 0000000..54efce6 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_store_detail_get_request.py @@ -0,0 +1,53 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbItemStoreDetailGetRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 门店详情查询rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.item.store.detail.get" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbItemStoreDetailGetKbItemShopDetailRequest: + def __init__( + self, + store_id: str = None + ): + """ + 门店ID + """ + self.store_id = store_id + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_store_relation_query_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_store_relation_query_request.py new file mode 100644 index 0000000..a9d5b1b --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_item_store_relation_query_request.py @@ -0,0 +1,63 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbItemStoreRelationQueryRequest(BaseRequest): + + def __init__( + self, + query_request: object = None + ): + """ + 商品门店关系查询rquest + """ + self._query_request = query_request + + @property + def query_request(self): + return self._query_request + + @query_request.setter + def query_request(self, query_request): + if isinstance(query_request, object): + self._query_request = query_request + else: + raise TypeError("query_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.item.store.relation.query" + + def to_dict(self): + request_dict = {} + if self._query_request is not None: + request_dict["query_request"] = convert_struct(self._query_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbItemStoreRelationQueryKbItemShopRelationRequest: + def __init__( + self, + item_id: str = None, + city_id: str = None, + biz_type: str = None + ): + """ + 商品ID + """ + self.item_id = item_id + """ + 城市ID + """ + self.city_id = city_id + """ + 业务类型(cps/cpa) + """ + self.biz_type = biz_type + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_create_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_create_request.py new file mode 100644 index 0000000..d50f879 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_create_request.py @@ -0,0 +1,98 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbOrderCreateRequest(BaseRequest): + + def __init__( + self, + order_dto: object = None + ): + """ + 订单对象 + """ + self._order_dto = order_dto + + @property + def order_dto(self): + return self._order_dto + + @order_dto.setter + def order_dto(self, order_dto): + if isinstance(order_dto, object): + self._order_dto = order_dto + else: + raise TypeError("order_dto must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.order.create" + + def to_dict(self): + request_dict = {} + if self._order_dto is not None: + request_dict["order_dto"] = convert_struct(self._order_dto) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbOrderCreateOrderDto: + def __init__( + self, + outer_order_id: str = None, + item_id: str = None, + quantity: int = None, + pay_order_fee: int = None, + order_fee: int = None, + sell_price: int = None, + title: str = None, + ext_info: str = None, + skip_pay: bool = None, + phone: str = None + ): + """ + 渠道订单号,需保证全局唯一 + """ + self.outer_order_id = outer_order_id + """ + 商品ID + """ + self.item_id = item_id + """ + 购买数量 + """ + self.quantity = quantity + """ + 等同sell_price + """ + self.pay_order_fee = pay_order_fee + """ + 商品的原价*份数,单位分 + """ + self.order_fee = order_fee + """ + 商品的活动价*份数,单位分 + """ + self.sell_price = sell_price + """ + 商品名称 + """ + self.title = title + """ + 扩展参数,json格式 + """ + self.ext_info = ext_info + """ + true预下单不支付,false下单并支付 + """ + self.skip_pay = skip_pay + """ + 加密后的手机号 + """ + self.phone = phone + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_pay_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_pay_request.py new file mode 100644 index 0000000..41dc2f6 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_pay_request.py @@ -0,0 +1,58 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbOrderPayRequest(BaseRequest): + + def __init__( + self, + order_pay_dto: object = None + ): + """ + 订单支付对象 + """ + self._order_pay_dto = order_pay_dto + + @property + def order_pay_dto(self): + return self._order_pay_dto + + @order_pay_dto.setter + def order_pay_dto(self, order_pay_dto): + if isinstance(order_pay_dto, object): + self._order_pay_dto = order_pay_dto + else: + raise TypeError("order_pay_dto must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.order.pay" + + def to_dict(self): + request_dict = {} + if self._order_pay_dto is not None: + request_dict["order_pay_dto"] = convert_struct(self._order_pay_dto) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbOrderPayOrderPayDto: + def __init__( + self, + outer_order_id: str = None, + biz_order_id: str = None + ): + """ + 渠道订单号 + """ + self.outer_order_id = outer_order_id + """ + 淘宝子单号 + """ + self.biz_order_id = biz_order_id + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_query_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_query_request.py new file mode 100644 index 0000000..23ab873 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_query_request.py @@ -0,0 +1,53 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbOrderQueryRequest(BaseRequest): + + def __init__( + self, + order_query_dto: object = None + ): + """ + 查询对象 + """ + self._order_query_dto = order_query_dto + + @property + def order_query_dto(self): + return self._order_query_dto + + @order_query_dto.setter + def order_query_dto(self, order_query_dto): + if isinstance(order_query_dto, object): + self._order_query_dto = order_query_dto + else: + raise TypeError("order_query_dto must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.order.query" + + def to_dict(self): + request_dict = {} + if self._order_query_dto is not None: + request_dict["order_query_dto"] = convert_struct(self._order_query_dto) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbOrderQueryOrderQueryDto: + def __init__( + self, + biz_order_id: str = None + ): + """ + 淘宝子单号 + """ + self.biz_order_id = biz_order_id + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_refund_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_refund_request.py new file mode 100644 index 0000000..c32afe6 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_order_refund_request.py @@ -0,0 +1,83 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbOrderRefundRequest(BaseRequest): + + def __init__( + self, + order_refund_dto: object = None + ): + """ + 退款对象 + """ + self._order_refund_dto = order_refund_dto + + @property + def order_refund_dto(self): + return self._order_refund_dto + + @order_refund_dto.setter + def order_refund_dto(self, order_refund_dto): + if isinstance(order_refund_dto, object): + self._order_refund_dto = order_refund_dto + else: + raise TypeError("order_refund_dto must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.order.refund" + + def to_dict(self): + request_dict = {} + if self._order_refund_dto is not None: + request_dict["order_refund_dto"] = convert_struct(self._order_refund_dto) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionKbOrderRefundOrderVoucherDetailDto: + def __init__( + self, + item_id: str = None, + voucher_id: str = None + ): + """ + 商品ID,必填 + """ + self.item_id = item_id + """ + 凭证ID,必填 + """ + self.voucher_id = voucher_id + +class AlibabaAlscUnionKbOrderRefundOrderRefundDto: + def __init__( + self, + reason: str = None, + biz_order_id: str = None, + voucher_list: list = None, + ext_info: str = None + ): + """ + 用户退款原因,必填 + """ + self.reason = reason + """ + 本地生活订单号,,必填 + """ + self.biz_order_id = biz_order_id + """ + 退款明细 + """ + self.voucher_list = voucher_list + """ + 扩展参数,json格式 + """ + self.ext_info = ext_info + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_store_item_query_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_store_item_query_request.py new file mode 100644 index 0000000..25ae17f --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_store_item_query_request.py @@ -0,0 +1,100 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbStoreItemQueryRequest(BaseRequest): + + def __init__( + self, + store_id: str = None, + biz_type: str = None, + pid: str = None, + sid: str = None + ): + """ + 门店ID + """ + self._store_id = store_id + """ + 场景类型("kb_natural";) + """ + self._biz_type = biz_type + """ + 推广位 + """ + self._pid = pid + """ + sid(申请权限后可用) + """ + self._sid = sid + + @property + def store_id(self): + return self._store_id + + @store_id.setter + def store_id(self, store_id): + if isinstance(store_id, str): + self._store_id = store_id + else: + raise TypeError("store_id must be str") + + @property + def biz_type(self): + return self._biz_type + + @biz_type.setter + def biz_type(self, biz_type): + if isinstance(biz_type, str): + self._biz_type = biz_type + else: + raise TypeError("biz_type must be str") + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + @property + def sid(self): + return self._sid + + @sid.setter + def sid(self, sid): + if isinstance(sid, str): + self._sid = sid + else: + raise TypeError("sid must be str") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.store.item.query" + + def to_dict(self): + request_dict = {} + if self._store_id is not None: + request_dict["store_id"] = convert_basic(self._store_id) + + if self._biz_type is not None: + request_dict["biz_type"] = convert_basic(self._biz_type) + + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + if self._sid is not None: + request_dict["sid"] = convert_basic(self._sid) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_store_query_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_store_query_request.py new file mode 100644 index 0000000..0a52421 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kb_store_query_request.py @@ -0,0 +1,252 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbStoreQueryRequest(BaseRequest): + + def __init__( + self, + page_number: int = None, + page_size: int = None, + session_id: str = None, + biz_type: str = None, + sort_type: str = None, + city_id: str = None, + kb_category_2_ids: str = None, + kb_category_3_ids: str = None, + longitude: str = None, + latitude: str = None, + range: int = None, + kb_category_1_ids: str = None + ): + """ + 页码(默认1) + """ + self._page_number = page_number + """ + 每页数目(默认10) + """ + self._page_size = page_size + """ + 会话ID(分页场景首次请求结果返回,后续请求必须携带,服务根据session_id相同请求次数自动翻页返回) + """ + self._session_id = session_id + """ + 场景类型("kb_natural";) + """ + self._biz_type = biz_type + """ + 排序类型,默认normal("normal"-门店创建时间倒序;"distance_asc"-距离最近) + """ + self._sort_type = sort_type + """ + 城市ID + """ + self._city_id = city_id + """ + 口碑二级类目(逗号分隔) + """ + self._kb_category_2_ids = kb_category_2_ids + """ + 口碑三级类目(逗号分隔) + """ + self._kb_category_3_ids = kb_category_3_ids + """ + 经度(经纬度、范围配合使用) + """ + self._longitude = longitude + """ + 纬度(经纬度、范围配合使用) + """ + self._latitude = latitude + """ + 范围(单位:米,经纬度、范围配合使用) + """ + self._range = range + """ + 口碑一级类目(逗号分隔) + """ + self._kb_category_1_ids = kb_category_1_ids + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def session_id(self): + return self._session_id + + @session_id.setter + def session_id(self, session_id): + if isinstance(session_id, str): + self._session_id = session_id + else: + raise TypeError("session_id must be str") + + @property + def biz_type(self): + return self._biz_type + + @biz_type.setter + def biz_type(self, biz_type): + if isinstance(biz_type, str): + self._biz_type = biz_type + else: + raise TypeError("biz_type must be str") + + @property + def sort_type(self): + return self._sort_type + + @sort_type.setter + def sort_type(self, sort_type): + if isinstance(sort_type, str): + self._sort_type = sort_type + else: + raise TypeError("sort_type must be str") + + @property + def city_id(self): + return self._city_id + + @city_id.setter + def city_id(self, city_id): + if isinstance(city_id, str): + self._city_id = city_id + else: + raise TypeError("city_id must be str") + + @property + def kb_category_2_ids(self): + return self._kb_category_2_ids + + @kb_category_2_ids.setter + def kb_category_2_ids(self, kb_category_2_ids): + if isinstance(kb_category_2_ids, str): + self._kb_category_2_ids = kb_category_2_ids + else: + raise TypeError("kb_category_2_ids must be str") + + @property + def kb_category_3_ids(self): + return self._kb_category_3_ids + + @kb_category_3_ids.setter + def kb_category_3_ids(self, kb_category_3_ids): + if isinstance(kb_category_3_ids, str): + self._kb_category_3_ids = kb_category_3_ids + else: + raise TypeError("kb_category_3_ids must be str") + + @property + def longitude(self): + return self._longitude + + @longitude.setter + def longitude(self, longitude): + if isinstance(longitude, str): + self._longitude = longitude + else: + raise TypeError("longitude must be str") + + @property + def latitude(self): + return self._latitude + + @latitude.setter + def latitude(self, latitude): + if isinstance(latitude, str): + self._latitude = latitude + else: + raise TypeError("latitude must be str") + + @property + def range(self): + return self._range + + @range.setter + def range(self, range): + if isinstance(range, int): + self._range = range + else: + raise TypeError("range must be int") + + @property + def kb_category_1_ids(self): + return self._kb_category_1_ids + + @kb_category_1_ids.setter + def kb_category_1_ids(self, kb_category_1_ids): + if isinstance(kb_category_1_ids, str): + self._kb_category_1_ids = kb_category_1_ids + else: + raise TypeError("kb_category_1_ids must be str") + + + def get_api_name(self): + return "alibaba.alsc.union.kb.store.query" + + def to_dict(self): + request_dict = {} + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._session_id is not None: + request_dict["session_id"] = convert_basic(self._session_id) + + if self._biz_type is not None: + request_dict["biz_type"] = convert_basic(self._biz_type) + + if self._sort_type is not None: + request_dict["sort_type"] = convert_basic(self._sort_type) + + if self._city_id is not None: + request_dict["city_id"] = convert_basic(self._city_id) + + if self._kb_category_2_ids is not None: + request_dict["kb_category_2_ids"] = convert_basic(self._kb_category_2_ids) + + if self._kb_category_3_ids is not None: + request_dict["kb_category_3_ids"] = convert_basic(self._kb_category_3_ids) + + if self._longitude is not None: + request_dict["longitude"] = convert_basic(self._longitude) + + if self._latitude is not None: + request_dict["latitude"] = convert_basic(self._latitude) + + if self._range is not None: + request_dict["range"] = convert_basic(self._range) + + if self._kb_category_1_ids is not None: + request_dict["kb_category_1_ids"] = convert_basic(self._kb_category_1_ids) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_order_details_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_order_details_get_request.py new file mode 100644 index 0000000..ed60627 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_order_details_get_request.py @@ -0,0 +1,176 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbcpaOrderDetailsGetRequest(BaseRequest): + + def __init__( + self, + date_type: int = None, + settle_state: int = None, + end_date: str = None, + page_size: int = None, + page_number: int = None, + start_date: str = None, + order_state: int = None, + pid: str = None + ): + """ + 时间维度,1-付款时间 2-创建时间 3-结算时间 4-更新时间 + """ + self._date_type = date_type + """ + 结算状态,1-已结算 2-未结算 不传-所有状态 + """ + self._settle_state = settle_state + """ + 查询结束时间 + """ + self._end_date = end_date + """ + 每页返回数据大小,默认10,最大返回50 + """ + self._page_size = page_size + """ + 页码,默认第一页,取值范围1~50 + """ + self._page_number = page_number + """ + 查询开始时间 + """ + self._start_date = start_date + """ + 订单状态,0-已失效 1-已下单 2-已付款 4-已收货 不传-全部状态 + """ + self._order_state = order_state + """ + 推广位pid + """ + self._pid = pid + + @property + def date_type(self): + return self._date_type + + @date_type.setter + def date_type(self, date_type): + if isinstance(date_type, int): + self._date_type = date_type + else: + raise TypeError("date_type must be int") + + @property + def settle_state(self): + return self._settle_state + + @settle_state.setter + def settle_state(self, settle_state): + if isinstance(settle_state, int): + self._settle_state = settle_state + else: + raise TypeError("settle_state must be int") + + @property + def end_date(self): + return self._end_date + + @end_date.setter + def end_date(self, end_date): + if isinstance(end_date, str): + self._end_date = end_date + else: + raise TypeError("end_date must be str") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def start_date(self): + return self._start_date + + @start_date.setter + def start_date(self, start_date): + if isinstance(start_date, str): + self._start_date = start_date + else: + raise TypeError("start_date must be str") + + @property + def order_state(self): + return self._order_state + + @order_state.setter + def order_state(self, order_state): + if isinstance(order_state, int): + self._order_state = order_state + else: + raise TypeError("order_state must be int") + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + + def get_api_name(self): + return "alibaba.alsc.union.kbcpa.order.details.get" + + def to_dict(self): + request_dict = {} + if self._date_type is not None: + request_dict["date_type"] = convert_basic(self._date_type) + + if self._settle_state is not None: + request_dict["settle_state"] = convert_basic(self._settle_state) + + if self._end_date is not None: + request_dict["end_date"] = convert_basic(self._end_date) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._start_date is not None: + request_dict["start_date"] = convert_basic(self._start_date) + + if self._order_state is not None: + request_dict["order_state"] = convert_basic(self._order_state) + + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_punish_order_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_punish_order_get_request.py new file mode 100644 index 0000000..55190df --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_punish_order_get_request.py @@ -0,0 +1,119 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbcpaPunishOrderGetRequest(BaseRequest): + + def __init__( + self, + date_type: int = None, + end_date: str = None, + page_size: int = None, + page_number: int = None, + start_date: str = None + ): + """ + 时间维度,1.订单结算时间 2.维权创建时间 3.维权完成时间 + """ + self._date_type = date_type + """ + 截止查询时间 + """ + self._end_date = end_date + """ + 每页返回数据大小,默认20,最大返回50 + """ + self._page_size = page_size + """ + 页码,默认第一页,取值范围1~50 + """ + self._page_number = page_number + """ + 开始查询时间 + """ + self._start_date = start_date + + @property + def date_type(self): + return self._date_type + + @date_type.setter + def date_type(self, date_type): + if isinstance(date_type, int): + self._date_type = date_type + else: + raise TypeError("date_type must be int") + + @property + def end_date(self): + return self._end_date + + @end_date.setter + def end_date(self, end_date): + if isinstance(end_date, str): + self._end_date = end_date + else: + raise TypeError("end_date must be str") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def start_date(self): + return self._start_date + + @start_date.setter + def start_date(self, start_date): + if isinstance(start_date, str): + self._start_date = start_date + else: + raise TypeError("start_date must be str") + + + def get_api_name(self): + return "alibaba.alsc.union.kbcpa.punish.order.get" + + def to_dict(self): + request_dict = {} + if self._date_type is not None: + request_dict["date_type"] = convert_basic(self._date_type) + + if self._end_date is not None: + request_dict["end_date"] = convert_basic(self._end_date) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._start_date is not None: + request_dict["start_date"] = convert_basic(self._start_date) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_refund_order_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_refund_order_get_request.py new file mode 100644 index 0000000..3bdcff4 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpa_refund_order_get_request.py @@ -0,0 +1,138 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbcpaRefundOrderGetRequest(BaseRequest): + + def __init__( + self, + date_type: int = None, + end_date: str = None, + page_size: int = None, + page_number: int = None, + start_date: str = None, + pid: str = None + ): + """ + 时间维度,1.订单结算时间 2.维权创建时间 3.维权完成时间 4更新时间 + """ + self._date_type = date_type + """ + 查询结束时间 + """ + self._end_date = end_date + """ + 每页返回数据大小,默认20,最大返回50 + """ + self._page_size = page_size + """ + 页码,默认第一页,取值范围1~50 + """ + self._page_number = page_number + """ + 查询开始时间 + """ + self._start_date = start_date + """ + 推广位pid + """ + self._pid = pid + + @property + def date_type(self): + return self._date_type + + @date_type.setter + def date_type(self, date_type): + if isinstance(date_type, int): + self._date_type = date_type + else: + raise TypeError("date_type must be int") + + @property + def end_date(self): + return self._end_date + + @end_date.setter + def end_date(self, end_date): + if isinstance(end_date, str): + self._end_date = end_date + else: + raise TypeError("end_date must be str") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def start_date(self): + return self._start_date + + @start_date.setter + def start_date(self, start_date): + if isinstance(start_date, str): + self._start_date = start_date + else: + raise TypeError("start_date must be str") + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + + def get_api_name(self): + return "alibaba.alsc.union.kbcpa.refund.order.get" + + def to_dict(self): + request_dict = {} + if self._date_type is not None: + request_dict["date_type"] = convert_basic(self._date_type) + + if self._end_date is not None: + request_dict["end_date"] = convert_basic(self._end_date) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._start_date is not None: + request_dict["start_date"] = convert_basic(self._start_date) + + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_positive_order_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_positive_order_get_request.py new file mode 100644 index 0000000..682c96d --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_positive_order_get_request.py @@ -0,0 +1,252 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbcpxPositiveOrderGetRequest(BaseRequest): + + def __init__( + self, + date_type: int = None, + settle_state: int = None, + end_date: str = None, + biz_unit: int = None, + page_size: int = None, + page_number: int = None, + start_date: str = None, + order_state: int = None, + flow_type: str = None, + pid: str = None, + order_id: str = None, + include_used_store_id: bool = None + ): + """ + 时间维度,1-付款时间 2-创建时间 3-结算时间 4-更新时间 + """ + self._date_type = date_type + """ + 结算状态,1-已结算 2-未结算 不传-全部状态 + """ + self._settle_state = settle_state + """ + 查询截止时间,精确到时分秒。开始和结束时间不能超过31天 + """ + self._end_date = end_date + """ + 1-CPA 2-CPS + """ + self._biz_unit = biz_unit + """ + 每页返回数据大小,默认10,最大返回50 + """ + self._page_size = page_size + """ + 页码,默认第一页,取值范围1~50 + """ + self._page_number = page_number + """ + 查询起始时间,精确到时分秒。开始和结束时间不能超过31天 + """ + self._start_date = start_date + """ + 订单状态,0-已失效 1-已下单 2-已付款 4-已收货 不传-全部状态 + """ + self._order_state = order_state + """ + 场景值,支持多场景(英文逗号分隔)查询7卡券订单,8卡券核销订单,10-媒体出资CPS红包,11-媒体出资霸王餐加码红包 + """ + self._flow_type = flow_type + """ + 推广位pid + """ + self._pid = pid + """ + 淘宝子订单号或饿了么订单号 + """ + self._order_id = order_id + """ + 是否包含核销门店 + """ + self._include_used_store_id = include_used_store_id + + @property + def date_type(self): + return self._date_type + + @date_type.setter + def date_type(self, date_type): + if isinstance(date_type, int): + self._date_type = date_type + else: + raise TypeError("date_type must be int") + + @property + def settle_state(self): + return self._settle_state + + @settle_state.setter + def settle_state(self, settle_state): + if isinstance(settle_state, int): + self._settle_state = settle_state + else: + raise TypeError("settle_state must be int") + + @property + def end_date(self): + return self._end_date + + @end_date.setter + def end_date(self, end_date): + if isinstance(end_date, str): + self._end_date = end_date + else: + raise TypeError("end_date must be str") + + @property + def biz_unit(self): + return self._biz_unit + + @biz_unit.setter + def biz_unit(self, biz_unit): + if isinstance(biz_unit, int): + self._biz_unit = biz_unit + else: + raise TypeError("biz_unit must be int") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def start_date(self): + return self._start_date + + @start_date.setter + def start_date(self, start_date): + if isinstance(start_date, str): + self._start_date = start_date + else: + raise TypeError("start_date must be str") + + @property + def order_state(self): + return self._order_state + + @order_state.setter + def order_state(self, order_state): + if isinstance(order_state, int): + self._order_state = order_state + else: + raise TypeError("order_state must be int") + + @property + def flow_type(self): + return self._flow_type + + @flow_type.setter + def flow_type(self, flow_type): + if isinstance(flow_type, str): + self._flow_type = flow_type + else: + raise TypeError("flow_type must be str") + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + @property + def order_id(self): + return self._order_id + + @order_id.setter + def order_id(self, order_id): + if isinstance(order_id, str): + self._order_id = order_id + else: + raise TypeError("order_id must be str") + + @property + def include_used_store_id(self): + return self._include_used_store_id + + @include_used_store_id.setter + def include_used_store_id(self, include_used_store_id): + if isinstance(include_used_store_id, bool): + self._include_used_store_id = include_used_store_id + else: + raise TypeError("include_used_store_id must be bool") + + + def get_api_name(self): + return "alibaba.alsc.union.kbcpx.positive.order.get" + + def to_dict(self): + request_dict = {} + if self._date_type is not None: + request_dict["date_type"] = convert_basic(self._date_type) + + if self._settle_state is not None: + request_dict["settle_state"] = convert_basic(self._settle_state) + + if self._end_date is not None: + request_dict["end_date"] = convert_basic(self._end_date) + + if self._biz_unit is not None: + request_dict["biz_unit"] = convert_basic(self._biz_unit) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._start_date is not None: + request_dict["start_date"] = convert_basic(self._start_date) + + if self._order_state is not None: + request_dict["order_state"] = convert_basic(self._order_state) + + if self._flow_type is not None: + request_dict["flow_type"] = convert_basic(self._flow_type) + + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + if self._order_id is not None: + request_dict["order_id"] = convert_basic(self._order_id) + + if self._include_used_store_id is not None: + request_dict["include_used_store_id"] = convert_basic(self._include_used_store_id) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_punish_order_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_punish_order_get_request.py new file mode 100644 index 0000000..2195ba1 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_punish_order_get_request.py @@ -0,0 +1,195 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbcpxPunishOrderGetRequest(BaseRequest): + + def __init__( + self, + date_type: int = None, + end_date: str = None, + biz_unit: int = None, + page_size: int = None, + page_number: int = None, + start_date: str = None, + flow_type: str = None, + pid: str = None, + order_id: str = None + ): + """ + 时间维度,1.订单结算时间 2.维权创建时间 3.维权完成时间 4更新时间 + """ + self._date_type = date_type + """ + 查询截止时间。开始和结束时间不能超过31天 + """ + self._end_date = end_date + """ + 1-CPA 2-CPS + """ + self._biz_unit = biz_unit + """ + 每页返回数据大小,默认10,最大返回50 + """ + self._page_size = page_size + """ + 页码,默认第一页,取值范围1~50 + """ + self._page_number = page_number + """ + 查询起始时间。开始和结束时间不能超过31天 + """ + self._start_date = start_date + """ + 场景值,支持多场景(英文逗号分隔)查询7卡券订单,8卡券核销订单,10-媒体出资CPS红包,11-媒体出资霸王餐加码红包 + """ + self._flow_type = flow_type + """ + 推广位pid + """ + self._pid = pid + """ + 淘宝子订单号 + """ + self._order_id = order_id + + @property + def date_type(self): + return self._date_type + + @date_type.setter + def date_type(self, date_type): + if isinstance(date_type, int): + self._date_type = date_type + else: + raise TypeError("date_type must be int") + + @property + def end_date(self): + return self._end_date + + @end_date.setter + def end_date(self, end_date): + if isinstance(end_date, str): + self._end_date = end_date + else: + raise TypeError("end_date must be str") + + @property + def biz_unit(self): + return self._biz_unit + + @biz_unit.setter + def biz_unit(self, biz_unit): + if isinstance(biz_unit, int): + self._biz_unit = biz_unit + else: + raise TypeError("biz_unit must be int") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def start_date(self): + return self._start_date + + @start_date.setter + def start_date(self, start_date): + if isinstance(start_date, str): + self._start_date = start_date + else: + raise TypeError("start_date must be str") + + @property + def flow_type(self): + return self._flow_type + + @flow_type.setter + def flow_type(self, flow_type): + if isinstance(flow_type, str): + self._flow_type = flow_type + else: + raise TypeError("flow_type must be str") + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + @property + def order_id(self): + return self._order_id + + @order_id.setter + def order_id(self, order_id): + if isinstance(order_id, str): + self._order_id = order_id + else: + raise TypeError("order_id must be str") + + + def get_api_name(self): + return "alibaba.alsc.union.kbcpx.punish.order.get" + + def to_dict(self): + request_dict = {} + if self._date_type is not None: + request_dict["date_type"] = convert_basic(self._date_type) + + if self._end_date is not None: + request_dict["end_date"] = convert_basic(self._end_date) + + if self._biz_unit is not None: + request_dict["biz_unit"] = convert_basic(self._biz_unit) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._start_date is not None: + request_dict["start_date"] = convert_basic(self._start_date) + + if self._flow_type is not None: + request_dict["flow_type"] = convert_basic(self._flow_type) + + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + if self._order_id is not None: + request_dict["order_id"] = convert_basic(self._order_id) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_refund_order_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_refund_order_get_request.py new file mode 100644 index 0000000..93735cf --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_kbcpx_refund_order_get_request.py @@ -0,0 +1,195 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionKbcpxRefundOrderGetRequest(BaseRequest): + + def __init__( + self, + date_type: int = None, + end_date: str = None, + biz_unit: int = None, + page_size: int = None, + page_number: int = None, + start_date: str = None, + flow_type: str = None, + pid: str = None, + order_id: str = None + ): + """ + 时间维度,1.订单结算时间 2.维权创建时间 3.维权完成时间 4更新时间 + """ + self._date_type = date_type + """ + 查询截止时间。开始和结束时间不能超过31天 + """ + self._end_date = end_date + """ + 1-CPA 2-CPS + """ + self._biz_unit = biz_unit + """ + 每页返回数据大小,默认10,最大返回50 + """ + self._page_size = page_size + """ + 页码,默认第一页,取值范围1~50 + """ + self._page_number = page_number + """ + 查询起始时间。开始和结束时间不能超过31天 + """ + self._start_date = start_date + """ + 场景值,支持多场景(英文逗号分隔)查询7卡券订单,8卡券核销订单,10-媒体出资CPS红包,11-媒体出资霸王餐加码红包 + """ + self._flow_type = flow_type + """ + 推广位pid + """ + self._pid = pid + """ + 淘宝子订单号或饿了么订单号 + """ + self._order_id = order_id + + @property + def date_type(self): + return self._date_type + + @date_type.setter + def date_type(self, date_type): + if isinstance(date_type, int): + self._date_type = date_type + else: + raise TypeError("date_type must be int") + + @property + def end_date(self): + return self._end_date + + @end_date.setter + def end_date(self, end_date): + if isinstance(end_date, str): + self._end_date = end_date + else: + raise TypeError("end_date must be str") + + @property + def biz_unit(self): + return self._biz_unit + + @biz_unit.setter + def biz_unit(self, biz_unit): + if isinstance(biz_unit, int): + self._biz_unit = biz_unit + else: + raise TypeError("biz_unit must be int") + + @property + def page_size(self): + return self._page_size + + @page_size.setter + def page_size(self, page_size): + if isinstance(page_size, int): + self._page_size = page_size + else: + raise TypeError("page_size must be int") + + @property + def page_number(self): + return self._page_number + + @page_number.setter + def page_number(self, page_number): + if isinstance(page_number, int): + self._page_number = page_number + else: + raise TypeError("page_number must be int") + + @property + def start_date(self): + return self._start_date + + @start_date.setter + def start_date(self, start_date): + if isinstance(start_date, str): + self._start_date = start_date + else: + raise TypeError("start_date must be str") + + @property + def flow_type(self): + return self._flow_type + + @flow_type.setter + def flow_type(self, flow_type): + if isinstance(flow_type, str): + self._flow_type = flow_type + else: + raise TypeError("flow_type must be str") + + @property + def pid(self): + return self._pid + + @pid.setter + def pid(self, pid): + if isinstance(pid, str): + self._pid = pid + else: + raise TypeError("pid must be str") + + @property + def order_id(self): + return self._order_id + + @order_id.setter + def order_id(self, order_id): + if isinstance(order_id, str): + self._order_id = order_id + else: + raise TypeError("order_id must be str") + + + def get_api_name(self): + return "alibaba.alsc.union.kbcpx.refund.order.get" + + def to_dict(self): + request_dict = {} + if self._date_type is not None: + request_dict["date_type"] = convert_basic(self._date_type) + + if self._end_date is not None: + request_dict["end_date"] = convert_basic(self._end_date) + + if self._biz_unit is not None: + request_dict["biz_unit"] = convert_basic(self._biz_unit) + + if self._page_size is not None: + request_dict["page_size"] = convert_basic(self._page_size) + + if self._page_number is not None: + request_dict["page_number"] = convert_basic(self._page_number) + + if self._start_date is not None: + request_dict["start_date"] = convert_basic(self._start_date) + + if self._flow_type is not None: + request_dict["flow_type"] = convert_basic(self._flow_type) + + if self._pid is not None: + request_dict["pid"] = convert_basic(self._pid) + + if self._order_id is not None: + request_dict["order_id"] = convert_basic(self._order_id) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_media_zone_add_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_media_zone_add_request.py new file mode 100644 index 0000000..3af4327 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_media_zone_add_request.py @@ -0,0 +1,62 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionMediaZoneAddRequest(BaseRequest): + + def __init__( + self, + zone_name: str = None, + media_id: str = None + ): + """ + 推广位名称 + """ + self._zone_name = zone_name + """ + 媒体id,工具商渠道必填 + """ + self._media_id = media_id + + @property + def zone_name(self): + return self._zone_name + + @zone_name.setter + def zone_name(self, zone_name): + if isinstance(zone_name, str): + self._zone_name = zone_name + else: + raise TypeError("zone_name must be str") + + @property + def media_id(self): + return self._media_id + + @media_id.setter + def media_id(self, media_id): + if isinstance(media_id, str): + self._media_id = media_id + else: + raise TypeError("media_id must be str") + + + def get_api_name(self): + return "alibaba.alsc.union.media.zone.add" + + def to_dict(self): + request_dict = {} + if self._zone_name is not None: + request_dict["zone_name"] = convert_basic(self._zone_name) + + if self._media_id is not None: + request_dict["media_id"] = convert_basic(self._media_id) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_media_zone_get_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_media_zone_get_request.py new file mode 100644 index 0000000..74da2dc --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_media_zone_get_request.py @@ -0,0 +1,62 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionMediaZoneGetRequest(BaseRequest): + + def __init__( + self, + page: int = None, + limit: int = None + ): + """ + 页码,从1开始 + """ + self._page = page + """ + 每页展示条数 + """ + self._limit = limit + + @property + def page(self): + return self._page + + @page.setter + def page(self, page): + if isinstance(page, int): + self._page = page + else: + raise TypeError("page must be int") + + @property + def limit(self): + return self._limit + + @limit.setter + def limit(self, limit): + if isinstance(limit, int): + self._limit = limit + else: + raise TypeError("limit must be int") + + + def get_api_name(self): + return "alibaba.alsc.union.media.zone.get" + + def to_dict(self): + request_dict = {} + if self._page is not None: + request_dict["page"] = convert_basic(self._page) + + if self._limit is not None: + request_dict["limit"] = convert_basic(self._limit) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/alibaba_alsc_union_promotion_link_analyze_request.py b/libs/topsdk/defaultability/request/alibaba_alsc_union_promotion_link_analyze_request.py new file mode 100644 index 0000000..f1aa273 --- /dev/null +++ b/libs/topsdk/defaultability/request/alibaba_alsc_union_promotion_link_analyze_request.py @@ -0,0 +1,58 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class AlibabaAlscUnionPromotionLinkAnalyzeRequest(BaseRequest): + + def __init__( + self, + promotion_link_analyze_request: object = None + ): + """ + 查询request对象 + """ + self._promotion_link_analyze_request = promotion_link_analyze_request + + @property + def promotion_link_analyze_request(self): + return self._promotion_link_analyze_request + + @promotion_link_analyze_request.setter + def promotion_link_analyze_request(self, promotion_link_analyze_request): + if isinstance(promotion_link_analyze_request, object): + self._promotion_link_analyze_request = promotion_link_analyze_request + else: + raise TypeError("promotion_link_analyze_request must be object") + + + def get_api_name(self): + return "alibaba.alsc.union.promotion.link.analyze" + + def to_dict(self): + request_dict = {} + if self._promotion_link_analyze_request is not None: + request_dict["promotion_link_analyze_request"] = convert_struct(self._promotion_link_analyze_request) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + +class AlibabaAlscUnionPromotionLinkAnalyzePromotionLinkAnalyzeRequest: + def __init__( + self, + type: int = None, + link: str = None + ): + """ + 链接类型(1-h5;2-h5短链;3-微信小程序;4-饿了么APP) + """ + self.type = type + """ + 推广链接 + """ + self.link = link + diff --git a/libs/topsdk/defaultability/request/taobao_tmc_message_produce_request.py b/libs/topsdk/defaultability/request/taobao_tmc_message_produce_request.py new file mode 100644 index 0000000..c33f015 --- /dev/null +++ b/libs/topsdk/defaultability/request/taobao_tmc_message_produce_request.py @@ -0,0 +1,247 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcMessageProduceRequest(BaseRequest): + + def __init__( + self, + content: str = None, + ex_content: str = None, + target_appkey: str = None, + target_group: str = None, + topic: str = None, + media_content: bytes = None, + media_content2: bytes = None, + media_content3: bytes = None, + media_content5: bytes = None, + media_content4: bytes = None, + delay_millis: int = None, + expires_millis: int = None + ): + """ + 消息内容的JSON表述,必须按照topic的定义来填充 + """ + self._content = content + """ + 消息的扩增属性,用json格式表示 + """ + self._ex_content = ex_content + """ + 直发消息需要传入目标appkey + """ + self._target_appkey = target_appkey + """ + 目标分组,一般为default + """ + self._target_group = target_group + """ + 消息类型 + """ + self._topic = topic + """ + 回传的文件内容,目前仅支持jpg,png,bmp,gif,pdf类型的文件,文件最大1M。只有消息中有byte[]类型的数据时,才需要传此字段; 否则不需要传此字段。 + """ + self._media_content = media_content + """ + 回传的文件内容,目前仅支持jpg,png,bmp,gif,pdf类型的文件,文件最大1M。只有消息中有byte[]类型的数据时,才需要传此字段; 否则不需要传此字段。具体对应到沙体中的什么值,请参考消息字段说明。 + """ + self._media_content2 = media_content2 + """ + 回传的文件内容,目前仅支持jpg,png,bmp,gif,pdf类型的文件,文件最大1M。只有消息中有byte[]类型的数据时,才需要传此字段; 否则不需要传此字段。具体对应到沙体中的什么值,请参考消息字段说明。 + """ + self._media_content3 = media_content3 + """ + 回传的文件内容,目前仅支持jpg,png,bmp,gif,pdf类型的文件,文件最大1M。只有消息中有byte[]类型的数据时,才需要传此字段; 否则不需要传此字段。具体对应到沙体中的什么值,请参考消息字段说明。 + """ + self._media_content5 = media_content5 + """ + 回传的文件内容,目前仅支持jpg,png,bmp,gif,pdf类型的文件,文件最大1M。只有消息中有byte[]类型的数据时,才需要传此字段; 否则不需要传此字段。具体对应到沙体中的什么值,请参考消息字段说明。 + """ + self._media_content4 = media_content4 + """ + 延时参数 时间戳 预期发送时间 + """ + self._delay_millis = delay_millis + """ + 提前过期 相对时间差 毫秒 + """ + self._expires_millis = expires_millis + + @property + def content(self): + return self._content + + @content.setter + def content(self, content): + if isinstance(content, str): + self._content = content + else: + raise TypeError("content must be str") + + @property + def ex_content(self): + return self._ex_content + + @ex_content.setter + def ex_content(self, ex_content): + if isinstance(ex_content, str): + self._ex_content = ex_content + else: + raise TypeError("ex_content must be str") + + @property + def target_appkey(self): + return self._target_appkey + + @target_appkey.setter + def target_appkey(self, target_appkey): + if isinstance(target_appkey, str): + self._target_appkey = target_appkey + else: + raise TypeError("target_appkey must be str") + + @property + def target_group(self): + return self._target_group + + @target_group.setter + def target_group(self, target_group): + if isinstance(target_group, str): + self._target_group = target_group + else: + raise TypeError("target_group must be str") + + @property + def topic(self): + return self._topic + + @topic.setter + def topic(self, topic): + if isinstance(topic, str): + self._topic = topic + else: + raise TypeError("topic must be str") + + @property + def media_content(self): + return self._media_content + + @media_content.setter + def media_content(self, media_content): + if isinstance(media_content, bytes): + self._media_content = media_content + else: + raise TypeError("media_content must be bytes") + + @property + def media_content2(self): + return self._media_content2 + + @media_content2.setter + def media_content2(self, media_content2): + if isinstance(media_content2, bytes): + self._media_content2 = media_content2 + else: + raise TypeError("media_content2 must be bytes") + + @property + def media_content3(self): + return self._media_content3 + + @media_content3.setter + def media_content3(self, media_content3): + if isinstance(media_content3, bytes): + self._media_content3 = media_content3 + else: + raise TypeError("media_content3 must be bytes") + + @property + def media_content5(self): + return self._media_content5 + + @media_content5.setter + def media_content5(self, media_content5): + if isinstance(media_content5, bytes): + self._media_content5 = media_content5 + else: + raise TypeError("media_content5 must be bytes") + + @property + def media_content4(self): + return self._media_content4 + + @media_content4.setter + def media_content4(self, media_content4): + if isinstance(media_content4, bytes): + self._media_content4 = media_content4 + else: + raise TypeError("media_content4 must be bytes") + + @property + def delay_millis(self): + return self._delay_millis + + @delay_millis.setter + def delay_millis(self, delay_millis): + if isinstance(delay_millis, int): + self._delay_millis = delay_millis + else: + raise TypeError("delay_millis must be int") + + @property + def expires_millis(self): + return self._expires_millis + + @expires_millis.setter + def expires_millis(self, expires_millis): + if isinstance(expires_millis, int): + self._expires_millis = expires_millis + else: + raise TypeError("expires_millis must be int") + + + def get_api_name(self): + return "taobao.tmc.message.produce" + + def to_dict(self): + request_dict = {} + if self._content is not None: + request_dict["content"] = convert_basic(self._content) + + if self._ex_content is not None: + request_dict["ex_content"] = convert_basic(self._ex_content) + + if self._target_appkey is not None: + request_dict["target_appkey"] = convert_basic(self._target_appkey) + + if self._target_group is not None: + request_dict["target_group"] = convert_basic(self._target_group) + + if self._topic is not None: + request_dict["topic"] = convert_basic(self._topic) + + if self._delay_millis is not None: + request_dict["delay_millis"] = convert_basic(self._delay_millis) + + if self._expires_millis is not None: + request_dict["expires_millis"] = convert_basic(self._expires_millis) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + if self._media_content is not None: + file_param_dict["media_content"] = convert_basic(self._media_content) + if self._media_content2 is not None: + file_param_dict["media_content2"] = convert_basic(self._media_content2) + if self._media_content3 is not None: + file_param_dict["media_content3"] = convert_basic(self._media_content3) + if self._media_content5 is not None: + file_param_dict["media_content5"] = convert_basic(self._media_content5) + if self._media_content4 is not None: + file_param_dict["media_content4"] = convert_basic(self._media_content4) + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/taobao_tmc_user_cancel_request.py b/libs/topsdk/defaultability/request/taobao_tmc_user_cancel_request.py new file mode 100644 index 0000000..9eb0a4c --- /dev/null +++ b/libs/topsdk/defaultability/request/taobao_tmc_user_cancel_request.py @@ -0,0 +1,62 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcUserCancelRequest(BaseRequest): + + def __init__( + self, + nick: str = None, + user_platform: str = None + ): + """ + 用户昵称 + """ + self._nick = nick + """ + 用户所属的平台类型,tbUIC:淘宝用户; icbu: icbu用户;ae:ae用户 + """ + self._user_platform = user_platform + + @property + def nick(self): + return self._nick + + @nick.setter + def nick(self, nick): + if isinstance(nick, str): + self._nick = nick + else: + raise TypeError("nick must be str") + + @property + def user_platform(self): + return self._user_platform + + @user_platform.setter + def user_platform(self, user_platform): + if isinstance(user_platform, str): + self._user_platform = user_platform + else: + raise TypeError("user_platform must be str") + + + def get_api_name(self): + return "taobao.tmc.user.cancel" + + def to_dict(self): + request_dict = {} + if self._nick is not None: + request_dict["nick"] = convert_basic(self._nick) + + if self._user_platform is not None: + request_dict["user_platform"] = convert_basic(self._user_platform) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/taobao_tmc_user_get_request.py b/libs/topsdk/defaultability/request/taobao_tmc_user_get_request.py new file mode 100644 index 0000000..d7f807c --- /dev/null +++ b/libs/topsdk/defaultability/request/taobao_tmc_user_get_request.py @@ -0,0 +1,81 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcUserGetRequest(BaseRequest): + + def __init__( + self, + fields: list = None, + nick: str = None, + user_platform: str = None + ): + """ + 需返回的字段列表,多个字段以半角逗号分隔。可选值:TmcUser结构体中的所有字段,一定要返回topic。 + """ + self._fields = fields + """ + 用户昵称 + """ + self._nick = nick + """ + 用户所属的平台类型,tbUIC:淘宝用户; icbu: icbu用户;ae:ae用户 + """ + self._user_platform = user_platform + + @property + def fields(self): + return self._fields + + @fields.setter + def fields(self, fields): + if isinstance(fields, list): + self._fields = fields + else: + raise TypeError("fields must be list") + + @property + def nick(self): + return self._nick + + @nick.setter + def nick(self, nick): + if isinstance(nick, str): + self._nick = nick + else: + raise TypeError("nick must be str") + + @property + def user_platform(self): + return self._user_platform + + @user_platform.setter + def user_platform(self, user_platform): + if isinstance(user_platform, str): + self._user_platform = user_platform + else: + raise TypeError("user_platform must be str") + + + def get_api_name(self): + return "taobao.tmc.user.get" + + def to_dict(self): + request_dict = {} + if self._fields is not None: + request_dict["fields"] = convert_basic_list(self._fields) + + if self._nick is not None: + request_dict["nick"] = convert_basic(self._nick) + + if self._user_platform is not None: + request_dict["user_platform"] = convert_basic(self._user_platform) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/defaultability/request/taobao_tmc_user_permit_request.py b/libs/topsdk/defaultability/request/taobao_tmc_user_permit_request.py new file mode 100644 index 0000000..508484c --- /dev/null +++ b/libs/topsdk/defaultability/request/taobao_tmc_user_permit_request.py @@ -0,0 +1,43 @@ +from typing import List +from libs.topsdk.client import BaseRequest +from libs.topsdk.util import convert_struct_list,convert_basic_list,convert_struct,convert_basic +from datetime import datetime + + +class TaobaoTmcUserPermitRequest(BaseRequest): + + def __init__( + self, + topics: list = None + ): + """ + 消息主题列表,用半角逗号分隔。当用户订阅的topic是应用订阅的子集时才需要设置,不设置表示继承应用所订阅的所有topic,一般情况建议不要设置。 + """ + self._topics = topics + + @property + def topics(self): + return self._topics + + @topics.setter + def topics(self, topics): + if isinstance(topics, list): + self._topics = topics + else: + raise TypeError("topics must be list") + + + def get_api_name(self): + return "taobao.tmc.user.permit" + + def to_dict(self): + request_dict = {} + if self._topics is not None: + request_dict["topics"] = convert_basic_list(self._topics) + + return request_dict + + def get_file_param_dict(self): + file_param_dict = {} + return file_param_dict + diff --git a/libs/topsdk/requirements.txt b/libs/topsdk/requirements.txt new file mode 100644 index 0000000..b67f0cc --- /dev/null +++ b/libs/topsdk/requirements.txt @@ -0,0 +1,5 @@ +certifi==2021.10.8 +charset-normalizer==2.0.7 +idna==3.3 +requests==2.26.0 +urllib3==1.26.7 diff --git a/libs/topsdk/top.py b/libs/topsdk/top.py new file mode 100644 index 0000000..30e2e11 --- /dev/null +++ b/libs/topsdk/top.py @@ -0,0 +1,89 @@ +import logging +from datetime import datetime + +from libs.topsdk.client import TopApiClient, TopException +from libs.topsdk.defaultability.defaultability import Defaultability +from libs.topsdk.defaultability.request.alibaba_alsc_union_eleme_promotion_officialactivity_get_request import \ + AlibabaAlscUnionElemePromotionOfficialactivityGetRequest +from libs.topsdk.defaultability.request.alibaba_alsc_union_kbcpx_positive_order_get_request import \ + AlibabaAlscUnionKbcpxPositiveOrderGetRequest +from libs.topsdk.defaultability.request.alibaba_alsc_union_media_zone_add_request import \ + AlibabaAlscUnionMediaZoneAddRequest +from libs.topsdk.defaultability.request.alibaba_alsc_union_media_zone_get_request import \ + AlibabaAlscUnionMediaZoneGetRequest +from libs.topsdk.util import convert_basic + +loger = logging.getLogger('apps') + + +class TopUtils(object): + def __init__(self, appkey, secret, gateway_url='http://gw.api.taobao.com/router/rest'): + self.appkey = appkey + self.secret = secret + self.gateway_url = gateway_url + self.client = TopApiClient(appkey=self.appkey, app_sercet=self.secret, + top_gateway_url=self.gateway_url, + verify_ssl=False) + self.ability = Defaultability(client=self.client) + + def get_order_info(self, start_date, date_type=4, end_date=None, biz_unit=2, page_size=10, page_number=1, **kwargs): + request = AlibabaAlscUnionKbcpxPositiveOrderGetRequest() + request.date_type = date_type + request.end_date = convert_basic(end_date) or datetime.today().strftime('%Y-%m-%d %H:%M:%S') + request.biz_unit = biz_unit + request.page_size = page_size + request.page_number = page_number + request.start_date = convert_basic(start_date) + + if kwargs: + for attr, value in kwargs.items(): + setattr(request, attr, value) + try: + response = self.ability.alibaba_alsc_union_kbcpx_positive_order_get(request) + if response.get('biz_error_code') == 0: + return response.get('result'), response.get('total_count') + except TopException as e: + print(e) + loger.error(f'拉取订单失败, {e}') + + def get_zone_list(self, page=1, limit=10): + request = AlibabaAlscUnionMediaZoneGetRequest() + request.page = page + request.limit = limit + try: + response = self.ability.alibaba_alsc_union_media_zone_get(request) + if response.get('biz_error_code') == 0: + return response.get('result') + except TopException as e: + print(e) + loger.error(f'获取推广位失败, {e}') + + def add_zone(self, zone_name, media_id): + request = AlibabaAlscUnionMediaZoneAddRequest() + request.zone_name = zone_name + request.media_id = media_id + + try: + response = self.ability.alibaba_alsc_union_media_zone_add(request) + if response.get('biz_error_code') == 0: + return response.get('result') + except TopException as e: + print(e) + loger.error(f'创建推广位失败, {e}') + + def get_official_activity(self, pid, activity_id, sid=None, include_wx_img=True, include_qr_code=True): + request = AlibabaAlscUnionElemePromotionOfficialactivityGetRequest(query_request={ + 'pid': pid, + 'activity_id': activity_id, + 'sid': sid, + 'include_wx_img': include_wx_img, + 'include_qr_code': include_qr_code, + }) + + try: + response = self.ability.alibaba_alsc_union_eleme_promotion_officialactivity_get(request) + if response.get('result_code') == 0: + return response.get('data') + except TopException as e: + print(e) + loger.error(f'获取官方活动失败, {e}') diff --git a/libs/topsdk/util.py b/libs/topsdk/util.py new file mode 100644 index 0000000..f04e966 --- /dev/null +++ b/libs/topsdk/util.py @@ -0,0 +1,115 @@ +import hmac +import json +from datetime import datetime, date +from hashlib import md5, sha256 + + +def get_sign(param_dict: dict, request_dict: dict, app_secret: str, sign_method: str) -> str: + if sign_method == "hmac-sha256": + return get_sign_with_hmac_sha256(param_dict, request_dict, app_secret) + elif sign_method == "md5": + return get_sign_with_md5(param_dict, request_dict, app_secret) + else: + raise Exception("Unsupported sign method: " + sign_method) + + +def get_sign_with_md5(param_dict: dict, request_dict: dict, app_secret: str) -> str: + """ + 使用md5计算签名 + :param param_dict: 公共参数字典 + :param request_dict: api参数字典 + :param app_secret: 密钥 + :return: + """ + param_dict.update(request_dict) + keys = list(param_dict.keys()) + keys.sort() + parameters = "%s%s%s" % (app_secret, + str().join( + '%s%s' % (k, param_dict[k]) for k in keys if not isinstance(param_dict[k], bytes)), + app_secret) + sign = md5(parameters.encode("utf-8")).hexdigest().upper() + return sign + + +def get_sign_with_hmac_sha256(param_dict: dict, request_dict: dict, app_secret: str): + """ + 使用hmac sha256计算签名 + :param param_dict: 公共参数字典 + :param request_dict: api参数字典 + :param app_secret: 密钥 + :return: + """ + param_dict.update(request_dict) + keys = list(param_dict.keys()) + keys.sort() + parameters = str().join('%s%s' % (k, param_dict[k]) for k in keys if not isinstance(param_dict[k], bytes)) + sign = hmac.new(app_secret.encode("utf-8"), parameters.encode("utf-8"), digestmod=sha256).hexdigest().upper() + return str(sign) + + +def json_default(value): + if isinstance(value, (datetime, date)): + return value.strftime('%Y-%m-%d %H:%M:%S') + else: + return value.__dict__ + + +class TopJsonEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, (datetime, date)): + return obj.strftime('%Y-%m-%d %H:%M:%S') + else: + return json.JSONEncoder.default(self, obj) + + +def convert_basic(param): + """ + 转换基本类型 + :param param: + :return: + """ + if isinstance(param, (datetime, date)): + return param.strftime("%Y-%m-%d %H:%M:%S") + elif isinstance(param, bool): + return str(param).lower() + elif isinstance(param, bytes): + return param + else: + return str(param) + + +def convert_basic_list(param): + """ + 转换字符串、数字类型列表,列表 -> 逗号分割字符串 + :param param: + :return: + """ + if isinstance(param, (list, tuple, set)): + return ",".join(convert_basic(i) for i in param) + else: + return param + + +def convert_struct(param): + """ + 转换dict 对象类型参数,转json字符串 + :param param: + :return: + """ + if isinstance(param, str): + return param + else: + return json.dumps(param, cls=TopJsonEncoder, default=json_default, ensure_ascii=False) + + +def convert_struct_list(param): + """ + 转换复杂类型列表 + :param param: + :return: + """ + if isinstance(param, (set, list, tuple)): + return json.dumps(param, cls=TopJsonEncoder, default=json_default, ensure_ascii=False) + else: + return str(param) diff --git a/libs/wechat/__init__.py b/libs/wechat/__init__.py new file mode 100644 index 0000000..92f8595 --- /dev/null +++ b/libs/wechat/__init__.py @@ -0,0 +1 @@ +from .worker import WechatWorkerUtil diff --git a/libs/wechat/worker.py b/libs/wechat/worker.py new file mode 100644 index 0000000..1829797 --- /dev/null +++ b/libs/wechat/worker.py @@ -0,0 +1,311 @@ +import requests +import logging +from utils.redis_utils import RedisUtils + +from yzk_wechat_event.settings.constant import Constant + +logger = logging.getLogger('apps') + + +class WechatWorkerUtil(object): + base_url = 'https://qyapi.weixin.qq.com/cgi-bin' + redis_util = RedisUtils() + + def __init__(self, corpid, secret): + self.corpid = corpid + self.secret = secret + + def get_access_token(self): + key = f'{Constant.WECHAT_WORKER_TOKEN}{self.corpid}:{self.secret}' + token = self.redis_util.get(key) + if token: + return token + url = f'{self.base_url}/gettoken?corpid={self.corpid}&corpsecret={self.secret}' + data = requests.get(url).json() + if data.get('errcode') != 0: + logger.error(f'获取token失败--->{data}') + return + token = data.get('access_token') + self.redis_util.set_ex( + key, Constant.WECHAT_WORKER_TOKEN_EXPIRES, token) + return token + + def get_department_list(self, department_id=None): + token = self.get_access_token() + url = f'{self.base_url}/department/list?access_token={token}&id={department_id}' + data = requests.get(url).json() + if data.get('errcode') == 0: + return True, data.get('department') + logger.error(f'获取部门失败--->{data}') + return False, data.get('errmsg') + + def get_sub_department_id(self, department_id=None, token=None): + token = token or self.get_access_token() + url = f'{self.base_url}/department/simplelist?access_token={token}&id={department_id}' + data = requests.get(url).json() + if data.get('errcode') == 0: + department_list = data.get('department_id') + return True, department_list + return False, data.get('errmsg') + + def get_department_user(self, department_id, token=None): + """ + 获取部门成员 + """ + token = token or self.get_access_token() + url = f'{self.base_url}/user/simplelist?access_token={token}&department_id={department_id}' + data = requests.get(url).json() + if data.get('errcode') == 0: + user_list = data.get('userlist') + return True, [user.get('userid') for user in user_list] + logger.error(f'获取用户失败-->{data}') + return False, data.get('errmsg') + + def get_department_user_detail(self, department_id, token=None): + token = token or self.get_access_token() + url = f'{self.base_url}/user/list?access_token={token}&department_id={department_id}' + data = requests.get(url).json() + if data.get('errcode') == 0: + user_list = data.get('userlist') + return True, user_list + return False, data.get('errmsg') + + def get_department_user_by_ids(self, ids): + token = self.get_access_token() + user_ids = [] + for department_id in ids: + user_id_list = self.get_department_user(department_id, token) + user_ids = [*user_ids, *user_id_list] + return user_ids + + def get_dep_user_group_by_dep_id(self, ids): + token = self.get_access_token() + user_id_mapping = {} + for department_id in ids: + user_id_list = self.get_department_user(department_id, token) + user_id_mapping[department_id] = user_id_list + return user_id_mapping + + def get_user_fan_count(self, userid): + token = self.get_access_token() + url = f'{self.base_url}/externalcontact/list?access_token={token}&userid={userid}' + data = requests.get(url).json() + if data.get('errcode') == 0: + external_userid = data.get('external_userid') + return len(external_userid) + return 0 + + def get_user_id_list(self, access_token=None, cursor=None): + access_token = access_token or self.get_access_token() + url = f'{self.base_url}/user/list_id?access_token={access_token}' + data = { + 'limit': 10000, + 'cursor': cursor + } + res = requests.post(url=url, json=data).json() + if res.get('errcode') == 0: + return True, res.get('dept_user'), res.get('next_cursor') + return False, res.get('errmsg'), None + + def get_user(self, user_id, access_token=None): + access_token = access_token or self.get_access_token() + url = f'{self.base_url}/user/get?access_token={access_token}&userid={user_id}' + res = requests.get(url).json() + if res.get('errcode') == 0: + return res + return + + def get_tags(self, access_token=None, body=None): + access_token = access_token or self.get_access_token() + url = f'{self.base_url}/externalcontact/get_corp_tag_list?access_token={access_token}' + res = requests.post(url, json=body).json() + if res.get('errcode') == 0: + return True, res.get('tag_group') + return False, res.get('errmsg') + + def get_external_contact_list(self, userid): + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/list?access_token={access_token}&userid={userid}' + res = requests.get(url).json() + if res.get('errcode') == 0: + return True, res.get('external_userid') + return False, res.get('errmsg') + + def get_external_contact(self, external_userid, cursor=None): + access_token = self.get_access_token() + params = { + 'access_token': access_token, + 'external_userid': external_userid, + } + if cursor: + params['cursor'] = cursor + url = f'{self.base_url}/externalcontact/get' + res = requests.get(url, params=params) + res = res.json() + if res.get('errcode') == 0: + return True, (res.get('external_contact'), res.get('follow_user')) + return False, res.get('errmsg') + + def get_external_contact_batch_by_user(self, userid_list, cursor='', limit=100): + url = f'{self.base_url}/externalcontact/batch/get_by_user?access_token={self.get_access_token()}' + data = { + 'userid_list': userid_list, + 'cursor': cursor, + 'limit': limit + } + res = requests.post(url, json=data).json() + if res.get('errcode') == 0: + return True, res.get('external_contact_list'), res.get('next_cursor') or None + return False, res.get('errmsg'), None + + def add_contact_way( + self, + _type=2, + scene=2, + style=1, + remark=None, + skip_verify=True, + state=None, + user=None, + party=None, + is_temp=False, + expires_in=None, + chat_expires_in=None, + unionid=None, + is_exclusive=False, + conclusions=None + ): + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/add_contact_way?access_token={access_token}' + data = { + 'type': _type, + 'scene': scene, + 'style': style, + 'remark': remark, + 'skip_verify': skip_verify, + 'state': state, + 'user': user, + 'party': party, + 'is_temp': is_temp, + 'expires_in': expires_in, + 'chat_expires_in': chat_expires_in, + 'unionid': unionid, + 'is_exclusive': is_exclusive, + 'conclusions': conclusions + } + res = requests.post(url, json=data).json() + if res.get('errcode') == 0: + return True, (res.get('config_id'), res.get('qr_code')) + return False, res.get('errmsg') + + def update_contact_way(self, config_id, user, skip_verify=True, style=1, remark=None, state=None, party=None, + expires_in=None, chat_expires_in=None, unionid=None, conclusions=None): + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/update_contact_way?access_token={access_token}' + data = { + 'config_id': config_id, + 'remark': remark, + 'user': user, + 'skip_verify': skip_verify, + 'style': style, + 'state': state, + 'party': party, + 'expires_in': expires_in, + 'chat_expires_in': chat_expires_in, + 'unionid': unionid, + 'conclusions': conclusions + } + res = requests.post(url, json=data).json() + if res.get('errcode') == 0: + return True, (res.get('config_id'), res.get('qr_code')) + return False, res.get('errmsg') + + def get_external_groupchat_list(self, owner_filter=None, status_filter=0, cursor=None, limit=1000): + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/groupchat/list?access_token={access_token}' + data = { + 'owner_filter': owner_filter, + 'status_filter': status_filter, + 'cursor': cursor, + 'limit': limit, + } + res = requests.post(url, json=data).json() + if res.get('errcode') == 0: + return True, (res.get('group_chat_list'), res.get('next_cursor')) + return False, res.get('errmsg') + + def get_external_groupchat(self, chat_id, need_name=1): + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/groupchat/get?access_token={access_token}' + data = { + 'chat_id': chat_id, + 'need_name': need_name, + } + res = requests.post(url, json=data).json() + if res.get('errcode') == 0: + return True, res.get('group_chat') + return False, res.get('errmsg') + + def get_externalcontact_follow_user_list(self): + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/get_follow_user_list?access_token={access_token}' + res = requests.get(url=url).json() + if res.get('errcode') == 0: + return True, res.get('follow_user') + return False, res.get('errmsg') + + def get_groupmsg_list_v2(self, start_time, end_time, chat_type='single', creator=None, filter_type=1, limit=100, + cursor=None): + """ + access_token 是 调用接口凭证 + chat_type 是 群发任务的类型,默认为single,表示发送给客户,group表示发送给客户群 + start_time 是 群发任务记录开始时间 + end_time 是 群发任务记录结束时间 + creator 否 群发任务创建人企业账号id + filter_type 否 创建人类型。0:企业发表 1:个人发表 2:所有,包括个人创建以及企业创建,默认情况下为所有类型 + limit 否 返回的最大记录数,整型,最大值100,默认值50,超过最大值时取默认值 + cursor 否 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + """ + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/get_groupmsg_list_v2?access_token={access_token}' + data = { + 'chat_type': chat_type, + 'start_time': start_time, + 'end_time': end_time, + 'creator': creator, + 'filter_type': filter_type, + 'limit': limit, + 'cursor': cursor, + } + res = requests.post(url, json=data).json() + if res.get('errcode') == 0: + return True, (res.get('group_msg_list'), res.get('next_cursor')) + return False, (res.get('errmsg'), None) + + def get_groupmsg_task(self, msgid, limit=1000, cursor=None): + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/get_groupmsg_task?access_token={access_token}' + data = { + 'msgid': msgid, + 'limit': limit, + 'cursor': cursor, + } + res = requests.post(url, json=data).json() + if res.get('errcode') == 0: + return True, (res.get('task_list'), res.get('next_cursor')) + return False, (res.get('errmsg'), None) + + def get_groupmsg_send_result(self, msgid, userid, limit=1000, cursor=None): + access_token = self.get_access_token() + url = f'{self.base_url}/externalcontact/get_groupmsg_send_result?access_token={access_token}' + data = { + 'msgid': msgid, + 'userid': userid, + 'limit': limit, + 'cursor': cursor, + } + res = requests.post(url, json=data).json() + + if res.get('errcode') == 0: + return True, (res.get('send_list'), res.get('next_cursor')) + return False, (res.get('errmsg'), None) diff --git a/logs/.gitignore b/logs/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..4ba3051 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yzk_wechat_event.settings.base') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..daa45519aabf79f92e74e96e418d91d3acdfacd2 GIT binary patch literal 2238 zcma)-%}yIZ5QO_2DUY%UuYn|ooRU)x5r;@Q+iPQtfAKCDc=#msP3s=Suu}h!Yn8ixpHu`*sd3@EYj*}S0nf{)}$M_KMIlqcc zw1d+ZI$igtH*u?~AfG2>(2d*1Ld>XpmEvQ?KMhW{V%mw>N*UV$e&IG1yGHRQv0NvV z(}a-OPZc|;=e>~N;Io;-RQ1y%kocS!~lRHofjosREV3Iu4Tr3i%Ve_VW?XtM=**6MIJ`#O>3du?QQgUN2Krz0EDz$pQI2(| zO6H#@wi6E!_R`4upGiA3IMF2ObtVRO?<9HUkSe`&+V{9geJ@lCrf|UJ=RHD+R?2Ag zs`TJIYA;lQQ!iDR4KOR^&6Co;15_{C4l~IE`Ks4t9>-M0Q{>F>=DqJ^B_>B<7LHa3 zb>{J)r_ns|=vKXuMeitRCoR%uz)SRNchsp{=YfNVw*3@gY3-MqOW7tM+lSnK-;bMK z7h+T##g{=0KeobPa@UEOa|*YWIBjAoh9G2JeW!3$m)y70e>^$W+~|_`TVa=~*>j$5 zog&_IFdio#z=Qk5#D`jWDyTq){k&A3y+rhNM%%~eV~Fqj$~I~HJ6|}m_w2_@Rc9yd zG*vnn^~&XW?Zp9A*l3lWonCA4S?WFcu%Yr;TAOvNDKXu>I;(Ytt;aO;PD2Ui;(NK5 z+W556bFZv&yUDZm%)ROLU~l=>Iqu0jquk?J;_QB{7ZvTmOQ9{)5nH~KHgw_X6ep4M zBYwxP6!osiE+h6+OuzM9iQ*Nb!=(J;&6L-vl5d22qZV^2y>;=_v&h>V&U^L1?RAI$ z-+8+i&57NrhHrc6XsolDSF0&JJa;pa`XzF!PV6?+%|2#t`R2jGZe_#MQQ5HI*wLMM zOp-6~Tvpb*5gzWBUWXXv7tAQQ!M~AH;pW-UtNn7H++>zkJ@;`;%=mp{`rbX+ou186 QE;{P`Ozg@AU>?T)2M0t&xBvhE literal 0 HcmV?d00001 diff --git a/start b/start new file mode 100644 index 0000000..86b4d1e --- /dev/null +++ b/start @@ -0,0 +1,4 @@ +#!/bin/bash + +gunicorn -c yzk_wechat_event/gunicorn.conf.py yzk_wechat_event.wsgi:application & +tail -f logs/*.log diff --git a/start-celeryworker b/start-celeryworker new file mode 100644 index 0000000..37cc69f --- /dev/null +++ b/start-celeryworker @@ -0,0 +1,10 @@ +#!/bin/bash + +set -o errexit + +set -o nounset + + +watchmedo auto-restart -d yzk_wechat_event/ -p "*.py" -- celery -A yzk_wechat_event worker -l info --logfile=logs/celery.log --concurrency=4 -Q celery & +celery -A yzk_wechat_event beat -l info --logfile=logs/celery_beat.log & +tail -f logs/*.log diff --git a/utils/CustomField.py b/utils/CustomField.py new file mode 100644 index 0000000..79c276d --- /dev/null +++ b/utils/CustomField.py @@ -0,0 +1,31 @@ +from rest_framework import serializers + + +class CustomDateField(serializers.DateField): + def __init__(self, *args, **kwargs): + super().__init__(*args, format='%Y-%m-%d', **kwargs) + + +class CustomDateTimeField(serializers.DateTimeField): + def __init__(self, *args, **kwargs): + super().__init__(*args, format='%Y-%m-%d %H:%M:%S', **kwargs) + + +class CustomSerializerMethodField(serializers.SerializerMethodField): + + def __init__(self, method_name=None, **kwargs): + super().__init__(method_name, **kwargs) + self.field_name = None + + def bind(self, field_name, parent): + self.field_name = field_name + return super().bind(field_name, parent) + + def to_representation(self, value): + method = getattr(self.parent, self.method_name) + return method(value, self.field_name) + + +class CustomMoneyField(serializers.IntegerField): + def to_representation(self, value): + return value / 100 if value else 0 diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/base_serializer.py b/utils/base_serializer.py new file mode 100644 index 0000000..03fb45e --- /dev/null +++ b/utils/base_serializer.py @@ -0,0 +1,144 @@ +from rest_framework.exceptions import APIException +from rest_framework.fields import empty + +from rest_framework import serializers, status +from rest_framework.request import Request + +from apps.qc.models import QcWechatbizuserinfo, QcCorpinfo +from apps.qc.utils import get_query_by_corpkey +from apps.user.models import User +from libs.wechat import WechatWorkerUtil + +from utils.CustomField import CustomDateTimeField +from utils.exceptions import CustomProjectException + + +class ValidationError(APIException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = 'Invalid input.' + default_code = 'invalid' + + def __init__(self, detail=None): + self.detail = detail + + +class CurrentUserIdDefault(serializers.CurrentUserDefault): + + def __call__(self, serializer_field): + user = super().__call__(serializer_field) + return user.pk + + +class BaseSerializer(serializers.ModelSerializer): + + def __init__(self, instance=None, data=empty, request=None, **kwargs): + super().__init__(instance, data, **kwargs) + self.request: Request = request or self.context.get("request", None) + self.user = self.request.user if self.request else "AnonymousUser" + + +class QcUserInfoSerializer(BaseSerializer): + userinfo = serializers.SerializerMethodField() + user_mapping = None + + def get_userinfo(self, obj): + view = self.context.get("view") + queryset = self.context.get("queryset") + if not hasattr(view, 'userinfo_fields'): + return + if 'pk' not in view.userinfo_fields: + view.userinfo_fields.append('pk') + if self.user_mapping is None: + self.user_mapping = {} + uids = [q.uid for q in queryset] + users = User.objects.filter( + pk__in=uids).values(*view.userinfo_fields) + for user in users: + self.user_mapping[user.get('pk')] = user + return self.user_mapping.get(obj.uid, {}) + + +class QcWechatUserInfoSerializer(BaseSerializer): + wechat_userinfo = serializers.SerializerMethodField() + wechat_user_mapping = None + + def get_wechat_userinfo(self, obj): + view = self.context.get("view") + queryset = self.context.get("queryset") + if not hasattr(view, 'wechat_userinfo_fields'): + wechat_userinfo_fields = ('username', 'alias', 'userid') + else: + wechat_userinfo_fields = view.wechat_userinfo_fields + if self.wechat_user_mapping is None: + self.wechat_user_mapping = {} + userids = [q.userid for q in queryset] + users = QcWechatbizuserinfo.objects.filter( + userid__in=userids).values(*wechat_userinfo_fields) + for user in users: + self.wechat_user_mapping[user.get('userid')] = user + return self.wechat_user_mapping.get(obj.userid, {}) + + +class CurrentUserSerializer(BaseSerializer): + user = serializers.HiddenField( + default=serializers.CurrentUserDefault() + ) + + +class CurrentUserIdSerializer(BaseSerializer): + uid = serializers.HiddenField( + default=CurrentUserIdDefault() + ) + + +class UserNameSerializer(serializers.ModelSerializer): + user = serializers.StringRelatedField(source='user.username') + + +class DateTimeListSerializer(BaseSerializer): + create_time = CustomDateTimeField(read_only=True) + update_time = CustomDateTimeField(read_only=True) + + +class CorpkeySerializer(BaseSerializer): + corpkey = serializers.SerializerMethodField() + + def get_corpkey(self, obj): + return f'{obj.corpid}-{obj.agentid}' + + +class CorpSerializerMixin: + + def get_corp(self, corpkey=None): + if not hasattr(self, 'corp'): + if corpkey is None: + corpkey = self.request.query_params.get('corpkey') or self.request.data.get('corpkey') + if corpkey is None: + raise ValidationError('corpkey is required') + uid = self.request.user.pk + expression = get_query_by_corpkey(corpkey) + corp = QcCorpinfo.objects.filter(**expression, uid=uid).first() + if corp is None: + raise CustomProjectException(detail='企业不存在') + setattr(self, 'corp', corp) + return getattr(self, 'corp') + + +class WechatWorkerMixin(CorpSerializerMixin): + def get_wechat_worker(self): + if not hasattr(self, 'wechat_worker'): + corp = self.get_corp() + wechat_worker = WechatWorkerUtil(corp.corpid, corp.appsecret) + setattr(self, 'wechat_worker', wechat_worker) + return getattr(self, 'wechat_worker') + + +class SerializerFactory: + + @staticmethod + def build(model, fields, **kwargs): + meta = type(str('Meta'), (object,), {'model': model, 'fields': fields}) + kwargs.update({'Meta': meta}) + serializer_class = type(str('%sModelSerializerByType' % + model._meta.object_name), (BaseSerializer,), kwargs) + return serializer_class diff --git a/utils/base_viewsets.py b/utils/base_viewsets.py new file mode 100644 index 0000000..167d0b4 --- /dev/null +++ b/utils/base_viewsets.py @@ -0,0 +1,199 @@ +import datetime + +from django.db.models import Q +from django.db.models.fields import IntegerField +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated as DRFIsAuthenticated +from rest_framework.viewsets import ModelViewSet, GenericViewSet + +from utils.jwt_authenticate import MyJSONWebTokenAuthentication +from utils.pagination import CustomPagination +from utils.response import ApiResponse +from utils.tools import generate_options_by_choices + + +class IsAuthenticated(DRFIsAuthenticated): + def has_permission(self, request, view): + # 不等于匿名用户 + return bool(request.user and str(request.user) != 'AnonymousUser') + + +class BaseModelViewSet(ModelViewSet): + permission_classes = [IsAuthenticated] + authentication_classes = [MyJSONWebTokenAuthentication] + white_list = [] + filter_fields = [] + fuzzy_filter_fields = [] + exclude_param_blank = False + multi_filter_fields = [] + pagination_class = CustomPagination + + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset()) + _page = request.query_params.get('page') + page_size = request.query_params.get('page_size') + page = self.paginate_queryset(queryset) + context = self.get_serializer_context() + context['queryset'] = page + if page is not None and any([_page, page_size]): + serializer = self.get_serializer(page, many=True, context=context) + return self.get_paginated_response(serializer.data) + serializer = self.get_serializer(queryset, many=True, context=context) + return ApiResponse(data=serializer.data) + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return ApiResponse(data=serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + def retrieve(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(instance) + return ApiResponse(data=serializer.data) + + def update(self, request, *args, **kwargs): + partial = kwargs.pop('partial', False) + instance = self.get_object() + serializer = self.get_serializer( + instance, data=request.data, partial=partial) + serializer.is_valid(raise_exception=True) + self.perform_update(serializer) + + if getattr(instance, '_prefetched_objects_cache', None): + # If 'prefetch_related' has been applied to a queryset, we need to + # forcibly invalidate the prefetch cache on the instance. + instance._prefetched_objects_cache = {} + + return ApiResponse(data=serializer.data) + + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + if hasattr(instance, 'is_delete'): + instance.is_delete = True + instance.save() + else: + instance.delete() + return ApiResponse(message='删除成功', data=None) + + def get_serializer_class(self): + if isinstance(self.serializer_class, dict): + return self.serializer_class.get(self.action) or self.serializer_class.get('default') + + return self.serializer_class + + def check_permissions(self, request): + if request.method.lower() in ['options']: + return True + if self.action in self.white_list: + return True + return super().check_permissions(request) + + def get_queryset(self): + action_queryset_func = getattr( + self, f'filter_{self.action}_queryset', None) + if action_queryset_func is not None and callable(action_queryset_func): + queryset = action_queryset_func() + else: + queryset = super().get_queryset() + q = Q() + if not self.detail: + for key in self.filter_fields: + if key not in self.request.query_params.keys(): + continue + value = self.request.query_params.get(key) + if not value and not self.exclude_param_blank: + continue + if key in self.fuzzy_filter_fields: + q.add(Q(**{f'{key}__icontains': value}), Q.AND) + else: + q.add(Q(**{key: value}), Q.AND) + + for key in self.multi_filter_fields: + list_key = f'{key}[]' + if list_key in self.request.query_params.keys(): + value = self.request.query_params.getlist(list_key) + if value is not None: + q.add(Q(**{f'{key}__in': value}), Q.AND) + if hasattr(self.queryset.model, 'user'): + q.add(Q(user=self.request.user), Q.AND) + if hasattr(self.queryset.model, 'uid'): + uid = self.queryset.model._meta.get_field('uid') + if isinstance(uid, IntegerField): + q.add(Q(Q(uid=self.request.user.id) | Q(uid=0)), Q.AND) + if hasattr(self.queryset.model, 'is_delete'): + q.add(Q(is_delete=False), Q.AND) + if hasattr(self.queryset.model, 'user') and self.action == 'list': + q.add(Q(user_id=self.request.user.id), Q.AND) + # queryset = queryset.select_related('user') + queryset = queryset.filter(q) + return queryset + + +class OrderDataMixin: + + def order_data(self, data): + params = self.request.query_params + field = params.get('field') + if field is None: + return data + order = params.get('order', 'descend') + + def sort_fn(obj): + value = obj.get(field) + try: + value = int(value) + return value + except Exception: + try: + return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S') + except Exception: + return datetime.datetime(year=1998, month=2, day=1) + + return sorted(data, key=sort_fn, reverse=order == 'descend') + + +class ChoicesMixin: + choices_map = {} + + def get_choices(self): + data = {} + for key, choices in self.choices_map.items(): + data[key] = generate_options_by_choices(choices.choices) + return data + + @action(methods=['get'], detail=False) + def choices(self, request, *args, **kwargs): + data = self.get_choices() + return ApiResponse(data=data) + + +class ReadOnlyModelViewSet(BaseModelViewSet): + def destroy(self, request, *args, **kwargs): + return ApiResponse() + + def update(self, request, *args, **kwargs): + return ApiResponse() + + def create(self, request, *args, **kwargs): + return ApiResponse() + + +class BaseChoicesModelViewSet(BaseModelViewSet, ChoicesMixin): + pass + + +class ReadOnlyChoicesModelViewSet(ReadOnlyModelViewSet, ChoicesMixin): + pass + + +class BaseViewSet(GenericViewSet): + permission_classes = [IsAuthenticated] + authentication_classes = [MyJSONWebTokenAuthentication] + + def get_serializer_class(self): + if isinstance(self.serializer_class, dict): + return self.serializer_class.get(self.action) or self.serializer_class.get('default') + return self.serializer_class diff --git a/utils/encryptor.py b/utils/encryptor.py new file mode 100644 index 0000000..0679b9f --- /dev/null +++ b/utils/encryptor.py @@ -0,0 +1,21 @@ +class StringEncryptor: + encrypted_list = ['8', 'h', 'b', '1', '9', 'f', 'p', '0', 'y', 'u', 's', 'i', 'c', 't', '5', 'q', 'e', 'j', 'g', + 'v', 'm', '3', 'x', 'a', '6', 'o', '2', '4', 'k', 'n', 'r', 'z', 'd', '7', 'l', 'w'] + decrypted_list = ['d', '1', 'r', 'b', '5', 'k', 'f', '9', '7', '8', 'j', 's', '3', '2', 'h', 'w', 'y', 'l', '6', + '4', 'c', 'a', 'v', 'n', 'm', 'o', 'p', 'e', 'q', 'z', '0', 'g', 't', 'i', 'x', 'u'] + + @classmethod + def encrypt(cls, string): + hashed_string = '' + for s in string: + encrypted_index = cls.encrypted_list.index(s) + hashed_string += cls.decrypted_list[encrypted_index] + return hashed_string + + @classmethod + def decrypt(cls, hashed_string): + decrypted_string = '' + for s in hashed_string: + decrypted_index = cls.decrypted_list.index(s) + decrypted_string += cls.encrypted_list[decrypted_index] + return decrypted_string diff --git a/utils/exceptions.py b/utils/exceptions.py new file mode 100644 index 0000000..53fbb32 --- /dev/null +++ b/utils/exceptions.py @@ -0,0 +1,68 @@ +import traceback + +from django.core.exceptions import PermissionDenied +from django.http import Http404 + +from rest_framework import exceptions +from rest_framework.views import set_rollback +import logging + +from utils.response import ApiResponse + +logger = logging.getLogger('log') + + +def custom_exception(exc, context): + if isinstance(exc, Http404): + exc = exceptions.NotFound() + elif isinstance(exc, PermissionDenied): + exc = exceptions.PermissionDenied() + message = "服务器内部错误" + headers = {} + code = 400 + if isinstance(exc, exceptions.APIException): + code = exc.status_code + if getattr(exc, 'auth_header', None): + headers['WWW-Authenticate'] = exc.auth_header + if getattr(exc, 'wait', None): + headers['Retry-After'] = '%d' % exc.wait + + if isinstance(exc.detail, dict): + try: + if hasattr(exc.detail.serializer, 'Meta'): + model = exc.detail.serializer.Meta.model + message = '' + for field, e in exc.detail.items(): + if hasattr(model, field): + verbose_name = model._meta.get_field(field).verbose_name + # message += verbose_name + ','.join(e) + ',' + message += verbose_name + for error in e: + if isinstance(error, str): + message += f',{error}' + if isinstance(error, dict): + values = error.values() + for value in values: + if isinstance(value, list): + message += ',' + ','.join(value) + else: + message += f',{value}' + else: + message += ','.join(e) + ',' + else: + message = exc.detail + except Exception: + message = exc.detail + else: + message = exc.detail + set_rollback() + try: + logger.error(traceback.format_exc()) + return ApiResponse(code=code, message=str(message), status=200, headers=headers) + except Exception: + logger.error(traceback.format_exc()) + return ApiResponse(code=code, message=str(message), status=200, headers=headers) + + +class CustomProjectException(exceptions.APIException): + pass diff --git a/utils/jwt.py b/utils/jwt.py new file mode 100644 index 0000000..cadd295 --- /dev/null +++ b/utils/jwt.py @@ -0,0 +1,20 @@ +def jwt_response_payload_handler(token, user=None, request=None): + """ + Returns the response data for both the login and refresh views. + Override to return a custom response such as including the + serialized representation of the User. + + Example: + + def jwt_response_payload_handler(token, user=None, request=None): + return { + 'token': token, + 'user': UserSerializer(user, context={'request': request}).data + } + + """ + return { + 'id': user.id, + 'token': token, + 'username': user.username, + } diff --git a/utils/jwt_authenticate.py b/utils/jwt_authenticate.py new file mode 100644 index 0000000..53e72e8 --- /dev/null +++ b/utils/jwt_authenticate.py @@ -0,0 +1,101 @@ +import jwt +from django.utils.translation import gettext_lazy as _ +from rest_framework import HTTP_HEADER_ENCODING +from rest_framework import exceptions +from rest_framework.authentication import BaseAuthentication +from rest_framework_jwt.authentication import JSONWebTokenAuthentication +from rest_framework_jwt.settings import api_settings + +from apps.user.models import User +from utils.redis_utils import RedisUtils + +jwt_decode_handler = api_settings.JWT_DECODE_HANDLER +jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER + + +def get_authorization_header(request, key='HTTP_AUTHORIZATION'): + """ + Return request's 'Authorization:' header, as a bytestring. + + Hide some test client ickyness where the header can be unicode. + """ + auth = request.META.get(key, b'') + if isinstance(auth, str): + # Work around django test client oddness + auth = auth.encode(HTTP_HEADER_ENCODING) + return auth + + +class MyJSONWebTokenAuthentication(JSONWebTokenAuthentication): + + def authenticate(self, request): + """ + Returns a two-tuple of `User` and token if a valid signature has been + supplied using JWT-based authentication. Otherwise returns `None`. + """ + jwt_value = self.get_jwt_value(request) + if jwt_value is None: + return None + + redis_utils = RedisUtils("token") + payload = None + try: + payload = redis_utils.read_value_to_object(jwt_value) or jwt_decode_handler(jwt_value) + if not payload: + msg = _('Signature has expired.') + raise exceptions.AuthenticationFailed(msg) + except jwt.ExpiredSignature: + msg = _('Signature has expired.') + raise exceptions.AuthenticationFailed(msg) + except jwt.DecodeError: + msg = _('Error decoding signature.') + raise exceptions.AuthenticationFailed(msg) + except jwt.InvalidTokenError: + raise exceptions.AuthenticationFailed() + redis_utils.write_value_as_string(jwt_value, payload, 60 * 60 * 30) + user = self.authenticate_credentials(payload) + + return user, jwt_value + + def authenticate_credentials(self, payload): + """ + Returns an active user that matches the payload's user id and email. + """ + username = jwt_get_username_from_payload(payload) + + if not username: + msg = _('Invalid payload.') + raise exceptions.AuthenticationFailed(msg) + + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + msg = _('Invalid signature.') + raise exceptions.AuthenticationFailed(msg) + + return user + + def get_jwt_value(self, request): + + auth = get_authorization_header(request) + + if not auth: + if api_settings.JWT_AUTH_COOKIE: + return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE) + return None + + return auth + + +class UserAppSecretAuthentication(BaseAuthentication): + def authenticate(self, request): + app_secret = get_authorization_header(request, key='APP_SECRET') + if not app_secret: + raise exceptions.AuthenticationFailed() + user = User.objects.filter(app_secret=app_secret).first() + if user is None or not user.is_active: + raise exceptions.AuthenticationFailed() + return user, app_secret + + def authenticate_header(self, request): + pass diff --git a/utils/logger.py b/utils/logger.py new file mode 100644 index 0000000..dd91f7e --- /dev/null +++ b/utils/logger.py @@ -0,0 +1,11 @@ +import logging + + +class CustomFileHandler(logging.FileHandler): + def _get_or_create_filename(self, suffix): + # Override the method to include the date in the filename + if self.stream is None: + # Reopen the file with a new filename that includes the date + filename = self.baseFilename + suffix + self.stream = self._open(filename, 'a') + return self.baseFilename diff --git a/utils/pagination.py b/utils/pagination.py new file mode 100644 index 0000000..26089a2 --- /dev/null +++ b/utils/pagination.py @@ -0,0 +1,49 @@ +from collections import OrderedDict + +from django.core.paginator import InvalidPage +from rest_framework.pagination import PageNumberPagination +from rest_framework.response import Response + + +class CustomPagination(PageNumberPagination): + page_size = 9999 + page_query_param = 'page' + page_size_query_param = 'page_size' + max_page_size = 1000 + + def __init__(self): + self.total = 0 + + def paginate_queryset(self, queryset, request, view=None): + page_size = self.get_page_size(request) + if not page_size: + return None + + paginator = self.django_paginator_class(queryset, page_size) + page_number = self.get_page_number(request, paginator) + self.total = paginator.count + # + # if (total // page_size) == 0: + # page_number = 1 + # else: + # page_number = (total // page_size) if int(page_number) > (total // page_size) + 1 else page_number + + try: + self.page = paginator.page(page_number) + except InvalidPage as exc: + return [] + + if paginator.num_pages > 1 and self.template is not None: + # The browsable API should display pagination controls. + self.display_page_controls = True + + self.request = request + return list(self.page) + + def get_paginated_response(self, data): + return Response(OrderedDict([ + ('total', self.total), + ('code', 0), + ('message', 'ok'), + ('result', data), + ])) diff --git a/utils/redis_utils.py b/utils/redis_utils.py new file mode 100644 index 0000000..65d1e49 --- /dev/null +++ b/utils/redis_utils.py @@ -0,0 +1,39 @@ +import json + +from django_redis import get_redis_connection + + +class RedisUtils: + def __init__(self, cache='default'): + self.redis_conn = get_redis_connection(cache) + + def write_value_as_string(self, key, value, ex=None): + if ex: + return self.set_ex(key, ex, json.dumps(value)) + return self.set(key, json.dumps(value)) + + def read_value_to_object(self, key): + value = self.redis_conn.get(key) + if value: + return json.loads(value) + + def set(self, key, value): + return self.redis_conn.set(key, value) + + def get(self, key): + value = self.redis_conn.get(key) + if value: + return value.decode() + + def set_ex(self, key, timeout, value): + return self.redis_conn.setex(key, time=timeout, value=value) + + def hset(self, key, field, value): + return self.redis_conn.hset(key, field, value) + + def hget(self, key, field): + value = self.redis_conn.hget(key, field) + return value and value.decode() + + def delete(self, key): + return self.redis_conn.delete(key) diff --git a/utils/renders.py b/utils/renders.py new file mode 100644 index 0000000..bc5c587 --- /dev/null +++ b/utils/renders.py @@ -0,0 +1,11 @@ +from rest_framework import renderers + + +class FileRender(renderers.BaseRenderer): + charset = None + render_style = 'binary' + media_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + format = 'xls' + + def render(self, data, accepted_media_type=None, renderer_context=None): + return data.get('data') diff --git a/utils/response.py b/utils/response.py new file mode 100644 index 0000000..4a5e85b --- /dev/null +++ b/utils/response.py @@ -0,0 +1,19 @@ +from rest_framework.response import Response + + +class ApiResponse(Response): + + def __init__(self, code=None, message="ok", data=None, status=200, total=None, res_type='success', + template_name=None, headers=None, exception=False, content_type='application/json'): + super(Response, self).__init__(None, status=status) + code = code or 0 + self.data = {"code": code, "message": message, "result": data, 'type': res_type} + if total: + self.data.update({'total': total}) + self.template_name = template_name + self.exception = exception + self.content_type = content_type + + if headers: + for name, value in headers.items(): + self[name] = value diff --git a/utils/throttling.py b/utils/throttling.py new file mode 100644 index 0000000..62178fb --- /dev/null +++ b/utils/throttling.py @@ -0,0 +1,42 @@ +from django.core.exceptions import ImproperlyConfigured +from rest_framework.exceptions import APIException +from rest_framework.throttling import SimpleRateThrottle + + +class ThrottleBase(SimpleRateThrottle): + """ + 接口访问频率限制基类 + """ + throttled_message = '请求评率超过限制' + rate = '3/m' + + def get_cache_key(self, request, view): + super().get_cache_key(request, view) + + def allow_request(self, request, view): + flag = super().allow_request(request, view) + if not flag: + raise APIException(detail=self.throttled_message, code=500) + return flag + + def get_rate(self): + """ + Determine the string representation of the allowed request rate. + """ + if not getattr(self, 'scope', None): + msg = ("You must set either `.scope` or `.rate` for '%s' throttle" % + self.__class__.__name__) + raise ImproperlyConfigured(msg) + return self.rate + + +class PhoneCodeThrottle(ThrottleBase): + rate = '1/m' + scope = "phone_code" + throttled_message = "您的请求频率过快,请稍后再试。" + + def get_cache_key(self, request, view): + return self.cache_format % { + 'scope': self.scope, + 'ident': f"{request.data.get('mobile')}-{request.data.get('business')}" + } diff --git a/utils/tools.py b/utils/tools.py new file mode 100644 index 0000000..8f293a7 --- /dev/null +++ b/utils/tools.py @@ -0,0 +1,243 @@ +import datetime +import random +import re +from hashlib import md5 +from typing import Any +import uuid +from calendar import timegm + +from rest_framework_jwt.serializers import jwt_encode_handler +from rest_framework_jwt.settings import api_settings + + +def convert_to_relative_time(time_string): + # 将时间字符串转换为datetime对象 + if not isinstance(time_string, datetime.datetime): + time = datetime.datetime.strptime(time_string, "%Y-%m-%d %H:%M:%S") + else: + time = time_string + + # 获取当前时间 + current_time = datetime.datetime.now() + + # 计算时间差 + time_difference = current_time - time + + # 提取相对时间的各个单位 + days = time_difference.days + seconds = time_difference.seconds + minutes = seconds // 60 + hours = minutes // 60 + + # 根据时间差生成相对时间字符串 + if days > 0: + return f"{days}天前" + elif hours > 0: + return f"{hours}小时前" + elif minutes > 0: + return f"{minutes}分钟前" + else: + return "刚刚" + + +def generate_options_by_choices(choices): + data = [] + for value, label in choices: + data.append({ + 'value': value, + 'label': label, + }) + return data + + +def jwt_payload_handler(user): + username_field = 'username' + username = user.username + + payload = { + 'user_id': user.pk, + 'username': username, + 'exp': datetime.datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA + } + if hasattr(user, 'email'): + payload['email'] = user.email + if isinstance(user.pk, uuid.UUID): + payload['user_id'] = str(user.pk) + + payload[username_field] = username + + # Include original issued at time for a brand new token, + # to allow token refresh + if api_settings.JWT_ALLOW_REFRESH: + payload['orig_iat'] = timegm( + datetime.datetime.utcnow().utctimetuple() + ) + + if api_settings.JWT_AUDIENCE is not None: + payload['aud'] = api_settings.JWT_AUDIENCE + + if api_settings.JWT_ISSUER is not None: + payload['iss'] = api_settings.JWT_ISSUER + + return payload + + +def create_jwt(user): + payload = jwt_payload_handler(user) + return jwt_encode_handler(payload) + + +def generate_columns(model): + columns = [] + fields = model._meta.fields + for field in fields: + column = { + "title": field.verbose_name, + "dataIndex": field.name, + "align": 'center', + "slots": {"customRender": field.name}, + } + columns.append(column) + return columns + + +def md5_obj(byte_obj: Any): + md = md5() + md.update(byte_obj) + + return md.hexdigest() + + +class InviteCode: + r1 = ["Z", "X", "9", "L", "C", "7", "P", "Q", "W", "E", "8", "S", "2", "D", "5", "K", "3", "M", "J", "U", "F", "R", + "4", "V", "Y", "T", "N", "6", "B", "G", "H"] + r2 = ["C", "P", "K", "M", "Q", "W", "E", "S", "D", "Z", "X", "L", "J", "U", "F", "R", "V", "Y", "T", "N", "B", "G", + "H", "A"] + r3 = ["P", "5", "K", "3", "M", "J", "U", "F", "Q", "L", "W", "E", "8", "S", "2", "D", "Z", "X", "9", "Y", "T", "N", + "6", "B", "G", "H", "C", "7", "R", "4", "V"] + r4 = ["J", "U", "F", "R", "V", "Y", "Q", "W", "E", "S", "L", "K", "M", "D", "Z", "X", "A", "C", "P", "G", "H", "T", + "N", "B"] + r5 = ["M", "J", "U", "F", "R", "4", "Q", "W", "E", "8", "S", "2", "D", "Z", "X", "9", "L", "C", "7", "P", "5", "K", + "3", "V", "Y", "T", "N", "6", "B", "G", "H"] + r6 = ["Y", "T", "N", "B", "G", "H", "L", "C", "P", "Q", "W", "E", "S", "D", "Z", "X", "R", "V", "K", "M", "J", "U", + "F", "A"] + r7 = ["X", "9", "L", "C", "7", "P", "5", "K", "3", "Q", "W", "E", "8", "S", "2", "D", "Z", "M", "J", "U", "F", "R", + "4", "V", "Y", "T", "N", "6", "B", "G", "H"] + + @staticmethod + def create_code(id): + bin_len = 31 + ind = 0 + i = 1 + builder = [] + while id / bin_len > 0: + ind = int(id % bin_len) + if ind != 0 or (ind == 0 and id == bin_len) or (ind == 0 or i == 1): + if i == 1: + builder.append(InviteCode.r7[ind]) + id = int(id / len(InviteCode.r7)) + bin_len = len(InviteCode.r6) + elif i == 2: + builder.append(InviteCode.r6[ind]) + id = int(id / len(InviteCode.r6)) + bin_len = len(InviteCode.r5) + elif i == 3: + builder.append(InviteCode.r5[ind]) + id = int(id / len(InviteCode.r5)) + bin_len = len(InviteCode.r4) + elif i == 4: + builder.append(InviteCode.r4[ind]) + id = int(id / len(InviteCode.r4)) + bin_len = len(InviteCode.r3) + elif i == 5: + builder.append(InviteCode.r3[ind]) + id = int(id / len(InviteCode.r3)) + bin_len = len(InviteCode.r2) + elif i == 6: + builder.append(InviteCode.r2[ind]) + id = int(id / len(InviteCode.r2)) + bin_len = len(InviteCode.r1) + elif i == 7: + builder.append(InviteCode.r1[ind]) + id = int(id / len(InviteCode.r1)) + bin_len = len(InviteCode.r1) + i += 1 + + index = 0 + first_index = id if id > 0 and id / bin_len == 0 else 0 + if i == 1: + builder.append( + InviteCode.r7[id] + InviteCode.r6[index] + InviteCode.r5[index] + InviteCode.r4[index] + InviteCode.r3[ + index] + InviteCode.r2[index] + InviteCode.r1[index]) + elif i == 2: + builder.append( + InviteCode.r6[first_index] + InviteCode.r5[index] + InviteCode.r4[index] + InviteCode.r3[index] + + InviteCode.r2[index] + InviteCode.r1[index]) + elif i == 3: + builder.append( + InviteCode.r5[first_index] + InviteCode.r4[index] + InviteCode.r3[index] + InviteCode.r2[index] + + InviteCode.r1[index]) + elif i == 4: + builder.append( + InviteCode.r4[first_index] + InviteCode.r3[index] + InviteCode.r2[index] + InviteCode.r1[index]) + elif i == 5: + builder.append(InviteCode.r3[first_index] + InviteCode.r2[index] + InviteCode.r1[index]) + elif i == 6: + builder.append(InviteCode.r2[first_index] + InviteCode.r1[index]) + elif i == 7: + builder.append(InviteCode.r1[first_index]) + + str = ''.join(builder) + res = str[::-1] + code = res + return code + + @staticmethod + def decode(code): + chs = list(code) + res = 0 + c1 = chs[0] + index_of_c1 = InviteCode.r1.index(c1) + if index_of_c1 > 0: + temp = 24 * 31 * 24 * 31 * 24 * 31 + res += index_of_c1 * temp + c2 = chs[1] + index_of_c2 = InviteCode.r2.index(c2) + if index_of_c2 > 0: + temp = 31 * 24 * 31 * 24 * 31 + res += index_of_c2 * temp + c3 = chs[2] + index_of_c3 = InviteCode.r3.index(c3) + if index_of_c3 > 0: + temp = 24 * 31 * 24 * 31 + res += index_of_c3 * temp + c4 = chs[3] + index_of_c4 = InviteCode.r4.index(c4) + if index_of_c4 > 0: + temp = 31 * 24 * 31 + res += index_of_c4 * temp + c5 = chs[4] + index_of_c5 = InviteCode.r5.index(c5) + if index_of_c5 > 0: + temp = 24 * 31 + res += index_of_c5 * temp + c6 = chs[5] + index_of_c6 = InviteCode.r6.index(c6) + if index_of_c6 > 0: + temp = 31 + res += index_of_c6 * temp + c7 = chs[6] + index_of_c7 = InviteCode.r7.index(c7) + if index_of_c7 > 0: + res += index_of_c7 * 1 + return res + + +def is_mobile(mobile): + return bool(re.match(r'^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$', mobile)) + + +def generate_phone_code(length=6) -> str: + """ 生成手机验证码 """ + number_list = range(0, 10) + return ''.join(map(lambda i: str(i), random.choices(number_list, k=length))) diff --git a/yzk_wechat_event/__init__.py b/yzk_wechat_event/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yzk_wechat_event/asgi.py b/yzk_wechat_event/asgi.py new file mode 100644 index 0000000..373fde0 --- /dev/null +++ b/yzk_wechat_event/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for yzk_wechat_event project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yzk_wechat_event.settings') + +application = get_asgi_application() diff --git a/yzk_wechat_event/celery.py b/yzk_wechat_event/celery.py new file mode 100644 index 0000000..966d0fd --- /dev/null +++ b/yzk_wechat_event/celery.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import +import os +from datetime import timedelta + +from celery import Celery + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yzk_wechat_event.settings.base') + +app = Celery('yzk_wechat_event') + +app.config_from_object('yzk_wechat_event.settings.base', namespace='CELERY') + +app.autodiscover_tasks() + +# 定时任务的调度列表,用于注册定时任务 +app.conf.beat_schedule = { + 'warning_log': { + 'task': 'warning_log', + 'schedule': timedelta(minutes=1), + }, + 'check_authorization_task': { + 'task': 'check_authorization_task', + 'schedule': timedelta(minutes=30), + }, +} + +task_routes = { +} +# +app.conf.task_routes = task_routes +# app.conf.ONCE = { +# 'backend': 'celery_once.backends.Redis', +# 'settings': { +# 'url': 'redis://localhost:6379/12', +# 'default_timeout': 60 * 60 * 24, +# } +# } diff --git a/yzk_wechat_event/gunicorn.conf.py b/yzk_wechat_event/gunicorn.conf.py new file mode 100644 index 0000000..0d9f063 --- /dev/null +++ b/yzk_wechat_event/gunicorn.conf.py @@ -0,0 +1,17 @@ +import multiprocessing + +bind = "0.0.0.0:8000" # 绑定的ip与端口 +backlog = 512 # 监听队列数量,64-2048 +# chdir = '/home/test/server/bin' #gunicorn要切换到的目的工作目录 +worker_class = 'sync' # 使用gevent模式,还可以使用sync 模式,默认的是sync模式 +workers = multiprocessing.cpu_count() # 进程数 +threads = multiprocessing.cpu_count() * 4 # 指定每个进程开启的线程数 +loglevel = 'info' # 日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置 +access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' + +accesslog = "logs/gunicorn_access.log" # 访问日志文件 +errorlog = "logs/gunicorn_error.log" # 错误日志文件 +# accesslog = "-" # 访问日志文件,"-" 表示标准输出 +# errorlog = "-" # 错误日志文件,"-" 表示标准输出 + +proc_name = 'yzk_wechat_event' # 进程名 diff --git a/yzk_wechat_event/routers.py b/yzk_wechat_event/routers.py new file mode 100644 index 0000000..86d066b --- /dev/null +++ b/yzk_wechat_event/routers.py @@ -0,0 +1,24 @@ +class DatabaseRouter: + def db_for_read(self, model, **hints): + # 返回用于读取的数据库别名 + db_table = model._meta.db_table + suffix = db_table.split('_')[0] + if suffix: + return suffix + return 'default' + + def db_for_write(self, model, **hints): + # 返回用于写入的数据库别名 + db_table = model._meta.db_table + suffix = db_table.split('_')[0] + if suffix: + return suffix + return 'default' + + def allow_relation(self, obj1, obj2, **hints): + # 确定两个对象之间是否允许建立关联关系 + return True + + def allow_migrate(self, db, app_label, model_name=None, **hints): + # 确定给定应用程序的模型是否允许在给定数据库上进行迁移 + return True diff --git a/yzk_wechat_event/settings/__init__.py b/yzk_wechat_event/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yzk_wechat_event/settings/base.py b/yzk_wechat_event/settings/base.py new file mode 100644 index 0000000..03d15fb --- /dev/null +++ b/yzk_wechat_event/settings/base.py @@ -0,0 +1,263 @@ +import os +import time +from pathlib import Path + +from django.utils.log import DEFAULT_LOGGING +import django +from django.utils.encoding import smart_str, force_str +from django.utils.translation import gettext_lazy + + +django.utils.encoding.smart_text = smart_str +django.utils.encoding.force_text = force_str +django.utils.translation.ugettext = gettext_lazy + +BASE_DIR = Path(__file__).resolve().parent.parent + +SECRET_KEY = 'django-insecure-=2^vkg^oi_w#zh#k$g3ie@!b64m5r@&sm%yt08g#0nudbovq#*' + +DEBUG = False + +ALLOWED_HOSTS = ["*"] + +DJANGO_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +THIRD_PARTY_APPS = [ + "rest_framework", +] + +LOCAL_APPS = ["apps.jqr"] + +INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'yzk_wechat_event.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'yzk_wechat_event.wsgi.application' +DATABASE_ROUTERS = [ + 'yzk_wechat_event.routers.DatabaseRouter', +] +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'hdy', + 'USER': 'fw', + 'PASSWORD': 'dqw895dc$562JWBNW6812', + 'HOST': 'pgm-ly288x89fop3t0mz.pg.rds.aliyuncs.com', + 'PORT': 5432, + }, + 'py': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'hdy', + 'USER': 'fw', + 'PASSWORD': 'dqw895dc$562JWBNW6812', + 'HOST': 'pgm-ly288x89fop3t0mz.pg.rds.aliyuncs.com', + 'PORT': 5432, + }, + 'pb': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'xbtool', + 'USER': 'fw', + 'PASSWORD': 'dqw895dc$562JWBNW6812', + 'HOST': 'pgm-ly288x89fop3t0mz.pg.rds.aliyuncs.com', + 'PORT': 5432, + }, + 'qc': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'qwhm', + 'USER': 'fw', + 'PASSWORD': 'dqw895dc$562JWBNW6812', + 'HOST': 'pgm-ly288x89fop3t0mz.pg.rds.aliyuncs.com', + 'PORT': 5432, + }, + 'jqr': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'qwjqr', + 'USER': 'fw', + 'PASSWORD': 'dqw895dc$562JWBNW6812', + 'HOST': 'pgm-ly288x89fop3t0mz.pg.rds.aliyuncs.com', + 'PORT': 5432, + }, +} + +REDIS_HOST = '47.110.92.205' +REDIS_PORT = 7000 +REDIS_URI = f'redis://{REDIS_HOST}:{REDIS_PORT}' + +# 缓存配置 +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": f"{REDIS_URI}/11", + "OPTIONS": { + "IGNORE_EXCEPTIONS": True, + "CLIENT_CLASS": "django_redis.client.DefaultClient", + } + }, + "token": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": f"{REDIS_URI}/12", + "OPTIONS": { + "IGNORE_EXCEPTIONS": True, + "CLIENT_CLASS": "django_redis.client.DefaultClient", + } + } +} + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +USE_TZ = False +USE_I18N = True +USE_L10N = True +TIME_ZONE = "Asia/Shanghai" +LANGUAGE_CODE = "zh-hans" + +STATIC_URL = "/staticfiles/" +STATIC_ROOT = BASE_DIR / "staticfiles" +STATICFILES_DIR = [] +MEDIA_URL = "" +MEDIA_ROOT = BASE_DIR.parent / "" + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +REST_FRAMEWORK = { + # 自定义异常处理 + 'EXCEPTION_HANDLER': 'utils.exceptions.custom_exception', + # 自定义认证配置 + 'DEFAULT_AUTHENTICATION_CLASSES': ( + ), +} + +CELERY_BROKER_URL = f"{REDIS_URI}/8" +CELERY_RESULT_BACKEND = f"{REDIS_URI}/9" +CELERY_TIMEZONE = 'Asia/Shanghai' + +# # OSS 配置项 +# DEFAULT_FILE_STORAGE = 'django_oss_storage.backends.OssMediaStorage' +# OSS_ENDPOINT = "oss-cn-hangzhou.aliyuncs.com" +# OSS_ACCESS_KEY_ID = "LTAI5tQtisiTNSQy6Z1SmX8o" # AliCloud access key ID +# OSS_ACCESS_KEY_SECRET = "O8Ue8A6uvnIQ58coZtTG1279i0086L" # AliCloud access key secret +# OSS_BUCKET_NAME = 'xbtool' # The name of the bucket to store files in + +try: + from .local import * +except Exception as e: + print(e) + import logging.config + import logging + + logger = logging.getLogger(__name__) + + LOG_LEVEL = "INFO" + + if not os.path.exists("logs"): + os.makedirs("logs") + + log_filename = os.path.join("logs", "real_estate.log") + LOG_DIR = os.path.dirname(os.path.dirname( + os.path.dirname(os.path.abspath(__file__)))) + "/logs" + + logging.config.dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "formatters": { + 'standard': { + 'format': '[%(asctime)s] [%(filename)s:%(lineno)d] [%(module)s:%(funcName)s] ' + '[%(levelname)s]- %(message)s' + }, + "console": { + "format": "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" + }, + "file": { + "format": "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" + }, + "django.server": DEFAULT_LOGGING["formatters"]["django.server"], + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "formatter": "console" + }, + # 默认记录所有日志 + 'default': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(LOG_DIR, 'all-{}.log'.format(time.strftime('%Y-%m-%d'))), + 'maxBytes': 1024 * 1024 * 5, # 文件大小 + 'backupCount': 5, # 备份数 + 'formatter': 'standard', # 输出格式 + 'encoding': 'utf-8', # 设置默认编码,否则打印出来汉字乱码 + }, + # 输出错误日志 + 'error': { + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(LOG_DIR, 'error-{}.log'.format(time.strftime('%Y-%m-%d'))), + 'maxBytes': 1024 * 1024 * 5, # 文件大小 + 'backupCount': 5, # 备份数 + 'formatter': 'standard', # 输出格式 + 'encoding': 'utf-8', # 设置默认编码 + }, + # 输出info日志 + 'info': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(LOG_DIR, 'info-{}.log'.format(time.strftime('%Y-%m-%d'))), + 'maxBytes': 1024 * 1024 * 5, + 'backupCount': 5, + 'formatter': 'standard', + 'encoding': 'utf-8', # 设置默认编码 + }, + "django.server": DEFAULT_LOGGING["handlers"]["django.server"], + }, + "loggers": { + '': {"level": "INFO", "handlers": ["console", "info", "error", "default"], "propagate": False}, + "apps": {"level": "INFO", "handlers": ["console", "info", "error", "default"], "propagate": False}, + "django.server": DEFAULT_LOGGING["loggers"]["django.server"], + } + }) diff --git a/yzk_wechat_event/settings/constant.py b/yzk_wechat_event/settings/constant.py new file mode 100644 index 0000000..687b283 --- /dev/null +++ b/yzk_wechat_event/settings/constant.py @@ -0,0 +1,33 @@ +class Constant: + REG_SMS = 'reg:sms:' + LOGIN_SMS = 'login:sms:' + WECHAT_WORKER_TOKEN = 'wechat:worker:token:' + WECHAT_WORKER_TOKEN_EXPIRES = 6000 + ONE = 1 + TWO = 2 + THREE = 3 + FOUR = 4 + FIVE = 5 + TEN = 10 + SIXTY = 60 + + +class MiddlePageConstant: + # 快站(跳转) + QUICK_STATION = "1" + # 非快站(302) + NO_QUICK_STATION = "0" + # 饿了么口令活动id + ELM_ACTIVITY_ID = 10690 + + +SMS_BUSINESS_MAP = { + 'register': { + 'key': Constant.REG_SMS, + 'expire': Constant.FIVE * Constant.SIXTY, + }, + 'login': { + 'key': Constant.LOGIN_SMS, + 'expire': Constant.FIVE * Constant.SIXTY, + }, +} diff --git a/yzk_wechat_event/urls.py b/yzk_wechat_event/urls.py new file mode 100644 index 0000000..349ebd9 --- /dev/null +++ b/yzk_wechat_event/urls.py @@ -0,0 +1,22 @@ +""" +URL configuration for yzk_wechat_event project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + path('admin/', admin.site.urls), +] diff --git a/yzk_wechat_event/wsgi.py b/yzk_wechat_event/wsgi.py new file mode 100644 index 0000000..f369557 --- /dev/null +++ b/yzk_wechat_event/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for yzk_wechat_event project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yzk_wechat_event.settings') + +application = get_wsgi_application()