UserSubscriptionController.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. # -*- encoding: utf-8 -*-
  2. """
  3. @File : UserSubscriptionController.py
  4. @Time : 2024/5/21 16:31
  5. @Author : stephen
  6. @Email : zhangdongming@asj6.wecom.work
  7. @Software: PyCharm
  8. """
  9. import threading
  10. import time
  11. from datetime import datetime
  12. import requests
  13. from django.http import QueryDict
  14. from django.views import View
  15. from Model.models import Device_User, CountryModel, UserEmailSubscriptions
  16. from Object.ResponseObject import ResponseObject
  17. from Object.TokenObject import TokenObject
  18. from Object.YotpoCoreObject import YotpoCoreObject
  19. from Ansjer.config import LOGGER
  20. from django.conf import settings
  21. OMNI_API_KEY = settings.OMNI_API_KEY
  22. API_URL = "https://api.omnisend.com/v5/"
  23. class UserSubscriptionControllerView(View):
  24. def get(self, request, *args, **kwargs):
  25. request.encoding = 'utf-8'
  26. operation = kwargs.get('operation')
  27. return self.validation(request.GET, request, operation)
  28. def post(self, request, *args, **kwargs):
  29. request.encoding = 'utf-8'
  30. operation = kwargs.get('operation')
  31. return self.validation(request.POST, request, operation)
  32. def delete(self, request, *args, **kwargs):
  33. request.encoding = 'utf-8'
  34. operation = kwargs.get('operation')
  35. delete = QueryDict(request.body)
  36. if not delete:
  37. delete = request.GET
  38. return self.validation(delete, request, operation)
  39. def put(self, request, *args, **kwargs):
  40. request.encoding = 'utf-8'
  41. operation = kwargs.get('operation')
  42. put = QueryDict(request.body)
  43. return self.validation(put, request, operation)
  44. def validation(self, request_dict, request, operation):
  45. response = ResponseObject('cn')
  46. tko = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
  47. if tko.code != 0:
  48. return response.json(tko.code)
  49. response.lang = tko.lang
  50. userID = tko.userID
  51. if operation == 'checkSubscriptionStatus':
  52. return self.check_subscription_status(userID, response)
  53. elif operation == 'switchSubscription':
  54. return self.switch_subscription(userID, request_dict, response)
  55. else:
  56. return response.json(414)
  57. @staticmethod
  58. def check_subscription_status(user_id, response):
  59. """
  60. 检查订阅状态
  61. @param user_id: str
  62. @param response: 响应
  63. @return: response
  64. """
  65. user_qs = Device_User.objects.filter(userID=user_id)
  66. if not user_qs.exists():
  67. return response.json(104)
  68. user_sub = UserEmailSubscriptions.objects.filter(user_id=user_id).values('status', 'push_sub_status').first()
  69. if not user_sub:
  70. UserEmailSubscriptions.objects.create(user_id=user_id, status=0, push_sub_status=1,
  71. updated_time=int(time.time()), created_time=int(time.time()))
  72. return response.json(0, {"emailSubStatus": 0, 'pushSubStatus': 1})
  73. email_sub_status = 1 if user_sub["status"] == 1 else 0
  74. push_sub_status = 0 if user_sub["push_sub_status"] == 0 else 1
  75. user_sub = {"emailSubStatus": email_sub_status, 'pushSubStatus': push_sub_status}
  76. return response.json(0, user_sub)
  77. def switch_subscription(self, user_id, request_dict, response):
  78. """
  79. 邮件订阅开关
  80. @param user_id: str
  81. @param request_dict: dict
  82. @param response
  83. """
  84. user_qs = Device_User.objects.filter(userID=user_id)
  85. email_sub_status = request_dict.get('emailSubStatus', None)
  86. push_sub_status = request_dict.get('pushSubStatus', None)
  87. if not email_sub_status and not push_sub_status:
  88. return response.json(444, "emailSubStatus or pushSubStatus")
  89. if not user_qs.exists():
  90. return response.json(104)
  91. # 用户推送订阅
  92. if push_sub_status:
  93. push_sub_status = int(push_sub_status)
  94. user_sub_qs = UserEmailSubscriptions.objects.filter(user_id=user_id)
  95. if user_sub_qs.exists():
  96. user_sub_qs.update(push_sub_status=push_sub_status, updated_time=int(time.time()))
  97. else:
  98. user = user_qs.values('userEmail', 'phone').first()
  99. push_sub = {
  100. "user_id": user_id,
  101. "push_sub_status": push_sub_status,
  102. "created_time": int(time.time()),
  103. "updated_time": int(time.time())
  104. }
  105. if user["userEmail"]:
  106. push_sub["email"] = user["userEmail"]
  107. if user["phone"]:
  108. push_sub["phone"] = user["phone"]
  109. UserEmailSubscriptions.objects.create(**push_sub)
  110. else:
  111. user_sub = UserEmailSubscriptions.objects.filter(user_id=user_id).values('status',
  112. 'push_sub_status').first()
  113. if not user_sub:
  114. push_sub_status = 1
  115. else:
  116. push_sub_status = user_sub["push_sub_status"]
  117. if email_sub_status:
  118. # 修改数据库中订阅状态
  119. email_sub_status = int(email_sub_status)
  120. email_scription = email_sub_status
  121. user_sub_qs = UserEmailSubscriptions.objects.filter(user_id=user_id)
  122. # 邮件订阅
  123. if email_sub_status == 1:
  124. subscribers = user_qs.values('NickName', 'userEmail', 'region_country').first()
  125. if not subscribers["userEmail"]:
  126. LOGGER.info(f'subscribers{user_id}邮箱为空,无法订阅')
  127. return response.json(183)
  128. if user_sub_qs.exists():
  129. user_sub_qs.update(email=subscribers["userEmail"], status=1, updated_time=int(time.time()))
  130. else:
  131. UserEmailSubscriptions.objects.create(user_id=user_id, status=1, email=subscribers["userEmail"],
  132. created_time=int(time.time()), updated_time=int(time.time()))
  133. subscription_thread = threading.Thread(target=self.subscription, args=(subscribers,email_scription))
  134. subscription_thread.start()
  135. # 取消订阅
  136. elif email_sub_status == 0:
  137. device_user = Device_User.objects.filter(userID=user_id).values('userEmail').first()
  138. email = device_user["userEmail"]
  139. omni_unsubscription_thread = threading.Thread(target=UserSubscriptionControllerView.close_omni_subscribers, args=(email,))
  140. omni_unsubscription_thread.start()
  141. if device_user:
  142. customer = {
  143. "email": device_user["userEmail"],
  144. "first_name": device_user["userEmail"],
  145. "last_name": "APP",
  146. }
  147. try:
  148. yotpo = YotpoCoreObject()
  149. list_id = 8589406
  150. subscription_thread = threading.Thread(target=yotpo.close_subscribers, args=(customer, list_id))
  151. subscription_thread.start()
  152. if user_sub_qs.exists():
  153. customer["status"] = "unsubscription"
  154. user_sub_qs.update(email=device_user["userEmail"], status=0, sub_result=customer,
  155. updated_time=int(time.time()))
  156. except Exception as e:
  157. return response.json(500,'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  158. else:
  159. user_sub = UserEmailSubscriptions.objects.filter(user_id=user_id).values('status').first()
  160. if not user_sub:
  161. email_sub_status = 0
  162. else:
  163. email_sub_status = user_sub["status"]
  164. return response.json(0, {"emailSubStatus": email_sub_status, "pushSubStatus": push_sub_status})
  165. @staticmethod
  166. def subscription(subscribers, email_scription):
  167. """
  168. 订阅
  169. @param subscribers: dict
  170. @return: boolean
  171. """
  172. yotpo = YotpoCoreObject()
  173. email = subscribers.get("userEmail")
  174. try:
  175. # 查询顾客所在地区
  176. if subscribers["region_country"]:
  177. country = CountryModel.objects.filter(id=subscribers["region_country"]).values('country_code').first()
  178. if country:
  179. country_code = country["country_code"]
  180. else:
  181. country_code = ''
  182. # 构建顾客订阅格式
  183. customer = {
  184. "email": subscribers["userEmail"],
  185. "first_name": subscribers["userEmail"],
  186. "last_name": "APP",
  187. 'address': {
  188. "country_code": country_code,
  189. },
  190. "custom_properties": {
  191. "subscription_office": "ZosiApp",
  192. }
  193. }
  194. else:
  195. customer = {
  196. "email": subscribers["userEmail"],
  197. "first_name": subscribers["userEmail"],
  198. "last_name": "APP",
  199. "custom_properties": {
  200. "subscription_office": "ZosiApp",
  201. }
  202. }
  203. result = UserSubscriptionControllerView.sync_on_register(email, email_scription, country_code)
  204. if result:
  205. LOGGER.info(f'{email}{result}')
  206. try:
  207. result = yotpo.creat_and_update_customers(customer)
  208. list_id = 8589406
  209. sub_status, sub_result = yotpo.create_subscribers(customer, list_id)
  210. if result and sub_status:
  211. # 创建结果写入数据库
  212. user_sub_qs = UserEmailSubscriptions.objects.filter(email=subscribers["userEmail"])
  213. if user_sub_qs.exists():
  214. user_sub_qs.update(email=subscribers["userEmail"], status=1, sub_result=sub_result,list_id=list_id,
  215. updated_time=int(time.time()))
  216. LOGGER.info(f'在yotpo创建客户并订阅成功,customer:{customer}')
  217. return True
  218. else:
  219. LOGGER.info(f'在yotpo创建客户并订阅失败,customer:{customer}')
  220. except Exception as yotpo_err:
  221. LOGGER.warning(f'Yotpo接口异常,已跳过:{yotpo_err}')
  222. return False
  223. except Exception as e:
  224. LOGGER.error(f'{subscribers["userEmail"]}订阅失败:{e}')
  225. return False
  226. def sync_on_register(email, email_scription, country_code):
  227. """
  228. 注册时调用,写入 Omnisend。当email_scription==1为订阅打app subscribed标签
  229. """
  230. email_status = "subscribed" if str(email_scription) == '1' else "unsubscribed"
  231. payload = {
  232. "updateEnabled": True,
  233. "country": country_code,
  234. "identifiers": [
  235. {
  236. "type": "email",
  237. "id": email,
  238. "channels": {
  239. "email": {
  240. "status": email_status,
  241. "statusDate": datetime.utcnow().isoformat() + "Z",
  242. }
  243. }
  244. }
  245. ],
  246. "customProperties": {
  247. "source": "User Registration",
  248. "createdAt": datetime.utcnow().isoformat() + "Z",
  249. }
  250. }
  251. if str(email_scription) == "1":
  252. payload["tags"] = ["app subscribed"]
  253. headers = {
  254. "X-API-KEY": OMNI_API_KEY,
  255. "accept": "application/json",
  256. "content-type": "application/json"
  257. }
  258. response = requests.post(f"{API_URL}/contacts", headers=headers, json=payload)
  259. LOGGER.info(f"{email} 注册同步到omni平台: ({response.status_code})")
  260. return response.status_code, response.json()
  261. @staticmethod
  262. def close_omni_subscribers(email):
  263. """
  264. 关闭 Omnisend 订阅
  265. @param email: 用户邮箱
  266. @return: 成功返回 True,失败返回 False
  267. """
  268. try:
  269. payload = {
  270. "updateEnabled": True,
  271. "identifiers": [
  272. {
  273. "type": "email",
  274. "id": email,
  275. "channels": {
  276. "email": {
  277. "status": "unsubscribed",
  278. "statusDate": datetime.utcnow().isoformat() + "Z",
  279. }
  280. }
  281. }
  282. ],
  283. "customProperties": {
  284. "source": "User Unsubscription",
  285. "updatedAt": datetime.utcnow().isoformat() + "Z",
  286. },
  287. }
  288. headers = {
  289. "X-API-KEY": OMNI_API_KEY,
  290. "accept": "application/json",
  291. "content-type": "application/json"
  292. }
  293. response = requests.patch(
  294. f"{API_URL}/contacts?email={email}",
  295. headers=headers,
  296. json=payload
  297. )
  298. LOGGER.info(f"Omnisend 取消订阅 {email}: {response.status_code}")
  299. if response.status_code in [200, 201]:
  300. return True
  301. else:
  302. LOGGER.error(f"Omnisend API 失败: {response.text}")
  303. return False
  304. except Exception as e:
  305. LOGGER.error(f"关闭订阅异常 {email}: {str(e)}")
  306. return False