jiaweiqi 3 years ago
parent
commit
ce80a2de89
49 changed files with 1490 additions and 16 deletions
  1. 0 0
      apps/WechatApplet/__init__.py
  2. 17 0
      apps/WechatApplet/filters.py
  3. 0 0
      apps/WechatApplet/migrations/__init__.py
  4. 60 0
      apps/WechatApplet/models.py
  5. 0 0
      apps/__init__.py
  6. 1 0
      apps/account/__init__.py
  7. 15 0
      apps/account/filters.py
  8. 0 0
      apps/account/migrations/__init__.py
  9. 119 0
      apps/account/models.py
  10. 0 0
      apps/api/__init__.py
  11. 19 0
      apps/api/urls.py
  12. 217 0
      apps/api/views.py
  13. 61 0
      apps/base.py
  14. 0 0
      apps/collection/__init__.py
  15. 12 0
      apps/collection/filters.py
  16. 0 0
      apps/collection/migrations/__init__.py
  17. 22 0
      apps/collection/models.py
  18. 0 0
      apps/commodity/__init__.py
  19. 5 0
      apps/commodity/filters.py
  20. 0 0
      apps/commodity/migrations/__init__.py
  21. 75 0
      apps/commodity/models.py
  22. 6 0
      apps/customer/__init__.py
  23. 30 0
      apps/customer/filters.py
  24. 0 0
      apps/customer/migrations/__init__.py
  25. 120 0
      apps/customer/models.py
  26. 0 0
      apps/dashboard/__init__.py
  27. 11 0
      apps/dashboard/views.py
  28. 0 0
      apps/employee/__init__.py
  29. 15 0
      apps/employee/filters.py
  30. 0 0
      apps/employee/migrations/__init__.py
  31. 24 0
      apps/employee/models.py
  32. 0 0
      apps/images/__init__.py
  33. 0 0
      apps/images/migrations/__init__.py
  34. 189 0
      apps/images/models.py
  35. 0 0
      apps/log/__init__.py
  36. 20 0
      apps/log/filters.py
  37. 0 0
      apps/log/migrations/__init__.py
  38. 55 0
      apps/log/models.py
  39. 0 0
      apps/option/__init__.py
  40. 14 0
      apps/option/filters.py
  41. 0 0
      apps/option/migrations/__init__.py
  42. 43 0
      apps/option/models.py
  43. 1 0
      apps/order/__init__.py
  44. 72 0
      apps/order/filters.py
  45. 1 0
      apps/order/migrations/__init__.py
  46. 120 0
      apps/order/models.py
  47. 125 9
      cosmetics_shop/settings.py
  48. 5 3
      cosmetics_shop/urls.py
  49. 16 4
      manage.py

+ 0 - 0
apps/WechatApplet/__init__.py


+ 17 - 0
apps/WechatApplet/filters.py

@@ -0,0 +1,17 @@
+# coding=utf-8
+
+import django_filters
+
+from apps.WechatApplet.models import WechatApplet
+
+
+class WechatAppletFilter(django_filters.FilterSet):
+    tenant_name = django_filters.CharFilter(field_name='tenant__name', lookup_expr='icontains')
+    user_version = django_filters.CharFilter(field_name='user_version', lookup_expr='icontains')
+    wait_audit_version = django_filters.CharFilter(field_name='wait_audit_version', lookup_expr='icontains')
+    wait_audit_template = django_filters.CharFilter(field_name='wait_audit_template', lookup_expr='icontains')
+    audit_status = django_filters.CharFilter(field_name='audit_status')
+
+    class Meta:
+        model = WechatApplet
+        fields = '__all__'

+ 0 - 0
apps/WechatApplet/migrations/__init__.py


+ 60 - 0
apps/WechatApplet/models.py

@@ -0,0 +1,60 @@
+# coding=utf-8
+
+import time
+import os
+from django.conf import settings
+from django.db import models
+from django.utils import timezone
+from utils.wx.wechat import WeChat
+from utils.exceptions import CustomError
+
+
+class WechatApplet(models.Model):
+    authorizer_appid = models.CharField(max_length=512, verbose_name=u'小程序appid')
+    secret = models.CharField(max_length=512, verbose_name=u'小程序秘钥', null=True)
+
+    authorizer_refresh_token = models.CharField(max_length=512, verbose_name=u'刷新令牌', null=True)
+    authorizer_access_token = models.CharField(max_length=512, verbose_name=u'令牌', null=True)
+    access_token_gtime = models.DateTimeField(verbose_name=u"获取令牌时间", null=True)
+    expires_in = models.IntegerField(verbose_name=u'令牌有效期', null=True)
+
+    agent_num = models.CharField(max_length=512, verbose_name=u'商户号', default="")
+    agent_key = models.CharField(max_length=512, verbose_name=u'商户密钥', default="")
+
+    cert_serial_no = models.CharField(max_length=512, verbose_name=u'商户证书序列号', default="")
+    apiv3_key = models.CharField(max_length=512, verbose_name=u'商户APIv3密钥', default="")
+
+    create_ip = models.CharField(max_length=100, verbose_name=u'服务器IP地址', default="")
+    callback_api = models.CharField(max_length=100, verbose_name=u'微信支付回调API', default="")
+
+    class Meta:
+        db_table = "wechat_applet"
+        ordering = ['-id']
+        verbose_name = u'小程序'
+        default_permissions = ()
+        permissions = []
+
+    @staticmethod
+    def getByAppid(appid):
+        instance = WechatApplet.objects.filter(authorizer_appid=appid).first()
+        if not instance:
+            raise CustomError(u'未找到相应的小程序!')
+        return instance
+
+    def getAccessToken(self):
+        if self.authorizer_access_token:
+            last_time = time.mktime(self.access_token_gtime.timetuple()) + self.expires_in
+            now = time.mktime(timezone.now().timetuple())
+            if last_time > now:
+                return self.authorizer_access_token
+        gtime = timezone.now()
+        res = WeChat.getAccessToken(self.authorizer_appid, self.secret)
+        self.refreshAccessToken(gtime, res['access_token'], res['expires_in'])
+        return self.authorizer_access_token
+
+    def refreshAccessToken(self, gtime, access_token, expires_in, refresh_token=''):
+        self.authorizer_access_token = access_token
+        self.access_token_gtime = gtime
+        self.expires_in = expires_in
+        self.authorizer_refresh_token = refresh_token
+        self.save()

+ 0 - 0
apps/__init__.py


+ 1 - 0
apps/account/__init__.py

@@ -0,0 +1 @@
+#coding=utf-8

+ 15 - 0
apps/account/filters.py

@@ -0,0 +1,15 @@
+# coding=utf-8
+import django_filters
+
+from django.contrib.auth import get_user_model
+
+User = get_user_model()
+
+
+class UserFilter(django_filters.FilterSet):
+    username = django_filters.CharFilter(field_name='username', lookup_expr='icontains')
+    is_active = django_filters.CharFilter(field_name='is_active')
+
+    class Meta:
+        model = User
+        fields = ['username', 'is_active']

+ 0 - 0
apps/account/migrations/__init__.py


+ 119 - 0
apps/account/models.py

