TelecomObject.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. # -*- encoding: utf-8 -*-
  2. """
  3. @File : TelecomObject.py
  4. @Time : 2024/1/11 10:40
  5. @Author : stephen
  6. @Email : zhangdongming@asj6.wecom.work
  7. @Software: PyCharm
  8. """
  9. import json
  10. import requests
  11. import xmltodict
  12. from Ansjer.config import CT_USER_ID, CT_PASSWORD, CT_SECRET_KEY, LOGGER
  13. from Object.utils import LocalDateTimeUtil
  14. from Object.utils.DesUtils import DesUtils
  15. class TelecomObject:
  16. def __init__(self):
  17. self.user_id = CT_USER_ID
  18. self.password = CT_PASSWORD
  19. self.secret_key = CT_SECRET_KEY
  20. self.key1 = CT_SECRET_KEY[0:3]
  21. self.key2 = CT_SECRET_KEY[3:6]
  22. self.key3 = CT_SECRET_KEY[6:9]
  23. self.url = 'http://api.ct10649.com:9001/m2m_ec/query.do'
  24. self.session = requests.Session()
  25. def get_password_enc(self):
  26. return DesUtils.str_enc(self.password, self.key1, self.key2, self.key3)
  27. def get_sign(self, arr):
  28. return DesUtils.str_enc(DesUtils.natural_ordering(arr), self.key1, self.key2, self.key3)
  29. def get_required_arr(self, iccid, method):
  30. return [iccid, self.user_id, self.password, method]
  31. def get_params_dict_by_iccid(self, method, iccid, arr):
  32. return {'method': method, 'iccid': iccid,
  33. 'user_id': self.user_id,
  34. 'passWord': self.get_password_enc(),
  35. 'sign': self.get_sign(arr)}
  36. def get_params_dict_by_access_number(self, method, access_number, arr):
  37. return {'method': method, 'access_number': access_number,
  38. 'user_id': self.user_id,
  39. 'passWord': self.get_password_enc(),
  40. 'sign': self.get_sign(arr)}
  41. def query_card_main_status(self, iccid):
  42. """
  43. 卡主状态查询接口
  44. :param iccid: 卡的ICCID号。
  45. :return: 包含卡状态的结果字典。
  46. :raises ValueError: 如果响应为空或HTTP请求失败。
  47. """
  48. try:
  49. if not iccid:
  50. raise ValueError("error ICCID不能为空")
  51. method = 'queryCardMainStatus'
  52. arr = self.get_required_arr(iccid, method)
  53. # 密码加密
  54. re_params = self.get_params_dict_by_iccid(method, iccid, arr)
  55. response = self.session.get(self.url, params=re_params)
  56. if response.status_code != 200:
  57. LOGGER(f"*****TelecomObject.query_card_main_status error HTTP请求失败,状态码: {response.status_code}")
  58. return None
  59. result = response.json()
  60. LOGGER.info(f'*****TelecomObject.query_card_main_status****iccid:{iccid},result:{result}')
  61. return result
  62. except Exception as e:
  63. LOGGER.info('***TelecomObject.query_card_main_status:errLine:{}, errMsg:{}'
  64. .format(e.__traceback__.tb_lineno, repr(e)))
  65. return None
  66. def query_total_usage_by_date(self, access_number):
  67. """
  68. 总使用量查询接口(时间段) 不可跨月查询
  69. :param access_number: 11位接入号码
  70. :return: 套餐使用量查询结果
  71. :raises ValueError: 如果响应为空或HTTP请求失败。
  72. """
  73. try:
  74. if not access_number:
  75. raise ValueError("error access_number不能为空")
  76. start_date = LocalDateTimeUtil.get_first_day_of_month()
  77. end_date = LocalDateTimeUtil.get_last_day_of_month()
  78. method = 'queryTotalUsageByDate'
  79. arr = [method, self.user_id, access_number, self.password, start_date, end_date]
  80. re_params = self.get_params_dict_by_access_number(method, access_number, arr)
  81. re_params['startDate'] = start_date
  82. re_params['endDate'] = end_date
  83. response = self.session.get(self.url, params=re_params)
  84. if response.status_code != 200:
  85. LOGGER.info(f"*****TelecomObject.query_total_usage_by_date error HTTP请求失败,状态码: {response.status_code}")
  86. return None
  87. msg = response.text
  88. if not msg:
  89. return None
  90. # 检查响应类型
  91. content_type = response.headers.get('Content-Type', '')
  92. if 'application/xml' in content_type or 'text/xml' in content_type:
  93. result = xmltodict.parse(msg)
  94. elif 'application/json' in content_type or 'text/json' in content_type:
  95. result = json.loads(msg)
  96. LOGGER.info(f"***TelecomObject.query_total_usage_by_date查询流量异常{access_number},error{result}")
  97. return None
  98. else:
  99. return None
  100. return result
  101. except Exception as e:
  102. LOGGER.info('***TelecomObject.query_total_usage_by_date:errLine:{}, errMsg:{}'
  103. .format(e.__traceback__.tb_lineno, repr(e)))
  104. return None
  105. def disabled_number(self, access_number, orderTypeId):
  106. """
  107. 停机保号/复机/测试期去激活
  108. :param access_number: 11位接入号码
  109. :param orderTypeId: 19表示停机保号,20表示停机保号后复机,21表示测试期去激活,22表示测试期去激活后回到测试激活。
  110. :return: 流量总使用量查询(时间段)
  111. :raises ValueError: 如果响应为空或HTTP请求失败。
  112. """
  113. try:
  114. if not access_number:
  115. raise ValueError("*****TelecomObject.disabled_number error****access_number不能为空")
  116. method = 'disabledNumber'
  117. arr = [method, self.user_id, access_number, self.password, orderTypeId, '']
  118. re_params = self.get_params_dict_by_access_number(method, access_number, arr)
  119. re_params['orderTypeId'] = orderTypeId
  120. re_params['acctCd'] = ''
  121. response = self.session.get(self.url, params=re_params)
  122. if response.status_code != 200:
  123. LOGGER.info(f"*****TelecomObject.disabled_number error HTTP请求失败,状态码: {response.status_code}")
  124. return None
  125. msg = response.text
  126. if not msg:
  127. return None
  128. # 检查响应类型
  129. content_type = response.headers.get('Content-Type', '')
  130. if 'application/xml' in content_type or 'text/xml' in content_type:
  131. result = xmltodict.parse(msg)
  132. elif 'application/json' in content_type or 'text/json' in content_type:
  133. result = json.loads(msg)
  134. LOGGER.info(f"***TelecomObject.disabled_number停机/复机异常{access_number},error{result}")
  135. return None
  136. else:
  137. return None
  138. return result
  139. except Exception as e:
  140. LOGGER.info('***TelecomObject.disabled_number:errLine:{}, errMsg:{}'
  141. .format(e.__traceback__.tb_lineno, repr(e)))
  142. return None
  143. def single_cut_net(self, access_number, action):
  144. """
  145. 单独添加/恢复断网接口
  146. :param access_number: 11位接入号码
  147. :param action: ADD:单独添加断网;DEL:单独恢复断网
  148. :return: 单独添加/恢复断网结果
  149. :raises ValueError: 如果响应为空或HTTP请求失败。
  150. """
  151. try:
  152. if not access_number:
  153. raise ValueError("*****TelecomObject.single_cut_net***error:access_number不能为空")
  154. method = 'singleCutNet'
  155. arr = [method, self.user_id, access_number, self.password, action]
  156. re_params = self.get_params_dict_by_access_number(method, access_number, arr)
  157. re_params['action'] = action
  158. response = self.session.get(self.url, params=re_params)
  159. if response.status_code != 200:
  160. LOGGER.info(f"*****TelecomObject.query_card_main_status error HTTP请求失败,状态码: {response.status_code}")
  161. return None
  162. msg = response.text
  163. if not msg:
  164. return None
  165. LOGGER.info(f"*****singleCutNet access_number:{access_number},response:{msg}")
  166. # 检查响应类型
  167. content_type = response.headers.get('Content-Type', '')
  168. if 'application/xml' in content_type or 'text/xml' in content_type:
  169. result = xmltodict.parse(msg)
  170. elif 'application/json' in content_type or 'text/json' in content_type:
  171. result = json.loads(msg)
  172. LOGGER.info(f"***TelecomObject.query_card_main_status停机/复机异常{access_number},error{result}")
  173. return None
  174. elif 'text/plain;charset=utf-8' in content_type:
  175. return '-5' # 已执行过的操作
  176. else:
  177. return None
  178. return result
  179. except Exception as e:
  180. LOGGER.info('***TelecomObject.single_cut_net:errLine:{}, errMsg:{}'
  181. .format(e.__traceback__.tb_lineno, repr(e)))
  182. return None
  183. def get_telephone(self, iccid):
  184. """
  185. 接入号码查询接口
  186. :param iccid: 卡的ICCID号。
  187. :return: 接入号码查询结果
  188. :raises ValueError: 如果响应为空或HTTP请求失败。
  189. """
  190. try:
  191. if not iccid:
  192. raise ValueError("*****TelecomObject.get_telephone error****iccid不能为空")
  193. method = 'getTelephone'
  194. arr = [method, self.user_id, iccid, self.password]
  195. re_params = self.get_params_dict_by_iccid(method, iccid, arr)
  196. response = self.session.get(self.url, params=re_params)
  197. if response.status_code != 200:
  198. LOGGER.info(f"*****TelecomObject.get_telephone error HTTP请求失败,状态码: {response.status_code}")
  199. return None
  200. msg = response.text
  201. if not msg:
  202. return None
  203. # 检查响应类型
  204. return json.loads(msg)
  205. except Exception as e:
  206. LOGGER.info('***TelecomObject.get_telephone:errLine:{}, errMsg:{}'
  207. .format(e.__traceback__.tb_lineno, repr(e)))
  208. return None
  209. def query_traffic(self, iccid, need_dtl='0'):
  210. """
  211. 表示流量总使用量查询(当月)
  212. :param iccid: 卡的ICCID号。
  213. :param need_dtl: 0
  214. :return: 表示流量总使用量查询(当月)。
  215. :raises ValueError: 如果响应为空或HTTP请求失败。
  216. """
  217. try:
  218. if not iccid:
  219. raise ValueError("*****TelecomObject.query_traffic error****ICCID不能为空")
  220. method = 'queryTraffic'
  221. arr = [method, self.user_id, iccid, self.password]
  222. re_params = self.get_params_dict_by_iccid(method, iccid, arr)
  223. re_params['needDtl'] = need_dtl
  224. response = self.session.get(self.url, params=re_params)
  225. if response.status_code != 200:
  226. LOGGER.info(
  227. f"*****TelecomObject.query_traffic error HTTP请求失败,状态码: {response.status_code}")
  228. return None
  229. msg = response.text
  230. if not msg:
  231. return None
  232. # 检查响应类型
  233. content_type = response.headers.get('Content-Type', '')
  234. if 'application/xml' in content_type or 'text/xml' in content_type:
  235. result = xmltodict.parse(msg)
  236. elif 'application/json' in content_type or 'text/json' in content_type:
  237. result = json.loads(msg)
  238. LOGGER.info(f"***TelecomObject.query_traffic查询流量异常{iccid},error{result}")
  239. return None
  240. else:
  241. return None
  242. return result
  243. except Exception as e:
  244. LOGGER.info(
  245. '***TelecomObject.query_traffic:errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  246. return None
  247. def query_package_batch_flow(self, iccid, month_date):
  248. """
  249. 套餐使用量批量查询接口
  250. :param iccid: 卡的ICCID号。
  251. :param month_date: 例如20160501,表示查询2016年5月份的套餐;如果不填,则为查询实时套餐
  252. :return: 套餐使用量查询结果
  253. :raises ValueError: 如果响应为空或HTTP请求失败。
  254. """
  255. try:
  256. if not iccid:
  257. raise ValueError("*****TelecomObject.query_package_batch_flow error****ICCID不能为空")
  258. method = 'queryPakagePlus'
  259. arr = [method, self.user_id, iccid, self.password]
  260. re_params = self.get_params_dict_by_access_number(method, iccid, arr)
  261. response = self.session.get(self.url, params=re_params)
  262. if response.status_code != 200:
  263. LOGGER.info(f"*****TelecomObject.query_package_batch_flow error HTTP请求失败,状态码: {response.status_code}")
  264. return None
  265. msg = response.text
  266. if not msg:
  267. return None
  268. # 检查响应类型
  269. content_type = response.headers.get('Content-Type', '')
  270. if 'application/xml' in content_type or 'text/xml' in content_type:
  271. result = xmltodict.parse(msg)
  272. elif 'application/json' in content_type or 'text/json' in content_type:
  273. result = json.loads(msg)
  274. LOGGER.info(f"***TelecomObject.query_package_batch_flow查询流量异常{iccid},error{result}")
  275. return None
  276. else:
  277. LOGGER.info("***TelecomObject.query_package_batch_flow无法识别的响应类型: {}".format(content_type))
  278. return None
  279. return result
  280. except Exception as e:
  281. LOGGER.info(
  282. '***TelecomObject.query_package_batch_flow:errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno,
  283. repr(e)))
  284. return None
  285. def query_traffic_by_date(self, iccid):
  286. """
  287. 流量总使用量查询(时间段) 会被限流 成功则返回XML
  288. :param iccid: 卡的ICCID号。
  289. :return: 流量总使用量查询(时间段)
  290. :raises ValueError: 如果响应为空或HTTP请求失败。
  291. """
  292. try:
  293. if not iccid:
  294. raise ValueError("*****TelecomObject.query_traffic_by_date error****ICCID不能为空")
  295. method = 'queryTrafficByDate'
  296. arr = [method, self.user_id, iccid, self.password]
  297. re_params = self.get_params_dict_by_iccid(method, iccid, arr)
  298. re_params['startDate'] = '20240101'
  299. re_params['endDate'] = '20240115'
  300. re_params['needDtl'] = '0'
  301. response = self.session.get(self.url, params=re_params)
  302. if response.status_code != 200:
  303. LOGGER.info(f"*****TelecomObject.query_traffic_by_date error HTTP请求失败,状态码: {response.status_code}")
  304. return None
  305. msg = response.text
  306. if not msg:
  307. return None
  308. # 检查响应类型
  309. content_type = response.headers.get('Content-Type', '')
  310. if 'application/xml' in content_type or 'text/xml' in content_type:
  311. result = xmltodict.parse(msg)
  312. elif 'application/json' in content_type or 'text/json' in content_type:
  313. result = json.loads(msg)
  314. LOGGER.info(f"***TelecomObject.query_traffic_by_date 查询流量异常{iccid},error{result}")
  315. return None
  316. else:
  317. LOGGER.info("***TelecomObject.query_traffic_by_date 无法识别的响应类型: {}".format(content_type))
  318. return None
  319. return result
  320. except Exception as e:
  321. LOGGER.info('***TelecomObject.query_traffic_by_date:errLine:{}, errMsg:{}'
  322. .format(e.__traceback__.tb_lineno, repr(e)))
  323. return None
  324. def query_product_information(self, access_number):
  325. """
  326. 产品资料查询接口
  327. :param access_number: 接入号码access_number。
  328. :return: SIM卡的产品资料,包含基础信息、套餐、状态及 断网类型等
  329. :raises ValueError: 如果响应为空或HTTP请求失败。
  330. """
  331. try:
  332. if not access_number:
  333. raise ValueError("*****TelecomObject.query_product_information error****access_number不能为空")
  334. method = 'prodInstQuery'
  335. arr = [method, self.user_id, access_number, self.password]
  336. re_params = self.get_params_dict_by_access_number(method, access_number, arr)
  337. response = self.session.get(self.url, params=re_params)
  338. if response.status_code != 200:
  339. LOGGER.info(f"*****TelecomObject.query_product_information error HTTP请求失败,状态码: {response.status_code}")
  340. return None
  341. msg = response.text
  342. if not msg:
  343. return None
  344. content_type = response.headers.get('Content-Type', '')
  345. if 'application/xml' in content_type or 'text/xml' in content_type:
  346. result = xmltodict.parse(msg)
  347. elif 'application/json' in content_type or 'text/json' in content_type:
  348. result = json.loads(msg)
  349. LOGGER.info(f"***TelecomObject.query_product_information 查询产品资料异常:{access_number},error{result}")
  350. return None
  351. else:
  352. LOGGER.info("***TelecomObject.query_product_information 无法识别的响应类型: {}".format(content_type))
  353. return None
  354. return result
  355. except Exception as e:
  356. LOGGER.info('***TelecomObject.query_product_information:errLine:{}, errMsg:{}'
  357. .format(e.__traceback__.tb_lineno, repr(e)))
  358. return None
  359. def off_net_action_by_access_number(self, access_number, action, quota, a_type):
  360. """
  361. 表示达量断网新增、修改及取消接口
  362. :param access_number: 接入号码access_number。
  363. :param action: action=1,表示新增达量断网阈值;action=2,表示修改达量断网阈值;action=3,表示取消达量断网功能。
  364. 注:已达量断网的物联网卡无法通过取消达量断网功能实现恢复上网。
  365. :param quota: 要添加或调整的断网阈值(单位:M)比如 1024,注意:1)设为-1表示无限制2)设为0表示有上网流量产生就会立即断网3)只能设置为-1,0或正整数
  366. :param a_type: type表示要添加或调整的断网类型:设置为1:表示用户总使用量 设置为2:表示超出套餐外使用量
  367. :return: 修改结果
  368. :raises ValueError: 如果响应为空或HTTP请求失败。
  369. """
  370. try:
  371. if not access_number:
  372. raise ValueError("*****TelecomObject.off_net_action_by_access_number error****access_number不能为空")
  373. method = 'offNetAction'
  374. arr = [method, self.user_id, access_number, self.password, action, quota, a_type]
  375. re_params = self.get_params_dict_by_access_number(method, access_number, arr)
  376. re_params['action'] = action
  377. re_params['quota'] = quota
  378. re_params['type'] = a_type
  379. response = self.session.post(self.url, data=re_params)
  380. if response.status_code != 200:
  381. LOGGER.info(
  382. f"*****TelecomObject.off_net_action_by_access_number error HTTP请求失败,状态码: {response.status_code}")
  383. return None
  384. msg = response.text
  385. if not msg:
  386. return None
  387. content_type = response.headers.get('Content-Type', '')
  388. if 'application/xml' in content_type or 'text/xml' in content_type:
  389. result = xmltodict.parse(msg)
  390. elif 'application/json' in content_type or 'text/json' in content_type:
  391. result = json.loads(msg)
  392. LOGGER.info(f"***TelecomObject.off_net_action_by_access_number 查询产品资料异常:{access_number},error{result}")
  393. return None
  394. else:
  395. LOGGER.info("***TelecomObject.off_net_action_by_access_number 无法识别的响应类型: {}".format(content_type))
  396. return None
  397. return result
  398. except Exception as e:
  399. LOGGER.info('***TelecomObject.off_net_action_by_access_number:errLine:{}, errMsg:{}'
  400. .format(e.__traceback__.tb_lineno, repr(e)))
  401. return None