MarketCode

huangqimin 5 年之前
父節點
當前提交
212f24c882

+ 114 - 0
api/encrypt_views.py

@@ -4,16 +4,26 @@ from __future__ import division
4 4
 
5 5
 import random
6 6
 
7
+from django.conf import settings
8
+from django.db import transaction
7 9
 from django_logit import logit
8 10
 from django_response import response
11
+from pywe_marketcode import tickettocode
12
+from pywe_storage import RedisStorage
9 13
 
14
+from account.models import UserInfo
10 15
 from logs.models import MchInfoDecryptLogInfo, MchInfoEncryptLogInfo
16
+from marketcode.models import MarketCodeInfo
11 17
 from mch.models import ActivityInfo, BrandInfo, ModelInfo
12 18
 from utils.algorithm.b64 import b64_decrypt, b64_encrypt
13 19
 from utils.algorithm.caesar import caesar_decrypt, caesar_encrypt
14 20
 from utils.algorithm.rsalg import rsa_decrypt, rsa_encrypt
21
+from utils.error.errno_utils import MarketCodeStatusCode, UserStatusCode
22
+from utils.redis.connect import r
15 23
 
16 24
 
25
+WECHAT = settings.WECHAT
26
+
17 27
 # CIPHER_ALGORITHM = ('CAESAR', 'B64', 'RSA')
18 28
 CIPHER_ALGORITHM = ('CAESAR', )
19 29
 
@@ -35,6 +45,30 @@ def encrypt(request):
35 45
 
36 46
     mieli, created_at = MchInfoEncryptLogInfo.objects.get_or_create(plaintext=plaintext)
37 47
 
48
+    if settings.KODO_MARKET_CODE_ENABLED:
49
+        if created_at or not mieli.code:
50
+            with transaction.atomic():
51
+                marketcode = MarketCodeInfo.objects.select_for_update().filter(has_used=False).first()
52
+
53
+                if not marketcode:
54
+                    return response(MarketCodeStatusCode.MARKET_CODE_NOT_FOUND)
55
+
56
+                marketcode.has_used = True
57
+                marketcode.save()
58
+
59
+                mieli.code = marketcode.code
60
+                mieli.code_url = marketcode.code_url
61
+                mieli.brand_pk = brand_pk
62
+                mieli.model_pk = model_pk
63
+                mieli.distributor_pk = distributor_pk
64
+                mieli.sn = sn
65
+                mieli.operator_id = optor_id
66
+                mieli.save()
67
+
68
+        return response(200, data={
69
+            'ciphertext': mieli.code_url,
70
+        })
71
+
38 72
     if created_at:
39 73
         alg = random.choice(CIPHER_ALGORITHM)
40 74
 
@@ -134,3 +168,83 @@ def decrypt(request):
134 168
         'has_unexpired_activity': has_unexpired_activity,
135 169
         'coupon_info': coupon_info,
136 170
     })