@@ -0,0 +1,119 @@
+# coding=utf-8
+
+from django.db import models
+from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
+from django.utils import timezone
+from rest_framework.utils import model_meta
+
+from utils.exceptions import CustomError
+
+
+class UserManager(BaseUserManager):
+    def create_employee(self, username, password=None, **extra_fields):
+        return self.create_user(User.EMPLOYEE, username, password, **extra_fields)
+
+    def create_customer(self, username, password=None, **extra_fields):
+        return self.create_user(User.CUSTOMER, username, password, **extra_fields)
+
+    def create_superuser(self, username, password, **extra_fields):
+        u = self.create_employee(username, password, **extra_fields)
+        u.is_active = True
+        u.is_superuser = True
+        u.save(using=self._db)
+        return u
+
+    def create_user(self, type, username, password=None, **extra_fields):
+        if not username:
+            raise CustomError(u'请输入用户名!')
+        count = User.objects.filter(username=username).count()
+        if count > 0:
+            raise CustomError(u'该用户名已存在!')
+        user = self.model(
+            type=type,
+            username=username,
+            is_superuser=False,
+            last_login=timezone.now(),
+            **extra_fields
+        )
+
+        user.set_password(password)
+        user.save(using=self._db)
+        return user
+
+
+class User(AbstractBaseUser, PermissionsMixin):
+    EMPLOYEE = 1
+    CUSTOMER = 2
+    TYPE_CHOICES = (
+        (EMPLOYEE, u'员工'),
+        (CUSTOMER, u'客户'),
+    )
+
+    type = models.PositiveSmallIntegerField(verbose_name=u"类型", choices=TYPE_CHOICES, default=CUSTOMER, editable=False)
+    username = models.CharField(verbose_name=u'帐号', max_length=30, unique=True, db_index=True)
+    is_active = models.BooleanField(verbose_name=u'激活', default=True)
+    date_joined = models.DateTimeField(verbose_name=u'注册时间', default=timezone.now, editable=False)
+
+    objects = UserManager()
+
+    USERNAME_FIELD = 'username'
+    REQUIRED_FIELDS = []
+
+    class Meta:
+        db_table = "auth_user"
+        verbose_name = u'用户'
+        unique_together = [
+            ('username')
+        ]
+        index_together = (
+            'date_joined',
+        )
+        ordering = ['-id']
+        default_permissions = ()
+        permissions = []
+
+    def __unicode__(self):
+        return self.username
+
+    def is_customer(self):
+        if self.type & self.CUSTOMER:
+            return True
+        return False
+
+    def is_employee(self):
+        if self.type & self.EMPLOYEE:
+            return True
+        return False
+
+    def change_password(self, new_password, confirm_password, old_password):
+        if new_password != confirm_password:
+            raise CustomError(u'两次输入的密码不一致, 请检查')
+        if not self.check_password(old_password):
+            raise CustomError(u'原密码输入错误, 请检查')
+        self.set_password(new_password)
+
+    def update_item(self, validated_data):
+        def update():
+            info = model_meta.get_field_info(self)
+            for attr, value in validated_data.items():
+                if attr in info.relations and info.relations[attr].to_many:
+                    field = getattr(self, attr)
+                    field.set(value)
+                else:
+                    setattr(self, attr, value)
+
+        if not 'username' in validated_data:
+            raise CustomError(u'用户名不能为空!')
+        count = User.objects.filter(username=validated_data['username']).exclude(id=self.id).count()
+        if count > 0:
+            raise CustomError(u'该用户名已存在!')
+
+        if not 'password' in validated_data or not validated_data['password']:
+            validated_data['password'] = self.password
+            update()
+        else:
+            update()
+            self.set_password(validated_data['password'])
+        self.save()
+        return self
+

+ 0 - 0
apps/api/__init__.py


+ 19 - 0
apps/api/urls.py

@@ -0,0 +1,19 @@
+# coding=utf-8
+
+from django.conf.urls import url
+from .views import *
+urlpatterns = [
+    url(r'^callback_authorize/$', CallbackAuthorize.as_view()),
+    url(r'^redirect_authorize/(?P<pk>[0-9]+)/$', RedirectAuthorize.as_view()),
+    url('^(?P<appid>.+)/callback_event/$', CallbackEvent.as_view()),
+
+    url(r'^wechat_notify/(?P<appid>.+)/$', WechatNotifyView.as_view()),
+
+    url(r'^xgj/get_comppany/$', GetCompany.as_view()),
+    url(r'^xgj/get_member_card/$', GetMemberCard.as_view()),
+    url(r'^xgj/get_member_info/$', GetMemberInfo.as_view()),
+    url(r'^xgj/get_member_points/$', GetMemberPoints.as_view()),
+    url(r'^xgj/get_member_record/$', GetMemberRecord.as_view()),
+    url(r'^xgj/get_member_balance/$', GetMemberBalance.as_view()),
+]
+

+ 217 - 0
apps/api/views.py

