No Description

mch_views.py 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. # -*- coding: utf-8 -*-
  2. from __future__ import division
  3. import json
  4. from django.conf import settings
  5. from django.contrib.auth.hashers import check_password
  6. from django.db import transaction
  7. from django_logit import logit
  8. from django_response import response
  9. from ipaddr import client_ip
  10. from pywe_miniapp import get_phone_number
  11. from pywe_storage import RedisStorage
  12. from TimeConvert import TimeConvert as tc
  13. from account.models import UserInfo
  14. from coupon.models import CouponInfo, UserCouponInfo
  15. from integral.models import SaleclerkSubmitLogInfo
  16. from logs.models import AdministratorLoginLogInfo, MchInfoEncryptLogInfo
  17. from mch.models import (ActivityInfo, AdministratorInfo, BrandInfo, ConsumeInfoSubmitLogInfo, DistributorInfo,
  18. LatestAppInfo, LatestAppScreenInfo, ModelInfo, OperatorInfo, ConsumeShotUnbindingInfo)
  19. from statistic.models import ConsumeModelSaleStatisticInfo, ConsumeSaleStatisticInfo, ConsumeUserStatisticInfo
  20. from utils.error.errno_utils import (AdministratorStatusCode, OperatorStatusCode, ProductBrandStatusCode,
  21. ProductModelStatusCode, UserStatusCode)
  22. from utils.redis.connect import r
  23. from utils.redis.rkeys import MEMBER_SEND_COUPON_LIST, MEMBER_UPGRADE_INFO, MINI_PROGRAM_GIS_LIST
  24. WECHAT = settings.WECHAT
  25. @logit
  26. def optor_login_api(request):
  27. brand_id = request.POST.get('brand_id') or settings.KODO_DEFAULT_BRAND_ID
  28. if brand_id != settings.KODO_DEFAULT_BRAND_ID:
  29. return response(ProductBrandStatusCode.BRAND_NOT_MATCH)
  30. phone = request.POST.get('phone', '')
  31. password = request.POST.get('password', '')
  32. try:
  33. operator = OperatorInfo.objects.get(phone=phone, status=True)
  34. except OperatorInfo.DoesNotExist:
  35. return response(OperatorStatusCode.OPERATOR_NOT_FOUND)
  36. if operator.user_status == OperatorInfo.DISABLED:
  37. return response(OperatorStatusCode.OPERATOR_NOT_ACTIVATED)
  38. elif operator.user_status == OperatorInfo.DELETED:
  39. return response(OperatorStatusCode.OPERATOR_HAS_DELETED)
  40. if not check_password(password, operator.encryption):
  41. return response(OperatorStatusCode.OPERATOR_PASSWORD_ERROR)
  42. return response(200, 'Optor Login Success', u'操作员登录成功', data=operator.kododata)
  43. @logit
  44. def admin_login_api(request):
  45. brand_id = request.POST.get('brand_id') or settings.KODO_DEFAULT_BRAND_ID
  46. if brand_id != settings.KODO_DEFAULT_BRAND_ID:
  47. return response(ProductBrandStatusCode.BRAND_NOT_MATCH)
  48. phone = request.POST.get('phone', '')
  49. password = request.POST.get('password', '')
  50. try:
  51. administrator = AdministratorInfo.objects.get(phone=phone, status=True)
  52. except AdministratorInfo.DoesNotExist:
  53. return response(AdministratorStatusCode.ADMINISTRATOR_NOT_FOUND)
  54. if administrator.user_status == AdministratorInfo.DISABLED:
  55. return response(AdministratorStatusCode.ADMINISTRATOR_NOT_ACTIVATED)
  56. elif administrator.user_status == AdministratorInfo.DELETED:
  57. return response(AdministratorStatusCode.ADMINISTRATOR_HAS_DELETED)
  58. if not check_password(password, administrator.encryption):
  59. return response(AdministratorStatusCode.ADMINISTRATOR_PASSWORD_ERROR)
  60. AdministratorLoginLogInfo.objects.create(
  61. admin_id=administrator.admin_id,
  62. admin_name=administrator.name,
  63. login_ip=client_ip(request),
  64. login_at=tc.utc_datetime(),
  65. )
  66. request.session['admin_id'] = administrator.admin_id
  67. return response(200, 'Admin Login Success', u'管理员登录成功', data={
  68. 'admin_id': administrator.admin_id,
  69. 'admin_type': administrator.admin_type,
  70. 'admin_name': administrator.name,
  71. 'qrurl': settings.KODO_CLERK_AUTH_URL.format(administrator.brand_id),
  72. })
  73. @logit
  74. def bmd_infos(request):
  75. optor_id = request.POST.get('optor_id', '')
  76. try:
  77. operator = OperatorInfo.objects.get(operator_id=optor_id, status=True)
  78. except OperatorInfo.DoesNotExist:
  79. return response(OperatorStatusCode.OPERATOR_NOT_FOUND)
  80. if operator.user_status == OperatorInfo.DISABLED:
  81. return response(OperatorStatusCode.OPERATOR_NOT_ACTIVATED)
  82. brands = BrandInfo.objects.filter(brand_id=operator.brand_id, status=True).order_by('position')
  83. brands = [brand.data for brand in brands]
  84. tmpmodels = ModelInfo.objects.filter(brand_id=operator.brand_id, display=True, status=True).order_by('position')
  85. models = [model.data for model in tmpmodels]
  86. # jancodes = {model.jancode: model.data for model in tmpmodels}
  87. distributors = DistributorInfo.objects.filter(brand_id=operator.brand_id, status=True).order_by('position')
  88. distributors = [distributor.data for distributor in distributors]
  89. return response(200, data={
  90. 'optor_id': operator.operator_id,
  91. 'brands': brands,
  92. 'models': models,
  93. # 'jancodes': jancodes,
  94. 'distributors': distributors,
  95. })
  96. @logit
  97. def brands_list(request):
  98. brands = BrandInfo.objects.filter(status=True).order_by('position')
  99. brands = [brand.data for brand in brands]
  100. return response(200, data={
  101. 'brands': brands,
  102. })
  103. @logit
  104. def models_list(request):
  105. models = ModelInfo.objects.filter(status=True).order_by('position')
  106. models = [model.data for model in models]
  107. return response(200, data={
  108. 'models': models,
  109. })
  110. @logit
  111. def distributors_list(request):
  112. distributors = DistributorInfo.objects.filter(status=True).order_by('position')
  113. distributors = [distributor.data for distributor in distributors]
  114. return response(200, data={
  115. 'distributors': distributors,
  116. })
  117. @logit
  118. def upgrade_api(request):
  119. """ APP 升级 """
  120. src = request.POST.get('src', '')
  121. if src == 'datascreen':
  122. latestappmodel = LatestAppScreenInfo
  123. else:
  124. latestappmodel = LatestAppInfo
  125. try:
  126. appinfo = latestappmodel.objects.filter(status=True)[0].adr
  127. except IndexError:
  128. appinfo = {
  129. 'latest_version_code': '',
  130. 'latest_version_name': '',
  131. 'latest_url': '',
  132. }
  133. return response(200, 'Get Latest App Success', u'获取最新版信息成功', {
  134. 'appinfo': appinfo,
  135. })
  136. def getPhoneNumber(request):
  137. user_id = request.POST.get('user_id', '')
  138. wxcfg = WECHAT.get('MINIAPP', {})
  139. appid = wxcfg.get('appID')
  140. # Just for compatible because of store session_key has changed
  141. session_key = None if user_id else RedisStorage(r).get('{0}:{1}:sessionKey'.format(appid, ''))
  142. wxcfg = WECHAT.get('MINIAPP', {})
  143. appid = wxcfg.get('appID')
  144. secret = wxcfg.get('appsecret')
  145. iv = request.POST.get('iv', '')
  146. encryptedData = request.POST.get('encryptedData', '')
  147. # {
  148. # "phoneNumber": "13580006666",
  149. # "purePhoneNumber": "13580006666",
  150. # "countryCode": "86",
  151. # "watermark":
  152. # {
  153. # "appid": "APPID",
  154. # "timestamp": TIMESTAMP
  155. # }
  156. # }
  157. phone_number = get_phone_number(appid=appid, secret=secret, unid=user_id, session_key=session_key, encryptedData=encryptedData, iv=iv, storage=RedisStorage(r))
  158. return phone_number.get('purePhoneNumber', '')
  159. @logit(res=True)
  160. def consumer_phone_api(request):
  161. return response(200, 'Get Consumer Phone Success', u'获取消费者手机号成功', {
  162. 'purePhoneNumber': getPhoneNumber(request),
  163. })
  164. @logit(res=True)
  165. @transaction.atomic
  166. def consumer_info_api(request):
  167. user_id = request.POST.get('user_id', '')
  168. lat = request.POST.get('lat', .0)
  169. lon = request.POST.get('lon', .0)
  170. brandID = request.POST.get('brand_id', '') or request.POST.get('BrandID', '')
  171. modelID = request.POST.get('ModelID', '')
  172. distributorID = request.POST.get('DistributorID', '')
  173. serialNo = request.POST.get('SerialNo', '')
  174. verifyResult = request.POST.get('verifyResult', 0)
  175. code_version = request.POST.get('code_version', '')
  176. activities = json.loads(request.POST.get('activities', '[]'))
  177. if lat == 'undefined':
  178. lat = .0
  179. if lon == 'undefined':
  180. lon = .0
  181. # 校验用户是否存在
  182. try:
  183. user = UserInfo.objects.select_for_update().get(user_id=user_id)
  184. except UserInfo.DoesNotExist:
  185. return response(UserStatusCode.USER_NOT_FOUND)
  186. try:
  187. brand = BrandInfo.objects.get(brand_id=brandID)
  188. except BrandInfo.DoesNotExist:
  189. brand = None
  190. if not brand:
  191. try:
  192. brand = BrandInfo.objects.get(pk=brandID)
  193. except BrandInfo.DoesNotExist:
  194. return response(ProductBrandStatusCode.BRAND_NOT_FOUND)
  195. except ValueError:
  196. return response(ProductBrandStatusCode.BRAND_NOT_FOUND)
  197. try:
  198. model = ModelInfo.objects.get(pk=modelID)
  199. except ModelInfo.DoesNotExist:
  200. return response(ProductModelStatusCode.MODEL_NOT_FOUND)
  201. except ValueError:
  202. return response(ProductModelStatusCode.MODEL_NOT_FOUND)
  203. # 是否是新二维码,即统览码2
  204. if not code_version:
  205. code_version = 1
  206. code = filter(lambda ch: ch in '0123456789', serialNo)
  207. encrypt_logs = MchInfoEncryptLogInfo.objects.filter(model_pk=model.pk, sn=code)
  208. if encrypt_logs:
  209. code_version = encrypt_logs[0].version
  210. user.code_version = code_version
  211. user.save()
  212. # try:
  213. # distributor = DistributorInfo.objects.get(pk=distributorID)
  214. # except DistributorInfo.DoesNotExist:
  215. # return response(ProductDistributorStatusCode.DISTRIBUTOR_NOT_FOUND)
  216. # except ValueError:
  217. # return response(ProductDistributorStatusCode.DISTRIBUTOR_NOT_FOUND)
  218. dupload = ConsumeInfoSubmitLogInfo.objects.filter(
  219. brand_id=brand.brand_id,
  220. model_id=model.model_id,
  221. # distributor_id=distributor.distributor_id,
  222. distributor_id='',
  223. serialNo=serialNo,
  224. verifyResult=1,
  225. test_user=False,
  226. status=True
  227. ).exists()
  228. if dupload:
  229. act = None
  230. during_activity = False
  231. else:
  232. during_activity = True if activities else False
  233. # 更新销售员提交的表
  234. SaleclerkSubmitLogInfo.objects.filter(code=serialNo, model_pk=model.pk, status=True).update(has_scan=True)
  235. ymd = tc.local_string(format='%Y%m%d')
  236. # 记录用户信息提交记录
  237. log = ConsumeInfoSubmitLogInfo.objects.create(
  238. user_id=user_id,
  239. phone=user.phone,
  240. lat=lat,
  241. lon=lon,
  242. brand_id=brand.brand_id,
  243. brand_name=brand.brand_name,
  244. model_id=model.model_id,
  245. model_name=model.model_name,
  246. model_uni_name=model.model_uni_name,
  247. # distributor_id=distributor.distributor_id,
  248. # distributor_name=distributor.distributor_name,
  249. distributor_id='',
  250. distributor_name='',
  251. serialNo=serialNo,
  252. verifyResult=verifyResult,
  253. dupload=dupload,
  254. test_user=user.test_user,
  255. code_version=code_version,
  256. ym=ymd[:6],
  257. ymd=ymd,
  258. province=user.province_name if lat == 0.0 and lon == 0.0 else '',
  259. submit_during_activity=during_activity
  260. )
  261. if not dupload:
  262. for act in activities:
  263. try:
  264. coupon = CouponInfo.objects.get(coupon_id=act['coupon_id'])
  265. except CouponInfo.DoesNotExist:
  266. continue
  267. try:
  268. activity = ActivityInfo.objects.get(activity_id=act['activity_id'])
  269. except ActivityInfo.DoesNotExist:
  270. continue
  271. UserCouponInfo.objects.create(
  272. brand_id=coupon.brand_id,
  273. brand_name=coupon.brand_name,
  274. coupon_id=coupon.coupon_id,
  275. user_id=user_id,
  276. coupon_title=coupon.coupon_title,
  277. coupon_detail=coupon.coupon_detail,
  278. coupon_value=coupon.coupon_value,
  279. coupon_image=coupon.coupon_image,
  280. active_at=tc.utc_datetime(),
  281. expire_at=coupon.final_expire_at,
  282. is_coupon_admin_writeoff=coupon.is_coupon_admin_writeoff,
  283. coupon_valid_period=coupon.coupon_valid_period,
  284. coupon_limit_model_ids=coupon.coupon_limit_model_ids,
  285. coupon_from='PROMOTION',
  286. activity_id=activity.activity_id,
  287. activity_name=activity.activity_name,
  288. submit_pk=log.pk,
  289. )
  290. # 更新注册时间
  291. if not dupload:
  292. user.resgister_at = log.created_at
  293. user.save()
  294. if not user.test_user and not dupload:
  295. # TODO: Make statistic async
  296. cusi, _ = ConsumeUserStatisticInfo.objects.get_or_create(
  297. brand_id=brand.brand_id,
  298. ymd=ymd,
  299. )
  300. cusi.users = list(set(cusi.users + [log.user_id]))
  301. cusi.num = len(cusi.users)
  302. cusi.save()
  303. cusi, _ = ConsumeUserStatisticInfo.objects.get_or_create(
  304. brand_id=brand.brand_id,
  305. ymd=ymd[:6],
  306. )
  307. cusi.users = list(set(cusi.users + [log.user_id]))
  308. cusi.num = len(cusi.users)
  309. cusi.save()
  310. cusi, _ = ConsumeUserStatisticInfo.objects.get_or_create(
  311. brand_id=brand.brand_id,
  312. ymd=ymd[:4],
  313. )
  314. cusi.users = list(set(cusi.users + [log.user_id]))
  315. cusi.num = len(cusi.users)
  316. cusi.save()
  317. # 日销量统计
  318. cssi, _ = ConsumeSaleStatisticInfo.objects.select_for_update().get_or_create(
  319. brand_id=brand.brand_id,
  320. ymd=ymd,
  321. )
  322. cssi.num += 1
  323. cssi.save()
  324. # 月销量统计
  325. cssi, _ = ConsumeSaleStatisticInfo.objects.select_for_update().get_or_create(
  326. brand_id=brand.brand_id,
  327. ymd=ymd[:6],
  328. )
  329. cssi.num += 1
  330. cssi.save()
  331. # 年销量统计
  332. cssi, _ = ConsumeSaleStatisticInfo.objects.select_for_update().get_or_create(
  333. brand_id=brand.brand_id,
  334. ymd=ymd[:4],
  335. )
  336. cssi.num += 1
  337. cssi.save()
  338. # 日型号销量统计
  339. cmssi, _ = ConsumeModelSaleStatisticInfo.objects.select_for_update().get_or_create(
  340. brand_id=brand.brand_id,
  341. model_name=model.model_uni_name,
  342. ymd=ymd,
  343. )
  344. cmssi.users = list(set(cmssi.users + [user_id]))
  345. cmssi.num = len(cmssi.users)
  346. cmssi.save()
  347. # 月型号销量统计
  348. cmssi, _ = ConsumeModelSaleStatisticInfo.objects.select_for_update().get_or_create(
  349. brand_id=brand.brand_id,
  350. model_name=model.model_uni_name,
  351. ymd=ymd[:6],
  352. )
  353. cmssi.users = list(set(cmssi.users + [user_id]))
  354. cmssi.num = len(cmssi.users)
  355. cmssi.save()
  356. # 年型号销量统计
  357. cmssi, _ = ConsumeModelSaleStatisticInfo.objects.select_for_update().get_or_create(
  358. brand_id=brand.brand_id,
  359. model_name=model.model_uni_name,
  360. ymd=ymd[:4],
  361. )
  362. cmssi.users = list(set(cmssi.users + [user_id]))
  363. cmssi.num = len(cmssi.users)
  364. cmssi.save()
  365. r.rpushjson(MINI_PROGRAM_GIS_LIST, {
  366. 'brand_id': log.brand_id,
  367. 'user_id': log.user_id,
  368. 'lat': log.lat,
  369. 'lon': log.lon,
  370. 'phone': log.phone,
  371. 'ymd': ymd,
  372. 'serialNo': serialNo,
  373. 'model_id': model.model_id,
  374. 'pk': log.pk,
  375. })
  376. if not dupload:
  377. log.integral = model.shot_member_integral
  378. log.save()
  379. user.shots_num += 1
  380. user.integral += model.shot_member_integral
  381. # 配件不增加会员等级,只发放积分。
  382. to_send_coupon = False
  383. if user.level < UserInfo.MEMBER_BLACK_GOLD and model.shot_type_id != 'V6PkivthL4sdADp4GNpQ4C':
  384. user.level += 1
  385. to_send_coupon = True
  386. user.save()
  387. if to_send_coupon:
  388. # 发放会员权益
  389. r.rpushjson(MEMBER_SEND_COUPON_LIST, {
  390. 'brand_id': brand.brand_id,
  391. 'user_id': user_id,
  392. })
  393. # 会员升级提示
  394. r.set(MEMBER_UPGRADE_INFO % (brand.brand_id, user_id), 1)
  395. return response(200, 'Submit Consumer Info Success', u'提交消费者信息成功')
  396. @logit(res=True)
  397. @transaction.atomic
  398. def consumer_snlist_api(request):
  399. user_id = request.POST.get('user_id', '')
  400. # 校验用户是否存在
  401. try:
  402. user = UserInfo.objects.get(user_id=user_id)
  403. except UserInfo.DoesNotExist:
  404. return response(UserStatusCode.USER_NOT_FOUND)
  405. # 用户信息提交列表
  406. logs = ConsumeInfoSubmitLogInfo.objects.filter(user_id=user_id, status=True).distinct()
  407. seen = set()
  408. seen_add = seen.add
  409. logs = [log.data for log in logs if not ((log.serialNo, log.model_name) in seen or seen_add((log.serialNo, log.model_name)))]
  410. return response(200, 'Get Consumer Submit List Success', u'获取消费者提交列表成功', {
  411. 'logs': logs,
  412. })
  413. @logit(res=True)
  414. @transaction.atomic
  415. def consumer_model_list(request):
  416. user_id = request.POST.get('user_id', '')
  417. # 校验用户是否存在
  418. try:
  419. user = UserInfo.objects.get(user_id=user_id)
  420. except UserInfo.DoesNotExist:
  421. return response(UserStatusCode.USER_NOT_FOUND)
  422. # 返回型号列表
  423. models = ModelInfo.objects.filter(shot_type_id__isnull=False, status=True).order_by('-shot_member_name')
  424. models = [model.consumer_shot_data for model in models]
  425. return response(200, 'Get Model List Success', u'获取型号列表成功', {
  426. 'models': models,
  427. })
  428. @logit(res=True)
  429. @transaction.atomic
  430. def consumer_shot_unbinding(request):
  431. user_id = request.POST.get('user_id', '')
  432. submit_pk = request.POST.get('id', '')
  433. submit_at = request.POST.get('created_at', '')
  434. model_id = request.POST.get('model_id', '')
  435. sn = request.POST.get('serialNo', '')
  436. reason = request.POST.get('reason', '')
  437. # 校验用户是否存在
  438. try:
  439. user = UserInfo.objects.get(user_id=user_id)
  440. except UserInfo.DoesNotExist:
  441. return response(UserStatusCode.USER_NOT_FOUND)
  442. ConsumeShotUnbindingInfo.objects.update_or_create(user_id=user_id, submit_pk=submit_pk, defaults={
  443. 'model_id': model_id,
  444. 'sn': sn,
  445. 'submit_at': submit_at,
  446. 'reason': reason,
  447. })
  448. return response(200, 'Consume Shot Unbinding Success', u'消费者镜头解绑成功')