UnicomComboTaskController.py 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. # -*- encoding: utf-8 -*-
  2. """
  3. @File : UnicomComboTaskController.py
  4. @Time : 2022/6/30 16:23
  5. @Author : stephen
  6. @Email : zhangdongming@asj6.wecom.work
  7. @Software: PyCharm
  8. """
  9. import datetime
  10. import json
  11. import logging
  12. import threading
  13. import time
  14. from decimal import Decimal
  15. from django.core.paginator import Paginator
  16. from django.db import transaction
  17. from django.db.models import Q
  18. from django.views import View
  19. from Model.models import UnicomComboOrderInfo, UnicomCombo, Order_Model, UnicomDeviceInfo, UnicomFlowPush, \
  20. IotCardUsageHistory, AccessNumberTaskQueue, IotCardOrderUsageHistory, OperatingCosts
  21. from Object.RedisObject import RedisObject
  22. from Object.ResponseObject import ResponseObject
  23. from Object.TelecomObject import TelecomObject
  24. from Object.UnicomObject import UnicomObjeect
  25. from Object.utils import LocalDateTimeUtil
  26. from Service.TelecomService import TelecomService
  27. logger = logging.getLogger('info')
  28. class UnicomComboTaskView(View):
  29. def get(self, request, *args, **kwargs):
  30. request.encoding = 'utf-8'
  31. operation = kwargs.get('operation')
  32. return self.validation(request.GET, request, operation)
  33. def post(self, request, *args, **kwargs):
  34. request.encoding = 'utf-8'
  35. operation = kwargs.get('operation')
  36. return self.validation(request.POST, request, operation)
  37. def validation(self, request_dict, request, operation):
  38. response = ResponseObject()
  39. print(request)
  40. if operation == 'check-activate':
  41. return self.check_activate_combo(request_dict, response)
  42. elif operation == 'check-flow':
  43. return self.check_flow_usage(response)
  44. elif operation == 'check-flow-expire':
  45. return self.check_flow_expire(response)
  46. elif operation == 'check-expire':
  47. today = datetime.datetime.today()
  48. year = today.year
  49. month = today.month
  50. self.query_unused_combo_and_activate(request_dict.get('iccid'), year, month, '666')
  51. return response.json(0)
  52. elif operation == 'updateFlowUsed': # 更新流量使用
  53. self.unicom_flow_used(request_dict, response)
  54. return response.json(0)
  55. elif operation == 'queryFlowUsedHistory':
  56. return self.query_flow_used_history(response)
  57. elif operation == 'queryFlowCache':
  58. return self.query_flow_cache(response)
  59. elif operation == 'getDeviceUsageHistory':
  60. return self.get_device_usage_history(response)
  61. elif operation == 'updateCardCycleFlow':
  62. return self.update_device_cycle_flow(response)
  63. elif operation == 'executeAccessTask':
  64. return self.get_access_number_change_task(response)
  65. elif operation == 'queryTotalTrafficToday':
  66. return self.query_total_traffic_today(response)
  67. elif operation == 'createMonthlyCost':
  68. return self.query_4G_order_info(response)
  69. else:
  70. return response.json(414)
  71. @classmethod
  72. def check_activate_combo(cls, request_dict, response):
  73. """
  74. 定时检查是否有次月激活套餐
  75. @param request_dict:
  76. @param response:
  77. @return:
  78. """
  79. print(request_dict)
  80. logger.info('--->进入监控次月激活联通套餐')
  81. now_time = int(time.time())
  82. combo_order_info_qs = UnicomComboOrderInfo.objects.filter(status=0, next_month_activate=True,
  83. activation_time__lte=now_time,
  84. expire_time__gte=now_time, is_del=0).values()
  85. if not combo_order_info_qs.exists():
  86. return response.json(0)
  87. try:
  88. today = datetime.datetime.today()
  89. year = today.year
  90. month = today.month
  91. with transaction.atomic():
  92. unicom_api = UnicomObjeect()
  93. for item in combo_order_info_qs:
  94. if item['order_id']:
  95. order_id = item['order_id']
  96. order_qs = Order_Model.objects.filter(orderID=order_id, status=1)
  97. if not order_qs.exists():
  98. continue
  99. combo_order_qs = UnicomComboOrderInfo.objects.filter(status=1, iccid=item['iccid'])
  100. # 当前已有套餐正在使用则跳出当前循环
  101. if combo_order_qs.exists():
  102. continue
  103. combo_id = item['combo_id']
  104. combo_qs = UnicomCombo.objects.filter(id=combo_id).values()
  105. if not combo_qs.exists():
  106. continue
  107. # 查询当月用量情况
  108. sim_card_qs = UnicomDeviceInfo.objects.filter(iccid=iccid).values('card_type')
  109. card_type = 0
  110. if sim_card_qs.exists():
  111. card_type = sim_card_qs[0]['card_type']
  112. flow_total_usage = unicom_api.get_flow_usage_total(item['iccid'], card_type)
  113. flow_total_usage = Decimal(flow_total_usage).quantize(
  114. Decimal('0.00')) if flow_total_usage > 0 else 0
  115. flow_total_usage = str(flow_total_usage)
  116. iccid = item['iccid']
  117. # 检查激活iccid
  118. unicom_api.change_device_to_activate(iccid)
  119. cls.query_unused_combo_and_activate(iccid, year, month, flow_total_usage)
  120. logger.info('激活成功,订单编号:{}'.format(order_id))
  121. return response.json(0)
  122. except Exception as e:
  123. logger.info('出错了~次月激活套餐异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  124. return response.json(177, repr(e))
  125. @classmethod
  126. def check_flow_usage(cls, response):
  127. """
  128. 检查流量使用情况
  129. @return:
  130. """
  131. logger.info('--->进入监控流量使用情况')
  132. try:
  133. combo_order_qs = UnicomComboOrderInfo.objects.filter(status=1, is_del=False, combo__is_unlimited=0).values()
  134. if not combo_order_qs.exists():
  135. return response.json(0)
  136. asy = threading.Thread(target=UnicomComboTaskView.async_monitoring_flow, args=(combo_order_qs,))
  137. asy.start()
  138. return response.json(0)
  139. except Exception as e:
  140. logger.info('出错了~检测流量用量详情异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  141. return response.json(177, repr(e))
  142. @classmethod
  143. def async_monitoring_flow(cls, combo_order_qs):
  144. """
  145. 异步检测流量使用详情
  146. """
  147. try:
  148. unicom_api = UnicomObjeect()
  149. today = datetime.datetime.today()
  150. year = today.year
  151. month = today.month
  152. now_time = int(time.time())
  153. for item in combo_order_qs:
  154. iccid = item['iccid']
  155. u_device_info_qs = UnicomDeviceInfo.objects.filter(iccid=iccid)
  156. if not u_device_info_qs.exists():
  157. continue
  158. u_device_info_qs = u_device_info_qs.first()
  159. card_type = u_device_info_qs.card_type
  160. activate_usage_flow = float(item['flow_total_usage']) if item['flow_total_usage'] else 0.0
  161. combo_id = item['combo_id']
  162. combo_qs = UnicomCombo.objects.filter(id=combo_id).values()
  163. if not combo_qs.exists():
  164. continue
  165. combo_qs = combo_qs.first()
  166. flow_total = combo_qs['flow_total']
  167. # 队列已使用总流量总量
  168. flow_total_usage = unicom_api.get_flow_usage_total(iccid, card_type)
  169. flow_total_usage = float(flow_total_usage)
  170. is_expire = False
  171. flow = activate_usage_flow + flow_total # 激活套餐时ICCID历史用量+当前套餐流量
  172. if flow_total_usage > 0:
  173. # 初始套餐已使用流量 + 套餐总流量
  174. if flow_total_usage >= flow:
  175. is_expire = True
  176. usage = (flow_total_usage - activate_usage_flow) if flow_total_usage > activate_usage_flow else 0
  177. cls.flow_warning_push(u_device_info_qs.user_id, u_device_info_qs.serial_no, item['id'], flow_total,
  178. usage)
  179. # 已达量,检查是否有当月未使用套餐 没有则停卡
  180. if is_expire:
  181. flow_exceed = flow_total_usage - flow
  182. UnicomComboOrderInfo.objects.filter(id=item['id']) \
  183. .update(status=2, updated_time=now_time, flow_exceed=flow_exceed)
  184. activate_status = cls.query_unused_combo_and_activate(iccid, year, month,
  185. flow_total_usage)
  186. logger.info('-->当前卡{}流量已用完,是否有生效套餐:{}'.format(iccid, activate_status))
  187. if not activate_status: # 停用或断网
  188. if card_type == 3: # 鼎芯电信
  189. TelecomService().update_access_number_network(iccid, u_device_info_qs.access_number, 'ADD',
  190. '套餐流量已用完')
  191. else:
  192. unicom_api.change_device_to_disable(iccid=iccid, card_type=card_type, reason='套餐流量已用完')
  193. except Exception as e:
  194. logger.info('异步~检测流量用量详情异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  195. @staticmethod
  196. def flow_warning_push(app_user_id, serial_no, combo_order_id, flow_total, flow_usage):
  197. """
  198. 监控流量使用大于85%and小于96%进行消息推送提醒
  199. @param app_user_id: app用户id
  200. @param serial_no: 序列号
  201. @param combo_order_id: 当前套餐订单id
  202. @param flow_total: 套餐流量总量
  203. @param flow_usage: 套餐已使用流量
  204. @return:
  205. """
  206. try:
  207. if not app_user_id:
  208. return False
  209. now_time = int(time.time())
  210. push_data = {'combo_order_id': str(combo_order_id), 'serial_no': serial_no,
  211. 'flow_total_usage': flow_usage, 'flow_total': flow_total, 'status': 0,
  212. 'updated_time': now_time,
  213. 'created_time': now_time, 'user_id': app_user_id}
  214. if 0 < flow_total and 0 < flow_usage < flow_total:
  215. res = flow_usage / flow_total * 100
  216. if 85 < res <= 95:
  217. flow_push = UnicomFlowPush.objects.filter(serial_no=serial_no, combo_order_id=combo_order_id)
  218. if not flow_push.exists():
  219. UnicomFlowPush.objects.create(**push_data)
  220. elif flow_usage >= flow_total:
  221. push_data['flow_total_usage'] = flow_total
  222. push_data['type'] = 1
  223. UnicomFlowPush.objects.create(**push_data)
  224. return True
  225. except Exception as e:
  226. logger.info('出错了~异常流量监控,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  227. @staticmethod
  228. def query_unused_combo_and_activate(iccid, year, month, usage_flow):
  229. """
  230. 查询未使用套餐并激活
  231. @param iccid:
  232. @param year:
  233. @param month:
  234. @param usage_flow:
  235. @return:
  236. """
  237. try:
  238. now_time = int(time.time())
  239. combo_order_qs = UnicomComboOrderInfo.objects \
  240. .filter(expire_time__gt=now_time, activation_time__lte=now_time, status=0, iccid=iccid) \
  241. .order_by('created_time')
  242. if not combo_order_qs.exists():
  243. return False
  244. combo_order = combo_order_qs.first()
  245. if not combo_order.order_id:
  246. return False
  247. order_qs = Order_Model.objects.filter(orderID=combo_order.order_id, status=1)
  248. if not order_qs.exists():
  249. return False
  250. upd_data = {
  251. 'status': 1,
  252. 'year': year,
  253. 'month': month,
  254. 'flow_total_usage': str(usage_flow),
  255. 'activation_time': now_time,
  256. 'updated_time': now_time,
  257. }
  258. UnicomComboOrderInfo.objects.filter(id=combo_order.id).update(**upd_data)
  259. asy = threading.Thread(target=UnicomComboTaskView.async_combo_sys_msg_push,
  260. args=(iccid, combo_order.id, 3))
  261. asy.start()
  262. return True
  263. except Exception as e:
  264. logger.info('出错了~激活套餐,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  265. return False
  266. @classmethod
  267. def check_flow_expire(cls, response):
  268. """
  269. 检查流量到期停卡操作
  270. @param response:
  271. @return:
  272. """
  273. logger.info('check_flow_expire进入监控流量到期停卡或激活叠加包')
  274. now_time = int(time.time())
  275. combo_order_qs = UnicomComboOrderInfo.objects.filter(~Q(status=2), expire_time__lte=now_time,
  276. is_del=False).values()
  277. if not combo_order_qs.exists():
  278. return response.json(0)
  279. asy = threading.Thread(target=UnicomComboTaskView.async_deactivate_expire_package,
  280. args=(combo_order_qs, now_time))
  281. asy.start()
  282. return response.json(0)
  283. @staticmethod
  284. def async_deactivate_expire_package(combo_order_qs, now_time):
  285. """
  286. 异步停用过期套餐
  287. @param combo_order_qs: 过期套餐querySet
  288. @param now_time 当前实际
  289. """
  290. today = datetime.datetime.today()
  291. year = today.year
  292. month = today.month
  293. iccid_with_type = []
  294. for item in combo_order_qs:
  295. try:
  296. icc_id = item['iccid']
  297. um_device_qs = UnicomDeviceInfo.objects.filter(iccid=icc_id)
  298. if not um_device_qs.exists():
  299. continue
  300. um_device = um_device_qs.first()
  301. card_type = um_device.card_type # 这里获取card_type
  302. UnicomComboOrderInfo.objects.filter(id=item['id']).update(status=2, updated_time=now_time)
  303. iccid_with_type.append((icc_id, card_type)) # 存入元组
  304. logger.info('修改套餐已失效成功,iccid:{}'.format(icc_id))
  305. except Exception as e:
  306. logger.info('async_deactivate_expire_package套餐过期修改失效异常,{}'
  307. 'errLine:{}, errMsg:{}'.format(item['iccid'], e.__traceback__.tb_lineno, repr(e)))
  308. continue
  309. # set无序不重复元素集
  310. unique_iccid_type = {iccid: card_type for iccid, card_type in iccid_with_type}
  311. iccid_with_type = list(unique_iccid_type.items())
  312. unicom_api = UnicomObjeect()
  313. for item, card_type in iccid_with_type:
  314. try:
  315. activate_combo_qs = UnicomComboOrderInfo.objects.filter(iccid=item, status=1, expire_time__gt=now_time,
  316. is_del=False).values()
  317. if activate_combo_qs.exists():
  318. continue
  319. usage_flow = unicom_api.get_flow_usage_total(item, card_type)
  320. # 查询是否有未使用套餐,有则进行激活 否 则调用API停卡
  321. result = UnicomComboTaskView().query_unused_combo_and_activate(item, year, month, usage_flow)
  322. if not result: # 没有可用套餐进行停卡
  323. # 停用设备
  324. unicom_api.change_device_to_disable(iccid=item, card_type=card_type, reason='没有可用套餐')
  325. logger.info('调用停卡API successful,iccid:{}'.format(item))
  326. combo_order_info_qs = UnicomComboOrderInfo.objects.filter(iccid=item, status=2) \
  327. .values('id').order_by('-updated_time')
  328. combo_order = combo_order_info_qs.first()
  329. asy = threading.Thread(target=UnicomComboTaskView.async_combo_sys_msg_push,
  330. args=(item, combo_order['id'], 4))
  331. asy.start()
  332. else:
  333. unicom_api.change_device_to_activate(item)
  334. except Exception as e:
  335. logger.info('async_deactivate_expire_package套餐过期停卡异常,{}'
  336. 'errLine:{}, errMsg:{}'.format(item, e.__traceback__.tb_lineno, repr(e)))
  337. continue
  338. @staticmethod
  339. def async_combo_sys_msg_push(iccid, combo_order_id, push_type):
  340. """
  341. 异步保存消息推送 激活|过期
  342. @param iccid:
  343. @param combo_order_id:
  344. @param push_type:
  345. @return:
  346. """
  347. try:
  348. now_time = int(time.time())
  349. ud_info_qs = UnicomDeviceInfo.objects.filter(iccid=iccid).values('serial_no', 'user_id')
  350. push_data = {'combo_order_id': str(combo_order_id), 'serial_no': ud_info_qs.first()['serial_no'],
  351. 'status': 0, 'type': push_type,
  352. 'updated_time': now_time,
  353. 'created_time': now_time, 'user_id': ud_info_qs.first()['user_id']}
  354. UnicomFlowPush.objects.create(**push_data)
  355. except Exception as e:
  356. logger.info('-->出错了~,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  357. @staticmethod
  358. def unicom_flow_used(request_dict, response):
  359. """
  360. 查询设备每张卡流量使用情况
  361. @param request_dict:
  362. @param response:
  363. @return:
  364. """
  365. page_size = int(request_dict.get('pageSize', 1))
  366. device_count = UnicomDeviceInfo.objects.filter(card_type=0).count()
  367. total_pages = device_count // page_size + (device_count % page_size > 0) # 计算总页数
  368. for page_number in range(1, total_pages + 1):
  369. u_device_qs = UnicomDeviceInfo.objects.filter(card_type=0).values('id', 'iccid', 'sim_used_flow', 'card_type').order_by(
  370. '-created_time')[(page_number - 1) * page_size:page_number * page_size]
  371. asy = threading.Thread(target=UnicomComboTaskView.thread_collect_flow_used, args=(u_device_qs,))
  372. asy.start()
  373. return response.json(0)
  374. @staticmethod
  375. def thread_collect_flow_used(u_device_qs):
  376. for item in u_device_qs:
  377. try:
  378. unicom_api = UnicomObjeect()
  379. n_time = int(time.time())
  380. # 队列已使用总流量总量
  381. flow_total_usage = unicom_api.get_flow_usage_total(item['iccid'], item['card_type'])
  382. UnicomDeviceInfo.objects.filter(id=item['id']).update(updated_time=n_time,
  383. sim_used_flow=flow_total_usage)
  384. except Exception as e:
  385. print(repr(e))
  386. continue
  387. @classmethod
  388. def query_flow_used_history(cls, response):
  389. # 获取符合条件的卡片对象查询集,并按创建时间升序排序
  390. card_qs = UnicomDeviceInfo.objects.filter(card_type=0).values('iccid').order_by('created_time')
  391. if not card_qs.exists():
  392. return response.json(0)
  393. asy = threading.Thread(target=UnicomComboTaskView.async_bulk_create_usage_history, args=(card_qs,))
  394. asy.start()
  395. return response.json(0)
  396. @staticmethod
  397. def async_bulk_create_usage_history(qs):
  398. """
  399. 异步批量创建流量用量历史记录
  400. """
  401. redis_obj = RedisObject()
  402. current_time = int(time.time()) # 获取当前时间戳
  403. for item in qs:
  404. iccid = item['iccid']
  405. key = 'monthly_flow_' + iccid
  406. flow_dict = redis_obj.get_all_hash_data(key) # 获取Redis中指定键的哈希表数据
  407. iot_card_list = [] # 创建一个空的列表,用于批量创建IotCardUsageHistory对象
  408. for k, v in flow_dict.items():
  409. try:
  410. cycle = datetime.datetime.strptime(str(k.decode()), '%Y-%m') # 将字符串日期解析为datetime类型
  411. flow = float(v)
  412. iot_card_list.append(IotCardUsageHistory(
  413. iccid=iccid,
  414. card_type=1,
  415. cycle=int(cycle.strftime('%Y%m')), # 将日期转换为整数形式,如202201
  416. flow_total_usage=flow,
  417. created_time=current_time,
  418. updated_time=current_time
  419. ))
  420. except Exception as e:
  421. print(repr(e))
  422. continue
  423. # 批量创建IotCardUsageHistory对象
  424. if iot_card_list:
  425. IotCardUsageHistory.objects.bulk_create(iot_card_list)
  426. @classmethod
  427. def query_flow_cache(cls, response):
  428. """
  429. 查询流量缓存永久的将设置过期时间为10分钟
  430. """
  431. redis = RedisObject()
  432. try:
  433. res = redis.get_keys('ASJ:UNICOM:FLOW:*')
  434. keys = [key.decode() for key in res]
  435. # 进行进一步的处理或打印
  436. for key in keys:
  437. ttl = redis.get_ttl(key)
  438. if ttl == -1:
  439. logger.info('iccidFlow:{}'.format(key))
  440. redis.CONN.expire(key, 60 * 10)
  441. return response.json(0)
  442. except Exception as e:
  443. logger.info('出错了~次月激活套餐异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  444. return response.json(177, repr(e))
  445. @staticmethod
  446. def get_device_usage_history(response):
  447. """
  448. 查询联通设备历史用量
  449. """
  450. card_qs = UnicomDeviceInfo.objects.filter(card_type=0).values('iccid')
  451. if not card_qs.exists():
  452. return response.json(0)
  453. asy = threading.Thread(target=UnicomComboTaskView.async_update_device_usage_history, args=(card_qs,))
  454. asy.start()
  455. return response.json(0)
  456. @staticmethod
  457. def async_update_device_usage_history(qs):
  458. """
  459. 异步更新设备用量历史
  460. @param qs: 联通iccid集合
  461. """
  462. try:
  463. u_service = UnicomObjeect()
  464. iot_card_list = []
  465. now_time = int(time.time())
  466. # 获取当前时间
  467. current_date = datetime.datetime.now()
  468. # 计算上个月的时间
  469. last_month_date = current_date - datetime.timedelta(days=current_date.day)
  470. # 例格式化日期为 "202307"
  471. formatted_date = last_month_date.strftime('%Y%m')
  472. for item in qs:
  473. params = {'iccid': item['iccid']}
  474. result = u_service.query_device_usage_history(**params)
  475. res_dict = u_service.get_text_dict(result)
  476. if res_dict['code'] == 0 and res_dict['data']:
  477. for cycle in res_dict['data']:
  478. if cycle['flowTotalUsage'] <= 0 or cycle['cycle'] != int(formatted_date):
  479. continue
  480. iot_card_list.append(IotCardUsageHistory(
  481. iccid=item['iccid'],
  482. card_type=1,
  483. cycle=cycle['cycle'],
  484. flow_total_usage=cycle['flowTotalUsage'],
  485. created_time=now_time,
  486. updated_time=now_time
  487. ))
  488. if not iot_card_list:
  489. return None
  490. # 使用Django的Paginator进行分页
  491. paginator = Paginator(iot_card_list, 300)
  492. for page_num in range(1, paginator.num_pages + 1):
  493. IotCardUsageHistory.objects.bulk_create(paginator.page(page_num).object_list)
  494. except Exception as e:
  495. logger.info('查询账期流量异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  496. return None
  497. @staticmethod
  498. def update_device_cycle_flow(response):
  499. """
  500. 更新设备账期流量接口
  501. """
  502. card_qs = UnicomDeviceInfo.objects.filter(card_type=0, status=2).values('iccid', 'card_type')
  503. if not card_qs.exists():
  504. return response.json(0)
  505. logger.info('总数:{}'.format(card_qs.count()))
  506. asy = threading.Thread(target=UnicomComboTaskView.async_update_device_cycle_flow, args=(card_qs,))
  507. asy.start()
  508. return response.json(0)
  509. @staticmethod
  510. def async_update_device_cycle_flow(qs):
  511. """
  512. 异步更新设备账期流量
  513. """
  514. try:
  515. logger.info('进入异步更新设备卡账期流量~~~~')
  516. unicom_api = UnicomObjeect()
  517. for item in qs:
  518. try:
  519. unicom_api.get_flow_usage_total(item['iccid'], item['card_type'])
  520. except Exception as e:
  521. logger.error(f"{item['iccid']}异步更新设备账期流量异常,errLine:{e.__traceback__.tb_lineno}, errMsg:{str(e)}")
  522. continue
  523. except Exception as e:
  524. logger.info('更新账期流量异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  525. @staticmethod
  526. def get_access_number_change_task(response):
  527. """
  528. 获取接入号码变更任务
  529. """
  530. task_qs = AccessNumberTaskQueue.objects.exclude(status=1).filter(count__lt=4).order_by('created_time')
  531. if not task_qs.exists():
  532. return response.json(0)
  533. logger.info('接入卡号变更任务总数:{}'.format(task_qs.count()))
  534. asy = threading.Thread(target=UnicomComboTaskView.async_update_access_number_status, args=(task_qs,))
  535. asy.start()
  536. return response.json(0)
  537. @staticmethod
  538. def async_update_access_number_status(qs):
  539. """
  540. 异步更新设备账期流量
  541. """
  542. try:
  543. logger.info('进入异步更新接入卡号网络状态~~~~')
  544. redis = RedisObject()
  545. iccid_list = []
  546. for item in qs:
  547. try:
  548. key = f'ASJ:TELECOM:CHANGE:{item.iccid}'
  549. change_data = redis.get_data(key)
  550. if change_data:
  551. iccid_list.append(item.iccid)
  552. continue
  553. if item.iccid in iccid_list: # 避免同一ICCID多次执行
  554. continue
  555. action_value = 'DEL' if item.action == 2 else 'ADD'
  556. result = TelecomObject().single_cut_net(item.access_number, action_value) # 变更网络状态
  557. now_time = int(time.time())
  558. count = item.count + 1
  559. cache_data = {'iccid': item.iccid, 'actionValue': action_value}
  560. if not result: # 失败修改执行次数
  561. iccid_list.append(item.iccid)
  562. data = {'status': 2, 'count': count, 'updated_time': now_time}
  563. AccessNumberTaskQueue.objects.filter(id=item.id).update(**data)
  564. redis.CONN.setnx(key, json.dumps(cache_data))
  565. redis.CONN.expire(key, 130) # 130 秒不再变更当前ICCID网络状态
  566. continue
  567. data = {'status': 1, 'count': count, 'completion_time': now_time, 'updated_time': now_time}
  568. if result == '-5': # 已符合变更后状态
  569. data['result'] = {'-5': '该号码未订购单独断网功能!;流水号:1000000190202402201640359500'}
  570. else:
  571. data['result'] = result
  572. iccid_list.append(item.iccid)
  573. AccessNumberTaskQueue.objects.filter(id=item.id).update(**data) # 成功后修改任务记录状态
  574. redis.CONN.setnx(key, json.dumps(cache_data))
  575. redis.CONN.expire(key, 130) # 130 秒不再变更当前ICCID网络状态
  576. except Exception as e:
  577. print(repr(e))
  578. continue
  579. except Exception as e:
  580. logger.info('异步变更卡网络状态异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  581. @staticmethod
  582. def query_total_traffic_today(response):
  583. """
  584. 4G订单查询今日总流量消耗
  585. @return:
  586. """
  587. asy = threading.Thread(target=UnicomComboTaskView.async_save_today_use_float)
  588. asy.start()
  589. logger.info('查询4G订单总消耗流量记录')
  590. return response.json(0)
  591. @staticmethod
  592. def async_save_today_use_float():
  593. # 查询正在使用套餐并且大于等于2024年激活 不等于免费测试流量的订单数据
  594. today = datetime.datetime.today()
  595. n_data = today.strftime('%Y%m%d')
  596. start_time, end_time = LocalDateTimeUtil.get_start_and_end_time(today.strftime('%Y-%m-%d'), '%Y-%m-%d')
  597. o_combo_qs = UnicomComboOrderInfo.objects \
  598. .filter(status=1, year__gte=2024, created_time__gte=1714492800, created_time__lt=start_time) \
  599. .exclude(combo__combo_type=4) \
  600. .values('order_id', 'iccid', 'flow_total_usage')
  601. try:
  602. unicom_api = UnicomObjeect()
  603. if not o_combo_qs.exists():
  604. return
  605. n_time = int(time.time())
  606. day = n_data[-2:]
  607. iccid_list = []
  608. for item in o_combo_qs:
  609. iccid = item['iccid']
  610. try:
  611. sim_card_qs = UnicomDeviceInfo.objects.filter(iccid=iccid).values('card_type')
  612. card_type = 0
  613. if sim_card_qs.exists():
  614. card_type = sim_card_qs[0]['card_type']
  615. flow = float(unicom_api.get_flow_usage_total(iccid, card_type))
  616. if flow == 0:
  617. continue
  618. old_flow = float(item['flow_total_usage'])
  619. if flow == old_flow:
  620. continue
  621. total_usage = Decimal(flow - old_flow).quantize(Decimal('0.00'))
  622. iccid_list.append(IotCardOrderUsageHistory(
  623. iccid=iccid,
  624. order_id=item['order_id'],
  625. card_type=1,
  626. cycle=int(n_data), # 将日期转换为整数形式,如202201
  627. cycle_date=int(day),
  628. total_traffic=total_usage,
  629. created_time=n_time,
  630. updated_time=n_time
  631. ))
  632. if len(iccid_list) >= 300:
  633. IotCardOrderUsageHistory.objects.bulk_create(iccid_list)
  634. iccid_list = []
  635. except Exception as e:
  636. logger.error(
  637. '查询日用量异常iccid{},errLine:{}, errMsg:{}'.format(iccid, e.__traceback__.tb_lineno, repr(e)))
  638. if iccid_list:
  639. IotCardOrderUsageHistory.objects.bulk_create(iccid_list)
  640. except Exception as e:
  641. logger.error('统计4G卡日用量异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  642. @classmethod
  643. def query_4G_order_info(cls, response):
  644. # 获取当前月的开始时间戳
  645. first_timestamp, last_timestamp = cls.get_current_month_range()
  646. # 统计条件失效时间大于当月开始时间(6月成本账单)统计范围订单创建时间大于5月1号的套餐
  647. # 查询APP上显示的套餐
  648. u_order_qs = UnicomComboOrderInfo.objects.filter(expire_time__gte=first_timestamp, combo__is_show=True
  649. , created_time__gte=1714492800) \
  650. .exclude(combo_id__in=[24, 26, 34]).values('status', 'combo__expiration_days', 'flow_total_usage',
  651. 'combo__flow_total', 'order_id',
  652. 'activation_time', 'expire_time', 'created_time', 'iccid'
  653. ).order_by('created_time')
  654. asy = threading.Thread(target=UnicomComboTaskView.created_flow_monthly_cost,
  655. args=(u_order_qs, first_timestamp, last_timestamp))
  656. asy.start()
  657. print(datetime.datetime.today())
  658. print(u_order_qs.count())
  659. # 查询APP不显示购买的套餐 执行异步
  660. u_order_qs = UnicomComboOrderInfo.objects.filter(expire_time__gte=first_timestamp, combo__is_show=False
  661. , created_time__gte=1714492800) \
  662. .exclude(combo_id__in=[24, 26, 34]).values('status', 'combo__expiration_days', 'flow_total_usage',
  663. 'combo__flow_total', 'order_id',
  664. 'activation_time', 'expire_time', 'created_time', 'iccid'
  665. ).order_by('created_time')
  666. asy = threading.Thread(target=UnicomComboTaskView.created_flow_monthly_cost,
  667. args=(u_order_qs, first_timestamp, last_timestamp))
  668. asy.start()
  669. print(u_order_qs.count())
  670. return response.json(0)
  671. @classmethod
  672. def created_flow_monthly_cost(cls, u_order_qs, first_timestamp, last_timestamp):
  673. print(f'开始了{datetime.datetime.now()}')
  674. first_date = int(datetime.datetime.fromtimestamp(first_timestamp).strftime('%Y%m%d'))
  675. costs_list = []
  676. for item in u_order_qs:
  677. try:
  678. # 查询订单是否存在
  679. order_qs = Order_Model.objects.filter(orderID=item['order_id'], status=1).values('orderID', 'UID',
  680. 'payTime', 'price',
  681. 'store_meal_name')
  682. if not order_qs.exists():
  683. continue
  684. u_order = item
  685. price = float(order_qs[0]['price'])
  686. day_average_price = 0 # 收入分摊/天 订单金额/套餐总天数
  687. if price > day_average_price:
  688. expiration_days = int(u_order['combo__expiration_days'])
  689. day_average_price = price / expiration_days
  690. # 套餐到期时间与当月结算时间相差天数=剩余使用天数
  691. expire_time = datetime.datetime.fromtimestamp(u_order['expire_time'])
  692. month_time = datetime.datetime.fromtimestamp(last_timestamp)
  693. # 剩余使用天数
  694. days_diff = (expire_time - month_time).days
  695. # 当月结算时间与套餐创建时间相差天数=当月结算天数
  696. if u_order['created_time'] >= first_timestamp:
  697. c_time = datetime.datetime.fromtimestamp(u_order['created_time'])
  698. else:
  699. c_time = datetime.datetime.fromtimestamp(first_timestamp)
  700. settlement_days = (month_time - c_time).days + 1
  701. settlement_days = 0 if settlement_days < 0 else settlement_days
  702. # 组装当月成本账单结构
  703. costs_vo = {
  704. 'settlement_days': settlement_days, # 当月结算天数
  705. 'order_id': order_qs[0]['orderID'],
  706. 'uid': order_qs[0]['UID'],
  707. 'day_average_price': Decimal(day_average_price).quantize(Decimal('0.0000')), # 收入分摊/天
  708. 'purchase_quantity': u_order['combo__flow_total'],
  709. 'start_time': u_order['created_time'],
  710. 'end_time': u_order['expire_time'],
  711. 'created_time': last_timestamp,
  712. 'remaining_usage_time': days_diff, # 剩余使用时间
  713. 'time': first_timestamp,
  714. 'country_name': '中国',
  715. 'order_type': '4G',
  716. 'region': '国内',
  717. 'price': order_qs[0]['price']
  718. }
  719. days = int(u_order['combo__expiration_days'])
  720. if settlement_days > 0 and price > 0:
  721. # 实际收入/套餐总天数*结算天数
  722. costs_vo['monthly_income'] = Decimal((price - price * 0.0054) / days * settlement_days).quantize(
  723. Decimal('0.0000'))
  724. month_average_price = day_average_price * settlement_days if day_average_price > 0 else 0
  725. # 收入分摊/天*当月使用天数
  726. costs_vo['month_average_price'] = Decimal(month_average_price).quantize(Decimal('0.00'))
  727. # 使用filter()方法进行查询
  728. uos_qs = UnicomComboOrderInfo.objects.filter(
  729. iccid=u_order['iccid'], expire_time__gte=first_timestamp
  730. ).exclude(combo_id__in=[24, 26, 34])
  731. con = uos_qs.count()
  732. profit_margin = 0
  733. cost = 2
  734. profit = 0 # 当月利润
  735. # 查询IOT卡订单用量历史
  736. n_flow = IotCardOrderUsageHistory.objects.filter(
  737. order_id=u_order['order_id'],
  738. cycle__gte=first_date
  739. ).order_by('-cycle_date')
  740. cost = cost if not uos_qs.exists() else cost / con # 联通4G单卡月租2元/对应套餐数量 当月成本分摊
  741. if n_flow.exists():
  742. # 查询IOT卡订单用量历史
  743. old_flow = IotCardOrderUsageHistory.objects.filter(
  744. order_id=u_order['order_id'],
  745. cycle__lt=first_date
  746. ).order_by('-cycle_date')
  747. old_use_flow = float(old_flow.first().total_traffic if old_flow.exists() else 0)
  748. n_user_flow = float(n_flow.first().total_traffic)
  749. actual_flow = n_user_flow - old_use_flow if n_user_flow > 0 else 0
  750. costs_vo['actual_flow'] = actual_flow
  751. # 基础分摊成本
  752. flow_cost = 0
  753. # 流量使用成本
  754. if actual_flow > 0:
  755. flow_cost = (actual_flow / 1024 * 0.4)
  756. cost = cost + flow_cost
  757. if 'monthly_income' in costs_vo:
  758. profit = float(costs_vo['monthly_income']) - cost
  759. profit_margin = profit / cost * (1 * 100)
  760. costs_vo['actual_flow'] = Decimal(actual_flow).quantize(Decimal('0.00'))
  761. elif price > 0:
  762. if 'monthly_income' in costs_vo:
  763. profit = float(costs_vo['monthly_income']) - cost
  764. profit_margin = profit / cost * (1 * 100)
  765. costs_vo['flow_cost'] = Decimal(cost).quantize(Decimal('0.00'))
  766. costs_vo['profit'] = Decimal(profit).quantize(Decimal('0.00'))
  767. costs_vo['profit_margin'] = Decimal(profit_margin).quantize(Decimal('0.00'))
  768. fee = price * 0.0054 if price > 0 else 0
  769. costs_vo['fee'] = Decimal(fee).quantize(Decimal('0.00'))
  770. costs_vo['real_income'] = Decimal(price - fee).quantize(Decimal('0.00'))
  771. costs_vo['expire'] = order_qs[0]['store_meal_name']
  772. costs_list.append(OperatingCosts(**costs_vo))
  773. # # 批量插入操作,每300条记录插入一次
  774. if len(costs_list) >= 300:
  775. OperatingCosts.objects.bulk_create(costs_list)
  776. costs_list = []
  777. except Exception as e:
  778. logger.error('统计4G卡日用量异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  779. continue
  780. # 插入剩余的记录
  781. if costs_list:
  782. OperatingCosts.objects.bulk_create(costs_list)
  783. return True
  784. @staticmethod
  785. def get_current_month_range():
  786. # 获取当前日期
  787. today = datetime.datetime.today()
  788. # 获取当前月份第一天
  789. first_day = today.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
  790. # 获取下个月第一天
  791. next_month = today.replace(day=28) + datetime.timedelta(days=4) # 跳过了2月份的28号日期
  792. last_day = next_month - datetime.timedelta(days=next_month.day)
  793. # 转换为时间戳
  794. first_timestamp = int(first_day.timestamp())
  795. last_timestamp = int(last_day.timestamp())
  796. return first_timestamp, last_timestamp