AppCampaignController.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. import time
  2. import pytz
  3. import json
  4. from datetime import datetime
  5. from datetime import time as Time
  6. from Model.models import AppAdvertiseCampaign, DeviceTypeModel, CountryModel, Device_User, OpenScreenCampaign
  7. from Ansjer.config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, SERVER_TYPE
  8. from django.core.paginator import Paginator
  9. from django.views import View
  10. from django.db.models import Prefetch
  11. from django.db.models import Q
  12. from Object.AWS.AmazonS3Util import AmazonS3Util
  13. from Object.ResponseObject import ResponseObject
  14. from Object.TokenObject import TokenObject
  15. class AppCampaignView(View):
  16. def get(self, request, *args, **kwargs):
  17. request.encoding = 'utf-8'
  18. operation = kwargs.get('operation')
  19. request_dict = request.GET
  20. return self.validation(request_dict, request, operation)
  21. def post(self, request, *args, **kwargs):
  22. request.encoding = 'utf-8'
  23. operation = kwargs.get('operation')
  24. request_dict = request.POST
  25. return self.validation(request_dict, request, operation)
  26. def validation(self, request_dict, request, operation):
  27. language = request_dict.get('language', 'en')
  28. response = ResponseObject(language, 'pc')
  29. if operation == 'getCampaignList': # 获取广告活动列表
  30. return self.get_campaign_list(request_dict, response)
  31. elif operation == 'addCampaign': # 添加广告活动
  32. return self.add_campaign(request, request_dict, response)
  33. elif operation == 'updateCampaign': # 更新广告活动
  34. return self.update_campaign(request, request_dict, response)
  35. elif operation == 'deleteCampaign': # 删除广告活动
  36. return self.delete_campaign(request_dict, response)
  37. elif operation == 'switchCampaign': # 广告活动开关
  38. return self.switch_campaign(request_dict, response)
  39. elif operation == 'appGetCampaigns':
  40. return self.app_get_campaigns(request_dict, response)
  41. elif operation == 'recordUserBehavior':
  42. return self.record_user_behavior(request_dict, response)
  43. elif operation == 'getUserBehaviorLog':
  44. return self.get_user_behavior_log(request_dict, response)
  45. else:
  46. return response.json(414)
  47. def get_campaign_list(self, request_dict, response):
  48. """
  49. 查询广告活动列表
  50. @param request_dict: 请求参数
  51. @param response: 响应对象
  52. @return: 响应对象包含广告活动列表
  53. """
  54. campaign_name = request_dict.get('campaign_name', None)
  55. campaign_country = request_dict.get('campaign_country', None)
  56. status = request_dict.get('status', None)
  57. pageNo = request_dict.get('pageNo', 1)
  58. pageSize = request_dict.get('pageSize', 20)
  59. unknown_country = 0
  60. # 连接并获取国家和设备类型
  61. country_prefetch = Prefetch('country', queryset=CountryModel.objects.only('country_name'),
  62. to_attr='country_list')
  63. device_type_prefetch = Prefetch('device_type', queryset=DeviceTypeModel.objects.only('name'),
  64. to_attr='device_type_list')
  65. app_advertise_campaign_qs = AppAdvertiseCampaign.objects.prefetch_related(country_prefetch,
  66. device_type_prefetch)
  67. # 过滤
  68. if campaign_name:
  69. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(campaign_name=campaign_name)
  70. if status:
  71. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(status=status)
  72. if campaign_country:
  73. campaign_country_list = campaign_country.split(',')
  74. if "未知地区" in campaign_country_list:
  75. unknown_country = 1
  76. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(
  77. Q(country__country_name__in=campaign_country_list) | Q(unknown_country=unknown_country)).distinct()
  78. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(~Q(status=2))
  79. # 分页
  80. paginator = Paginator(app_advertise_campaign_qs.order_by('id'), pageSize)
  81. campaigns = paginator.page(pageNo)
  82. if SERVER_TYPE == 'Ansjer.cn_config.formal_settings' or SERVER_TYPE == 'Ansjer.cn_config.test_settings':
  83. s3_url = "https://ansjerfilemanager.s3.cn-northwest-1.amazonaws.com.cn/app/campaign/"
  84. else:
  85. s3_url = "https://ansjerfilemanager.s3.amazonaws.com/app/campaign/"
  86. # 添加设备名和地区返回
  87. campaign_list = []
  88. for campaign in campaigns.object_list:
  89. if campaign.unknown_country == 0:
  90. countries = ",".join([country.country_name for country in campaign.country_list])
  91. else:
  92. country_list = campaign.country_list
  93. country_names = []
  94. for country in country_list:
  95. country_names.append(country.country_name)
  96. country_names.append("未知地区")
  97. countries = ",".join(country_names)
  98. campaign_data = {
  99. 'id': campaign.id,
  100. 'image_url': s3_url + campaign.image_url,
  101. 'campaign_name': campaign.campaign_name,
  102. 'campaign_url': campaign.campaign_url,
  103. 'campaign_type': campaign.campaign_type,
  104. 'status': campaign.status,
  105. 'campaign_start_date': campaign.campaign_start_date,
  106. 'campaign_end_date': campaign.campaign_end_date,
  107. 'campaign_show_stime': campaign.campaign_show_stime,
  108. 'campaign_show_etime': campaign.campaign_show_etime,
  109. 'countries': countries,
  110. 'device_types': ",".join([device.name for device in campaign.device_type_list]),
  111. }
  112. campaign_list.append(campaign_data)
  113. data = {
  114. 'list': campaign_list,
  115. 'total': paginator.count,
  116. }
  117. return response.json(0, data)
  118. def add_campaign(self, request, request_dict, response):
  119. """
  120. 添加新的广告活动
  121. @param request_dict: 包含所有请求参数的字典
  122. @param response: 响应对象
  123. @return: 响应对象
  124. """
  125. file = request.FILES.get('posterFile', None)
  126. campaign_name = request_dict.get('campaign_name', None)
  127. campaign_url = request_dict.get('campaign_url', None)
  128. campaign_type = request_dict.get('campaign_type', None)
  129. status = request_dict.get('status', None)
  130. device_type_names = json.loads(request_dict.get('device_type_list', None)) # 设备类型名称列表
  131. country_name_list = json.loads(request_dict.get('country_name_list', None)) # 地区列表
  132. campaign_start_time = request_dict.get('campaign_start_time', None)
  133. campaign_end_time = request_dict.get('campaign_end_time', None)
  134. campaign_show_stime = request_dict.get('campaign_show_stime', 0)
  135. campaign_show_etime = request_dict.get('campaign_show_etime', 86399)
  136. if not all([campaign_name, campaign_type, device_type_names, campaign_url,
  137. country_name_list, campaign_start_time, campaign_end_time, file]):
  138. return response.json(444)
  139. # 针对特殊地区的处理,表没设计好用这个处理挽救一下
  140. unknown_country = 0
  141. if "未知地区" in country_name_list:
  142. unknown_country = 1
  143. country_name_list.remove("未知地区")
  144. if SERVER_TYPE == 'Ansjer.cn_config.formal_settings' or SERVER_TYPE == 'Ansjer.cn_config.test_settings':
  145. regin = 0
  146. AWS_SES_ACCESS_REGION = "cn-northwest-1"
  147. else:
  148. regin = 1
  149. AWS_SES_ACCESS_REGION = 'us-east-1'
  150. fileName = file.name
  151. try:
  152. create_time = int(time.time())
  153. update_time = int(time.time())
  154. # 保存图片到存储桶
  155. bucket_name = 'ansjerfilemanager'
  156. file_key = f'app/campaign/OpenScreenAdvertise/{update_time}_{fileName}'
  157. s3 = AmazonS3Util(AWS_ACCESS_KEY_ID[0], AWS_SECRET_ACCESS_KEY[0], "cn-northwest-1")
  158. # 地址:https://ansjerfilemanager.s3.amazonaws.com/app/campaign/OpenScreenAdvertise/XXX.jpg
  159. s3.upload_file_obj(
  160. bucket_name,
  161. file_key,
  162. file,
  163. {'ContentType': file.content_type, 'ACL': 'public-read'})
  164. # 创建 AppAdvertiseCampaign 实例
  165. new_campaign = AppAdvertiseCampaign.objects.create(
  166. image_url=f"OpenScreenAdvertise/{update_time}_{fileName}",
  167. campaign_name=campaign_name,
  168. campaign_url=campaign_url,
  169. campaign_type=campaign_type,
  170. status=status,
  171. unknown_country=unknown_country,
  172. campaign_start_date=campaign_start_time,
  173. campaign_end_date=campaign_end_time,
  174. campaign_show_stime=campaign_show_stime,
  175. campaign_show_etime=campaign_show_etime,
  176. create_time=create_time,
  177. update_time=update_time,
  178. )
  179. # 根据名称查找 DeviceTypeModel 的实例并建立关系
  180. device_type_instances = DeviceTypeModel.objects.filter(name__in=device_type_names)
  181. for device_type_instance in device_type_instances:
  182. new_campaign.device_type.add(device_type_instance)
  183. # 根据 ID 关联 CountryModel 实例
  184. country_instances = CountryModel.objects.filter(country_name__in=country_name_list)
  185. for country_instance in country_instances:
  186. new_campaign.country.add(country_instance)
  187. except Exception as e:
  188. return response.json(178)
  189. return response.json(0)
  190. def update_campaign(self, request, request_dict, response):
  191. campaign_id = request_dict.get('id', None)
  192. campaign_name = request_dict.get('campaign_name', None)
  193. campaign_url = request_dict.get('campaign_url', None)
  194. campaign_type = request_dict.get('campaign_type', None)
  195. device_type_names = json.loads(request_dict.get('device_type_list', '[]')) # 设备类型名称列表
  196. country_name_list = json.loads(request_dict.get('country_name_list', '[]')) # 地区列表
  197. campaign_start_time = request_dict.get('campaign_start_time', None)
  198. campaign_end_time = request_dict.get('campaign_end_time', None)
  199. campaign_show_stime = request_dict.get('campaign_show_stime', None)
  200. campaign_show_etime = request_dict.get('campaign_show_etime', None)
  201. file = request.FILES.get('posterFile', None)
  202. if not campaign_id:
  203. return response.json(444)
  204. if SERVER_TYPE == 'Ansjer.cn_config.formal_settings' or SERVER_TYPE == 'Ansjer.cn_config.test_settings':
  205. regin = 0
  206. AWS_SES_ACCESS_REGION = "cn-northwest-1"
  207. else:
  208. regin = 1
  209. AWS_SES_ACCESS_REGION = 'us-east-1'
  210. try:
  211. update_time = int(time.time())
  212. campaign = AppAdvertiseCampaign.objects.get(id=campaign_id)
  213. if country_name_list is not None:
  214. if "未知地区" in country_name_list:
  215. campaign.unknown_country = 1
  216. country_name_list.remove("未知地区")
  217. else:
  218. campaign.unknown_country = 0
  219. if file is not None:
  220. # 删除存储桶原来的图片
  221. s3 = AmazonS3Util(AWS_ACCESS_KEY_ID[regin], AWS_SECRET_ACCESS_KEY[regin], AWS_SES_ACCESS_REGION)
  222. bucket_name = 'ansjerfilemanager'
  223. if campaign.image_url:
  224. s3.delete_obj(bucket_name, f"app/campaign/{campaign.image_url}")
  225. # 添加新的图片
  226. file_key = f'app/campaign/OpenScreenAdvertise/{update_time}_{file.name}'
  227. s3.upload_file_obj(
  228. bucket_name,
  229. file_key,
  230. file,
  231. {'ContentType': file.content_type, 'ACL': 'public-read'}
  232. )
  233. campaign.image_url = f'OpenScreenAdvertise/{update_time}_{file.name}'
  234. if campaign_name is not None:
  235. campaign.campaign_name = campaign_name
  236. if campaign_url is not None:
  237. campaign.campaign_url = campaign_url
  238. if campaign_type is not None:
  239. campaign.campaign_type = campaign_type
  240. if campaign_start_time is not None:
  241. campaign.campaign_start_date = campaign_start_time
  242. if campaign_end_time is not None:
  243. campaign.campaign_end_date = campaign_end_time
  244. if campaign_show_stime is not None:
  245. campaign.campaign_show_stime = campaign_show_stime
  246. if campaign_show_etime is not None:
  247. campaign.campaign_show_etime = campaign_show_etime
  248. # 更新多对多字段 - 设备类型
  249. if device_type_names:
  250. device_types = DeviceTypeModel.objects.filter(name__in=device_type_names)
  251. campaign.device_type.set(device_types)
  252. # 更新多对多字段 - 国家/地区
  253. if country_name_list:
  254. countries = CountryModel.objects.filter(country_name__in=country_name_list)
  255. campaign.country.set(countries)
  256. campaign.update_time = update_time
  257. # 保存更新
  258. campaign.save()
  259. except Exception as e:
  260. return response.json(177)
  261. return response.json(0)
  262. def switch_campaign(self, request_dict, response):
  263. campaign_id = request_dict.get('id')
  264. status = request_dict.get('status')
  265. if not campaign_id:
  266. return response.json(444)
  267. try:
  268. AppAdvertiseCampaign.objects.filter(pk=campaign_id).update(status=status, update_time=int(time.time()))
  269. return response.json(0)
  270. except Exception as e:
  271. return response.json(444)
  272. def delete_campaign(self, request_dict, response):
  273. campaign_id = request_dict.get('id')
  274. if SERVER_TYPE == 'Ansjer.cn_config.formal_settings' or SERVER_TYPE == 'Ansjer.cn_config.test_settings':
  275. regin = 0
  276. AWS_SES_ACCESS_REGION = "cn-northwest-1"
  277. else:
  278. regin = 1
  279. AWS_SES_ACCESS_REGION = 'us-east-1'
  280. if not campaign_id:
  281. return response.json(444)
  282. try:
  283. campaign = AppAdvertiseCampaign.objects.get(pk=campaign_id)
  284. s3 = AmazonS3Util(AWS_ACCESS_KEY_ID[regin], AWS_SECRET_ACCESS_KEY[regin], AWS_SES_ACCESS_REGION)
  285. bucket_name = 'ansjerfilemanager'
  286. if campaign.image_url:
  287. s3.delete_obj(bucket_name, f"app/campaign/{campaign.image_url}")
  288. # 清除多对多关系
  289. campaign.device_type.clear()
  290. campaign.country.clear()
  291. # 保留在广告表中
  292. campaign.status = 2
  293. campaign.update_time = int(time.time())
  294. campaign.save()
  295. return response.json(0)
  296. except Exception as e:
  297. return response.json(176)
  298. def get_timezone_offset(self, tz):
  299. """
  300. UTC-8格式转化
  301. """
  302. sign = tz[3] # "+" 或 "-"
  303. hours = int(tz[4:]) # 时差小时数
  304. timezone_name = f"Etc/GMT{sign}{hours}" if sign == "-" else f"Etc/GMT+{hours}"
  305. return pytz.timezone(timezone_name)
  306. def app_get_campaigns(cls, request_dict, response):
  307. """
  308. APP获取广告活动列表
  309. @param request_dict: 请求参数
  310. @param response: 响应对象
  311. @return: 响应对象
  312. """
  313. tz = request_dict.get('tz', 'UTC+0')
  314. token = request_dict.get('token', None)
  315. if not token:
  316. return response.json(444)
  317. token = TokenObject(token)
  318. if token.code != 0:
  319. return response.json(token.code)
  320. user_id = token.userID
  321. if SERVER_TYPE == 'Ansjer.cn_config.formal_settings' or SERVER_TYPE == 'Ansjer.cn_config.test_settings':
  322. s3_url = "https://ansjerfilemanager.s3.cn-northwest-1.amazonaws.com.cn/app/campaign/"
  323. else:
  324. s3_url = "https://ansjerfilemanager.s3.amazonaws.com/app/campaign/"
  325. # 当日时间戳区间获取
  326. timezone = cls.get_timezone_offset(tz)
  327. current_date = datetime.now().date()
  328. start_of_day = datetime.combine(current_date, Time.min)
  329. end_of_day = datetime.combine(current_date, Time.max)
  330. start_timestamp = int(start_of_day.timestamp())
  331. end_timestamp = int(end_of_day.timestamp())
  332. open_screen_campaign_qs = OpenScreenCampaign.objects.filter(
  333. user_id=user_id,
  334. create_time__gt=start_timestamp,
  335. create_time__lt=end_timestamp
  336. )
  337. if not open_screen_campaign_qs.exists():
  338. OpenScreenCampaign.objects.create(user_id=user_id,
  339. create_time=int(time.time()))
  340. try:
  341. country_prefetch = Prefetch('country', queryset=CountryModel.objects.only('id'), to_attr='country_list')
  342. device_type_prefetch = Prefetch('device_type', queryset=DeviceTypeModel.objects.only('type'),
  343. to_attr='device_type_list')
  344. app_advertise_campaign_qs = AppAdvertiseCampaign.objects.prefetch_related(country_prefetch,
  345. device_type_prefetch)
  346. device_info_qs = Device_User.objects.filter(userID=user_id).values("region_country").first()
  347. country_id = device_info_qs.get('region_country')
  348. if country_id != 0:
  349. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(country__id=country_id, status=1)
  350. else:
  351. app_advertise_campaign_qs = app_advertise_campaign_qs.filter(status=1, unknown_country=1)
  352. # 返回 广告名称、广告类型、开始时间、结束时间、广告图片、活动链接
  353. campaigns_list = []
  354. for campaign in app_advertise_campaign_qs:
  355. # 时区处理
  356. campaigns_start_date = (datetime.utcfromtimestamp(campaign.campaign_start_date)
  357. .replace(tzinfo=pytz.utc).astimezone(timezone).strftime('%Y-%m-%d'))
  358. campaign_end_date = (datetime.utcfromtimestamp(campaign.campaign_end_date)
  359. .replace(tzinfo=pytz.utc).astimezone(timezone).strftime('%Y-%m-%d'))
  360. campaign_start_firstday = (
  361. datetime.utcfromtimestamp(campaign.campaign_start_date + campaign.campaign_show_stime)
  362. .replace(tzinfo=pytz.utc).astimezone(timezone).strftime('%Y-%m-%d %H:%M'))
  363. campaign_end_firstday = (
  364. datetime.utcfromtimestamp(campaign.campaign_start_date + campaign.campaign_show_etime)
  365. .replace(tzinfo=pytz.utc).astimezone(timezone).strftime('%Y-%m-%d %H:%M'))
  366. campaigns_list.append({
  367. 'campaign_id': campaign.id,
  368. 'image_url': s3_url + campaign.image_url,
  369. 'campaign_url': campaign.campaign_url,
  370. 'campaign_name': campaign.campaign_name,
  371. 'campaign_type': campaign.campaign_type,
  372. 'campaign_date': f"{campaigns_start_date},{campaign_end_date}",
  373. 'campaign_start_oneday': campaign_start_firstday,
  374. 'campaign_end_oneday': campaign_end_firstday,
  375. 'device_types': [device.type for device in campaign.device_type_list],
  376. })
  377. return response.json(0, {
  378. 'campaigns': campaigns_list
  379. })
  380. except Exception as e:
  381. return response.json(173)
  382. def record_user_behavior(cls, request_dict, response):
  383. """
  384. 记录用户行为
  385. @param request_dict: 请求参数
  386. @param response: 响应对象
  387. @return: 响应对象
  388. """
  389. status = request_dict.get('status', None) # 1.未跳过 2.已跳过 3.点击广告
  390. campaign_id = request_dict.get('campaign_id', None)
  391. token = request_dict.get('token', None)
  392. if not token:
  393. return response.json(444)
  394. token = TokenObject(token)
  395. if token.code != 0:
  396. return response.json(token.code)
  397. user_id = token.userID
  398. if not all([status, campaign_id]):
  399. return response.json(444)
  400. # 当日时间(各个时区都不同)戳区间获取
  401. current_date = datetime.now().date()
  402. start_of_day = datetime.combine(current_date, Time.min)
  403. end_of_day = datetime.combine(current_date, Time.max)
  404. start_timestamp = int(start_of_day.timestamp())
  405. end_timestamp = int(end_of_day.timestamp())
  406. try:
  407. # 筛选符合条件的记录
  408. open_screen_campaigns = OpenScreenCampaign.objects.filter(
  409. user_id=user_id,
  410. create_time__gt=start_timestamp,
  411. create_time__lt=end_timestamp,
  412. status=0,
  413. )
  414. # 检查是否存在记录
  415. if not open_screen_campaigns.exists():
  416. # 如果不存在,则创建新记录
  417. OpenScreenCampaign.objects.create(
  418. user_id=user_id,
  419. status=status,
  420. campaign_id_id=campaign_id,
  421. create_time=int(time.time()),
  422. update_time=int(time.time())
  423. )
  424. else:
  425. # 如果存在,则更新最新的记录
  426. latest_campaign = open_screen_campaigns.latest("create_time")
  427. latest_campaign.status = status
  428. latest_campaign.campaign_id_id = campaign_id
  429. latest_campaign.update_time = int(time.time())
  430. latest_campaign.save()
  431. except Exception as e:
  432. return response.json(177, {'error': '更新错误'})
  433. return response.json(0)
  434. def get_user_behavior_log(self, request_dict, response):
  435. status = request_dict.get('status', None)
  436. user_id = request_dict.get('user_id', None)
  437. behavior_stime = request_dict.get('start_time', None)
  438. behavior_etime = request_dict.get('end_time', None)
  439. page = request_dict.get('page', 1) # 默认为第一页
  440. page_size = request_dict.get('page_size', 10) # 默认每页显示10条记录
  441. behavior_log_list = []
  442. open_screen_campaigns = OpenScreenCampaign.objects.all()
  443. if user_id is not None:
  444. open_screen_campaigns = open_screen_campaigns.filter(user_id=user_id)
  445. if status is not None:
  446. open_screen_campaigns = open_screen_campaigns.filter(status=status)
  447. # 时间过滤
  448. if behavior_stime is not None and behavior_etime is not None:
  449. open_screen_campaigns = open_screen_campaigns.filter(update_time__range=[behavior_stime, behavior_etime])
  450. elif behavior_stime is not None:
  451. open_screen_campaigns = open_screen_campaigns.filter(update_time__gte=behavior_stime)
  452. elif behavior_etime is not None:
  453. open_screen_campaigns = open_screen_campaigns.filter(update_time__lte=behavior_etime)
  454. if not open_screen_campaigns.exists():
  455. return response.json(0, {"behavior_log_list": [], "total": 0})
  456. # 分页
  457. open_screen_campaigns = open_screen_campaigns.order_by('id')
  458. paginator = Paginator(open_screen_campaigns, page_size)
  459. open_screen_campaigns_page = paginator.page(page)
  460. for campaign in open_screen_campaigns_page:
  461. campaign_name = campaign.campaign_id.campaign_name if campaign.campaign_id else '广告不存在'
  462. behavior_log_list.append({
  463. "campaign_name": campaign_name,
  464. "user_id": campaign.user_id,
  465. "status": campaign.status,
  466. "behavior_time": campaign.update_time
  467. })
  468. return response.json(0, {"behavior_log_list": behavior_log_list,
  469. "total": paginator.count})