171
+
172
+
173
+@logit(res=True)
174
+def decrypt2(request):
175
+    code_ticket = request.POST.get('code_ticket', '')
176
+    user_id = request.POST.get('user_id', '')
177
+
178
+    try:
179
+        user = UserInfo.objects.get(user_id=user_id)
180
+    except UserInfo.DoesNotExist:
181
+        return response(UserStatusCode.USER_NOT_FOUND)
182
+
183
+    wxcfg = WECHAT.get('JSAPI', {})
184
+
185
+    appid = wxcfg.get('appID')
186
+    secret = wxcfg.get('appsecret')
187
+
188
+    code_info = tickettocode(code_ticket=code_ticket, openid=user.openid_miniapp, appid=appid, secret=secret, token=None, storage=RedisStorage(r))
189
+
190
+    code = code_info.get('code', '')
191
+
192
+    try:
193
+        miel = MchInfoEncryptLogInfo.objects.get(code=code)
194
+    except MchInfoEncryptLogInfo.DoesNotExist:
195
+        return response()
196
+
197
+    plaintext = miel.plaintext
198
+
199
+    # brand_id#model_id#distributor_id#sn#time
200
+    # AAAA#AAAAAA#AAAAA#AAAAAAAAAAAAAA#180224
201
+    brand_pk, model_pk, distributor_pk, sn, time = plaintext.split('#')
202
+
203
+    try:
204
+        brand = BrandInfo.objects.get(pk=brand_pk)
205
+    except BrandInfo.DoesNotExist:
206
+        brand = None
207
+
208
+    try:
209
+        model = ModelInfo.objects.get(pk=model_pk)
210
+    except ModelInfo.DoesNotExist:
211
+        model = None
212
+
213
+    mdli, created_at = MchInfoDecryptLogInfo.objects.get_or_create(ciphertext=ciphertext, defaults={
214
+        'brand_pk': brand_pk,
215
+        'model_pk': model_pk,
216
+        'distributor_pk': distributor_pk,
217
+        'sn': sn,
218
+        'decrypt_count': 1,
219
+    })
220
+
221
+    if not created_at:
222
+        mdli.decrypt_count += 1
223
+        mdli.save()
224
+
225
+    act = ActivityInfo.objects.filter(status=True).order_by('-pk').first()
226
+    has_unexpired_activity = True if act and act.has_unexpired_activity(model.model_uni_name) else False
227
+
228
+    coupon_info = {
229
+        'coupon_expire_at': act.final_coupon_expire_at(created_at=None),
230
+        'coupon_value': act.coupon_value,
231
+    } if has_unexpired_activity else {
232
+        'coupon_expire_at': '',
233
+        'coupon_value': 0,
234
+    }
235
+
236
+    return response(200, data={
237
+        'plaintext': plaintext,
238
+        'logo_url': brand.brand_logo_url if brand else '',
239
+        'model_imgs': model.images if model else [],
240
+        'goodsInfo': {
241
+            'BrandID': brand_pk,
242
+            'Brand': brand.brand_name if brand else '',
243
+            'ModelID': model_pk,
244
+            'Model': (model.model_full_name or model.model_name) if model else '',
245
+            'DistributorID': distributor_pk,
246
+            'SerialNo': sn,
247
+        },
248
+        'has_unexpired_activity': has_unexpired_activity,
249
+        'coupon_info': coupon_info,
250
+    })

+ 1 - 0
api/urls.py