@@ -0,0 +1,217 @@
+# coding=utf-8
+
+import json
+import xmltodict
+
+from django.db import transaction
+from django.http import HttpResponse
+from apps.customer import customer_log
+from rest_framework.views import APIView
+
+from utils.wx.WXBizMsgCrypt import WXBizMsgCrypt
+from utils.exceptions import CustomError
+from utils import response_ok, response_error
+from utils.wechatpay import WechatPayNotify
+
+from apps.log.models import BizLog
+from apps.WechatTp.models import WechatTp
+from apps.WechatApplet.models import WechatApplet
+from apps.tenant.models import Tenant
+from apps.pay.models import Pay
+from apps.tenant.biz import TenantBiz
+from utils.xgj.xgj import XGJ
+from utils.permission import IsCustomerUser
+
+
+class CallbackAuthorize(APIView):
+    '''验证票据(component_verify_ticket)在第三方平台创建审核通过后,微信服务器会向其 ”授权事件接收URL”
+    每隔 10 分钟以 POST 的方式推送 component_verify_ticket 接收 POST 请求后,只需直接返回字符串 success。
+    为了加强安全性,postdata 中的 xml 将使用服务申请时的加解密 key 来进行加密'''
+
+    def post(self, request):
+        sMsgSignature = request.GET.get('msg_signature')
+        sTimeStamp = request.GET.get('timestamp')
+        sNonce = request.GET.get('nonce')
+        sPostData = request.body.decode('utf-8')
+
+        try:
+            component = WechatTp.getDefault()
+            if component:
+                appid = component.getAppid()
+                msg_crypt = WXBizMsgCrypt(component.getToken(), component.getEncodeKey(), appid)
+                ret, decryp_xml = msg_crypt.DecryptMsg(sPostData, sMsgSignature, sTimeStamp, sNonce)
+                data = json.loads(json.dumps(xmltodict.parse(decryp_xml)))['xml']
+                if data['AppId'] == appid:
+                    if data['InfoType'] == 'component_verify_ticket':
+                        component.refreshVerifyTicket(data['ComponentVerifyTicket'])
+                    elif data['InfoType'] == 'unauthorized':
+                        authorizer_appid = data['AuthorizerAppid']
+                        authorizer = WechatApplet.getByAppidAndComponentAppid(authorizer_appid, appid)
+                        if authorizer:
+                            authorizer.revoke()
+        except:
+            pass
+
+        return HttpResponse('success')
+
+
+class RedirectAuthorize(APIView):
+    '''小程序绑定授权回调'''
+
+    def get(self, request, pk):
+        try:
+            with transaction.atomic():
+                tenant = Tenant.objects.select_for_update().filter(id=pk).first()
+                app = TenantBiz.bindWechatApplet(tenant, request.GET.get('auth_code'))
+                BizLog.objects.addnew(None, None, BizLog.INSERT, u'租户绑定小程序, id=%d' % app.id)
+        except Exception as e:
+            return response_error(str(e))
+        return response_ok()
+
+
+class CallbackEvent(APIView):
+    '''消息与事件接收'''
+
+    def post(self, request, appid):
+        appid = appid
+        sMsgSignature = request.GET.get('msg_signature')
+        sTimeStamp = request.GET.get('timestamp')
+        sNonce = request.GET.get('nonce')
+        sPostData = request.body.decode('utf-8')
+
+        try:
+            tp = WechatTp.getDefault()
+            msg_crypt = WXBizMsgCrypt(tp.getToken(), tp.getEncodeKey(), tp.getAppid())
+            ret, decryp_xml = msg_crypt.DecryptMsg(sPostData, sMsgSignature, sTimeStamp, sNonce)
+            data = json.loads(json.dumps(xmltodict.parse(decryp_xml)))['xml']
+            if data['MsgType'] == 'event':
+                app = WechatApplet.getByAppid(appid)
+                if data['Event'] == 'weapp_audit_success':  # 代码审核通过
+                    app.weapp_audit_success()
+                elif data['Event'] == 'weapp_audit_fail':  # 代码审核不通过
+                    app.weapp_audit_fail(data['Reason'])
+                elif data['Event'] == 'weapp_audit_delay':  # 代码审核延后
+                    app.weapp_audit_delay(data['Reason'])
+        except:
+            pass
+        return HttpResponse('success')
+
+class WechatNotifyView(APIView):
+
+    def dispatch(self, request, *args, **kwargs):
+        appid = kwargs['appid']
+        applet = WechatApplet.getByAppid(appid)
+        param = request.body.decode('utf-8')
+        notify = WechatPayNotify(param, applet.tenant_key)
+
+        try:
+            data = notify.handle()
+            if not data:
+                raise CustomError(u'错误的请求!')
+
+            result_code = data['result_code']
+            if result_code != 'SUCCESS':
+                raise CustomError(u'错误的请求!')
+
+            no = data['out_trade_no']
+            amount = int(data['total_fee']) * 100
+
+            with transaction.atomic():
+                pay = Pay.getByNo(no)
+                pay.payConfirm(amount)
+                customer_log(pay.customer, BizLog.INSERT, u'支付成功,no=%s' % no, param)
+        except:
+            return HttpResponse(WechatPayNotify.response_fail())
+        return HttpResponse(WechatPayNotify.response_ok())
+
+
+class GetCompany(APIView):
+    permission_classes = [IsCustomerUser, ]
+
+    def get(self, request):
+        appid = request.GET.get('appid')
+        try:
+            res = XGJ.get_company(appid)
+        except Exception as e:
+            return response_error(str(e))
+        return response_ok(res)
+
+
+class GetMemberCard(APIView):
+    permission_classes = [IsCustomerUser, ]
+
+    def post(self, request):
+        appid = request.POST.get('appid')
+        company_id = request.POST.get('company_id')
+        tel = request.POST.get('tel')
+        number = request.POST.get('number')
+        try:
+            res = XGJ.get_member_card(appid, company_id, tel, number)
+        except Exception as e:
+            return response_error(str(e))
+        return response_ok(res)
+
+
+class GetMemberInfo(APIView):
+    permission_classes = [IsCustomerUser, ]
+
+    def get(self, request):
+        appid = request.GET.get('appid')
+        customer = request.customer
+        if not customer.member_id:
+            raise CustomError(u'请先绑定会员卡')
+        try:
+            res = XGJ.get_member_info(appid, str(customer.member_id))
+        except Exception as e:
+            return response_error(str(e))
+        return response_ok(res)
+
+
+class GetMemberPoints(APIView):
+    permission_classes = [IsCustomerUser, ]
+
+    def get(self, request):
+        appid = request.GET.get('appid')
+        page = request.GET.get('page')
+        pageSize = request.GET.get('pageSize')
+        customer = request.customer
+        if not customer.member_id:
+            raise CustomError(u'请先绑定会员卡')
+        try:
+            res = XGJ.get_member_points(appid, str(customer.member_id), page, pageSize)
+        except Exception as e:
+            return response_error(str(e))
+        return response_ok(res)
+
+
+class GetMemberRecord(APIView):
+    permission_classes = [IsCustomerUser, ]
+
+    def get(self, request):
+        appid = request.GET.get('appid')
+        page = request.GET.get('page')
+        pageSize = request.GET.get('pageSize')
+        customer = request.customer
+        if not customer.member_id:
+            raise CustomError(u'请先绑定会员卡')
+        try:
+            res = XGJ.get_member_record(appid, str(customer.member_id), page, pageSize)
+        except Exception as e:
+            return response_error(str(e))
+        return response_ok(res)
+
+
+class GetMemberBalance(APIView):
+    permission_classes = [IsCustomerUser, ]
+
+    def get(self, request):
+        appid = request.GET.get('appid')
+        customer = request.customer
+        if not customer.member_id:
+            raise CustomError(u'未绑定会员卡')
+        try:
+            res = XGJ.get_member_balance(appid, str(customer.member_id))
+        except Exception as e:
+            return response_error(str(e))
+        return response_ok(res)
+

+ 61 - 0
apps/base.py

@@ -0,0 +1,61 @@
+#coding=utf-8
+
+class Formater():
+    @staticmethod
+    def formatStr(value):
+        res = u''
+        if value != None:
+            try:
+                res = unicode(value)
+            except:
+                pass
+        return res
+
+    @staticmethod
+    def formatCount(value):
+        return int(round(float(value or 0) * 100,0))
+
+    @staticmethod
+    def formatPrice(value):
+        return int(round(float(value or 0) * 100,0))
+
+    @staticmethod
+    def formatCountShow(value):
+        return '%.2f' % (float(value or 0)/100.0)
+
+    @staticmethod
+    def formatPriceShow(value):
+        return '%.2f' % (float(value or 0)/100.0)
+
+    @staticmethod
+    def formatAmount(value):
+        return int(round(float(value or 0) * 10000,0))
+
+    @staticmethod
+    def formatAmountShow(value):
+        return '%.2f' % (float(value or 0) / 10000.0 + 0.0000001)
+
+class CustomFormaterByUser():
+    @staticmethod
+    def formatEmptyStr(value,user):
+        return Formater.formatEmptyStr(value)
+
+    @staticmethod
+    def formatEmptyFloat(value, user):
+        #return round(value or 0, 2)
+        return Formater.formatEmptyFloat(value)
+
+    @staticmethod
+    def formatTime(value,user):
+        return Formater.formatTime(value)
+
+    @staticmethod
+    def formatDate(value, user):
+        return Formater.formatDate(value)
+
+    @staticmethod
+    def formatCountShow(value,user):
+        return Formater.formatCountShow(value)
+    @staticmethod
+    def formatPartAmountShow(value, user):
+        return Formater.formatAmountShow(value)

+ 0 - 0
apps/collection/__init__.py


+ 12 - 0
apps/collection/filters.py

@@ -0,0 +1,12 @@
+# coding=utf-8
+
+import django_filters
+
+from apps.collection.models import Collection
+
+
+class CollectionFilter(django_filters.FilterSet):
+
+    class Meta:
+        model = Collection
+        fields = '__all__'

+ 0 - 0
apps/collection/migrations/__init__.py


+ 22 - 0
apps/collection/models.py

@@ -0,0 +1,22 @@
+# coding=utf-8
+
+from django.db import models
+from django.utils import timezone
+
+from apps.customer.models import Customer
+from apps.commodity.models import Commodity
+
+
+class Collection(models.Model):
+
+    customer = models.ForeignKey(Customer, verbose_name=u'收藏人', on_delete=models.PROTECT)
+    commodity = models.ForeignKey(Commodity, verbose_name=u'收藏商品', on_delete=models.PROTECT)
+    create_time = models.DateTimeField(verbose_name=u'收藏时间', default=timezone.now, editable=False)
+
+    class Meta:
+        db_table = "collection"
+        verbose_name = u'收藏'
+        ordering = ['-id']
+        default_permissions = ()
+
+

+ 0 - 0
apps/commodity/__init__.py


+ 5 - 0
apps/commodity/filters.py

@@ -0,0 +1,5 @@
+# coding=utf-8
+
+import django_filters
+from django.db.models import Q
+

+ 0 - 0
apps/commodity/migrations/__init__.py


+ 75 - 0
apps/commodity/models.py

