VersionManagementController.py 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import hashlib
  4. import json
  5. import os
  6. import threading
  7. import time
  8. import boto3
  9. import botocore
  10. import requests
  11. from django.core.paginator import Paginator
  12. from django.db import transaction
  13. from django.views.generic.base import View
  14. from packaging import version as pacVer
  15. from Ansjer.config import BASE_DIR
  16. from Ansjer.config import LOGGER, CONFIG_TEST, SERVER_DOMAIN_OTA, CONFIG_CN, CONFIG_INFO
  17. from Model.models import Equipment_Version, App_Info, AppSetModel, App_Colophon, Pc_Info, CountryModel, \
  18. Device_Info, UidSetModel, Device_User, IPAddr, DeviceVersionInfo, iotdeviceInfoModel
  19. from Object.RedisObject import RedisObject
  20. from Object.ResponseObject import ResponseObject
  21. from Object.TokenObject import TokenObject
  22. from Object.UrlTokenObject import UrlTokenObject
  23. from Service.CommonService import CommonService
  24. from django.conf import settings
  25. AWS_ACCESS_KEY_ID = settings.AWS_ACCESS_KEY_ID
  26. AWS_SECRET_ACCESS_KEY = settings.AWS_SECRET_ACCESS_KEY
  27. class VersionManagement(View):
  28. def get(self, request, *args, **kwargs):
  29. request.encoding = 'utf-8'
  30. operation = kwargs.get('operation')
  31. return self.validation(request.GET, request, operation)
  32. def post(self, request, *args, **kwargs):
  33. request.encoding = 'utf-8'
  34. operation = kwargs.get('operation')
  35. return self.validation(request.POST, request, operation)
  36. def validation(self, request_dict, request, operation):
  37. language = request_dict.get('language', 'en')
  38. response = ResponseObject(language, 'pc')
  39. if operation == 'upLoadFile':
  40. return self.upLoadFile(request, request_dict, response)
  41. else:
  42. tko = TokenObject(
  43. request.META.get('HTTP_AUTHORIZATION'),
  44. returntpye='pc')
  45. if tko.code != 0:
  46. return response.json(tko.code)
  47. response.lang = tko.lang
  48. userID = tko.userID
  49. if operation == 'getEquipmentVersionList':
  50. return self.getEquipmentVersionList(request_dict, response)
  51. elif operation == 'editVersionInformation':
  52. return self.editVersionInformation(request_dict, response)
  53. elif operation == 'deleteEquipmentVersion':
  54. return self.deleteEquipmentVersion(request_dict, response)
  55. elif operation == 'getAppVersionList':
  56. return self.getAppVersionList(request_dict, response)
  57. elif operation == 'addOrEditAppInfo':
  58. return self.addOrEditAppInfo(request_dict, response)
  59. elif operation == 'deleteAppVersion':
  60. return self.deleteAppVersion(request_dict, response)
  61. elif operation == 'getAppSet':
  62. return self.getAppSet(request_dict, response)
  63. elif operation == 'editAppSet':
  64. return self.editAppSet(request_dict, response)
  65. elif operation == 'getAppRecordList':
  66. return self.getAppRecordList(request_dict, response)
  67. elif operation == 'getAppBundleIdList':
  68. return self.getAppBundleIdList(request_dict, response)
  69. elif operation == 'addOrEditAppRecord':
  70. return self.addOrEditAppRecord(request_dict, response)
  71. elif operation == 'deleteAppRecord':
  72. return self.deleteAppRecord(request_dict, response)
  73. elif operation == 'getPcInfoList':
  74. return self.getPcInfoList(request_dict, response)
  75. elif operation == 'editPcVersion':
  76. return self.editPcVersion(request_dict, response)
  77. elif operation == 'deletePcInfo':
  78. return self.deletePcInfo(request_dict, response)
  79. elif operation == 'getCountryList':
  80. return self.getCountryList(request_dict, response)
  81. elif operation == 'deviceAutoUpdate':
  82. return self.device_auto_update(userID, request_dict, response)
  83. else:
  84. return response.json(404)
  85. def getEquipmentVersionList(self, request_dict, response):
  86. mci = request_dict.get('mci', None)
  87. lang = request_dict.get('lang', None)
  88. version = request_dict.get('version', None)
  89. pageNo = request_dict.get('pageNo', None)
  90. pageSize = request_dict.get('pageSize', None)
  91. if not all([pageNo, pageSize]):
  92. return response.json(444)
  93. page = int(pageNo)
  94. line = int(pageSize)
  95. try:
  96. equipment_version_qs = Equipment_Version.objects.filter().order_by('-update_time')
  97. if mci:
  98. equipment_version_qs = equipment_version_qs.filter(mci=mci)
  99. if lang:
  100. equipment_version_qs = equipment_version_qs.filter(lang=lang)
  101. if version:
  102. equipment_version_qs = equipment_version_qs.filter(version__contains=version)
  103. total = equipment_version_qs.count()
  104. equipment_version_qs = equipment_version_qs.values()[(page - 1) * line:page * line]
  105. equipment_version_list = CommonService.qs_to_list(equipment_version_qs)
  106. for equipment_version in equipment_version_list:
  107. new_equipment_version = equipment_version['version'][1:]
  108. d_code = new_equipment_version.rsplit('.', 1)[1]
  109. software_ver = new_equipment_version.rsplit('.', 1)[0].replace('V', '')
  110. device_ver_info_qs = DeviceVersionInfo.objects.filter(d_code=d_code, software_ver=software_ver)
  111. if device_ver_info_qs.exists():
  112. equipment_version['is_hav_dev_ver_info'] = 1
  113. else:
  114. equipment_version['is_hav_dev_ver_info'] = 0
  115. return response.json(0, {'list': equipment_version_list, 'total': total})
  116. except Exception as e:
  117. print(e)
  118. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  119. def upLoadFile(self, request, request_dict, response):
  120. file = request.FILES.get('file', None)
  121. mci = request_dict.get('mci', '')
  122. lang = request_dict.get('lang', '')
  123. ESN = request_dict.get('ESN', '')
  124. max_ver = request_dict.get('max_ver', '')
  125. channel = request_dict.get('channel', '')
  126. resolutionRatio = request_dict.get('resolutionRatio', '')
  127. Description = request_dict.get('Description', '')
  128. status = request_dict.get('status', 0)
  129. isPopup = request_dict.get('isPopup', 0)
  130. auto_update = request_dict.get('autoUpdate', 0)
  131. data_json = request_dict.get('dataJson', None)
  132. if not all([file, mci, lang, ESN, max_ver, channel, resolutionRatio]):
  133. return response.json(444)
  134. try:
  135. with transaction.atomic():
  136. nowTime = CommonService.timestamp_to_str(timestamp=int(time.time()))
  137. channel = int(channel)
  138. resolutionRatio = int(resolutionRatio)
  139. status = int(status)
  140. isPopup = int(isPopup)
  141. if data_json:
  142. data_json = eval(data_json)
  143. # 文件名为设备版本,最后一个'.'的前面为软件版本,后面为设备规格名称
  144. # V2.2.4.16E201252CA,软件版本:2.2.4,设备规格名称:16E201252CA
  145. # V1.7.2.36C11680X30411F000600000150001Z,软件版本:1.7.2,设备规格名称:36C11680X30411F000600000150001Z
  146. file_name = str(file) # 文件名
  147. # .img和.tar.gz文件
  148. file_type_index = file_name.find('.img')
  149. if file_type_index == -1:
  150. file_type_index = file_name.find('.tar')
  151. if file_type_index == -1:
  152. return response.json(903)
  153. version = file_name[:file_type_index] # 设备版本
  154. version_index = version.rindex('.')
  155. softwareVersion = version[1:version_index] # 软件版本
  156. code = version[version_index + 1:] # 设备规格名称
  157. chipModelList2Code = code[:4] # 主芯片
  158. type = code[8:10] # 设备机型
  159. companyCode = code[-1:] # 公司代码
  160. fileSize = file.size
  161. filePath = '/'.join(('static/otapack', mci, lang, file_name))
  162. file_data = file.read()
  163. fileMd5 = hashlib.md5(file_data).hexdigest()
  164. data_dict = {'mci': mci, 'lang': lang, 'ESN': ESN, 'max_ver': max_ver, 'channel': channel,
  165. 'resolutionRatio': resolutionRatio, 'Description': Description, 'status': status,
  166. 'is_popup': isPopup, 'version': version, 'softwareVersion': softwareVersion, 'code': code,
  167. 'chipModelList2Code': chipModelList2Code, 'type': type, 'companyCode': companyCode,
  168. 'fileSize': fileSize, 'filePath': filePath, 'fileMd5': fileMd5, 'update_time': nowTime,
  169. 'data_json': data_json, 'auto_update': auto_update}
  170. # Equipment_Version表创建或更新数据
  171. equipment_version_qs = Equipment_Version.objects.filter(code=code, lang=lang)
  172. if not equipment_version_qs.exists():
  173. Equipment_Version.objects.create(eid=CommonService.getUserID(getUser=False, setOTAID=True),
  174. **data_dict)
  175. else:
  176. equipment_version_qs.update(**data_dict)
  177. # 上传文件到服务器
  178. upload_path = '/'.join((BASE_DIR, 'static/otapack', mci, lang)).replace('\\', '/') + '/'
  179. if not os.path.exists(upload_path): # 上传目录不存在则创建
  180. os.makedirs(upload_path)
  181. # 文件上传
  182. full_name = upload_path + file_name
  183. if os.path.exists(full_name): # 删除同名文件
  184. os.remove(full_name)
  185. with open(full_name, 'wb+') as write_file:
  186. for chunk in file.chunks():
  187. write_file.write(chunk)
  188. LOGGER.info('versionManagement/upLoadFile成功上传{}'.format(file_name))
  189. if not DeviceVersionInfo.objects.filter(d_code=code, software_ver=softwareVersion).exists():
  190. return response.json(0, "该版本尚未添加设备版本信息")
  191. return response.json(0)
  192. except Exception as e:
  193. LOGGER.info(
  194. 'versionManagement/upLoadFile接口异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  195. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  196. def editVersionInformation(self, request_dict, response):
  197. eid = request_dict.get('eid', None)
  198. ESN = request_dict.get('ESN', '')
  199. max_ver = request_dict.get('max_ver', '')
  200. status = request_dict.get('status', '')
  201. channel = request_dict.get('channel', '')
  202. resolutionRatio = request_dict.get('resolutionRatio', '')
  203. Description = request_dict.get('Description', '')
  204. is_popup = request_dict.get('is_popup', '')
  205. auto_update = request_dict.get('autoUpdate', 0)
  206. data_json = request_dict.get('dataJson', None)
  207. if not eid:
  208. return response.json(444)
  209. status = 1 if status == 'true' else 0
  210. if data_json:
  211. data_json = eval(data_json)
  212. try:
  213. equipment_version_qs = Equipment_Version.objects.filter(eid=eid)
  214. if not equipment_version_qs.exists():
  215. return response.json(173)
  216. data_dict = {'ESN': ESN, 'max_ver': max_ver, 'status': status, 'channel': channel,
  217. 'auto_update': auto_update, 'data_json': data_json,
  218. 'resolutionRatio': resolutionRatio, 'Description': Description, 'is_popup': is_popup}
  219. equipment_version_qs.update(**data_dict)
  220. return response.json(0)
  221. except Exception as e:
  222. print(e)
  223. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  224. def deleteEquipmentVersion(self, request_dict, response):
  225. eid = request_dict.get('eid', None)
  226. if not eid:
  227. return response.json(444)
  228. try:
  229. equipment_version_qs = Equipment_Version.objects.filter(eid=eid)
  230. filePath = equipment_version_qs.values('filePath')[0]['filePath']
  231. equipment_version_qs.delete()
  232. # 删除文件
  233. full_name = '/'.join((BASE_DIR, filePath)).replace('\\', '/')
  234. if os.path.exists(full_name):
  235. os.remove(full_name)
  236. return response.json(0)
  237. except Exception as e:
  238. print(e)
  239. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  240. def getAppVersionList(self, request_dict, response):
  241. app_type = request_dict.get('app_type', None)
  242. appName = request_dict.get('appName', None)
  243. version = request_dict.get('version', None)
  244. pageNo = request_dict.get('pageNo', None)
  245. pageSize = request_dict.get('pageSize', None)
  246. if not all([pageNo, pageSize]):
  247. return response.json(444)
  248. page = int(pageNo)
  249. line = int(pageSize)
  250. try:
  251. app_info_qs = App_Info.objects.filter()
  252. if app_type:
  253. if app_type == 'IOS':
  254. app_type = 1
  255. elif app_type == '安卓':
  256. app_type = 2
  257. else:
  258. app_type = 3
  259. app_info_qs = app_info_qs.filter(app_type=app_type)
  260. if appName:
  261. app_info_qs = app_info_qs.filter(appName__contains=appName)
  262. if version:
  263. app_info_qs = app_info_qs.filter(version__contains=version)
  264. total = app_info_qs.count()
  265. app_info_qs = app_info_qs.values()[(page - 1) * line:page * line]
  266. app_info_list = CommonService.qs_to_list(app_info_qs)
  267. return response.json(0, {'list': app_info_list, 'total': total})
  268. except Exception as e:
  269. print(e)
  270. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  271. def addOrEditAppInfo(self, request_dict, response):
  272. id = request_dict.get('id', None)
  273. appName = request_dict.get('appName', '')
  274. appBundleId = request_dict.get('appBundleId', '')
  275. bundleVersion = request_dict.get('bundleVersion', '')
  276. newAppversion = request_dict.get('newAppversion', '')
  277. minAppversion = request_dict.get('minAppversion', '')
  278. content = request_dict.get('content', '')
  279. app_type = request_dict.get('app_type', '')
  280. downloadLink = request_dict.get('downloadLink', '')
  281. try:
  282. app_type = int(app_type)
  283. data_dict = {'appName': appName, 'appBundleId': appBundleId, 'bundleVersion': bundleVersion,
  284. 'newAppversion': newAppversion, 'minAppversion': minAppversion, 'content': content,
  285. 'app_type': app_type, 'downloadLink': downloadLink}
  286. if not id: # 添加
  287. App_Info.objects.create(**data_dict)
  288. else: # 编辑
  289. app_info_qs = App_Info.objects.filter(id=id)
  290. if not app_info_qs.exists():
  291. return response.json(173)
  292. app_info_qs.update(**data_dict)
  293. return response.json(0)
  294. except Exception as e:
  295. print(e)
  296. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  297. def deleteAppVersion(self, request_dict, response):
  298. appBundleId = request_dict.get('appBundleId', None)
  299. if not appBundleId:
  300. return response.json(444)
  301. try:
  302. App_Info.objects.filter(appBundleId=appBundleId).delete()
  303. AppSetModel.objects.filter(appBundleId=appBundleId).delete()
  304. return response.json(0)
  305. except Exception as e:
  306. print(e)
  307. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  308. def getAppSet(self, request_dict, response):
  309. appBundleId = request_dict.get('appBundleId', None)
  310. if not appBundleId:
  311. return response.json(444)
  312. try:
  313. app_set_qs = AppSetModel.objects.filter(appBundleId=appBundleId).values('content')
  314. if app_set_qs.exists():
  315. content = app_set_qs[0]['content']
  316. return response.json(0, {'content': content})
  317. else:
  318. nowTime = int(time.time())
  319. AppSetModel.objects.create(
  320. appBundleId=appBundleId,
  321. addTime=nowTime,
  322. updTime=nowTime
  323. )
  324. return response.json(0)
  325. except Exception as e:
  326. print(e)
  327. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  328. def editAppSet(self, request_dict, response):
  329. appBundleId = request_dict.get('appBundleId', None)
  330. content = request_dict.get('content', None)
  331. if not all([appBundleId, content]):
  332. return response.json(444)
  333. try:
  334. AppSetModel.objects.filter(appBundleId=appBundleId).update(content=content)
  335. return response.json(0)
  336. except Exception as e:
  337. print(e)
  338. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  339. def getAppRecordList_1(self, request_dict, response):
  340. app_type = request_dict.get('app_type', 'IOS')
  341. pageNo = request_dict.get('pageNo', None)
  342. pageSize = request_dict.get('pageSize', None)
  343. if not all([pageNo, pageSize]):
  344. return response.json(444)
  345. page = int(pageNo)
  346. line = int(pageSize)
  347. try:
  348. if app_type:
  349. if app_type == 'IOS':
  350. app_type = 1
  351. elif app_type == '安卓':
  352. app_type = 2
  353. else:
  354. app_type = 3
  355. app_colophon_qs = App_Colophon.objects.filter(app_id__app_type=app_type).order_by('app_id').values_list('app_id__appBundleId', flat=True).distinct()
  356. if not app_colophon_qs.exists():
  357. return response.json(173)
  358. total = app_colophon_qs.count()
  359. app_colophon_list = list(app_colophon_qs[(page - 1) * line:page * line])
  360. app_info_qs = App_Colophon.objects.filter(app_id__appBundleId__in=app_colophon_list).\
  361. values("id", "lang", "newApp_version", "content","version_time", "app_id__appBundleId", "app_id__appName", "app_id__app_type")
  362. app_info_list = list(app_info_qs)
  363. data_dict = {}
  364. # 组装数据
  365. for app_info in app_info_list:
  366. for app_colophon in app_colophon_list:
  367. if app_colophon not in data_dict.keys():
  368. data_dict[app_colophon] = []
  369. if app_colophon == app_info['app_id__appBundleId']:
  370. data_dict[app_colophon].append(app_info)
  371. for k, v in enumerate(data_dict):
  372. new = sorted(data_dict[v], key=lambda x: x['id'], reverse=True)
  373. data_dict[v] = new
  374. res = {
  375. 'data_dict': data_dict,
  376. 'total': total
  377. }
  378. return response.json(0, res)
  379. except Exception as e:
  380. print(e)
  381. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  382. def getAppRecordList(self, request_dict, response):
  383. app_type = request_dict.get('appType', 'IOS')
  384. queryVersion = request_dict.get('queryVersion', None)
  385. queryAppBundleId = request_dict.get('queryAppBundleId', None)
  386. pageNo = request_dict.get('pageNo', None)
  387. pageSize = request_dict.get('pageSize', None)
  388. if not all([pageNo, pageSize]):
  389. return response.json(444)
  390. page = int(pageNo)
  391. line = int(pageSize)
  392. try:
  393. if app_type == 'IOS':
  394. app_type = 1
  395. elif app_type == '安卓':
  396. app_type = 2
  397. else:
  398. app_type = 3
  399. app_colophon_qs = App_Colophon.objects.filter(app_id__app_type=app_type).order_by('app_id').values_list('app_id__appBundleId', flat=True).distinct()
  400. if not app_colophon_qs.exists():
  401. return response.json(173)
  402. total = app_colophon_qs.count()
  403. app_colophon_list = list(app_colophon_qs[(page - 1) * line:page * line])
  404. app_info_qs = App_Colophon.objects.filter(app_id__appBundleId__in=app_colophon_list).\
  405. values("id", "lang", "newApp_version", "content", "version_time", "app_id__appBundleId", "app_id__appName", "app_id__app_type")
  406. app_info_list = list(app_info_qs)
  407. app_record_list = [] # 响应的app record数据
  408. appBundleId_list = [] # 记录已添加过的appBundleId
  409. # 组装数据
  410. for app_info in app_info_list:
  411. version = app_info['lang'] + app_info['newApp_version']
  412. if app_info['app_id__appBundleId'] not in appBundleId_list:
  413. appBundleId_list.append(app_info['app_id__appBundleId'])
  414. newApp_version_list = [[app_info['lang'], app_info['newApp_version']]]
  415. app_record_dict = {
  416. 'app_id__appBundleId': app_info['app_id__appBundleId'],
  417. 'app_id__appName': app_info['app_id__appName'],
  418. 'app_id__app_type': app_info['app_id__app_type'],
  419. 'version': version,
  420. 'newApp_version_list': newApp_version_list,
  421. 'id': app_info['id'],
  422. 'content': app_info['content'],
  423. 'version_time': time.strftime("%Y-%m-%d", time.localtime(app_info['version_time'])),
  424. }
  425. if queryVersion and queryVersion == version and queryAppBundleId == app_info['app_id__appBundleId']:
  426. app_record_dict['id'] = app_info['id']
  427. app_record_dict['content'] = app_info['content']
  428. app_record_dict['version_time'] = time.strftime("%Y-%m-%d", time.localtime(app_info['version_time']))
  429. app_record_list.append(app_record_dict)
  430. else:
  431. index = appBundleId_list.index(app_info['app_id__appBundleId'])
  432. newApp_version_list = [app_info['lang'], app_info['newApp_version']]
  433. if queryVersion and queryVersion == version and queryAppBundleId == app_info['app_id__appBundleId']:
  434. # app_record_list里对应字典插入值
  435. app_record_list[index]['id'] = app_info['id']
  436. app_record_list[index]['content'] = app_info['content']
  437. app_record_list[index]['version_time'] = time.strftime("%Y-%m-%d", time.localtime(app_info['version_time']))
  438. app_record_list[index]['version'] = version
  439. app_record_list[index]['newApp_version_list'].insert(0, newApp_version_list)
  440. else:
  441. app_record_list[index]['newApp_version_list'].append(newApp_version_list)
  442. res = {
  443. 'app_record_list': app_record_list,
  444. 'total': total
  445. }
  446. return response.json(0, res)
  447. except Exception as e:
  448. print(e)
  449. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  450. def getAppBundleIdList(self, request_dict, response):
  451. print('request_dict:', request_dict)
  452. app_type = request_dict.get('appType', 'IOS')
  453. try:
  454. if app_type == 'IOS':
  455. app_type = 1
  456. elif app_type == '安卓':
  457. app_type = 2
  458. else:
  459. app_type = 3
  460. app_info_qs = App_Info.objects.filter(app_type=app_type).values('id', 'appBundleId')
  461. appBundleIdList = list(app_info_qs)
  462. return response.json(0, {'appBundleIdList': appBundleIdList})
  463. except Exception as e:
  464. print(e)
  465. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  466. def addOrEditAppRecord(self, request_dict, response):
  467. print('request_dict:', request_dict)
  468. appBundleId = request_dict.get('app_id__appBundleId', None)
  469. newApp_version = request_dict.get('version', None)
  470. version_time = request_dict.get('version_time', None)
  471. cn_content = request_dict.get('cnContent', None)
  472. en_content = request_dict.get('enContent', None)
  473. content = request_dict.get('content', None)
  474. app_colophon_id = request_dict.get('id', None)
  475. if not all([appBundleId, newApp_version, version_time]):
  476. return response.json(444)
  477. try:
  478. version_time = int(time.mktime(time.strptime(version_time, '%Y-%m-%d'))) # 字符串转时间戳
  479. if app_colophon_id: # 编辑
  480. # 编辑获取的版本信息前两位为语言
  481. lang = newApp_version[:2]
  482. newApp_version = newApp_version[2:]
  483. App_Colophon.objects.filter(id=app_colophon_id).update(lang=lang, newApp_version=newApp_version,
  484. content=content, version_time=version_time)
  485. else: # 添加
  486. app_info_qs = App_Info.objects.filter(appBundleId=appBundleId).values('id')
  487. if not app_info_qs.exists():
  488. return response.json(173)
  489. data_dict = {
  490. 'app_id_id': app_info_qs[0]['id'],
  491. 'newApp_version': newApp_version,
  492. 'version_time': version_time,
  493. 'lang': 'cn',
  494. 'content': cn_content,
  495. }
  496. with transaction.atomic():
  497. # 创建中文内容数据
  498. App_Colophon.objects.create(**data_dict)
  499. # 创建英文内容数据
  500. data_dict['lang'] = 'en'
  501. data_dict['content'] = en_content
  502. App_Colophon.objects.create(**data_dict)
  503. return response.json(0)
  504. except Exception as e:
  505. print(e)
  506. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  507. def deleteAppRecord(self, request_dict, response):
  508. print('request_dict:', request_dict)
  509. app_colophon_id = request_dict.get('app_colophon_id', None)
  510. try:
  511. if not app_colophon_id:
  512. return response.json(444)
  513. app_colophon_qs = App_Colophon.objects.filter(id=app_colophon_id)
  514. if not app_colophon_qs.exists():
  515. return response.json(173)
  516. app_colophon_qs.delete()
  517. return response.json(0)
  518. except Exception as e:
  519. print(e)
  520. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  521. def getPcInfoList(self, request_dict, response):
  522. print('request_dict:', request_dict)
  523. pc_name = request_dict.get('pcName', None)
  524. pageNo = request_dict.get('pageNo', None)
  525. pageSize = request_dict.get('pageSize', None)
  526. if not all([pageNo, pageSize]):
  527. return response.json(444)
  528. page = int(pageNo)
  529. line = int(pageSize)
  530. try:
  531. pc_info_qs = Pc_Info.objects.filter()
  532. if pc_name:
  533. pc_info_qs = Pc_Info.objects.filter(pc_name__contains=pc_name)
  534. if not pc_info_qs.exists():
  535. return response.json(173)
  536. total = pc_info_qs.count()
  537. pc_info_qs = pc_info_qs.values()[(page - 1) * line:page * line]
  538. pc_info_list = CommonService.qs_to_list(pc_info_qs)
  539. return response.json(0, {'list': pc_info_list, 'total': total})
  540. except Exception as e:
  541. print(e)
  542. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  543. def editPcVersion(self, request_dict, response):
  544. pc_info_id = request_dict.get('id', None)
  545. pc_name = request_dict.get('pc_name', '')
  546. bundle_version = request_dict.get('bundle_version', '')
  547. pc_version = request_dict.get('pc_version', '')
  548. pc_test = request_dict.get('pc_test', '')
  549. lang = request_dict.get('lang', '')
  550. file_type = request_dict.get('file_type', '')
  551. package = request_dict.get('package', '')
  552. explain = request_dict.get('explain', '')
  553. is_update = request_dict.get('is_update', '')
  554. is_open = request_dict.get('is_open', '')
  555. content = request_dict.get('content', '')
  556. authority = request_dict.get('authority', '')
  557. download_link = request_dict.get('download_link', '')
  558. if not pc_info_id:
  559. return response.json(444)
  560. try:
  561. pc_info_qs = Pc_Info.objects.filter(id=pc_info_id)
  562. if not pc_info_qs.exists():
  563. return response.json(173)
  564. data_dict = {
  565. 'pc_name': pc_name,
  566. 'bundle_version': bundle_version,
  567. 'pc_version': pc_version,
  568. 'pc_test': pc_test,
  569. 'lang': lang,
  570. 'file_type': file_type,
  571. 'package': package,
  572. 'explain': explain,
  573. 'is_update': is_update,
  574. 'is_open': is_open,
  575. 'content': content,
  576. 'authority': authority,
  577. 'download_link': download_link,
  578. }
  579. pc_info_qs.update(**data_dict)
  580. return response.json(0)
  581. except Exception as e:
  582. print(e)
  583. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  584. def deletePcInfo(self, request_dict, response):
  585. print('request_dict:', request_dict)
  586. pc_info_id = request_dict.get('id', None)
  587. try:
  588. if not pc_info_id:
  589. return response.json(444)
  590. pc_info_qs = Pc_Info.objects.filter(id=pc_info_id)
  591. if not pc_info_qs.exists():
  592. return response.json(173)
  593. # 删除存储桶的文件
  594. Key = pc_info_qs.values('download_link')[0]['download_link']
  595. pc_info_qs.delete()
  596. aws_s3_client = boto3.client(
  597. 's3',
  598. region_name='cn-northwest-1',
  599. aws_access_key_id=AWS_ACCESS_KEY_ID[0],
  600. aws_secret_access_key=AWS_SECRET_ACCESS_KEY[0],
  601. config=botocore.client.Config(signature_version='s3v4'),
  602. )
  603. try:
  604. aws_s3_client.delete_object(Bucket='pc-package', Key=Key)
  605. finally:
  606. return response.json(0)
  607. except Exception as e:
  608. print(e)
  609. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  610. def getCountryList(self, request_dict, response):
  611. try:
  612. country_qs = CountryModel.objects.all().values_list('country_name', flat=True)
  613. return response.json(0, list(country_qs))
  614. except Exception as e:
  615. print(e)
  616. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  617. @classmethod
  618. def device_auto_update(cls, user_id, request_dict, response):
  619. verId = request_dict.get('verId', None)
  620. if not verId:
  621. return response.json(444)
  622. LOGGER.info(f'版本ID:{verId},操作用户{user_id}')
  623. version_qs = Equipment_Version.objects.filter(eid=verId, auto_update=True, status=1)
  624. if not version_qs.exists():
  625. return response.json(173)
  626. agent_thread = threading.Thread(
  627. target=VersionManagement().device_async_update,
  628. args=(version_qs.first(),) # 取单个对象
  629. )
  630. agent_thread.start()
  631. return response.json(0)
  632. @staticmethod
  633. def device_async_update(version_qs):
  634. code = version_qs.code
  635. version = version_qs.softwareVersion
  636. ver_data = version_qs.data_json
  637. uid_set_qs = UidSetModel.objects.filter(ucode=code).exclude(version=version) \
  638. .values('uid', 'ip', 'version')
  639. LOGGER.info(f'通知设备自动升级查询符合数量{uid_set_qs.count()}')
  640. if not uid_set_qs.exists():
  641. return
  642. try:
  643. # 创建分页器,每页100条数据
  644. paginator = Paginator(uid_set_qs, 100)
  645. # 遍历每一页数据
  646. for page_num in range(1, paginator.num_pages + 1):
  647. # 获取当前页的数据
  648. page_data = paginator.page(page_num)
  649. # 遍历当前页的每一条数据
  650. for data in page_data:
  651. uid = data['uid']
  652. result = VersionManagement.check_version_auto_update(uid, '', ver_data)
  653. if not result:
  654. LOGGER.info(f'{uid}判断是否符合自动升级:{result}')
  655. continue
  656. serial_number = CommonService.get_serial_number_by_uid(uid)
  657. now_ver = data['version']
  658. # 将版本号带V字符换成空
  659. now_ver_clean = now_ver.replace("V", "")
  660. # 采用packaging模块中的Version类进行版本号比较
  661. now_version = pacVer.Version(now_ver_clean)
  662. new_version = pacVer.Version(version)
  663. if new_version > now_version: # 新版本大于当前设备版本执行MQTT发送升级
  664. VersionManagement.send_auto_update_url(version_qs, uid, serial_number, code, now_ver)
  665. LOGGER.info(f'uid={uid},ucode={code},当前版本{now_ver},最新版本{version}')
  666. except Exception as e:
  667. LOGGER.error(f'异步自动升级异常:ucode:{code},error:{repr(e)}')
  668. @classmethod
  669. def check_version_auto_update(cls, uid, user_id, ver_data):
  670. """
  671. :param uid: 设备UID
  672. :param user_id: 用户id
  673. :param ver_data: 版本指定升级数据
  674. :return: True | False
  675. """
  676. try:
  677. if not ver_data: # 过滤值默认当前版本所有设备自动升级
  678. return True
  679. if uid and ver_data['uid_list']: # 当前版本指定UID
  680. return uid in ver_data['uid_list']
  681. if user_id and ver_data['country_list'] and CONFIG_INFO not in [CONFIG_CN, CONFIG_TEST]: # 当前版本指定用户国家
  682. user_qs = Device_User.objects.filter(userID=user_id).values('region_country')
  683. if not user_qs.exists(): # 用户不存在不升级
  684. return False
  685. if user_qs[0]['region_country'] == 0: # 用户未选择国家 不进行升级提示
  686. return False
  687. return user_qs[0]['region_country'] in ver_data['country_list']
  688. if CONFIG_INFO in [CONFIG_CN, CONFIG_TEST] and ver_data['addr']: # 中国区可指定城市
  689. city_list = ver_data['addr']['city_list']
  690. if not city_list:
  691. return True
  692. uid_set_qs = UidSetModel.objects.filter(uid=uid).values('ip')
  693. if not uid_set_qs:
  694. return False
  695. ip_qs = IPAddr.objects.filter(ip=uid_set_qs[0]['ip']).values('city').order_by('-id')
  696. if not ip_qs:
  697. return False
  698. return ip_qs[0]['city'] in city_list
  699. return False
  700. except Exception as e:
  701. LOGGER.error(f'检测是否符合自动升级{repr(e)}')
  702. return False
  703. @staticmethod
  704. def send_auto_update_url(version_qs, uid, serial_number, code, now_ver):
  705. """
  706. 发送自动升级URL
  707. @param version_qs: 最新版本querySet
  708. @param uid: 设备UID
  709. @param serial_number: 设备序列号
  710. @param code: 设备规格码
  711. @param now_ver: 设备当前版本好
  712. @return:
  713. """
  714. try:
  715. file_path = version_qs.filePath
  716. version = version_qs.version
  717. mci = version_qs.mci
  718. ver = version_qs.softwareVersion
  719. max_ver = version_qs.max_ver
  720. if ver <= max_ver:
  721. user_qs = Device_Info.objects.filter(UID=uid, isShare=False).values('userID_id')
  722. if not user_qs.exists():
  723. LOGGER.info(f'{uid}未添加该设备返回不发MQTT')
  724. return False
  725. user_id = user_qs[0]['userID_id']
  726. # 创建url的token
  727. param_url = "ansjer/" + CommonService.RandomStr(6) + "/" + file_path
  728. data = {'Url': param_url, 'user_id': user_id,
  729. 'uid': uid, 'serial_number': serial_number, 'old_version': "V" + now_ver + "." + code,
  730. 'new_version': version, 'mci': mci}
  731. device_info_value = json.dumps(data)
  732. expire = 600
  733. str_uuid = str(user_id)
  734. if serial_number and serial_number != 'null':
  735. str_uuid += serial_number
  736. elif uid and uid != 'null':
  737. str_uuid += uid
  738. str_uuid += now_ver
  739. device_info_key = 'ASJ:SERVER:VERSION:{}'.format(str_uuid)
  740. LOGGER.info('缓存key={}'.format(device_info_key))
  741. redisObject = RedisObject()
  742. redisObject.set_data(device_info_key, device_info_value, expire)
  743. url_tko = UrlTokenObject()
  744. file_path = url_tko.generate(data={'uid': str_uuid})
  745. url = SERVER_DOMAIN_OTA + 'dlotapack/' + file_path
  746. # 主题名称
  747. topic_name = f'ansjer/generic/{serial_number}'
  748. # 发布消息内容
  749. msg = f'{{"commandCode":1,"data":{{"url":"{url}"}}}}'
  750. result = VersionManagement.publish_to_aws_iot_mqtt(serial_number, topic_name, msg)
  751. LOGGER.info(f'uid={uid}发送数据msg={msg},发送MQTT结果={result}')
  752. return True
  753. return False
  754. except Exception as e:
  755. LOGGER.error(f'自动升级异常{repr(e)}')
  756. return False
  757. @staticmethod
  758. def publish_to_aws_iot_mqtt(identification_code, topic_name, msg, qos=1):
  759. """
  760. 发布消息到AWS IoT MQTT(仅尝试一次,无重试)
  761. @param identification_code: 标识码
  762. @param topic_name: 主题名
  763. @param msg: 消息内容(JSON字符串)
  764. @param qos: QoS等级(0或1)
  765. @return: 成功返回True,失败返回False
  766. """
  767. # 入参校验
  768. if not isinstance(identification_code, str) or not identification_code.strip():
  769. LOGGER.error("标识码为空或无效")
  770. return False
  771. if not isinstance(topic_name, str) or not topic_name.strip():
  772. LOGGER.error("主题名为空或无效")
  773. return False
  774. if qos not in (0, 1):
  775. LOGGER.warning("QoS不合法,默认使用1")
  776. qos = 1
  777. # 生成ThingName
  778. thing_name = f'LC_{identification_code}' if identification_code.endswith(
  779. '11L') else f'Ansjer_Device_{identification_code}'
  780. try:
  781. # 查询设备信息
  782. iot_device = iotdeviceInfoModel.objects.filter(thing_name=thing_name).values('endpoint',
  783. 'token_iot_number').first()
  784. if not iot_device:
  785. LOGGER.error(f"未查询到设备信息:{thing_name}")
  786. return False
  787. endpoint = iot_device.get('endpoint', '').strip()
  788. token = iot_device.get('token_iot_number', '').strip()
  789. if not endpoint or not token:
  790. LOGGER.error("设备信息不完整(endpoint或token缺失)")
  791. return False
  792. # 构造请求
  793. encoded_topic = requests.utils.quote(topic_name, safe='')
  794. request_url = f"https://{endpoint}/topics/{encoded_topic}?qos={qos}"
  795. signature = CommonService.rsa_sign(token)
  796. if not signature:
  797. LOGGER.error("Token签名失败")
  798. return False
  799. headers = {
  800. 'x-amz-customauthorizer-name': 'Ansjer_Iot_Auth',
  801. 'Token': token,
  802. 'x-amz-customauthorizer-signature': signature
  803. }
  804. # 发送请求(仅一次)
  805. response = requests.post(
  806. url=request_url,
  807. headers=headers,
  808. data=msg,
  809. timeout=10 # 10秒超时
  810. )
  811. # 结果判断
  812. if response.status_code == 200:
  813. res_json = response.json()
  814. if res_json.get('message') == 'OK':
  815. LOGGER.info(f"发布成功:{topic_name}")
  816. return True
  817. LOGGER.error(f"响应异常:{res_json}")
  818. else:
  819. LOGGER.error(f"请求失败,状态码:{response.status_code}")
  820. return False
  821. except Exception as e:
  822. LOGGER.error(f"发布失败:{str(e)}")
  823. return False