feat: 添加对订阅的显示与支持 #29
|
|
@ -16,6 +16,7 @@
|
||||||
"@midwayjs/swagger": "^3.20.2",
|
"@midwayjs/swagger": "^3.20.2",
|
||||||
"@midwayjs/typeorm": "^3.20.0",
|
"@midwayjs/typeorm": "^3.20.0",
|
||||||
"@midwayjs/validate": "^3.20.2",
|
"@midwayjs/validate": "^3.20.2",
|
||||||
|
"@woocommerce/woocommerce-rest-api": "^1.0.2",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
|
|
|
||||||
146
pnpm-lock.yaml
146
pnpm-lock.yaml
|
|
@ -44,6 +44,9 @@ importers:
|
||||||
'@midwayjs/validate':
|
'@midwayjs/validate':
|
||||||
specifier: ^3.20.2
|
specifier: ^3.20.2
|
||||||
version: 3.20.17
|
version: 3.20.17
|
||||||
|
'@woocommerce/woocommerce-rest-api':
|
||||||
|
specifier: ^1.0.2
|
||||||
|
version: 1.0.2
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.7.9
|
specifier: ^1.7.9
|
||||||
version: 1.13.2
|
version: 1.13.2
|
||||||
|
|
@ -303,6 +306,10 @@ packages:
|
||||||
'@types/supertest@2.0.16':
|
'@types/supertest@2.0.16':
|
||||||
resolution: {integrity: sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==}
|
resolution: {integrity: sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==}
|
||||||
|
|
||||||
|
'@woocommerce/woocommerce-rest-api@1.0.2':
|
||||||
|
resolution: {integrity: sha512-G+0VwM0MINF83KnT7Rg/htm9EEYADWvDPT/UWEJdZ0de1vXvsPrr4M1ksKaxgKHO8qIJViRrIHCtrui2JoVA+Q==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
@ -416,6 +423,10 @@ packages:
|
||||||
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
|
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
cipher-base@1.0.7:
|
||||||
|
resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
class-transformer@0.5.1:
|
class-transformer@0.5.1:
|
||||||
resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==}
|
resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==}
|
||||||
|
|
||||||
|
|
@ -474,6 +485,15 @@ packages:
|
||||||
copy-to@2.0.1:
|
copy-to@2.0.1:
|
||||||
resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==}
|
resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==}
|
||||||
|
|
||||||
|
core-util-is@1.0.3:
|
||||||
|
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||||
|
|
||||||
|
create-hash@1.2.0:
|
||||||
|
resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
|
||||||
|
|
||||||
|
create-hmac@1.1.7:
|
||||||
|
resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==}
|
||||||
|
|
||||||
cron@3.5.0:
|
cron@3.5.0:
|
||||||
resolution: {integrity: sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==}
|
resolution: {integrity: sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==}
|
||||||
|
|
||||||
|
|
@ -692,6 +712,10 @@ packages:
|
||||||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
hash-base@3.1.2:
|
||||||
|
resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
hasown@2.0.2:
|
hasown@2.0.2:
|
||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -769,6 +793,9 @@ packages:
|
||||||
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
|
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
isarray@1.0.0:
|
||||||
|
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
|
||||||
|
|
||||||
isarray@2.0.5:
|
isarray@2.0.5:
|
||||||
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
||||||
|
|
||||||
|
|
@ -858,6 +885,9 @@ packages:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
md5.js@1.3.5:
|
||||||
|
resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
|
||||||
|
|
||||||
media-typer@0.3.0:
|
media-typer@0.3.0:
|
||||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
@ -931,6 +961,9 @@ packages:
|
||||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
oauth-1.0a@2.2.6:
|
||||||
|
resolution: {integrity: sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==}
|
||||||
|
|
||||||
object-inspect@1.13.4:
|
object-inspect@1.13.4:
|
||||||
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
|
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -979,6 +1012,9 @@ packages:
|
||||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
process-nextick-args@2.0.1:
|
||||||
|
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||||
|
|
||||||
proxy-from-env@1.1.0:
|
proxy-from-env@1.1.0:
|
||||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||||
|
|
||||||
|
|
@ -986,6 +1022,9 @@ packages:
|
||||||
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
|
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
|
|
||||||
|
querystringify@2.2.0:
|
||||||
|
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
|
||||||
|
|
||||||
queue-lit@1.5.2:
|
queue-lit@1.5.2:
|
||||||
resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==}
|
resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
@ -997,6 +1036,9 @@ packages:
|
||||||
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
readable-stream@2.3.8:
|
||||||
|
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
|
||||||
|
|
||||||
readdirp@3.6.0:
|
readdirp@3.6.0:
|
||||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
engines: {node: '>=8.10.0'}
|
engines: {node: '>=8.10.0'}
|
||||||
|
|
@ -1008,6 +1050,9 @@ packages:
|
||||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
requires-port@1.0.0:
|
||||||
|
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||||
|
|
||||||
resolve-pkg-maps@1.0.0:
|
resolve-pkg-maps@1.0.0:
|
||||||
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
||||||
|
|
||||||
|
|
@ -1015,9 +1060,16 @@ packages:
|
||||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||||
|
|
||||||
|
ripemd160@2.0.3:
|
||||||
|
resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
run-parallel@1.2.0:
|
run-parallel@1.2.0:
|
||||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||||
|
|
||||||
|
safe-buffer@5.1.2:
|
||||||
|
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
||||||
|
|
||||||
safe-buffer@5.2.1:
|
safe-buffer@5.2.1:
|
||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||||
|
|
||||||
|
|
@ -1124,6 +1176,9 @@ packages:
|
||||||
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
string_decoder@1.1.1:
|
||||||
|
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
|
||||||
|
|
||||||
strip-ansi@6.0.1:
|
strip-ansi@6.0.1:
|
||||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -1249,6 +1304,12 @@ packages:
|
||||||
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
url-parse@1.5.10:
|
||||||
|
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
||||||
|
|
||||||
|
util-deprecate@1.0.2:
|
||||||
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
uuid@11.1.0:
|
uuid@11.1.0:
|
||||||
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
|
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
@ -1570,6 +1631,15 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/superagent': 4.1.14
|
'@types/superagent': 4.1.14
|
||||||
|
|
||||||
|
'@woocommerce/woocommerce-rest-api@1.0.2':
|
||||||
|
dependencies:
|
||||||
|
axios: 1.13.2
|
||||||
|
create-hmac: 1.1.7
|
||||||
|
oauth-1.0a: 2.2.6
|
||||||
|
url-parse: 1.5.10
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- debug
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
@ -1679,6 +1749,12 @@ snapshots:
|
||||||
|
|
||||||
chownr@3.0.0: {}
|
chownr@3.0.0: {}
|
||||||
|
|
||||||
|
cipher-base@1.0.7:
|
||||||
|
dependencies:
|
||||||
|
inherits: 2.0.4
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
to-buffer: 1.2.2
|
||||||
|
|
||||||
class-transformer@0.5.1: {}
|
class-transformer@0.5.1: {}
|
||||||
|
|
||||||
cli-table3@0.6.5:
|
cli-table3@0.6.5:
|
||||||
|
|
@ -1734,6 +1810,25 @@ snapshots:
|
||||||
|
|
||||||
copy-to@2.0.1: {}
|
copy-to@2.0.1: {}
|
||||||
|
|
||||||
|
core-util-is@1.0.3: {}
|
||||||
|
|
||||||
|
create-hash@1.2.0:
|
||||||
|
dependencies:
|
||||||
|
cipher-base: 1.0.7
|
||||||
|
inherits: 2.0.4
|
||||||
|
md5.js: 1.3.5
|
||||||
|
ripemd160: 2.0.3
|
||||||
|
sha.js: 2.4.12
|
||||||
|
|
||||||
|
create-hmac@1.1.7:
|
||||||
|
dependencies:
|
||||||
|
cipher-base: 1.0.7
|
||||||
|
create-hash: 1.2.0
|
||||||
|
inherits: 2.0.4
|
||||||
|
ripemd160: 2.0.3
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
sha.js: 2.4.12
|
||||||
|
|
||||||
cron@3.5.0:
|
cron@3.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/luxon': 3.4.2
|
'@types/luxon': 3.4.2
|
||||||
|
|
@ -1943,6 +2038,13 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols: 1.1.0
|
has-symbols: 1.1.0
|
||||||
|
|
||||||
|
hash-base@3.1.2:
|
||||||
|
dependencies:
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 2.3.8
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
to-buffer: 1.2.2
|
||||||
|
|
||||||
hasown@2.0.2:
|
hasown@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
@ -2021,6 +2123,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
which-typed-array: 1.1.19
|
which-typed-array: 1.1.19
|
||||||
|
|
||||||
|
isarray@1.0.0: {}
|
||||||
|
|
||||||
isarray@2.0.5: {}
|
isarray@2.0.5: {}
|
||||||
|
|
||||||
isexe@2.0.0: {}
|
isexe@2.0.0: {}
|
||||||
|
|
@ -2138,6 +2242,12 @@ snapshots:
|
||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
|
md5.js@1.3.5:
|
||||||
|
dependencies:
|
||||||
|
hash-base: 3.1.2
|
||||||
|
inherits: 2.0.4
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
media-typer@0.3.0: {}
|
media-typer@0.3.0: {}
|
||||||
|
|
||||||
merge2@1.4.1: {}
|
merge2@1.4.1: {}
|
||||||
|
|
@ -2203,6 +2313,8 @@ snapshots:
|
||||||
|
|
||||||
normalize-path@3.0.0: {}
|
normalize-path@3.0.0: {}
|
||||||
|
|
||||||
|
oauth-1.0a@2.2.6: {}
|
||||||
|
|
||||||
object-inspect@1.13.4: {}
|
object-inspect@1.13.4: {}
|
||||||
|
|
||||||
on-finished@2.4.1:
|
on-finished@2.4.1:
|
||||||
|
|
@ -2238,12 +2350,16 @@ snapshots:
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|
||||||
|
process-nextick-args@2.0.1: {}
|
||||||
|
|
||||||
proxy-from-env@1.1.0: {}
|
proxy-from-env@1.1.0: {}
|
||||||
|
|
||||||
qs@6.14.0:
|
qs@6.14.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
side-channel: 1.1.0
|
side-channel: 1.1.0
|
||||||
|
|
||||||
|
querystringify@2.2.0: {}
|
||||||
|
|
||||||
queue-lit@1.5.2: {}
|
queue-lit@1.5.2: {}
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
@ -2255,6 +2371,16 @@ snapshots:
|
||||||
iconv-lite: 0.4.24
|
iconv-lite: 0.4.24
|
||||||
unpipe: 1.0.0
|
unpipe: 1.0.0
|
||||||
|
|
||||||
|
readable-stream@2.3.8:
|
||||||
|
dependencies:
|
||||||
|
core-util-is: 1.0.3
|
||||||
|
inherits: 2.0.4
|
||||||
|
isarray: 1.0.0
|
||||||
|
process-nextick-args: 2.0.1
|
||||||
|
safe-buffer: 5.1.2
|
||||||
|
string_decoder: 1.1.1
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
|
||||||
readdirp@3.6.0:
|
readdirp@3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
@ -2263,14 +2389,23 @@ snapshots:
|
||||||
|
|
||||||
require-directory@2.1.1: {}
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
|
requires-port@1.0.0: {}
|
||||||
|
|
||||||
resolve-pkg-maps@1.0.0: {}
|
resolve-pkg-maps@1.0.0: {}
|
||||||
|
|
||||||
reusify@1.1.0: {}
|
reusify@1.1.0: {}
|
||||||
|
|
||||||
|
ripemd160@2.0.3:
|
||||||
|
dependencies:
|
||||||
|
hash-base: 3.1.2
|
||||||
|
inherits: 2.0.4
|
||||||
|
|
||||||
run-parallel@1.2.0:
|
run-parallel@1.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask: 1.2.3
|
queue-microtask: 1.2.3
|
||||||
|
|
||||||
|
safe-buffer@5.1.2: {}
|
||||||
|
|
||||||
safe-buffer@5.2.1: {}
|
safe-buffer@5.2.1: {}
|
||||||
|
|
||||||
safe-regex-test@1.1.0:
|
safe-regex-test@1.1.0:
|
||||||
|
|
@ -2375,6 +2510,10 @@ snapshots:
|
||||||
emoji-regex: 9.2.2
|
emoji-regex: 9.2.2
|
||||||
strip-ansi: 7.1.2
|
strip-ansi: 7.1.2
|
||||||
|
|
||||||
|
string_decoder@1.1.1:
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: 5.1.2
|
||||||
|
|
||||||
strip-ansi@6.0.1:
|
strip-ansi@6.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex: 5.0.1
|
ansi-regex: 5.0.1
|
||||||
|
|
@ -2483,6 +2622,13 @@ snapshots:
|
||||||
|
|
||||||
unpipe@1.0.0: {}
|
unpipe@1.0.0: {}
|
||||||
|
|
||||||
|
url-parse@1.5.10:
|
||||||
|
dependencies:
|
||||||
|
querystringify: 2.2.0
|
||||||
|
requires-port: 1.0.0
|
||||||
|
|
||||||
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
uuid@11.1.0: {}
|
uuid@11.1.0: {}
|
||||||
|
|
||||||
vary@1.1.2: {}
|
vary@1.1.2: {}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { MidwayConfig } from '@midwayjs/core';
|
import { MidwayConfig } from '@midwayjs/core';
|
||||||
import { Product } from '../entity/product.entty';
|
import { Product } from '../entity/product.entity';
|
||||||
import { Category } from '../entity/category.entity';
|
import { Category } from '../entity/category.entity';
|
||||||
import { WpProduct } from '../entity/wp_product.entity';
|
import { WpProduct } from '../entity/wp_product.entity';
|
||||||
import { Variation } from '../entity/variation.entity';
|
import { Variation } from '../entity/variation.entity';
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import {
|
||||||
CreateOrderNoteDTO,
|
CreateOrderNoteDTO,
|
||||||
QueryOrderDTO,
|
QueryOrderDTO,
|
||||||
QueryOrderSalesDTO,
|
QueryOrderSalesDTO,
|
||||||
|
QueryOrderItemDTO,
|
||||||
} from '../dto/order.dto';
|
} from '../dto/order.dto';
|
||||||
import { User } from '../decorator/user.decorator';
|
import { User } from '../decorator/user.decorator';
|
||||||
import { ErpOrderStatus } from '../enums/base.enum';
|
import { ErpOrderStatus } from '../enums/base.enum';
|
||||||
|
|
@ -97,6 +98,26 @@ export class OrderController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse()
|
||||||
|
@Get('/getOrderItems')
|
||||||
|
async getOrderItems(@Query() param: QueryOrderSalesDTO) {
|
||||||
|
try {
|
||||||
|
return successResponse(await this.orderService.getOrderItems(param));
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error?.message || '获取失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse()
|
||||||
|
@Get('/getOrderItemList')
|
||||||
|
async getOrderItemList(@Query() param: QueryOrderItemDTO) {
|
||||||
|
try {
|
||||||
|
return successResponse(await this.orderService.getOrderItemList(param));
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error?.message || '获取失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
type: OrderDetailRes,
|
type: OrderDetailRes,
|
||||||
})
|
})
|
||||||
|
|
@ -109,6 +130,16 @@ export class OrderController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse()
|
||||||
|
@Get('/:orderId/related')
|
||||||
|
async getRelatedByOrder(@Param('orderId') orderId: number) {
|
||||||
|
try {
|
||||||
|
return successResponse(await this.orderService.getRelatedByOrder(orderId));
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error?.message || '获取失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
type: BooleanRes,
|
type: BooleanRes,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export class SubscriptionController {
|
||||||
@Inject()
|
@Inject()
|
||||||
subscriptionService: SubscriptionService;
|
subscriptionService: SubscriptionService;
|
||||||
|
|
||||||
|
// 同步订阅:根据站点 ID 拉取并更新本地订阅数据
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
@ApiOkResponse({ type: BooleanRes })
|
||||||
@Post('/sync/:siteId')
|
@Post('/sync/:siteId')
|
||||||
async sync(@Param('siteId') siteId: string) {
|
async sync(@Param('siteId') siteId: string) {
|
||||||
|
|
@ -21,6 +22,7 @@ export class SubscriptionController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 订阅列表:分页 + 筛选
|
||||||
@ApiOkResponse({ type: SubscriptionListRes })
|
@ApiOkResponse({ type: SubscriptionListRes })
|
||||||
@Get('/list')
|
@Get('/list')
|
||||||
async list(@Query() query: QuerySubscriptionDTO) {
|
async list(@Query() query: QuerySubscriptionDTO) {
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,10 @@ export class QueryOrderDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
payment_method: string;
|
payment_method: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '仅订阅订单(父订阅订单或包含订阅商品)' })
|
||||||
|
@Rule(RuleType.bool().default(false))
|
||||||
|
isSubscriptionOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryOrderSalesDTO {
|
export class QueryOrderSalesDTO {
|
||||||
|
|
@ -119,11 +123,11 @@ export class QueryOrderSalesDTO {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.date().required())
|
@Rule(RuleType.date())
|
||||||
startDate: Date;
|
startDate: Date;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.date().required())
|
@Rule(RuleType.date())
|
||||||
endDate: Date;
|
endDate: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,3 +145,37 @@ export class CreateOrderNoteDTO {
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class QueryOrderItemDTO {
|
||||||
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
current: number;
|
||||||
|
|
||||||
|
@ApiProperty({ example: '10', description: '每页大小' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
pageSize: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.string().allow(''))
|
||||||
|
siteId: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.string().allow(''))
|
||||||
|
name: string; // 商品名称关键字
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.string().allow(''))
|
||||||
|
externalProductId: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.string().allow(''))
|
||||||
|
externalVariationId: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.date())
|
||||||
|
startDate: Date;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.date())
|
||||||
|
endDate: Date;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { Category } from '../entity/category.entity';
|
import { Category } from '../entity/category.entity';
|
||||||
import { Order } from '../entity/order.entity';
|
import { Order } from '../entity/order.entity';
|
||||||
import { Product } from '../entity/product.entty';
|
import { Product } from '../entity/product.entity';
|
||||||
import { StockPoint } from '../entity/stock_point.entity';
|
import { StockPoint } from '../entity/stock_point.entity';
|
||||||
import { PaginatedWrapper } from '../utils/paginated-response.util';
|
import { PaginatedWrapper } from '../utils/paginated-response.util';
|
||||||
import {
|
import {
|
||||||
|
|
@ -119,7 +119,7 @@ export class PaymentMethodListRes extends SuccessArrayWrapper(
|
||||||
PaymentMethodDTO
|
PaymentMethodDTO
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
// 订阅分页数据
|
// 订阅分页数据(列表 + 总数等分页信息)
|
||||||
export class SubscriptionPaginatedResponse extends PaginatedWrapper(Subscription) {}
|
export class SubscriptionPaginatedResponse extends PaginatedWrapper(Subscription) {}
|
||||||
// 订阅分页返回数据
|
// 订阅分页返回数据(统一成功包装)
|
||||||
export class SubscriptionListRes extends SuccessWrapper(SubscriptionPaginatedResponse) {}
|
export class SubscriptionListRes extends SuccessWrapper(SubscriptionPaginatedResponse) {}
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,34 @@ import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
import { Rule, RuleType } from '@midwayjs/validate';
|
||||||
import { SubscriptionStatus } from '../enums/base.enum';
|
import { SubscriptionStatus } from '../enums/base.enum';
|
||||||
|
|
||||||
|
// 订阅列表查询参数(分页与筛选)
|
||||||
export class QuerySubscriptionDTO {
|
export class QuerySubscriptionDTO {
|
||||||
|
// 当前页码(从 1 开始)
|
||||||
@ApiProperty({ example: 1, description: '页码' })
|
@ApiProperty({ example: 1, description: '页码' })
|
||||||
@Rule(RuleType.number().default(1))
|
@Rule(RuleType.number().default(1))
|
||||||
current: number;
|
current: number;
|
||||||
|
|
||||||
|
// 每页数量
|
||||||
@ApiProperty({ example: 10, description: '每页大小' })
|
@ApiProperty({ example: 10, description: '每页大小' })
|
||||||
@Rule(RuleType.number().default(10))
|
@Rule(RuleType.number().default(10))
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
|
||||||
|
// 站点 ID(可选)
|
||||||
@ApiProperty({ description: '站点ID' })
|
@ApiProperty({ description: '站点ID' })
|
||||||
@Rule(RuleType.string().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
siteId: string;
|
siteId: string;
|
||||||
|
|
||||||
|
// 订阅状态筛选(可选),支持枚举值
|
||||||
@ApiProperty({ description: '订阅状态', enum: SubscriptionStatus })
|
@ApiProperty({ description: '订阅状态', enum: SubscriptionStatus })
|
||||||
@Rule(RuleType.string().valid(...Object.values(SubscriptionStatus)).allow(''))
|
@Rule(RuleType.string().valid(...Object.values(SubscriptionStatus)).allow(''))
|
||||||
status: SubscriptionStatus | '';
|
status: SubscriptionStatus | '';
|
||||||
|
|
||||||
|
// 客户邮箱(模糊匹配,可选)
|
||||||
@ApiProperty({ description: '客户邮箱' })
|
@ApiProperty({ description: '客户邮箱' })
|
||||||
@Rule(RuleType.string().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
customer_email: string;
|
customer_email: string;
|
||||||
|
|
||||||
|
// 关键字(订阅ID、邮箱等,模糊匹配,可选)
|
||||||
@ApiProperty({ description: '关键字(订阅ID、邮箱等)' })
|
@ApiProperty({ description: '关键字(订阅ID、邮箱等)' })
|
||||||
@Rule(RuleType.string().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
keyword: string;
|
keyword: string;
|
||||||
|
|
|
||||||
|
|
@ -76,16 +76,61 @@ export class OrderItem {
|
||||||
@Expose()
|
@Expose()
|
||||||
total_tax: number;
|
total_tax: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ nullable: true })
|
||||||
|
@Expose()
|
||||||
|
tax_class?: string; // 税类(来自 line_items.tax_class)
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ type: 'json', nullable: true })
|
||||||
|
@Expose()
|
||||||
|
taxes?: any[]; // 税明细(来自 line_items.taxes,数组)
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ type: 'json', nullable: true })
|
||||||
|
@Expose()
|
||||||
|
meta_data?: any[]; // 行项目元数据(包含订阅相关键值)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
sku?: string;
|
sku?: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ nullable: true })
|
||||||
|
@Expose()
|
||||||
|
global_unique_id?: string; // 全局唯一ID(部分主题/插件会提供)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column('decimal', { precision: 10, scale: 2 })
|
@Column('decimal', { precision: 10, scale: 2 })
|
||||||
@Expose()
|
@Expose()
|
||||||
price: number;
|
price: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ type: 'json', nullable: true })
|
||||||
|
@Expose()
|
||||||
|
image?: { id?: string | number; src?: string }; // 商品图片(对象,包含 id/src)
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ nullable: true })
|
||||||
|
@Expose()
|
||||||
|
parent_name?: string; // 父商品名称(组合/捆绑时可能使用)
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ nullable: true })
|
||||||
|
@Expose()
|
||||||
|
bundled_by?: string; // 捆绑来源标识(bundled_by)
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ nullable: true })
|
||||||
|
@Expose()
|
||||||
|
bundled_item_title?: string; // 捆绑项标题
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({ type: 'json', nullable: true })
|
||||||
|
@Expose()
|
||||||
|
bundled_items?: any[]; // 捆绑项列表(数组)
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: '2022-12-12 11:11:11',
|
example: '2022-12-12 11:11:11',
|
||||||
description: '创建时间',
|
description: '创建时间',
|
||||||
|
|
|
||||||
|
|
@ -12,96 +12,115 @@ import { SubscriptionStatus } from '../enums/base.enum';
|
||||||
@Entity('subscription')
|
@Entity('subscription')
|
||||||
@Exclude()
|
@Exclude()
|
||||||
export class Subscription {
|
export class Subscription {
|
||||||
|
// 本地主键,自增 ID
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
|
// 站点唯一标识,用于区分不同来源站点
|
||||||
@ApiProperty({ description: '来源站点唯一标识' })
|
@ApiProperty({ description: '来源站点唯一标识' })
|
||||||
@Column()
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: string;
|
siteId: string;
|
||||||
|
|
||||||
|
// WooCommerce 订阅的原始 ID(字符串化),用于幂等更新
|
||||||
@ApiProperty({ description: 'WooCommerce 订阅 ID' })
|
@ApiProperty({ description: 'WooCommerce 订阅 ID' })
|
||||||
@Column()
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
externalSubscriptionId: string;
|
externalSubscriptionId: string;
|
||||||
|
|
||||||
|
// 订阅状态(active/cancelled/on-hold 等)
|
||||||
@ApiProperty({ type: SubscriptionStatus })
|
@ApiProperty({ type: SubscriptionStatus })
|
||||||
@Column({ type: 'enum', enum: SubscriptionStatus })
|
@Column({ type: 'enum', enum: SubscriptionStatus })
|
||||||
@Expose()
|
@Expose()
|
||||||
status: SubscriptionStatus;
|
status: SubscriptionStatus;
|
||||||
|
|
||||||
|
// 货币代码,例如 USD/CAD
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
@Expose()
|
@Expose()
|
||||||
currency: string;
|
currency: string;
|
||||||
|
|
||||||
|
// 总金额,保留两位小数
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column('decimal', { precision: 10, scale: 2, default: 0 })
|
@Column('decimal', { precision: 10, scale: 2, default: 0 })
|
||||||
@Expose()
|
@Expose()
|
||||||
total: number;
|
total: number;
|
||||||
|
|
||||||
|
// 计费周期(day/week/month/year)
|
||||||
@ApiProperty({ description: '计费周期 e.g. day/week/month/year' })
|
@ApiProperty({ description: '计费周期 e.g. day/week/month/year' })
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
@Expose()
|
@Expose()
|
||||||
billing_period: string;
|
billing_period: string;
|
||||||
|
|
||||||
|
// 计费周期间隔(例如 1/3/12)
|
||||||
@ApiProperty({ description: '计费周期间隔 e.g. 1/3/12' })
|
@ApiProperty({ description: '计费周期间隔 e.g. 1/3/12' })
|
||||||
@Column({ type: 'int', default: 0 })
|
@Column({ type: 'int', default: 0 })
|
||||||
@Expose()
|
@Expose()
|
||||||
billing_interval: number;
|
billing_interval: number;
|
||||||
|
|
||||||
|
// 客户 ID(WooCommerce 用户 ID)
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'int', default: 0 })
|
@Column({ type: 'int', default: 0 })
|
||||||
@Expose()
|
@Expose()
|
||||||
customer_id: number;
|
customer_id: number;
|
||||||
|
|
||||||
|
// 客户邮箱(从 billing.email 或 customer_email 提取)
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
@Expose()
|
@Expose()
|
||||||
customer_email: string;
|
customer_email: string;
|
||||||
|
|
||||||
|
// 父订单/订阅 ID(如有)
|
||||||
@ApiProperty({ description: '父订单/父订阅ID(如有)' })
|
@ApiProperty({ description: '父订单/父订阅ID(如有)' })
|
||||||
@Column({ type: 'int', default: 0 })
|
@Column({ type: 'int', default: 0 })
|
||||||
@Expose()
|
@Expose()
|
||||||
parent_id: number;
|
parent_id: number;
|
||||||
|
|
||||||
|
// 订阅开始时间
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
@Column({ type: 'timestamp', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
start_date: Date;
|
start_date: Date;
|
||||||
|
|
||||||
|
// 试用结束时间
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
@Column({ type: 'timestamp', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
trial_end: Date;
|
trial_end: Date;
|
||||||
|
|
||||||
|
// 下次支付时间
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
@Column({ type: 'timestamp', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
next_payment_date: Date;
|
next_payment_date: Date;
|
||||||
|
|
||||||
|
// 订阅结束时间
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
@Column({ type: 'timestamp', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
end_date: Date;
|
end_date: Date;
|
||||||
|
|
||||||
|
// 商品项(订阅行项目)
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
line_items: any[];
|
line_items: any[];
|
||||||
|
|
||||||
|
// 额外元数据(键值对)
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
meta_data: any[];
|
meta_data: any[];
|
||||||
|
|
||||||
|
// 创建时间(数据库自动生成)
|
||||||
@ApiProperty({ example: '2022-12-12 11:11:11', description: '创建时间', required: true })
|
@ApiProperty({ example: '2022-12-12 11:11:11', description: '创建时间', required: true })
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
|
// 更新时间(数据库自动生成)
|
||||||
@ApiProperty({ example: '2022-12-12 11:11:11', description: '更新时间', required: true })
|
@ApiProperty({ example: '2022-12-12 11:11:11', description: '更新时间', required: true })
|
||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { OrderItem } from '../entity/order_item.entity';
|
||||||
import { OrderItemOriginal } from '../entity/order_items_original.entity';
|
import { OrderItemOriginal } from '../entity/order_items_original.entity';
|
||||||
import { OrderSale } from '../entity/order_sale.entity';
|
import { OrderSale } from '../entity/order_sale.entity';
|
||||||
import { WpProduct } from '../entity/wp_product.entity';
|
import { WpProduct } from '../entity/wp_product.entity';
|
||||||
import { Product } from '../entity/product.entty';
|
import { Product } from '../entity/product.entity';
|
||||||
import { OrderFee } from '../entity/order_fee.entity';
|
import { OrderFee } from '../entity/order_fee.entity';
|
||||||
import { OrderRefund } from '../entity/order_refund.entity';
|
import { OrderRefund } from '../entity/order_refund.entity';
|
||||||
import { OrderRefundItem } from '../entity/order_retund_item.entity';
|
import { OrderRefundItem } from '../entity/order_retund_item.entity';
|
||||||
|
|
@ -23,6 +23,7 @@ import {
|
||||||
} from '../enums/base.enum';
|
} from '../enums/base.enum';
|
||||||
import { Variation } from '../entity/variation.entity';
|
import { Variation } from '../entity/variation.entity';
|
||||||
import { CreateOrderNoteDTO, QueryOrderSalesDTO } from '../dto/order.dto';
|
import { CreateOrderNoteDTO, QueryOrderSalesDTO } from '../dto/order.dto';
|
||||||
|
import dayjs = require('dayjs');
|
||||||
import { OrderDetailRes } from '../dto/reponse.dto';
|
import { OrderDetailRes } from '../dto/reponse.dto';
|
||||||
import { OrderNote } from '../entity/order_note.entity';
|
import { OrderNote } from '../entity/order_note.entity';
|
||||||
import { User } from '../entity/user.entity';
|
import { User } from '../entity/user.entity';
|
||||||
|
|
@ -38,7 +39,7 @@ export class OrderService {
|
||||||
sites: WpSite[];
|
sites: WpSite[];
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
wPService: WPService;
|
wpService: WPService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
stockService: StockService;
|
stockService: StockService;
|
||||||
|
|
@ -101,14 +102,14 @@ export class OrderService {
|
||||||
customerModel: Repository<Customer>;
|
customerModel: Repository<Customer>;
|
||||||
|
|
||||||
async syncOrders(siteId: string) {
|
async syncOrders(siteId: string) {
|
||||||
const orders = await this.wPService.getOrders(siteId); // 调用 WooCommerce API 获取订单
|
const orders = await this.wpService.getOrders(siteId); // 调用 WooCommerce API 获取订单
|
||||||
for (const order of orders) {
|
for (const order of orders) {
|
||||||
await this.syncSingleOrder(siteId, order);
|
await this.syncSingleOrder(siteId, order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncOrderById(siteId: string, orderId: string) {
|
async syncOrderById(siteId: string, orderId: string) {
|
||||||
const order = await this.wPService.getOrder(siteId, orderId);
|
const order = await this.wpService.getOrder(siteId, orderId);
|
||||||
await this.syncSingleOrder(siteId, order, true);
|
await this.syncSingleOrder(siteId, order, true);
|
||||||
}
|
}
|
||||||
// 订单状态切换表
|
// 订单状态切换表
|
||||||
|
|
@ -131,7 +132,7 @@ export class OrderService {
|
||||||
throw new Error(`更新订单信息,但失败,原因为 ${siteId} 的站点信息不存在`)
|
throw new Error(`更新订单信息,但失败,原因为 ${siteId} 的站点信息不存在`)
|
||||||
}
|
}
|
||||||
// 同步更新回 wordpress 的 order 状态
|
// 同步更新回 wordpress 的 order 状态
|
||||||
await this.wPService.updateOrder(site, order.id, { status: order.status });
|
await this.wpService.updateOrder(site, order.id, { status: order.status });
|
||||||
order.status = this.orderAutoNextStatusMap[originStatus];
|
order.status = this.orderAutoNextStatusMap[originStatus];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新订单状态失败,原因为:', error)
|
console.error('更新订单状态失败,原因为:', error)
|
||||||
|
|
@ -153,7 +154,6 @@ export class OrderService {
|
||||||
});
|
});
|
||||||
// 更新状态
|
// 更新状态
|
||||||
await this.autoUpdateOrderStatus(siteId, order);
|
await this.autoUpdateOrderStatus(siteId, order);
|
||||||
const orderId = (await this.saveOrder(siteId, orderData)).id;
|
|
||||||
const externalOrderId = order.id;
|
const externalOrderId = order.id;
|
||||||
if (
|
if (
|
||||||
existingOrder &&
|
existingOrder &&
|
||||||
|
|
@ -166,6 +166,8 @@ export class OrderService {
|
||||||
if (existingOrder && !existingOrder.is_editable && !forceUpdate) {
|
if (existingOrder && !existingOrder.is_editable && !forceUpdate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const orderRecord = await this.saveOrder(siteId, orderData);
|
||||||
|
const orderId = orderRecord.id;
|
||||||
await this.saveOrderItems({
|
await this.saveOrderItems({
|
||||||
siteId,
|
siteId,
|
||||||
orderId,
|
orderId,
|
||||||
|
|
@ -309,6 +311,7 @@ export class OrderService {
|
||||||
externalOrderId: string;
|
externalOrderId: string;
|
||||||
orderItems: Record<string, any>[];
|
orderItems: Record<string, any>[];
|
||||||
}) {
|
}) {
|
||||||
|
console.log('saveOrderItems params',params)
|
||||||
const { siteId, orderId, externalOrderId, orderItems } = params;
|
const { siteId, orderId, externalOrderId, orderItems } = params;
|
||||||
const currentOrderItems = await this.orderItemModel.find({
|
const currentOrderItems = await this.orderItemModel.find({
|
||||||
where: { siteId, externalOrderId: externalOrderId },
|
where: { siteId, externalOrderId: externalOrderId },
|
||||||
|
|
@ -442,7 +445,7 @@ export class OrderService {
|
||||||
refunds: Record<string, any>[];
|
refunds: Record<string, any>[];
|
||||||
}) {
|
}) {
|
||||||
for (const item of refunds) {
|
for (const item of refunds) {
|
||||||
const refund = await this.wPService.getOrderRefund(
|
const refund = await this.wpService.getOrderRefund(
|
||||||
siteId,
|
siteId,
|
||||||
externalOrderId,
|
externalOrderId,
|
||||||
item.id
|
item.id
|
||||||
|
|
@ -613,6 +616,7 @@ export class OrderService {
|
||||||
customer_email,
|
customer_email,
|
||||||
payment_method,
|
payment_method,
|
||||||
billing_phone,
|
billing_phone,
|
||||||
|
isSubscriptionOnly = false,
|
||||||
}, userId = undefined) {
|
}, userId = undefined) {
|
||||||
const parameters: any[] = [];
|
const parameters: any[] = [];
|
||||||
|
|
||||||
|
|
@ -639,6 +643,37 @@ export class OrderService {
|
||||||
o.payment_method as payment_method,
|
o.payment_method as payment_method,
|
||||||
cs.order_count as order_count,
|
cs.order_count as order_count,
|
||||||
cs.total_spent as total_spent,
|
cs.total_spent as total_spent,
|
||||||
|
CASE WHEN EXISTS (
|
||||||
|
SELECT 1 FROM subscription s
|
||||||
|
WHERE s.siteId = o.siteId AND s.parent_id = o.externalOrderId
|
||||||
|
) THEN 1 ELSE 0 END AS isSubscription,
|
||||||
|
(
|
||||||
|
SELECT COALESCE(
|
||||||
|
JSON_ARRAYAGG(
|
||||||
|
JSON_OBJECT(
|
||||||
|
'id', s.id,
|
||||||
|
'externalSubscriptionId', s.externalSubscriptionId,
|
||||||
|
'status', s.status,
|
||||||
|
'currency', s.currency,
|
||||||
|
'total', s.total,
|
||||||
|
'billing_period', s.billing_period,
|
||||||
|
'billing_interval', s.billing_interval,
|
||||||
|
'customer_id', s.customer_id,
|
||||||
|
'customer_email', s.customer_email,
|
||||||
|
'parent_id', s.parent_id,
|
||||||
|
'start_date', s.start_date,
|
||||||
|
'trial_end', s.trial_end,
|
||||||
|
'next_payment_date', s.next_payment_date,
|
||||||
|
'end_date', s.end_date,
|
||||||
|
'line_items', s.line_items,
|
||||||
|
'meta_data', s.meta_data
|
||||||
|
)
|
||||||
|
),
|
||||||
|
JSON_ARRAY()
|
||||||
|
)
|
||||||
|
FROM subscription s
|
||||||
|
WHERE s.siteId = o.siteId AND s.parent_id = o.externalOrderId
|
||||||
|
) AS related,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
JSON_ARRAYAGG(
|
JSON_ARRAYAGG(
|
||||||
CASE WHEN s.id IS NOT NULL THEN JSON_OBJECT(
|
CASE WHEN s.id IS NOT NULL THEN JSON_OBJECT(
|
||||||
|
|
@ -698,12 +733,14 @@ export class OrderService {
|
||||||
totalQuery += ` AND o.date_created <= ?`;
|
totalQuery += ` AND o.date_created <= ?`;
|
||||||
parameters.push(endDate);
|
parameters.push(endDate);
|
||||||
}
|
}
|
||||||
|
// 支付方式筛选(使用参数化,避免SQL注入)
|
||||||
if (payment_method) {
|
if (payment_method) {
|
||||||
sqlQuery += ` AND o.payment_method like "%${payment_method}%" `;
|
sqlQuery += ` AND o.payment_method LIKE ?`;
|
||||||
totalQuery += ` AND o.payment_method like "%${payment_method}%" `;
|
totalQuery += ` AND o.payment_method LIKE ?`;
|
||||||
|
parameters.push(`%${payment_method}%`);
|
||||||
}
|
}
|
||||||
const user = await this.userModel.findOneBy({ id: userId });
|
const user = await this.userModel.findOneBy({ id: userId });
|
||||||
if (user?.permissions?.includes('order-10-days')) {
|
if (user?.permissions?.includes('order-10-days') && !startDate && !endDate) {
|
||||||
sqlQuery += ` AND o.date_created >= ?`;
|
sqlQuery += ` AND o.date_created >= ?`;
|
||||||
totalQuery += ` AND o.date_created >= ?`;
|
totalQuery += ` AND o.date_created >= ?`;
|
||||||
const tenDaysAgo = new Date();
|
const tenDaysAgo = new Date();
|
||||||
|
|
@ -728,6 +765,21 @@ export class OrderService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 仅订阅订单过滤:父订阅订单 或 行项目包含订阅相关元数据(兼容 JSON 与字符串存储)
|
||||||
|
if (isSubscriptionOnly) {
|
||||||
|
const subCond = `
|
||||||
|
AND (
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM subscription s
|
||||||
|
WHERE s.siteId = o.siteId AND s.parent_id = o.externalOrderId
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
sqlQuery += subCond;
|
||||||
|
totalQuery += subCond;
|
||||||
|
}
|
||||||
|
|
||||||
if (customer_email) {
|
if (customer_email) {
|
||||||
sqlQuery += ` AND o.customer_email LIKE ?`;
|
sqlQuery += ` AND o.customer_email LIKE ?`;
|
||||||
totalQuery += ` AND o.customer_email LIKE ?`;
|
totalQuery += ` AND o.customer_email LIKE ?`;
|
||||||
|
|
@ -773,7 +825,6 @@ export class OrderService {
|
||||||
|
|
||||||
// 执行查询
|
// 执行查询
|
||||||
const orders = await this.orderModel.query(sqlQuery, parameters);
|
const orders = await this.orderModel.query(sqlQuery, parameters);
|
||||||
|
|
||||||
return { items: orders, total, current, pageSize };
|
return { items: orders, total, current, pageSize };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -785,7 +836,8 @@ export class OrderService {
|
||||||
keyword,
|
keyword,
|
||||||
customer_email,
|
customer_email,
|
||||||
billing_phone,
|
billing_phone,
|
||||||
}) {
|
isSubscriptionOnly = false,
|
||||||
|
}: any) {
|
||||||
const query = this.orderModel
|
const query = this.orderModel
|
||||||
.createQueryBuilder('order')
|
.createQueryBuilder('order')
|
||||||
.select('order.orderStatus', 'status')
|
.select('order.orderStatus', 'status')
|
||||||
|
|
@ -823,11 +875,24 @@ export class OrderService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isSubscriptionOnly) {
|
||||||
|
query.andWhere(`(
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM subscription s
|
||||||
|
WHERE s.siteId = order.siteId AND s.parent_id = order.externalOrderId
|
||||||
|
)
|
||||||
|
)`);
|
||||||
|
}
|
||||||
|
|
||||||
return await query.getRawMany();
|
return await query.getRawMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderSales({ siteId, startDate, endDate, current, pageSize, name, exceptPackage }: QueryOrderSalesDTO) {
|
async getOrderSales({ siteId, startDate, endDate, current, pageSize, name, exceptPackage }: QueryOrderSalesDTO) {
|
||||||
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
||||||
|
const defaultStart = dayjs().subtract(30, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
const defaultEnd = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
startDate = (startDate as any) || defaultStart as any;
|
||||||
|
endDate = (endDate as any) || defaultEnd as any;
|
||||||
const offset = (current - 1) * pageSize;
|
const offset = (current - 1) * pageSize;
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
@ -1031,6 +1096,10 @@ export class OrderService {
|
||||||
name,
|
name,
|
||||||
}: QueryOrderSalesDTO) {
|
}: QueryOrderSalesDTO) {
|
||||||
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
||||||
|
const defaultStart = dayjs().subtract(30, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
const defaultEnd = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
startDate = (startDate as any) || defaultStart as any;
|
||||||
|
endDate = (endDate as any) || defaultEnd as any;
|
||||||
// 分页查询
|
// 分页查询
|
||||||
let sqlQuery = `
|
let sqlQuery = `
|
||||||
WITH product_purchase_counts AS (
|
WITH product_purchase_counts AS (
|
||||||
|
|
@ -1133,6 +1202,64 @@ export class OrderService {
|
||||||
pageSize,
|
pageSize,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getOrderItemList({
|
||||||
|
siteId,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
current,
|
||||||
|
pageSize,
|
||||||
|
name,
|
||||||
|
externalProductId,
|
||||||
|
externalVariationId,
|
||||||
|
}: any) {
|
||||||
|
const params: any[] = [];
|
||||||
|
let sql = `
|
||||||
|
SELECT
|
||||||
|
oi.*,
|
||||||
|
o.id AS orderId,
|
||||||
|
o.externalOrderId AS orderExternalOrderId,
|
||||||
|
o.date_created AS orderDateCreated,
|
||||||
|
o.customer_email AS orderCustomerEmail,
|
||||||
|
o.orderStatus AS orderStatus,
|
||||||
|
o.siteId AS orderSiteId,
|
||||||
|
CASE WHEN
|
||||||
|
JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"is_subscription"')
|
||||||
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_wcs_bought_as_subscription"')
|
||||||
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_wcsatt_scheme"')
|
||||||
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_subscription"')
|
||||||
|
THEN 1 ELSE 0 END AS isSubscriptionItem
|
||||||
|
FROM order_item oi
|
||||||
|
INNER JOIN \`order\` o ON o.id = oi.orderId
|
||||||
|
WHERE 1=1
|
||||||
|
`;
|
||||||
|
let countSql = `
|
||||||
|
SELECT COUNT(*) AS total
|
||||||
|
FROM order_item oi
|
||||||
|
INNER JOIN \`order\` o ON o.id = oi.orderId
|
||||||
|
WHERE 1=1
|
||||||
|
`;
|
||||||
|
const pushFilter = (cond: string, value: any) => {
|
||||||
|
sql += cond; countSql += cond; params.push(value);
|
||||||
|
};
|
||||||
|
if (startDate) pushFilter(' AND o.date_created >= ?', startDate);
|
||||||
|
if (endDate) pushFilter(' AND o.date_created <= ?', endDate);
|
||||||
|
if (siteId) pushFilter(' AND oi.siteId = ?', siteId);
|
||||||
|
if (name) {
|
||||||
|
pushFilter(' AND oi.name LIKE ?', `%${name}%`);
|
||||||
|
}
|
||||||
|
if (externalProductId) pushFilter(' AND oi.externalProductId = ?', externalProductId);
|
||||||
|
if (externalVariationId) pushFilter(' AND oi.externalVariationId = ?', externalVariationId);
|
||||||
|
|
||||||
|
sql += ' ORDER BY o.date_created DESC LIMIT ? OFFSET ?';
|
||||||
|
const listParams = [...params, pageSize, (current - 1) * pageSize];
|
||||||
|
|
||||||
|
const items = await this.orderItemModel.query(sql, listParams);
|
||||||
|
const [countRow] = await this.orderItemModel.query(countSql, params);
|
||||||
|
const total = Number(countRow?.total || 0);
|
||||||
|
|
||||||
|
return { items, total, current, pageSize };
|
||||||
|
}
|
||||||
async getOrderDetail(id: number): Promise<OrderDetailRes> {
|
async getOrderDetail(id: number): Promise<OrderDetailRes> {
|
||||||
const order = await this.orderModel.findOne({ where: { id } });
|
const order = await this.orderModel.findOne({ where: { id } });
|
||||||
const site = this.sites.find(site => site.id === order.siteId);
|
const site = this.sites.find(site => site.id === order.siteId);
|
||||||
|
|
@ -1199,15 +1326,55 @@ export class OrderService {
|
||||||
console.log('create order sale origin error: ', error.message);
|
console.log('create order sale origin error: ', error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关联数据:订阅与相关订单(用于前端关联展示)
|
||||||
|
let relatedList: any[] = [];
|
||||||
|
try {
|
||||||
|
const related = await this.getRelatedByOrder(id);
|
||||||
|
const subs = Array.isArray(related?.subscriptions) ? related.subscriptions : [];
|
||||||
|
const ords = Array.isArray(related?.orders) ? related.orders : [];
|
||||||
|
const seen = new Set<string>();
|
||||||
|
const merge = [...subs, ...ords];
|
||||||
|
for (const it of merge) {
|
||||||
|
const key = it?.externalSubscriptionId
|
||||||
|
? `sub:${it.externalSubscriptionId}`
|
||||||
|
: it?.externalOrderId
|
||||||
|
? `ord:${it.externalOrderId}`
|
||||||
|
: `id:${it?.id}`;
|
||||||
|
if (!seen.has(key)) {
|
||||||
|
seen.add(key);
|
||||||
|
relatedList.push(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 关联查询失败不影响详情返回
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...order,
|
...order,
|
||||||
siteName: site.siteName,
|
siteName: site?.siteName,
|
||||||
email: site.email,
|
email: site?.email,
|
||||||
items,
|
items,
|
||||||
sales,
|
sales,
|
||||||
refundItems,
|
refundItems,
|
||||||
notes,
|
notes,
|
||||||
shipment,
|
shipment,
|
||||||
|
related: relatedList,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRelatedByOrder(orderId: number) {
|
||||||
|
const order = await this.orderModel.findOne({ where: { id: orderId } });
|
||||||
|
if (!order) throw new Error('订单不存在');
|
||||||
|
const siteId = order.siteId;
|
||||||
|
const subSql = `
|
||||||
|
SELECT * FROM subscription s
|
||||||
|
WHERE s.siteId = ? AND s.parent_id = ?
|
||||||
|
`;
|
||||||
|
const subscriptions = await this.orderModel.query(subSql, [siteId, order.externalOrderId]);
|
||||||
|
return {
|
||||||
|
order,
|
||||||
|
subscriptions,
|
||||||
|
orders: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1261,9 +1428,9 @@ export class OrderService {
|
||||||
async cancelOrder(id: number) {
|
async cancelOrder(id: number) {
|
||||||
const order = await this.orderModel.findOne({ where: { id } });
|
const order = await this.orderModel.findOne({ where: { id } });
|
||||||
if (!order) throw new Error(`订单 ${id}不存在`);
|
if (!order) throw new Error(`订单 ${id}不存在`);
|
||||||
const site = this.wPService.geSite(order.siteId);
|
const site = this.wpService.geSite(order.siteId);
|
||||||
if (order.status !== OrderStatus.CANCEL) {
|
if (order.status !== OrderStatus.CANCEL) {
|
||||||
await this.wPService.updateOrder(site, order.externalOrderId, {
|
await this.wpService.updateOrder(site, order.externalOrderId, {
|
||||||
status: OrderStatus.CANCEL,
|
status: OrderStatus.CANCEL,
|
||||||
});
|
});
|
||||||
order.status = OrderStatus.CANCEL;
|
order.status = OrderStatus.CANCEL;
|
||||||
|
|
@ -1275,9 +1442,9 @@ export class OrderService {
|
||||||
async refundOrder(id: number) {
|
async refundOrder(id: number) {
|
||||||
const order = await this.orderModel.findOne({ where: { id } });
|
const order = await this.orderModel.findOne({ where: { id } });
|
||||||
if (!order) throw new Error(`订单 ${id}不存在`);
|
if (!order) throw new Error(`订单 ${id}不存在`);
|
||||||
const site = this.wPService.geSite(order.siteId);
|
const site = this.wpService.geSite(order.siteId);
|
||||||
if (order.status !== OrderStatus.REFUNDED) {
|
if (order.status !== OrderStatus.REFUNDED) {
|
||||||
await this.wPService.updateOrder(site, order.externalOrderId, {
|
await this.wpService.updateOrder(site, order.externalOrderId, {
|
||||||
status: OrderStatus.REFUNDED,
|
status: OrderStatus.REFUNDED,
|
||||||
});
|
});
|
||||||
order.status = OrderStatus.REFUNDED;
|
order.status = OrderStatus.REFUNDED;
|
||||||
|
|
@ -1289,9 +1456,9 @@ export class OrderService {
|
||||||
async completedOrder(id: number) {
|
async completedOrder(id: number) {
|
||||||
const order = await this.orderModel.findOne({ where: { id } });
|
const order = await this.orderModel.findOne({ where: { id } });
|
||||||
if (!order) throw new Error(`订单 ${id}不存在`);
|
if (!order) throw new Error(`订单 ${id}不存在`);
|
||||||
const site = this.wPService.geSite(order.siteId);
|
const site = this.wpService.geSite(order.siteId);
|
||||||
if (order.status !== OrderStatus.COMPLETED) {
|
if (order.status !== OrderStatus.COMPLETED) {
|
||||||
await this.wPService.updateOrder(site, order.externalOrderId, {
|
await this.wpService.updateOrder(site, order.externalOrderId, {
|
||||||
status: OrderStatus.COMPLETED,
|
status: OrderStatus.COMPLETED,
|
||||||
});
|
});
|
||||||
order.status = OrderStatus.COMPLETED;
|
order.status = OrderStatus.COMPLETED;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Provide } from '@midwayjs/core';
|
import { Provide } from '@midwayjs/core';
|
||||||
import { In, Like, Not, Repository } from 'typeorm';
|
import { In, Like, Not, Repository } from 'typeorm';
|
||||||
import { Product } from '../entity/product.entty';
|
import { Product } from '../entity/product.entity';
|
||||||
import { Category } from '../entity/category.entity';
|
import { Category } from '../entity/category.entity';
|
||||||
import { paginate } from '../utils/paginate.util';
|
import { paginate } from '../utils/paginate.util';
|
||||||
import { PaginationParams } from '../interface';
|
import { PaginationParams } from '../interface';
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Between, Like, Repository, LessThan, MoreThan } from 'typeorm';
|
||||||
import { Stock } from '../entity/stock.entity';
|
import { Stock } from '../entity/stock.entity';
|
||||||
import { StockRecord } from '../entity/stock_record.entity';
|
import { StockRecord } from '../entity/stock_record.entity';
|
||||||
import { paginate } from '../utils/paginate.util';
|
import { paginate } from '../utils/paginate.util';
|
||||||
import { Product } from '../entity/product.entty';
|
import { Product } from '../entity/product.entity';
|
||||||
import {
|
import {
|
||||||
CreatePurchaseOrderDTO,
|
CreatePurchaseOrderDTO,
|
||||||
CreateStockPointDTO,
|
CreateStockPointDTO,
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,27 @@ import { QuerySubscriptionDTO } from '../dto/subscription.dto';
|
||||||
@Provide()
|
@Provide()
|
||||||
export class SubscriptionService {
|
export class SubscriptionService {
|
||||||
@Inject()
|
@Inject()
|
||||||
wPService: WPService;
|
wpService: WPService;
|
||||||
|
|
||||||
@InjectEntityModel(Subscription)
|
@InjectEntityModel(Subscription)
|
||||||
subscriptionModel: Repository<Subscription>;
|
subscriptionModel: Repository<Subscription>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步指定站点的订阅列表
|
||||||
|
* - 从 WooCommerce 拉取订阅并逐条入库/更新
|
||||||
|
*/
|
||||||
async syncSubscriptions(siteId: string) {
|
async syncSubscriptions(siteId: string) {
|
||||||
const subs = await this.wPService.getSubscriptions(siteId);
|
const subs = await this.wpService.getSubscriptions(siteId);
|
||||||
for (const sub of subs) {
|
for (const sub of subs) {
|
||||||
await this.syncSingleSubscription(siteId, sub);
|
await this.syncSingleSubscription(siteId, sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步单条订阅
|
||||||
|
* - 规范化字段、设置幂等键 externalSubscriptionId
|
||||||
|
* - 已存在则更新,不存在则新增
|
||||||
|
*/
|
||||||
async syncSingleSubscription(siteId: string, sub: any) {
|
async syncSingleSubscription(siteId: string, sub: any) {
|
||||||
const { line_items, ...raw } = sub;
|
const { line_items, ...raw } = sub;
|
||||||
const entity: Partial<Subscription> = {
|
const entity: Partial<Subscription> = {
|
||||||
|
|
@ -44,6 +53,9 @@ export class SubscriptionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取订阅分页列表(支持站点、状态、邮箱与关键字筛选)
|
||||||
|
*/
|
||||||
async getSubscriptionList({
|
async getSubscriptionList({
|
||||||
current = 1,
|
current = 1,
|
||||||
pageSize = 10,
|
pageSize = 10,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Config, Provide } from '@midwayjs/core';
|
import { Config, Provide } from '@midwayjs/core';
|
||||||
import axios, { AxiosRequestConfig } from 'axios';
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
|
import WooCommerceRestApi, { WooCommerceRestApiVersion } from '@woocommerce/woocommerce-rest-api';
|
||||||
import { WpSite } from '../interface';
|
import { WpSite } from '../interface';
|
||||||
import { WpProduct } from '../entity/wp_product.entity';
|
import { WpProduct } from '../entity/wp_product.entity';
|
||||||
import { Variation } from '../entity/variation.entity';
|
import { Variation } from '../entity/variation.entity';
|
||||||
|
|
@ -11,6 +12,64 @@ export class WPService {
|
||||||
@Config('wpSite')
|
@Config('wpSite')
|
||||||
sites: WpSite[];
|
sites: WpSite[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建 URL,自动规范各段的斜杠,避免出现多 / 或少 / 导致请求失败
|
||||||
|
* 使用示例:this.buildURL(wpApiUrl, '/wp-json', 'wc/v3/products', productId)
|
||||||
|
*/
|
||||||
|
private buildURL(base: string, ...parts: Array<string | number>): string {
|
||||||
|
// 去掉 base 末尾多余斜杠,但不影响协议中的 //
|
||||||
|
const baseSanitized = String(base).replace(/\/+$/g, '');
|
||||||
|
// 规范各段前后斜杠
|
||||||
|
const segments = parts
|
||||||
|
.filter((p) => p !== undefined && p !== null)
|
||||||
|
.map((p) => String(p))
|
||||||
|
.map((s) => s.replace(/^\/+|\/+$/g, ''))
|
||||||
|
.filter(Boolean);
|
||||||
|
const joined = [baseSanitized, ...segments].join('/');
|
||||||
|
// 折叠除协议外的多余斜杠,例如 https://example.com//a///b -> https://example.com/a/b
|
||||||
|
return joined.replace(/([^:])\/{2,}/g, '$1/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 WooCommerce SDK 实例
|
||||||
|
* @param site 站点配置
|
||||||
|
* @param namespace API 命名空间,默认 wc/v3;订阅推荐 wcs/v1
|
||||||
|
*/
|
||||||
|
private createApi(site: WpSite, namespace: WooCommerceRestApiVersion = 'wc/v3') {
|
||||||
|
return new WooCommerceRestApi({
|
||||||
|
url: site.wpApiUrl,
|
||||||
|
consumerKey: site.consumerKey,
|
||||||
|
consumerSecret: site.consumerSecret,
|
||||||
|
// SDK 的版本字段有联合类型限制,这里兼容插件命名空间(例如 wcs/v1)
|
||||||
|
version: namespace,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 SDK 获取单页数据,并返回数据与 totalPages
|
||||||
|
*/
|
||||||
|
private async sdkGetPage<T>(api: any, resource: string, params: Record<string, any> = {}) {
|
||||||
|
const page = params.page ?? 1;
|
||||||
|
const per_page = params.per_page ?? 100;
|
||||||
|
const res = await api.get(resource.replace(/^\/+/, ''), { ...params, page, per_page });
|
||||||
|
const data = res.data as T[];
|
||||||
|
const totalPages = Number(res.headers?.['x-wp-totalpages'] ?? 1);
|
||||||
|
return { items: data, totalPages, page, per_page };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 SDK 聚合分页数据,返回全部数据
|
||||||
|
*/
|
||||||
|
private async sdkGetAll<T>(api: any, resource: string, params: Record<string, any> = {}, maxPages: number = 50): Promise<T[]> {
|
||||||
|
const result: T[] = [];
|
||||||
|
for (let page = 1; page <= maxPages; page++) {
|
||||||
|
const { items, totalPages } = await this.sdkGetPage<T>(api, resource, { ...params, page });
|
||||||
|
result.push(...items);
|
||||||
|
if (page >= totalPages) break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 WordPress 数据
|
* 获取 WordPress 数据
|
||||||
* @param wpApiUrl WordPress REST API 的基础地址
|
* @param wpApiUrl WordPress REST API 的基础地址
|
||||||
|
|
@ -31,7 +90,8 @@ export class WPService {
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const { wpApiUrl, consumerKey, consumerSecret } = site;
|
const { wpApiUrl, consumerKey, consumerSecret } = site;
|
||||||
const url = `${wpApiUrl}/wp-json${endpoint}`;
|
// 构建 URL,规避多/或少/问题
|
||||||
|
const url = this.buildURL(wpApiUrl, '/wp-json', endpoint);
|
||||||
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
|
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
|
||||||
'base64'
|
'base64'
|
||||||
);
|
);
|
||||||
|
|
@ -60,11 +120,13 @@ export class WPService {
|
||||||
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
|
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
|
||||||
'base64'
|
'base64'
|
||||||
);
|
);
|
||||||
|
console.log(`!!!wpApiUrl, consumerKey, consumerSecret, auth`,wpApiUrl, consumerKey, consumerSecret, auth)
|
||||||
let hasMore = true;
|
let hasMore = true;
|
||||||
while (hasMore) {
|
while (hasMore) {
|
||||||
const config: AxiosRequestConfig = {
|
const config: AxiosRequestConfig = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${wpApiUrl}/wp-json${endpoint}`,
|
// 构建 URL,规避多/或少/问题
|
||||||
|
url: this.buildURL(wpApiUrl, '/wp-json', endpoint),
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${auth}`,
|
Authorization: `Basic ${auth}`,
|
||||||
},
|
},
|
||||||
|
|
@ -95,14 +157,13 @@ export class WPService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProducts(site: WpSite): Promise<WpProduct[]> {
|
async getProducts(site: WpSite): Promise<WpProduct[]> {
|
||||||
return await this.fetchPagedData<WpProduct>('/wc/v3/products', site);
|
const api = this.createApi(site, 'wc/v3');
|
||||||
|
return await this.sdkGetAll<WpProduct>(api, 'products');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getVariations(site: WpSite, productId: number): Promise<Variation[]> {
|
async getVariations(site: WpSite, productId: number): Promise<Variation[]> {
|
||||||
return await this.fetchPagedData<Variation>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
`/wc/v3/products/${productId}/variations`,
|
return await this.sdkGetAll<Variation>(api, `products/${productId}/variations`);
|
||||||
site
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getVariation(
|
async getVariation(
|
||||||
|
|
@ -110,10 +171,9 @@ export class WPService {
|
||||||
productId: number,
|
productId: number,
|
||||||
variationId: number
|
variationId: number
|
||||||
): Promise<Variation> {
|
): Promise<Variation> {
|
||||||
return await this.fetchData<Variation>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
`/wc/v3/products/${productId}/variations/${variationId}`,
|
const res = await api.get(`products/${productId}/variations/${variationId}`);
|
||||||
site
|
return res.data as Variation;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrder(
|
async getOrder(
|
||||||
|
|
@ -121,37 +181,27 @@ export class WPService {
|
||||||
orderId: string
|
orderId: string
|
||||||
): Promise<Record<string, any>> {
|
): Promise<Record<string, any>> {
|
||||||
const site = this.geSite(siteId);
|
const site = this.geSite(siteId);
|
||||||
return await this.fetchData<Record<string, any>>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
`/wc/v3/orders/${orderId}`,
|
const res = await api.get(`orders/${orderId}`);
|
||||||
site
|
return res.data as Record<string, any>;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
async getOrders(siteId: string): Promise<Record<string, any>[]> {
|
async getOrders(siteId: string): Promise<Record<string, any>[]> {
|
||||||
const site = this.geSite(siteId);
|
const site = this.geSite(siteId);
|
||||||
return await this.fetchPagedData<Record<string, any>>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
'/wc/v3/orders',
|
return await this.sdkGetAll<Record<string, any>>(api, 'orders');
|
||||||
site
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 WooCommerce Subscriptions
|
* 获取 WooCommerce Subscriptions
|
||||||
* 优先尝试 wc/v1/subscriptions (Subscriptions 插件提供), 如失败则回退 wc/v3/subscriptions(部分版本提供)。
|
* 优先尝试 wc/v1/subscriptions(Subscriptions 插件提供),失败时回退 wc/v3/subscriptions。
|
||||||
|
* 返回所有分页合并后的订阅数组。
|
||||||
*/
|
*/
|
||||||
async getSubscriptions(siteId: string): Promise<Record<string, any>[]> {
|
async getSubscriptions(siteId: string): Promise<Record<string, any>[]> {
|
||||||
const site = this.geSite(siteId);
|
const site = this.geSite(siteId);
|
||||||
try {
|
// 优先使用 Subscriptions 命名空间 wcs/v1,失败回退 wc/v3
|
||||||
return await this.fetchPagedData<Record<string, any>>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
'/wc/v1/subscriptions',
|
return await this.sdkGetAll<Record<string, any>>(api, 'subscriptions');
|
||||||
site
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
// fallback
|
|
||||||
return await this.fetchPagedData<Record<string, any>>(
|
|
||||||
'/wc/v3/subscriptions',
|
|
||||||
site
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderRefund(
|
async getOrderRefund(
|
||||||
|
|
@ -160,10 +210,9 @@ export class WPService {
|
||||||
refundId: number
|
refundId: number
|
||||||
): Promise<Record<string, any>> {
|
): Promise<Record<string, any>> {
|
||||||
const site = this.geSite(siteId);
|
const site = this.geSite(siteId);
|
||||||
return await this.fetchData<Record<string, any>>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
`/wc/v3/orders/${orderId}/refunds/${refundId}`,
|
const res = await api.get(`orders/${orderId}/refunds/${refundId}`);
|
||||||
site
|
return res.data as Record<string, any>;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderRefunds(
|
async getOrderRefunds(
|
||||||
|
|
@ -171,10 +220,8 @@ export class WPService {
|
||||||
orderId: number
|
orderId: number
|
||||||
): Promise<Record<string, any>[]> {
|
): Promise<Record<string, any>[]> {
|
||||||
const site = this.geSite(siteId);
|
const site = this.geSite(siteId);
|
||||||
return await this.fetchPagedData<Record<string, any>>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
`/wc/v3/orders/${orderId}/refunds`,
|
return await this.sdkGetAll<Record<string, any>>(api, `orders/${orderId}/refunds`);
|
||||||
site
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderNote(
|
async getOrderNote(
|
||||||
|
|
@ -183,10 +230,9 @@ export class WPService {
|
||||||
noteId: number
|
noteId: number
|
||||||
): Promise<Record<string, any>> {
|
): Promise<Record<string, any>> {
|
||||||
const site = this.geSite(siteId);
|
const site = this.geSite(siteId);
|
||||||
return await this.fetchData<Record<string, any>>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
`/wc/v3/orders/${orderId}/notes/${noteId}`,
|
const res = await api.get(`orders/${orderId}/notes/${noteId}`);
|
||||||
site
|
return res.data as Record<string, any>;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderNotes(
|
async getOrderNotes(
|
||||||
|
|
@ -194,10 +240,8 @@ export class WPService {
|
||||||
orderId: number
|
orderId: number
|
||||||
): Promise<Record<string, any>[]> {
|
): Promise<Record<string, any>[]> {
|
||||||
const site = this.geSite(siteId);
|
const site = this.geSite(siteId);
|
||||||
return await this.fetchPagedData<Record<string, any>>(
|
const api = this.createApi(site, 'wc/v3');
|
||||||
`/wc/v3/orders/${orderId}/notes`,
|
return await this.sdkGetAll<Record<string, any>>(api, `orders/${orderId}/notes`);
|
||||||
site
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateData<T>(
|
async updateData<T>(
|
||||||
|
|
@ -211,7 +255,8 @@ export class WPService {
|
||||||
);
|
);
|
||||||
const config: AxiosRequestConfig = {
|
const config: AxiosRequestConfig = {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
url: `${wpApiUrl}/wp-json${endpoint}`,
|
// 构建 URL,规避多/或少/问题
|
||||||
|
url: this.buildURL(wpApiUrl, '/wp-json', endpoint),
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${auth}`,
|
Authorization: `Basic ${auth}`,
|
||||||
},
|
},
|
||||||
|
|
@ -309,7 +354,14 @@ export class WPService {
|
||||||
);
|
);
|
||||||
const config: AxiosRequestConfig = {
|
const config: AxiosRequestConfig = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${wpApiUrl}/wp-json/wc-ast/v3/orders/${orderId}/shipment-trackings`,
|
// 构建 URL,规避多/或少/问题
|
||||||
|
url: this.buildURL(
|
||||||
|
wpApiUrl,
|
||||||
|
'/wp-json',
|
||||||
|
'wc-ast/v3/orders',
|
||||||
|
orderId,
|
||||||
|
'shipment-trackings'
|
||||||
|
),
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${auth}`,
|
Authorization: `Basic ${auth}`,
|
||||||
},
|
},
|
||||||
|
|
@ -332,7 +384,15 @@ export class WPService {
|
||||||
// 删除接口: DELETE /wp-json/wc-shipment-tracking/v3/orders/<order_id>/shipment-trackings/<tracking_id>
|
// 删除接口: DELETE /wp-json/wc-shipment-tracking/v3/orders/<order_id>/shipment-trackings/<tracking_id>
|
||||||
const config: AxiosRequestConfig = {
|
const config: AxiosRequestConfig = {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: `${wpApiUrl}/wp-json/wc-ast/v3/orders/${orderId}/shipment-trackings/${trackingId}`,
|
// 构建 URL,规避多/或少/问题
|
||||||
|
url: this.buildURL(
|
||||||
|
wpApiUrl,
|
||||||
|
'/wp-json',
|
||||||
|
'wc-ast/v3/orders',
|
||||||
|
orderId,
|
||||||
|
'shipment-trackings',
|
||||||
|
trackingId
|
||||||
|
),
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${auth}`,
|
Authorization: `Basic ${auth}`,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Product } from './../entity/product.entty';
|
import { Product } from '../entity/product.entity';
|
||||||
import { Config, Inject, Provide } from '@midwayjs/core';
|
import { Config, Inject, Provide } from '@midwayjs/core';
|
||||||
import { WPService } from './wp.service';
|
import { WPService } from './wp.service';
|
||||||
import { WpSite } from '../interface';
|
import { WpSite } from '../interface';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue