├── .github └── workflows │ ├── go.yml │ ├── goreleaser.yml │ └── goreportcard.yml ├── .gitignore ├── .goreleaser.yml ├── LICENSE ├── README.md ├── api ├── auth │ ├── const.go │ ├── doc.go │ ├── token.go │ ├── token_create.go │ ├── token_refresh.go │ └── url.go ├── ddk │ ├── cashgift_create.go │ ├── cashgift_status_update.go │ ├── cms_prom_url_generate.go │ ├── doc.go │ ├── goods.go │ ├── goods_detail.go │ ├── goods_pid_generate.go │ ├── goods_pid_query.go │ ├── goods_promotion_right_auth.go │ ├── goods_promotion_url_generate.go │ ├── goods_recommend_get.go │ ├── goods_search.go │ ├── goods_zs_unit_url_gen.go │ ├── member_authority_query.go │ ├── oauth │ │ ├── all_order_list_increment_get.go │ │ ├── cashgift_create.go │ │ ├── cashgift_status_update.go │ │ ├── cms_prom_url_generate.go │ │ ├── doc.go │ │ ├── goods_detail.go │ │ ├── goods_pid_generate.go │ │ ├── goods_pid_query.go │ │ ├── goods_promotion_url_generate.go │ │ ├── goods_recommend_get.go │ │ ├── goods_search.go │ │ ├── goods_zs_unit_url_gen.go │ │ ├── member_authority_query.go │ │ ├── order_detail_get.go │ │ ├── pid_mediaid_bind.go │ │ ├── resource_url_gen.go │ │ ├── rp_prom_url_generate.go │ │ └── weapp_qrcode_url_gen.go │ ├── order.go │ ├── order_detail_get.go │ ├── order_list_increment_get.go │ ├── order_list_range_get.go │ ├── pid.go │ ├── pid_mediaid_bind.go │ ├── promotion_goods_query.go │ ├── range_item.go │ ├── report_img_upload.go │ ├── report_video_upload.go │ ├── report_video_upload_fearless.go │ ├── report_video_upload_part.go │ ├── report_video_upload_part_complete.go │ ├── report_video_upload_part_init.go │ ├── resource.go │ ├── resource_url_gen.go │ ├── rp_prom_url_generate.go │ ├── statistic_data_query.go │ ├── url.go │ └── weapp_qrcode_url_gen.go ├── doc.go ├── goods │ ├── cat.go │ ├── cats_get.go │ ├── doc.go │ ├── opt.go │ └── opt_get.go ├── order │ ├── order.go │ └── order_information_get.go └── pmc │ ├── accrue_query.go │ ├── doc.go │ ├── message.go │ ├── reader.go │ ├── user_cancel.go │ ├── user_get.go │ └── user_permit.go ├── core ├── client.go ├── const.go ├── doc.go ├── internal │ └── debug │ │ └── debug.go └── otel.go ├── doc.go ├── go.mod ├── go.sum ├── model ├── doc.go ├── request.go ├── response.go └── types.go └── util ├── crypto.go ├── doc.go └── query ├── doc.go ├── encode.go └── encode_test.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | env: 13 | GOPATH: ${{ github.workspace }} 14 | package: src/github.com/${{ github.repository }} 15 | defaults: 16 | run: 17 | working-directory: ${{ env.GOPATH }}/${{ env.package }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | path: ${{ env.GOPATH }}/${{ env.package }} 22 | 23 | - name: Set up Go 24 | uses: actions/setup-go@v5 25 | with: 26 | go-version: ">=1.21.0" 27 | cache-dependency-path: | 28 | ${{ env.GOPATH }}/${{ env.package }}/go.mod 29 | ${{ env.GOPATH }}/${{ env.package }}/go.sum 30 | 31 | - name: Dependencies 32 | run: go mod download 33 | 34 | - name: Build 35 | working-directory: ${{ env.GOPATH }}/${{ env.package }} 36 | run: go build -v ./... 37 | 38 | - name: Test 39 | env: 40 | APPKEY: ${{ secrets.APPKEY }} 41 | SECRET: ${{ secrets.APP_SECRET }} 42 | run: go test -v ./... 43 | -------------------------------------------------------------------------------- /.github/workflows/goreleaser.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | env: 12 | cache: ${{ github.workspace }}/go.sum 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Set up Go 19 | uses: actions/setup-go@v5 20 | with: 21 | go-version: ">=1.21.0" 22 | cache-dependency-path: ${{ env.cache }} 23 | - name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v6 25 | with: 26 | # either 'goreleaser' (default) or 'goreleaser-pro' 27 | distribution: goreleaser 28 | version: latest 29 | args: release --clean 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution 33 | # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} 34 | -------------------------------------------------------------------------------- /.github/workflows/goreportcard.yml: -------------------------------------------------------------------------------- 1 | name: GoReportCard 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | name: GoReportCard 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: GoReportCard 9 | uses: creekorful/goreportcard-action@v1.0 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | .DS_Store 27 | */.DS_Store 28 | test 29 | config.toml 30 | .vim 31 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example .goreleaser.yml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | project_name: "openpdd" 4 | before: 5 | hooks: 6 | # You may remove this if you don't use go modules. 7 | - go mod tidy 8 | # you may remove this if you don't need go generate 9 | - go generate ./... 10 | builds: 11 | - skip: true 12 | archives: 13 | - id: oceanengine 14 | name_template: >- 15 | {{ .ProjectName }}_ 16 | {{- title .Os }}_ 17 | {{- if eq .Arch "amd64" }}x86_64 18 | {{- else if eq .Arch "386" }}i386 19 | {{- else }}{{ .Arch }}{{ end }} 20 | checksum: 21 | name_template: "checksums.txt" 22 | snapshot: 23 | version_template: "{{ .Tag }}-next" 24 | changelog: 25 | sort: asc 26 | filters: 27 | exclude: 28 | - "^docs:" 29 | - "^test:" 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 拼多多 OpenAPI golang版 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/bububa/openpdd.svg)](https://pkg.go.dev/github.com/bububa/openpdd) 4 | [![Go](https://github.com/bububa/openpdd/actions/workflows/go.yml/badge.svg)](https://github.com/bububa/openpdd/actions/workflows/go.yml) 5 | [![goreleaser](https://github.com/bububa/openpdd/actions/workflows/goreleaser.yml/badge.svg)](https://github.com/bububa/openpdd/actions/workflows/goreleaser.yml) 6 | [![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/bububa/openpdd.svg)](https://github.com/bububa/openpdd) 7 | [![GoReportCard](https://goreportcard.com/badge/github.com/bububa/openpdd)](https://goreportcard.com/report/github.com/bububa/openpdd) 8 | [![GitHub license](https://img.shields.io/github/license/bububa/openpdd.svg)](https://github.com/bububa/openpdd/blob/master/LICENSE) 9 | [![GitHub release](https://img.shields.io/github/release/bububa/openpdd.svg)](https://GitHub.com/bububa/openpdd/releases/) 10 | 11 | ## Implemented API 12 | 13 | - 授权(api/oauth) 14 | - 生成授权链接 [ URL(ctx context.Context, clt *core.SDKClient, req *URLRequest) string ] 15 | - 获取Access Token [ TokenCreate(ctx context.Context, clt *core.SDKClient, code string) (*Token, error) ] 16 | - 刷新Access Token [ TokenRefresh(ctx context.Context, clt *core.SDKClient, refreshToken string) (*Token, error) ] 17 | - 多多客(api/ddk) 18 | - 创建多多进宝推广位 [ GoodsPidGenerate(ctx context.Context, clt *core.SDKClient, req *GoodsPidGenerateRequest) ([]Pid, error) ] 19 | - 多多客生成单品推广小程序二维码url [ WeappQrcodeUrlGen(ctx context.Context, clt *core.SDKClient, req *WeappQrcodeUrlGenRequest) (string, error) ] 20 | - 多多礼金状态更新 [ CashgiftStatusUpdate(ctx context.Context, clt *core.SDKClient, req *CashgiftStatusUpdateRequest) (uint64, error) ] 21 | - 查询已经生成的推广位信息 [ GoodsPidQuery(ctx context.Context, clt *core.SDKClient, req *GoodsPidQueryRequest) (int, []Pid, error) ] 22 | - 批量绑定推广位的媒体id [ PidMediaIDBind(ctx context.Context, clt *core.SDKClient, mediaID uint64, pidList []string) (*PidMediaIDBindResult, error) ] 23 | - 多多进宝转链接口 [ GoodsZsUnitUrlGen(ctx context.Context, clt *core.SDKClient, req *GoodsZsUnitUrlGenRequest) (*PromURL, error) ] 24 | - 查询是否绑定备案 [ MemberAuthorityQuery(ctx context.Context, clt *core.SDKClient, req *MemberAuthorityQueryRequest) (bool, error) ] 25 | - 多多进宝数据统计查询接口 [ StatisticDataQuery(ctx context.Context, clt *core.SDKClient, req *StatisticDataQueryRequest) ([]StatisticData, error) ] 26 | - 生成商城-频道推广链接 [ CmsPromUrlGenerate(ctx context.Context, clt *core.SDKClient, req *CmsPromUrlGenerateRequest) (int, []PromURL, error) ] 27 | - 多多进宝推广链接生成 [ GoodsPromotionUrlGenerate(ctx context.Context, clt *core.SDKClient, req *GoodsPromotionUrlGenerateRequest) ([]PromURL, error) ] 28 | - 用时间段查询推广订单接口 [ OrderListRangeGet(ctx context.Context, clt *core.SDKClient, req *OrderListRangeGetRequest) (string, []Order, error) ] 29 | - 查询订单详情 [ OrderDetailGet(ctx context.Context, clt *core.SDKClient, req *OrderDetailGetRequest) (*Order, error) ] 30 | - 多多进宝商品推荐API [ GoodsRecommendGet(ctx context.Context, clt *core.SDKClient, req *GoodsRecommendGetRequest) (*GoodsRecommendGetResult, error) ] 31 | - 生成营销工具推广链接 [ RpPromUrlGenerate(ctx context.Context, clt *core.SDKClient, req *RpPromUrlGenerateRequest) (*RpPromUrlGenerateResult, error) ] 32 | - 最后更新时间段增量同步推广订单信息 [ OrderListIncrementGet(ctx context.Context, clt *core.SDKClient, req *OrderListIncrementGetRequest) (int, []Order, error) ] 33 | - 多多进宝商品查询 [ GoodsSearch(ctx context.Context, clt *core.SDKClient, req *GoodsSearchRequest) (*GoodsSearchResult, error) ] 34 | - 多多进宝商品详情查询 [ GoodsDetail(ctx context.Context, clt *core.SDKClient, req *GoodsDetailRequest) ([]Goods, error) ] 35 | - 生成多多进宝频道推广 [ ResourceUrlGen(ctx context.Context, clt *core.SDKClient, req *ResourceUrlGenRequest) (*PromURL, error) ] 36 | - 创建多多礼金 [ CashgiftCreate(ctx context.Context, clt *core.SDKClient, req *CashgiftCreateRequest) (*CashgiftCreateResult, error) ] 37 | - 多多进宝信息流渠道备案授权素材上传接口 [ GoodsPromotionRightAuth(ctx context.Context, clt *core.SDKClient, req *GoodsPromotionRightAuthRequest) error ] 38 | - 多多客信息流投放备案图片上传接口 [ ReportImgUpload(ctx context.Context, clt *core.SDKClient, req *ReportImgUploadRequest) (string, error) ] 39 | - 多多客信息流投放备案视频上传接口 [ ReportVideoUpload(ctx context.Context, clt *core.SDKClient, req *ReportVideoUploadRequest) (string, error) ] 40 | - 多多客信息流投放备案视频上传分片初始化接口 [ ReportVideoUploadPartInit(ctx context.Context, clt *core.SDKClient, contentType string) (string, error) ] 41 | - 多多客信息流投放备案视频上传分片上传接口 [ ReportVideoUploadPart(ctx context.Context, clt *core.SDKClient, req *ReportVideoUploadPartRequest) (string, error) ] 42 | - 多多客信息流投放备案视频上传分片完成接口 [ ReportVideoUploadPartComplete(ctx context.Context, clt *core.SDKClient, uploadSign string) (string, error) ] 43 | - 多多客工具(api/ddk/oauth) 44 | - 查询所有授权的多多客订单 [ AllOrderListIncrementGet(ctx context.Context, clt *core.SDKClient, req *AllOrderListIncrementGetRequest, accessToken string) (int, []ddk.Order, error) ] 45 | - 创建多多礼金 [ CashgiftCreate(ctx context.Context, clt *core.SDKClient, req *CashgiftCreateRequest, accessToken string) (*ddk.CashgiftCreateResult, error) ] 46 | - 多多礼金状态更新 [ CashgiftStatusUpdate(ctx context.Context, clt *core.SDKClient, req *CashgiftStatusUpdateRequest, accessToken string) (uint64, error) ] 47 | - 生成商城推广链接接口 [ CmsPromUrlGenerate(ctx context.Context, clt *core.SDKClient, req *CmsPromUrlGenerateRequest, accessToken string) (int, []ddk.PromURL, error) ] 48 | - 多多进宝商品详情查询 [ GoodsDetail(ctx context.Context, clt *core.SDKClient, req *GoodsDetailRequest, accessToken string) ([]ddk.Goods, error) ] 49 | - 多多进宝推广位创建接口 [ GoodsPidGenerate(ctx context.Context, clt *core.SDKClient, req *GoodsPidGenerateRequest, accessToken string) ([]Pid, error) ] 50 | - 多多客已生成推广位信息查询 [ GoodsPidQuery(ctx context.Context, clt *core.SDKClient, req *GoodsPidQueryRequest, accessToken string) (int, []ddk.Pid, error) ] 51 | - 生成多多进宝推广链接 [ GoodsPromotionUrlGenerate(ctx context.Context, clt *core.SDKClient, req *GoodsPromotionUrlGenerateRequest, accessToken string) ([]ddk.PromURL, error) ] 52 | - 运营频道商品查询API [ GoodsRecommendGet(ctx context.Context, clt *core.SDKClient, req *GoodsRecommendGetRequest, accessToken string) (*ddk.GoodsRecommendGetResult, error) ] 53 | - 多多进宝商品查询 [ GoodsSearch(ctx context.Context, clt *core.SDKClient, req *GoodsSearchRequest, accessToken string) (*ddk.GoodsSearchResult, error) ] 54 | - 多多进宝转链接口 [ GoodsZsUnitUrlGen(ctx context.Context, clt *core.SDKClient, req *GoodsZsUnitUrlGenRequest, accessToken string) (*ddk.PromURL, error) ] 55 | - 查询是否绑定备案 [ MemberAuthorityQuery(ctx context.Context, clt *core.SDKClient, req *MemberAuthorityQueryRequest, accessToken string) (bool, error) ] 56 | - 获取订单详情 [ OrderDetailGet(ctx context.Context, clt *core.SDKClient, req *OrderDetailGetRequest, accessToken string) (*ddk.Order, error) ] 57 | - 批量绑定推广位的媒体id [ PidMediaIDBind(ctx context.Context, clt *core.SDKClient, mediaID uint64, pidList []string, accessToken string) (*ddk.PidMediaIDBindResult, error) ] 58 | - 拼多多主站频道推广接口 [ ResourceUrlGen(ctx context.Context, clt *core.SDKClient, req *ResourceUrlGenRequest, accessToken string) (*ddk.PromURL, error) ] 59 | - 生成营销工具推广链接 [ RpPromUrlGenerate(ctx context.Context, clt *core.SDKClient, req *RpPromUrlGenerateRequest, accessToken string) (*ddk.RpPromUrlGenerateResult, error) ] 60 | - 多多客生成单品推广小程序二维码url [ WeappQrcodeUrlGen(ctx context.Context, clt *core.SDKClient, req *WeappQrcodeUrlGenRequest, accessToken string) (string, error) ] 61 | - 商品API (api/goods) 62 | - 商品标准类目接口 [ CatsGet(ctx context.Context, clt \*core.SDKClient, parentID uint64) ([]Cat, error) ] 63 | - 查询商品标签列表 [ OptGet(ctx context.Context, clt \*core.SDKClient, parentID uint64) ([]Opt, error) ] 64 | - 消息服务API (api/pmc) 65 | - 消息队列积压数量查询 [ AccureQuery(ctx context.Context, clt *core.SDKClient) (int64, error) ] 66 | - 取消用户的消息服务 [ UserCancel(ctx context.Context, clt *core.SDKClient, ownerID string) (bool, error) ] 67 | - 获取用户已开通消息 [ UserGet(ctx context.Context, clt *core.SDKClient, ownerID string) (*User, error) ] 68 | - 为已授权的用户开通消息服务 [ UserPermit(ctx context.Context, clt \*core.SDKClient, topics []string, accessToken string) (bool, error) ] 69 | - 接收消息推送 [ Read(ctx context.Context, clt \*core.SDKClient) <-chan []byte ] 70 | -------------------------------------------------------------------------------- /api/auth/const.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | const ( 4 | // SHOP_AUTH_WEB_URL 拼多多店铺WEB端网页授权 5 | SHOP_AUTH_WEB_URL = "https://fuwu.pinduoduo.com/service-market/auth" 6 | // SHOP_AUTH_H5_URL 拼多多店铺H5移动端网页授权 7 | SHOP_AUTH_H5_URL = "https://mai.pinduoduo.com/h5-login.html" 8 | // DDK_AUTH_URL 多多进宝推手WEB端网页授权 9 | DDK_AUTH_URL = "https://jinbao.pinduoduo.com/open.html" 10 | // KTT_AUTH_URL 快团团团长WEB端网页授权 11 | KTT_AUTH_URL = "https://oauth.pinduoduo.com/authorize/ktt" 12 | // LOGISTIC_AUTH_URL 拼多多电子面单用户WEB端网页授权 13 | LOGISTIC_AUTH_URL = "https://wb.pinduoduo.com/logistics/auth" 14 | ) 15 | 16 | // AuthType 授权类型 17 | type AuthType int 18 | 19 | const ( 20 | // AuthType_SHOP_WEB 拼多多店铺WEB端 21 | AuthType_SHOP_WEB AuthType = iota 22 | // AuthType_SHOP_H5 拼多多店铺H5移动端网页授权 23 | AuthType_SHOP_H5 24 | // AuthType_DDK 多多进宝推手WEB端网页授权 25 | AuthType_DDK 26 | // AuthType_KTT 快团团团长WEB端网页授权 27 | AuthType_KTT 28 | // AuthType_LOGISTIC 拼多多电子面单用户WEB端网页授权 29 | AuthType_LOGISTIC 30 | ) 31 | 32 | const ( 33 | // H5 view h5 34 | H5 string = "h5" 35 | // WEB view web 36 | WEB string = "web" 37 | ) 38 | -------------------------------------------------------------------------------- /api/auth/doc.go: -------------------------------------------------------------------------------- 1 | // Package auth 授权相关API 2 | package auth 3 | -------------------------------------------------------------------------------- /api/auth/token.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/bububa/openpdd/model" 5 | ) 6 | 7 | // Token 调用令牌 8 | type Token struct { 9 | model.BaseResponse 10 | OwnerName string `json:"owner_name,omitempty" xml:"owner_name,omitempty"` 11 | OwnerID model.Uint64 `json:"owner_id,omitempty" xml:"owner_id,omitempty"` 12 | AccessToken string `json:"access_token,omitempty" xml:"access_token,omitempty"` 13 | RefreshToken string `json:"refresh_token,omitempty" xml:"refresh_token,omitempty"` 14 | ExpiresIn int64 `json:"expires_in,omitempty" xml:"expires_in,omitempty"` 15 | ExpiresAt int64 `json:"expires_at,omitempty" xml:"expires_at,omitempty"` 16 | RefreshTokenExpiresIn int64 `json:"refresh_token_expires_in,omitempty" xml:"refresh_token_expires_in,omitempty"` 17 | RefreshTokenExpiresAt int64 `json:"refresh_token_expires_at,omitempty" xml:"refresh_token_expires_at,omitempty"` 18 | W1ExpiresIn int64 `json:"w1_expires_in,omitempty" xml:"w1_expires_in,omitempty"` 19 | W1ExpiresAt int64 `json:"w1_expires_at,omitempty" xml:"w1_expires_at,omitempty"` 20 | W2ExpiresIn int64 `json:"w2_expires_in,omitempty" xml:"w2_expires_in,omitempty"` 21 | W2ExpiresAt int64 `json:"w2_expires_at,omitempty" xml:"w2_expires_at,omitempty"` 22 | R1ExpiresIn int64 `json:"r1_expires_in,omitempty" xml:"r1_expires_in,omitempty"` 23 | R1ExpiresAt int64 `json:"r1_expires_at,omitempty" xml:"r1_expires_at,omitempty"` 24 | R2ExpiresAt int64 `json:"r2_expires_at,omitempty" xml:"r2_expires_at,omitempty"` 25 | R2ExpiresIn int64 `json:"r2_expires_in,omitempty" xml:"r2_expires_in,omitempty"` 26 | Scope []string `json:"scope,omitempty" xml:"scope,omitempty"` 27 | } 28 | -------------------------------------------------------------------------------- /api/auth/token_create.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // TokenCreateRequest 获取Access Token API Request 11 | type TokenCreateRequest struct { 12 | // Code 授权code,grantType==authorization_code 时需要 13 | Code string `json:"code,omitempty"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r TokenCreateRequest) GetType() string { 18 | return "pdd.pop.auth.token.create" 19 | } 20 | 21 | // TokenCreateResponse 获取Access Token API Response 22 | type TokenCreateResponse struct { 23 | model.CommonResponse 24 | // Response response 25 | Response *Token `json:"pop_auth_token_create_response,omitempty" xml:"pop_auth_token_create_response,omitempty"` 26 | } 27 | 28 | // TokenCreate 获取Access Token 29 | func TokenCreate(ctx context.Context, clt *core.SDKClient, code string) (*Token, error) { 30 | req := &TokenCreateRequest{ 31 | Code: code, 32 | } 33 | var resp TokenCreateResponse 34 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 35 | return nil, err 36 | } 37 | return resp.Response, nil 38 | } 39 | -------------------------------------------------------------------------------- /api/auth/token_refresh.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // TokenRefreshRequest 刷新Access Token API Request 11 | type TokenRefreshRequest struct { 12 | // RefreshToken grantType==refresh_token 时需要 13 | RefreshToken string `json:"refresh_token,omitempty"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r TokenRefreshRequest) GetType() string { 18 | return "pdd.pop.auth.token.refresh" 19 | } 20 | 21 | // TokenRefreshResponse 刷新Access Token API Response 22 | type TokenRefreshResponse struct { 23 | model.CommonResponse 24 | Data *Token `json:"pop_auth_token_refresh_response,omitempty" xml:"pop_auth_token_refresh_response,omitempty"` 25 | } 26 | 27 | // TokenRefresh 刷新Access Token 28 | func TokenRefresh(ctx context.Context, clt *core.SDKClient, refreshToken string) (*Token, error) { 29 | req := &TokenRefreshRequest{ 30 | RefreshToken: refreshToken, 31 | } 32 | var resp TokenRefreshResponse 33 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 34 | return nil, err 35 | } 36 | return resp.Data, nil 37 | } 38 | -------------------------------------------------------------------------------- /api/auth/url.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | "net/url" 6 | "strings" 7 | 8 | "github.com/bububa/openpdd/core" 9 | ) 10 | 11 | // URLRequest 组装授权页URL API Request 12 | type URLRequest struct { 13 | // AuthType 授权类型 14 | AuthType AuthType `json:"auth_type"` 15 | // RedirectURI 已创建应用-应用详情中查看回调地址字段值,在用户完成授权后,code值将回调至该地址中。注意:redirect_uri需进行url编码(encodeURIComponent) 16 | RedirectURI string `json:"redirect_uri,omitempty"` 17 | // State 自定义参数,授权完成后,原值将回调至redirect_uri中 18 | State string `json:"state,omitempty"` 19 | // View 可选web或h5,默认为web,H5移动端网页授权必填 20 | View string `json:"view,omitempty"` 21 | } 22 | 23 | // URL 组装授权页URL 24 | func URL(ctx context.Context, clt *core.SDKClient, req *URLRequest) string { 25 | var builder strings.Builder 26 | switch req.AuthType { 27 | case AuthType_SHOP_WEB: 28 | builder.WriteString(SHOP_AUTH_WEB_URL) 29 | case AuthType_SHOP_H5: 30 | builder.WriteString(SHOP_AUTH_H5_URL) 31 | case AuthType_DDK: 32 | builder.WriteString(DDK_AUTH_URL) 33 | case AuthType_KTT: 34 | builder.WriteString(KTT_AUTH_URL) 35 | case AuthType_LOGISTIC: 36 | builder.WriteString(LOGISTIC_AUTH_URL) 37 | } 38 | builder.WriteString("?") 39 | values := &url.Values{} 40 | values.Set("client_id", clt.ClientID()) 41 | values.Set("response_type", "code") 42 | values.Set("redirect_uri", req.RedirectURI) 43 | if req.State != "" { 44 | values.Set("state", req.State) 45 | } 46 | if req.AuthType == AuthType_SHOP_H5 { 47 | values.Set("view", H5) 48 | } else if req.View != "" { 49 | values.Set("view", req.View) 50 | } 51 | builder.WriteString(values.Encode()) 52 | return builder.String() 53 | } 54 | -------------------------------------------------------------------------------- /api/ddk/cashgift_create.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // CashgiftCreateRequest 创建多多礼金 API Request 11 | type CashgiftCreateRequest struct { 12 | // AcquireEndTime 券批次领取结束时间。note:此时间为时间戳,指格林威治时间 1970 年01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数 13 | AcquireEndTime int64 `json:"acquire_end_time,omitempty"` 14 | // AcquireStartTime 券批次领取开始时间。note:此时间为时间戳,指格林威治时间 1970 年01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数 15 | AcquireStartTime int64 `json:"acquire_start_time,omitempty"` 16 | // AutoTake 是否自动领券,默认false不自动领券 17 | AutoTake bool `json:"auto_take,omitempty"` 18 | // CouponAmount 礼金券面额,单位为分,创建固定面额礼金券必填(创建灵活面额礼金券时,券面额 = 商品券后价 - 期望礼金券后价) 19 | CouponAmount int64 `json:"coupon_amount,omitempty"` 20 | // CouponThresholdAmount 满减门槛,单位为分。满减门槛至少需为礼金券面额的2倍,仅对固定面额礼金券生效。 21 | CouponThresholdAmount int64 `json:"coupon_threshold_amount,omitempty"` 22 | // Duration 活动持续时间,validity_time_type为 1 时必填。相对时间类型为天级时,最大值为30,即领取后30天内有效;相对时间类型为小时级时,最大值为24,即领取后24小时内有效;相对时间类型为分钟级时,则最大值为60,即领取后60分钟内有效。 23 | Duration int `json:"duration,omitempty"` 24 | // ExpectAmount 期望礼金券后价,单位为分,最小值为1。创建灵活券 (generate_flexible_coupon为true) 时必填 25 | ExpectAmount int64 `json:"expect_amount,omitempty"` 26 | // FetchRiskCheck 领券是否过风控,默认true为过风控。 27 | FetchRiskCheck *bool `json:"fetch_risk_check,omitempty"` 28 | // FreezeCondition 收益保护开关开启(rate_decrease_monitor = true)时必填。0-监控项发生降低;1-监控项低于礼金面额,默认为0。 29 | FreezeCondition int `json:"freeze_condition,omitempty"` 30 | // FreezeWatchType 收益保护开关开启(rate_decrease_monitor = true)时必填。0-佣金;1-补贴;2-佣金+补贴,默认为0。 31 | FreezeWatchType int `json:"freeze_watch_type,omitempty"` 32 | // GenerateFlexibleCoupon 是否为灵活面额礼金券,默认false为固定面额礼金券 33 | GenerateFlexibleCoupon bool `json:"generate_flexible_coupon,omitempty"` 34 | // GenerateGlobal 是否开启全场景推广,默认false不开启全场景推广,仅支持固定面额且限定商品的礼金活动。 35 | GenerateGlobal bool `json:"generate_global,omitempty"` 36 | // GoodsSignList 商品goodsSign列表,例如:["c9r2omogKFFAc7WBwvbZU1ikIb16_J3CTa8HNN"],最多可支持传20个商品;若传空,则为不限商品礼金,不支持创建不限商品灵活礼金。goodsSign使用说明:https://jinbao.pinduoduo.com/qa-system?questionId=252 37 | GoodsSignList []string `json:"goods_sign_list,omitempty"` 38 | // LinkAcquireLimit 活动单链接可领券数量,默认无限制,最小值为1。 39 | LinkAcquireLimit int `json:"link_acquire_limit,omitempty"` 40 | // Name 礼金名称 41 | Name string `json:"name,omitempty"` 42 | // PidList 可使用推广位列表,例如:["60005_612"]。(列表中的PID方可推广该礼金) 43 | PidList []string `json:"p_id_list,omitempty"` 44 | // Quantity 礼金券数量,创建固定面额礼金券必填(创建灵活面额礼金券时,礼金券数量不固定,礼金总预算用完为止) 45 | Quantity int64 `json:"quantity,omitempty"` 46 | // RateDecreaseMonitor 收益保护开关,默认false表示关闭,仅支持固定面额且限定商品的礼金活动。开启状态下,系统将根据设置内容进行监控,当监控项满足冻结条件时,系统自动冻结礼金暂停推广,防止资金损失。(可通过多多礼金状态更新接口自行恢复推广) 47 | RateDecreaseMonitor bool `json:"rate_decrease_monitor,omitempty"` 48 | // RelativeTimeType 相对时间类型:1-天级;2-小时级;3-分钟级,有效期类型validityTimeType = 1时必填,默认为1。 例如: relative_time_type = 2, duration = 15, 表示领取后15小时内有效。 49 | RelativeTimeType int `json:"relative_time_type,omitempty"` 50 | // TotalAmount 礼金总预算,单位为分,创建灵活券 (generate_flexible_coupon为true) 时必填。默认情况,总金额 = 礼金券数量 * 礼金券面额 51 | TotalAmount int64 `json:"total_amount,omitempty"` 52 | // UserLimit 单用户可领券数量,可设置范围为1~10张,默认为1张。 53 | UserLimit int `json:"user_limit,omitempty"` 54 | // ValidityEndTime 券批次使用结束时间, validity_time_type为 2 时必填。note:此时间为时间戳,指格林威治时间 1970 年01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数 55 | ValidityEndTime int64 `json:"validity_end_time,omitempty"` 56 | // ValidityStartTime 券批次使用开始时间, validity_time_type为 2 时必填。note:此时间为时间戳,指格林威治时间 1970 年01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数 57 | ValidityStartTime int64 `json:"validity_start_time,omitempty"` 58 | // ValidityTimeType 有效期类型:1-领取后几天内有效;2-固定时间内有效 59 | ValidityTimeType int `json:"validity_time_type,omitempty"` 60 | } 61 | 62 | // GetType implement Request interface 63 | func (r CashgiftCreateRequest) GetType() string { 64 | return "pdd.ddk.cashgift.create" 65 | } 66 | 67 | // CashgiftCreateResponse 创建多多礼金 API Response 68 | type CashgiftCreateResponse struct { 69 | model.CommonResponse 70 | Response *CashgiftCreateResult `json:"create_cashgift_response,omitempty" xml:"create_cashgift_response,omitempty"` 71 | } 72 | 73 | type CashgiftCreateResult struct { 74 | // CashgiftID 礼金ID 75 | CashgiftID uint64 `json:"cash_gift_id,omitempty"` 76 | // Success 创建结果 77 | Success bool `json:"success,omitempty"` 78 | } 79 | 80 | // CashgiftCreate 创建多多礼金 81 | func CashgiftCreate(ctx context.Context, clt *core.SDKClient, req *CashgiftCreateRequest) (*CashgiftCreateResult, error) { 82 | var resp CashgiftCreateResponse 83 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 84 | return nil, err 85 | } 86 | return resp.Response, nil 87 | } 88 | -------------------------------------------------------------------------------- /api/ddk/cashgift_status_update.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // CashgiftStatusUpdateRequest 多多礼金状态更新 API Request 11 | type CashgiftStatusUpdateRequest struct { 12 | // ID 多多礼金ID 13 | ID uint64 `json:"cash_gift_id"` 14 | // UpdateType 礼金更新类型:0-停止礼金推广,1-恢复礼金推广 15 | UpdateType int `json:"update_type"` 16 | } 17 | 18 | // GetType implement Request interface 19 | func (r CashgiftStatusUpdateRequest) GetType() string { 20 | return "pdd.ddk.cashgift.status.update" 21 | } 22 | 23 | // CashgiftStatusUpdateResponse 多多礼金状态更新 API Response 24 | type CashgiftStatusUpdateResponse struct { 25 | model.CommonResponse 26 | Response struct { 27 | // ID 多多礼金ID 28 | ID uint64 `json:"cash_gift_id,omitempty" xml:"cash_gift_id,omitempty"` 29 | } `json:"update_cashgift_response" xml:"update_cashgift_response"` 30 | } 31 | 32 | // CashgiftStatusUpdate 多多礼金状态更新 33 | func CashgiftStatusUpdate(ctx context.Context, clt *core.SDKClient, req *CashgiftStatusUpdateRequest) (uint64, error) { 34 | var resp CashgiftStatusUpdateResponse 35 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 36 | return 0, err 37 | } 38 | return resp.Response.ID, nil 39 | } 40 | -------------------------------------------------------------------------------- /api/ddk/cms_prom_url_generate.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // CmsPromUrlGenerateRequest 生成商城-频道推广链接 API Request 11 | type CmsPromUrlGenerateRequest struct { 12 | // ChannelType 0, "1.9包邮";1, "今日爆款"; 2, "品牌清仓"; 3, "默认商城";4,"PC端专属商城";非必填 ,默认是3, 13 | ChannelType *int `json:"channel_type,omitempty"` 14 | // CustomParameters 自定义参数,为链接打上自定义标签。自定义参数最长限制64个字节。 15 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 16 | // GenerateMobile 是否生成手机跳转链接。true-是,false-否,默认false 17 | GenerateMobile bool `json:"generate_mobile,omitempty"` 18 | // GenerateSchemaURL 是否返回 schema URL 19 | GenerateSchemaURL bool `json:"generate_schema_url,omitempty"` 20 | // GenerateShortURL 是否生成短链接,true-是,false-否 21 | GenerateShortURL bool `json:"generate_short_url,omitempty"` 22 | // GenerateWeApp 是否生成拼多多福利券微信小程序推广信息 23 | GenerateWeApp bool `json:"generate_we_app,omitempty"` 24 | // Keyword 搜索关键词 25 | Keyword string `json:"keyword,omitempty"` 26 | // 单人团多人团标志。true-多人团,false-单人团 默认false 27 | MultiGroup bool `json:"multi_group,omitempty"` 28 | // 推广位列表,例如:["60005_612"] 29 | PidList string `json:"p_id_list,omitempty"` 30 | } 31 | 32 | // GetType implement Request interface 33 | func (r CmsPromUrlGenerateRequest) GetType() string { 34 | return "pdd.ddk.cms.prom.url.generate" 35 | } 36 | 37 | // CmsPromUrlGenerateResponse 生成商城-频道推广链接 API Response 38 | type CmsPromUrlGenerateResponse struct { 39 | model.CommonResponse 40 | // Response 商城推广链接返回对象 41 | Response struct { 42 | // Total total 43 | Total int `json:"total,omitempty"` 44 | // URLList 链接列表 45 | URLList []PromURL `json:"url_list,omitempty"` 46 | } `json:"cms_promotion_url_generate_response" xml:"cms_promotion_url_generate_response"` 47 | } 48 | 49 | // CmsPromUrlGenerate 生成商城-频道推广链接 50 | func CmsPromUrlGenerate(ctx context.Context, clt *core.SDKClient, req *CmsPromUrlGenerateRequest) (int, []PromURL, error) { 51 | var resp CmsPromUrlGenerateResponse 52 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 53 | return 0, nil, err 54 | } 55 | return resp.Response.Total, resp.Response.URLList, nil 56 | } 57 | -------------------------------------------------------------------------------- /api/ddk/doc.go: -------------------------------------------------------------------------------- 1 | // Package ddk 多多客相关API 2 | package ddk 3 | -------------------------------------------------------------------------------- /api/ddk/goods.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import "github.com/bububa/openpdd/model" 4 | 5 | // Goods 多多进宝商品 6 | type Goods struct { 7 | // CategoryName 类目名 8 | CategoryName string `json:"category_name,omitempty" xml:"category_name,omitempty"` 9 | // ActivityPromotionRate 活动佣金比例,千分比(特定活动期间的佣金比例) 10 | ActivityPromotionRate int `json:"activity_promotion_rate,omitempty" xml:"activity_promotion_rate,omitempty"` 11 | // ActivityTags 商品活动标记数组,例:[4,7],4-秒杀 7-百亿补贴等 12 | ActivityTags []int `json:"activity_tags,omitempty" xml:"activity_tags,omitempty"` 13 | // BrandName 商品品牌词信息,如“苹果”、“阿迪达斯”、“李宁”等 14 | BrandName string `json:"brand_name,omitempty" xml:"brand_name,omitempty"` 15 | // CashGiftAmount 全局礼金金额,单位分 16 | CashGiftAmount int64 `json:"cash_gift_amount,omitempty" xml:"cash_gift_amount,omitempty"` 17 | // CatID 商品类目id 18 | CatID model.Uint64 `json:"cat_id,omitempty" xml:"cat_id,omitempty"` 19 | // CatIDs 商品一~四级类目ID列表 20 | CatIDs []uint64 `json:"cat_ids,omitempty" xml:"cat_ids,omitempty"` 21 | // CouponDiscount 优惠券面额,单位为分 22 | CouponDiscount int64 `json:"coupon_discount,omitempty" xml:"coupon_discount,omitempty"` 23 | // CouponEnd 优惠券失效时间,UNIX时间戳 24 | CouponEnd int64 `json:"coupon_end,omitempty" xml:"coupon_end,omitempty"` 25 | // CouponMinOrderAmount 优惠券门槛价格,单位为分 26 | CouponMinOrderAmount int64 `json:"coupon_min_order_amount,omitempty" xml:"coupon_min_order_amount,omitempty"` 27 | // CouponPrice 优惠券金额 28 | CouponPrice int64 `json:"coupon_price,omitempty" xml:"coupon_price,omitempty"` 29 | // CouponRemainQuantity 优惠券剩余数量 30 | CouponRemainQuantity int `json:"coupon_remain_quantity,omitempty" xml:"coupon_remain_quantity,omitempty"` 31 | // CouponStartTime 优惠券生效时间,UNIX时间戳 32 | CouponStartTime int64 `json:"coupon_start_time,omitempty" xml:"coupon_start_time,omitempty"` 33 | // CouponTotalQuantity 优惠券总数量 34 | CouponTotalQuantity int `json:"coupon_total_quantity,omitempty" xml:"coupon_total_quantity,omitempty"` 35 | // CreateAt 创建时间 36 | CreateAt int64 `json:"create_at,omitempty" xml:"create_at,omitempty"` 37 | // DescTxt 描述分 38 | DescTxt string `json:"desc_txt,omitempty" xml:"desc_txt,omitempty"` 39 | // ExtraCouponAmount 额外优惠券,单位为分 40 | ExtraCouponAmount int64 `json:"extra_coupon_amount,omitempty" xml:"extra_coupon_amount,omitempty"` 41 | // GoodsDesc 商品描述 42 | GoodsDesc string `json:"goods_desc,omitempty" xml:"goods_desc,omitempty"` 43 | // GoodsImageURL 商品主图 44 | GoodsImageURL string `json:"goods_image_url,omitempty" xml:"goods_image_url,omitempty"` 45 | // GoodsGalleryURLs 46 | GoodsGalleryURLs []string `json:"goods_gallery_urls,omitempty" xml:"goods_gallery_urls,omitempty"` 47 | // VideoURLs 商品视频url 48 | VideoURLs []string `json:"video_urls,omitempty" xml:"video_urls,omitempty"` 49 | // GoodsLabels 商品特殊标签列表。例: [1],1-APP专享 50 | GoodsLabels []int `json:"goods_labels,omitempty" xml:"goods_labels,omitempty"` 51 | // GoodsName 商品名称 52 | GoodsName string `json:"goods_name,omitempty" xml:"goods_name,omitempty"` 53 | // GoodsRate 商品等级 54 | GoodsRate int `json:"goods_rate,omitempty" xml:"goods_rate,omitempty"` 55 | // GodsID 商品ID 56 | GoodsID uint64 `json:"goods_id,omitempty" xml:"goods_id,omitempty"` 57 | // GoodsSign 商品goodsSign,支持通过goodsSign查询商品。goodsSign是加密后的goodsId, goodsId已下线,请使用goodsSign来替代。使用说明:https://jinbao.pinduoduo.com/qa-system?questionId=252 58 | GoodsSign string `json:"goods_sign,omitempty" xml:"goods_sign,omitempty"` 59 | // GoodsThumbnailURL 商品缩略图 60 | GoodsThumbnailURL string `json:"goods_thumbnail_url,omitempty" xml:"goods_thumbnail_url,omitempty"` 61 | // GoodsType 商品类型 62 | GoodsType int `json:"goods_type,omitempty" xml:"goods_type,omitempty"` 63 | // HasCoupon 商品是否带券,true-带券,false-不带券 64 | HasCoupon bool `json:"has_coupon,omitempty" xml:"has_coupon,omitempty"` 65 | // HasMaterial 商品是否有素材(图文、视频) 66 | HasMaterial bool `json:"has_material,omitempty" xml:"has_material,omitempty"` 67 | // LgstTxt 物流分 68 | LgstTxt string `json:"lgst_txt,omitempty" xml:"lgst_txt,omitempty"` 69 | // MallID 商家id 70 | MallID uint64 `json:"mall_id,omitempty" xml:"mall_id,omitempty"` 71 | // MallName 店铺名称 72 | MallName string `json:"mall_name,omitempty" xml:"mall_name,omitempty"` 73 | // MarketFee 市场服务费 74 | MarketFee int64 `json:"market_fee,omitempty" xml:"market_fee,omitempty"` 75 | // MerchantType 商家类型 76 | MerchatType model.Int `json:"merchant_type,omitempty" xml:"merchant_type,omitempty"` 77 | // MinGroupPrice 最小成团价格,单位分 78 | MinGroupPrice int64 `json:"min_group_price,omitempty" xml:"min_group_price,omitempty"` 79 | // MinNormalPrice 最小单买价格,单位分 80 | MinNormalPrice int64 `json:"min_normal_price,omitempty" xml:"min_normal_price,omitempty"` 81 | // OptID 商品标签类目ID,使用pdd.goods.opt.get获取 82 | OptID model.Uint64 `json:"opt_id,omitempty" xml:"opt_id,omitempty"` 83 | // OptIDs 商品一~四级标签类目ID列表 84 | OptIDs []uint64 `json:"opt_ids,omitempty" xml:"opt_ids,omitempty"` 85 | // OptName 商品标签名 86 | OptName string `json:"opt_name,omitempty" xml:"opt_name,omitempty"` 87 | // PredictPromotionRate 比价行为预判定佣金,需要用户备案 88 | PredictPromotionRate int `json:"predict_promotion_rate,omitempty" xml:"predict_promotion_rate,omitempty"` 89 | // PromotionRate 佣金比例,千分比 90 | PromotionRate int `json:"promotion_rate,omitempty" xml:"promotion_rate,omitempty"` 91 | // QrcodeImageURL 二维码主图 92 | QrcodeImageURL string `json:"qrcode_image_url,omitempty" xml:"qrcode_image_url,omitempty"` 93 | // RealtimeSalesTip 商品近1小时在多多进宝的实时销量(仅实时热销榜提供) 94 | RealtimeSalesTip string `json:"realtime_sales_tip,omitempty" xml:"realtime_sales_tip,omitempty"` 95 | // SalesTip 销售量 96 | SalesTip string `json:"sales_tip,omitempty" xml:"sales_tip,omitempty"` 97 | // SearchID 搜索id,建议生成推广链接时候填写,提高收益。 98 | SearchID string `json:"search_id,omitempty" xml:"search_id,omitempty"` 99 | // ServTxt 服务分 100 | ServTxt string `json:"serv_txt,omitempty" xml:"serv_txt,omitempty"` 101 | // ShareDesc 分享描述 102 | ShareDesc string `json:"share_desc,omitempty" xml:"share_desc,omitempty"` 103 | // ShareRate 招商分成服务费比例,千分比 104 | ShareRate int `json:"share_rate,omitempty" xml:"share_rate,omitempty"` 105 | // SubsidyAmount 优势渠道专属商品补贴金额,单位为分。针对优质渠道的补贴活动,指定优势渠道可通过推广该商品获取相应补贴。补贴活动入口:[进宝网站-官方活动] 106 | SubsidyAmount int64 `json:"subsidy_amount,omitempty" xml:"subsidy_amount,omitempty"` 107 | // SubsidyDuoAmountTenMillion 官方活动给渠道的收入补贴金额,不允许直接给下级代理展示,单位为分 108 | SubsidyDuoAmountTenMillion int64 `json:"subsidy_duo_amount_ten_million,omitempty" xml:"subsidy_duo_amount_ten_million,omitempty"` 109 | // UnifiedTags 优惠标签列表,包括:"X元券","比全网低X元","服务费","精选素材","近30天低价","同款低价","同款好评","同款热销","旗舰店","一降到底","招商优选","商家优选","好价再降X元","全站销量XX","实时热销榜第X名","实时好评榜第X名","额外补X元"等 110 | UnifiedTags []string `json:"unified_tags,omitempty" xml:"unified_tags,omitempty"` 111 | } 112 | -------------------------------------------------------------------------------- /api/ddk/goods_detail.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // GoodsDetailRequest 多多进宝商品详情查询 API Request 11 | type GoodsDetailRequest struct { 12 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。(如果使用GET请求,请使用URLEncode处理参数) 13 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 14 | // GoodsImageType 商品主图类型:1-场景图,2-白底图,默认为0 15 | GoodsImageType int `json:"goods_image_type,omitempty"` 16 | // GoodsSign 商品goodsSign,支持通过goodsSign查询商品。goodsSign是加密后的goodsId, goodsId已下线,请使用goodsSign来替代。使用说明:https://jinbao.pinduoduo.com/qa-system?questionId=252 17 | GoodsSign string `json:"goods_sign,omitempty"` 18 | // NeedSkuInfo 是否获取sku信息,默认false不返回。(特殊渠道权限,需额外申请) 19 | NeedSkuInfo bool `json:"need_sku_info,omitempty"` 20 | // Pid 推广位id 21 | Pid string `json:"pid,omitempty"` 22 | // SearchID 搜索id,建议填写,提高收益。来自pdd.ddk.goods.recommend.get、pdd.ddk.goods.search、pdd.ddk.top.goods.list.query等接口 23 | SearchID string `json:"search_id,omitempty"` 24 | // ZsDuoID 招商多多客ID 25 | ZsDuoID uint64 `json:"zs_duo_id,omitempty"` 26 | } 27 | 28 | // GetType implement Request interface 29 | func (r GoodsDetailRequest) GetType() string { 30 | return "pdd.ddk.goods.detail" 31 | } 32 | 33 | // GoodsDetailResponse 多多进宝商品详情查询 API Response 34 | type GoodsDetailResponse struct { 35 | model.CommonResponse 36 | Response struct { 37 | // List 多多进宝商品对象列表 38 | List []Goods `json:"goods_details,omitempty" xml:"goods_details,omitempty"` 39 | } `json:"goods_detail_response" xml:"goods_detail_response"` 40 | } 41 | 42 | // GoodsDetail 多多进宝商品详情查询 43 | func GoodsDetail(ctx context.Context, clt *core.SDKClient, req *GoodsDetailRequest) ([]Goods, error) { 44 | var resp GoodsDetailResponse 45 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 46 | return nil, err 47 | } 48 | return resp.Response.List, nil 49 | } 50 | -------------------------------------------------------------------------------- /api/ddk/goods_pid_generate.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // GoodsPidGenerateRequest 创建多多进宝推广位 API Request 11 | type GoodsPidGenerateRequest struct { 12 | // Number 要生成的推广位数量,默认为10,范围为:1~100 13 | Number int `json:"number"` 14 | // PidNameList 推广位名称,例如["1","2"] 15 | PidNameList []string `json:"p_id_name_list,omitempty"` 16 | // MediaID 媒体id 17 | MediaID uint64 `json:"media_id,omitempty"` 18 | } 19 | 20 | // GetType implement Request interface 21 | func (r GoodsPidGenerateRequest) GetType() string { 22 | return "pdd.ddk.goods.pid.generate" 23 | } 24 | 25 | // GoodsPidGenerateResponse 创建多多进宝推广位 API Response 26 | type GoodsPidGenerateResponse struct { 27 | model.CommonResponse 28 | // Response response 29 | Response struct { 30 | // List 多多进宝推广位对象列表 31 | List []Pid `json:"p_id_list,omitempty"` 32 | } `json:"p_id_generate_response" xml:"p_id_generate_response"` 33 | } 34 | 35 | // GoodsPidGenerate 创建多多进宝推广位 36 | func GoodsPidGenerate(ctx context.Context, clt *core.SDKClient, req *GoodsPidGenerateRequest) ([]Pid, error) { 37 | var resp GoodsPidGenerateResponse 38 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 39 | return nil, err 40 | } 41 | return resp.Response.List, nil 42 | } 43 | -------------------------------------------------------------------------------- /api/ddk/goods_pid_query.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // GoodsPidQueryRequest 查询已经生成的推广位信息 API Request 11 | type GoodsPidQueryRequest struct { 12 | // Page 返回的页数 13 | Page int `json:"page,omitempty"` 14 | // PageSize 返回的每页推广位数量 15 | PageSize int `json:"page_size,omitempty"` 16 | // PidList 推广位列表,例如:["60005_612"] 17 | PidList []string `json:"pid_list,omitempty"` 18 | // Status 推广位状态:0-正常,1-封禁 19 | Status *int `json:"status,omitempty"` 20 | } 21 | 22 | // GetType implement Request interface 23 | func (r GoodsPidQueryRequest) GetType() string { 24 | return "pdd.ddk.goods.pid.query" 25 | } 26 | 27 | // GoodsPidQueryResponse 查询已经生成的推广位信息 API Response 28 | type GoodsPidQueryResponse struct { 29 | model.CommonResponse 30 | Response struct { 31 | // List 多多进宝推广位对象列表 32 | List []Pid `json:"p_id_list,omitempty"` 33 | // TotalCount 返回推广位总数 34 | TotalCount int `json:"total_count,omitempty"` 35 | } `json:"p_id_query_response"` 36 | } 37 | 38 | // GoodsPidQuery 查询已经生成的推广位信息 39 | func GoodsPidQuery(ctx context.Context, clt *core.SDKClient, req *GoodsPidQueryRequest) (int, []Pid, error) { 40 | var resp GoodsPidQueryResponse 41 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 42 | return 0, nil, err 43 | } 44 | return resp.Response.TotalCount, resp.Response.List, nil 45 | } 46 | -------------------------------------------------------------------------------- /api/ddk/goods_promotion_right_auth.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/bububa/openpdd/core" 8 | "github.com/bububa/openpdd/model" 9 | ) 10 | 11 | // GoodsPromotionRightAuthRequest 多多进宝信息流渠道备案授权素材上传接口 API Request 12 | type GoodsPromotionRightAuthRequest struct { 13 | // DemoURL 推广商品视频素材url 14 | DemoURL string `json:"demo_url,omitempty"` 15 | // DuoID 渠道duoId 16 | DuoID uint64 `json:"duo_id,omitempty"` 17 | // GoodsID 商品GoodsId 18 | GoodsID uint64 `json:"goods_id,omitempty"` 19 | // MallCertificationURL 商家资质证明图片url列表,1到3张图 20 | MallCertificationURL []string `json:"mall_certificate_url,omitempty"` 21 | // PromotionCodeURL 推广视频预览码url 22 | PromotionCodeURL string `json:"promotion_code_url,omitempty"` 23 | // PromotionEndTime 推广结束时间戳,毫秒 24 | PromotionEndTime int64 `json:"promotion_end_time,omitempty"` 25 | // PromotionStartTime 推广开始时间戳,毫秒 26 | PromotionStartTime int64 `json:"promotion_start_time,omitempty"` 27 | // ThumbPicURL 商品图片素材url列表,0到3张图 28 | ThumbPicURL []string `json:"thumb_pic_url,omitempty"` 29 | } 30 | 31 | // GetType implement Request interface 32 | func (r GoodsPromotionRightAuthRequest) GetType() string { 33 | return "pdd.ddk.goods.promotion.right.auth" 34 | } 35 | 36 | // GoodsPromotionRightAuthResponse 多多进宝信息流渠道备案授权素材上传接口 API Response 37 | type GoodsPromotionRightAuthResponse struct { 38 | model.CommonResponse 39 | Response struct { 40 | // Reason 备案失败原因 41 | Reason string `json:"reason,omitempty"` 42 | // Result 备案结果 43 | Result bool `json:"result,omitempty"` 44 | } `json:"goods_promotion_right_auth_response"` 45 | } 46 | 47 | // GoodsPromotionRightAuth 多多进宝信息流渠道备案授权素材上传接口 48 | func GoodsPromotionRightAuth(ctx context.Context, clt *core.SDKClient, req *GoodsPromotionRightAuthRequest) error { 49 | var resp GoodsPromotionRightAuthResponse 50 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 51 | return err 52 | } 53 | if !resp.Response.Result { 54 | return errors.New(resp.Response.Reason) 55 | } 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /api/ddk/goods_promotion_url_generate.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // GoodsPromotionUrlGenerateRequest 多多进宝推广链接生成 API Request 11 | type GoodsPromotionUrlGenerateRequest struct { 12 | // CashGiftID 多多礼金ID 13 | CashGiftID uint64 `json:"cash_gift_id,omitempty"` 14 | // CashGiftName 自定义礼金标题,用于向用户展示渠道专属福利,不超过12个字 15 | CashGiftName string `json:"cash_gift_name,omitempty"` 16 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。(如果使用GET请求,请使用URLEncode处理参数) 17 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 18 | // GenerateAuthorityURL 是否生成带授权的单品链接。如果未授权,则会走授权流程 19 | GenerateAuthorityURL bool `json:"generate_authority_url,omitempty"` 20 | // GenerateMallCollectCoupon 是否生成店铺收藏券推广链接 21 | GenerateMallCollectCoupon bool `json:"generate_mall_collect_coupon,omitempty"` 22 | // GenerateQQApp 是否生成qq小程序 23 | GenerateQQApp bool `json:"generate_qq_app,omitempty"` 24 | // GenerateSchemaURL 是否返回 schema URL 25 | GenerateSchemaURL bool `json:"generate_schema_url,omitempty"` 26 | // GenerateShortURL 是否生成短链接,true-是,false-否 27 | GenerateShortURL bool `json:"generate_short_url,omitempty"` 28 | // GenerateShortLink 获取微信ShortLink链接,仅支持单个商品 29 | GenerateShortLink bool `json:"generate_short_link,omitempty"` 30 | // GenerateWeApp 是否生成拼多多福利券微信小程序推广信息 31 | GenerateWeApp bool `json:"generate_we_app,omitempty"` 32 | // GoodsSignList 商品goodsSign列表,例如:["c9r2omogKFFAc7WBwvbZU1ikIb16_J3CTa8HNN"],支持批量生链。goodsSign是加密后的goodsId, goodsId已下线,请使用goodsSign来替代。使用说明:https://jinbao.pinduoduo.com/qa-system?questionId=252 33 | GoodsSignList []string `json:"goods_sign_list,omitempty"` 34 | // MaterialID 素材ID,可以通过商品详情接口获取商品素材信息 35 | MaterialID string `json:"material_id,omitempty"` 36 | // MultiGroup true--生成多人团推广链接 false--生成单人团推广链接(默认false)1、单人团推广链接:用户访问单人团推广链接,可直接购买商品无需拼团。2、多人团推广链接:用户访问双人团推广链接开团,若用户分享给他人参团,则开团者和参团者的佣金均结算给推手 37 | MultiGroup bool `json:"multi_group,omitempty"` 38 | // Pid 推广位ID 39 | Pid string `json:"p_id,omitempty"` 40 | // SearchID 搜索id,建议填写,提高收益。来自pdd.ddk.goods.recommend.get、pdd.ddk.goods.search、pdd.ddk.top.goods.list.query等接口 41 | SearchID string `json:"search_id,omitempty"` 42 | // ZsDouID 招商多多客ID 43 | ZsDouID uint64 `json:"zs_dou_id,omitempty"` 44 | } 45 | 46 | // GetType implement Request interface 47 | func (r GoodsPromotionUrlGenerateRequest) GetType() string { 48 | return "pdd.ddk.goods.promotion.url.generate" 49 | } 50 | 51 | // GoodsPromotionUrlGenerateResponse 多多进宝推广链接生成 API Response 52 | type GoodsPromotionUrlGenerateResponse struct { 53 | model.CommonResponse 54 | Response struct { 55 | // List 多多进宝推广链接对象列表 56 | List []PromURL `json:"goods_promotion_url_list" xml:"goods_promotion_url_list"` 57 | } `json:"goods_promotion_url_generate_response" xml:"goods_promotion_url_generate_response"` 58 | } 59 | 60 | // GoodsPromotionUrlGenerate 多多进宝推广链接生成 61 | func GoodsPromotionUrlGenerate(ctx context.Context, clt *core.SDKClient, req *GoodsPromotionUrlGenerateRequest) ([]PromURL, error) { 62 | var resp GoodsPromotionUrlGenerateResponse 63 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 64 | return nil, err 65 | } 66 | return resp.Response.List, nil 67 | } 68 | -------------------------------------------------------------------------------- /api/ddk/goods_recommend_get.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // GoodsRecommendGetRequest 多多进宝商品推荐API API Request 11 | type GoodsRecommendGetRequest struct { 12 | // ActivityTags 活动商品标记数组,例:[4,7],4-秒杀,7-百亿补贴,10851-千万补贴,11879-千万神券,10913-招商礼金商品,31-品牌黑标,10564-精选爆品-官方直推爆款,10584-精选爆品-团长推荐,24-品牌高佣,其他的值请忽略 13 | ActivityTags []int `json:"activity_tags,omitempty"` 14 | // CatID 猜你喜欢场景的商品类目,20100-百货,20200-母婴,20300-食品,20400-女装,20500-电器,20600-鞋包,20700-内衣,20800-美妆,20900-男装,21000-水果,21100-家纺,21200-文具,21300-运动,21400-虚拟,21500-汽车,21600-家装,21700-家具,21800-医药; 15 | CatID uint64 `json:"cat_id,omitempty"` 16 | // ChannelType 进宝频道推广商品: 1-今日销量榜,3-相似商品推荐,4-猜你喜欢(和进宝网站精选一致),5-实时热销榜,6-实时收益榜。默认值5 17 | ChannelType int `json:"channel_type,omitempty"` 18 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 为用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 为上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。 19 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 20 | // GoodsImageType 商品主图类型:1-场景图,2-白底图,默认为0 21 | GoodsImageType int `json:"goods_image_type,omitempty"` 22 | // GoodsSignList 商品goodsSign列表,相似商品推荐场景时必传,仅取数组的第一位,例如:["c9r2omogKFFAc7WBwvbZU1ikIb16_J3CTa8HNN"]。goodsSign是加密后的goodsId, goodsId已下线,请使用goodsSign来替代。使用说明:https://jinbao.pinduoduo.com/qa-system?questionId=252 23 | GoodsSignList []string `json:"goods_sign_list,omitempty"` 24 | // Limit 一页请求数量;默认值 : 20 25 | Limit int `json:"limit,omitempty"` 26 | // ListID 翻页时建议填写前页返回的list_id值 27 | ListID string `json:"list_id,omitempty"` 28 | // Offset 从多少位置开始请求;默认值 : 0,offset需是limit的整数倍,仅支持整页翻页 29 | Offest int `json:"offset,omitempty"` 30 | // Pid 推广位id 31 | Pid string `json:"pid,omitempty"` 32 | } 33 | 34 | // GetType implement Request interface 35 | func (r GoodsRecommendGetRequest) GetType() string { 36 | return "pdd.ddk.goods.recommend.get" 37 | } 38 | 39 | // GoodsRecommendGetResponse 多多进宝商品推荐API API Response 40 | type GoodsRecommendGetResponse struct { 41 | model.CommonResponse 42 | Response *GoodsRecommendGetResult `json:"goods_basic_detail_response,omitempty" xml:"goods_basic_detail_response,omitempty"` 43 | } 44 | 45 | // GoodsRecommendGetResult . 46 | type GoodsRecommendGetResult struct { 47 | // List 列表 48 | List []Goods `json:"list,omitempty" xml:"list,omitempty"` 49 | // ListID 翻页时必填前页返回的list_id值 50 | ListID string `json:"list_id,omitempty" xml:"list_id,omitempty"` 51 | // SearchID 搜索id,建议生成推广链接时候填写,提高收益。 52 | SearchID string `json:"search_id,omitempty" xml:"search_id,omitempty"` 53 | // Total total 54 | Total int `json:"total,omitempty" xml:"total,omitempty"` 55 | } 56 | 57 | // GoodsRecommendGet 多多进宝商品推荐API 58 | func GoodsRecommendGet(ctx context.Context, clt *core.SDKClient, req *GoodsRecommendGetRequest) (*GoodsRecommendGetResult, error) { 59 | var resp GoodsRecommendGetResponse 60 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 61 | return nil, err 62 | } 63 | return resp.Response, nil 64 | } 65 | -------------------------------------------------------------------------------- /api/ddk/goods_search.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // GoodsSearchRequest 多多进宝商品查询 API Request 11 | type GoodsSearchRequest struct { 12 | // ActivityTags 活动商品标记数组,例:[4,7],4-秒杀,7-百亿补贴,10851-千万补贴,11879-千万神券,10913-招商礼金商品,31-品牌黑标,10564-精选爆品-官方直推爆款,10584-精选爆品-团长推荐,24-品牌高佣,其他的值请忽略 13 | ActivityTags []int `json:"activity_tags,omitempty"` 14 | // BlockCatPackages 屏蔽商品类目包:1-拼多多小程序屏蔽的类目&关键词;2-虚拟类目;3-医疗器械;4-处方药;5-非处方药 15 | BlockCatPackages []uint64 `json:"block_cat_packages,omitempty"` 16 | // BlockCats 自定义屏蔽一级/二级/三级类目ID,自定义数量不超过20个;使用pdd.goods.cats.get接口获取cat_id 17 | BlockCats []uint64 `json:"block_cats,omitempty"` 18 | // CatID 商品类目ID,使用pdd.goods.cats.get接口获取 19 | CatID uint64 `json:"cat_id,omitempty"` 20 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。(如果使用GET请求,请使用URLEncode处理参数) 21 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 22 | // GoodsImageType 商品主图类型:1-场景图,2-白底图,默认为0 23 | GoodsImageType int `json:"goods_image_type,omitempty"` 24 | // GoodsSignList 商品goodsSign列表,例如:["c9r2omogKFFAc7WBwvbZU1ikIb16_J3CTa8HNN"],支持通过goodsSign查询商品。goodsSign是加密后的goodsId, goodsId已下线,请使用goodsSign来替代。使用说明:https://jinbao.pinduoduo.com/qa-system?questionId=252 25 | GoodsSignList []string `json:"goods_sign_list,omitempty"` 26 | // IsBrandGoods 是否为品牌商品 27 | IsBrandGoods bool `json:"is_brand_goods,omitempty"` 28 | // Keyword 商品关键词,与opt_id字段选填一个或全部填写。可支持goods_id、拼多多链接(即拼多多app商详的链接)、进宝长链/短链(即为pdd.ddk.goods.promotion.url.generate接口生成的长短链) 29 | Keyword string `json:"keyword,omitempty"` 30 | // ListID 翻页时建议填写前页返回的list_id值 31 | ListID string `json:"list_id,omitempty"` 32 | // MerchantType 店铺类型,1-个人,2-企业,3-旗舰店,4-专卖店,5-专营店,6-普通店(未传为全部) 33 | MerchantType int `json:"merchant_type,omitempty"` 34 | // MerchantTypeList 店铺类型数组,例如:[1,2] 35 | MerchantTypeList []int `json:"merchant_type_list,omitempty"` 36 | // OptID 商品标签类目ID,使用pdd.goods.opt.get获取 37 | OptID uint64 `json:"opt_id,omitempty"` 38 | // Page 默认值1,商品分页数 39 | Page int `json:"page,omitempty"` 40 | // PageSize 默认100,每页商品数量 41 | PageSize int `json:"page_size,omitempty"` 42 | // Pid 推广位id 43 | Pid string `json:"pid,omitempty"` 44 | // RangeList 筛选范围列表 样例:[{"range_id":0,"range_from":1,"range_to":1500},{"range_id":1,"range_from":1,"range_to":1500}] 45 | RangeList []RangeItem `json:"range_list,omitempty"` 46 | // SortType 排序方式:0-综合排序;1-按佣金比率升序;2-按佣金比例降序;3-按价格升序;4-按价格降序;5-按销量升序;6-按销量降序;7-优惠券金额排序升序;8-优惠券金额排序降序;9-券后价升序排序;10-券后价降序排序;11-按照加入多多进宝时间升序;12-按照加入多多进宝时间降序;13-按佣金金额升序排序;14-按佣金金额降序排序;15-店铺描述评分升序;16-店铺描述评分降序;17-店铺物流评分升序;18-店铺物流评分降序;19-店铺服务评分升序;20-店铺服务评分降序;27-描述评分击败同类店铺百分比升序,28-描述评分击败同类店铺百分比降序,29-物流评分击败同类店铺百分比升序,30-物流评分击败同类店铺百分比降序,31-服务评分击败同类店铺百分比升序,32-服务评分击败同类店铺百分比降序 47 | SortType int `json:"sort_type,omitempty"` 48 | // UseCustomized 是否使用个性化推荐,true表示使用,false表示不使用,默认true。 49 | UseCustomized *bool `json:"use_customized,omitempty"` 50 | // WithCoupon 是否只返回优惠券的商品,false返回所有商品,true只返回有优惠券的商品 51 | WithCoupon bool `json:"with_coupon,omitempty"` 52 | } 53 | 54 | // GetType implement Request interface 55 | func (r GoodsSearchRequest) GetType() string { 56 | return "pdd.ddk.goods.search" 57 | } 58 | 59 | // GoodsSearchResponse 多多进宝商品查询 API Response 60 | type GoodsSearchResponse struct { 61 | model.CommonResponse 62 | Response *GoodsSearchResult `json:"goods_search_response,omitempty" xml:"goods_search_response,omitempty"` 63 | } 64 | 65 | // GoodsSearchResult . 66 | type GoodsSearchResult struct { 67 | // List 列表 68 | List []Goods `json:"goods_list,omitempty" xml:"goods_list,omitempty"` 69 | // ListID 翻页时必填前页返回的list_id值 70 | ListID string `json:"list_id,omitempty" xml:"list_id,omitempty"` 71 | // SearchID 搜索id,建议生成推广链接时候填写,提高收益。 72 | SearchID string `json:"search_id,omitempty" xml:"search_id,omitempty"` 73 | // TotalCount total 74 | TotalCount int `json:"total_count,omitempty" xml:"total_count,omitempty"` 75 | } 76 | 77 | // GoodsSearch 多多进宝商品查询 78 | func GoodsSearch(ctx context.Context, clt *core.SDKClient, req *GoodsSearchRequest) (*GoodsSearchResult, error) { 79 | var resp GoodsSearchResponse 80 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 81 | return nil, err 82 | } 83 | return resp.Response, nil 84 | } 85 | -------------------------------------------------------------------------------- /api/ddk/goods_zs_unit_url_gen.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // GoodsZsUnitUrlGenRequest 多多进宝转链接口 API Request 11 | type GoodsZsUnitUrlGenRequest struct { 12 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。(如果使用GET请求,请使用URLEncode处理参数) 13 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 14 | // Pid 渠道id 15 | Pid string `json:"pid"` 16 | // SourceURL 需转链的链接,支持拼多多商品链接、进宝长链/短链(即为pdd.ddk.goods.promotion.url.generate接口生成的长短链) 17 | SourceURL string `json:"source_url"` 18 | } 19 | 20 | // GetType implement Request interface 21 | func (r GoodsZsUnitUrlGenRequest) GetType() string { 22 | return "pdd.ddk.goods.zs.unit.url.gen" 23 | } 24 | 25 | // GoodsZsUnitUrlGenResponse 多多进宝转链接口 API REesponse 26 | type GoodsZsUnitUrlGenResponse struct { 27 | model.CommonResponse 28 | // Response response 29 | Response *PromURL `json:"goods_zs_unit_generate_response" xml:"goods_zs_unit_generate_response"` 30 | } 31 | 32 | // GoodsZsUnitUrlGen 多多进宝转链接口 33 | func GoodsZsUnitUrlGen(ctx context.Context, clt *core.SDKClient, req *GoodsZsUnitUrlGenRequest) (*PromURL, error) { 34 | var resp GoodsZsUnitUrlGenResponse 35 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 36 | return nil, err 37 | } 38 | return resp.Response, nil 39 | } 40 | -------------------------------------------------------------------------------- /api/ddk/member_authority_query.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // MemberAuthorityQueryRequest 查询是否绑定备案 API Request 11 | type MemberAuthorityQueryRequest struct { 12 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。(如果使用GET请求,请使用URLEncode处理参数) 13 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 14 | // Pid 推广位id 15 | Pid string `json:"pid,omitempty"` 16 | } 17 | 18 | // GetType implement Request interface 19 | func (r MemberAuthorityQueryRequest) GetType() string { 20 | return "pdd.ddk.member.authority.query" 21 | } 22 | 23 | // MemberAuthorityQueryResponse 查询是否绑定备案 API Respose 24 | type MemberAuthorityQueryResponse struct { 25 | model.CommonResponse 26 | // Response response 27 | Response struct { 28 | // Bind 1-已绑定;0-未绑定 29 | Bind int `json:"bind,omitempty" xml:"bind,omitempty"` 30 | } `json:"authority_query_response" xml:"authority_query_response"` 31 | } 32 | 33 | // MemberAuthorityQuery 查询是否绑定备案 34 | func MemberAuthorityQuery(ctx context.Context, clt *core.SDKClient, req *MemberAuthorityQueryRequest) (bool, error) { 35 | var resp MemberAuthorityQueryResponse 36 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 37 | return false, err 38 | } 39 | return resp.Response.Bind == 1, nil 40 | } 41 | -------------------------------------------------------------------------------- /api/ddk/oauth/all_order_list_increment_get.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // AllOrderListIncrementGetRequest 查询所有授权的多多客订单API Request 11 | type AllOrderListIncrementGetRequest struct { 12 | ddk.OrderListIncrementGetRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r AllOrderListIncrementGetRequest) GetType() string { 17 | return "pdd.ddk.all.order.list.increment.get" 18 | } 19 | 20 | // AllOrderListIncrementGet 查询所有授权的多多客订单 21 | func AllOrderListIncrementGet(ctx context.Context, clt *core.SDKClient, req *AllOrderListIncrementGetRequest, accessToken string) (int, []ddk.Order, error) { 22 | var resp ddk.OrderListIncrementGetResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return 0, nil, err 25 | } 26 | return resp.Response.TotalCount, resp.Response.List, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/cashgift_create.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // CashgiftCreateRequest 创建多多礼金 API Request 11 | type CashgiftCreateRequest struct { 12 | ddk.CashgiftCreateRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r CashgiftCreateRequest) GetType() string { 17 | return "pdd.ddk.oauth.cashgift.create" 18 | } 19 | 20 | // CashgiftCreate 创建多多礼金 21 | func CashgiftCreate(ctx context.Context, clt *core.SDKClient, req *CashgiftCreateRequest, accessToken string) (*ddk.CashgiftCreateResult, error) { 22 | var resp ddk.CashgiftCreateResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return nil, err 25 | } 26 | return resp.Response, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/cashgift_status_update.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // CashgiftStatusUpdateRequest 多多礼金状态更新 API Request 11 | type CashgiftStatusUpdateRequest struct { 12 | ddk.CashgiftStatusUpdateRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r CashgiftStatusUpdateRequest) GetType() string { 17 | return "pdd.ddk.oauth.cashgift.status.update" 18 | } 19 | 20 | // CashgiftStatusUpdate 多多礼金状态更新 21 | func CashgiftStatusUpdate(ctx context.Context, clt *core.SDKClient, req *CashgiftStatusUpdateRequest, accessToken string) (uint64, error) { 22 | var resp ddk.CashgiftStatusUpdateResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return 0, err 25 | } 26 | return resp.Response.ID, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/cms_prom_url_generate.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // CmsPromUrlGenerateRequest 生成商城-频道推广链接 API Request 11 | type CmsPromUrlGenerateRequest struct { 12 | ddk.CmsPromUrlGenerateRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r CmsPromUrlGenerateRequest) GetType() string { 17 | return "pdd.ddk.oauth.cms.prom.url.generate" 18 | } 19 | 20 | // CmsPromUrlGenerate 生成商城-频道推广链接 21 | func CmsPromUrlGenerate(ctx context.Context, clt *core.SDKClient, req *CmsPromUrlGenerateRequest, accessToken string) (int, []ddk.PromURL, error) { 22 | var resp ddk.CmsPromUrlGenerateResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return 0, nil, err 25 | } 26 | return resp.Response.Total, resp.Response.URLList, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/doc.go: -------------------------------------------------------------------------------- 1 | // Package oauth 多多客工具A P I 2 | package oauth 3 | -------------------------------------------------------------------------------- /api/ddk/oauth/goods_detail.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // GoodsDetailRequest 多多进宝商品详情查询 API Request 11 | type GoodsDetailRequest struct { 12 | ddk.GoodsDetailRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r GoodsDetailRequest) GetType() string { 17 | return "pdd.ddk.oauth.goods.detail" 18 | } 19 | 20 | // GoodsDetail 多多进宝商品详情查询 21 | func GoodsDetail(ctx context.Context, clt *core.SDKClient, req *GoodsDetailRequest, accessToken string) ([]ddk.Goods, error) { 22 | var resp ddk.GoodsDetailResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return nil, err 25 | } 26 | return resp.Response.List, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/goods_pid_generate.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // GoodsPidGenerateRequest 创建多多进宝推广位 API Request 11 | type GoodsPidGenerateRequest struct { 12 | ddk.GoodsPidGenerateRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r GoodsPidGenerateRequest) GetType() string { 17 | return "pdd.ddk.oauth.goods.pid.generate" 18 | } 19 | 20 | // GoodsPidGenerate 创建多多进宝推广位 21 | func GoodsPidGenerate(ctx context.Context, clt *core.SDKClient, req *GoodsPidGenerateRequest, accessToken string) ([]ddk.Pid, error) { 22 | var resp ddk.GoodsPidGenerateResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return nil, err 25 | } 26 | return resp.Response.List, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/goods_pid_query.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // GoodsPidQueryRequest 查询已经生成的推广位信息 API Request 11 | type GoodsPidQueryRequest struct { 12 | ddk.GoodsPidQueryRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r GoodsPidQueryRequest) GetType() string { 17 | return "pdd.ddk.oauth.goods.pid.query" 18 | } 19 | 20 | // GoodsPidQuery 查询已经生成的推广位信息 21 | func GoodsPidQuery(ctx context.Context, clt *core.SDKClient, req *GoodsPidQueryRequest, accessToken string) (int, []ddk.Pid, error) { 22 | var resp ddk.GoodsPidQueryResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return 0, nil, err 25 | } 26 | return resp.Response.TotalCount, resp.Response.List, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/goods_promotion_url_generate.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // GoodsPromotionUrlGenerateRequest 多多进宝推广链接生成 API Request 11 | type GoodsPromotionUrlGenerateRequest struct { 12 | ddk.GoodsPromotionUrlGenerateRequest 13 | // ForceDuoID 是否使用多多客专属推广计划 14 | ForceDuoID bool `json:"force_duo_id,omitempty"` 15 | } 16 | 17 | // GetType implement Request interface 18 | func (r GoodsPromotionUrlGenerateRequest) GetType() string { 19 | return "pdd.ddk.oauth.goods.promotion.url.generate" 20 | } 21 | 22 | // GoodsPromotionUrlGenerate 多多进宝推广链接生成 23 | func GoodsPromotionUrlGenerate(ctx context.Context, clt *core.SDKClient, req *GoodsPromotionUrlGenerateRequest, accessToken string) ([]ddk.PromURL, error) { 24 | var resp ddk.GoodsPromotionUrlGenerateResponse 25 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 26 | return nil, err 27 | } 28 | return resp.Response.List, nil 29 | } 30 | -------------------------------------------------------------------------------- /api/ddk/oauth/goods_recommend_get.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // GoodsRecommendGetRequest 多多进宝商品推荐API API Request 11 | type GoodsRecommendGetRequest struct { 12 | ddk.GoodsRecommendGetRequest 13 | // ForceAuthDuoID 是否使用工具商专属推广计划,默认为false 14 | ForceAuthDuoID bool `json:"force_auth_duo_id,omitempty"` 15 | } 16 | 17 | // GetType implement Request interface 18 | func (r GoodsRecommendGetRequest) GetType() string { 19 | return "pdd.ddk.oauth.goods.recommend.get" 20 | } 21 | 22 | // GoodsRecommendGet 多多进宝商品推荐API 23 | func GoodsRecommendGet(ctx context.Context, clt *core.SDKClient, req *GoodsRecommendGetRequest, accessToken string) (*ddk.GoodsRecommendGetResult, error) { 24 | var resp ddk.GoodsRecommendGetResponse 25 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 26 | return nil, err 27 | } 28 | return resp.Response, nil 29 | } 30 | -------------------------------------------------------------------------------- /api/ddk/oauth/goods_search.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // GoodsSearchRequest 多多进宝商品查询 API Request 11 | type GoodsSearchRequest struct { 12 | ddk.GoodsSearchRequest 13 | // ForceAuthDuoID 是否使用工具商专属推广计划,默认为false 14 | ForceAuthDuoID bool `json:"force_auth_duo_id,omitempty"` 15 | } 16 | 17 | // GetType implement Request interface 18 | func (r GoodsSearchRequest) GetType() string { 19 | return "pdd.ddk.oauth.goods.search" 20 | } 21 | 22 | // GoodsSearch 多多进宝商品查询 23 | func GoodsSearch(ctx context.Context, clt *core.SDKClient, req *GoodsSearchRequest, accessToken string) (*ddk.GoodsSearchResult, error) { 24 | var resp ddk.GoodsSearchResponse 25 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 26 | return nil, err 27 | } 28 | return resp.Response, nil 29 | } 30 | -------------------------------------------------------------------------------- /api/ddk/oauth/goods_zs_unit_url_gen.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // GoodsZsUnitUrlGenRequest 多多进宝转链接口 API Request 11 | type GoodsZsUnitUrlGenRequest struct { 12 | ddk.GoodsZsUnitUrlGenRequest 13 | // GenerateSchemeURL 是否返回 schema URL 14 | GenerateSchemeURL bool `json:"generate_scheme_url,omitempty"` 15 | } 16 | 17 | // GetType implement Request interface 18 | func (r GoodsZsUnitUrlGenRequest) GetType() string { 19 | return "pdd.ddk.oauth.goods.zs.unit.url.gen" 20 | } 21 | 22 | // GoodsZsUnitUrlGen 多多进宝转链接口 23 | func GoodsZsUnitUrlGen(ctx context.Context, clt *core.SDKClient, req *GoodsZsUnitUrlGenRequest, accessToken string) (*ddk.PromURL, error) { 24 | var resp ddk.GoodsZsUnitUrlGenResponse 25 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 26 | return nil, err 27 | } 28 | return resp.Response, nil 29 | } 30 | -------------------------------------------------------------------------------- /api/ddk/oauth/member_authority_query.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // MemberAuthorityQueryRequest 查询是否绑定备案 API Request 11 | type MemberAuthorityQueryRequest struct { 12 | ddk.MemberAuthorityQueryRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r MemberAuthorityQueryRequest) GetType() string { 17 | return "pdd.ddk.oauth.member.authority.query" 18 | } 19 | 20 | // MemberAuthorityQuery 查询是否绑定备案 21 | func MemberAuthorityQuery(ctx context.Context, clt *core.SDKClient, req *MemberAuthorityQueryRequest, accessToken string) (bool, error) { 22 | var resp ddk.MemberAuthorityQueryResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return false, err 25 | } 26 | return resp.Response.Bind == 1, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/order_detail_get.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // OrderDetailGetRequest 查询订单详情 API Request 11 | type OrderDetailGetRequest struct { 12 | ddk.OrderDetailGetRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r OrderDetailGetRequest) GetType() string { 17 | return "pdd.ddk.oauth.order.detail.get" 18 | } 19 | 20 | // OrderDetailGet 查询订单详情 21 | func OrderDetailGet(ctx context.Context, clt *core.SDKClient, req *OrderDetailGetRequest, accessToken string) (*ddk.Order, error) { 22 | var resp ddk.OrderDetailGetResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return nil, err 25 | } 26 | return resp.Response, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/pid_mediaid_bind.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // PidMediaIDBindRequest 批量绑定推广位的媒体id API Request 11 | type PidMediaIDBindRequest struct { 12 | ddk.PidMediaIDBindRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r PidMediaIDBindRequest) GetType() string { 17 | return "pdd.ddk.oauth.pid.mediaid.bind" 18 | } 19 | 20 | // PidMediaIDBind 批量绑定推广位的媒体id 21 | func PidMediaIDBind(ctx context.Context, clt *core.SDKClient, mediaID uint64, pidList []string, accessToken string) (*ddk.PidMediaIDBindResult, error) { 22 | var ( 23 | req = &PidMediaIDBindRequest{ 24 | PidMediaIDBindRequest: ddk.PidMediaIDBindRequest{ 25 | MediaID: mediaID, 26 | PidList: pidList, 27 | }, 28 | } 29 | resp ddk.PidMediaIDBindResponse 30 | ) 31 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 32 | return nil, err 33 | } 34 | return resp.Response.Result, nil 35 | } 36 | -------------------------------------------------------------------------------- /api/ddk/oauth/resource_url_gen.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // ResourceUrlGenRequest 生成多多进宝频道推广 API Request 11 | type ResourceUrlGenRequest struct { 12 | ddk.ResourceUrlGenRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r ResourceUrlGenRequest) GetType() string { 17 | return "pdd.ddk.oauth.resource.url.gen" 18 | } 19 | 20 | // ResourceUrlGen 生成多多进宝频道推广 21 | func ResourceUrlGen(ctx context.Context, clt *core.SDKClient, req *ResourceUrlGenRequest, accessToken string) (*ddk.PromURL, error) { 22 | var resp ddk.ResourceUrlGenResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return nil, err 25 | } 26 | return resp.Response, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/rp_prom_url_generate.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // RpPromUrlGenerateRequest 生成营销工具推广链接 API Request 11 | type RpPromUrlGenerateRequest struct { 12 | ddk.RpPromUrlGenerateRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r RpPromUrlGenerateRequest) GetType() string { 17 | return "pdd.ddk.oauth.rp.prom.url.generate" 18 | } 19 | 20 | // RpPromUrlGenerate 生成营销工具推广链接 21 | func RpPromUrlGenerate(ctx context.Context, clt *core.SDKClient, req *RpPromUrlGenerateRequest, accessToken string) (*ddk.RpPromUrlGenerateResult, error) { 22 | var resp ddk.RpPromUrlGenerateResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return nil, err 25 | } 26 | return resp.Response, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/oauth/weapp_qrcode_url_gen.go: -------------------------------------------------------------------------------- 1 | package oauth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/api/ddk" 7 | "github.com/bububa/openpdd/core" 8 | ) 9 | 10 | // WeappQrcodeUrlGenRequest 多多客生成单品推广小程序二维码url API Request 11 | type WeappQrcodeUrlGenRequest struct { 12 | ddk.WeappQrcodeUrlGenRequest 13 | } 14 | 15 | // GetType implement Request interface 16 | func (r WeappQrcodeUrlGenRequest) GetType() string { 17 | return "pdd.ddk.oauth.weapp.qrcode.url.gen" 18 | } 19 | 20 | // WeappQrcodeUrlGen 多多客生成单品推广小程序二维码url 21 | func WeappQrcodeUrlGen(ctx context.Context, clt *core.SDKClient, req *WeappQrcodeUrlGenRequest, accessToken string) (string, error) { 22 | var resp ddk.WeappQrcodeUrlGenResponse 23 | if err := clt.Do(ctx, req, &resp, accessToken); err != nil { 24 | return "", err 25 | } 26 | return resp.Response.URL, nil 27 | } 28 | -------------------------------------------------------------------------------- /api/ddk/order.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | // Order 订单详情 4 | type Order struct { 5 | // ActivityTags 商品活动标记数组,例:[4,7],4-秒杀 7-百亿补贴等 6 | ActivityTags []int `json:"activity_tags,omitempty" xml:"activity_tags,omitempty"` 7 | // AuthDuoID 多多客工具id 8 | AuthDuoID uint64 `json:"auth_duo_id,omitempty" xml:"auth_duo_id,omitempty"` 9 | // BatchNo 结算批次号 10 | BathNo string `json:"batch_no,omitempty" xml:"batch_no,omitempty"` 11 | // CatIDs 商品一~四级类目ID列表 12 | CatIDs []uint64 `json:"cat_ids,omitempty" xml:"cat_ids,omitempty"` 13 | // CpaNew 是否是 cpa 新用户,1表示是,0表示否 14 | CpaNew int `json:"cpa_new,omitempty" xml:"cpa_new,omitempty"` 15 | // CpsSign CPS_Sign 16 | CpsSign string `json:"cps_sign,omitempty" xml:"cps_sign,omitempty"` 17 | // CustomParameters 自定义参数 18 | CustomParameters string `json:"custom_parameters,omitempty" xml:"custom_parameters,omitempty"` 19 | // FailReason 订单审核失败原因 20 | FailReason string `json:"fail_reason,omitempty" xml:"fail_reason,omitempty"` 21 | // GoodsCategoryName 商品一级类目名称 22 | GoodsCategoryName string `json:"goods_category_name,omitempty" xml:"goods_category_name,omitempty"` 23 | // GoodsID 商品id 24 | GoodsID uint64 `json:"goods_id,omitempty" xml:"goods_id,omitempty"` 25 | // GoodsName 商品名称 26 | GoodsName string `json:"goods_name,omitempty" xml:"goods_name,omitempty"` 27 | // GoodsPrice 商品价格(分) 28 | GoodsPrice int64 `json:"goods_price,omitempty" xml:"goods_price,omitempty"` 29 | // GoodsQuantity 商品数量 30 | GoodsQuantity int64 `json:"goods_quantity,omitempty" xml:"goods_quantity,omitempty"` 31 | // GoodsSign goodsSign是加密后的goodsId,goodsId已下线,请使用goodsSign来替代。需要注意的是:推广链接带有goodsSign信息时,订单会返回原goodsSign;反之,会生成新的goodsSign返回。 32 | GoodsSign string `json:"goods_sign,omitempty" xml:"goods_sign,omitempty"` 33 | // GoodsThumbnailURL 商品缩略图 34 | GoodsThumbnailURL string `json:"goods_thumbnail_url,omitempty" xml:"goods_thumbnail_url,omitempty"` 35 | // GroupID 成团编号 36 | GroupID uint64 `json:"group_id,omitempty" xml:"group_id,omitempty"` 37 | // IsDirect 是否直推 ,1表示是,0表示否 38 | IsDirect int `json:"is_direct,omitempty" xml:"is_direct,omitempty"` 39 | // MallID 店铺id 40 | MallID uint64 `json:"mall_id,omitempty" xml:"mall_id,omitempty"` 41 | // MallName 店铺名称 42 | MallName string `json:"mall_name,omitempty" xml:"mall_name,omitempty"` 43 | // NoSubsidyReason 非补贴订单原因,例如:"商品补贴达上限","达到单个用户下单上限","非指定落地页直推订单","订单超过2个月未审核成功"等 44 | NoSubsidyReason string `json:"no_subsidy_reason,omitempty" xml:"no_subsidy_reason,omitempty"` 45 | // OrderAmount 订单价格(分) 46 | OrderAmount int64 `json:"order_amount,omitempty" xml:"order_amount,omitempty"` 47 | // OrderCreateTime 订单创建时间(UNIX时间戳) 48 | OrderCreateTime int64 `json:"order_create_time,omitempty" xml:"order_create_time,omitempty"` 49 | // OrderGroupSuccessTime 订单创建时间(UNIX时间戳) 50 | OrderGroupSuccessTime int64 `json:"order_group_success_time,omitempty" xml:"order_group_success_time,omitempty"` 51 | // OrderModifyAt 订单最后更新时间(UNIX时间戳) 52 | OrderModifyAt int64 `json:"order_modify_at,omitempty" xml:"order_modify_at,omitempty"` 53 | // OrderPayTime 订单支付时间(UNIX时间戳) 54 | OrderPayTime int64 `json:"order_pay_time,omitempty" xml:"order_pay_time,omitempty"` 55 | // OrderReceiveTime 订单确认收货时间(UNIX时间戳) 56 | OrderReceiveTime int64 `json:"order_receive_time,omitempty" xml:"order_receive_time,omitempty"` 57 | // OrderSettleTime 订单结算时间(UNIX时间戳) 58 | OrderSettleTime int64 `json:"order_settle_time,omitempty" xml:"order_settle_time,omitempty"` 59 | // OrderSN 订单编号 60 | OrderSN string `json:"order_sn,omitempty" xml:"order_sn,omitempty"` 61 | // OrderStatus 订单状态 62 | OrderStatus int `json:"order_status,omitempty" xml:"order_status,omitempty"` 63 | // OrderStatusDesc 订单状态:0-已支付;1-已成团;2-确认收货;3-审核成功;4-审核失败(不可提现);5-已经结算 ;10-已处罚 64 | OrderStatusDesc string `json:"order_status_desc,omitempty" xml:"order_status_desc,omitempty"` 65 | // OrderVerifyTime 订单审核时间(UNIX时间戳) 66 | OrderVerifyTime int64 `json:"order_verify_time,omitempty" xml:"order_verify_time,omitempty"` 67 | // Pid 推广位id 68 | Pid string `json:"p_id,omitempty"` 69 | // PlatformDiscount 平台券金额,表示该订单使用的平台券金额,单位分 70 | PlatformDiscount int64 `json:"platform_discount,omitempty" xml:"platform_discount,omitempty"` 71 | // PointTime 打点时间 72 | PointTime int64 `json:"point_time,omitempty" xml:"point_time,omitempty"` 73 | // PriceCompareStatus 比价状态:0:正常,1:比价 74 | PriceCompareStatus int `json:"price_compare_status,omitempty" xml:"price_compare_status,omitempty"` 75 | // PromotionAmount 佣金(分) 76 | PromotionAmount int64 `json:"promotion_amount,omitempty" xml:"promotion_amount,omitempty"` 77 | // PromotionRate 佣金比例 千分比 78 | PromotionRate int `json:"promotion_rate,omitempty" xml:"promotion_rate,omitempty"` 79 | // RedPacketType 超级红包补贴类型:0-非红包补贴订单,1-季度新用户补贴 80 | RedPacketType int `json:"red_packet_type,omitempty" xml:"red_packet_type,omitempty"` 81 | // ReturnStatus 售后状态:0:无,1:售后中,2:售后完成 82 | ReturnStatus int `json:"return_status,omitempty" xml:"return_status,omitempty"` 83 | // SepDuoID 直播间订单推广duoId 84 | SepDuoID uint64 `json:"sep_duo_id,omitempty" xml:"sep_duo_id,omitempty"` 85 | // SepMarketFee 直播间推广佣金 86 | SepMarketFee int64 `json:"sep_market_fee,omitempty" xml:"sep_market_fee,omitempty"` 87 | // SepParams 直播间推广自定义参数 88 | SepParams string `json:"sep_params,omitempty" xml:"sep_params,omitempty"` 89 | // SepPid 直播间订单推广位 90 | SepPid string `json:"sep_pid,omitempty" xml:"sep_pid,omitempty"` 91 | // SepRate 直播间推广佣金比例 92 | SepRate int `json:"sep_rate,omitempty" xml:"sep_rate,omitempty"` 93 | // ShareAmount 招商分成服务费金额,单位为分 94 | ShareAmount int64 `json:"share_amount,omitempty" xml:"share_amount,omitempty"` 95 | // ShareRate 招商分成服务费比例,千分比 96 | ShareRate int `json:"share_rate,omitempty" xml:"share_rate,omitempty"` 97 | // SubSidyAmount 优势渠道专属商品补贴金额,单位为分。针对优质渠道的补贴活动,指定优势渠道可通过推广该商品获取相应补贴。补贴活动入口:[进宝网站-官方活动] 98 | SubSidyAmount int64 `json:"subsidy_amount,omitempty" xml:"subsidy_amount,omitempty"` 99 | // SubsidyDuoAmountLevel 等级补贴给渠道的收入补贴,不允许直接给下级代理展示,单位为分 100 | SubsidyDuoAmountLevel int64 `json:"subsidy_duo_amount_level,omitempty" xml:"subsidy_duo_amount_level,omitempty"` 101 | // SubsidyDuoAmountTenMillion 官方活动给渠道的收入补贴金额,不允许直接给下级代理展示,单位为分 102 | SubsidyDuoAmountTenMillion int64 `json:"subsidy_duo_amount_ten_million,omitempty" xml:"subsidy_duo_amount_ten_million,omitempty"` 103 | // SubsidyType 订单补贴类型:0-非补贴订单,1-千万补贴,2-社群补贴,3-多多星选,4-品牌优选,5-千万神券 104 | SubsidyType int `json:"subsidy_type,omitempty" xml:"subsidy_type,omitempty"` 105 | // Type 下单场景类型:0-单品推广,1-红包活动推广,4-多多进宝商城推广,7-今日爆款,8-品牌清仓,9-1.9包邮,77-刮刮卡活动推广,94-充值中心,101-品牌黑卡,103-百亿补贴频道,104-内购清单频道,105-超级红包 106 | Type int `json:"type,omitempty" xml:"type,omitempty"` 107 | // URLLastGenerateTime 链接最后一次生产时间 108 | URLLastGenerateTime int64 `json:"url_last_generate_time,omitempty" xml:"url_last_generate_time,omitempty"` 109 | // ZsDuoID 招商多多客id 110 | ZsDuoID uint64 `json:"zs_duo_id,omitempty" xml:"zs_duo_id,omitempty"` 111 | } 112 | -------------------------------------------------------------------------------- /api/ddk/order_detail_get.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // OrderDetailGetRequest 查询订单详情 API Request 11 | type OrderDetailGetRequest struct { 12 | // OrderSN 订单号 13 | OrderSN string `json:"order_sn,omitempty"` 14 | // QueryOrderType 订单类型:1-推广订单;2-直播间订单 15 | QueryOrderType int `json:"query_order_type,omitempty"` 16 | } 17 | 18 | // GetType implement Request interface 19 | func (r OrderDetailGetRequest) GetType() string { 20 | return "pdd.ddk.order.detail.get" 21 | } 22 | 23 | // OrderDetailGetResponse 查询订单详情 API Response 24 | type OrderDetailGetResponse struct { 25 | model.CommonResponse 26 | Response *Order `json:"order_detail_response,omitempty" xml:"order_detail_response,omitempty"` 27 | } 28 | 29 | // OrderDetailGet 查询订单详情 30 | func OrderDetailGet(ctx context.Context, clt *core.SDKClient, req *OrderDetailGetRequest) (*Order, error) { 31 | var resp OrderDetailGetResponse 32 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 33 | return nil, err 34 | } 35 | return resp.Response, nil 36 | } 37 | -------------------------------------------------------------------------------- /api/ddk/order_list_increment_get.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // OrderListIncrementGetRequest 最后更新时间段增量同步推广订单信息 API Request 11 | type OrderListIncrementGetRequest struct { 12 | // CashGiftOrder 是否为礼金订单,查询礼金订单时,订单类型不填(默认推广订单)。 13 | CashGiftOrder bool `json:"cash_gift_order,omitempty"` 14 | // EndUpdateTime 查询结束时间,和开始时间相差不能超过24小时。note:此时间为时间戳,指格林威治时间 1970 年01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数 15 | EndUpdateTime int64 `json:"end_update_time,omitempty"` 16 | // Page 第几页,从1到10000,默认1,注:使用最后更新时间范围增量同步时,必须采用倒序的分页方式(从最后一页往回取)才能避免漏单问题。 17 | Page int `json:"page,omitempty"` 18 | // PageSize 返回的每页结果订单数,默认为100,范围为10到100,建议使用40~50,可以提高成功率,减少超时数量。 19 | PageSize int `json:"page_size,omitempty"` 20 | // QueryOrderType 订单类型:1-推广订单;2-直播间订单 21 | QueryOrderType int `json:"query_order_type,omitempty"` 22 | // ReturnCount 是否返回总数,默认为true,如果指定false, 则返回的结果中不包含总记录数,通过此种方式获取增量数据,效率在原有的基础上有80%的提升。 23 | ReturnCount *bool `json:"return_count,omitempty"` 24 | // StartUpdateTime 最近90天内多多进宝商品订单更新时间--查询时间开始。note:此时间为时间戳,指格林威治时间 1970 年01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数 25 | StartUpdateTime int64 `json:"start_update_time,omitempty"` 26 | } 27 | 28 | // GetType implement Request interface 29 | func (r OrderListIncrementGetRequest) GetType() string { 30 | return "pdd.ddk.order.list.increment.get" 31 | } 32 | 33 | // OrderListIncrementGetResponse 最后更新时间段增量同步推广订单信息 API Response 34 | type OrderListIncrementGetResponse struct { 35 | model.CommonResponse 36 | Response struct { 37 | // List order list 38 | List []Order `json:"order_list,omitempty" xml:"order_list,omitempty"` 39 | // TotalCount total count 40 | TotalCount int `json:"total_count,omitempty" xml:"total_count,omitempty"` 41 | } `json:"order_list_get_response" xml:"order_list_get_response"` 42 | } 43 | 44 | // OrderListIncrementGet 最后更新时间段增量同步推广订单信息 45 | func OrderListIncrementGet(ctx context.Context, clt *core.SDKClient, req *OrderListIncrementGetRequest) (int, []Order, error) { 46 | var resp OrderListIncrementGetResponse 47 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 48 | return 0, nil, err 49 | } 50 | return resp.Response.TotalCount, resp.Response.List, nil 51 | } 52 | -------------------------------------------------------------------------------- /api/ddk/order_list_range_get.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // OrderListRangeGetRequest 用时间段查询推广订单接口 API Request 11 | type OrderListRangeGetRequest struct { 12 | // CashGiftOrder 是否为礼金订单,查询礼金订单时,订单类型不填(默认推广订单)。 13 | CashGiftOrder bool `json:"cash_gift_order,omitempty"` 14 | // EndTime 支付结束时间,格式: "yyyy-MM-dd HH:mm:ss" ,比如 "2020-12-01 00:00:00" 15 | EndTime string `json:"end_time,omitempty"` 16 | // StartTime 支付起始时间,格式: "yyyy-MM-dd HH:mm:ss" ,比如 "2020-12-01 00:00:00" 17 | StartTime string `json:"start_time,omitempty"` 18 | // LastOrderID 上一次的迭代器id(第一次不填) 19 | LastOrderID string `json:"last_order_id,omitempty"` 20 | // PageSize 每次请求多少条,建议300 21 | PageSize int `json:"page_size,omitempty"` 22 | // QueryOrderType 订单类型:1-推广订单;2-直播间订单 23 | QueryOrderType int `json:"query_order_type,omitempty"` 24 | } 25 | 26 | // GetType implement Request interface 27 | func (r OrderListRangeGetRequest) GetType() string { 28 | return "pdd.ddk.order.list.range.get" 29 | } 30 | 31 | // OrderListRangeGetResponse 用时间段查询推广订单接口 API Response 32 | type OrderListRangeGetResponse struct { 33 | model.CommonResponse 34 | Response struct { 35 | // LastOrderID last_order_id 36 | LastOrderID string `json:"last_order_id,omitempty" xml:"last_order_id,omitempty"` 37 | // OrderList 多多进宝推广位对象列表 38 | OrderList []Order `json:"order_list,omitempty" xml:"order_list,omitempty"` 39 | } `json:"order_list_get_response" xml:"order_list_get_response"` 40 | } 41 | 42 | // OrderListRangeGet 用时间段查询推广订单接口 43 | func OrderListRangeGet(ctx context.Context, clt *core.SDKClient, req *OrderListRangeGetRequest) (string, []Order, error) { 44 | var resp OrderListRangeGetResponse 45 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 46 | return "", nil, err 47 | } 48 | return resp.Response.LastOrderID, resp.Response.OrderList, nil 49 | } 50 | -------------------------------------------------------------------------------- /api/ddk/pid.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | // Pid 推广位 4 | type Pid struct { 5 | // CreateTime 推广位创建时间 6 | CreateTime int64 `json:"create_time,omitempty" xml:"create_time,omitempty"` 7 | // Name 推广位名称 8 | Name string `json:"pid_name,omitempty" xml:"pid_name,omitempty"` 9 | // ID 调用方推广位ID 10 | ID string `json:"p_id,omitempty" xml:"p_id,omitempty"` 11 | // MediaID 媒体id 12 | MediaID uint64 `json:"media_id,omitempty" xml:"media_id,omitempty"` 13 | // Status 推广位状态:0-正常,1-封禁 14 | Status int `json:"status,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /api/ddk/pid_mediaid_bind.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // PidMediaIDBindRequest 批量绑定推广位的媒体id API Request 11 | type PidMediaIDBindRequest struct { 12 | // MediaID 媒体id 13 | MediaID uint64 `json:"media_id"` 14 | // PidList 推广位列表,例如:["60005_612"],最多支持同时传入1000个 15 | PidList []string `json:"pid_list"` 16 | } 17 | 18 | // GetType implement Request interface 19 | func (r PidMediaIDBindRequest) GetType() string { 20 | return "pdd.ddk.pid.mediaid.bind" 21 | } 22 | 23 | // PidMediaIDBindResponse 批量绑定推广位的媒体id API Response 24 | type PidMediaIDBindResponse struct { 25 | model.CommonResponse 26 | // Response response 27 | Response struct { 28 | // Result 绑定结果 29 | Result *PidMediaIDBindResult `json:"result,omitempty" xml:"result,omitempty"` 30 | } `json:"p_id_bind_response" xml:"p_id_bind_response"` 31 | } 32 | 33 | // PidMediaIDBindResult 绑定结果 34 | type PidMediaIDBindResult struct { 35 | // Msg 绑定结果文本提示 36 | Msg string `json:"msg,omitempty" xml:"msg,omitempty"` 37 | // Result 绑定结果 38 | Result bool `json:"result,omitempty" xml:"result,omitempty"` 39 | } 40 | 41 | // PidMediaIDBind 批量绑定推广位的媒体id 42 | func PidMediaIDBind(ctx context.Context, clt *core.SDKClient, mediaID uint64, pidList []string) (*PidMediaIDBindResult, error) { 43 | var ( 44 | req = &PidMediaIDBindRequest{ 45 | MediaID: mediaID, 46 | PidList: pidList, 47 | } 48 | resp PidMediaIDBindResponse 49 | ) 50 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 51 | return nil, err 52 | } 53 | return resp.Response.Result, nil 54 | } 55 | -------------------------------------------------------------------------------- /api/ddk/promotion_goods_query.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // PromotionGoodsQueryRequest 多多进宝信息流投放商品报备进度查询 API Request 11 | type PromotionGoodsQueryRequest struct { 12 | // PageNumber 分页查询页数 13 | PageNumber int `json:"page_number,omitempty"` 14 | // PageSize 分页查询页大小 15 | PageSize int `json:"page_size,omitempty"` 16 | // GoodsID 商品id 17 | GoodsID uint64 `json:"goods_id,omitempty"` 18 | // MallID 店铺id 19 | MallID uint64 `json:"mall_id,omitempty"` 20 | // StatusList 查询状态列表 21 | StatusList []int `json:"status_list,omitempty"` 22 | // UpdateEndTime 最后更新结束时间(最长支持30天) 23 | UpdateEndTime int64 `json:"update_end_time,omitempty"` 24 | // UpdateStartTime 最后更新开始时间 25 | UpdateStartTime int64 `json:"update_start_time,omitempty"` 26 | } 27 | 28 | // GetType implement Request interface 29 | func (r PromotionGoodsQueryRequest) GetType() string { 30 | return "pdd.ddk.promotion.goods.query" 31 | } 32 | 33 | // PromotionGoodsQueryResponse 多多进宝信息流投放商品报备进度查询 API Response 34 | type PromotionGoodsQueryResponse struct { 35 | model.CommonResponse 36 | Response struct { 37 | // List 报备记录列表 38 | List []PromotionGoodsApplication `json:"application_list,omitempty"` 39 | // Total 报备记录总数 40 | Total int `json:"total,omitempty"` 41 | } `json:"response"` 42 | } 43 | 44 | // PromotionGoodsApplication 多多进宝信息流投放商品报备进度 45 | type PromotionGoodsApplication struct { 46 | // Comment 审核信息 47 | Comment string `json:"comment,omitempty"` 48 | // CommitTime 报备提交时间 49 | CommitTime int64 `json:"commit_time,omitempty"` 50 | // GoodsID 商品id 51 | GoodsID uint64 `json:"goods_id,omitempty"` 52 | // MallID 店铺id 53 | MallID uint64 `json:"mall_id,omitempty"` 54 | // PromotionEndTime 推广结束时间 55 | PromotionEndTime int64 `json:"promotion_end_time,omitempty"` 56 | // PromotionStartTime 推广开始时间 57 | PromotionStartTime int64 `json:"promotion_start_time,omitempty"` 58 | // Status 报备状态。0-已创建,1-已提交,2-已通过,3-已驳回 59 | Status int `json:"status,omitempty"` 60 | // UpdatedAt 最后更新时间 61 | UpdatedAt int64 `json:"updated_at,omitempty"` 62 | } 63 | 64 | // PromotionGoodsQuery 多多进宝信息流投放商品报备进度查询 65 | func PromotionGoodsQuery(ctx context.Context, clt *core.SDKClient, req *PromotionGoodsQueryRequest) (int, []PromotionGoodsApplication, error) { 66 | var resp PromotionGoodsQueryResponse 67 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 68 | return 0, nil, err 69 | } 70 | return resp.Response.Total, resp.Response.List, nil 71 | } 72 | -------------------------------------------------------------------------------- /api/ddk/range_item.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | // RangeItem 自定义红包抵后价和商品佣金区间对象 4 | type RangeItem struct { 5 | // RangeFrom 区间的开始值 6 | RangeFrom int64 `json:"range_from,omitempty"` 7 | // RangeID range_id为1表示红包抵后价(单位分), range_id为2表示佣金比例(单位千分之几) 8 | RangeID int `json:"range_id,omitempty"` 9 | // RangeTo 区间的结束值 10 | RangeTo int64 `json:"range_to,omitempty"` 11 | } 12 | -------------------------------------------------------------------------------- /api/ddk/report_img_upload.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | "io" 6 | 7 | "github.com/bububa/openpdd/core" 8 | "github.com/bububa/openpdd/model" 9 | ) 10 | 11 | // ReportImgUploadRequest 多多客信息流投放备案图片上传接口 API Request 12 | type ReportImgUploadRequest struct { 13 | // File 多多视频图片文件流 14 | File io.Reader `json:"file,omitempty"` 15 | // Filename 16 | Filename string `json:"filename,omitempty"` 17 | } 18 | 19 | // GetType implement Request interface 20 | func (r ReportImgUploadRequest) GetType() string { 21 | return "pdd.ddk.report.img.upload" 22 | } 23 | 24 | // Encode implement UploadRequest interface 25 | func (r ReportImgUploadRequest) Encode() []model.UploadField { 26 | return []model.UploadField{ 27 | { 28 | Key: "file", 29 | Value: r.Filename, 30 | Reader: r.File, 31 | }, 32 | } 33 | } 34 | 35 | // ReportImgUploadResponse 多多客信息流投放备案图片上传接口 API Response 36 | type ReportImgUploadResponse struct { 37 | model.CommonResponse 38 | Response struct { 39 | // URL 40 | URL string `json:"url,omitempty"` 41 | } `json:"response"` 42 | } 43 | 44 | // ReportImgUpload 多多进宝信息流渠道备案授权素材上传接口 45 | func ReportImgUpload(ctx context.Context, clt *core.SDKClient, req *ReportImgUploadRequest) (string, error) { 46 | var resp ReportImgUploadResponse 47 | if err := clt.Upload(ctx, req, &resp, ""); err != nil { 48 | return "", err 49 | } 50 | return resp.Response.URL, nil 51 | } 52 | -------------------------------------------------------------------------------- /api/ddk/report_video_upload.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | "io" 6 | 7 | "github.com/bububa/openpdd/core" 8 | "github.com/bububa/openpdd/model" 9 | ) 10 | 11 | // ReportVideoUploadRequest 多多客信息流投放备案视频上传接口 API Request 12 | type ReportVideoUploadRequest struct { 13 | // File 多多视频图片文件流 14 | File io.Reader `json:"file,omitempty"` 15 | // Filename 16 | Filename string `json:"filename,omitempty"` 17 | } 18 | 19 | // GetType implement Request interface 20 | func (r ReportVideoUploadRequest) GetType() string { 21 | return "pdd.ddk.report.video.upload" 22 | } 23 | 24 | // Encode implement UploadRequest interface 25 | func (r ReportVideoUploadRequest) Encode() []model.UploadField { 26 | return []model.UploadField{ 27 | { 28 | Key: "file", 29 | Value: r.Filename, 30 | Reader: r.File, 31 | }, 32 | } 33 | } 34 | 35 | // ReportVideoUploadResponse 多多客信息流投放备案视频上传接口 API Response 36 | type ReportVideoUploadResponse struct { 37 | model.CommonResponse 38 | Response struct { 39 | // URL 40 | URL string `json:"url,omitempty"` 41 | } `json:"response"` 42 | } 43 | 44 | // ReportVideoUpload 多多客信息流投放备案视频上传接口 45 | // 多多客信息流投放备案视频上传,上传视频大小有限制,单个文件超过20M需要走分片上传 46 | func ReportVideoUpload(ctx context.Context, clt *core.SDKClient, req *ReportVideoUploadRequest) (string, error) { 47 | var resp ReportVideoUploadResponse 48 | if err := clt.Upload(ctx, req, &resp, ""); err != nil { 49 | return "", err 50 | } 51 | return resp.Response.URL, nil 52 | } 53 | -------------------------------------------------------------------------------- /api/ddk/report_video_upload_fearless.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "context" 7 | "sync" 8 | "sync/atomic" 9 | 10 | "github.com/bububa/openpdd/core" 11 | ) 12 | 13 | // ReportVideoUploadFearless 多多客信息流投放备案视频上传接口 14 | // 多多客信息流投放备案视频上传,上传视频大小有限制,单个文件超过20M需要走分片上传 15 | // 根据上传文件大小自动选择API接口 16 | func ReportVideoUploadFearless(ctx context.Context, clt *core.SDKClient, req *ReportVideoUploadRequest, size int64, parallel int) (string, error) { 17 | var maxSize int64 = 20 * 1 << 20 18 | var chunkSize int64 = 5 * 1 << 20 19 | if size <= maxSize { 20 | return ReportVideoUpload(ctx, clt, req) 21 | } 22 | uploadSign, err := ReportVideoUploadPartInit(ctx, clt, "video/mp4") 23 | if err != nil { 24 | return "", err 25 | } 26 | if parallel <= 0 { 27 | parallel = 1 28 | } 29 | var ( 30 | fr = bufio.NewReader(req.File) 31 | b = make([]byte, chunkSize) 32 | wg sync.WaitGroup 33 | guard = make(chan struct{}, parallel) 34 | uploadErr = &atomic.Value{} 35 | partNum int 36 | ) 37 | for { 38 | n, err := fr.Read(b) 39 | if err != nil { 40 | break 41 | } 42 | partNum++ 43 | buf := bytes.NewReader(b[0:n]) 44 | partReq := ReportVideoUploadPartRequest{ 45 | PartFile: buf, 46 | PartNum: partNum, 47 | UploadSign: uploadSign, 48 | } 49 | guard <- struct{}{} 50 | wg.Add(1) 51 | go func(req *ReportVideoUploadPartRequest) { 52 | defer wg.Done() 53 | if _, err = ReportVideoUploadPart(ctx, clt, &partReq); err != nil { 54 | uploadErr.Store(err) 55 | } 56 | <-guard 57 | }(&partReq) 58 | } 59 | wg.Wait() 60 | if err := uploadErr.Load(); err != nil { 61 | return "", err.(error) 62 | } 63 | return ReportVideoUploadPartComplete(ctx, clt, uploadSign) 64 | } 65 | -------------------------------------------------------------------------------- /api/ddk/report_video_upload_part.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "strconv" 7 | 8 | "github.com/bububa/openpdd/core" 9 | "github.com/bububa/openpdd/model" 10 | ) 11 | 12 | // ReportVideoUploadPartRequest 多多客信息流投放备案视频上传分片上传接口 API Request 13 | type ReportVideoUploadPartRequest struct { 14 | // PartFile 当前分片的文件流 15 | PartFile io.Reader `json:"part_file,omitempty"` 16 | // PartNum 当前分片编号名,从1开始 17 | PartNum int `json:"part_num,omitempty"` 18 | // UploadSign 标记本次大文件上传的id(init阶段的返回值) 19 | UploadSign string `json:"upload_sign,omitempty"` 20 | } 21 | 22 | // GetType implement Request interface 23 | func (r ReportVideoUploadPartRequest) GetType() string { 24 | return "pdd.ddk.report.video.upload.part" 25 | } 26 | 27 | // Encode implement UploadRequest interface 28 | func (r ReportVideoUploadPartRequest) Encode() []model.UploadField { 29 | partNum := r.PartNum 30 | if partNum == 0 { 31 | partNum = 1 32 | } 33 | return []model.UploadField{ 34 | { 35 | Key: "part_file", 36 | Reader: r.PartFile, 37 | }, 38 | { 39 | Key: "part_num", 40 | Value: strconv.Itoa(partNum), 41 | }, 42 | { 43 | Key: "upload_sign", 44 | Value: r.UploadSign, 45 | }, 46 | } 47 | } 48 | 49 | // ReportVideoUploadPartResponse 多多客信息流投放备案视频上传分片上传接口 API Response 50 | type ReportVideoUploadPartResponse struct { 51 | model.CommonResponse 52 | Response struct { 53 | // UploadPartNum 表示本次成功上传的part number 54 | UploadPartNum int `json:"uploaded_part_num,omitempty"` 55 | } `json:"response"` 56 | } 57 | 58 | // ReportVidoeUploadPart 多多客信息流投放备案视频上传分片上传接口 59 | func ReportVideoUploadPart(ctx context.Context, clt *core.SDKClient, req *ReportVideoUploadPartRequest) (int, error) { 60 | var resp ReportVideoUploadPartResponse 61 | if err := clt.Upload(ctx, req, &resp, ""); err != nil { 62 | return 0, err 63 | } 64 | return resp.Response.UploadPartNum, nil 65 | } 66 | -------------------------------------------------------------------------------- /api/ddk/report_video_upload_part_complete.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // ReportVideoUploadPartCompleteRequest 多多客信息流投放备案视频上传分片完成接口 API Request 11 | type ReportVideoUploadPartCompleteRequest struct { 12 | // UploadSign 标记本次大文件上传的id(init阶段的返回值) 13 | UploadSign string `json:"upload_sign,omitempty"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r ReportVideoUploadPartCompleteRequest) GetType() string { 18 | return "pdd.ddk.report.video.upload.part.complete" 19 | } 20 | 21 | // Encode implement UploadRequest interface 22 | func (r ReportVideoUploadPartCompleteRequest) Encode() []model.UploadField { 23 | return []model.UploadField{ 24 | { 25 | Key: "upload_sign", 26 | Value: r.UploadSign, 27 | }, 28 | } 29 | } 30 | 31 | // ReportVideoUploadPartCompleteResponse 多多客信息流投放备案视频上传分片完成接口 API Response 32 | type ReportVideoUploadPartCompleteResponse struct { 33 | model.CommonResponse 34 | Response struct { 35 | // URL 创建的视频资源对应的vid 36 | URL string `json:"url,omitempty"` 37 | } `json:"response"` 38 | } 39 | 40 | // ReportVidoeUploadPartComplete 多多客信息流投放备案视频上传分片完成接口 41 | func ReportVideoUploadPartComplete(ctx context.Context, clt *core.SDKClient, uploadSign string) (string, error) { 42 | var resp ReportVideoUploadPartCompleteResponse 43 | if err := clt.Upload(ctx, &ReportVideoUploadPartCompleteRequest{ 44 | UploadSign: uploadSign, 45 | }, &resp, ""); err != nil { 46 | return "", err 47 | } 48 | return resp.Response.URL, nil 49 | } 50 | -------------------------------------------------------------------------------- /api/ddk/report_video_upload_part_init.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // ReportVideoUploadPartInitRequest 多多客信息流投放备案视频上传分片初始化接口 API Request 11 | type ReportVideoUploadPartInitRequest struct { 12 | // ContentType 文件对应的contentType,且必须为视频类型 13 | ContentType string `json:"content_type,omitempty"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r ReportVideoUploadPartInitRequest) GetType() string { 18 | return "pdd.ddk.report.video.upload.part.init" 19 | } 20 | 21 | // Encode implement UploadRequest interface 22 | func (r ReportVideoUploadPartInitRequest) Encode() []model.UploadField { 23 | return []model.UploadField{ 24 | { 25 | Key: "content_type", 26 | Value: r.ContentType, 27 | }, 28 | } 29 | } 30 | 31 | // ReportVideoUploadPartInitResponse 多多客信息流投放备案视频上传分片初始化接口 API Response 32 | type ReportVideoUploadPartInitResponse struct { 33 | model.CommonResponse 34 | Response struct { 35 | UploadSign string `json:"upload_sign,omitempty"` 36 | } `json:"response"` 37 | } 38 | 39 | // ReportVidoeUploadPartInit 多多客信息流投放备案视频上传分片初始化接口 40 | func ReportVideoUploadPartInit(ctx context.Context, clt *core.SDKClient, contentType string) (string, error) { 41 | var resp ReportVideoUploadPartInitResponse 42 | if err := clt.Upload(ctx, &ReportVideoUploadPartInitRequest{ 43 | ContentType: contentType, 44 | }, &resp, ""); err != nil { 45 | return "", err 46 | } 47 | return resp.Response.UploadSign, nil 48 | } 49 | -------------------------------------------------------------------------------- /api/ddk/resource.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | // PromResource . 4 | type PromResource struct { 5 | // Desc 活动描述 6 | Desc string `json:"desc,omitempty"` 7 | // URL 活动地址 8 | URL string `json:"url,omitempty"` 9 | } 10 | -------------------------------------------------------------------------------- /api/ddk/resource_url_gen.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // ResourceUrlGenRequest 生成多多进宝频道推广 API Request 11 | type ResourceUrlGenRequest struct { 12 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key 13 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 14 | // GenerateSchemaURL 是否返回 schema URL 15 | GenerateSchemaURL bool `json:"generate_schema_url,omitempty"` 16 | // GenerateWeApp 是否生成拼多多福利券微信小程序推广信息 17 | GenerateWeApp bool `json:"generate_we_app,omitempty"` 18 | // Pid 推广位 19 | Pid string `json:"pid,omitempty"` 20 | // ResourceType 频道来源:4-限时秒杀,39997-充值中心, 39998-活动转链,39996-百亿补贴,39999-电器城,40000-领券中心,50005-火车票 21 | ResourceType int `json:"resource_type,omitempty"` 22 | // URL 原链接 23 | URL string `json:"url,omitempty"` 24 | } 25 | 26 | // GetType implement Request interface 27 | func (r ResourceUrlGenRequest) GetType() string { 28 | return "pdd.ddk.resource.url.gen" 29 | } 30 | 31 | // ResourceUrlGenResponse 生成多多进宝频道推广 API Response 32 | type ResourceUrlGenResponse struct { 33 | model.CommonResponse 34 | Response *PromURL `json:"resource_url_response,omitempty" xml:"resource_url_response,omitempty"` 35 | } 36 | 37 | // ResourceUrlGen 生成多多进宝频道推广 38 | func ResourceUrlGen(ctx context.Context, clt *core.SDKClient, req *ResourceUrlGenRequest) (*PromURL, error) { 39 | var resp ResourceUrlGenResponse 40 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 41 | return nil, err 42 | } 43 | return resp.Response, nil 44 | } 45 | -------------------------------------------------------------------------------- /api/ddk/rp_prom_url_generate.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // RpPromUrlGenerateRequest 生成营销工具推广链接 API Request 11 | type RpPromUrlGenerateRequest struct { 12 | // Amount 初始金额(单位分),有效金额枚举值:300、500、700、1100和1600,默认300 13 | Amount int64 `json:"amount,omitempty"` 14 | // ChannelType 营销工具类型,必填:-1-活动列表,0-红包(需申请推广权限),2–新人红包,3-刮刮卡,5-员工内购,10-生成绑定备案链接,12-砸金蛋,14-千万补贴B端页面,15-充值中心B端页面,16-千万补贴C端页面,17-千万补贴投票页面,23-超级红包,24-礼金全场N折活动B端页面,27-带货赢千万,28-满减券活动B端页面,29-满减券活动C端页面,30-免单B端页面,31-免单C端页面,32-转盘得现金B端页面,33-转盘得现金C端页面,34-千万神券C端页面,35-千万神券B端页面;红包推广权限申请流程链接:https://jinbao.pinduoduo.com/qa-system?questionId=289 15 | ChannelType int `json:"channel_type,omitempty"` 16 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。(如果使用GET请求,请使用URLEncode处理参数) 17 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 18 | // DiyYuanParam 一元购自定义参数,json格式,例如:{"goods_sign":"Y9b2_0uSWMFPGSaVwvfZAlm_y2ADLWZl_JQ7UYaS80K"} 19 | DiyYuanParam *DiyYuanParam `json:"diy_yuan_param,omitempty"` 20 | // DiyRedPacketParam 红包自定义参数,json格式 21 | DiyRedPacketParam *DiyRedPacketParam `json:"diy_red_packet_param,omitempty"` 22 | // GenerateQQApp 是否生成qq小程序 23 | GenerateQQApp bool `json:"generate_qq_app,omitempty"` 24 | // GenerateSchemaURL 是否返回 schema URL 25 | GenerateSchemaURL bool `json:"generate_schema_url,omitempty"` 26 | // GenerateShortURL 是否生成短链接。true-是,false-否,默认false 27 | GenerateShortURL bool `json:"generate_short_url,omitempty"` 28 | // GenerateWeApp 是否生成拼多多福利券微信小程序推广信息 29 | GenerateWeApp bool `json:"generate_we_app,omitempty"` 30 | // PidList 推广位列表,长度最大为1,例如:["60005_612"]。活动页生链要求传入授权备案信息,不支持批量生链。 31 | PidList []string `json:"p_id_list,omitempty"` 32 | // ScratchCardAmount 刮刮卡指定金额(单位分),可指定2-100元间数值,即有效区间为:[200,10000] 33 | ScratchCardAmount int64 `json:"scratch_card_amount,omitempty"` 34 | } 35 | 36 | // DiyYuanParam 一元购自定义参数,json格式,例如:{"goods_sign":"Y9b2_0uSWMFPGSaVwvfZAlm_y2ADLWZl_JQ7UYaS80K"} 37 | type DiyYuanParam struct { 38 | // GoodsSign 商品goodsSign,支持通过goodsSign查询商品。goodsSign是加密后的goodsId, goodsId已下线,请使用goodsSign来替代。使用说明:https://jinbao.pinduoduo.com/qa-system?questionId=252 39 | GoodsSign string `json:"goods_sign,omitempty"` 40 | } 41 | 42 | // DiyRedPacketParam 红包自定义参数,json格式 43 | type DiyRedPacketParam struct { 44 | // AmountProbability 红包金额列表,200、300、500、1000、2000,单位分。红包金额和红包抵后价设置只能二选一,默认设置了红包金额会忽略红包抵后价设置 45 | AmountProbability []int64 `json:"amount_probability,omitempty"` 46 | // DisText 设置玩法,false-现金红包, true-现金券 47 | DisText *bool `json:"dis_text,omitempty"` 48 | // NotShowBackground 推广页设置,false-红包开启页, true-红包领取页 49 | NotShowBackground *bool `json:"not_show_background,omitempty"` 50 | // OptID 优先展示类目 51 | OptID uint64 `json:"opt_id,omitempty"` 52 | // RangeItems 自定义红包抵后价和商品佣金区间对象数组 53 | RangeItems []RangeItem `json:"range_items,omitempty"` 54 | } 55 | 56 | // GetType implement Request interface 57 | func (r RpPromUrlGenerateRequest) GetType() string { 58 | return "pdd.ddk.rp.prom.url.generate" 59 | } 60 | 61 | // RpPromUrlGenerateResponse 生成营销工具推广链接 API Response 62 | type RpPromUrlGenerateResponse struct { 63 | model.CommonResponse 64 | Response *RpPromUrlGenerateResult `json:"rp_promotion_url_generate_response,omitempty" xml:"rp_promotion_url_generate_response,omitempty"` 65 | } 66 | 67 | // RpPromUrlGenerateResult . 68 | type RpPromUrlGenerateResult struct { 69 | // URLList url_list 70 | URLList []PromURL `json:"url_list,omitempty" xml:"url_list,omitempty"` 71 | // ResourceList resource_list 72 | ResourceList []PromResource `json:"resource_list,omitempty" xml:"resource_list,omitempty"` 73 | } 74 | 75 | // RpPromUrlGenerate 生成营销工具推广链接 76 | func RpPromUrlGenerate(ctx context.Context, clt *core.SDKClient, req *RpPromUrlGenerateRequest) (*RpPromUrlGenerateResult, error) { 77 | var resp RpPromUrlGenerateResponse 78 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 79 | return nil, err 80 | } 81 | return resp.Response, nil 82 | } 83 | -------------------------------------------------------------------------------- /api/ddk/statistic_data_query.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | type PeroidType int 11 | 12 | const ( 13 | PeroidType_7DAY PeroidType = 1 14 | PeroidType_MONTH PeroidType = 2 15 | ) 16 | 17 | // StatisticDataQueryRequest 多多进宝数据统计查询接口 API Request 18 | type StatisticDataQueryRequest struct { 19 | // Page 分页数,默认值: 1 20 | Page int `json:"page,omitempty"` 21 | // PageSize 每页结果数,默认值: 20 22 | PageSize int `json:"page_size,omitempty"` 23 | // PeriodType 周期类型: 1-每7天,2-自然月 24 | PeroidType PeroidType `json:"peroid_type,omitempty"` 25 | // StatisticType 数据类型: 1-增量补贴数据 26 | StatisticType int `json:"statistic_type,omitempty"` 27 | // Time 查询时间点,格式: "yyyy-MM-dd"。period_type为1时,查询时间点前7天的数据;period_type为2时,查询时间点所在自然月的数据。 28 | Time string `json:"time,omitempty"` 29 | } 30 | 31 | // GetType implement Request interface 32 | func (r StatisticDataQueryRequest) GetType() string { 33 | return "pdd.ddk.statistics.data.query" 34 | } 35 | 36 | // StatisticDataQueryResponse 多多进宝数据统计查询接口 API Resposne 37 | type StatisticDataQueryResponse struct { 38 | model.CommonResponse 39 | Response struct { 40 | List []StatisticData `json:"data_list,omitempty" xml:"data_list,omitempty"` 41 | } `json:"statistics_data_response" xml:"statistics_data_response"` 42 | } 43 | 44 | // StatisticData . 45 | type StatisticData struct { 46 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。(如果使用GET请求,请使用URLEncode处理参数) 47 | CustomParameters string `json:"custom_parameters,omitempty" xml:"custom_parameters,omitempty"` 48 | // EndTime 结束时间,格式: "yyyy-MM-dd" 49 | EndTime string `json:"end_time,omitempty" xml:"end_time,omitempty"` 50 | // StartTime 开始时间,格式: "yyyy-MM-dd" 51 | StartTime string `json:"start_time,omitempty" xml:"start_time,omitempty"` 52 | // OrderAmount GMV,单位为分 53 | OrderAmount int64 `json:"order_amount,omitempty" xml:"order_amount,omitempty"` 54 | // OrderNum 订单数 55 | OrderNum int64 `json:"order_num,omitempty" xml:"order_num,omitempty"` 56 | // Pid 推广位ID 57 | Pid string `json:"p_id,omitempty" xml:"p_id,omitempty"` 58 | } 59 | 60 | // StatisticDataQuery 多多进宝数据统计查询接口 61 | func StatisticDataQuery(ctx context.Context, clt *core.SDKClient, req *StatisticDataQueryRequest) ([]StatisticData, error) { 62 | var resp StatisticDataQueryResponse 63 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 64 | return nil, err 65 | } 66 | return resp.Response.List, nil 67 | } 68 | -------------------------------------------------------------------------------- /api/ddk/url.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | // PromURL 链接 4 | type PromURL struct { 5 | // MobileShortURL 推广短链接(可唤起拼多多app) 6 | MobileShortURL string `json:"mobile_short_url,omitempty" xml:"mobile_short_url,omitempty"` 7 | // MobileURL 推广长链接(唤起拼多多app) 8 | MobileURL string `json:"mobile_url,omitempty" xml:"mobile_url,omitempty"` 9 | // MultiGroupMobileShortURL 推广短链接(唤起拼多多app) 10 | MultiGroupMobileShortURL string `json:"multi_group_mobile_short_url,omitempty" xml:"multi_group_mobile_short_url,omitempty"` 11 | // MultiGroupMobileURL 推广长链接(可唤起拼多多app) 12 | MultiGroupMobileURL string `json:"multi_group_mobile_url,omitempty" xml:"multi_group_mobile_url,omitempty"` 13 | // MultiGroupShortURL 双人团推广短链接 14 | MultiGroupShortURL string `json:"multi_group_short_url,omitempty" xml:"multi_group_short_url,omitempty"` 15 | // MultiGroupURL 双人团推广长链接 16 | MultiGroupURL string `json:"multi_group_url,omitempty" xml:"multi_group_url,omitempty"` 17 | // ShortURL 对应出参url的短链接,与url功能一致。 18 | ShortURL string `json:"short_url,omitempty" xml:"short_url,omitempty"` 19 | // URL 普通推广长链接,唤起H5页面 20 | URL string `json:"url,omitempty" xml:"url,omitempty"` 21 | // WeAppWebViewURL 22 | WeAppWebViewURL string `json:"we_app_web_view_url,omitempty" xml:"we_app_web_view_url,omitempty"` 23 | // WeAppWebViewShortURL 24 | WeAppWebViewShortURL string `json:"we_app_web_view_short_url,omitempty" xml:"we_app_web_view_short_url,omitempty"` 25 | // Sign CPSsign 26 | Sign string `json:"sign,omitempty" xml:"sign,omitempty"` 27 | // SchemaURL schema链接,用户安装拼多多APP的情况下会唤起APP(需客户端支持schema跳转协议) 28 | SchemaURL string `json:"schema_url,omitempty" xml:"schema_url,omitempty"` 29 | // TzSchemaURL 使用此推广链接,用户安装多多团长APP的情况下会唤起APP(需客户端支持schema跳转协议) 30 | TzSchemaURL string `json:"tz_schema_url,omitempty" xml:"tz_shceme_url,omitempty"` 31 | // WeixinCode 微信小程序码 32 | WeixinCode string `json:"weixin_code,omitempty" xml:"weixin_code,omitempty"` 33 | // WeixinShortLink 微信ShortLink链接 34 | WeixinShortLink string `json:"weixin_short_link,omitempty" xml:"weixin_short_link,omitempty"` 35 | // MultiURLLIst 双人团链接列表 36 | MultiURLList *PromURL `json:"multi_url_list,omitempty" xml:"multi_url_list,omitempty"` 37 | // SingleURLList 单人团链接列表 38 | SingleURLList *PromURL `json:"single_url_list,omitempty" xml:"single_url_list,omitempty"` 39 | // WeAppInfo 拼多多福利券微信小程序信息 40 | WeAppInfo *WeAppInfo `json:"we_app_info,omitempty" xml:"we_app_info,omitempty"` 41 | // QQAppInfo qq小程序信息 42 | QQAppInfo *WeAppInfo `json:"qq_app_info,omitempty" xml:"qq_app_info,omitempty"` 43 | } 44 | 45 | // WeAppInfo 拼多多福利券微信小程序信息 46 | type WeAppInfo struct { 47 | // AppID 小程序id 48 | AppID string `json:"app_id,omitempty" xml:"app_id,omitempty"` 49 | // WeAppIconURL 小程序图片 50 | WeAppIconURl string `json:"we_app_icon_url,omitempty" xml:"we_app_icon_url,omitempty"` 51 | // BannerURL Banner图 52 | BannerURL string `json:"banner_url,omitempty" xml:"banner_url,omitempty"` 53 | // Desc 描述 54 | Desc string `json:"desc,omitempty" xml:"desc,omitempty"` 55 | // SourceDisplayName 来源名 56 | SourceDisplayName string `json:"source_display_name,omitempty" xml:"source_display_name,omitempty"` 57 | // PagePath 小程序path值 58 | PagePath string `json:"page_path,omitempty" xml:"page_path,omitempty"` 59 | // UserName 用户名 60 | UserName string `json:"user_name,omitempty" xml:"user_name,omitempty"` 61 | // Title 小程序标题 62 | Title string `json:"title,omitempty" xml:"title,omitempty"` 63 | } 64 | -------------------------------------------------------------------------------- /api/ddk/weapp_qrcode_url_gen.go: -------------------------------------------------------------------------------- 1 | package ddk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // WeappQrcodeUrlGenRequest 多多客生成单品推广小程序二维码url API Request 11 | type WeappQrcodeUrlGenRequest struct { 12 | // CashGiftID 多多礼金ID 13 | CashGiftID uint64 `json:"cash_gift_id,omitempty"` 14 | // CustomParameters 自定义参数,为链接打上自定义标签;自定义参数最长限制64个字节;格式为: {"uid":"11111","sid":"22222"} ,其中 uid 用户唯一标识,可自行加密后传入,每个用户仅且对应一个标识,必填; sid 上下文信息标识,例如sessionId等,非必填。该json字符串中也可以加入其他自定义的key。(如果使用GET请求,请使用URLEncode处理参数) 15 | CustomParameters interface{} `json:"custom_parameters,omitempty"` 16 | // GenerateMallCollectCoupon 是否生成店铺收藏券推广链接 17 | GenerateMallCollectCoupon bool `json:"generate_mall_collect_coupon,omitempty"` 18 | // GoodsSignList 商品goodsSign列表,例如:["c9r2omogKFFAc7WBwvbZU1ikIb16_J3CTa8HNN"],支持批量生链。goodsSign是加密后的goodsId, goodsId已下线,请使用goodsSign来替代。使用说明:https://jinbao.pinduoduo.com/qa-system?questionId=252 19 | GoodsSignList []string `json:"goods_sign_list,omitempty"` 20 | // Pid 推广位ID 21 | Pid string `json:"p_id,omitempty"` 22 | // ZsDouID 招商多多客ID 23 | ZsDouID uint64 `json:"zs_dou_id,omitempty"` 24 | } 25 | 26 | // GetType implement Request interface 27 | func (r WeappQrcodeUrlGenRequest) GetType() string { 28 | return "pdd.ddk.weapp.qrcode.url.gen" 29 | } 30 | 31 | // WeappQrcodeUrlGenResponse 多多客生成单品推广小程序二维码url API Response 32 | type WeappQrcodeUrlGenResponse struct { 33 | model.CommonResponse 34 | Response struct { 35 | // URL 单品推广小程序二维码url 36 | URL string `json:"url" xml:"url"` 37 | } `json:"weapp_qrcode_generate_response" xml:"weapp_qrcode_generate_response"` 38 | } 39 | 40 | // WeappQrcodeUrlGen 多多客生成单品推广小程序二维码url 41 | func WeappQrcodeUrlGen(ctx context.Context, clt *core.SDKClient, req *WeappQrcodeUrlGenRequest) (string, error) { 42 | var resp WeappQrcodeUrlGenResponse 43 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 44 | return "", err 45 | } 46 | return resp.Response.URL, nil 47 | } 48 | -------------------------------------------------------------------------------- /api/doc.go: -------------------------------------------------------------------------------- 1 | // Package api . 2 | package api 3 | -------------------------------------------------------------------------------- /api/goods/cat.go: -------------------------------------------------------------------------------- 1 | package goods 2 | 3 | // Cat 类目 4 | type Cat struct { 5 | // ID 商品类目ID 6 | ID uint64 `json:"cat_id,omitempty"` 7 | // Name 商品类目名称 8 | Name string `json:"cat_name,omitempty"` 9 | // Level 类目层级,1-一级类目,2-二级类目,3-三级类目,4-四级类目 10 | Level int `json:"level,omitempty"` 11 | // ParentID id所属父类目ID,其中,parent_id=0时为顶级节点 12 | ParentID uint64 `json:"parent_cat_id,omitempty"` 13 | } 14 | -------------------------------------------------------------------------------- /api/goods/cats_get.go: -------------------------------------------------------------------------------- 1 | package goods 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // CatsGetRequest 商品标准类目接口 API Request 11 | type CatsGetRequest struct { 12 | // ParentCatID 值=0时为顶点cat_id,通过树顶级节点获取cat树 13 | ParentCatID uint64 `json:"parent_cat_id"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r CatsGetRequest) GetType() string { 18 | return "pdd.goods.cats.get" 19 | } 20 | 21 | // CatsGetResponse 商品标准类目接口 API Response 22 | type CatsGetResponse struct { 23 | model.CommonResponse 24 | Response struct { 25 | List []Cat `json:"goods_cats_list,omitempty"` 26 | } `json:"goods_cats_get_response"` 27 | } 28 | 29 | // CatsGet 商品标准类目接口 30 | func CatsGet(ctx context.Context, clt *core.SDKClient, parentID uint64) ([]Cat, error) { 31 | req := CatsGetRequest{ 32 | ParentCatID: parentID, 33 | } 34 | var resp CatsGetResponse 35 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 36 | return nil, err 37 | } 38 | return resp.Response.List, nil 39 | } 40 | -------------------------------------------------------------------------------- /api/goods/doc.go: -------------------------------------------------------------------------------- 1 | // Package goods 商品API 2 | package goods 3 | -------------------------------------------------------------------------------- /api/goods/opt.go: -------------------------------------------------------------------------------- 1 | package goods 2 | 3 | // Opt 商品标签 4 | type Opt struct { 5 | // ID 商品标签ID 6 | ID uint64 `json:"opt_id,omitempty"` 7 | // Name 商品标签名 8 | Name string `json:"opt_name,omitempty"` 9 | // ParentID id所属父ID,其中,parent_id=0时为顶级节点 10 | ParentID uint64 `json:"parent_opt_id,omitempty"` 11 | // Level 层级,1-一级,2-二级,3-三级,4-四级 12 | Level int `json:"level,omitempty"` 13 | } 14 | -------------------------------------------------------------------------------- /api/goods/opt_get.go: -------------------------------------------------------------------------------- 1 | package goods 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // OptGetRequest 查询商品标签列表 API Request 11 | type OptGetRequest struct { 12 | // ParentCatID 值=0时为顶点cat_id,通过树顶级节点获取cat树 13 | ParentCatID uint64 `json:"parent_cat_id"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r OptGetRequest) GetType() string { 18 | return "pdd.goods.opt.get" 19 | } 20 | 21 | // OptGetResponse 查询商品标签列表 API Response 22 | type OptGetResponse struct { 23 | model.CommonResponse 24 | Response struct { 25 | List []Opt `json:"goods_opt_list,omitempty"` 26 | } `json:"goods_opt_get_response"` 27 | } 28 | 29 | // OptGet 查询商品标签列表 30 | func OptGet(ctx context.Context, clt *core.SDKClient, parentID uint64) ([]Opt, error) { 31 | req := OptGetRequest{ 32 | ParentCatID: parentID, 33 | } 34 | var resp OptGetResponse 35 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 36 | return nil, err 37 | } 38 | return resp.Response.List, nil 39 | } 40 | -------------------------------------------------------------------------------- /api/order/order.go: -------------------------------------------------------------------------------- 1 | package pdd 2 | 3 | type Order struct { 4 | Address string `json:"address"` 5 | AddressMask string `json:"address_mask"` 6 | AfterSalesStatus int `json:"after_sales_status"` 7 | BondedWarehouse string `json:"bonded_warehouse"` 8 | BuyerMemo string `json:"buyer_memo"` 9 | CapitalFreeDiscount float64 `json:"capital_free_discount"` 10 | CardInfoList []CardInfo `json:"card_info_list"` 11 | CatId1 int64 `json:"cat_id_1"` 12 | CatId2 int64 `json:"cat_id_2"` 13 | CatId3 int64 `json:"cat_id_3"` 14 | CatId4 int64 `json:"cat_id_4"` 15 | City string `json:"city"` 16 | CityId int `json:"city_id"` 17 | ConfirmStatus int `json:"confirm_status"` 18 | ConfirmTime string `json:"confirm_time"` 19 | ConsolidateInfo ConsolidateInfo `json:"consolidate_info"` 20 | Country string `json:"country"` 21 | CountryId int `json:"country_id"` 22 | CreatedTime string `json:"created_time"` 23 | DeliveryHomeValue float64 `json:"delivery_home_value"` 24 | DeliveryInstallValue float64 `json:"delivery_install_value"` 25 | DeliveryOneDay int `json:"delivery_one_day"` 26 | DiscountAmount float64 `json:"discount_amount"` 27 | DuoduoWholesale int `json:"duoduo_wholesale"` 28 | ExtraDeliveryList []ExtraDelivery `json:"extra_delivery_list"` 29 | FreeSf int `json:"free_sf"` 30 | GiftDeliveryList []GiftDelivery `json:"gift_delivery_list"` 31 | GiftList []GiftInfo `json:"gift_list"` 32 | GoodsAmount float64 `json:"goods_amount"` 33 | GroupOrderId int64 `json:"group_order_id"` 34 | GroupRole int `json:"group_role"` 35 | GroupStatus int `json:"group_status"` 36 | HomeDeliveryType int `json:"home_delivery_type"` 37 | HomeInstallValue float64 `json:"home_install_value"` 38 | InnerTransactionId string `json:"inner_transaction_id加密"` 39 | InvoiceStatus int `json:"invoice_status"` 40 | IsLuckyFlag int `json:"is_lucky_flag"` 41 | IsPreSale int `json:"is_pre_sale"` 42 | IsStockOut int `json:"is_stock_out"` 43 | ItemList []ItemInfo `json:"item_list"` 44 | LastShipTime string `json:"last_ship_time"` 45 | LogisticsId int64 `json:"logistics_id"` 46 | MktBizType int `json:"mkt_biz_type"` 47 | OnlySupportReplace int `json:"only_support_replace"` 48 | OrderChangeAmount float64 `json:"order_change_amount"` 49 | OrderDepotInfo DepotInfo `json:"order_depot_info"` 50 | OrderSn string `json:"order_sn"` 51 | OrderStatus int `json:"order_status"` 52 | OrderTagList []OrderTag `json:"order_tag_list"` 53 | PayAmount float64 `json:"pay_amount"` 54 | PayNo string `json:"pay_no加密"` 55 | PayTime string `json:"pay_time"` 56 | PayType string `json:"pay_type"` 57 | PlatformDiscount float64 `json:"platform_discount"` 58 | Postage float64 `json:"postage"` 59 | PreSaleTime string `json:"pre_sale_time"` 60 | PromiseDeliveryTime string `json:"promise_delivery_time"` 61 | Province string `json:"province"` 62 | ProvinceId int `json:"province_id"` 63 | ReceiveTime string `json:"receive_time"` 64 | ReceiverAddress string `json:"receiver_address"` 65 | ReceiverAddressMask string `json:"receiver_address_mask"` 66 | ReceiverName string `json:"receiver_name"` 67 | ReceiverNameMask string `json:"receiver_name_mask"` 68 | ReceiverPhone string `json:"receiver_phone"` 69 | ReceiverPhoneMask string `json:"receiver_phone_mask"` 70 | RefundStatus int `json:"refund_status"` 71 | Remark string `json:"remark"` 72 | RemarkTag int `json:"remark_tag"` 73 | RemarkTagName string `json:"remark_tag_name"` 74 | ResendDeliveryList []ResendDelivery `json:"resend_delivery_list"` 75 | ReturnFreightPayer int `json:"return_freight_payer"` 76 | RiskControlStatus int `json:"risk_control_status"` 77 | SelfContained int `json:"self_contained"` 78 | SellerDiscount float64 `json:"seller_discount"` 79 | ServiceFeeDetail []ServiceFeeDetail `json:"service_fee_detail"` 80 | ShipAdditionalLinkOrder string `json:"ship_additional_link_order"` 81 | ShipAdditionalOriginOrder string `json:"ship_additional_origin_order"` 82 | ShippingTime string `json:"shipping_time"` 83 | ShippingType int `json:"shipping_type"` 84 | StepOrderInfo StepOrderInfo `json:"step_order_info"` 85 | StockOutHandleStatus int `json:"stock_out_handle_status"` 86 | StoreInfo StoreInfo `json:"store_info"` 87 | SupportNationwideWarranty int `json:"support_nationwide_warranty"` 88 | Town string `json:"town"` 89 | TownId int `json:"town_id"` 90 | TrackingNumber string `json:"tracking_number"` 91 | TradeType int `json:"trade_type"` 92 | UpdatedAt string `json:"updated_at"` 93 | UrgeShippingTime string `json:"urge_shipping_time"` 94 | YypsDate string `json:"yyps_date"` 95 | YypsTime string `json:"yyps_time"` 96 | } 97 | 98 | type CardInfo struct { 99 | CardNo string `json:"card_no"` 100 | MaskPassword string `json:"mask_password"` 101 | } 102 | 103 | type ConsolidateInfo struct { 104 | ConsolidateType int `json:"consolidate_type,omitempty"` 105 | } 106 | 107 | type ExtraDelivery struct { 108 | LogisticsId int `json:"logistics_id"` 109 | TrackingNumber string `json:"tracking_number"` 110 | } 111 | 112 | type GiftDelivery struct { 113 | LogisticsId int `json:"logistics_id"` 114 | TrackingNumber string `json:"tracking_number"` 115 | } 116 | 117 | type GiftInfo struct { 118 | GoodsCount int `json:"goods_count"` 119 | GoodsId int64 `json:"goods_id"` 120 | GoodsImg string `json:"goods_img"` 121 | GoodsName string `json:"goods_name"` 122 | GoodsPrice float64 `json:"goods_price"` 123 | GoodsSpec string `json:"goods_spec"` 124 | OuterGoodsId string `json:"outer_goods_id"` 125 | OuterId string `json:"outer_id"` 126 | SkuId int64 `json:"sku_id"` 127 | } 128 | 129 | type ItemInfo struct { 130 | GoodsCount int `json:"goods_count"` 131 | GoodsId int64 `json:"goods_id"` 132 | GoodsImg string `json:"goods_img"` 133 | GoodsName string `json:"goods_name"` 134 | GoodsPrice float64 `json:"goods_price"` 135 | GoodsSpec string `json:"goods_spec"` 136 | OuterGoodsId string `json:"outer_goods_id"` 137 | OuterId string `json:"outer_id"` 138 | SkuId int64 `json:"sku_id"` 139 | } 140 | 141 | type DepotInfo struct { 142 | DepotCode string `json:"depot_code"` 143 | DepotId string `json:"depot_id"` 144 | DepotName string `json:"depot_name"` 145 | DepotType int `json:"depot_type"` 146 | WareId string `json:"ware_id"` 147 | WareName string `json:"ware_name"` 148 | WareSn string `json:"ware_sn"` 149 | WareSubInfoList []WareSubInfo `json:"ware_sub_info_list"` 150 | } 151 | 152 | type WareSubInfo struct { 153 | WareId int64 `json:"ware_id"` 154 | WareName string `json:"ware_name"` 155 | WareQuantity int64 `json:"ware_quantity"` 156 | WareSn string `json:"ware_sn"` 157 | WareType int `json:"ware_type"` 158 | } 159 | 160 | type OrderTag struct { 161 | Name string `json:"name"` 162 | Value int `json:"value"` 163 | } 164 | 165 | type ResendDelivery struct { 166 | LogisticsId int `json:"logistics_id"` 167 | TrackingNumber string `json:"tracking_number"` 168 | } 169 | 170 | type ServiceFeeDetail struct { 171 | ServiceFee float64 `json:"service_fee"` 172 | ServiceName string `json:"service_name"` 173 | } 174 | 175 | type StepOrderInfo struct { 176 | AdvancedPaidFee float64 `json:"advanced_paid_fee"` 177 | StepDiscountAmount float64 `json:"step_discount_amount"` 178 | StepPaidFee float64 `json:"step_paid_fee"` 179 | StepTradeStatus int `json:"step_trade_status"` 180 | } 181 | 182 | type StoreInfo struct { 183 | StoreId int64 `json:"store_id"` 184 | StoreName string `json:"store_name"` 185 | StoreNumber string `json:"store_number"` 186 | } 187 | -------------------------------------------------------------------------------- /api/order/order_information_get.go: -------------------------------------------------------------------------------- 1 | package pdd 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // OrderDetailGetRequest 查询订单详情 API Request 11 | type OrderDetailGetRequest struct { 12 | // OrderSN 订单号 13 | OrderSN string `json:"order_sn,omitempty"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r OrderDetailGetRequest) GetType() string { 18 | return "pdd.order.information.get" 19 | } 20 | 21 | // OrderInformationGetResponse 查询订单详情 API Response 22 | type OrderInformationGetResponse struct { 23 | model.CommonResponse 24 | Response *OrderInfoGetResponse `json:"order_info_get_response,omitempty" xml:"order_info_get_response,omitempty"` 25 | } 26 | 27 | type OrderInfoGetResponse struct { 28 | OrderInfo *Order `json:"order_info,omitempty" xml:"order_info,omitempty"` 29 | } 30 | 31 | // OrderDetailGet 查询订单详情 32 | func OrderDetailGet(ctx context.Context, clt *core.SDKClient, req *OrderDetailGetRequest) (*Order, error) { 33 | var resp OrderInformationGetResponse 34 | if err := clt.Do(ctx, req, &resp, ""); err != nil { 35 | return nil, err 36 | } 37 | return resp.Response.OrderInfo, nil 38 | } 39 | -------------------------------------------------------------------------------- /api/pmc/accrue_query.go: -------------------------------------------------------------------------------- 1 | package pmc 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // AccrueQueryRequest 消息队列积压数量查询 API Request 11 | type AccrueQueryRequest struct{} 12 | 13 | // GetType implement Request interface 14 | func (r AccrueQueryRequest) GetType() string { 15 | return "pdd.pmc.accure.query" 16 | } 17 | 18 | // AccrueQueryResponse 消息队列积压数量查询 API Response 19 | type AccrueQueryResponse struct { 20 | model.CommonResponse 21 | Response struct { 22 | // Number 消息积压数量 23 | Number int64 `json:"number,omitempty"` 24 | } `json:"pmc_user_get_response"` 25 | } 26 | 27 | // AccrueQuery 消息队列积压数量查询 28 | func AccureQuery(ctx context.Context, clt *core.SDKClient) (int64, error) { 29 | var resp AccrueQueryResponse 30 | if err := clt.Do(ctx, &AccrueQueryRequest{}, &resp, ""); err != nil { 31 | return 0, err 32 | } 33 | return resp.Response.Number, nil 34 | } 35 | -------------------------------------------------------------------------------- /api/pmc/doc.go: -------------------------------------------------------------------------------- 1 | // Package pmc 消息服务 API 2 | package pmc 3 | -------------------------------------------------------------------------------- /api/pmc/message.go: -------------------------------------------------------------------------------- 1 | package pmc 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | type CommandType string 10 | 11 | const ( 12 | CommandType_COMMON CommandType = "Common" 13 | CommandType_FAIL CommandType = "Fail" 14 | CommandType_HEARTBEAT CommandType = "HeartBeat" 15 | CommandType_ACK CommandType = "Ack" 16 | ) 17 | 18 | type EventType string 19 | 20 | const ( 21 | EventType_TradeModifiedEvent EventType = "pdd_ddjb_TradeModified" 22 | ) 23 | 24 | // Command pmc record 25 | type Command struct { 26 | ID uint64 `json:"id,omitempty"` 27 | CommandType CommandType `json:"commandType,omitempty"` 28 | Time int64 `json:"time,omitempty"` 29 | SendTime int64 `json:"sendTime,omitempty"` 30 | Message Message `json:"message,omitempty"` 31 | } 32 | 33 | func (c Command) IsError() bool { 34 | return c.CommandType == CommandType_FAIL 35 | } 36 | 37 | func (c Command) Error() string { 38 | return c.Message.Content 39 | } 40 | 41 | func (c Command) Event() (Event, error) { 42 | if c.IsError() { 43 | return nil, c 44 | } 45 | switch c.Message.Type { 46 | case EventType_TradeModifiedEvent: 47 | var ev TradeModifiedEvent 48 | if err := json.Unmarshal([]byte(c.Message.Content), &ev); err != nil { 49 | fmt.Println(c.Message.Content) 50 | return nil, err 51 | } 52 | return &ev, nil 53 | } 54 | return nil, errors.New("unknown event") 55 | } 56 | 57 | type Message struct { 58 | // Type event type 59 | Type EventType `json:"type,omitempty"` 60 | // Content 61 | Content string `json:"content,omitempty"` 62 | // MallID 商户ID 63 | MallID int64 `json:"mallID,omitempty"` 64 | } 65 | 66 | // Event pmc event 67 | type Event interface { 68 | Type() EventType 69 | } 70 | 71 | // TradeModifiedEvent 多多进宝订单状态变更 72 | type TradeModifiedEvent struct { 73 | // Tid 订单号 74 | Tid string `json:"tid,omitempty"` 75 | // Status 订单状态:0-已支付;1-已成团;2-确认收货;3-审核成功;4-审核失败(不可提现);5-已经结算 ;10-已处罚 76 | Status int `json:"status,omitempty"` 77 | // Pid 推广位ID 78 | Pid string `json:"pid,omitempty"` 79 | // CustomParameters 代理身份自定义参数 80 | CustomParameters string `json:"custom_parameters,omitempty"` 81 | // ModifyTime 最后更新时间 82 | ModifyTime int64 `json:"modify_time,omitempty"` 83 | } 84 | 85 | // Type implement Event interface 86 | func (m TradeModifiedEvent) Type() EventType { 87 | return EventType_TradeModifiedEvent 88 | } 89 | 90 | type AckMessage struct { 91 | ID uint64 `json:"id"` 92 | CommandType CommandType `json:"commandType"` 93 | Time int64 `json:"time"` 94 | SendTime int64 `json:"sendTime"` 95 | Type EventType `json:"type"` 96 | MallID int64 `json:"mallID"` 97 | } 98 | -------------------------------------------------------------------------------- /api/pmc/reader.go: -------------------------------------------------------------------------------- 1 | package pmc 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "net" 8 | "time" 9 | 10 | "github.com/bububa/openpdd/core" 11 | "github.com/gobwas/ws" 12 | "github.com/gobwas/ws/wsutil" 13 | ) 14 | 15 | // Read read pmc message 16 | func Read(ctx context.Context, clt *core.SDKClient) <-chan struct { 17 | Command 18 | Conn net.Conn 19 | } { 20 | ch := make(chan struct { 21 | Command 22 | Conn net.Conn 23 | }) 24 | go func() { 25 | defer close(ch) 26 | conn, err := reconnect(ctx, clt) 27 | if err != nil { 28 | return 29 | } 30 | for { 31 | select { 32 | case <-ctx.Done(): 33 | return 34 | default: 35 | if payload, err := wsutil.ReadServerText(conn); err != nil { 36 | time.Sleep(1 * time.Second) 37 | if conn, err = reconnect(ctx, clt); err != nil { 38 | return 39 | } 40 | } else { 41 | var cmd Command 42 | if err := json.Unmarshal(payload, &cmd); err == nil { 43 | ch <- struct { 44 | Command 45 | Conn net.Conn 46 | }{Command: cmd, Conn: conn} 47 | } 48 | } 49 | } 50 | } 51 | }() 52 | return ch 53 | } 54 | 55 | func reconnect(ctx context.Context, clt *core.SDKClient) (net.Conn, error) { 56 | conn, _, _, err := ws.Dial(ctx, clt.WSSUrl()) 57 | if err != nil { 58 | fmt.Println(err) 59 | } 60 | return conn, err 61 | } 62 | -------------------------------------------------------------------------------- /api/pmc/user_cancel.go: -------------------------------------------------------------------------------- 1 | package pmc 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // UserCancelRequest 取消用户的消息服务 API Request 11 | type UserCancelRequest struct { 12 | // OwnerID 用户唯一id 13 | OwnerID string `json:"owner_id,omitempty"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r UserCancelRequest) GetType() string { 18 | return "pdd.pmc.user.cancel" 19 | } 20 | 21 | // UserCancelResponse 取消用户的消息服务 API Response 22 | type UserCancelResponse struct { 23 | model.CommonResponse 24 | Response struct { 25 | // IsSuccess 是否成功 26 | IsSuccess bool `json:"is_success,omitempty"` 27 | } `json:"tmc_user_cancel_response"` 28 | } 29 | 30 | // UserCancel 取消用户的消息服务 31 | func UserCancel(ctx context.Context, clt *core.SDKClient, ownerID string) (bool, error) { 32 | req := UserCancelRequest{ 33 | OwnerID: ownerID, 34 | } 35 | var resp UserCancelResponse 36 | if err := clt.Do(ctx, &req, &resp, ""); err != nil { 37 | return false, err 38 | } 39 | return resp.Response.IsSuccess, nil 40 | } 41 | -------------------------------------------------------------------------------- /api/pmc/user_get.go: -------------------------------------------------------------------------------- 1 | package pmc 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/bububa/openpdd/core" 7 | "github.com/bububa/openpdd/model" 8 | ) 9 | 10 | // UserGetRequest 获取用户已开通消息 API Request 11 | type UserGetRequest struct { 12 | // OwnerID 用户唯一id 13 | OwnerID string `json:"owner_id,omitempty"` 14 | } 15 | 16 | // GetType implement Request interface 17 | func (r UserGetRequest) GetType() string { 18 | return "pdd.pmc.user.get" 19 | } 20 | 21 | // UserGetResponse 获取用户已开通消息 API Response 22 | type UserGetResponse struct { 23 | model.CommonResponse 24 | Response struct { 25 | // User 开通的用户数据 26 | User *User `json:"pmc_user,omitempty"` 27 | } `json:"pmc_user_get_response"` 28 | } 29 | 30 | // User 开通的用户数据 31 | type User struct { 32 | // Created 用户首次开通时间 33 | Created string `json:"created,omitempty"` 34 | // Modified 用户最后开通时间 35 | Modified string `json:"modified,omitempty"` 36 | // IsExpire 用户授权是否有效,0表示授权有效,1表示授权过期 37 | IsExpire int `json:"is_expire,omitempty"` 38 | // OwnerID 用户ID 39 | OwnerID string `json:"owner_id,omitempty"` 40 | // Topics 用户开通的消息类型列表。如果为空表示应用开通的所有类型 41 | Topics []string `json:"topics,omitempty"` 42 | } 43 | 44 | // UserGet 获取用户已开通消息 45 | func UserGet(ctx context.Context, clt *core.SDKClient, ownerID string) (*User, error) { 46 | req := UserGetRequest{ 47 | OwnerID: ownerID, 48 | } 49 | var resp UserGetResponse 50 | if err := clt.Do(ctx, &req, &resp, ""); err != nil { 51 | return nil, err 52 | } 53 | return resp.Response.User, nil 54 | } 55 | -------------------------------------------------------------------------------- /api/pmc/user_permit.go: -------------------------------------------------------------------------------- 1 | package pmc 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | 7 | "github.com/bububa/openpdd/core" 8 | "github.com/bububa/openpdd/model" 9 | ) 10 | 11 | // UserPermitRequest 为已授权的用户开通消息服务 API Request 12 | type UserPermitRequest struct { 13 | // Topics 消息主题列表,用半角逗号分隔。当用户订阅的topic是应用订阅的子集时才需要设置,不设置表示继承应用所订阅的所有topic,一般情况建议不要设置。 14 | Topics string `json:"topics,omitempty"` 15 | } 16 | 17 | // GetType implement Request interface 18 | func (r UserPermitRequest) GetType() string { 19 | return "pdd.pmc.user.permit" 20 | } 21 | 22 | // UserPermitResponse 为已授权的用户开通消息服务 API Response 23 | type UserPermitResponse struct { 24 | model.CommonResponse 25 | Response struct { 26 | // IsSuccess 是否成功 27 | IsSuccess bool `json:"is_success,omitempty"` 28 | } `json:"pmc_user_permit_response"` 29 | } 30 | 31 | // UserPermit 为已授权的用户开通消息服务 32 | func UserPermit(ctx context.Context, clt *core.SDKClient, topics []string, accessToken string) (bool, error) { 33 | var req UserPermitRequest 34 | if len(topics) > 0 { 35 | req.Topics = strings.Join(topics, ",") 36 | } 37 | var resp UserPermitResponse 38 | if err := clt.Do(ctx, &req, &resp, accessToken); err != nil { 39 | return false, err 40 | } 41 | return resp.Response.IsSuccess, nil 42 | } 43 | -------------------------------------------------------------------------------- /core/client.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/base64" 7 | "encoding/json" 8 | "io" 9 | "mime/multipart" 10 | "net/http" 11 | "net/url" 12 | "sort" 13 | "strconv" 14 | "strings" 15 | "sync" 16 | "time" 17 | 18 | "github.com/bububa/openpdd/core/internal/debug" 19 | "github.com/bububa/openpdd/model" 20 | "github.com/bububa/openpdd/util" 21 | "github.com/bububa/openpdd/util/query" 22 | ) 23 | 24 | var ( 25 | onceInit sync.Once 26 | httpClient *http.Client 27 | ) 28 | 29 | func defaultHttpClient() *http.Client { 30 | onceInit.Do(func() { 31 | transport := http.DefaultTransport.(*http.Transport).Clone() 32 | transport.MaxIdleConns = 100 33 | transport.MaxConnsPerHost = 100 34 | transport.MaxIdleConnsPerHost = 100 35 | httpClient = &http.Client{ 36 | Transport: transport, 37 | Timeout: time.Second * 60, 38 | } 39 | }) 40 | return httpClient 41 | } 42 | 43 | // SDKClient sdk client 44 | type SDKClient struct { 45 | client *http.Client 46 | tracer *Otel 47 | clientID string 48 | secret string 49 | dataType model.RequestDataType 50 | gw string 51 | uploadGw string 52 | debug bool 53 | } 54 | 55 | // NewSDKClient 创建SDKClient 56 | func NewSDKClient(clientID string, secret string) *SDKClient { 57 | return &SDKClient{ 58 | clientID: clientID, 59 | secret: secret, 60 | gw: GATEWAY, 61 | uploadGw: UPLOAD_GATEWAY, 62 | dataType: model.RequestDataType_JSON, 63 | client: defaultHttpClient(), 64 | } 65 | } 66 | 67 | // SetDebug 设置debug模式 68 | func (c *SDKClient) SetDebug(debug bool) { 69 | c.debug = debug 70 | } 71 | 72 | // SetHttpClient 设置http.Client 73 | func (c *SDKClient) SetHttpClient(client *http.Client) { 74 | c.client = client 75 | } 76 | 77 | // SetGateway 设置gateway 78 | func (c *SDKClient) SetGateway(gw string) { 79 | c.gw = gw 80 | } 81 | 82 | func (c *SDKClient) SetUploadGateway(gw string) { 83 | c.uploadGw = gw 84 | } 85 | 86 | // SetDataType 设置返回数据格式 87 | func (c *SDKClient) SetDataType(t model.RequestDataType) { 88 | c.dataType = t 89 | } 90 | 91 | func (c *SDKClient) WithTracer(namespace string) { 92 | c.tracer = NewOtel(namespace, c.ClientID()) 93 | } 94 | 95 | // ClientID returns client client_id 96 | func (c *SDKClient) ClientID() string { 97 | return c.clientID 98 | } 99 | 100 | func (c *SDKClient) Do(ctx context.Context, req model.Request, resp model.Response, accessToken string) error { 101 | values, err := query.Values(req) 102 | if err != nil { 103 | return err 104 | } 105 | values.Set("type", req.GetType()) 106 | values.Set("client_id", c.clientID) 107 | values.Set("timestamp", strconv.FormatInt(time.Now().Unix(), 10)) 108 | values.Set("data_type", string(c.dataType)) 109 | if accessToken != "" { 110 | values.Set("access_token", accessToken) 111 | } 112 | values.Set("sign", c.sign(values)) 113 | return c.get(ctx, req.GetType(), values, resp) 114 | } 115 | 116 | func (c *SDKClient) Upload(ctx context.Context, req model.UploadRequest, resp model.Response, accessToken string) error { 117 | fields := req.Encode() 118 | fields = append(fields, []model.UploadField{ 119 | { 120 | Key: "type", 121 | Value: req.GetType(), 122 | }, { 123 | Key: "client_id", 124 | Value: c.clientID, 125 | }, { 126 | Key: "timestamp", 127 | Value: strconv.FormatInt(time.Now().Unix(), 10), 128 | }, { 129 | Key: "date_type", 130 | Value: string(c.dataType), 131 | }, 132 | }...) 133 | if accessToken != "" { 134 | fields = append(fields, model.UploadField{ 135 | Key: "access_token", 136 | Value: accessToken, 137 | }) 138 | } 139 | fields = append(fields, model.UploadField{ 140 | Key: "sign", 141 | Value: c.signUploadFields(fields), 142 | }) 143 | return c.upload(ctx, req.GetType(), fields, resp) 144 | } 145 | 146 | func (c *SDKClient) post(ctx context.Context, methodName string, req url.Values, resp model.Response) error { 147 | payload := req.Encode() 148 | var builder strings.Builder 149 | builder.WriteString(c.gw) 150 | if req != nil { 151 | builder.WriteString("?") 152 | builder.WriteString(payload) 153 | } 154 | reqUrl := builder.String() 155 | debug.PrintGetRequest(reqUrl, c.debug) 156 | httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.gw, strings.NewReader(payload)) 157 | if err != nil { 158 | return err 159 | } 160 | httpReq.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") 161 | return c.WithSpan(ctx, methodName, httpReq, resp, []byte(payload), c.fetch) 162 | } 163 | 164 | func (c *SDKClient) get(ctx context.Context, methodName string, req url.Values, resp model.Response) error { 165 | var builder strings.Builder 166 | builder.WriteString(c.gw) 167 | if req != nil { 168 | builder.WriteString("?") 169 | builder.WriteString(req.Encode()) 170 | } 171 | reqUrl := builder.String() 172 | debug.PrintGetRequest(reqUrl, c.debug) 173 | httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) 174 | if err != nil { 175 | return err 176 | } 177 | return c.WithSpan(ctx, methodName, httpReq, resp, nil, c.fetch) 178 | } 179 | 180 | func (c *SDKClient) upload(ctx context.Context, methodName string, req []model.UploadField, resp model.Response) error { 181 | var buf bytes.Buffer 182 | var builder strings.Builder 183 | mw := multipart.NewWriter(&buf) 184 | mp := make(map[string]string, len(req)) 185 | for _, f := range req { 186 | var ( 187 | fw io.Writer 188 | r io.Reader 189 | err error 190 | ) 191 | if f.Reader != nil { 192 | if fw, err = mw.CreateFormFile(f.Key, f.Value); err != nil { 193 | return err 194 | } 195 | r = f.Reader 196 | builder.WriteString("@") 197 | if f.Value == "" { 198 | f.Value = "/tmp" 199 | } 200 | builder.WriteString(f.Value) 201 | mp[f.Key] = builder.String() 202 | builder.Reset() 203 | } else { 204 | if fw, err = mw.CreateFormField(f.Key); err != nil { 205 | return err 206 | } 207 | r = strings.NewReader(f.Value) 208 | mp[f.Key] = f.Value 209 | } 210 | if _, err = io.Copy(fw, r); err != nil { 211 | return err 212 | } 213 | } 214 | mw.Close() 215 | debug.PrintPostMultipartRequest(c.uploadGw, mp, c.debug) 216 | httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.uploadGw, &buf) 217 | if err != nil { 218 | return err 219 | } 220 | httpReq.Header.Add("Content-Type", mw.FormDataContentType()) 221 | bs, _ := json.Marshal(mp) 222 | return c.WithSpan(ctx, methodName, httpReq, resp, bs, c.fetch) 223 | } 224 | 225 | // fetch execute http request 226 | func (c *SDKClient) fetch(httpReq *http.Request, resp model.Response) (*http.Response, error) { 227 | httpResp, err := http.DefaultClient.Do(httpReq) 228 | if err != nil { 229 | return httpResp, err 230 | } 231 | defer httpResp.Body.Close() 232 | if resp == nil { 233 | resp = &model.CommonResponse{} 234 | } 235 | err = debug.DecodeJSONHttpResponse(httpResp.Body, resp, c.debug) 236 | if err != nil { 237 | debug.PrintError(err, c.debug) 238 | return httpResp, err 239 | } 240 | if resp.IsError() { 241 | return httpResp, resp.Error() 242 | } 243 | return httpResp, nil 244 | } 245 | 246 | func (c *SDKClient) sign(values url.Values) string { 247 | params := make([]string, 0, len(values)) 248 | var builder strings.Builder 249 | for k := range values { 250 | builder.WriteString(k) 251 | builder.WriteString(values.Get(k)) 252 | params = append(params, builder.String()) 253 | builder.Reset() 254 | } 255 | sort.Strings(params) 256 | builder.WriteString(c.secret) 257 | for _, v := range params { 258 | builder.WriteString(v) 259 | } 260 | builder.WriteString(c.secret) 261 | rawSign := builder.String() 262 | return strings.ToUpper(util.Md5String(rawSign)) 263 | } 264 | 265 | func (c *SDKClient) signUploadFields(fields []model.UploadField) string { 266 | params := make([]string, 0, len(fields)) 267 | var builder strings.Builder 268 | for _, f := range fields { 269 | if f.Reader != nil { 270 | continue 271 | } 272 | builder.WriteString(f.Key) 273 | builder.WriteString(f.Value) 274 | params = append(params, builder.String()) 275 | builder.Reset() 276 | } 277 | sort.Strings(params) 278 | builder.WriteString(c.secret) 279 | for _, v := range params { 280 | builder.WriteString(v) 281 | } 282 | builder.WriteString(c.secret) 283 | rawSign := builder.String() 284 | return strings.ToUpper(util.Md5String(rawSign)) 285 | } 286 | 287 | func (c *SDKClient) WSSUrl() string { 288 | ts := strconv.FormatInt(time.Now().UnixMilli(), 10) 289 | var builder strings.Builder 290 | builder.WriteString(WSSEndPoint) 291 | builder.WriteString("/") 292 | builder.WriteString(c.clientID) 293 | builder.WriteString("/") 294 | builder.WriteString(ts) 295 | builder.WriteString("/") 296 | builder.WriteString(c.signWSS(ts)) 297 | return builder.String() 298 | } 299 | 300 | func (c *SDKClient) signWSS(ts string) string { 301 | var builder strings.Builder 302 | builder.WriteString(c.clientID) 303 | builder.WriteString(ts) 304 | builder.WriteString(c.secret) 305 | return base64.StdEncoding.EncodeToString([]byte(util.Md5String(builder.String()))) 306 | } 307 | 308 | func (c *SDKClient) WithSpan(ctx context.Context, methodName string, req *http.Request, resp model.Response, payload []byte, fn func(*http.Request, model.Response) (*http.Response, error)) error { 309 | if c.tracer == nil { 310 | _, err := fn(req, resp) 311 | return err 312 | } 313 | return c.tracer.WithSpan(ctx, methodName, req, resp, payload, fn) 314 | } 315 | -------------------------------------------------------------------------------- /core/const.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | const ( 4 | // GATEWAY 默认APIgateway 5 | GATEWAY = "https://gw-api.pinduoduo.com/api/router" 6 | // UPLOAD GATEWAY 7 | UPLOAD_GATEWAY = "https://gw-upload.pinduoduo.com/api/upload" 8 | // WSSEndPoint wws endpoint 9 | WSSEndPoint = "wss://message-api.pinduoduo.com/message" 10 | ) 11 | -------------------------------------------------------------------------------- /core/doc.go: -------------------------------------------------------------------------------- 1 | // Package core 包含SDKClient 2 | package core 3 | -------------------------------------------------------------------------------- /core/internal/debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | // PrintError print error with debug 12 | func PrintError(err error, debug bool) { 13 | if !debug { 14 | return 15 | } 16 | log.Println("[DEBUG] [ERROR]", err) 17 | } 18 | 19 | // PrintStringResponse print string response with debug 20 | func PrintStringResponse(str string, debug bool) { 21 | if !debug { 22 | return 23 | } 24 | log.Println("[DEBUG] [RESPONSE]", str) 25 | } 26 | 27 | // PrintGetRequest print get request with debug 28 | func PrintGetRequest(url string, debug bool) { 29 | if !debug { 30 | return 31 | } 32 | log.Println("[DEBUG] [API] GET", url) 33 | } 34 | 35 | // PrintPostJSONRequest print json request with debug 36 | func PrintPostJSONRequest(url string, body []byte, debug bool) { 37 | if !debug { 38 | return 39 | } 40 | const format = "[DEBUG] [API] JSON POST %s\n" + 41 | "http request body:\n%s\n" 42 | 43 | buf := bytes.NewBuffer(make([]byte, 0, len(body)+1024)) 44 | if err := json.Indent(buf, body, "", " "); err == nil { 45 | body = buf.Bytes() 46 | } 47 | log.Printf(format, url, body) 48 | } 49 | 50 | // PrintJSONRequest print json request with debug 51 | func PrintJSONRequest(method string, url string, header http.Header, body []byte, debug bool) { 52 | if !debug { 53 | return 54 | } 55 | 56 | const format = "[DEBUG] [API] JSON %s %s\n" + 57 | "http request header:\n%s\n" + 58 | "http request body:\n%s\n" 59 | 60 | buf := bytes.NewBuffer(make([]byte, 0, len(body)+1024)) 61 | if err := json.Indent(buf, body, "", " "); err == nil { 62 | body = buf.Bytes() 63 | } 64 | 65 | headerJson, _ := json.Marshal(header) 66 | 67 | log.Printf(format, method, url, headerJson, body) 68 | } 69 | 70 | // PrintPostMultipartRequest print multipart/form-data post request with debug 71 | func PrintPostMultipartRequest(url string, mp map[string]string, debug bool) { 72 | if !debug { 73 | return 74 | } 75 | body, _ := json.Marshal(mp) 76 | const format = "[DEBUG] [API] multipart/form-data POST %s\n" + 77 | "http request body:\n%s\n" 78 | 79 | buf := bytes.NewBuffer(make([]byte, 0, len(body)+1024)) 80 | if err := json.Indent(buf, body, "", " "); err == nil { 81 | body = buf.Bytes() 82 | } 83 | log.Printf(format, url, body) 84 | } 85 | 86 | // DecodeJSONHttpResponse decode json response with debug 87 | func DecodeJSONHttpResponse(r io.Reader, v interface{}, debug bool) error { 88 | if !debug { 89 | return json.NewDecoder(r).Decode(v) 90 | } 91 | body, err := io.ReadAll(r) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | body2 := body 97 | buf := bytes.NewBuffer(make([]byte, 0, len(body2)+1024)) 98 | if err := json.Indent(buf, body2, "", " "); err == nil { 99 | body2 = buf.Bytes() 100 | } 101 | log.Printf("[DEBUG] [API] http response body:\n%s\n", body2) 102 | 103 | return json.Unmarshal(body, v) 104 | } 105 | -------------------------------------------------------------------------------- /core/otel.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | "go.opentelemetry.io/otel" 10 | "go.opentelemetry.io/otel/attribute" 11 | "go.opentelemetry.io/otel/codes" 12 | "go.opentelemetry.io/otel/metric" 13 | semconv10 "go.opentelemetry.io/otel/semconv/v1.10.0" 14 | semconv "go.opentelemetry.io/otel/semconv/v1.26.0" 15 | "go.opentelemetry.io/otel/trace" 16 | 17 | "github.com/bububa/openpdd/model" 18 | ) 19 | 20 | const instrumentationName = "github.com/bububa/openpdd" 21 | 22 | type Otel struct { 23 | traceProvider trace.TracerProvider 24 | tracer trace.Tracer 25 | meterProvider metric.MeterProvider 26 | meter metric.Meter 27 | histogram metric.Int64Histogram 28 | counter metric.Int64Counter 29 | attrs []attribute.KeyValue 30 | } 31 | 32 | func NewOtel(namespace string, appKey string) *Otel { 33 | ret := &Otel{ 34 | traceProvider: otel.GetTracerProvider(), 35 | meterProvider: otel.GetMeterProvider(), 36 | attrs: []attribute.KeyValue{ 37 | attribute.String("sdk", "openpdd"), 38 | attribute.String("appkey", appKey), 39 | }, 40 | } 41 | if namespace == "" { 42 | namespace = instrumentationName 43 | } 44 | ret.tracer = ret.traceProvider.Tracer(namespace) 45 | ret.meter = ret.meterProvider.Meter(namespace) 46 | if histogram, err := ret.meter.Int64Histogram( 47 | semconv.HTTPClientRequestDurationName, 48 | metric.WithDescription(semconv.HTTPClientRequestDurationDescription), 49 | metric.WithUnit("milliseconds"), 50 | ); err == nil { 51 | ret.histogram = histogram 52 | } 53 | if counter, err := ret.meter.Int64Counter( 54 | semconv.HTTPClientActiveRequestsName, 55 | metric.WithDescription(semconv.HTTPClientActiveRequestsDescription), 56 | metric.WithUnit(semconv.HTTPClientActiveRequestsUnit), 57 | ); err == nil { 58 | ret.counter = counter 59 | } 60 | return ret 61 | } 62 | 63 | func (o *Otel) WithSpan(ctx context.Context, methodName string, req *http.Request, resp model.Response, payload []byte, fn func(*http.Request, model.Response) (*http.Response, error)) error { 64 | startTime := time.Now() 65 | attrs := append(o.attrs, 66 | semconv10.HTTPURLKey.String(req.URL.String()), 67 | semconv10.HTTPMethodKey.String(req.Method), 68 | semconv10.HTTPTargetKey.String(methodName), 69 | semconv.URLFull(req.URL.String()), 70 | semconv.HTTPRequestMethodKey.String(req.Method), 71 | semconv.URLPath(req.URL.Path), 72 | semconv.URLDomain(req.URL.Host), 73 | semconv.HTTPRequestSizeKey.Int64(req.ContentLength), 74 | ) 75 | if payload != nil { 76 | attrs = append(attrs, attribute.String("payload", string(payload))) 77 | } 78 | 79 | _, span := o.tracer.Start(ctx, fmt.Sprintf("http.%s", req.Method), 80 | trace.WithSpanKind(trace.SpanKindClient), 81 | trace.WithAttributes(attrs...), 82 | ) 83 | defer span.End() 84 | httpResp, err := fn(req, resp) 85 | if o.histogram != nil { 86 | o.histogram.Record(ctx, time.Since(startTime).Milliseconds(), metric.WithAttributes(o.attrs...)) 87 | } 88 | if o.counter != nil { 89 | counterAttrs := append(o.attrs, semconv10.HTTPTargetKey.String(methodName)) 90 | o.counter.Add(ctx, 1, metric.WithAttributes(counterAttrs...)) 91 | } 92 | if !span.IsRecording() { 93 | return err 94 | } 95 | if httpResp != nil { 96 | span.SetAttributes(semconv.HTTPResponseStatusCode(httpResp.StatusCode), semconv.HTTPResponseSizeKey.Int64(httpResp.ContentLength), semconv.NetworkProtocolName(httpResp.Proto)) 97 | } 98 | if err != nil { 99 | span.RecordError(err) 100 | span.SetStatus(codes.Error, err.Error()) 101 | } 102 | return err 103 | } 104 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package openpdd 拼多多 OpenAPI 2 | package openpdd 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bububa/openpdd 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/gobwas/ws v1.3.0 7 | github.com/google/go-cmp v0.6.0 8 | go.opentelemetry.io/otel v1.29.0 9 | go.opentelemetry.io/otel/metric v1.29.0 10 | go.opentelemetry.io/otel/trace v1.29.0 11 | ) 12 | 13 | require ( 14 | github.com/go-logr/logr v1.4.2 // indirect 15 | github.com/go-logr/stdr v1.2.2 // indirect 16 | github.com/gobwas/httphead v0.1.0 // indirect 17 | github.com/gobwas/pool v0.2.1 // indirect 18 | golang.org/x/sys v0.13.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 4 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 5 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 6 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 7 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 8 | github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= 9 | github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= 10 | github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= 11 | github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 12 | github.com/gobwas/ws v1.3.0 h1:sbeU3Y4Qzlb+MOzIe6mQGf7QR4Hkv6ZD0qhGkBFL2O0= 13 | github.com/gobwas/ws v1.3.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= 14 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 15 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 16 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 18 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 19 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 20 | go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= 21 | go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= 22 | go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= 23 | go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= 24 | go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= 25 | go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= 26 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 27 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 28 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 29 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 30 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | -------------------------------------------------------------------------------- /model/doc.go: -------------------------------------------------------------------------------- 1 | // Package model API models 2 | package model 3 | -------------------------------------------------------------------------------- /model/request.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "io" 4 | 5 | // RequestDataType 请求返回的数据格式,可选参数为XML或JSON,默认为JSON 6 | type RequestDataType string 7 | 8 | const ( 9 | // RequestDataType_JSON 返回JSON格式 10 | RequestDataType_JSON RequestDataType = "JSON" 11 | // RequestDataType_XML 返回X M Lg格式 12 | RequestDataType_XML RequestDataType = "XML" 13 | ) 14 | 15 | // Request 请求 16 | type Request interface { 17 | GetType() string 18 | } 19 | 20 | // CommonRequest 请求公共参数 21 | type CommonRequest struct { 22 | // Type API接口名,形如:pdd.* 23 | Type string `json:"type,omitempty"` 24 | // ClientID 已创建成功的应用标志client_id,可在应用详情和中查看 25 | ClientID string `json:"client_id,omitempty"` 26 | // Sign API入参参数签名,签名值根据如下算法给出计算过程 27 | Sign string `json:"sign,omitempty"` 28 | // AccessToken 用户授权令牌access_token,通过pdd.pop.auth.token.create获取 29 | AccessToken string `json:"access_token,omitempty"` 30 | // Version API版本,默认为V1,无要求不传此参数 31 | Version string `json:"version,omitempty"` 32 | // DataType 请求返回的数据格式,可选参数为XML或JSON,默认为JSON 33 | DataType RequestDataType `json:"data_type,omitempty"` 34 | // Timestamp 时间戳,格式为UNIX时间(秒) 35 | Timestamp int64 `json:"timestamp,omitempty"` 36 | } 37 | 38 | // GetType implement Request interface 39 | func (r CommonRequest) GetType() string { 40 | return r.Type 41 | } 42 | 43 | // UploadField multipart/form-data post request field struct 44 | type UploadField struct { 45 | // Reader upload file reader 46 | Reader io.Reader 47 | // Key field key 48 | Key string 49 | // Value field value 50 | Value string 51 | } 52 | 53 | // UploadRequest multipart/form-data reqeust interface 54 | type UploadRequest interface { 55 | // Encode encode request to UploadFields 56 | Encode() []UploadField 57 | GetType() string 58 | } 59 | -------------------------------------------------------------------------------- /model/response.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | // Response 请求返回 9 | type Response interface { 10 | IsError() bool 11 | Error() error 12 | } 13 | 14 | // ErrorResponse Response Error 15 | type ErrorResponse struct { 16 | BaseResponse 17 | ErrorCode int `json:"error_code" xml:"error_code"` 18 | ErrorMsg string `json:"error_msg" xml:"error_msg"` 19 | SubMsg string `json:"sub_msg" xml:"sub_msg"` 20 | SubCode string `json:"sub_code" xml:"sub_code"` 21 | } 22 | 23 | // Error implement Error interface 24 | func (e ErrorResponse) Error() string { 25 | var builder strings.Builder 26 | builder.WriteString("CODE:") 27 | builder.WriteString(strconv.Itoa(e.ErrorCode)) 28 | builder.WriteString(", MSG:") 29 | builder.WriteString(e.ErrorMsg) 30 | if e.SubCode != "" { 31 | builder.WriteString(", SUB_CODE:") 32 | builder.WriteString(e.SubCode) 33 | } 34 | if e.SubMsg != "" { 35 | builder.WriteString(", SUB_MSG:") 36 | builder.WriteString(e.SubMsg) 37 | } 38 | return builder.String() 39 | } 40 | 41 | // CommonResponse API Response 42 | type CommonResponse struct { 43 | ErrorResponse *ErrorResponse `json:"error_response,omitempty" xml:"error_response,omitempty"` 44 | } 45 | 46 | // IsError 判断Response是否Error 47 | func (r CommonResponse) IsError() bool { 48 | return r.ErrorResponse != nil && r.ErrorResponse.ErrorCode != 0 49 | } 50 | 51 | // Error returns response error 52 | func (r CommonResponse) Error() error { 53 | if !r.IsError() { 54 | return nil 55 | } 56 | return r.ErrorResponse 57 | } 58 | 59 | // BaseResponse . 60 | type BaseResponse struct { 61 | // RequestID . 62 | RequestID string `json:"request_id,omitempty" xml:"request_id,omitempty"` 63 | } 64 | -------------------------------------------------------------------------------- /model/types.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "strconv" 4 | 5 | // Uint64 support string quoted number in json 6 | type Uint64 uint64 7 | 8 | // UnmarshalJSON implement json Unmarshal interface 9 | func (u64 *Uint64) UnmarshalJSON(b []byte) (err error) { 10 | if b[0] == '"' && b[len(b)-1] == '"' { 11 | b = b[1 : len(b)-1] 12 | } 13 | i, _ := strconv.ParseUint(string(b), 10, 64) 14 | *u64 = Uint64(i) 15 | return 16 | } 17 | 18 | func (u64 Uint64) Value() uint64 { 19 | return uint64(u64) 20 | } 21 | 22 | // Int support string quoted number in json 23 | type Int int 24 | 25 | // UnmarshalJSON implement json Unmarshal interface 26 | func (i32 *Int) UnmarshalJSON(b []byte) (err error) { 27 | if b[0] == '"' && b[len(b)-1] == '"' { 28 | b = b[1 : len(b)-1] 29 | } 30 | i, _ := strconv.Atoi(string(b)) 31 | *i32 = Int(i) 32 | return 33 | } 34 | 35 | func (i32 Int) Value() int { 36 | return int(i32) 37 | } 38 | -------------------------------------------------------------------------------- /util/crypto.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "io" 7 | ) 8 | 9 | func Md5String(src string) string { 10 | h := md5.New() 11 | io.WriteString(h, src) 12 | return hex.EncodeToString(h.Sum(nil)) 13 | } 14 | -------------------------------------------------------------------------------- /util/doc.go: -------------------------------------------------------------------------------- 1 | // Package util 工具相关 2 | package util 3 | -------------------------------------------------------------------------------- /util/query/doc.go: -------------------------------------------------------------------------------- 1 | // Package query encode struct to url.Values 2 | package query 3 | -------------------------------------------------------------------------------- /util/query/encode.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "reflect" 8 | "strconv" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | var timeType = reflect.TypeOf(time.Time{}) 14 | 15 | // Values returns the url.Values encoding of v. 16 | func Values(v interface{}) (url.Values, error) { 17 | values := make(url.Values) 18 | val := reflect.ValueOf(v) 19 | for val.Kind() == reflect.Ptr { 20 | if val.IsNil() { 21 | return values, nil 22 | } 23 | val = val.Elem() 24 | } 25 | 26 | if v == nil { 27 | return values, nil 28 | } 29 | 30 | if val.Kind() != reflect.Struct { 31 | return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) 32 | } 33 | 34 | err := reflectValue(values, val) 35 | return values, err 36 | } 37 | 38 | // reflectValue populates the values parameter from the struct fields in val. 39 | func reflectValue(values url.Values, val reflect.Value) error { 40 | var embedded []reflect.Value 41 | typ := val.Type() 42 | for i := 0; i < typ.NumField(); i++ { 43 | sf := typ.Field(i) 44 | if sf.PkgPath != "" && !sf.Anonymous { // unexported 45 | continue 46 | } 47 | 48 | sv := val.Field(i) 49 | tag := sf.Tag.Get("json") 50 | if tag == "-" { 51 | continue 52 | } 53 | name, opts := parseTag(tag) 54 | 55 | if name == "" { 56 | if sf.Anonymous { 57 | v := reflect.Indirect(sv) 58 | if v.IsValid() && v.Kind() == reflect.Struct { 59 | // save embedded struct for later processing 60 | embedded = append(embedded, v) 61 | continue 62 | } 63 | } 64 | name = sf.Name 65 | } 66 | 67 | if opts.Contains("omitempty") && isEmptyValue(sv) { 68 | continue 69 | } 70 | 71 | values.Add(name, valueString(sv, opts, sf)) 72 | } 73 | for _, f := range embedded { 74 | if err := reflectValue(values, f); err != nil { 75 | return err 76 | } 77 | } 78 | 79 | return nil 80 | } 81 | 82 | // valueString returns the string representation of a value. 83 | func valueString(v reflect.Value, opts tagOptions, sf reflect.StructField) string { 84 | for v.Kind() == reflect.Ptr { 85 | if v.IsNil() { 86 | return "" 87 | } 88 | v = v.Elem() 89 | } 90 | 91 | if v.Type() == timeType { 92 | t := v.Interface().(time.Time) 93 | if opts.Contains("unix") { 94 | return strconv.FormatInt(t.Unix(), 10) 95 | } 96 | if opts.Contains("unixmilli") { 97 | return strconv.FormatInt((t.UnixNano() / 1e6), 10) 98 | } 99 | if opts.Contains("unixnano") { 100 | return strconv.FormatInt(t.UnixNano(), 10) 101 | } 102 | if layout := sf.Tag.Get("layout"); layout != "" { 103 | return t.Format(layout) 104 | } 105 | return t.Format(time.RFC3339) 106 | } 107 | 108 | if v.Kind() == reflect.Slice || v.Kind() == reflect.Array || v.Kind() == reflect.Map || v.Kind() == reflect.Struct { 109 | if isEmptyValue(v) { 110 | switch v.Kind() { 111 | case reflect.Struct: 112 | return "{}" 113 | default: 114 | return "[]" 115 | } 116 | } 117 | if b, err := json.Marshal(v.Interface()); err != nil { 118 | return "" 119 | } else { 120 | return string(b) 121 | } 122 | } else if v.Kind() == reflect.Interface { 123 | return valueString(reflect.ValueOf(v.Interface()), opts, sf) 124 | } 125 | 126 | return fmt.Sprint(v.Interface()) 127 | } 128 | 129 | // isEmptyValue checks if a value should be considered empty for the purposes 130 | // of omitting fields with the "omitempty" option. 131 | func isEmptyValue(v reflect.Value) bool { 132 | switch v.Kind() { 133 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 134 | return v.Len() == 0 135 | case reflect.Bool: 136 | return !v.Bool() 137 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 138 | return v.Int() == 0 139 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 140 | return v.Uint() == 0 141 | case reflect.Float32, reflect.Float64: 142 | return v.Float() == 0 143 | case reflect.Interface, reflect.Ptr: 144 | return v.IsNil() 145 | } 146 | 147 | type zeroable interface { 148 | IsZero() bool 149 | } 150 | 151 | if z, ok := v.Interface().(zeroable); ok { 152 | return z.IsZero() 153 | } 154 | 155 | return false 156 | } 157 | 158 | // tagOptions is the string following a comma in a struct field's "url" tag, or 159 | // the empty string. It does not include the leading comma. 160 | type tagOptions []string 161 | 162 | // parseTag splits a struct field's url tag into its name and comma-separated 163 | // options. 164 | func parseTag(tag string) (string, tagOptions) { 165 | s := strings.Split(tag, ",") 166 | return s[0], s[1:] 167 | } 168 | 169 | // Contains checks whether the tagOptions contains the specified option. 170 | func (o tagOptions) Contains(option string) bool { 171 | for _, s := range o { 172 | if s == option { 173 | return true 174 | } 175 | } 176 | return false 177 | } 178 | -------------------------------------------------------------------------------- /util/query/encode_test.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | "net/url" 5 | "reflect" 6 | "testing" 7 | "time" 8 | 9 | "github.com/google/go-cmp/cmp" 10 | ) 11 | 12 | // test that Values(input) matches want. If not, report an error on t. 13 | func testValue(t *testing.T, input interface{}, want url.Values) { 14 | v, err := Values(input) 15 | if err != nil { 16 | t.Errorf("Values(%q) returned error: %v", input, err) 17 | } 18 | if diff := cmp.Diff(want, v); diff != "" { 19 | t.Errorf("Values(%#v) mismatch:\n%s", input, diff) 20 | } 21 | } 22 | 23 | func TestValues_BasicTypes(t *testing.T) { 24 | tests := []struct { 25 | input interface{} 26 | want url.Values 27 | }{ 28 | // zero values 29 | {struct{ V string }{}, url.Values{"V": {""}}}, 30 | {struct{ V int }{}, url.Values{"V": {"0"}}}, 31 | {struct{ V uint }{}, url.Values{"V": {"0"}}}, 32 | {struct{ V float32 }{}, url.Values{"V": {"0"}}}, 33 | {struct{ V bool }{}, url.Values{"V": {"false"}}}, 34 | 35 | // simple non-zero values 36 | {struct{ V string }{"v"}, url.Values{"V": {"v"}}}, 37 | {struct{ V int }{1}, url.Values{"V": {"1"}}}, 38 | {struct{ V uint }{1}, url.Values{"V": {"1"}}}, 39 | {struct{ V float32 }{0.1}, url.Values{"V": {"0.1"}}}, 40 | {struct{ V bool }{true}, url.Values{"V": {"true"}}}, 41 | 42 | // time values 43 | { 44 | struct { 45 | V time.Time 46 | }{time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)}, 47 | url.Values{"V": {"2000-01-01T12:34:56Z"}}, 48 | }, 49 | { 50 | struct { 51 | V time.Time `json:",unix"` 52 | }{time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)}, 53 | url.Values{"V": {"946730096"}}, 54 | }, 55 | { 56 | struct { 57 | V time.Time `json:",unixmilli"` 58 | }{time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)}, 59 | url.Values{"V": {"946730096000"}}, 60 | }, 61 | { 62 | struct { 63 | V time.Time `json:",unixnano"` 64 | }{time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)}, 65 | url.Values{"V": {"946730096000000000"}}, 66 | }, 67 | { 68 | struct { 69 | V time.Time `layout:"2006-01-02"` 70 | }{time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)}, 71 | url.Values{"V": {"2000-01-01"}}, 72 | }, 73 | } 74 | 75 | for _, tt := range tests { 76 | testValue(t, tt.input, tt.want) 77 | } 78 | } 79 | 80 | func TestValues_Pointers(t *testing.T) { 81 | str := "s" 82 | strPtr := &str 83 | 84 | tests := []struct { 85 | input interface{} 86 | want url.Values 87 | }{ 88 | // nil pointers (zero values) 89 | {struct{ V *string }{}, url.Values{"V": {""}}}, 90 | {struct{ V *int }{}, url.Values{"V": {""}}}, 91 | 92 | // non-zero pointer values 93 | {struct{ V *string }{&str}, url.Values{"V": {"s"}}}, 94 | {struct{ V **string }{&strPtr}, url.Values{"V": {"s"}}}, 95 | 96 | // slices of pointer values 97 | {struct{ V []*string }{}, url.Values{"V": {"[]"}}}, 98 | {struct{ V []*string }{[]*string{&str, &str}}, url.Values{"V": {`["s","s"]`}}}, 99 | 100 | // pointer to slice 101 | {struct{ V *[]string }{}, url.Values{"V": {""}}}, 102 | {struct{ V *[]string }{&[]string{"a", "b"}}, url.Values{"V": {`["a","b"]`}}}, 103 | 104 | // pointer values for the input struct itself 105 | {(*struct{})(nil), url.Values{}}, 106 | {&struct{}{}, url.Values{}}, 107 | {&struct{ V string }{}, url.Values{"V": {""}}}, 108 | {&struct{ V string }{"v"}, url.Values{"V": {"v"}}}, 109 | } 110 | 111 | for _, tt := range tests { 112 | testValue(t, tt.input, tt.want) 113 | } 114 | } 115 | 116 | func TestValues_Slices(t *testing.T) { 117 | tests := []struct { 118 | input interface{} 119 | want url.Values 120 | }{ 121 | // slices of strings 122 | { 123 | struct{ V []string }{}, 124 | url.Values{"V": {"[]"}}, 125 | }, 126 | { 127 | struct{ V []string }{[]string{"a", "b"}}, 128 | url.Values{"V": {`["a","b"]`}}, 129 | }, 130 | // arrays of strings 131 | { 132 | struct{ V [2]string }{}, 133 | url.Values{"V": {`["",""]`}}, 134 | }, 135 | { 136 | struct{ V [2]string }{[2]string{"a", "b"}}, 137 | url.Values{"V": {`["a","b"]`}}, 138 | }, 139 | } 140 | 141 | for _, tt := range tests { 142 | testValue(t, tt.input, tt.want) 143 | } 144 | } 145 | 146 | func TestValues_NestedTypes(t *testing.T) { 147 | type SubNested struct { 148 | Value string `json:"value"` 149 | } 150 | 151 | type Nested struct { 152 | A SubNested `json:"a"` 153 | B *SubNested `json:"b"` 154 | Ptr *SubNested `json:"ptr,omitempty"` 155 | } 156 | 157 | tests := []struct { 158 | input interface{} 159 | want url.Values 160 | }{ 161 | { 162 | struct { 163 | Nest Nested `json:"nest"` 164 | }{ 165 | Nested{ 166 | A: SubNested{ 167 | Value: "v", 168 | }, 169 | }, 170 | }, 171 | url.Values{ 172 | "nest": {`{"a":{"value":"v"},"b":null}`}, 173 | }, 174 | }, 175 | { 176 | struct { 177 | Nest Nested `json:"nest"` 178 | }{ 179 | Nested{ 180 | Ptr: &SubNested{ 181 | Value: "v", 182 | }, 183 | }, 184 | }, 185 | url.Values{ 186 | "nest": {`{"a":{"value":""},"b":null,"ptr":{"value":"v"}}`}, 187 | }, 188 | }, 189 | { 190 | nil, 191 | url.Values{}, 192 | }, 193 | } 194 | 195 | for _, tt := range tests { 196 | testValue(t, tt.input, tt.want) 197 | } 198 | } 199 | 200 | func TestValues_OmitEmpty(t *testing.T) { 201 | str := "" 202 | 203 | tests := []struct { 204 | input interface{} 205 | want url.Values 206 | }{ 207 | {struct{ v string }{}, url.Values{}}, // non-exported field 208 | { 209 | struct { 210 | V string `json:",omitempty"` 211 | }{}, 212 | url.Values{}, 213 | }, 214 | { 215 | struct { 216 | V string `json:"-"` 217 | }{}, 218 | url.Values{}, 219 | }, 220 | { 221 | struct { 222 | V string `json:"omitempty"` // actually named omitempty 223 | }{}, 224 | url.Values{"omitempty": {""}}, 225 | }, 226 | { 227 | // include value for a non-nil pointer to an empty value 228 | struct { 229 | V *string `json:",omitempty"` 230 | }{&str}, 231 | url.Values{"V": {""}}, 232 | }, 233 | } 234 | 235 | for _, tt := range tests { 236 | testValue(t, tt.input, tt.want) 237 | } 238 | } 239 | 240 | func TestValues_EmbeddedStructs(t *testing.T) { 241 | type Inner struct { 242 | V string 243 | } 244 | type Outer struct { 245 | Inner 246 | } 247 | type OuterPtr struct { 248 | *Inner 249 | } 250 | type Mixed struct { 251 | Inner 252 | V string 253 | } 254 | type unexported struct { 255 | Inner 256 | V string 257 | } 258 | type Exported struct { 259 | unexported 260 | } 261 | 262 | tests := []struct { 263 | input interface{} 264 | want url.Values 265 | }{ 266 | { 267 | Outer{Inner{V: "a"}}, 268 | url.Values{"V": {"a"}}, 269 | }, 270 | { 271 | OuterPtr{&Inner{V: "a"}}, 272 | url.Values{"V": {"a"}}, 273 | }, 274 | { 275 | Mixed{Inner: Inner{V: "a"}, V: "b"}, 276 | url.Values{"V": {"b", "a"}}, 277 | }, 278 | { 279 | // values from unexported embed are still included 280 | Exported{ 281 | unexported{ 282 | Inner: Inner{V: "bar"}, 283 | V: "foo", 284 | }, 285 | }, 286 | url.Values{"V": {"foo", "bar"}}, 287 | }, 288 | } 289 | 290 | for _, tt := range tests { 291 | testValue(t, tt.input, tt.want) 292 | } 293 | } 294 | 295 | func TestValues_InvalidInput(t *testing.T) { 296 | _, err := Values("") 297 | if err == nil { 298 | t.Errorf("expected Values() to return an error on invalid input") 299 | } 300 | } 301 | 302 | func TestIsEmptyValue(t *testing.T) { 303 | str := "string" 304 | tests := []struct { 305 | value interface{} 306 | empty bool 307 | }{ 308 | // slices, arrays, and maps 309 | {[]int{}, true}, 310 | {[]int{0}, false}, 311 | {[0]int{}, true}, 312 | {[3]int{}, false}, 313 | {[3]int{1}, false}, 314 | {map[string]string{}, true}, 315 | {map[string]string{"a": "b"}, false}, 316 | 317 | // strings 318 | {"", true}, 319 | {" ", false}, 320 | {"a", false}, 321 | 322 | // bool 323 | {true, false}, 324 | {false, true}, 325 | 326 | // ints of various types 327 | {(int)(0), true}, {(int)(1), false}, {(int)(-1), false}, 328 | {(int8)(0), true}, {(int8)(1), false}, {(int8)(-1), false}, 329 | {(int16)(0), true}, {(int16)(1), false}, {(int16)(-1), false}, 330 | {(int32)(0), true}, {(int32)(1), false}, {(int32)(-1), false}, 331 | {(int64)(0), true}, {(int64)(1), false}, {(int64)(-1), false}, 332 | {(uint)(0), true}, {(uint)(1), false}, 333 | {(uint8)(0), true}, {(uint8)(1), false}, 334 | {(uint16)(0), true}, {(uint16)(1), false}, 335 | {(uint32)(0), true}, {(uint32)(1), false}, 336 | {(uint64)(0), true}, {(uint64)(1), false}, 337 | 338 | // floats 339 | {(float32)(0), true}, {(float32)(0.0), true}, {(float32)(0.1), false}, 340 | {(float64)(0), true}, {(float64)(0.0), true}, {(float64)(0.1), false}, 341 | 342 | // pointers 343 | {(*int)(nil), true}, 344 | {new([]int), false}, 345 | {&str, false}, 346 | 347 | // time 348 | {time.Time{}, true}, 349 | {time.Now(), false}, 350 | 351 | // unknown type - always false unless a nil pointer, which are always empty. 352 | {(*struct{ int })(nil), true}, 353 | {struct{ int }{}, false}, 354 | {struct{ int }{0}, false}, 355 | {struct{ int }{1}, false}, 356 | } 357 | 358 | for _, tt := range tests { 359 | got := isEmptyValue(reflect.ValueOf(tt.value)) 360 | want := tt.empty 361 | if got != want { 362 | t.Errorf("isEmptyValue(%v) returned %t; want %t", tt.value, got, want) 363 | } 364 | } 365 | } 366 | 367 | func TestParseTag(t *testing.T) { 368 | name, opts := parseTag("field,foobar,foo") 369 | if name != "field" { 370 | t.Fatalf("name = %q, want field", name) 371 | } 372 | for _, tt := range []struct { 373 | opt string 374 | want bool 375 | }{ 376 | {"foobar", true}, 377 | {"foo", true}, 378 | {"bar", false}, 379 | {"field", false}, 380 | } { 381 | if opts.Contains(tt.opt) != tt.want { 382 | t.Errorf("Contains(%q) = %v", tt.opt, !tt.want) 383 | } 384 | } 385 | } 386 | --------------------------------------------------------------------------------