@@ -0,0 +1,75 @@
+# coding=utf-8
+
+from django.db import models
+from django.utils import timezone
+from django.conf import settings
+
+from apps.images.models import Images
+from apps.option.models import Option
+
+
+class Commodity(models.Model):
+
+    name = models.CharField(verbose_name=u'商品名称', max_length=100)
+    category = models.ForeignKey(Option, verbose_name=u'类别', on_delete=models.PROTECT)
+    show_image = models.ForeignKey(Images, verbose_name=u'图片', on_delete=models.PROTECT, null=True, blank=True)
+    initial_sale_count = models.IntegerField(verbose_name=u'起始销量', default=0)
+    total_sales = models.IntegerField(verbose_name=u'实际总销量', default=0, editable=False)
+    sort = models.PositiveSmallIntegerField(verbose_name=u'排序', default=0)
+    status = models.PositiveSmallIntegerField(choices=settings.SALES_STATUS_CHOICES, verbose_name=u"状态", default=settings.ONLINE)
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'创建人', editable=False, on_delete=models.PROTECT)
+    create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now, editable=False)
+    delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
+
+    class Meta:
+        db_table = "commodity"
+        verbose_name = u'商品信息'
+        ordering = ['sort','-id']
+        index_together = (
+            'name',
+            'create_time',
+        )
+        default_permissions = ()
+        permissions = []
+
+
+class CommodityImage(models.Model):
+    DETAILS = 1
+    CAROUSEL = 2
+    TYPE_CHOICE = (
+        (DETAILS, u'详情'),
+        (CAROUSEL, u'轮播')
+    )
+
+    commodity = models.ForeignKey(Commodity, verbose_name=u'商品', on_delete=models.PROTECT, editable=False)
+    type = models.PositiveSmallIntegerField(choices=TYPE_CHOICE, default=DETAILS)
+    image = models.ForeignKey(Images, verbose_name=u'图片', on_delete=models.PROTECT)
+
+    class Meta:
+        db_table = "commodity_image"
+        verbose_name = u'商品图片'
+        default_permissions = ()
+        permissions = []
+
+
+class CommodityDetails(models.Model):
+    CASH = 1
+    POINT = 2
+    TYPE_CHOICE = (
+        (CASH, u'现金商品'),
+        (POINT, u'积分商品')
+    )
+    commodity = models.ForeignKey(Commodity,verbose_name=u'商品', on_delete=models.PROTECT)
+    type = models.PositiveSmallIntegerField(verbose_name=u'类型', choices=TYPE_CHOICE, default=CASH)
+    price = models.BigIntegerField(verbose_name=u'销售价格')
+    vip_price = models.BigIntegerField(verbose_name=u'会员价格')
+    point_price = models.BigIntegerField(verbose_name=u'积分价格')
+    sales = models.IntegerField(verbose_name=u'实际销量', null=True, default=0)
+    status = models.PositiveSmallIntegerField(choices=settings.SALES_STATUS_CHOICES, verbose_name=u"状态", default=settings.ONLINE)
+
+    class Meta:
+        db_table = "commodity_details"
+        verbose_name = u'商品明细'
+        ordering = ['-id']
+        default_permissions = ()
+        permissions = []

+ 6 - 0
apps/customer/__init__.py

@@ -0,0 +1,6 @@
+#coding=utf-8
+
+
+def customer_log(customer, type, description, data=None):
+    from apps.log.models import BizLog
+    BizLog.objects.addnew(customer.tenant, customer.user, type, description, data)

+ 30 - 0
apps/customer/filters.py

@@ -0,0 +1,30 @@
+# coding=utf-8
+
+import django_filters
+
+from apps.customer.models import Customer, CustomerAddress
+
+
+class CustomerFilter(django_filters.FilterSet):
+    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
+    tel = django_filters.CharFilter(field_name='tel', lookup_expr='icontains')
+    range_time = django_filters.CharFilter(method='find_base_range_time')
+
+    def find_base_range_time(self, queryset, *args):
+        if args[1]:
+            range_time = args[1].split(' - ')
+            end_time = range_time[1] + ' 23:59:59'
+            queryset = queryset.filter(user__date_joined__gte=range_time[0], user__date_joined__lte=end_time)
+        return queryset
+
+
+    class Meta:
+        model = Customer
+        fields = '__all__'
+
+
+class CustomerAddressFilter(django_filters.FilterSet):
+
+    class Meta:
+        model = CustomerAddress
+        fields = '__all__'

+ 0 - 0
apps/customer/migrations/__init__.py


+ 120 - 0
apps/customer/models.py

@@ -0,0 +1,120 @@
+# coding=utf-8
+
+from django.db import models
+from django.utils import timezone
+from django.conf import settings
+
+
+class Customer(models.Model):
+    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'用户', editable=False, related_name='customer_user', on_delete=models.PROTECT)
+    is_distributor = models.BooleanField(verbose_name=u"分销商", default=False)
+
+    name = models.CharField(max_length=100, verbose_name=u"姓名")
+    tel = models.CharField(max_length=50, verbose_name=u'电话')
+    gender = models.PositiveSmallIntegerField(choices=settings.GENDER_CHOICES, verbose_name=u'性别', null=True)
+    face = models.CharField(max_length=200, verbose_name=u'头像', null=True)
+
+    balance = models.BigIntegerField(verbose_name=u'余额', default=0, editable=False)
+    points = models.IntegerField(verbose_name=u'积分', default=0, editable=False)
+
+    success_count = models.IntegerField(verbose_name=u'成交次数', default=0, editable=False)
+    total_amount = models.BigIntegerField(verbose_name=u'累计消费金额', default=0, editable=False)
+    total_point = models.BigIntegerField(verbose_name=u'累计消费积分', default=0, editable=False)
+
+    class Meta:
+        db_table = "customer"
+        verbose_name = u'客户管理'
+        ordering = ['-id']
+        index_together = (
+            'name',
+            'tel',
+        )
+        default_permissions = ()
+        permissions = []
+
+
+class SuperiorDistributor(models.Model):
+    customer = models.ForeignKey(Customer, verbose_name=u'客户', editable=False, related_name='superior_distributor_customer', on_delete=models.PROTECT)
+    one_level = models.ForeignKey(Customer, verbose_name=u'一级分销', related_name='superior_distributor_one_level', on_delete=models.PROTECT, null=True, blank=True)
+    tow_level = models.ForeignKey(Customer, verbose_name=u'二级分销', related_name='superior_distributor_tow_level', on_delete=models.PROTECT, null=True, blank=True)
+    three_level = models.ForeignKey(Customer, verbose_name=u'三级分销', related_name='superior_distributor_three_level', on_delete=models.PROTECT, null=True, blank=True)
+    four_level = models.ForeignKey(Customer, verbose_name=u'四级分销', related_name='superior_distributor_four_level', on_delete=models.PROTECT, null=True, blank=True)
+    five_level = models.ForeignKey(Customer, verbose_name=u'五级分销', related_name='superior_distributor_five_level', on_delete=models.PROTECT, null=True, blank=True)
+
+    class Meta:
+        db_table = "superior_distributor"
+        verbose_name = u'上级分销'
+        ordering = ['-id']
+        default_permissions = ()
+        permissions = []
+
+
+class CustomerWechat(models.Model):
+    customer = models.ForeignKey(Customer, verbose_name=u'客户', on_delete=models.PROTECT, editable=False, null=True)
+    openid = models.CharField(max_length=512, verbose_name=u"openid")
+    session_key = models.CharField(max_length=512, verbose_name=u'session_key',null=True)
+
+    class Meta:
+        db_table = "customer_wechat"
+        verbose_name = u'微信客户'
+        unique_together = [
+            ('openid')
+        ]
+        default_permissions = ()
+
+
+class Area(models.Model):
+    PROVINCE = 0
+    CITY = 1
+    COUNTY = 2
+    LEVEL_CHOICES = (
+        (PROVINCE, u'省级'),
+        (CITY, u'市级'),
+        (COUNTY, u'县区级'),
+    )
+
+    name = models.CharField(verbose_name=u'名称', max_length=50, blank=True, null=True)
+    level = models.IntegerField(verbose_name=u'层级', choices=LEVEL_CHOICES)
+    province_name = models.CharField(u'所属省', max_length=50, blank=True, null=True)
+    city_name = models.CharField(u'所属地市', max_length=50, blank=True, null=True)
+    province = models.ForeignKey('Area', verbose_name=u'所属省', related_name='province_children', on_delete=models.PROTECT, blank=True, null=True)
+    city = models.ForeignKey('Area', verbose_name=u'所属地市', related_name='city_children', on_delete=models.PROTECT, blank=True, null=True)
+
+    def __unicode__(self):
+        return '%s' % (self.name)
+
+    class Meta:
+        verbose_name = u"地区"
+        db_table = "area"
+
+
+class CustomerAddress(models.Model):
+    customer = models.ForeignKey(Customer, verbose_name=u'客户', on_delete=models.PROTECT, editable=False)
+    name = models.CharField(verbose_name=u'收货人姓名', max_length=20)
+    tel = models.CharField(verbose_name=u'收货人电话', max_length=11)
+    province = models.ForeignKey(Area, verbose_name=u'省', related_name='address_province', on_delete=models.PROTECT)
+    city = models.ForeignKey(Area, verbose_name=u'地市', related_name='address_city', null=True, on_delete=models.PROTECT)
+    county = models.ForeignKey(Area, verbose_name=u'区县', related_name='address_county', null=True, on_delete=models.PROTECT)
+    address = models.CharField(verbose_name=u'详细地址', max_length=500)
+    default = models.BooleanField(verbose_name=u'默认', default=False)
+    delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
+    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now, editable=False)
+
+    class Meta:
+        db_table = 'customer_address'
+        verbose_name = u'收货地址'
+        ordering = ['-default', '-id']
+        default_permissions = ()
+
+    def get_address(self):
+        return '{}{}{}{}'.format(self.province.name, self.city.name, self.county.name, self.address)
+
+    def destory(self, queryset):
+        if self.default:
+            self.default = False
+            default = queryset.first()
+            if default:
+                default.default = True
+                default.save()
+        self.delete = True
+        self.save()