@@ -209,6 +209,7 @@ urlpatterns += [
209 209
 urlpatterns += [
210 210
     url(r'^encrypt$', encrypt_views.encrypt, name='encrypt'),
211 211
     url(r'^decrypt$', encrypt_views.decrypt, name='decrypt'),
212
+    url(r'^decrypt2$', encrypt_views.decrypt2, name='decrypt2'),
212 213
 ]
213 214
 
214 215
 urlpatterns += [

+ 4 - 0
kodo/settings.py

@@ -59,6 +59,7 @@ INSTALLED_APPS = (
59 59
     'guideline',
60 60
     'integral',
61 61
     'logs',
62
+    'marketcode',
62 63
     'mch',
63 64
     'member',
64 65
     'message',
@@ -437,6 +438,9 @@ COMPONENT_CALLBACK_CONFIG = {
437 438
 # 测试文件
438 439
 TESTING_ZBAR = os.path.join(BASE_DIR, 'utils/zbar/zbar.jpg').replace('\\', '/')
439 440
 
441
+# 一物一码设置
442
+KODO_MARKET_CODE_ENABLED = False
443
+
440 444
 # 开发调试相关配置
441 445
 if DEBUG:
442 446
     try:

+ 1 - 1
logs/admin.py

@@ -7,7 +7,7 @@ from logs.models import MchInfoDecryptLogInfo, MchInfoEncryptLogInfo, MchLogInfo
7 7
 
8 8
 
9 9
 class MchInfoEncryptLogInfoAdmin(ReadOnlyModelAdmin, admin.ModelAdmin):
10
-    list_display = ('plaintext', 'alg', 'ciphertext', 'brand_pk', 'model_pk', 'distributor_pk', 'sn', 'operator_id', 'status', 'created_at', 'updated_at')
10
+    list_display = ('plaintext', 'alg', 'ciphertext', 'brand_pk', 'model_pk', 'distributor_pk', 'sn', 'code', 'code_url', 'operator_id', 'status', 'created_at', 'updated_at')
11 11
     list_filter = ('alg', 'brand_pk', 'model_pk', 'distributor_pk', 'operator_id', 'status')
12 12
 
13 13
 

+ 25 - 0
logs/migrations/0006_auto_20200113_1832.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2020-01-13 10:32
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('logs', '0005_mchinfodecryptloginfo'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='mchinfoencryptloginfo',
17
+            name='code',
18
+            field=models.CharField(blank=True, db_index=True, help_text='\u4e5d\u4f4d\u7684\u5b57\u7b26\u4e32\u539f\u59cb\u7801', max_length=16, null=True, verbose_name='code'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='mchinfoencryptloginfo',
22
+            name='code_url',
23
+            field=models.CharField(blank=True, db_index=True, help_text='28\u4f4d\u666e\u901a\u7801\u5b57\u7b26\t', max_length=128, null=True, verbose_name='code_url'),
24
+        ),
25
+    ]

+ 4 - 0
logs/models.py

@@ -18,6 +18,10 @@ class MchInfoEncryptLogInfo(BaseModelMixin):
18 18
 
19 19
     sn = models.CharField(_(u'sn'), max_length=32, blank=True, null=True, help_text=u'序列号', db_index=True)
20 20
 
21
+    # 一物一码
22
+    code = models.CharField(_(u'code'), max_length=16, blank=True, null=True, help_text=u'九位的字符串原始码', db_index=True)
23
+    code_url = models.CharField(_(u'code_url'), max_length=128, blank=True, null=True, help_text=u'28位普通码字符	', db_index=True)
24
+
21 25
     operator_id = models.CharField(_(u'operator_id'), max_length=32, blank=True, null=True, help_text=u'操作员唯一标识', db_index=True)
22 26
 
23 27
     class Meta:

+ 0 - 0
marketcode/__init__.py


+ 12 - 0
marketcode/admin.py

@@ -0,0 +1,12 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.contrib import admin
4
+
5
+from marketcode.models import MarketCodeInfo
6
+
7
+
8
+class MarketCodeInfoAdmin(admin.ModelAdmin):
9
+    list_display = ('isv_application_id', 'application_id', 'code', 'code_url', 'has_used', 'status', 'created_at', 'updated_at')
10
+
11
+
12
+admin.site.register(MarketCodeInfo, MarketCodeInfoAdmin)

+ 8 - 0
marketcode/apps.py

@@ -0,0 +1,8 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.apps import AppConfig
5
+
6
+
7
+class MarketcodeConfig(AppConfig):
8
+    name = 'marketcode'

+ 34 - 0
marketcode/migrations/0001_initial.py

@@ -0,0 +1,34 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2020-01-13 10:32
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    initial = True
11
+
12
+    dependencies = [
13
+    ]
14
+
15
+    operations = [
16
+        migrations.CreateModel(
17
+            name='MarketCodeInfo',
18
+            fields=[
19
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
21
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
22
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
23
+                ('isv_application_id', models.CharField(blank=True, db_index=True, help_text='\u5916\u90e8\u5355\u53f7', max_length=128, null=True, verbose_name='isv_application_id')),
24
+                ('application_id', models.IntegerField(db_index=True, default=0, help_text='\u7533\u8bf7\u5355\u53f7', verbose_name='application_id')),
25
+                ('code', models.CharField(blank=True, db_index=True, help_text='\u4e5d\u4f4d\u7684\u5b57\u7b26\u4e32\u539f\u59cb\u7801', max_length=16, null=True, verbose_name='code')),
26
+                ('code_url', models.CharField(blank=True, db_index=True, help_text='28\u4f4d\u666e\u901a\u7801\u5b57\u7b26\t', max_length=128, null=True, verbose_name='code_url')),
27
+                ('has_used', models.BooleanField(db_index=True, default=True, help_text='\u662f\u5426\u5df2\u4f7f\u7528', verbose_name='has_used')),
28
+            ],
29
+            options={
30
+                'verbose_name': '\u4e00\u7269\u4e00\u7801\u4fe1\u606f',
31
+                'verbose_name_plural': '\u4e00\u7269\u4e00\u7801\u4fe1\u606f',
32
+            },
33
+        ),
34
+    ]

+ 20 - 0
marketcode/migrations/0002_auto_20200113_1851.py

@@ -0,0 +1,20 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2020-01-13 10:51
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('marketcode', '0001_initial'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AlterField(
16
+            model_name='marketcodeinfo',
17
+            name='has_used',
18
+            field=models.BooleanField(db_index=True, default=False, help_text='\u662f\u5426\u5df2\u4f7f\u7528', verbose_name='has_used'),
19
+        ),
20
+    ]

+ 25 - 0
marketcode/migrations/0003_auto_20200114_0220.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2020-01-13 18:20
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('marketcode', '0002_auto_20200113_1851'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='marketcodeinfo',
17
+            name='code_index',
18
+            field=models.IntegerField(default=0, help_text='\u8be5\u7801\u5728\u6279\u6b21\u4e2d\u7684\u504f\u79fb\u91cf\t', verbose_name='code_index'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='marketcodeinfo',
22
+            name='lattice',
23
+            field=models.CharField(blank=True, help_text='361 \u5b57\u8282\u7684 01 \u70b9\u9635\uff0c\u7528\u4e8e\u652f\u6301\u751f\u6210 19 * 19 \u7684\u5fae\u578b\u7801\uff0c0 \u4e3a\u767d\uff0c1 \u4e3a\u9ed1', max_length=361, null=True, verbose_name='code'),
24
+        ),
25
+    ]

+ 0 - 0
marketcode/migrations/__init__.py


+ 24 - 0
marketcode/models.py

@@ -0,0 +1,24 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import models
4
+from django.utils.translation import ugettext_lazy as _
5
+from django_models_ext import BaseModelMixin
6
+
7
+
8
+class MarketCodeInfo(BaseModelMixin):
9
+    isv_application_id = models.CharField(_(u'isv_application_id'), max_length=128, blank=True, null=True, help_text=u'外部单号', db_index=True)
10
+    application_id = models.IntegerField(_(u'application_id'), default=0, help_text=u'申请单号', db_index=True)
11
+
12
+    lattice = models.CharField(_(u'code'), max_length=361, blank=True, null=True, help_text=u'361 字节的 01 点阵,用于支持生成 19 * 19 的微型码,0 为白,1 为黑')
13
+    code = models.CharField(_(u'code'), max_length=16, blank=True, null=True, help_text=u'九位的字符串原始码', db_index=True)
14
+    code_index = models.IntegerField(_(u'code_index'), default=0, help_text=u'该码在批次中的偏移量	')
15
+    code_url = models.CharField(_(u'code_url'), max_length=128, blank=True, null=True, help_text=u'28位普通码字符	', db_index=True)
16
+
17
+    has_used = models.BooleanField(_(u'has_used'), default=False, help_text=_(u'是否已使用'), db_index=True)
18
+
19
+    class Meta:
20
+        verbose_name = _(u'一物一码信息')
21
+        verbose_name_plural = _(u'一物一码信息')
22
+
23
+    def __unicode__(self):
24
+        return unicode(self.pk)

+ 6 - 0
marketcode/tests.py

@@ -0,0 +1,6 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.test import TestCase
5
+
6
+# Create your tests here.

+ 6 - 0
marketcode/views.py

@@ -0,0 +1,6 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.shortcuts import render
5
+
6
+# Create your views here.

+ 25 - 0
mch/migrations/0042_auto_20200113_1832.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2020-01-13 10:32
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('mch', '0041_activityinfo_coupon_id'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AlterField(
16
+            model_name='modelinfo',
17
+            name='integral',
18
+            field=models.IntegerField(default=100, help_text='\u3010\u9500\u552e\u5458\u3011\u5361\u8def\u91cc', verbose_name='integral'),
19
+        ),
20
+        migrations.AlterField(
21
+            model_name='modelinfo',
22
+            name='shot_member_integral',
23
+            field=models.IntegerField(default=0, help_text='\u3010\u6d88\u8d39\u8005\u3011\u955c\u5934\u4f1a\u5458\u79ef\u5206', verbose_name='shot_member_integral'),
24
+        ),
25
+    ]

+ 41 - 0
pre/market_code.py

@@ -0,0 +1,41 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf import settings
4
+from pywe_marketcode import applycodedownload
5
+from pywe_storage import RedisStorage
6
+
7
+from marketcode.models import MarketCodeInfo
8
+from utils.redis.connect import r
9
+
10
+
11
+WECHAT = settings.WECHAT
12
+
13
+
14
+def marketcodedownload(application_id, code_start, code_end, isv_application_id=''):
15
+    wxcfg = WECHAT.get('JSAPI', {})
16
+
17
+    appid = wxcfg.get('appID')
18
+    secret = wxcfg.get('appsecret')
19
+
20
+    codes = applycodedownload(application_id=application_id, code_start=code_start, code_end=code_end, appid=appid, secret=secret, token=None, storage=RedisStorage(r))
21
+
22
+    lattice, code, code_index, code_url = ''
23
+    for idx, item in enumerate(codes):
24
+        if idx % 4 == 0:
25
+            lattice = item
26
+        if idx % 4 == 1:
27
+            code = item
28
+        if idx % 4 == 2:
29
+            code_index = item
30
+        if idx % 4 == 3:
31
+            code_url = item
32
+            MarketCodeInfo.objects.create(
33
+                isv_application_id=isv_application_id,
34
+                application_id=application_id,
35
+                lattice=lattice,
36
+                code=code,
37
+                code_index=code_index,
38
+                code_url=code_url
39
+            )
40
+
41
+

+ 1 - 0
requirements_pywe.txt

@@ -1,6 +1,7 @@
1 1
 pywe-component==1.0.1
2 2
 pywe-component-preauthcode==1.0.3
3 3
 pywe-jssdk==1.1.0
4
+pywe-marketcode==1.0.1
4 5
 pywe-membercard==1.0.1
5 6
 pywe-miniapp==1.1.5
6 7
 pywe-oauth==1.0.7

+ 5 - 0
utils/error/errno_utils.py

@@ -90,6 +90,11 @@ class MemberCouponStatusCode(BaseStatusCode):
90 90
     USER_COUPON_HAS_EXPIRED = StatusCodeField(504012, 'User Coupon Has Expired', description=u'用户优惠券已过期')
91 91
 
92 92
 
93
+class MarketCodeStatusCode(BaseStatusCode):
94
+    """ 一物一码相关错误码 5050xx """
95
+    MARKET_CODE_NOT_FOUND = StatusCodeField(505001, 'Market Code Not Found', description=u'一物一码不存在')
96
+
97
+
93 98
 class LensmanStatusCode(BaseStatusCode):
94 99
     """ 摄影师相关错误码 4000xx """
95 100
     LENSMAN_NOT_FOUND = StatusCodeField(400001, 'Lensman Not Found', description=u'摄影师不存在')