TelecomService.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. # -*- encoding: utf-8 -*-
  2. """
  3. @File : TelecomService.py
  4. @Time : 2024/1/24 15:12
  5. @Author : stephen
  6. @Email : zhangdongming@asj6.wecom.work
  7. @Software: PyCharm
  8. """
  9. import json
  10. import re
  11. import time
  12. from decimal import Decimal
  13. from Ansjer.config import LOGGER
  14. from Model.models import UnicomDeviceInfo, LogModel, AccessNumberTaskQueue
  15. from Object.RedisObject import RedisObject
  16. from Object.TelecomObject import TelecomObject
  17. class TelecomService:
  18. @classmethod
  19. def query_total_usage_by_access_number(cls, iccid, key, expire=600):
  20. """
  21. 根据接入号码查询设备总流量使用量(实现缓存)
  22. @param iccid: 20位ICCID
  23. @param key: 缓存key
  24. @param expire: 失效时间
  25. @return: 查询流量结果
  26. """
  27. redis = RedisObject()
  28. sim_flow_used_total = redis.get_data(key)
  29. if sim_flow_used_total:
  30. return Decimal(sim_flow_used_total).quantize(Decimal('0.00'))
  31. else:
  32. # 查询SIM卡信息
  33. sim_qs = UnicomDeviceInfo.objects.filter(iccid=iccid)
  34. if not sim_qs:
  35. return None
  36. sim_vo = sim_qs.first()
  37. telecom = TelecomObject()
  38. # 查询电信当月流量用量
  39. data = telecom.query_total_usage_by_date(sim_vo.access_number)
  40. if data and data['SvcCont']['resultCode'] == '0' and 'dataCumulationTotal' in data['SvcCont']['result']:
  41. cycle_total = data['SvcCont']['result'].get('dataCumulationTotal')
  42. else:
  43. LOGGER.info(f'query_total_usage_by_access_number查询流量异常,iccid:{iccid}')
  44. return sim_vo.sim_used_flow + sim_vo.sim_cycle_used_flow
  45. cycle_total = Decimal(cycle_total).quantize(Decimal('0.00'))
  46. n_time = int(time.time())
  47. # 判断数据库周期流量用量 是否大于API查询出来的周期用量 如果是则判定进入了下一个周期
  48. if sim_vo.sim_cycle_used_flow != 0 and sim_vo.sim_cycle_used_flow > cycle_total:
  49. sim_used_flow = sim_vo.sim_used_flow + sim_vo.sim_cycle_used_flow
  50. sim_qs.update(
  51. sim_used_flow=sim_used_flow,
  52. sim_cycle_used_flow=cycle_total,
  53. updated_time=n_time
  54. )
  55. # 队列用量历史总量 + 上一个周期流量 + 当前周期流量 = 总消耗流量
  56. sim_flow_used_total = sim_used_flow + cycle_total
  57. elif cycle_total > sim_vo.sim_cycle_used_flow: # API周期用量大于当前数据库用量则更新记录
  58. sim_qs.update(sim_cycle_used_flow=cycle_total, updated_time=n_time)
  59. # 队列用量历史总量 + 当前周期流量 = 总消耗流量
  60. sim_flow_used_total = sim_vo.sim_used_flow + cycle_total
  61. else:
  62. sim_flow_used_total = sim_vo.sim_used_flow + sim_vo.sim_cycle_used_flow
  63. # 缓存当月流量数据
  64. redis.CONN.setnx(key, str(sim_flow_used_total))
  65. redis.CONN.expire(key, expire)
  66. return sim_flow_used_total
  67. @classmethod
  68. def update_access_number_network(cls, iccid, access_number, action_value, reason=''):
  69. """
  70. 根据接入号码修改卡网络状态
  71. @param reason:
  72. @param iccid:
  73. @param access_number: 11位接入号码
  74. @param action_value: ADD:单独添加断网,DEL:单独恢复上网
  75. @return: 修改后结果 None | success
  76. """
  77. try:
  78. # 获取当前卡号是否有过变更网络状态缓存记录
  79. redis = RedisObject()
  80. key = f'ASJ:TELECOM:CHANGE:{iccid}'
  81. change_data = redis.get_data(key)
  82. action = 1 if action_value == 'ADD' else 2
  83. now_time = int(time.time())
  84. # 变更网络存表数据
  85. data = {'iccid': iccid, 'access_number': access_number, 'type': 2, 'action': action, 'previous_status': 0,
  86. 'new_status': 0, 'count': 0, 'reason': reason,
  87. 'status': 0, 'created_time': now_time, 'updated_time': now_time}
  88. if change_data:
  89. c_data = json.loads(change_data)
  90. if c_data['actionValue'] == action_value: # 判断是否重复操作
  91. return None
  92. AccessNumberTaskQueue.objects.create(**data)
  93. LOGGER.info(f'{iccid}未执行变更网络,数据已加入任务队列记录')
  94. return None
  95. # 获取当前卡号网络状态 接口有调用限制并发 15000次/秒 所以尝试多次调用
  96. now_network_status = 0
  97. i = 0
  98. while i < 3:
  99. network_result = cls.get_access_number_network_status(access_number)
  100. if network_result:
  101. now_network_status = 2 if network_result == 'connect' else 1
  102. break
  103. i += 1
  104. if 0 < now_network_status == action: # 当前状态与要变更的状态相同则不调用变更API
  105. ip = '127.0.0.1'
  106. params = {'action': action_value, 'result': 'No need for changes', 'access_number': access_number}
  107. explain = f'{iccid}单独断网或恢复网络'
  108. cls.create_operation_log('updateNetworkStatus', ip, params, explain)
  109. return None
  110. result = TelecomObject().single_cut_net(access_number, action_value)
  111. # 更新变更状态
  112. data['previous_status'] = now_network_status
  113. data['new_status'] = action
  114. data['count'] = 1
  115. cache_data = {'iccid': iccid, 'actionValue': action_value}
  116. if not result: # 变更电信卡网络状态 失败处理
  117. data['status'] = 2
  118. data['result'] = {'code': '101007', 'message': '该接入号码高频次受理此业务,调用暂时受限,请稍后再试'}
  119. AccessNumberTaskQueue.objects.create(**data) # 失败后保存数据记录,走重试机制
  120. redis.CONN.setnx(key, json.dumps(cache_data)) # 缓存当前卡号130秒不再调用变更网络状态接口,防止调用限制
  121. redis.CONN.expire(key, 130)
  122. return None
  123. data['status'] = 1
  124. data['completion_time'] = now_time
  125. if result == '-5': # 已符合变更后状态
  126. data['result'] = {'-5': '该号码未订购单独断网功能!;流水号:1000000190202402201640359500'}
  127. else:
  128. data['result'] = result
  129. AccessNumberTaskQueue.objects.create(**data)
  130. redis.CONN.setnx(key, json.dumps(cache_data))
  131. redis.CONN.expire(key, 130)
  132. return 'success'
  133. except Exception as e:
  134. LOGGER.info('***{} TelecomService.update_access_number_network:errLine:{}, errMsg:{}'
  135. .format(access_number, e.__traceback__.tb_lineno, repr(e)))
  136. return None
  137. @classmethod
  138. def create_operation_log(cls, url, ip, request_dict, describe):
  139. """
  140. 创建操作日志
  141. @param url: 请求路径
  142. @param describe: 描述
  143. @param ip: 当前IP
  144. @param request_dict: 请求参数
  145. @return: True | False
  146. """
  147. try:
  148. # 记录操作日志
  149. content = json.loads(json.dumps(request_dict))
  150. log = {
  151. 'ip': ip,
  152. 'user_id': 1,
  153. 'status': 200,
  154. 'time': int(time.time()),
  155. 'content': json.dumps(content),
  156. 'url': url,
  157. 'operation': describe,
  158. }
  159. LogModel.objects.create(**log)
  160. return True
  161. except Exception as e:
  162. print('日志异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  163. return False
  164. @classmethod
  165. def get_access_number_network_status(cls, access_number):
  166. """
  167. 根据access_number查询是否单独断网
  168. @param access_number: 11位接入号码
  169. @return: 当前网络数据
  170. """
  171. try:
  172. result = TelecomObject().query_product_information(access_number)
  173. if not result:
  174. return None
  175. if result and result['SvcCont']['resultCode'] == '0':
  176. # 获取产品信息
  177. prod_infos = result["SvcCont"]["result"]["prodInfos"]
  178. # 获取 funProdInfos 列表
  179. fun_prod_infos = prod_infos["funProdInfos"]
  180. # 遍历 funProdInfos 列表
  181. for prod_info in fun_prod_infos:
  182. # 获取是否已单独断网信息
  183. if isinstance(prod_info, dict) and "productName" in prod_info:
  184. is_disconnect = prod_info.get("productName")
  185. if "是否已单独断网" in is_disconnect:
  186. is_disconnect_value = re.search(r"(?<=:).+", is_disconnect).group(0)
  187. # 进行判断
  188. if is_disconnect_value == "否":
  189. return 'connect'
  190. else:
  191. return 'disconnect'
  192. return None
  193. except Exception as e:
  194. print('***{}get_access_number_network_status,errLine:{}, errMsg:{}'
  195. .format(access_number, e.__traceback__.tb_lineno, repr(e)))
  196. return None