+ 0 - 0
apps/dashboard/__init__.py


+ 11 - 0
apps/dashboard/views.py

@@ -0,0 +1,11 @@
+# coding=utf-8
+
+from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
+
+def index(request):
+    user_id = request.META.get('HTTP_USER_ID')
+    token = request.META.get('HTTP_ACCESS_TOKEN')
+    if not user_id or not token:
+        return HttpResponseRedirect('/views/account/login.html')
+    else:
+        return HttpResponsePermanentRedirect("/views/index.html")

+ 0 - 0
apps/employee/__init__.py


+ 15 - 0
apps/employee/filters.py

@@ -0,0 +1,15 @@
+# coding=utf-8
+
+import django_filters
+
+from apps.employee.models import Employee
+
+
+class EmployeeFilter(django_filters.FilterSet):
+    is_active = django_filters.CharFilter(field_name='user__is_active')
+    username = django_filters.CharFilter(field_name='user__username', lookup_expr='icontains')
+    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
+
+    class Meta:
+        model = Employee
+        fields = '__all__'

+ 0 - 0
apps/employee/migrations/__init__.py


+ 24 - 0
apps/employee/models.py

@@ -0,0 +1,24 @@
+# coding=utf-8
+
+from django.db import models
+from django.conf import settings
+
+
+class Employee(models.Model):
+    user = models.OneToOneField(settings.AUTH_USER_MODEL, editable=False, related_name='employee', on_delete=models.PROTECT, verbose_name=u'职工')
+    name = models.CharField(max_length=100, verbose_name=u"姓名")
+    tel = models.CharField(max_length=50, verbose_name=u'电话')
+    gender = models.PositiveSmallIntegerField(choices=settings.GENDER_CHOICES, verbose_name=u'性别')
+
+    class Meta:
+        db_table = "employee"
+        verbose_name = u'员工管理'
+        ordering = ['-id']
+        index_together = (
+            'name',
+            'tel',
+            'gender',
+        )
+        default_permissions = ()
+        permissions = []
+

+ 0 - 0
apps/images/__init__.py


+ 0 - 0
apps/images/migrations/__init__.py


+ 189 - 0
apps/images/models.py

@@ -0,0 +1,189 @@
+# coding=utf-8
+
+from PIL import Image
+
+from django.db import models
+from django.conf import settings
+from django.utils import timezone
+
+from utils.file_operation import UploadFile, DeleteFile, DownloadFace
+
+
+class ImagesManager(models.Manager):
+
+    def employee_addnew(self, employee, type, file):
+        return self._addnew(employee.user, type, file)
+
+    def customer_addnew(self, customer, type, file):
+        return self._addnew(customer.user, type, file)
+
+    def _addnew(self, user, type, file):
+        width = None
+        height = None
+
+        path = ImagesManager.calculatePath(type, user.id)
+        modify_size = ImagesManager.canModifySize(type)
+        filename = UploadFile(file, path)
+        fullname = "%s/%s" % (settings.MEDIA_ROOT, filename)
+
+        try:
+            img = Image.open(fullname)
+            width, height = img.size
+            # 缩略图压缩宽或高最大200
+            if modify_size and (width > 200 or height > 200):
+                if width > height:
+                    size = (200, int(height * 200 / width))
+                elif width < height:
+                    size = (int(width * 200 / height), 200)
+                else:
+                    size = (200, 200)
+                img = img.resize(size, Image.ANTIALIAS)
+                img.save(fullname)
+                width, height = img.size
+        except:
+            pass
+        instance = self.model(
+            user=user,
+            type=type,
+            name=file.name,
+            picture=filename,
+            width=width,
+            height=height,
+            create_time=timezone.now()
+        )
+        instance.save()
+        return instance
+
+    @staticmethod
+    def calculatePath(type, user_id):
+        path_map = {
+            Images.PACKAGE_FILE: package_detail_file,
+            Images.PACKAGE_THUMBNAIL_FILE: package_thumbnail_file,
+            Images.PACKAGE_PLAYBILL_FILE: package_playbill_file,
+            Images.POSTER_FILE: poster_file,
+            Images.SALES_ADVISOR_FACE: sales_advisor_face_file,
+            Images.SALES_ADVISOR_CODE: sales_advisor_code_file,
+            Images.SHOP_FILE: shop_file,
+            Images.VEHICLE_SERIES_THUMBNAIL: vehicle_series_thumbnail_file,
+            Images.VEHICLE_MODEL_THUMBNAIL: vehicle_model_thumbnail_file,
+            Images.VEHICLE_MODEL_PLAYBILL: vehicle_model_playbill_file,
+            Images.VEHICLE_IMAGES: vehicle_model_detail_file,
+            Images.VEHICLE_MODEL_IMAGES: vehicle_detail_file,
+            Images.CUSTOMER_FACE: customer_face_file,
+            Images.ACTIVITY_PLAYBILL_FILE: activity_playbill_file,
+        }
+
+        return path_map[type] + str(user_id) + '/'
+
+    @staticmethod
+    def canModifySize(type):
+        modify_size = False
+        can_modify_list = [
+            Images.PACKAGE_THUMBNAIL_FILE,
+            Images.SALES_ADVISOR_FACE,
+            Images.SALES_ADVISOR_CODE,
+            Images.SHOP_FILE,
+            Images.VEHICLE_SERIES_THUMBNAIL,
+            Images.VEHICLE_MODEL_THUMBNAIL,
+        ]
+        if type in can_modify_list:
+            modify_size = True
+
+        return modify_size
+
+
+package_detail_file = "package/detail/"
+package_thumbnail_file = "package/thumbnail/"
+package_playbill_file = "package/playbill/"
+poster_file = 'poster/'
+sales_advisor_face_file = "sales_advisor/face/"
+sales_advisor_code_file = "sales_advisor/code/"
+shop_file = "shop/"
+vehicle_series_thumbnail_file = 'vehicle_series/'
+vehicle_model_thumbnail_file = 'vehicle_model/thumbnail/'
+vehicle_model_playbill_file = 'vehicle_model/playbill/'
+vehicle_model_detail_file = 'vehicle_model/detail/'
+vehicle_detail_file = 'vehicle/detail/'
+customer_face_file = 'customer/face/'
+activity_playbill_file = 'activity/playbill/'
+
+
+class Images(models.Model):
+    PACKAGE_FILE = 1
+    PACKAGE_THUMBNAIL_FILE = 2
+    PACKAGE_PLAYBILL_FILE = 3
+    POSTER_FILE = 4
+    SALES_ADVISOR_FACE = 5
+    SALES_ADVISOR_CODE = 6
+    SHOP_FILE = 7
+    VEHICLE_SERIES_THUMBNAIL = 8
+    VEHICLE_MODEL_THUMBNAIL = 9
+    VEHICLE_MODEL_PLAYBILL = 10
+    VEHICLE_IMAGES = 11
+    VEHICLE_MODEL_IMAGES = 12
+    CUSTOMER_FACE = 13
+    ACTIVITY_PLAYBILL_FILE= 14
+    TYPE_CHOICES = (
+        (PACKAGE_FILE, u'商品图片'),
+        (PACKAGE_THUMBNAIL_FILE, u'商品缩略图'),
+        (PACKAGE_PLAYBILL_FILE, u'商品海报'),
+        (POSTER_FILE, u'广告图片'),
+        (SALES_ADVISOR_FACE, u'销售顾问头像'),
+        (SALES_ADVISOR_CODE, u'销售顾问二维码'),
+        (SHOP_FILE, u'门店图片'),
+        (VEHICLE_SERIES_THUMBNAIL, u'车系缩略图'),
+        (VEHICLE_MODEL_THUMBNAIL, u'车型缩略图'),
+        (VEHICLE_MODEL_PLAYBILL, u'车型海报'),
+        (VEHICLE_IMAGES, u'整车图片'),
+        (VEHICLE_MODEL_IMAGES, u'车型图片'),
+        (CUSTOMER_FACE, u'客户头像'),
+        (ACTIVITY_PLAYBILL_FILE, u'活动封面'),
+    )
+
+    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'操作人', on_delete=models.PROTECT)
+    type = models.PositiveSmallIntegerField(choices=TYPE_CHOICES, verbose_name=u"类型")
+    name = models.CharField(verbose_name=u'图片名', max_length=250)
+    picture = models.CharField(verbose_name=u'图片路径', max_length=250)
+    width = models.IntegerField(verbose_name=u"图片宽度")
+    height = models.IntegerField(verbose_name=u"图片高度")
+    create_time = models.DateTimeField(verbose_name=u'上传时间', default=timezone.now, editable=False)
+
+    objects = ImagesManager()
+
+    class Meta:
+        db_table = "images"
+        verbose_name = u'图片'
+        ordering = ['-create_time']
+        index_together = (
+            'type',
+            'create_time',
+        )
+        default_permissions = ()
+
+    def del_images(self):
+        picture = self.picture
+        self.delete()
+        DeleteFile(picture)
+
+    def get_path(self):
+        return '%s%s%s' % (settings.SERVER_DOMAIN, settings.MEDIA_URL, self.picture)
+
+    @staticmethod
+    def downLoadFace(customer, url):
+        save_path = customer_face_file + str(customer.user.id) + '/'
+        filename = DownloadFace(url, save_path, 'jpg')
+        name = filename.split('/')[-1]
+        fullname = "%s/%s" % (settings.MEDIA_ROOT, filename)
+        width = None
+        height = None
+        try:
+            img = Image.open(fullname)
+            width, height = img.size
+        except:
+            pass
+        instance = Images.objects.create(
+            tenant=customer.tenant, user=customer.user, type=Images.CUSTOMER_FACE, name=name, picture=filename,
+            width=width, height=height, create_time=timezone.now()
+        )
+        instance.save()
+        return instance

+ 0 - 0
apps/log/__init__.py


+ 20 - 0
apps/log/filters.py

@@ -0,0 +1,20 @@
+# coding=utf-8
+
+import django_filters
+
+from .models import BizLog
+
+from utils.format import clean_datetime_range
+
+
+class BizLogFilter(django_filters.FilterSet):
+    type = django_filters.ChoiceFilter(choices=BizLog.TYPE_CHOICES, field_name='type')
+    create_time = django_filters.DateTimeFromToRangeFilter(field_name='create_time')
+
+    class Meta:
+        model = BizLog
+        fields = ('create_time', 'type', )
+
+    def __init__(self, data=None, *args, **kwargs):
+        data = clean_datetime_range(data, 'create_time')
+        super(BizLogFilter, self).__init__(data, *args, **kwargs)

+ 0 - 0
apps/log/migrations/__init__.py


+ 55 - 0
apps/log/models.py

@@ -0,0 +1,55 @@
+# coding=utf-8
+
+import json
+import datetime
+
+from django.db import models
+from django.utils import timezone
+from django.conf import settings
+
+from utils.format import strftime, strfdate
+
+
+class BizLogManager(models.Manager):
+    def addnew(self, tenant, user, type, description, data=None):
+        def default(o):
+            if isinstance(o, datetime.datetime):
+                return strftime(o)
+            elif isinstance(o, datetime.date):
+                return strfdate(o)
+
+        row = self.model(tenant=tenant, user=user, type=type, description=description)
+        if data:
+            row.data = json.dumps(data, default=default)
+        row.save()
+        return row
+
+
+class BizLog(models.Model):
+    INSERT = 1
+    UPDATE = 2
+    DELETE = 3
+    TYPE_CHOICES = (
+        (INSERT, u'添加'),
+        (UPDATE, u'修改'),
+        (DELETE, u'删除'),
+    )
+    TYPE_JSON = [{'id': item[0], 'value': item[1]} for item in TYPE_CHOICES]
+
+    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=True, blank=True)
+    type = models.PositiveSmallIntegerField(choices=TYPE_CHOICES, verbose_name=u"类别")
+    description = models.CharField(max_length=1000, verbose_name=u"内容")
+    data = models.TextField(verbose_name=u"数据", null=True, blank=True)
+    create_time = models.DateTimeField(verbose_name=u"添加时间", default=timezone.now, editable=False)
+
+    objects = BizLogManager()
+
+    class Meta:
+        db_table = "system_log"
+        ordering = ['-id']
+        index_together = (
+            'create_time',
+            'type',
+        )
+        verbose_name = u'系统日志'
+        default_permissions = ()

+ 0 - 0
apps/option/__init__.py


+ 14 - 0
apps/option/filters.py

@@ -0,0 +1,14 @@
+# coding=utf-8
+
+import django_filters
+
+from apps.option.models import Option
+
+
+class OptionFilter(django_filters.FilterSet):
+    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
+    type = django_filters.CharFilter(field_name='type')
+
+    class Meta:
+        model = Option
+        fields = '__all__'

+ 0 - 0
apps/option/migrations/__init__.py


+ 43 - 0
apps/option/models.py

@@ -0,0 +1,43 @@
+# coding=utf-8
+
+from django.db import models
+from django.db.models import Q
+from django.conf import settings
+from django.utils import timezone
+
+
+class Option(models.Model):
+    COMMODITY_CATEGORY = 1
+    EXPRESS_COMPANY = 2
+
+    TYPE_CHOICES = (
+        (COMMODITY_CATEGORY, u'商品类别'),
+        (EXPRESS_COMPANY, u'快递公司'),
+    )
+
+    type = models.PositiveSmallIntegerField(choices=TYPE_CHOICES, verbose_name=u"类别")
+    name = models.CharField(verbose_name=u'名称', max_length=100)
+    sort = models.PositiveSmallIntegerField(verbose_name=u'排序', help_text=u'越小越靠前', default=0)
+    notes = models.CharField(max_length=500, verbose_name=u"备注",null=True)
+    enable = models.BooleanField(verbose_name=u"在用", default=True)
+    delete = models.BooleanField(verbose_name=u"删除", default=False, editable=False)
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'创建人', editable=False, on_delete=models.PROTECT)
+    create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now, editable=False)
+
+    class Meta:
+        db_table = "system_option"
+        verbose_name = u'系统选项'
+        ordering = ['type', 'sort', ]
+        index_together = (
+            'type',
+            'name',
+        )
+        default_permissions = ()
+        permissions = []
+
+    @staticmethod
+    def is_exist(type, name, exclude_id=None):
+        rows = Option.objects.filter(type=type, name=name, delete=False)
+        if exclude_id:
+            rows = rows.filter(~Q(id=exclude_id))
+        return rows.count()

+ 1 - 0
apps/order/__init__.py

@@ -0,0 +1 @@
+# coding=utf-8

+ 72 - 0
apps/order/filters.py

@@ -0,0 +1,72 @@
+# coding=utf-8
+import django_filters
+
+from .models import *
+from apps.base import clean_datetime_range
+
+class OrderFilter(django_filters.FilterSet):
+    no = django_filters.CharFilter(field_name='no', lookup_expr='icontains')
+    name = django_filters.CharFilter(field_name='agent__name', lookup_expr='icontains')
+    create_time_0 = django_filters.DateTimeFilter(field_name='create_time', lookup_expr='gte')
+    create_time_1 = django_filters.DateTimeFilter(field_name='create_time', lookup_expr='lte')
+    create_user_name = django_filters.CharFilter(field_name='create_user__name', lookup_expr='icontains')
+    pay_no = django_filters.CharFilter(field_name='pay__pay_no', lookup_expr='icontains')
+    area_manager = django_filters.CharFilter(field_name='agent__area_manager__name', lookup_expr='icontains')
+
+    class Meta:
+        model = Order
+        fields = '__all__'
+
+    def __init__(self, data=None, *args, **kwargs):
+        data = clean_datetime_range(data, 'create_time')
+        super(OrderFilter, self).__init__(data, *args, **kwargs)
+
+class LogisticsFilter(django_filters.FilterSet):
+    no = django_filters.CharFilter(field_name='no', lookup_expr='icontains')
+
+    class Meta:
+        model = Logistics
+        fields = '__all__'
+
+
+class OrderDetailsFilter(django_filters.FilterSet):
+    no = django_filters.CharFilter(field_name='order__no', lookup_expr='icontains')
+
+    class Meta:
+        model = OrderDetails
+        fields = '__all__'
+
+
+class ShoppingCartFilter(django_filters.FilterSet):
+
+    class Meta:
+        model = ShoppingCart
+        fields = '__all__'
+
+class OrderPaymentVoucherFilter(django_filters.FilterSet):
+
+    class Meta:
+        model = OrderPaymentVoucher
+        fields = '__all__'
+
+
+class CustomerOrderFilter(django_filters.FilterSet):
+    no = django_filters.CharFilter(field_name='no', lookup_expr='icontains')
+    name = django_filters.CharFilter(field_name='agent__name', lookup_expr='icontains')
+    create_time_0 = django_filters.DateTimeFilter(field_name='create_time', lookup_expr='gte')
+    create_time_1 = django_filters.DateTimeFilter(field_name='create_time', lookup_expr='lte')
+
+    class Meta:
+        model = CustomerOrder
+        fields = '__all__'
+
+    def __init__(self, data=None, *args, **kwargs):
+        data = clean_datetime_range(data, 'create_time')
+        super(CustomerOrderFilter, self).__init__(data, *args, **kwargs)
+
+
+class CustomerShoppingCartFilter(django_filters.FilterSet):
+
+    class Meta:
+        model = CustomerShoppingCart
+        fields = '__all__'

+ 1 - 0
apps/order/migrations/__init__.py

@@ -0,0 +1 @@
+# coding=utf-8

+ 120 - 0
apps/order/models.py

@@ -0,0 +1,120 @@
+# coding=utf-8
+
+from django.db import models
+from django.conf import settings
+from django.utils import timezone
+from apps.option.models import Option
+from apps.commodity.models import CommodityDetails
+from apps.customer.models import Customer, CustomerAddress
+
+
+class Pay(models.Model):
+    WAIT = 1
+    PAY = 2
+    UNDO = 3
+    STATUS_CHOICES = (
+        (WAIT, u'待付款'),
+        (PAY, u'已付款'),
+        (UNDO, u'已取消'),
+    )
+
+    no = models.CharField(max_length=64, verbose_name=u"单号")
+    status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u'状态', default=WAIT)
+    precreate_amount = models.BigIntegerField(verbose_name=u"预支付金额")
+    amount = models.BigIntegerField(verbose_name=u"支付金额", null=True)
+    transaction_id = models.CharField(max_length=100, verbose_name=u"微信支付订单号", null=True)
+    customer = models.ForeignKey(Customer, verbose_name=u'下单人', related_name='pay_customer', on_delete=models.PROTECT)
+    create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now)
+
+    class Meta:
+        db_table = "pay"
+        verbose_name = u"支付信息"
+        ordering = ('-id',)
+        index_together = (
+            'create_time',
+            'status',
+        )
+        unique_together = (
+            'no',
+        )
+        default_permissions = ()
+
+
+class ShoppingCart(models.Model):
+    commodity_details = models.ForeignKey(CommodityDetails, verbose_name=u'产品明细', on_delete=models.PROTECT)
+    quantity = models.PositiveIntegerField(verbose_name=u'数量', default=1)
+    create_time = models.DateTimeField(verbose_name=u'创建时间', auto_now_add=True)
+    customer = models.ForeignKey(Customer, related_name='shopping_cart_customer', verbose_name=u"下单人", on_delete=models.PROTECT, editable=False)
+
+    class Meta:
+        db_table = 'shopping_cart'
+        verbose_name = u'购物车'
+        ordering = ['-id', ]
+        default_permissions = ()
+
+
+class Order(models.Model):
+    WAIT_PAY = 0
+    CONFIRM_PAY = 1
+    WAIT_DISPATCH = 2
+    CONFIRM_DISPATCH = 3
+    FINISH = 4
+    CANCEL = 5
+    STATUS_CHOICES = (
+        (WAIT_PAY, u'待付款'),
+        (CONFIRM_PAY, u'已付款'),
+        (WAIT_DISPATCH, u'待发货'),
+        (CONFIRM_DISPATCH, u'已发货'),
+        (FINISH, u'已完成'),
+        (CANCEL, u'已取消'),
+    )
+
+    no = models.CharField(max_length=50, verbose_name=u'订单号', blank=True)
+    pay = models.ForeignKey(Pay, verbose_name=u'支付信息', on_delete=models.PROTECT, null=True, editable=False)
+
+    total_count = models.IntegerField(verbose_name=u'总数量', default=0, editable=False)
+    total_amount = models.BigIntegerField(verbose_name=u'总金额', default=0, editable=False)
+    total_point = models.BigIntegerField(verbose_name=u'总积分', default=0, editable=False)
+    status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u"订单状态", default=WAIT_PAY)
+    notes = models.CharField(max_length=500, verbose_name=u"备注", null=True)
+
+    customer = models.ForeignKey(Customer, verbose_name=u'创建人', editable=False, on_delete=models.PROTECT)
+    create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now, editable=False)
+    cancel = models.BooleanField(verbose_name=u"取消", default=False)
+    cancel_reason = models.CharField(max_length=100, verbose_name=u"取消原因", null=True, blank=True)
+    cancel_user =models.ForeignKey(settings.AUTH_USER_MODEL, related_name='order_cancel_user', verbose_name=u"取消人", on_delete=models.PROTECT, null=True, blank=True)
+    cancel_time = models.DateTimeField(verbose_name=u"取消时间", null=True, blank=True)
+
+    address = models.ForeignKey(CustomerAddress, verbose_name=u'收货地址', related_name='order_address', null=True, on_delete=models.PROTECT)
+    name = models.CharField(verbose_name=u'姓名', max_length=20, null=True)
+    tel = models.CharField(verbose_name=u'手机', max_length=15, null=True)
+    user_address = models.CharField(verbose_name=u'详细地址', max_length=200, null=True)
+
+    express_no = models.CharField(max_length=50, verbose_name=u'快递单号', blank=True)
+    express_company = models.ForeignKey(Option, verbose_name=u'快递公司', on_delete=models.PROTECT)
+    dispatch_time = models.DateField(verbose_name=u'发货时间', null=True)
+
+    class Meta:
+        db_table = "order"
+        verbose_name = u"客户订单"
+        ordering = ('-id',)
+        default_permissions = ()
+        permissions = []
+
+
+class OrderDetails(models.Model):
+
+    order = models.ForeignKey(Order, verbose_name=u'订单', on_delete=models.PROTECT)
+    commodity = models.ForeignKey(CommodityDetails, verbose_name=u'商品', on_delete=models.PROTECT)
+    price = models.IntegerField(verbose_name=u'价格', null=True, default=0)
+    point = models.IntegerField(verbose_name=u'积分', null=True, default=0)
+    count = models.IntegerField(verbose_name=u'数量', null=True, default=0)
+    amount = models.BigIntegerField(verbose_name=u'总金额', null=True, default=0)
+    point_amount = models.BigIntegerField(verbose_name=u'总积分', null=True, default=0)
+
+    class Meta:
+        db_table = u'order_details'
+        verbose_name = u'订单明细'
+        ordering = ('-id', )
+        default_permissions = ()
+        permissions = []

