UnicomObject.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. # -*- encoding: utf-8 -*-
  2. """
  3. @File : UnicomObject.py
  4. @Time : 2022/6/17 11:03
  5. @Author : stephen
  6. @Email : zhangdongming@asj6.wecom.work
  7. @Software: PyCharm
  8. """
  9. import base64
  10. import json
  11. import logging
  12. import requests
  13. from Crypto.Cipher import AES
  14. from Ansjer.config import unicomAppUrl, unicomAppId, unicomAppSecret, unicomTenantId, \
  15. unicomEncodeKey, unicomIvKey, unicomUserName, unicomPassword, unicomPushKey, SERVER_DOMAIN_SSL
  16. from Object.RedisObject import RedisObject
  17. from Object.utils import SM3Util
  18. from Object.utils.SymmetricCryptoUtil import AESencrypt
  19. """
  20. 联通4Gapi
  21. 具体参数查看接口文档
  22. https://www.showdoc.com.cn/unicomJYHapi/8158648460007467
  23. """
  24. logger = logging.getLogger('info')
  25. class UnicomObjeect:
  26. def __init__(self):
  27. self.appUrl = unicomAppUrl
  28. self.appId = unicomAppId
  29. self.appSecret = unicomAppSecret
  30. self.tenantId = unicomTenantId
  31. self.encodeKey = unicomEncodeKey
  32. self.ivKey = unicomIvKey
  33. self.username = unicomUserName
  34. self.password = unicomPassword
  35. self.pushKey = unicomPushKey
  36. self.headers = {'Tenant': self.tenantId, 'content-type': 'application/x-www-form-urlencoded'}
  37. # pip install snowland-smx
  38. def createSign(self, reverse=False, **sign_params):
  39. """
  40. 调用接口(API)时需要对请求参数进行签名(sign)验证,
  41. 算法:
  42. 根据参数名称将你的所有请求参数按照字母先后顺序排序: key = value & key = value,对除签名外的所有请求参数按
  43. key
  44. 做的升序排列。
  45. 如:将
  46. foo = 1, bar = 2, baz = 3
  47. 排序为
  48. bar = 2, baz = 3, foo = 1
  49. 参数名和参数值链接后,得到拼装字符串(注意:参数名与参数值左右两边不能包含空格)
  50. bar = 2 & baz = 3 & foo = 1
  51. pushkey拼接到参数字符尾部进行
  52. SM3
  53. 加密,再转化成大写,格式是
  54. (SM3(key1=value1 & key2=value2 &...& key= pushKey)).upcase
  55. @param reverse:
  56. @param sign_params:
  57. @return:
  58. """
  59. dict_2 = dict(sorted(sign_params.items(), key=lambda item: item[0], reverse=reverse))
  60. data_list = []
  61. for item in dict_2.items():
  62. if item[0] and item[1]:
  63. data_list.append("{}={}".format(item[0], item[1]))
  64. val = '&'.join(data_list)
  65. push_key = '&key={}'.format(self.pushKey)
  66. val = val + push_key
  67. return SM3Util.Hash_sm3(val).upper()
  68. def get_login_authorization(self):
  69. """
  70. 获取登录授权
  71. 注意登录认证Authorization和登录后的请求认证不一样
  72. 算法:appId+":"+appSecret base64转码生成 前面加Basic
  73. @return: "Basic " + base64_data
  74. """
  75. voucher = self.appId + ':' + self.appSecret
  76. base64_data = str(base64.b64encode(voucher.encode("utf-8")), "utf-8")
  77. return "Basic " + base64_data
  78. def get_encode_password(self):
  79. """
  80. 获取对称加密
  81. @return: encrypt_pwd
  82. """
  83. aes = AESencrypt(self.encodeKey.encode('utf-8'), AES.MODE_CBC, self.ivKey.encode('utf-8'),
  84. paddingMode="ZeroPadding",
  85. characterSet='utf-8')
  86. encrypt_pwd = aes.encryptFromString(self.password)
  87. return encrypt_pwd
  88. def generate_token(self):
  89. """
  90. 生成令牌
  91. @return: token
  92. """
  93. url = self.appUrl + '/auc/oauth/token'
  94. pwd = self.get_encode_password()
  95. body = {'username': self.username, 'password': pwd, 'grant_type': 'password', 'scope': 'server'}
  96. headers = self.headers
  97. headers['Authorization'] = self.get_login_authorization()
  98. response_data = requests.post(url, data=body, headers=headers)
  99. response_data = json.loads(response_data.text)
  100. return response_data['access_token']
  101. def refresh_token(self, refresh_token):
  102. """
  103. 刷新令牌
  104. @param refresh_token:
  105. @return:
  106. """
  107. url = self.appUrl + '/auc/oauth/token?grant_type=refresh_token'
  108. body = {'refresh_token': refresh_token, 'grant_type': 'refresh_token'}
  109. headers = self.headers
  110. headers['Authorization'] = self.get_login_authorization()
  111. response_data = requests.post(url, data=body, headers=headers)
  112. return response_data.text
  113. def business_unify_headers(self):
  114. """
  115. 业务统一headers
  116. 在请求头中增加key为Authorization,value为"Bearer " + token
  117. @return: headers
  118. """
  119. token = self.generate_token()
  120. headers = self.headers
  121. headers['Authorization'] = 'Bearer ' + token
  122. return headers
  123. # 业务api 注意 get与post 请求数据类型不同 post使用:application/json
  124. def verify_device(self, **re_params):
  125. """
  126. 验证设备
  127. @param re_params:
  128. @return:
  129. """
  130. url = self.appUrl + '/platform/api/device/verify-device'
  131. return requests.get(url, params=re_params, headers=self.business_unify_headers())
  132. def query_device_status(self, **re_params):
  133. """
  134. 查询设备状态
  135. @param re_params:
  136. @return:
  137. """
  138. url = self.appUrl + '/platform/api/device/device-status'
  139. return requests.get(url, params=re_params, headers=self.business_unify_headers())
  140. def update_device_state(self, **re_data):
  141. """
  142. 修改设备状态
  143. @param re_data:
  144. @return:
  145. """
  146. url = self.appUrl + '/platform/api/device/async-device-state'
  147. headers = self.business_unify_headers()
  148. headers['content-type'] = 'application/json'
  149. re_data['callbackUrl'] = SERVER_DOMAIN_SSL + 'unicom/api/device-status-change'
  150. return requests.post(url, data=json.dumps(re_data), headers=headers)
  151. def query_device_usage_history(self, **re_params):
  152. """
  153. 查询设备用量历史
  154. @param re_params:
  155. @return:
  156. """
  157. url = self.appUrl + '/platform/api/usage/device-usage-history'
  158. return requests.get(url, params=re_params, headers=self.business_unify_headers())
  159. def query_current_renew_list_usage_details(self, **re_params):
  160. """
  161. 查询设备当前队列用量详情
  162. @param re_params:
  163. @return:
  164. """
  165. url = self.appUrl + '/platform/api/usage/current-renewlist-usage-details'
  166. return requests.get(url, params=re_params, headers=self.business_unify_headers())
  167. def get_device_batch_detail(self, **re_data):
  168. """
  169. 查询设备当前队列用量详情
  170. @return:
  171. """
  172. url = self.appUrl + '/platform/api/device/batch-detail'
  173. headers = self.business_unify_headers()
  174. headers['content-type'] = 'application/json'
  175. return requests.post(url, data=json.dumps(re_data), headers=headers)
  176. def query_package_list(self, **re_params):
  177. """
  178. 查询套餐列表
  179. @return:
  180. """
  181. url = self.appUrl + '/platform/api/package/list'
  182. return requests.get(url, params=re_params, headers=self.business_unify_headers())
  183. def query_renewal_list(self, **re_params):
  184. """
  185. 续费套餐列表
  186. @param re_params:
  187. @return:
  188. """
  189. url = self.appUrl + '/platform/api/package/list'
  190. return requests.get(url, params=re_params, headers=self.business_unify_headers())
  191. def async_buy_package(self, **re_data):
  192. """
  193. 查询设备当前队列用量详情
  194. @return:
  195. """
  196. url = self.appUrl + '/platform/api/package/async-buy-package'
  197. headers = self.business_unify_headers()
  198. headers['content-type'] = 'application/json'
  199. return requests.post(url, data=json.dumps(re_data), headers=headers)
  200. @staticmethod
  201. def get_text_dict(result):
  202. """
  203. 响应结果转字典
  204. @param result:
  205. @return:
  206. """
  207. if result.status_code == 200:
  208. return json.loads(result.text)
  209. return None
  210. @staticmethod
  211. def get_flow_total_usage(key, expire=0, **usage_data):
  212. """
  213. 设备当前队列用量详情(实现缓存)
  214. @param key: 缓存key
  215. @param expire: 失效时间
  216. @param usage_data: 查询参数
  217. @return: 返回结果dict
  218. """
  219. redis = RedisObject()
  220. flow_usage_details = redis.get_data(key)
  221. if flow_usage_details:
  222. flow_usage_details = json.loads(flow_usage_details)
  223. else:
  224. flow_usage_details = UnicomObjeect().query_current_renew_list_usage_details(**usage_data)
  225. flow_usage_details = UnicomObjeect().get_text_dict(flow_usage_details)
  226. redis.set_data(key=key, val=json.dumps(flow_usage_details), expire=expire)
  227. return flow_usage_details
  228. @staticmethod
  229. def get_flow_usage_total(iccid):
  230. """
  231. 获取实时当前套餐队列总已用流量
  232. @param iccid: 联通id
  233. @return: flow_total_usage 当前套餐总已使用流量
  234. """
  235. flow_key = 'ASJ:UNICOM:FLOW:{}'
  236. usage_data = {'iccid': iccid, 'realTime': True}
  237. expire_time = 60 * 10 + 60
  238. flow_usage_details = UnicomObjeect().get_flow_total_usage(flow_key.format(iccid), expire_time, **usage_data)
  239. # 当月实际总使用流量
  240. flow_total_usage = 0
  241. if flow_usage_details and flow_usage_details['success']:
  242. flow_total_usage = flow_usage_details['data']['flowTotalUsage']
  243. return flow_total_usage
  244. @staticmethod
  245. def change_device_to_activate(iccid):
  246. """
  247. 根据iccid判断是否激活,未激活则修改为激活状态
  248. @param iccid:
  249. @return:
  250. """
  251. if iccid:
  252. re_data = {'iccid': iccid}
  253. result = UnicomObjeect().query_device_status(**re_data)
  254. res_dict = UnicomObjeect().get_text_dict(result)
  255. # 状态不等于1(激活)时进行激活 1:激活;2:停用
  256. if res_dict['data']['status'] != 1:
  257. re_data = {"iccid": iccid, "status": 1}
  258. UnicomObjeect().update_device_state(**re_data)
  259. return True
  260. return None
  261. @staticmethod
  262. def change_device_to_disable(iccid):
  263. """
  264. 修改设备为停用,并查看是否修改成功
  265. @param iccid:
  266. @return:
  267. """
  268. if iccid:
  269. re_data = {"iccid": iccid, "status": 2}
  270. response = UnicomObjeect().update_device_state(**re_data)
  271. logger.info('停用iccid响应结果:{}'.format(response.text))
  272. # 查询是否停用成功
  273. re_data.pop('status')
  274. result = UnicomObjeect().query_device_status(**re_data)
  275. res_dict = UnicomObjeect().get_text_dict(result)
  276. logger.info('查询iccid状态:{}'.format(res_dict))
  277. if res_dict['data']['status'] != 3:
  278. re_data['status'] = 2
  279. response = UnicomObjeect().update_device_state(**re_data)
  280. logger.info('再次停卡:{}'.format(response.text))
  281. return True
  282. return None
  283. @staticmethod
  284. def current_sim_traffic_usage_details(icc_id):
  285. """
  286. 当前sim卡流量使用详情
  287. @param icc_id:20位数字iccid
  288. @return: traffic
  289. """
  290. redis = RedisObject()
  291. traffic_key = 'ASJ:UNICOM:FLOW:{}'.format(icc_id)
  292. traffic_sys = 'ASJ:SIM:TRAFFIC:{}'.format(icc_id)
  293. traffic_val = redis.get_data(traffic_key)
  294. if traffic_val:
  295. traffic_dict = json.loads(traffic_val)
  296. redis.set_data(key=traffic_sys, val=traffic_val, expire=60 * 60 * 24)
  297. else:
  298. traffic_val = redis.get_data(traffic_sys)
  299. if not traffic_val:
  300. return 0
  301. traffic_dict = json.loads(traffic_val)
  302. return traffic_dict['data']['flowTotalUsage']