CampaignController.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. import time
  2. import json
  3. from Model.models import AppAdvertiseCampaign, DeviceTypeModel, CountryModel, OpenScreenCampaign
  4. from Ansjer.config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, SERVER_TYPE, LOGGER
  5. from django.core.paginator import Paginator
  6. from django.views import View
  7. from django.db.models import Prefetch
  8. from django.db.models import Q, F
  9. from Object.AWS.AmazonS3Util import AmazonS3Util
  10. from Object.ResponseObject import ResponseObject
  11. from Object.TokenObject import TokenObject
  12. class CampaignView(View):
  13. def get(self, request, *args, **kwargs):
  14. request.encoding = 'utf-8'
  15. operation = kwargs.get('operation')
  16. request_dict = request.GET
  17. return self.validation(request_dict, request, operation)
  18. def post(self, request, *args, **kwargs):
  19. request.encoding = 'utf-8'
  20. operation = kwargs.get('operation')
  21. request_dict = request.POST
  22. return self.validation(request_dict, request, operation)
  23. def validation(self, request_dict, request, operation):
  24. language = request_dict.get('language', 'en')
  25. response = ResponseObject(language, 'pc')
  26. if operation == 'getCountryList':
  27. return self.get_country_list(response)
  28. else:
  29. tko = TokenObject(
  30. request.META.get('HTTP_AUTHORIZATION'),
  31. returntpye='pc')
  32. if tko.code != 0:
  33. return response.json(tko.code)
  34. response.lang = tko.lang
  35. if operation == 'getCampaignList': # 获取广告活动列表
  36. return self.get_campaign_list(request_dict, response)
  37. elif operation == 'addCampaign': # 添加广告活动
  38. return self.add_campaign(request, request_dict, response)
  39. elif operation == 'updateCampaign': # 更新广告活动
  40. return self.update_campaign(request, request_dict, response)
  41. elif operation == 'deleteCampaign': # 删除广告活动
  42. return self.delete_campaign(request_dict, response)
  43. elif operation == 'switchCampaign': # 广告活动开关
  44. return self.switch_campaign(request_dict, response)
  45. elif operation == 'getUserBehaviorLog': # 获取用户行为日志
  46. return self.get_user_behavior_log(request_dict, response)
  47. else:
  48. return response.json(414)
  49. def get_campaign_list(self, request_dict, response):
  50. """
  51. 查询广告活动列表
  52. @param request_dict: 请求参数
  53. @param response: 响应对象
  54. @return: 响应对象包含广告活动列表
  55. """
  56. campaign_name = request_dict.get('campaign_name', None)
  57. campaign_country = request_dict.get('campaign_country', None)
  58. status = request_dict.get('status', None)
  59. pageNo = request_dict.get('pageNo', 1)
  60. pageSize = request_dict.get('pageSize', 20)
  61. unknown_country = 0
  62. try:
  63. # 连接并获取国家和设备类型
  64. country_prefetch = Prefetch('country', queryset=CountryModel.objects.only('country_name'),
  65. to_attr='country_list')
  66. device_type_prefetch = Prefetch('device_type', queryset=DeviceTypeModel.objects.only('name'),
  67. to_attr='device_type_list')
  68. app_advertise_campaign_qs = AppAdvertiseCampaign.objects.prefetch_related(country_prefetch,
  69. device_type_prefetch)
  70. # 过滤
  71. if campaign_name:
  72. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(campaign_name=campaign_name)
  73. if status:
  74. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(status=status)
  75. if campaign_country:
  76. campaign_country_list = campaign_country.split(',')
  77. if "未知地区" in campaign_country_list:
  78. unknown_country = 1
  79. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(
  80. Q(country__country_name__in=campaign_country_list) | Q(unknown_country=unknown_country)).distinct()
  81. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(~Q(status=2))
  82. # 分页
  83. paginator = Paginator(app_advertise_campaign_qs.order_by('id'), pageSize)
  84. campaigns = paginator.page(pageNo)
  85. # 添加设备名和地区返回
  86. campaign_list = []
  87. for campaign in campaigns.object_list:
  88. if campaign.unknown_country == 0:
  89. countries = ",".join([country.country_name for country in campaign.country_list])
  90. else:
  91. country_list = campaign.country_list
  92. country_names = []
  93. for country in country_list:
  94. country_names.append(country.country_name)
  95. country_names.append("未知地区")
  96. countries = ",".join(country_names)
  97. s3_url, _, _ = self.s3_server()
  98. banner_campaign = campaign.banner_campaign
  99. for sort in banner_campaign:
  100. banner_campaign[sort]["image"] = s3_url + banner_campaign[sort]["image"]
  101. campaign_data = {
  102. 'id': campaign.id,
  103. 'image_url': s3_url + campaign.image_url,
  104. 'banner_campaign': banner_campaign,
  105. 'campaign_name': campaign.campaign_name,
  106. 'campaign_url': campaign.campaign_url,
  107. 'campaign_type': campaign.campaign_type,
  108. 'status': campaign.status,
  109. 'campaign_start_date': campaign.campaign_start_date,
  110. 'campaign_end_date': campaign.campaign_end_date,
  111. 'campaign_show_stime': campaign.campaign_show_stime,
  112. 'campaign_show_etime': campaign.campaign_show_etime,
  113. 'app_bundle_type': campaign.app_bundle_type,
  114. 'countries': countries,
  115. 'device_types': ",".join([device.name for device in campaign.device_type_list]),
  116. }
  117. campaign_list.append(campaign_data)
  118. data = {
  119. 'list': campaign_list,
  120. 'total': paginator.count,
  121. }
  122. return response.json(0, data)
  123. except Exception as e:
  124. print(e)
  125. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  126. def add_campaign(self, request, request_dict, response):
  127. """
  128. 添加新的广告活动
  129. @param request: 包含文件信息
  130. @param request_dict: 包含所有请求参数的字典
  131. @param response: 响应对象
  132. @return: 响应对象
  133. """
  134. try:
  135. campaign_name = request_dict.get('campaign_name', None)
  136. campaign_url = request_dict.get('campaign_url', None)
  137. status = request_dict.get('status', 2)
  138. campaign_start_time = request_dict.get('campaign_start_time', None)
  139. campaign_end_time = request_dict.get('campaign_end_time', None)
  140. campaign_show_stime = request_dict.get('campaign_show_stime', 0)
  141. campaign_show_etime = request_dict.get('campaign_show_etime', 86399)
  142. app_bundle_type = request_dict.get('app_bundle_type', None)
  143. poster_file = request.FILES.get('posterFile', None)
  144. banner_files = request.FILES.getlist('bannerFiles', None)
  145. banner_campaign_urls = json.loads(request_dict.get('banner_campaign_url', "[]"))
  146. device_type_names = json.loads(request_dict.get('device_type_list', "[]")) # 设备类型名称列表
  147. country_name_list = json.loads(request_dict.get('country_name_list', "[]")) # 地区列表
  148. required_fields = [campaign_name, campaign_url, campaign_start_time, campaign_end_time,
  149. app_bundle_type]
  150. list_fields = [device_type_names, country_name_list, banner_campaign_urls]
  151. # 检查基本字段是否为None
  152. if any(field is None for field in required_fields):
  153. return response.json(444)
  154. # 检查列表类型的字段是否为空
  155. if any(not field for field in list_fields):
  156. return response.json(444)
  157. # 确保至少提供了一个文件
  158. if not poster_file and not banner_files:
  159. return response.json(444)
  160. # 针对特殊地区的处理,表没设计好用这个处理挽救一下
  161. unknown_country = 0
  162. if "未知地区" in country_name_list:
  163. unknown_country = 1
  164. country_name_list.remove("未知地区")
  165. # 上传文件到S3
  166. banner_image_urls = self.upload_files_to_s3(banner_files,
  167. "BannerAdvertise") if banner_files is not None else []
  168. poster_image_url = self.upload_files_to_s3(poster_file,
  169. "OpenScreenAdvertise")[0] if poster_file is not None else ""
  170. campaign_type = []
  171. if banner_image_urls is not []:
  172. campaign_type.append(1)
  173. banner_campaign = {
  174. str(index): {"image": image_url, "url": campaign_url}
  175. for index, (image_url, campaign_url) in
  176. enumerate(zip(banner_image_urls, banner_campaign_urls), start=1)
  177. }
  178. else:
  179. banner_campaign = {}
  180. if poster_image_url != "":
  181. campaign_type.append(2)
  182. create_time = int(time.time())
  183. update_time = int(time.time())
  184. # 创建 AppAdvertiseCampaign 实例
  185. new_campaign = AppAdvertiseCampaign.objects.create(
  186. image_url=poster_image_url,
  187. campaign_name=campaign_name,
  188. campaign_url=campaign_url,
  189. banner_campaign=banner_campaign,
  190. campaign_type=campaign_type,
  191. status=status,
  192. unknown_country=unknown_country,
  193. app_bundle_type=app_bundle_type,
  194. campaign_start_date=campaign_start_time,
  195. campaign_end_date=campaign_end_time,
  196. campaign_show_stime=campaign_show_stime,
  197. campaign_show_etime=campaign_show_etime,
  198. create_time=create_time,
  199. update_time=update_time,
  200. )
  201. # 根据名称查找 DeviceTypeModel 的实例并建立关系
  202. device_type_instances = DeviceTypeModel.objects.filter(name__in=device_type_names)
  203. for device_type_instance in device_type_instances:
  204. new_campaign.device_type.add(device_type_instance)
  205. # 根据 ID 关联 CountryModel 实例
  206. country_instances = CountryModel.objects.filter(country_name__in=country_name_list)
  207. for country_instance in country_instances:
  208. new_campaign.country.add(country_instance)
  209. return response.json(0)
  210. except Exception as e:
  211. print(e)
  212. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  213. def update_campaign(self, request, request_dict, response):
  214. """
  215. 更新广告活动
  216. @param request: 包含文件信息
  217. @param request_dict: 包含所有请求参数的字典
  218. @param response: 响应对象
  219. @return: 响应对象
  220. """
  221. try:
  222. campaign_id = request_dict.get('id', None)
  223. campaign_name = request_dict.get('campaign_name', None)
  224. campaign_url = request_dict.get('campaign_url', None)
  225. campaign_start_time = request_dict.get('campaign_start_time', None)
  226. campaign_end_time = request_dict.get('campaign_end_time', None)
  227. campaign_show_stime = request_dict.get('campaign_show_stime', None)
  228. campaign_show_etime = request_dict.get('campaign_show_etime', None)
  229. app_bundle_type = request_dict.get('app_bundle_type', None)
  230. device_type_names = json.loads(request_dict.get('device_type_list', "[]")) # 设备类型名称列表
  231. country_name_list = json.loads(request_dict.get('country_name_list', "[]")) # 地区列表
  232. poster_file = request.FILES.get('posterFile', None)
  233. # 更改轮播图广告图片
  234. sort_files = json.loads(request_dict.get('sort_files', "[]"))
  235. banner_files = request.FILES.getlist('bannerFiles', None)
  236. # 更改轮播图链接
  237. sort_urls = json.loads(request_dict.get('sort_url', "[]"))
  238. banner_urls = json.loads(request_dict.get('banner_urls', "[]"))
  239. if not campaign_id:
  240. return response.json(444)
  241. update_time = int(time.time())
  242. campaign = AppAdvertiseCampaign.objects.filter(pk=campaign_id).first()
  243. # 未知地区特殊处理
  244. if country_name_list is not None:
  245. if "未知地区" in country_name_list:
  246. campaign.unknown_country = 1
  247. country_name_list.remove("未知地区")
  248. else:
  249. campaign.unknown_country = 0
  250. # 开屏广告图片
  251. if poster_file is not None:
  252. self.del_file_to_s3(campaign.image_url)
  253. poster_image_url = self.upload_files_to_s3(poster_file,
  254. "OpenScreenAdvertise")[0]
  255. campaign.image_url = poster_image_url
  256. if 1 not in campaign.campaign_type:
  257. campaign.campaign_type.append(1)
  258. # 更改轮播广告图片
  259. if banner_files is not None:
  260. image_num = 0
  261. banner_image_urls = self.upload_files_to_s3(banner_files, "BannerAdvertise")
  262. for sort in sort_files:
  263. old_image = campaign.banner_campaign[str(sort)]["image"]
  264. self.del_file_to_s3(old_image)
  265. campaign.banner_campaign[str(sort)]["image"] = banner_image_urls[image_num]
  266. image_num = image_num + 1
  267. if 2 not in campaign.campaign_type:
  268. campaign.campaign_type.append(2)
  269. # 更改轮播广告链接
  270. if banner_urls:
  271. url_num = 0
  272. for sort in sort_urls:
  273. campaign.banner_campaign[str(sort)]["url"] = banner_urls[url_num]
  274. url_num = url_num + 1
  275. # 常规字段
  276. if campaign_name is not None:
  277. campaign.campaign_name = campaign_name
  278. if campaign_url is not None:
  279. campaign.campaign_url = campaign_url
  280. if app_bundle_type is not None:
  281. campaign.app_bundle_type = app_bundle_type
  282. if campaign_start_time is not None:
  283. campaign.campaign_start_date = campaign_start_time
  284. if campaign_end_time is not None:
  285. campaign.campaign_end_date = campaign_end_time
  286. if campaign_show_stime is not None:
  287. campaign.campaign_show_stime = campaign_show_stime
  288. if campaign_show_etime is not None:
  289. campaign.campaign_show_etime = campaign_show_etime
  290. # 更新多对多字段 - 设备类型
  291. if device_type_names:
  292. device_types = DeviceTypeModel.objects.filter(name__in=device_type_names)
  293. campaign.device_type.set(device_types)
  294. # 更新多对多字段 - 国家/地区
  295. if country_name_list:
  296. countries = CountryModel.objects.filter(country_name__in=country_name_list)
  297. campaign.country.set(countries)
  298. campaign.update_time = update_time
  299. # 保存更新
  300. campaign.save()
  301. return response.json(0)
  302. except Exception as e:
  303. print(e)
  304. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  305. def delete_campaign(self, request_dict, response):
  306. """
  307. 删除广告活动
  308. @param request_dict: 包含所有请求参数的字典
  309. @param response: 响应对象
  310. @return: 响应对象
  311. """
  312. campaign_id = request_dict.get('id')
  313. if not campaign_id:
  314. return response.json(444)
  315. try:
  316. campaign = AppAdvertiseCampaign.objects.get(pk=campaign_id)
  317. if campaign.image_url != "":
  318. self.del_file_to_s3(campaign.image_url)
  319. if campaign.banner_campaign is not {}:
  320. banner_campaign = []
  321. for sort in campaign.banner_campaign:
  322. banner = campaign.banner_campaign[sort]
  323. banner_campaign.append(banner)
  324. self.del_file_to_s3(banner_campaign)
  325. # 清除多对多关系
  326. campaign.device_type.clear()
  327. campaign.country.clear()
  328. # 保留在广告表中
  329. campaign.status = 2
  330. campaign.update_time = int(time.time())
  331. campaign.save()
  332. return response.json(0)
  333. except Exception as e:
  334. print(e)
  335. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  336. def switch_campaign(self, request_dict, response):
  337. """
  338. 广告活动 开启/关闭
  339. @param request_dict: 包含所有请求参数的字典
  340. @param response: 响应对象
  341. @return: 响应对象
  342. """
  343. campaign_id = request_dict.get('id')
  344. status = request_dict.get('status')
  345. if not all([campaign_id, status]):
  346. return response.json(444)
  347. try:
  348. AppAdvertiseCampaign.objects.filter(pk=campaign_id).update(status=status, update_time=int(time.time()))
  349. return response.json(0)
  350. except Exception as e:
  351. print(e)
  352. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  353. def get_user_behavior_log(self, request_dict, response):
  354. campaign_ids = request_dict.get('campaign_ids', None)
  355. start_time = request_dict.get('start_time', None)
  356. end_time = request_dict.get('end_time', None)
  357. if not all([start_time, end_time]):
  358. return response.json(444)
  359. try:
  360. open_screen_campaign_qs = OpenScreenCampaign.objects.filter(
  361. update_time__range=[int(start_time), int(end_time)])
  362. if campaign_ids is not None:
  363. open_screen_campaign_qs = open_screen_campaign_qs.filter(
  364. Q(campaign_id_id__isnull=True) | Q(campaign_id_id__in=json.loads(campaign_ids)))
  365. open_screen_campaign_qs = open_screen_campaign_qs.select_related('campaign_id').annotate(
  366. campaign_name=F('campaign_id__campaign_name'),
  367. campaign_type=F('campaign_id__campaign_type'),
  368. campaign_status=F('campaign_id__status'),
  369. start_date=F('campaign_id__campaign_start_date'),
  370. end_date=F('campaign_id__campaign_end_date'),
  371. start_time=F('campaign_id__campaign_show_stime'),
  372. end_time=F('campaign_id__campaign_show_etime')
  373. ).values('id', 'user_id', 'status', 'update_time', 'create_time', 'campaign_id', 'campaign_name',
  374. 'campaign_type', 'campaign_status', 'start_date', 'end_date', 'start_time', 'end_time')
  375. if not open_screen_campaign_qs.exists():
  376. return response.json(0, {'list': []})
  377. campaigns_list = list(open_screen_campaign_qs)
  378. return response.json(0, {'list': campaigns_list})
  379. except Exception as e:
  380. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  381. def get_country_list(self, response):
  382. try:
  383. if SERVER_TYPE == 'Ansjer.us_config.formal_settings':
  384. region_api = 'https://www.dvema.com/'
  385. elif SERVER_TYPE == 'Ansjer.eur_config.formal_settings':
  386. region_api = 'https://api.zositeche.com/'
  387. elif SERVER_TYPE == 'Ansjer.cn_config.formal_settings':
  388. region_api = 'https://www.zositechc.cn/'
  389. else:
  390. region_api = 'https://test.zositechc.cn/'
  391. country_qs = CountryModel.objects.filter(region__api=region_api).values('country_name')
  392. if not country_qs.exists():
  393. return response.json(173)
  394. country_list = []
  395. for country in country_qs:
  396. country_list.append(country['country_name'])
  397. return response.json(0, {'list': country_list})
  398. except Exception as e:
  399. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  400. @staticmethod
  401. def upload_files_to_s3(files, path_prefix):
  402. """
  403. 广告图片文件上传到S3存储桶
  404. @param files: 文件 request.FILES获取
  405. @param path_prefix: 存储路径
  406. @return image_urls: 列表
  407. """
  408. try:
  409. _, regin, AWS_SES_ACCESS_REGION = CampaignView.s3_server()
  410. # 确保files是一个列表,统一处理单个文件和多个文件
  411. if not isinstance(files, list):
  412. files = [files]
  413. image_urls = []
  414. bucket_name = "ansjerfilemanager"
  415. s3 = AmazonS3Util(AWS_ACCESS_KEY_ID[regin], AWS_SECRET_ACCESS_KEY[regin], AWS_SES_ACCESS_REGION)
  416. for file in files:
  417. timestamp = int(time.time())
  418. file_key = f'app/campaign/{path_prefix}/{timestamp}_{file.name}'
  419. s3.upload_file_obj(bucket_name, file_key, file,
  420. {'ContentType': file.content_type, 'ACL': 'public-read'})
  421. image_urls.append(f"{path_prefix}/{timestamp}_{file.name}")
  422. return image_urls
  423. except Exception as e:
  424. LOGGER.info('存储桶添加异常:error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  425. return None
  426. @staticmethod
  427. def del_file_to_s3(path_list):
  428. """
  429. S3存储桶 删除文件
  430. @param path_list: 单一字符串或者列表
  431. @return
  432. """
  433. try:
  434. _, regin, AWS_SES_ACCESS_REGION = CampaignView.s3_server()
  435. if not isinstance(path_list, list):
  436. path_list = [path_list]
  437. for path in path_list:
  438. # 删除存储桶原来的图片
  439. s3 = AmazonS3Util(AWS_ACCESS_KEY_ID[regin], AWS_SECRET_ACCESS_KEY[regin], AWS_SES_ACCESS_REGION)
  440. bucket_name = 'ansjerfilemanager'
  441. s3.delete_obj(bucket_name, f"app/campaign/{path}")
  442. except Exception as e:
  443. LOGGER.info('存储桶删除异常:error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  444. @staticmethod
  445. def s3_server():
  446. if SERVER_TYPE == 'Ansjer.cn_config.formal_settings' or SERVER_TYPE == 'Ansjer.cn_config.test_settings':
  447. s3_url = "https://ansjerfilemanager.s3.cn-northwest-1.amazonaws.com.cn/app/campaign/"
  448. regin = 0
  449. AWS_SES_ACCESS_REGION = "cn-northwest-1"
  450. else:
  451. s3_url = "https://ansjerfilemanager.s3.amazonaws.com/app/campaign/"
  452. regin = 1
  453. AWS_SES_ACCESS_REGION = 'us-east-1'
  454. return s3_url, regin, AWS_SES_ACCESS_REGION