+ 125 - 9
cosmetics_shop/settings.py

@@ -1,3 +1,4 @@
+# coding=utf-8
 """
 Django settings for cosmetics_shop project.
 
@@ -10,7 +11,7 @@ For the full list of settings and their values, see
 https://docs.djangoproject.com/en/1.9/ref/settings/
 """
 
-import os
+import os, datetime
 
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -25,32 +26,77 @@ SECRET_KEY = '%domhs7p_)nl&c-$@al(xsk+u^y*qezl!m@l_k72)3n+%ct$z5'
 # SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = True
 
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ['*']
 
 
 # Application definition
 
 INSTALLED_APPS = [
-    'django.contrib.admin',
+    # 'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+
+    'corsheaders',
+    'rest_framework',
+    'rest_framework_jwt',
+    'django_filters',
+
+    'apps.account',
+    'apps.collection',
+    'apps.commodity',
+    'apps.customer',
+    'apps.dashboard',
+    'apps.employee',
+    'apps.images',
+    'apps.log',
+    'apps.option',
+    'apps.order',
+    'apps.WechatApplet',
 ]
 
-MIDDLEWARE_CLASSES = [
+MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
+    'corsheaders.middleware.CorsMiddleware',
 ]
 
+CORS_ALLOW_CREDENTIALS = True  # 允许携带cookie
+CORS_ORIGIN_ALLOW_ALL = True
+CORS_ALLOW_METHODS = (
+    'DELETE',
+    'GET',
+    'OPTIONS',
+    'PATCH',
+    'POST',
+    'PUT',
+    'VIEW',
+)
+CORS_ALLOW_HEADERS = (
+    'XMLHttpRequest',
+    'X_FILENAME',
+    'accept-encoding',
+    'authorization',
+    'content-type',
+    'dnt',
+    'origin',
+    'token',
+    'user-agent',
+    'x-csrftoken',
+    'x-requested-with',
+    'Pragma',
+)
+
 ROOT_URLCONF = 'cosmetics_shop.urls'
+AUTH_USER_MODEL = "account.User"
+AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']
 
 TEMPLATES = [
     {
@@ -76,8 +122,12 @@ WSGI_APPLICATION = 'cosmetics_shop.wsgi.application'
 
 DATABASES = {
     'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': 'cosmetics_shop',
+        'USER': 'carwin',
+        'PASSWORD': 'carwin!@#',
+        'HOST': '192.168.2.55',
+        'PORT': 3306,
     }
 }
 
@@ -104,9 +154,9 @@ AUTH_PASSWORD_VALIDATORS = [
 # Internationalization
 # https://docs.djangoproject.com/en/1.9/topics/i18n/
 
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = 'zh-hans'
 
-TIME_ZONE = 'UTC'
+TIME_ZONE = 'Asia/Shanghai'
 
 USE_I18N = True
 
@@ -119,3 +169,69 @@ USE_TZ = True
 # https://docs.djangoproject.com/en/1.9/howto/static-files/
 
 STATIC_URL = '/static/'
+
+MEDIA_URL = '/up/'
+MEDIA_ROOT = os.path.join(BASE_DIR, "uis/up/")
+
+UIS_URL = '/'
+UIS_ROOT = os.path.join(BASE_DIR, "uis/")
+
+SERVER_DOMAIN = 'https://xapp.aiche360.cn'
+
+JWT_AUTH = {
+    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=30),
+    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=360),
+    'JWT_ALLOW_REFRESH': True,
+}
+
+REST_FRAMEWORK = {
+    'DEFAULT_PERMISSION_CLASSES': (
+        'rest_framework.permissions.AllowAny',
+    ),
+    'DEFAULT_AUTHENTICATION_CLASSES': (
+        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
+        #'rest_framework.authentication.SessionAuthentication',
+        #'rest_framework.authentication.BasicAuthentication',
+    ),
+    'DEFAULT_PARSER_CLASSES': (
+        'rest_framework.parsers.JSONParser',
+        'rest_framework.parsers.FormParser',
+        'rest_framework.parsers.MultiPartParser'
+    ),
+    'DEFAULT_RENDERER_CLASSES': (
+        'rest_framework.renderers.JSONRenderer',
+        # 'rest_framework.renderers.BrowsableAPIRenderer',
+    ),
+    'DEFAULT_FILTER_BACKENDS': (
+        'django_filters.rest_framework.DjangoFilterBackend',
+    ),
+    #'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
+    'DEFAULT_PAGINATION_CLASS': 'utils.pagination.CustomPagination',
+    #'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
+    'EXCEPTION_HANDLER': 'utils.handler.custom_exception_handler',
+    'PAGE_SIZE': 10,
+    'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S",
+    'NON_FIELD_ERRORS_KEY': "error",  # 序列化器错误KEY名称
+}
+
+# 导入本地设置
+try:
+    from markwin.local_settings import *
+except ImportError:
+    pass
+
+FEMALE = 1
+MALE = 2
+UNKNOWN = 3
+GENDER_CHOICES = (
+    (FEMALE, u'女'),
+    (MALE, u'男'),
+    (UNKNOWN, u'男'),
+)
+
+OFFLINE = 0
+ONLINE = 1
+SALES_STATUS_CHOICES = (
+    (OFFLINE, u'下架'),
+    (ONLINE, u'上架'),
+)

+ 5 - 3
cosmetics_shop/urls.py

@@ -13,9 +13,11 @@ Including another URLconf
     1. Import the include() function: from django.conf.urls import url, include
     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
 """
-from django.conf.urls import url
-from django.contrib import admin
+from django.conf.urls import url, include
+from django.conf.urls.static import static
+from django.conf import settings
+from apps.dashboard.views import index
 
 urlpatterns = [
-    url(r'^admin/', admin.site.urls),
+    url(r'^$', index),
 ]

+ 16 - 4
manage.py

@@ -1,10 +1,22 @@
 #!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
 import os
 import sys
 
-if __name__ == "__main__":
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cosmetics_shop.settings")
-
-    from django.core.management import execute_from_command_line
 
+def main():
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cosmetics_shop.settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
     execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+    main()
+